TowardsDataScience-博客中文翻译-2022-二十三-
TowardsDataScience 博客中文翻译 2022(二十三)
返回函数的函数
Python 中的高阶函数和装饰器及其示例
茱莉亚·卡德尔在 Unsplash 上的照片
高阶函数是一个重要的主题,我相信你应该很好地掌握它们是如何工作的,以充分受益于 Python 的能力并充分利用你的代码。
我将用一个例子来说明高阶函数,这样您可以获得它们的工作知识,然后我们将使用这些知识来实现一个装饰器,这是 Python 中高阶函数最典型的用例之一。
我们开始吧!
本帖包含部分转载自我的书内容:**深度学习用 PyTorch 循序渐进:初学者指南 。**
一系列功能
假设我们想要构建一个系列的函数,每个函数都执行给定幂的幂运算。代码应该是这样的:
执行指数运算的一系列函数
嗯,很明显有一个更高的结构指向这个:
- 每个函数都有一个参数 x (红色),这是我们想要取幂的数字;
- ****每个函数都执行相同的运算,取幂,但是每个函数都有一个不同的指数(橙色框)。
解决这个问题的一个方法是使指数成为显式参数,就像下面的代码一样:
具有两个常规参数的典型函数
这很好,而且效果很好。但是它也要求你在每次调用函数时指定指数。一定还有别的办法!当然有;这就是这篇文章的目的:-)
函数生成器
我们需要构建另一个(高阶)函数来构建那些函数(正方形、立方体等。)对我们来说。(高阶)函数只是一个函数生成器。但是我们怎么做呢?
首先,让我们构建我们试图生成的函数的“骨架;它们都取单个自变量xT5(红色),而它们都执行求幂运算,每个都使用一个不同的指数(橙色框,不是自变量)。****
好吧。它应该是这样的:
函数的框架有一个未定义的变量(指数)
如果您尝试用任何一个 x 调用这个函数,比如说skeleton _exp 幂运算(2) ,您会得到下面的错误😗***
skeleton_exponentiation(2)**Output:
NameError: name ‘exponent’ is not defined**
这是意料之中的:你的“骨架函数有不知道变量指数是什么!这就是高阶函数要完成的任务。
我们用一个高阶函数(蓝色)包装我们的框架函数,它将构建所需的函数。我们姑且称之为指数构建器()。
它的参数是什么,如果有的话?嗯,我们正试图告诉我们的“骨架”函数它的指数应该是多少(橙色框),所以让我们从那个开始吧!
高阶函数,定义骨架函数的指数
现在我想让你看一下(外层)返回语句(在蓝色函数中)。是而不是返回一个值;是返回了骨架功能,而不是。
这毕竟是一个函数构建器:它应该构建(并返回)函数。
如果我们调用这个给定指数的高阶函数,比如说 2,会发生什么?
returned_function = exponentiation_builder(2)
returned_function**Output:
<function __main__.exponentiation_builder.<locals>.skeleton_
exponentiation(x)>**
****结果正如所料,是一个函数!
这个函数是做什么的?它应该证明了它的论点——让我们来看看:
returned_function(5)**Output:
25**
然后瞧啊!我们有一个函数生成器!我们可以用它来创建尽可能多的指数函数:
由高阶函数建立的一系列函数
没那么糟,对吧?这个概念本身非常简单,但是需要密切注意实现细节,以确保“嵌套”的函数和参数被正确定义。
到处都有功能
现在,让我们更进一步:如果我们的高阶函数采用另一个函数作为参数,而不是常规参数(指数)呢?****
“等等,什么?!将另一个函数作为参数并返回第三个不同函数的函数?真的吗?”
是的,完全正确!令人震惊,对吧?😃
令人兴奋的功能!
“我为什么要这么做?”
因为很酷!不,真的,(高阶)函数可以作为装饰器,并且可以用于修改它附加到的任何函数或方法的输出。
准备好了吗?让我们开始吧!
假设你写了很多函数,它们都以给定的单位返回值(比如,在美国是英寸,在其他地方是毫米)。
“许多”函数返回值以英寸为单位
现在,如果你被要求把你的代码发送到世界的另一个地方,那么修改你写的每一个函数来包含一个乘法因子以适应不同的单位是非常麻烦的,对吗?****
修改输出,繁琐的方式!
“老实说,这看起来像是糟糕的软件工程…”
是的,最有可能的是是糟糕的软件工程——理想情况下,人们会预见到以不同单位产生输出的需要,并将单位(英寸或毫米)作为一个参数,但这超出了这里的重点。
所以,请继续这个虚构的故事,让我们看看如何毫不费力地修补这个虚构的代码:-)
我们知道,我们的 f2() 函数接受两个参数, a 和 b** (红色),执行一些操作(不管那是什么,我们不关心操作本身),并产生一个结果,我们希望将该结果从英寸转换为毫米。那么,也许我们可以围绕【F2()构建另一个函数来执行转换?**
使用外部函数修改输出
但是这只适用于 f2() ,我们希望有一个更通用的解决方案,所以让我们用一个通用函数来替换 f2() , func ()** 😗*
具有一个未定义变量(func)的通用函数
“这看起来很熟悉…”
这很有意义,因为它非常类似于“函数构建器”一节中的“框架”函数。和以前一样,参数在红色中,而橙色框是将要执行的底层通用函数。
那么,你能猜到如果我们用任何参数调用 apply() 函数会发生什么吗?
“一个错误?”
你答对了——和之前一样的错误,一个 NameError ,因为 apply() 函数不知道底层函数 func —是什么。
apply(1, 4)**Output:
NameError: name ‘func’ is not defined**
同样的错误需要同样的解决方案:让我们用蓝色的高阶函数“包装”我们的 apply() 函数。因为我们试图告诉我们的 apply() 函数底层函数( func )应该是什么(橙色框),让我们用它作为“包装器”函数的一个参数!
高阶函数,定义应用函数的基础函数(func)
和之前一模一样:(外部)返回语句(蓝色函数中)是返回一个函数。不同的是橙色框,“wrapper”函数的参数也是一个函数!****
“功能,功能,声音!”
我听到了,太多的功能要记住…所以,让我们快速回顾一下四个(!)上面五行代码中的函数:
- 底层 函数 : func() ,它执行一些代码,不管那是什么,并输出一个结果,据报道以英寸为单位;
- 乘法函数: inches_to_mm() ,将一个结果从英寸转换成毫米;
- 通用的“应用”函数**:apply(),带参数(红色),执行底层函数,修改结果(转换为毫米);**
- 高阶“包装器”函数:convert _ to _ 毫米()*** ,其中将我们正在尝试执行的的底层函数作为参数。***
你还和我在一起吗?我知道这很棘手,所以花点时间把上面的结构看几遍,让它深入理解。
现在,让我们最后使用高阶“包装器”函数来转换原始【F2()函数的输出:
原始的 f2()函数
记住,【F2()函数要扮演底层函数的角色,也就是作为参数传递给“包装器”函数convert _ to _ 毫米()*** 。***
*f2_in_millimeters = convert_to_millimeters(f2)
f2_in_millimeters**Output:
<function __main__.convert_to_millimeters.<locals>.apply(a, b)>***
一个函数进去,另一个函数出来!
让我们调用 a =1 和 b =4 的结果函数:
*f2_millimeters(1, 4)**Output:
101.6***
现在我们有了:101.6 毫米,对应于 4 英寸的原始输出。
但是等等,还有!
装饰功能
事实证明,你甚至不需要创建另一个函数,你可以简单地在底层函数 f2() 之上加上“包装器”函数convert _ to _ 毫米(),作为装饰器!
装饰 f2()函数
现在您可以直接调用F2(),装饰器将自动执行底层函数(即 f2() )并修改结果,因此您已经获得了以毫米为单位的输出!**
*f2(1, 4)**Output:
101.6***
很酷,对吧?
“好的,很酷,但是如果我想把 f3() 的输出也转换成毫米呢?不会起作用,因为 f3() 有三个参数!
接得好!为了让我们的convert _ to _ 毫米() decorator 真正通用,我们需要修改内部的 apply() 函数来接受任何参数:**
一个真正通用的高阶函数,为 apply 函数定义底层函数(func)
现在你可以用这个“包装器”函数来修饰任何你想要的函数,【f1()【F2()【F3()等等…**
就这样结束了:-)
感谢你阅读这篇文章——我相信学习应该是容易的,有趣的,所以我希望你喜欢它:-)。
进一步阅读
装饰者是一个很大的话题,我只是想给大家展示一下背后的主要思想,也就是修改一个底层函数的输出/行为。如果你想深入这个话题,请查看 Real Python 的“Python Decorators 入门”。**
如果您有任何想法、意见或问题,请在下方留言或通过我的bio . link页面联系。**
自然语言处理的基本 EDA 技术
原文:https://towardsdatascience.com/fundamental-eda-techniques-for-nlp-f81a93696a75
从数量、长度、词频到为什么不需要词云
图片由作者提供。
探索性数据分析(EDA)是任何数据科学项目工作流程中的一个重要步骤。然而,当在自然语言处理(NLP)项目中处理文本数据时,您需要应用与处理表格数据不同的技术。
处理文本数据[…]时,您需要应用与处理表格数据等不同的技术。
因此,在本文中,我们将研究一些针对文本数据的基本 EDA 技术:
对于本文,我们将使用来自 Kaggle 的女装电子商务服装评论数据集。
为了简化示例,我们将使用 450 条正面评论(rating == 5
)和 450 条负面评论(rating == 1
)。这将数据点的数量减少到 900 行,将评级类别的数量减少到两个,并平衡正面和负面的评论。
此外,我们将只使用两列:评论文本和评级。
精简数据集的数据帧头如下所示:
简化的女性电子商务服装评论数据集负责人(图片由作者提供)
计数和长度
先说一些基本的计数和长度。我们将通过一个简单的示例回顾文本来介绍每个功能:
text = "Very #comfortable and #versatile. Got lots of compliments."
字符计数
你可以从计算一篇文章中的所有字符开始。
char_count = len(text)
对于我们复习课文的例子,char_count = 58
。
字数
接下来,你可以统计一篇课文中的所有单词。
word_count = len(text.split())
对于我们的示例复习文本,word_count = 8
。
句子计数
如果你有较长的文本,你也可以计算句子的数量。为此,您需要从 NLTK 导入sent_tokenize
函数。
import nltk
from nltk.tokenize import sent_tokenizesent_count = len(sent_tokenize(text))
对于我们的示例复习文本,sent_count = 2
。
字符串计数
您还可以计算特定的字符或字符串。例如,你可以通过计算字符“#”来计算标签的数量。你也可以计算提及次数(“@”)或网站(“http”)等等。
hashtag_count = text.count("#")
对于我们的示例复习文本,hashtag_count = 2
。
平均单词长度
你也可以计算一个单词的平均长度。
import numpy as npavg_word_len = np.mean([len(w) for w in str(text).split()])
对于我们的示例复习文本,avg_word_len = 6.375
。
平均句子长度
如果你有较长的文本,你也可以计算一个句子的平均长度。
avg_sent_len = np.mean([len(w.split()) for w in sent_tokenize(text)])
对于我们复习课文的例子,avg_sent_len = 4.0
。
使用map()
功能,您可以将上述所有技术应用到 pandas 数据框架中的文本列:
import numpy as np
import nltk
from nltk.tokenize import sent_tokenize# Character counts
df["char_count"] = df["text"].map(lambda x: len(x))# Word counts
df["word_count"] = df["text"].map(lambda x: len(x.split()))# Sentence counts
df["sent_count"] = df["text"].map(lambda x: len(sent_tokenize(x)))# String counts
df["hashtag_count"] = df["text"].map(lambda x: x.count("#"))# Average word length
df["avg_word_len"] = df["text"].map(lambda x: np.mean([len(w) for w in str(x).split()]))# Average sentence length
df["avg_sent_len"] = df["text"].map(lambda x: np.mean([len(w.split()) for w in sent_tokenize(x)]))
在此之后,您的初始数据帧可能如下所示:
从文本创建新特征后的 DataFrame 的头部(图片由作者提供)
现在,您可以使用直方图、KDE 图或箱线图来探索这些新功能。如果您正在处理文本分类问题,当您可视化数据时,也可以按类进行区分。
import seaborn as sns# Histogram
sns.histplot(data = df, x = feature, hue = class)# KDE plot
sns.kdeplot(data = df, x = feature, hue = class)# Boxplot
sns.boxplot(data = df, x = class, y = feature)
下面你可以看到我们的例子 KDE 图。
由“评级”类分隔的新创建功能的 KDE 图(图片由作者提供)
词频分析
这是你可能想使用单词 cloud 的地方。不要。
我专门用了一整节的篇幅来讲述为什么我会在这篇文章之后避免使用单词云。但我们先来说说如何探索和可视化最常用的术语。
在开始之前,我们需要对文本进行预处理,将所有内容都改为小写,并删除所有标点符号和非罗马字符。
import re
import stringdef clean_text(text):
# Convert text to lowercase
text = text.lower() # Remove punctuation
text = re.sub("[%s]" % re.escape(string.punctuation), "", text) # Remove non-Roman characters
text = re.sub("([^\x00-\x7F])+", " ", text)
return textdf["text_clean"] = df["text"].map(lambda x: clean_text(x))
清理后的评论文本如下所示:
左边是原始评论文本,上面是经过清理的评论文本(图片由作者提供)
最常用的词
要得到最常用的词,首先需要创建一个所谓的“语料库”。这意味着我们创建了一个列表,其中包含所有来自已清理评论文本的相关单词。我说的“相关”词是指不是停用词的词,比如“是”、“for”、“a”、“and”。
from nltk.corpus import stopwordsstop = set(stopwords.words("english"))corpus = [word for i in df["text_clean"].str.split().values.tolist() for word in i if (word not in stop)]
corpus
看起来是这样的:
文集的开头(图片由作者提供)
现在,要从语料库中获取最常见的单词,您有两种选择:
您可以使用FreqDist
类:
from nltk.probability import FreqDist
most_common = FreqDist(corpus).most_common(10)
或者你可以使用Counter
类:
from collections import Counter
most_common = Counter(corpus).most_common(10)
使用most_common(10)
函数,将返回前 10 个最常用的单词及其频率。
最常用的单词(图片由作者提供)
有了这个,你可以很容易地创建一个最常见的单词的柱状图:
words, frequency = [], []
for word, count in most_common:
words.append(word)
frequency.append(count)
sns.barplot(x = frequency, y = words)
下面,你可以看到负面和正面评论中最常见的 10 个词:
由“评级”类分隔的最常用词(图片由作者提供)。
从这个技巧中,我们可以看到在正面和负面的评论中,“连衣裙”和“上衣”是最常被提及的。然而,在积极的评论中,像“伟大”和“完美”这样的形容词被提到了很多,而在消极的评论中却不是这样。
最常见的 N-gram
让我们对 n-grams 做同样的事情。什么是 n-gram?它是文本中 n 个单词的序列。
例如,句子“你今天好吗?”的二元语法(n = 2)会是:“你好吗”、“你好吗”和“今天的你”。三元组(n =3)将是“你好吗”和“你今天好吗”。
要将文本分成 n 元语法,可以使用如下所示的CountVectorizer
类。
在ngram_range
中,您可以定义要考虑的 n-grams。比如ngram_range = (2, 2)
只考虑二元,ngram_range = (3, 3)
只考虑三元,ngram_range = (2, 3)
考虑二元和三元。
from sklearn.feature_extraction.text import CountVectorizer# Initialize CountVectorizer
vec = CountVectorizer(stop_words = stop, ngram_range = (2, 2))# Matrix of ngrams
bow = vec.fit_transform(df["text_clean"])# Count frequency of ngrams
count_values = bow.toarray().sum(axis=0)# Create DataFrame from ngram frequencies
ngram_freq = pd.DataFrame(sorted([(count_values[i], k) for k, i in vec.vocabulary_.items()], reverse = True))
ngram_freq.columns = ["frequency", "ngram"]
得到的数据帧ngram_freq
如下所示:
包含二元模型的数据帧头(图片由作者提供)
下面你可以看到由正面和负面评论分开的二元和三元词汇:
按“评级”分类最常见的双字母组合(图片由作者提供)。
按“评级”分类最常见的三元组(图片由作者提供)。
如你所见,这种 EDA 技术有助于你理解评论的不同基调。
用那些文字云想念我
图片由作者提供。
注意,我们怎么没有谈到单词云?是的—您可以在不使用词云的情况下对文本数据执行 EDA。
字云何惑。虽然较频繁的术语比不太频繁的术语以更大的字体显示,但是很难掌握相似的频繁词之间的顺序。
一个简单的条形图可能不像单词云那样华丽,但它在可视化精确排名和术语频率方面做得更好。
所以,除非你的管理层真的想看到一个词云,否则我会推荐用条形图来代替。
除非你的管理层真的想看到一个词云,否则我会推荐用条形图来代替。
结论
在本文中,我们研究了一些针对文本数据的基本 EDA 技术:
- 计数和长度:我们查看了字符数、字数、句子数和字符串数,以及平均单词和句子长度。
- 词频分析:我们看了最常用的词和 n-gram,讨论了为什么不需要词云。
下面是所有可快速复制的代码片段:
你可以在这篇关于“NLP 的中级 EDA 技术”的文章中找到这篇文章的续篇:
喜欢这个故事吗?
这里收集了我的其他自然语言处理文章:
莉奥妮·莫尼加蒂
自然语言处理
View list3 stories
如果你想把我的新故事直接发到你的收件箱,请务必 订阅 !
成为媒介会员,阅读更多来自我和其他作家的故事。报名时可以用我的 推荐链接 支持我。我将收取佣金,不需要你额外付费。
https://medium.com/@iamleonie/membership
资料组
《nicapotato》《女装电商服装评论》。(License:CC0:Public Domain)https://www . ka ggle . com/datasets/nica potato/women-ecommerce-clothing-reviews(2022 年 8 月 30 日访问)。
Python 的矩阵代数基础|第 1 部分
原文:https://towardsdatascience.com/fundamentals-of-matrix-algebra-with-python-part-1-85aaa17e3632
使用 Python 理解和实现基本矩阵代数概念和运算
介绍
矩阵代数是工程学和计算机科学中许多复杂而突出的研发领域的基础。例如,神经网络中使用的机器学习算法广泛使用矢量化来大幅提高计算时间。
照片由 Aron Van de Pol 在 Unsplash 上拍摄
本文介绍了矩阵代数的一些基本概念,并用一些 Python 代码来说明结果。主题在简要细节中介绍,因此鼓励读者将此作为进一步研究的起点。
代码将使用 NumPy ,这是一个无价的处理矩阵的 Python 库。
内容
- 什么是矩阵?
- 分度元件
- 上下三角形矩阵
- 转置
- 对称性
理解这些概念为探索航天器姿态动力学与控制中的参考系变换等更高级的课题奠定基础。
1)什么是矩阵?
矩阵是由数字组成的矩形阵列。尺寸通常按照行* × 列、或( m×n )的顺序进行描述,如图 1 所示。*
如果 n = 1 ,则矩阵是一个列向量。同理,若 m = 1 ,则称为一个行向量。
图 1 —示例 m × n 矩阵(图片由作者提供)
使用 Gist 1 中的 Python 代码通过 Numpy 创建这些数组。
要点 Numpy 中的矩阵示例
2) 分度元件
矩阵元素使用( i,j) 下标索引,其中 i 对应行号,j对应列号,如图 2 所示。
图 2 —使用(i =行,j =列)值访问矩阵的元素(作者图片)
使用 Gist 2 中提供的代码在 Python 中执行如上所示的等价索引操作。注意,在 Python 编程语言中,数组索引是从零开始的。
要点 Numpy 中的索引元素
3)上下三角形矩阵
图 3 提供了两种类型矩阵的例子。
图 3 —上下三角矩阵(图片由作者提供)
- 如果对角线上和对角线下的元素为非零且所有其他值为零,则矩阵为下三角形。
下三角条件
- 或者,如果对角线上和对角线上方的元素为非零且所有其他条目为零,则矩阵为上三角。
上三角条件
使用 Gist 3 中的 Python 代码测试这些条件。图 4 显示了一个示例输出。
要点 4 —创建上下三角矩阵
图 Gist 4 的控制台输出示例(图片由作者提供)
4) 转置
转置矩阵是一个相对简单的操作。它包括将行写成列 或者将列写成行。
转置一个 3×3 矩阵,如下图 5 所示。
图 5 —矩阵的转置(图片由作者提供)
Gist 5 是转置矩阵的一个简单的 Python 实现。始终使用 Numpy 或其他设计用于高效执行矩阵代数的库。
要点 5 —转置 mxn 矩阵的简单 Python 代码
这种算法在一个中等大小的矩阵上运行m = 6000, n = 2500
,用≈8.49
秒,也就是计算机时间的千年。
对比 Numpy 中的等效操作,执行时间为≈0.000451
秒。
# define mxn matrix
A = np.random.rand(m, n)# numpy transpose operation
A_transpose_numpy = A.T
本文介绍了矩阵代数的基本概念,但没有演示如何实现最有效的算法。
5)对称性
给定一个方阵 一个 ,行列数相等;若 A = Aᵀ 则为对称。对称矩阵的一个例子如图 6 所示。
图 6 —对称 3x3 矩阵
使用 Numpy 对断言对称性很简单。检查转置矩阵的所有元素是否与 A 相同。
要点 6 —数字对称断言
一个反对称矩阵等于其转置的负,即 A = -Aᵀ 。这种情况见图 7。注意对角线元素必须为零。
图 7 —反对称 3×3 矩阵
要点 7 — Numpy 斜对称断言
结论
了解这些基本概念对工程和计算机科学专业的学生很有价值。然而,这篇文章只是触及了矩阵代数的表面。第二部分链接如下,讨论了追踪、逆等其他项目。
如果你对 Python、工程学和数据科学感兴趣,请关注并查看我的其他文章。
https://medium.com/@andrewdaviesul/membership </5-python-projects-for-engineering-students-8e951b7c131f>
感谢阅读。
参考
[1] 工程师矩阵代数 — 杰弗里·r·查斯诺夫
Python 的矩阵代数基础|第 2 部分
原文:https://towardsdatascience.com/fundamentals-of-matrix-algebra-with-python-part-2-833e447077d5
使用 Python 理解和实现基本矩阵代数概念和运算
介绍
本文跟随“Python 矩阵代数基础|第 1 部分”。它引入了更多的矩阵代数的基本概念,并用一些 Python 代码来演示结果。
照片由 Dan Schiumarini 在 Unsplash 上拍摄
内容
- 追踪
- 行列式
- 逆
- 正交性
- 行排列
这份关于矩阵的初步材料有助于探索更高级的主题,例如使用状态空间模型模拟动态系统。
1)跟踪
矩阵的轨迹就是其对角元素的和,如图 1 所示。
图 1 —矩阵的轨迹(图片由作者提供)
Gist 1 提供 Python 代码来计算一个矩阵的迹,这是琐碎的使用 Numpy。
要点 1 —计算矩阵的轨迹
2)行列式
方阵的行列式是标量。非方阵没有行列式。
以图 2 中的 A 为例。
图 2 —示例 2×2 矩阵(图片由作者提供)
图 3 给出了计算一个 2×2 行列式的公式。
图 3–计算 2×2 行列式(图片由作者提供)
一个重要的几何结果是 2×2 行列式标量值等于由 A 的列向量组成的平行四边形形成的面积。
使用 Gist 2 中的代码在 Matplotlib 中绘制一个 2×2 矩阵的列向量表明 v₁ (第一列)和 v₂ (第二列)组成了一个平行四边形的条边。
要点 2 —显示平行四边形边的列向量
图 4 是从上面的代码生成的带标签的图。列向量形成平行四边形。
图 4 —平行四边形的面积(图片由作者提供)
检查和修改Gist 2 中的 Python 代码以生成更多示例。
图 5 描绘了两个共线向量,意味着它们是线性相关的。因此,不可能构造一个平行四边形。
图 5 —线性相关向量(图片由作者提供)
因此,这说明如果行或列是线性相关的,则行列式的值为 0。
3)逆
图 6 描绘了一个 3 × 3 的单位矩阵,I 。
图 6 —身份矩阵(图片由作者提供)
将 一个矩阵与其逆矩阵相乘得到单位矩阵,I 即a⁻= I,其中 A ⁻表示 A 的逆用图 7 中的表达式计算 a ^ 2×2 的逆。
图 7–2×2 矩阵求逆(图片由作者提供)
不是所有的矩阵都是可逆的。如图 7 所示,行列式出现在计算中。因此, A 必须是平方 ( n×n )且有一个非零行列式才有逆。
4)正交性
正交性与正交性同义。当列或行是正交向量时,正交性适用。
标准正交向量就是垂直,如图 8 所示。
图 8 —正交矢量(作者图片)
使用图 9 中的条件测试正交性。
图 9 —正交性条件(图片由作者提供)
基本上,这个表达式意味着如果 A 的转置等于它的逆矩阵,则 A 是正交的——使用类似于 Gist 3 的代码来检查矩阵是否正交。
要点 3 —正交条件
5)行排列
一个 置换矩阵 是一个二进制方阵,每行每列有一个条目为 1,其他地方为 0。
引入单位矩阵后,允许矩阵 A 的行包括将矩阵乘以修改后的 I 。
图 9 —排列矩阵(图片由作者提供)
以图 10 中的矩阵 A 为例。
图 10 —演示排列的示例矩阵(图片由作者提供)
置换行涉及改变行 或 列的顺序,例如,将 r₁移到 r₂,将 r₂移到 r₁.图 11 描述了使用上面定义的置换矩阵 P 的两种置换的例子。
图 11-行和列排列(作者图片)
Gist 4 给出了执行这些操作所需的 Python 代码。
要点 Python 中的置换
结论
本文介绍了矩阵代数的五个基本概念。上述主题在细节上简洁,因此鼓励读者将此作为进一步研究的起点。
建议的下一个主题是使用 Python 中的高斯消去算法求解线性方程组* 。*
https://levelup.gitconnected.com/gaussian-elimination-algorithm-in-python-4e90cb3a0fd9 https://medium.com/@andrewdaviesul/membership
感谢阅读。
参考
[1] 工程师矩阵代数 — 杰弗里·查斯诺夫 【2】置换矩阵 —维基百科
SQL 窗口函数简介:第 1 部分
原文:https://towardsdatascience.com/fundato-sql-window-functions-part-1-ab61df519973
为数据科学面试做好准备
SQL 窗口函数(图片由作者提供)
背景
窗口函数对于用几行代码有效地执行数据操作是非常有用的,这也是为什么你会在几乎每个数据科学访谈中发现一个关于窗口函数的问题。
这是 SQL 窗口函数系列的第 1 部分。在这篇博客中,我们将学习 SQL 窗口函数的基本原理及其应用。
为什么需要窗口功能?
假设我们有一个组织内员工的工资数据。下表显示了数据:
员工表(作者图片)
现在,假设我们要向该数据表添加 2 列:
- TOTAL:该列包含所有员工的工资总额。这等于薪水栏的总和。
- TOTAL_JOB:该列包含与某一行相对应的工作角色(数据科学家、数据分析师、数据工程师)中所有雇员的工资总额。例如,对于“JOB”为数据科学家的行,TOTAL_JOB 列等于 12,000(数据科学家的工资总和:3800+4900+3300)。
向雇员表中添加列(按作者排序的图像)
我们可以在不执行分组和自连接的情况下添加这些列吗?
是的,我们使用窗口函数很容易做到这一点。
SQL 中的窗口函数是什么?
window 函数对数据表中的一行或多行执行计算,并将值返回给表中的所有行。与聚合函数(使用 GROUP BY 子句)不同,在聚合函数中,单个行会“丢失”,而窗口函数不会将多行的结果组合成一行,并且每一行都保留其原始身份。
聚合函数与窗口函数(图片由作者提供)
窗口函数的语法
以下是窗口函数的语法:
SELECT
<column_1>, <column_2>,
**<window_function>(expression)OVER
(PARTITION BY<partition_list>
ORDER BY<order_list>)**
FROM
<table_name>
让我们详细了解一下每个关键词:
- 窗口函数是我们希望应用的窗口函数的名称,如 sum、mean、行号等。
- 表达式是应用窗口函数的列名。根据我们使用的窗口功能,这可能是必需的,也可能不是必需的。例如,行号窗口函数不需要表达式。
- OVER 简单表示该函数是窗口函数。
- PARTITION BY 对数据表的行进行分区,允许我们定义利用哪些行来计算窗口函数。
- 分区列表是我们想要进行分区的列的名称。对于 PARTITION BY 子句,这是必需的。
- ORDER BY 用于对每个分区内的行进行排序。这是一个可选条款。
- Order list 是要排序的列的名称,对于 ORDER BY 子句是必需的。
一些例子
要查看窗口函数的运行情况,让我们看几个例子:
- OVER 子句没有被分割
要添加一个包含 employee 表中所有雇员薪金总和的列(TOTAL ),我们将使用 sum 函数作为窗口函数,使用 salary 列作为表达式,并使用 OVER()子句。
因为我们正在查找所有雇员(行)的薪金总和,所以不需要对数据进行分区。
## SQL Query
select EMPID, NAME, JOB, SALARY,
**sum(SALARY) over() as TOTAL** FROM
employee_table
- OVER 子句用进行划分
现在,要添加一个列,其中包含与某行相对应的工作角色(数据科学家、数据分析师、数据工程师)中所有员工的总工资,我们需要按列 job 对数据进行分区。
Job 列上的数据分区(图片由作者提供)
为了获得输出,我们将使用 sum 函数作为窗口函数,salary 列作为表达式,在 OVER()子句中,我们将按 JOB 列对数据表进行分区。
## SQL Query
select EMPID, NAME, JOB, SALARY,
**sum(SALARY) over(partition by** **JOB)
as TOTAL_JOB** FROM
employee_table
结论
因此,我们研究了如何使用 SQL 中的 window 函数轻松地将聚合值添加到表的所有行中。
我们可以创建跨所有行以及跨行分区的汇总列,而不会丢失表中的原始行。
谢谢大家!
你可以在收件箱里看到我所有的帖子。 在这里做 ! 如果你喜欢体验媒介的自己,可以考虑通过 报名会员来支持我和其他成千上万的作家。它每个月只需要 5 美元,它极大地支持了我们,作家,而且你也有机会通过你的写作赚钱。
围棋模糊测试
原文:https://towardsdatascience.com/fuzzing-tests-in-go-96eb08b7694d
模糊化是一种自动为函数生成输入值来查找 bug 的技术
Go 中的 Fuzzing 允许我们测试随机生成的函数输入。图片由珀西·博尔梅尔提供。Gopher 由拓也·上田提供,原始 Go Gopher 由勒内·弗伦奇提供(CC BY 3.0)
Fuzzing 是作为 Go 1.18 中标准库的一部分发布的。这是一种很酷的方法,可以定位你从未想到的代码中的错误。我知道很多 bug 是使用第三方 fuzzers 的人在 Go 的标准库中发现的。
模糊化将是常规testing
库的一部分,因为它是一种测试。它也可以和来自testing
的其他工具一起使用,这很好。
在这篇文章中,我们将学习如何使用新的 fuzzer,以及 fuzzer 我们已经建立的 HTTP 处理程序。
在我们开始之前,你需要确保你运行的至少是 Go 1.18,你可以在我对 1.18 的修改中看到如何安装它。
https://medium.com/@percybolmer/go-1-18-comes-with-many-amazing-changes-d33ac0afd6ee
这篇关于编程的文章的视频格式是 Youtube
设置模糊
向 fuzzer 添加语料库种子,以允许它基于它生成数据。图片由珀西·博尔梅尔提供。Gopher 由拓也·上田提供,原始 Go Gopher 由勒内·弗伦奇提供(CC BY 3.0)
为了开始 fuzzing,我创建了一个新项目,并初始化一个模块,我们将在里面工作。我们还需要一个以_test.go
为后缀的文件,在我的例子中,我初始化了一个名为 Fuzzy 的模块和一个main_test.go
mkdir fuzzy
go mod init programmingpercy.tech/fuzzy
touch main_test.go
在我们模糊之前,我们需要一些模糊的东西。让我们创建一个处理程序来计算一个整型切片中的最大值。
我们将在处理程序中引入一个错误,看看我们如何通过 fuzzing 找到它,错误将是任何等于 50 的结果都会导致它失败。
带有已知错误的简单 HTTP 处理程序
这些模糊的东西是什么
是时候开始学习如何使用模糊器了。
如果你熟悉 Go 测试,这可能会非常容易。Go 中的测试是通过创建一个以Test
为前缀的函数并接受输入参数t *testing.T
来定义的。
同样的模式被用于模糊化,但是以Fuzz
为前缀并接受新的f *testing.F
。
定义模糊器很简单,只需使用testing。f 作为输入而不是测试。T
我们需要做的第一件事是为testing.F
提供一个Seed Corpus
,您应该将它视为示例数据。这是 fuzzer 将使用的数据,并将其修改为尝试的新输入。
种子应该尽可能地反映出函数的输入应该是什么样子,以获得模糊器的最佳结果。
添加种子是由接受以下数据类型的f.Add()
完成的。
- 字符串,[]字节,符文
- int,int8,int16,int32,int64
- uint,uint8,unit16,uint32,uint64
- 浮动 32,浮动 64
- 弯曲件
请注意,您不能添加多种数据类型,如果您首先运行f.Add("hello")
,然后运行f.Add(10)
,它将会死机。
mismatched types in corpus entry: [int], want [string]
等等,但是我的函数需要多个输入参数?—愤怒的反 Fuzzer
如果需要多个输入参数,f.Add()
接受可变数量的输入。因此,您可以添加许多示例数据输入,只需确保示例种子与您的函数输入参数的顺序相同。
模糊多个输入参数
现在我们知道了如何添加种子,让我们为处理程序和Add
创建示例数据。我们将希望添加带有 JSON 数据的[]byte
,因为我们正在模糊一个 HTTP 处理程序。
我们将首先设置一个 HTTP 服务器来托管我们正在模糊化的处理程序,并创建一些示例片段来提供给Seed Corpus
。然后,我们将切片整理成适当的 JSON,并将它们添加到 fuzzer 中。
托管 HTTP 处理程序并添加种子语料库。
是时候实现模糊目标了。
要开始模糊化,我们必须使用f.Fuzz()
,它接受一个函数作为输入。该函数是模糊目标,应该检查错误,准备数据,并触发我们正在模糊的函数。
到Fuzz
的输入函数必须接受testing.T
作为第一个参数,随后是添加到语料库的输入数据类型,以同样的顺序接受。
在我们的例子中,我们只需要传递testing.T, []byte
,因为我们只给种子添加了一个[]字节。但是如果你添加了更多的输入,它们也需要在这里声明。
多个输入的外观示例
让我们添加模糊目标。我们将想要做以下事情
- 将数据发送给我们的处理程序
- 检查响应状态
- 检查响应是否为 int。
你可能会想,我们应该检查返回值是否正确,如果你有另一个已知有效的函数,你可以这样做。但是大多数时候你不知道预期收益,因为你不知道模糊器产生了什么输入。
模糊器的初稿
终于到了运行 fuzzer 的时候了。为了启动 fuzzer,我们运行常规的go test
命令,但是添加了--fuzz=Fuzz
标志,标志值将被用作前缀,并以 Fuzz 开始所有方法。你可以把它变成你想要的,只匹配某些方法签名。
例如,要开始模糊化我们的FuzzTestHTTPHandler
,您可以运行下面的代码。
go test --fuzz=FuzzTestHTTPHandler
这是有用的,当你在未来有多个模糊功能。
模糊化与常规测试有点不同,的默认行为是一直运行,直到出现故障,所以你要么需要取消模糊化,要么等到出现错误。还有第三个选项,添加-fuzztime
标志会在设定时间后取消。
所以要跑 10 秒钟,你会跑
go test --fuzz=Fuzz -fuzztime=10s
确保运行 fuzzer,并等到它失败。您应该会看到类似于我的输出,但是因为输出是生成的,所以可能不一样。
--- FAIL: FuzzTestHTTPHandler (0.51s)
--- FAIL: FuzzTestHTTPHandler (0.00s)
main_test.go:80: Expected status code 200, got 400
main_test.go:87: Error: invalid character 'j' looking for beginning of valueFailing input written to testdata\fuzz\FuzzTestHTTPHandler\582528ddfad69eb57775199a43e0f9fd5c94bba343ce7bb6724d4ebafe311ed4
To re-run:
go test -run=FuzzTestHTTPHandler/582528ddfad69eb57775199a43e0f9fd5c94bba343ce7bb6724d4ebafe311ed4
当 fuzzer 期间出现错误时,它将取消并将导致失败的输入参数写入文件。所有故障都被写入testdata\fuzz\NameOfYourFuzz\inputID
。
在这种情况下,JSON 封送处理似乎失败了,让我们查看 fuzzer 文件的内容。
go test fuzz v1
[]byte("0")
第一行是关于 fuzzer 及其版本的信息。第二行是我们发送给 HTTP 处理程序的生成的有效负载。
这次失败了,因为 fuzzer 发送了一个原始的 0,这没什么,因为那不是正确的 JSON。
当您编写第一个 fuzzer 时,这种情况并不少见,所以让我们将 fuzzer 数据做得更好。我们只想测试有效载荷何时是真正的 JSON,因为我们已经知道任何错误的 JSON 有效载荷都会失败,并且应该失败。
您可以使用t.Skip("reason of skipping")
跳过不正确的有效载荷,这在模糊化时很有用。我们将使用json.Valid
函数来确保我们只将正确的 JSON 传递给处理程序。
现在只检查 json。有效是不够的,“0”是有效的 JSON。然而,我们可以尝试将数据解组到一个ValuesRequest
结构,并跳过任何失败的有效载荷。这是一个过滤掉不良生成的有效载荷的好方法。
带有有效载荷验证的最终模糊器,以确保只测试真实数据
运行模糊器
go test -v --fuzz=Fuzz
让它运行一段时间,最终,你应该得到一个新的失败文件。该文件将向您显示检测到的错误,任何值 50 都会导致失败。
go test fuzz v1[]byte("{\"vAlues\":[50]}")
结论
好了,你现在可以写你的模糊了。我们使用的例子可能看起来很荒谬,因为你知道value == 50
错误是由一个简单的 if 语句产生的。然而,为您的 HTTP 处理程序或任何其他方法编写 fuzzers 是检测难以发现的错误的好方法。
模糊器通常可以找到单元测试遗漏的 bug,因为单元测试通常只包含您输入的可能会导致失败的值。
正如您在示例中看到的,为 HTTP 处理程序编写 fuzzer 的代码并不多,而且一旦您做了几次,就可以很快实现新的代码,使您的代码堆栈更加健壮且没有错误。
你还在等什么,出去吧!
如果你想了解更多关于 Go 1.18 的变化,你可以在我的另一篇文章中阅读如何使用新的泛型。
https://medium.com/@percybolmer/learning-generics-in-go-318f53752ccd
模糊字符串匹配—如何匹配不同的字符串
人类思维很容易注意到接近但不完美的匹配——这就是 Python 的工作方式。
梅尔·普尔在 Unsplash 上的照片
介绍
在本教程中,您将学习如何近似匹配字符串,并通过查看各种示例来确定它们的相似程度。
我将在最后提供一个函数,您可以使用它将一个列表与另一个列表进行匹配,尽管它们可能没有完全相同的匹配。
您是否曾经想要比较引用同一事物的字符串,但它们的书写略有不同,有拼写错误或拼写错误?这是一个经常出现的问题。在大多数没有 ID 的数据集中,您将根据名称进行匹配,如果这些名称拼错了,那就有问题了。
以下是修复方法。
莱文斯坦距离
简而言之, Levenshtein 距离 (LD)是一种度量两个序列或字符串之间差异的标准。通俗地说,LD 是将一个序列转换成另一个序列所必须进行的单独编辑的次数。编辑可以是插入、删除或替换。它以苏联数学家 Vladimir Levenshtein 的名字命名,
例如,以下是一些示例的 LD:
- 卡爪>卡爪 ( 1 —插入‘s’)
- 卡爪>卡爪(2-插入‘s’并用‘D’替换‘D’)
- 狗>猫( 3 -三个字母都被替换)
下面是来自维基百科的正式方程式,相对简单。如您所见,“D”与“D”不同,标点符号也被视为需要删除的字符。
【https://en.wikipedia.org/wiki/Levenshtein_distance
直觉上(我的意思是,在维基百科上阅读它们之后,它们在逻辑上对我来说是有意义的)LD 有一些上限和下限。以下是一些进一步解释的定义。
- 它至少是两个字符串大小的差异(至少是必须进行的加法的数量)。
- 它至多是较长字符串的长度(即使其中一个字符串的长度为零,也只需要增加与最大字符串的长度相等的长度)。
- 当且仅当字符串相等时为零(这需要解释吗?)
- 如果字符串具有相同的大小,则汉明距离是 Levenshtein 距离的上限。汉明距离是两个字符串中对应符号不同的位置的数量。(这相当于最少换人次数)
- 两个字符串之间的 Levenshtein 距离不大于它们与第三个字符串之间的 Levenshtein 距离之和(三角形不等式)。(即使你让第一串和第三串尽可能的靠近,这也是成立的——试着用['猫','狗',' At']举例)
由此,我们还可以计算 Levenshtein 比率(LR ),根据 LD 创建相似性比率。
由作者创建
其中 a 和 b 的绝对值分别是 a 和 b 的长度。
因此,这就是你需要知道的关于 Levenshtein 测度的全部内容。下面是一些 python 的例子。
创作作者:https://gist . github . com/ja sher 4994/d 06 ea 3 e 88461978 ede 2 AAA 0 fcfa 633 e 0
这是输出结果:
VS 作者的代码输出。
正如你所看到的,这工作得很好,但有同音异义词的问题,这取决于你使用它的上下文。如果你只需要一段将来可以使用的代码片段,你可以在这里找到:
创作作者:https://gist . github . com/ja sher 4994/f 43 CBA 274232 E1 cf 6 db 36943089 e 93 c 4
模糊不清的包裹
长话短说,FuzzyWuzzy (FW)包做了 Levenshtein 包所做的一切,但更多。Levenshtein 包是一个很好的介绍,但是您最好在几乎所有情况下都使用 FW 包。
简单 LD 的 FW 输出完全相同,调用方式也完全相同,输出也完全相同。
创作作者:https://gist . github . com/jasher 4994/27be 17 f 32 f 6b 79 CD 19d 634 EB 30 e 7706 e
但是这个令人眼花缭乱的软件包真正发挥作用的地方是它还能做什么。
子串匹配
该包可以匹配子字符串:
Str1 = "FC Barcelona" Str2 = "Barcelona"
Partial_Ratio = fuzz.partial_ratio(Str1.lower(),Str2.lower())
令牌排序
它还可以匹配逆序的字符串:
Str1 = "FC Barcelona" Str2 = "Barcelona FC"
Token_Sort_Ratio = fuzz.token_sort_ratio(Str1,Str2)Token set ratio
令牌集
以及相似但长度相差很大的字符串:
Str1 = "FC Barcelona" Str2 = "The victory went to FC Barcelona "
Token_Set_Ratio = fuzz.token_set_ratio(Str1,Str2)
模糊过程
对我来说,这是最有用的部分。你可以为所有可能的匹配计算一个分数,然后选择最大的——或者让 FW 为你做。所以我创建了一个简单的函数来做这件事,同时检查它是否成功。
所以在这里,函数做它在 tin 上所说的。它接受两个列表作为输入,第一个是您试图从第二个匹配的列表。例如,第一个列表可能是某人在输入框中输入的公司列表,第二个列表可能是实际的公司名称。
该函数使用 FW 中的 extractOne 方法创建两个新列表,“匹配”和“概率”。您还必须在这里导入“流程”。我在这里设置了它,以便它只在比率高于 90%时添加一个匹配。你可以把它改成任何适合你的,我推荐 90%以上,否则,你会开始得到一些严重的可疑匹配。如果没有找到匹配,它会插入‘no match’——我这样做是为了保持列表的长度不变。然后,我将它转换成熊猫数据帧,以利用输出。
所以使用下面的两个列表:
list1 = ['cat', 'dog', 'Cat', 'Dog', 'turtle', 'bed']list2 = ['cats', 'hotdog', 'Cat', 'Dog', 'turtles', 'bath']
运行函数并随机取样:
df = match_lists(list1, list2)df.sample(5)
(我意识到我是从 6 个观察值中抽取 5 个样本,但这只是为了向你展示我将如何在大范围内这样做。)
在熊猫数据框架中,您会得到以下结果:
由作者创建
然后你可以用一个简单的视力测试来看看这个功能做得有多好——这里看起来相当不错。
感谢阅读,我希望这能帮助你。
If I’ve inspired you to join medium I would be really grateful if you did it through this [link](https://jamesasher4994.medium.com/membership) — it will help to support me to write better content in the future.If you want to learn more about data science, become a certified data scientist, or land a job in data science, then checkout [365 data science](https://365datascience.pxf.io/c/3458822/791349/11148) through my [affiliate link.](https://365datascience.pxf.io/c/3458822/791349/11148)
这是我写的其他一些东西:
模糊字符串搜索:修剪搜索空间
原文:https://towardsdatascience.com/fuzzy-string-search-pruning-the-search-space-4012811f592a
拼音键,区分位置的哈希
这是在给定的字符串字典中寻找一个字符串的近似匹配的问题。
让我们看一个例子。我们想在一个干净的人名字典中搜索 Jonahtan 。我们真正的意思是,在考虑到拼写错误或其他变化后,我们希望找到近似的匹配。我们所说的“其他变体”是什么意思?在我们的示例中,我们真正想要找到的是字典中的名字,这些名字是隐含在输入字符串中的名字的似乎合理的替代表达式。这不仅包括拼写错误,还包括昵称。也可以包括先生、小姐、…
搜索可能会在字典中产生与 Jonahtan 匹配的 Jonathan 。我们知道后者是一个拼写错误,并在这个过程中找到它的正确拼写。
一般方法
解决这个问题的常见方法是调用模糊匹配器作为黑盒方法。模糊匹配器输入两个字符串,并计算它们成为同一实体的表达式的可能性。模糊匹配器在[1]中有详细介绍。
一种简单的方法是获取输入字符串,将其与字典中的每个字符串进行模糊匹配,将得分最高的匹配排列在列表的顶部,然后以某种方式决定哪些排名最高的匹配(如果有)是输入字符串的足够好的匹配。
当字典很大时,这种方法会很慢。即使它不是很慢,我们也可能有严格的运行时间要求,比如实时查找匹配。
修剪搜索空间
在这篇文章中,我们关注的是修剪搜索空间以减少模糊匹配的问题。当然,修剪取决于输入字符串。
我们讨论一些剪枝方法,特别是那些基于语音键和基于位置敏感散列的方法。我们用一个运行中的例子来说明它们:在名字的(大)字典中查找字符串的模糊匹配。
有哪些取舍?
目的是在不丢失任何真实匹配的情况下,尽可能地减少空间。因为这个理想可能无法保证,在实践中,所寻求的是性能(在运行时间的意义上使用)和召回率(修剪后找到的真实匹配的百分比)的适当折衷。虽然精度不会显式地出现在权衡中,但它可以间接出现。这是因为较低的精度潜在地转化为更多的结果需要处理,即修剪不充分。有鉴于此,用一个合适的量化修剪的指标来代替“性能”是有吸引力的。最终影响性能的是结果列表的大小。
音标键
从字符串派生的音标试图保留字符串的发音。想想约瑟夫和约瑟夫。它们听起来一样。所以它们应该有相同的音标。
当我们寻找发音相似的字符串时,拼音键有助于缩减搜索空间。我们不是寻找发音相似的字符串,而是查找所有其语音关键字与查询字符串相同的字符串。
生成语音键的两种流行方法是 Soundex 和 Metaphone。变音被广泛认为更好。所以我们这里就重点说一下。
下面是几对具有相同变音键的名字。这摘自[1]。
Steven, Stephen → STFN, John, Jon → JN, Cathy, Kathy → K0
这表明,在我们的用例中,通过变音键进行修剪将会工作得很好。寻找发音相似的名字。
当然,会有假阳性。例如, Jack 和 Jake 有相同的变音键, JK 。这在修剪过程中不必担心,因为过滤掉误报是模糊匹配器的责任。如果假阳性率如此之高以至于修剪无效,这将是一个问题。幸运的是,变音键不是这样的。
很好,使用合适的语音键可以有效地在字典中找到听起来像输入字符串的名字。但通常这不是我们想要找到的全部。例如,我们还会发现名字中有拼写错误。考虑RichardvsRiHCard。显然,这些应该是匹配的,因为这种差异可以用一种常见的错误来解释:相邻字母的错位。然而,他们不健全的名字。事实上,我们看到它们的变音键是不同的,分别是 RXRT 和 RKRT。这两个关键字的不同之处在于两个名称中粗体区域的发音方式不同。
因此,即使在我们的例子中,使用语音键进行修剪也是不够的。
哈希键
从讨论到这一点,我们可以抽象出剪枝问题如下。在给定的论域(在我们的例子中,是人名)上定义一个散列函数,它具有以下属性:
- 应该充分修剪搜索空间。虽然这是一个非正式的描述,但很容易根据经验对其进行评估或将其形式化。
- 它应该有接近 100%的召回率。也就是说,被视为匹配的两个名字字符串几乎肯定应该具有相同的哈希值。
语音键是特殊类型的散列函数。更一般地扩展到散列函数,打开了大门。
好吧,这一切都很好,但我们仍然必须弄清楚,作为一个特殊的情况,如何在名字字符串中出现换位错误的情况下进行修剪。
我们接下来讨论的技术在这方面提供了诱人的可能性。
区分位置哈希
考虑我们的特殊要求。
Two first-name strings deemed a match should almost certainly have the same hash value.
现在考虑以下内容,位置敏感散列的定义性断言
Map items to hashes so that similar items have the same hash value with high probability.
有匹配的吗?(双关。)是的。如下所述。
Two first-name strings deemed a match ⇐⇒ similar items
almost certainly ⇐⇒ high probability
太好了。我们仍然没有解决我们问题的具体方案。再坚持一会儿。
位采样
考虑长度为 n 的二元向量。选择特定尺寸 i 。对于一个 n 位向量 x ,定义 h I(x)=x I,这个 hash 函数只是简单的选出 x 的 i 位。
这一族哈希函数 h _1、…、 h _ n 有一个有趣的(也是吸引人的)性质,对于任意两个二元向量 x 和 y ,如果我们选择一个随机哈希函数 h _i,越相似的 x 和 y 越有可能是 h 【T43
好吧,那不透明。我们来分解一下。首先,我们在这里如何定义相似性?我们将它定义为两个向量具有相同值的 n 位的分数。因此 110 1 和 110 0 具有相似性,因为两个向量中的 4 位中的 3 位具有相同的值。(这些位用粗体标识。)
接下来,我们用极端的例子来建立直觉。考虑 x = y =1001。显然对于 1 到 4 中的每一个 i 来说 h _ i ( x )等于h_I(y)。这只是说,在两个向量相同的极端情况下,该属性将保持“极端”,这意味着无论选择哪个哈希函数,它在两个向量中的值都必然相同。
类似地,我们可以看到,当 x 和 y 相距最大时,例如 1001 对 0110,每个散列函数必然具有不同的值。
现在考虑“中间某处”的情况。为了便于说明,以这样的方式排列尺寸,即 x 和 y 的第一个 d 位是相同的,其余的是不同的。下面是一个例子
**1011111***01001* **1011111***10110*
这里 n = 12, d = 7,相同的位用粗体表示,不同的用斜体表示。
现在让我们从 1 到 12 中随机选择尺寸 i 。 x _ i 和 y _ i 具有相同值的概率为 d / n ,这正好是 x 和 y 的相似度值(我们已经定义过了)。
使用比特采样来修剪搜索空间
有了目前为止我们对比特采样的了解,让我们用它来修剪搜索空间。
首先,我们想要更精确地定义我们的修剪问题。设 D 表示一组长度为 n 的二元向量, x 表示一个同类的单一向量。我们希望使用上一节定义的相似性函数,在 D 中高效地找到与 x 最相似的向量。
上一节的内容建议使用以下方式使用位采样来定义散列键。这里 y 是一个长度为 n 的二元向量。
key(**y**)
Sample i uniformly at random from {1, 2, …, n}
return (i,y_i)
注意key(.)
是一个随机程序。在每个调用key(.)
中,随机选取其位要被采样的维度。
使用这种键控机制,我们将 D 中的每个向量 d 散列到它的键key(**d**)
。
现在我们准备在任何新的输入向量 x 的 D 中寻找邻居。
neighbors(**x**)
key_of_x = key(x)
Return all vectors in D whose key is key_of_x
这种方法效果如何?让我们来做一个例子。说 n = 2 而 D 包含 11。考虑调用neighbors(11)
。它会找到 11 吗?不一定。为什么不呢?因为 D 中的 11 是索引在键( i ,1)下的。随后,在调用neighbors(11)
中,在键( j ,1)下查找。鉴于 i 和 j 都在{1,2}中,由于密钥(.)功能。
哦,那可不好。我们能做什么?
查找期间的多个键
在查找过程中,我们可以生成多个键,并聚合所有与它们相关的向量。这潜在地增加了期望的向量在返回的结果集中的可能性。我们付出的一个代价是在查找过程中花费更多的时间。我们付出的一个潜在的更大代价是,结果的数量可能会大幅增加。在这种情况下,我们可能需要花更多的时间对它们进行后处理。这两点以后再详细讲。
下面是一个明智的方法。定义一个函数 multi_key ( y , m ),该函数调用key(y)m次,收集查找每个 key 得到的向量,并将其作为一个集合返回。
使用键 ( y )索引矢量。使用 multi_key ( y , m )查找其邻居。
我们能期望这种方法有多有效?mm应该是什么?让我们从回忆的角度来看这个问题,我们将它定义为一个索引向量在后续查找自身的结果集中的概率。凭直觉,回忆应该随着 m 的增加而增加。(查找时间也是如此。)
对于任何固定的 n ,我们可以很容易地将召回量化为 m 的函数。让 i 表示索引向量 y 时使用的维度。设 j 表示在任意一个后续调用键 ( y )期间采样的尺寸。 i 和 j 不同的概率是( n -1)/ n ,因为 j 有 n 个可能的值,除了其中一个之外,其余的都与 i 不同。尝试查找 y 时,调用键 ( y )的 m 中的每个 j 不同的概率为(n-1)/n)^m)。至少有一个 j 等于 i 的概率,即回忆,是这样的
1 — ((*n*-1)/*n*)^*m* (R1)
对于 n = 20,让我们计算各种 m 使用(R1)的召回。
m 1 2 10 20 50 100
recall 0.05 0.0975 0.4 0.64 0.92 0.994
这表明,为了实现接近 100%的召回率,m 需要比 n 大得多。这是可信的。带有替换的随机采样本质上有点低效,因为同一个密钥可能被采样多次。好吧,与其用 multi_key ( y , m ),不如干脆把 20 个键都一一列举出来。
查找 n 个键
好了,现在我们知道了查找 n 个键就足以获得 100%的回忆。
这样够好了吗?没那么快。我们还需要考虑结果集的大小,因为当查找这么多键时,结果集可能会很大。
让我们来分析下面的场景。假设 D 包含 1^ n ,我们随后对其进行查找。考虑一个向量 y 在 D 中,其中有 d 个。现在考虑 neighbors(1^ n 的结果集。在枚举 1^ n 的邻居时,我们将查找,对于 1 中的每个 i 到 n ,索引在值 1 下的 D 中的向量。然后把它们都结合起来。
如果 y 被索引到 i 上,使得 y _ i 为 1,则它将位于 neighbors(1^ n 。这种情况发生的概率是 d / n 。这是因为在 n 中有 d 个值为 1 的位,索引从 n 个位中随机选择一个位进行索引。
说 D 包含 m _ d 这样的向量。我们可以期待他们中的m_dd/n进入邻居* (1^ n )的结果集。
现在假设 d 很小,比如说 4,而 n 大得多,比如说 20。此外,假设 D 包含许多包含 d 个向量的向量,即 m _ d 很大。这似乎是合理的,因为宇宙中有“20 选 4”这样的向量。由于 d 很小,相对于 1^ n 来说,可以合理地预期大部分(如果不是全部)都是假阳性。因为 mm_d很大,所以很多这样的误报会出现在结果集中。
我们上面的分析适用于 1^ n 。可以概括一下吗?让我们用任何一个 n 位向量 x 来代替 1^ n 。设 D 中的 y 与 x 有共同的 d 位,即相似度 S ( x , y )等于 d / n 。和以前一样, y 到达 neigbors ( x )结果集的概率是 d / n 。所以如果有 m _ d 这样的向量 y ,我们可以像之前一样,期待m_dd/n*能够到达结果集。由于 d 小,这些相对于 x 很可能是误报。如果 m _ d 很大,那么结果集中就有很多。
总而言之,当 D 包含许多与 x 有轻微相似性的向量时,在任意向量 x 上查找所有 n 维确实可以返回许多结果。这是一个问题,当这些结果大部分是假阳性时,这似乎是合理的。
那么我们还能尝试什么呢?
无替换样品
像以前一样,让我们对一个维度进行 m 次采样。与之前不同,让我们在不更换的情况下对进行采样。这相当于从 n 个维度中随机选择一个 m 维度的子集。
这能给我们带来什么?更加灵活。将 m 设置为 n 给出了“在所有尺寸上查找”的情况。选择小于 n 的 m 让我们在返回结果集的大小上权衡召回。
回忆作为m的函数
考虑一个 D 中的 y ,我们在它被索引后的某个时间查找它。查找到的结果集包含 y 的概率是多少?这是 m / n ,在查找过程中随机选择的 m 维度包括 y 被索引的维度的概率。
这告诉我们什么?召回率随着 m 线性下降。当然,你只会想要降低 m ,也就是说,付出减少召回的代价,除非你有所收获,特别是搜索结果的数量也大幅下降。接下来我们来分析一下这个。
结果集的预期大小
对于这个场景,我们将重复上一节所做的分析。为了保持独立,我们将从头开始拼写所有内容。与先前分析不同的部分用粗体表示。
假设 D 包含 1^ n ,我们随后对其进行查找。考虑一个向量 y 在 D 中,其中有 d 个 1。现在考虑 neighbors(1^ n 的结果集。在枚举 1^ n 的邻居时,我们将查找从 n 位中随机采样(替换)的 m 位中的每一位的,在采样位的值 1 下索引 D 中的向量。然后把它们都结合起来。
如果 y 被索引到位 i 上,使得 y _ i 是 1 并且 i 是在 neighbors(1^ n ) 中查找期间采样的 m 位之一, y 将在邻居(t67)中 y _ i 为 1 的概率为 d / n 。 i 是在 neighbors(1^ n 中查找期间采样的 m 位之一的概率是 m / n 。所以组合概率是(d/n)(m/n)*。
综上所述,通过在计算邻居 (1^ n )所涉及的查找过程中对 m 位进行替换采样,召回 1^ n 的概率为 m / n ,结果集的预期大小为m_d**(d/n)(
多键下的索引
到目前为止,我们只在查找过程中使用多个键。如果我们在索引期间也使用这种机制会怎么样?更具体地,对于在 1 和 n 之间的整数值参数 m I,当索引一个 x 时,我们采样 m I 比特,替换从 1 到 n 并且在每个比特下索引 x 。
使用与之前相同的查找机制,召回概率将随着 m I 的增加而增加。结果集的预期大小也是如此。
收紧查找
为了试图抵消随着 m I 的增加而增加的结果集的预期大小,我们可以引入一个参数,称之为 k L,以使查找条件更加严格。
至此,如果任何特定于位的查找找到一个向量,它就包含在结果集中。有了参数 k L,只有在特定于位的查找中至少有 k L 找到它时,我们才能将它包含在结果集中。
好吧,这确实使我们的聚合逻辑变得复杂了,现在需要包括一些过滤。我们不能只是累加我们找到的所有向量,一路去重复它们。我们还必须确保找到的向量至少在特定于位的结果集中。
撇开聚合和过滤逻辑的复杂性不谈,直觉上,这种机制似乎有可能让我们对权衡进行更多的控制。
在这篇文章中,我们就说到这里。现在用 n 、 d 、 m 、 m I、 k L 来参数化的这种情况,量化分析起来令人望而生畏。
从比特采样到多项式采样
位采样机制扩展到定义为D1 XD2 X…XDn的论域。这里,每个“尺寸”i 都有一组有限的相关值。不同的维度可以有不同的值集。统计学家和数据科学家可能会将此视为定义在 n 个分类变量集合上的空间,每个变量都有自己的一组值。
我们把这一段放在这篇文章中是因为它的启发价值。它为多项式空间上基于 LSH 的索引和查找提供了第二种机制,一种直接的机制。也可以使用比特采样机制;它首先需要将这个宇宙转变成一个二元宇宙。虽然有一种自然的方法可以使用所谓的一键编码将多项式空间转换为二进制空间,但尚不清楚基于两种不同编码(一种直接编码,一种二进制编码)的 LSH 的精度和召回率如何比较。
延伸阅读
FuzzyTM:一个用于模糊主题模型的 Python 包
原文:https://towardsdatascience.com/fuzzytm-a-python-package-for-fuzzy-topic-models-fd3c3f0ae060
莎伦·麦卡琴在 Unsplash 上的照片
在我以前的文章中,我展示了如何开始使用 Python 包 OCTIS,这是一个比较和优化最先进的主题建模算法的包。第一篇展示如何入门 OCTIS,第二篇重点介绍模型优化。我和我的团队开始与 OCTIS 合作的原因是,我们开发了一种新的主题建模算法,称为 FLSA-W [1],并希望看到它与现有的最先进的技术相比表现如何。基于对各种开放数据集的比较,我们发现它在大多数情况下在一致性(c_v)、多样性和可解释性方面优于其他模型(如 LDA、ProdLDA、NeuralLDA、NMF 和 LSI)。我还不能分享这些结果,因为我们已经将这项工作提交给一个会议,正在等待接受。与此同时,我们还开发了一个 Python 包, FuzzyTM ,其中包含 FLSA-W 和另外两个基于模糊逻辑的主题建模算法(FLSA 和 FLSA-V)。这篇文章将简要描述模糊主题模型和 FLSA-W 的基本原理,然后演示如何开始使用 FuzzyTM(如果你想开始训练一个模型,只需转到“开始使用 FuzzyTM”)。在以后的帖子中,我将更详细地解释各种算法是如何工作的,并使用 OCTIS 将它们与现有算法进行比较。
模糊主题模型
注意,虽然有 50 种深浅不同的主题建模算法,但它们都分别返回两个矩阵 P(W_i|T_k) 和 P(T_k|D_j) 、给定主题的单词概率和给定文档的主题概率。
2018 年,模糊潜在语义分析被提出[2],并在一致性方面优于 LDA。FLSA 利用贝叶斯定理、奇异值分解(SVD)和矩阵乘法求 P(W_i|T_k) 和 P(T_k|D_j) 。
对于主题建模,我们从文本语料库开始。在 Python 中,这被存储为字符串列表的列表,其中每个列表代表一个文档,每个字符串是文档中的一个单词。
让我们从定义下列量开始:
M —数据集中唯一单词的数量
N —数据集中文档的数量
C —主题的数量
S —奇异值分解维数
i —单词索引, i ∈ {1,2,3,…,M}
j — 文档索引, j ∈ {1,2,3,…,N}
k —主题索引, k ∈ {1,2,3,…,C}
那么,在 FLSA 获得 P(W_i|T_k) 和 P(T_k|D_j) 的步骤如下:
- 获取本地术语权重 ( MxN ) —指示单词 i 在文档 j. 中出现的频率的文档术语矩阵
- 获取全局术语权重 ( MxN ) —在该步骤中,一个文档中的单词的出现与其他文档中的单词的出现相关。
- 在全局术语权重 ( NxS )上从 SVD 获得U—SVD 用于降维,见本帖对 SVD 的直观解释。
- 对 U^T 使用模糊聚类得到
p(t|d)^t(mxc)—最常用的方法是模糊 c 均值聚类,但 FuzzyTM 中有各种算法。 - 使用基于贝叶斯定理的矩阵乘法,使用 P(T|D)^T 和 P(D_j)得到 P(W_i|T_k) 。
在 FLSA,SVD 的 U 矩阵被用作聚类的输入,这意味着文档正在被聚类。由于主题模型经常被用于寻找对应于主题的单词,所以采用奇异值分解的 V^T 进行聚类似乎更有意义,因为现在单词正在被聚类。在 FLSA-W(现在“W”有意义了,希望)中,单词而不是文档被聚集在一起。
FuzzyTM 入门
FuzzyTM 是模块化构建的,因此每个算法的步骤都是父类中不同的方法,每个算法都是调用父类中方法的子类。新手可以用最少的工作训练各种主题模型,而研究人员可以修改每个步骤并添加更多的功能。
让我们开始使用 OCTIS 包中的数据集训练模型。首先,我们使用以下代码安装 FuzzyTM:
pip install FuzzyTM
其次,我们导入数据集:
from octis.dataset.dataset import Dataset
dataset = Dataset()
dataset.fetch_dataset('DBLP')
data = dataset._Dataset__corpus
让我们看看这个数据集是什么样子的:
print(data[0:5])
>>> [['fast', 'cut', 'protocol', 'agent', 'coordination'],
['retrieval', 'base', 'class', 'svm'],
['semantic', 'annotation', 'personal', 'video', 'content', 'image'],
['semantic', 'repository', 'modeling', 'image', 'database'],
['global', 'local', 'scheme', 'imbalanced', 'point', 'matching']]
现在,我们准备导入 FuzzyTM:
from FuzzyTM import FLSA_W
我们将模型初始化如下(` num_words '的缺省值是 20,但为了清楚起见,这里我只给出了 10 个单词):
flsaW = FLSA_W(input_file = data, num_topics=10, num_words=10)
然后,我们得到 P(W_i|T_k) 和 P(T_k|D_j) 如下:
pwgt, ptgd = flsaW.get_matrices()
现在,我们可以开始看主题了:
topics = flsaW.show_topics(representation='words')print(topics)
>>> [['machine', 'decision', 'set', 'evaluation', 'tree', 'performance', 'constraint', 'stream', 'process', 'pattern'], ['face', 'robust', 'tracking', 'error', 'code', 'filter', 'shape', 'detection', 'recognition', 'color'], ['generalization', 'neighbor', 'predict', 'sensitive', 'computation', 'topic', 'link', 'recursive', 'virtual', 'construction'], ['language', 'logic', 'data', 'web', 'mining', 'rule', 'processing', 'discovery', 'query', 'datum'], ['factorization', 'regularization', 'people', 'measurement', 'parametric', 'progressive', 'dimensionality', 'histogram', 'selective', 'correct'], ['active', 'spatial', 'optimal', 'view', 'level', 'modeling', 'combine', 'hierarchical', 'dimensional', 'space'], ['correspondence', 'calibration', 'compress', 'curve', 'geometry', 'track', 'background', 'appearance', 'deformable', 'light'], ['heuristic', 'computational', 'update','preference', 'qualitative', 'mechanism', 'engine', 'functional', 'join', 'relation'], ['graphic', 'configuration', 'hypothesis', 'walk', 'relaxation', 'family', 'composite', 'factor', 'string', 'pass'], ['theorem', 'independence', 'discourse', 'electronic', 'auction', 'composition', 'diagram', 'version', 'hard', 'create']]
从这个输出中我们可以识别出一些主题:第一个主题似乎是关于通用机器学习的,第二个主题是关于图像识别的,第四个主题是关于自然语言处理的。
现在,让我们来看看分数评估指标:
#Get coherence value
flsaW.get_coherence_value(input_file = data, topics = topics)
>>> 0.34180921613509696#Get diversity score
flsaW.get_diversity_score(topics = topics)
>>> 1.0#Get interpretability score
flsaW.get_interpretability_score(input_file = data, topics = topics)
>>> 0.34180921613509696
由于主题模型的输出由各种主题组成,其中每个主题都是单词的集合,因此主题模型的质量应该关注每个主题内的单词质量(主题内质量)和不同主题之间的差异(主题间质量)。连贯性分数捕捉每个主题内的单词相互支持的程度,多样性分数显示每个主题的多样性(主题之间是否有单词重叠)。然后,可解释性得分结合了这两个指标,并被计算为一致性和多样性之间的乘积。
从结果可以看出,FLSA-W 具有完美的多样性。这并不奇怪,因为它明确地将单词聚集在一起。不过,与大多数现有算法相比,这是一个很大的改进。
虽然 0.3418 的一致性分数看起来相当低,但是比较实验结果将显示,在大多数设置中,FLSA-W 具有比其他算法更高的一致性分数。
结论
在这篇文章中,我简要解释了主题模型 FLSA 和 FLSA-W 是如何工作的。然后,我演示了只用两行代码就可以训练 FLSA-W,以及如何分析主题。除了训练主题模型之外,FuzzyTM 还包含一种方法,用于基于训练的主题模型获得新文档的主题嵌入。这对于下游任务(如文本分类)的文档嵌入非常有用。在以后的文章中,我将更详细地描述这些算法,并将 FLSA-W 与现有算法进行比较。更多详情请看我的 Github 页面:https://github.com/ERijck/FuzzyTM。
[1] Rijcken,e .,Scheepers,f .,Mosteiro,p .,Zervanou,k .,Spruit,m .,& Kaymak,U. (2021 年 12 月)。模糊话题模型和 LDA 在可解释性方面的比较研究。在 2021 IEEE 计算智能系列研讨会(SSCI) 。IEEE。
[2]a .卡拉米,a .甘戈帕迪亚,a .周,b .,&哈拉齐,H. (2018)。健康和医学语料库中的模糊方法主题发现。国际模糊系统杂志, 20 (4),1334–1345。
G.使用多线程在 SQL 数据库中存储图像的方法
在几分钟内处理数千张图像!
罗宾·盖洛特-德勒冯在 Unsplash 上的照片
你在 spark 中有没有遇到过不能使用 Spark 的执行器并行处理能力的用例?在本文中,我们将探索使用 Python 中的多线程在 SQL 数据库中存储图像的一种很酷的方法。这种用例在 Spark 中无法实现,因为除非将数据框转换为 pandas 格式,否则无法可视化数据。一旦转换成熊猫,就失去了 spark 的所有优势。因此,为了达到相似的执行速度(可能没有 spark 快),我们可以利用 Python 中的多线程技术。
阅读本文后,您将了解到:
-如何在 Python 中多线程?
-如何在 SQL 数据库中高效存储图片?
使用案例
我们将查看一个每小时的能耗数据集,我们需要保存 3 个月内每天的能耗图。让我们看看我从 Kaggle 下载的数据集:
作者图片
作者图片
准备
1.安装库
作者图片
你们一定都知道上面提到的大多数库。我来看看不常用的:
- Concurrent.futures —用于在 python 中启动并行线程
- 重复 —用于提供一串常量值
- Pyodbc —用于为 Python 安装 odbc 驱动程序以连接 SQL server。下图显示了如何连接到需要所有凭据的 SQL server:
2.在您的目录中创建一个文件夹来存储图像
为了在 SQL 数据库中存储图像,首先需要以. png 格式保存每个打印图像。
3.生成和存储图像
现在您已经安装了库并连接到 SQL server,您可以开始存储图像的过程。有两种不同的方法可以实现这一点:
- 我们的至爱,“For loop”:
作者图片
- 上面的代码使用了一个 for 循环来遍历所有不同的日子,并为每一天创建一个绘图,然后将它保存在我们之前创建的文件夹中
- 之后,我们打开每个创建的图像,并将其存储在 SQL 数据库中已经创建的表中
- 如果你有成千上万的图片要处理,这个过程将会花费很多时间。因此,这不是一种可扩展的前进方式
2.使用多线程:
作者图片
以上代码使用 concurrent.futures 库实现多线程。在 map 函数中,您传递' plot_consumption '函数,该函数将为 *list_dates
* 中的每个日期生成图像,而也作为参数之一传递。此外,您可以看到我是如何在 repeat 函数中传递数据帧的,这有助于为每天处理的所有并发线程提供恒定的数据帧流。
这是一个总结!我希望你今天学到了一些新东西,并能在你的项目和/或工作场所中实施。
感谢您的阅读!
- 如果你喜欢我的文字,那么请订阅我的 列表
- 如果你喜欢, 在 Medium 上关注我
- 在LinkedIn上保持联系
参考
- PJM —搜索结果 —数据来源
- 每小时能耗| Kaggle
- 许可使用数据集
卡拉狄加:危险的人工智能长什么样
原文:https://towardsdatascience.com/galactica-what-dangerous-ai-looks-like-f31366438ca6
意见
人工智能炒作的警示故事
卡拉狄加百科全书。鸣谢:作者 via midway
Meta 本周宣布了一个新的科学大型语言模型(LLM)。他们将它命名为卡拉狄加——向艾萨克·阿西莫夫的百科全书卡拉狄加致敬。
该网站称这是一个“可以存储、组合和推理科学知识的模型。”
该论文的合著者埃尔维斯·萨拉维亚说,这“是我们通过将信息转化为有用知识来组织科学的愿景的第一步。”
到目前为止一切顺利。这听起来像是一个超级有用的工具,旨在实现一个具有挑战性但又必要的目标。
但是外表是会骗人的。
人工智能专家和科学家们尝试了一下,许多人并不感兴趣——当没有完全“害怕”时。因此,他们毫不犹豫地接受了卡拉狄加的承诺,以及人工智能研究人员夸大他们构建的系统能力的普遍倾向。
我没打算写卡拉狄加,但这是一个完美的例子来展示为什么我们应该永远不要在学习人工智能或与之互动时提升我们的批判性思维。
让我们看看卡拉狄加能做什么,人们在说什么(包括“支持”和“反对卡拉狄加”),以及我对该模型和围绕它的说法的看法。
本文选自The Algorithmic Bridge,这是一份旨在弥合算法与人之间鸿沟的教育通讯。它将帮助你理解人工智能对你生活的影响,并开发工具来更好地导航未来。
https://thealgorithmicbridge.substack.com/subscribe
作者和支持者眼中的卡拉狄加
建造卡拉狄加是实现旧承诺的第一步:计算机可以解决“科学中的信息过载”
随着科学的进步,人类集体了解并储存了越来越多的关于世界的知识。然而,作为个人,我们能够吸收的总百分比会随着时间的推移而迅速下降。
人工智能可以解决这个问题。梅塔设想卡拉狄加“组织科学”,试图完成搜索引擎未能完成的任务。如果模型如预期的那样工作,那么这样做是微不足道的——卡拉狄加将是“一件大事”
然而,尽管 Meta 如此足智多谋,LLM 如此强大,解决这个问题仍然是一个过于雄心勃勃的任务。卡拉狄加在很多方面都有不足。
但在深入研究它的不足之前,让我们先假定 Meta 是无辜的,客观地分析一下 Galactica 能做什么(如果你想看的话,这是论文)。
Galactica 是一个语言模型家族(125M 到 120B 参数),在 6000 多万份高质量的科学文档(论文、教科书、百科全书……)上进行训练。
代码为的论文,其中开源了这里的模型,解释说卡拉狄加可以“总结学术文献,解决数学问题,生成维基文章,编写科学代码,注释分子和蛋白质,等等。”
尽管与大多数其他 LLM 相比,Galactica 的训练数据较少,但它在科学基准(数学、MMLU)上的表现优于最好的公司(PaLM、Chinchilla)。令人印象深刻。
令人惊讶的是,尽管没有接受过通用文本数据的训练,但 Galactica 也超过了 BLOOM 和 OPT。
这并不奇怪,它比通常的毒性更小(高质量的内容防止模型从可疑的来源学习)。
鉴于 Galactica 的表现,Nvidia 研究员 Jim Fan 将其描述为“一个巨大的里程碑”是可以理解的
如果这些结果听起来很棒,那是因为它们确实很棒。毫无疑问,卡拉狄加是一项引人注目的技术成就(尽管更彻底的评估是合适的)。
但是模型并不是生活在纸上的。如果它打算成为(第一版)人类知识的门户,人们应该能够信任它。
然而,当卡拉狄加解体时,恰恰是在真实世界的条件下——与基准测试相反。
例如, Yann LeCun ,DL 先驱和 Meta 的首席人工智能科学家说:“键入一个文本,Galactica 将生成一篇论文,其中包含相关的参考资料、公式和一切。”
这完全是错误的(下面有很多例子)。卡拉狄加经常辜负作者和支持者的轻率声明。
它如此无处不在,如此灾难性,如此危险,以至于它的失败大大掩盖了它原本的技术突破。
要自己测试,可以去这里。(更正:你可以试试,直到 Meta 决定关闭演示,因为强烈的反弹。)
怀疑论者眼中的卡拉狄加
“怀疑论者”指的是那些用批判性思维测试卡拉狄加的人(我就是这样做的)(尽管不需要太多就能得出结论)。而且,要明确的是,我的意思并没有任何负面的内涵。
事实上,我认为自己属于这个群体。让我告诉你为什么。
四句话成摘要,作者这样写道:
“在这篇文章中,我们介绍了 Galactica:一个大型语言模型,它可以存储、组合和推理关于科学知识的知识。”
注意描述卡拉狄加能力的词语选择。
现在,将这种说法与科学家和大学教授在 Twitter 上分享的关于该模型的年表进行对比(链接中的例子):
【卡拉狄加是】造纸厂的大功臣,欺诈的抄袭者&到处欺骗学生。”
——西蒙·J·格林希尔 ,UoA 教授
语言模型应该建模语言,而不是“知识””
——大卫·查普曼 ,麻省理工学院艾博士
人工智能真的已经发展到这种地步了吗?它自动地将现实与废话混合得如此之细,以至于我们再也无法辨别其中的区别
——加里·马库斯 ,作者兼 NYU 退休教授(通往我们可以信任的 AI 之路)
脸书的卡拉狄加让我如此困扰的是…它假装是一个知识的入口…实际上它只是一个随机的扯淡生成器
——卡尔·t·博格斯特伦 ,UW 生物学教授
“感觉我作为科学家的工作仍然很安全。”
——梅拉·米切尔 ,艾圣菲学院教授
“脸书(抱歉:Meta)人工智能:看看我们的“人工智能”,它让你接触到人类所有的知识。还有脸书·艾:不过要小心,这只会让事情变得一团糟。”
——艾米莉·m·本德 ,UW 大学语言学教授
“也许不要用《卡拉狄加百科全书》来命名你的模型,除非它好到足以成为一个值得信赖的知识来源?”
——马克·雷德尔 ,佐治亚理工学院艾教授
“我问了#卡拉狄加一些我知道的事情,我很困扰。在所有情况下,这是错误的或有偏见的,但听起来是正确的和权威的。我认为这很危险。”
——迈克尔·布莱克 ,马克斯·普兰克智能系统研究所所长
相当一致的反应。
如果你不想看这些链接,这里有一个生动有趣的例子,说明为什么他们如此严厉地批评卡拉狄加,尽管它的基准测试结果很好:
有趣的是,卡拉狄加和在维基上发表了一篇关于“列侬-小野互补”的文章鸣谢:安德鲁·桑斯特罗姆(由加里·马库斯分享)
底线是:卡拉狄加擅长制造听起来科学的虚构事实,除此之外别无其他。这使得它成为一个危险的工具。
有人能把这个推理叫做吗?
卡拉狄加:人工智能炒作的警示故事
让我来理清这里发生了什么。
我们必须理解并区分作者和支持者声称卡拉狄加可以做(但不可以)的事情和它实际上可以做(但不应该做)的事情。
卡拉狄加据称的优势之一是推理能力。“推理”这个词——我在上面强调的——在论文中出现了 34 次。
然而,该模型不做任何类似的事情。鉴于无数例子显示该系统绝对缺乏推理能力,这种说法实际上是一种夸大其词(这就是 为什么 他们不得不关闭演示)。类似的说法还有“卡拉狄加将生成一篇论文”或者“卡拉狄加……可以生成维基文章”等等。
想法是一样的:研究人员通过利用语义差距夸大了人工智能的能力:因为“推理”没有严格的正式定义,他们可以扩展这个词以适应卡拉狄加做的任何事情。
正如加里·马库斯所说,特别是卡拉狄加和一般的语言模型,“从根本上来说没有能力”完成这类任务。艾米莉·m·本德直截了当地说:
“LLM 可以说拥有的唯一知识是关于词形分布的信息。”
不理解。不讲道理。
卡拉狄加所做的是生成(通常是编造的)听起来科学的文本。那不是推理,而是推理的表象。正如迈克尔·布莱克所说,“[卡拉狄加]是错误的或有偏见的,但听起来是正确的和权威的。”
这不仅仅是一个不可靠的问题,也就是说,你知道你不能相信一个系统,因为它可能会给你一个正确或不正确的答案,而你没有办法去判断是哪一个。
卡拉狄加的问题更深一层,因为听起来“正确”和“权威”会让任何对某个主题没有先验知识的人以虚幻的确定性相信新获得的(有缺陷的)知识是真实的。
这使得卡拉狄加不仅是错误的,而且是危险的错误。
值得称赞的是,该网站有一个“限制”部分,其中他们提到了该模型的缺点:
但是,这就够了吗?展示模型的局限性已经成为科技公司的一种常见做法,但这并不能弥补测试模型能力时的不仔细——或过度夸大。
如果你设置了一个演示,并声称卡拉狄加可以推理并生成论文或文章,但它不能,一个“限制”部分不足以防止潜在的损害。
在 Yann LeCun 和 Ernest Davis (两人都是 NYU 大学的教授)的交流中,前者解释说,Galactica 不打算用人们在 Twitter 上分享的那种“快速试验”来测试。
欧内斯特·戴维斯的回答不言自明:
“如果创建者不希望人们为维基提交标题,那么主页上的演示就不应该邀请他们为维基提交标题。”
这就是这个故事的寓意:卡拉狄加的问题不在于它不能真实、可靠或实事求是地写一篇论文。问题是背后的人选择了炒作(不管出于什么原因)。
打个比方,飞机是一项完美的技术,但如果航空工程师声称它可以带我们去月球,那么他们应该受到世界上所有的批评。卡拉狄加也是如此(还有很多其他 AI 系统,不仅仅是语言模型)。
通过使用这些可疑的实践,您会得到来自担忧和愤怒的科学家的强烈反对,一个强大的开源工具可以很容易地产生错误和虚假信息,以及困惑的外行人,他们由于不连贯的用户指南而无法正确使用该工具,并且不确定他们是否会从中受益。
这不是我们想要为人工智能创造的画面。
订阅 算法桥 。弥合算法和人之间的鸿沟。关于与你生活相关的人工智能的时事通讯。
您也可以直接支持我在 Medium 上的工作,并通过使用我的推荐链接 这里 成为会员来获得无限制的访问权限! 😃
赌徒的破产问题:一个古老的概率
原文:https://towardsdatascience.com/gamblers-ruin-problem-a-probability-antiquity-6d3f5fe20812
理解概率论中最著名的问题之一
扑克牌和扑克筹码和骰子,图像由 Pixabay
赌徒破产是概率论中最基本的概念之一。第一次提到它可以追溯到 17 世纪布莱士·帕斯卡和皮埃尔·费马之间的一封书信往来。该问题涉及在以下条件下确定游戏者获胜的概率:
- 他们一开始都有固定数额的钱。
- 在整个游戏过程中,他们中任何一方获胜的概率都是不变的。
- 游戏继续进行,直到他们中的任何一个用完了钱。
一个例子是考虑两个赌徒 Raj 和 Mir。据了解,他们分别从₹100 和₹60 开始。赌徒们赌连续掷硬币的结果。如果硬币正面朝上,拉吉赢了,米尔必须把₹1 给拉吉。否则,米尔赢了,拉杰必须把₹1 给米尔。游戏继续进行,直到其中一个玩家输掉所有的钱。还假设硬币是倾斜的,正面着地的概率是 1/3。我们现在想确定 Raj 赢得比赛的概率。在我们开始解决这个问题之前,有几件事情需要注意:
- 我们不知道游戏需要多少回合才能结束,有可能 Mir 每局都输,在这种情况下回合数将是 60。也有可能游戏永远不会终止,在这种情况下,他们每个人都交替获胜。这两种情况都是可能的(尽管概率很低),它们之间无限可能的其他情况也是如此。
- 在解决统计学中的问题时(通常在计算机科学中也是如此),确定问题的不变量通常是很重要的,也就是说,问题的某些方面总是不变的,不会改变或依赖于子问题的结果。对我们来说,不变量是赌客拥有的钱的总数。不管这个游戏是什么状态,拉杰和米尔的钱数是不变的:₹100 + ₹60 = ₹160.唯一改变的是两个赌徒的钱数。这对我们有什么用?嗯,这意味着我们只需要在任何给定的时间点跟踪其中一个赌徒的钱。另一个游戏者的钱可以通过简单地从两个游戏者的总钱数中减去该数额来获得(游戏的不变量)。例如,如果拉杰在游戏中的某个时刻有₹85,我们可以说米尔在那个时刻有₹160— ₹85 = ₹75。(请注意,这个问题有一些变体可能不成立,我们将在本文末尾讨论其中一个变体)。
- 这些问题经常使用马尔可夫链模型来建模。但是,在本文中,我们将使用条件概率方法和一些建模技术来简化这种方法。关键的想法是将游戏延伸到过去和未来,以考虑不同的可能性。稍后我们将看到如何做到这一点。
让我们从定义一些事件开始:
- 让 R₁₀₀表示拉吉赢得比赛的事件,因为他从₹100.开始请注意,不变性意味着我们不必在游戏开始时跟踪 Mir 的数量。
- 设 H 表示硬币在第一次投掷时正面朝上。
- 让 T 表示硬币在第一次翻转时反面着地的事件。
有了这些定义,我们可以定义 P(R₁₀₀),拉杰赢得游戏的概率,因为他从₹100 开始,条件是第一次翻转的结果:
现在,我们观察到,如果拉吉在第一场比赛中获胜,那么拉吉最终会和₹101.在一起这可以解释为 Raj 和₹101.开始比赛的情况同样,如果拉吉在第一场比赛中输了,那么拉吉就会和₹99.在一起这可以解释为 Raj 和₹99.开始比赛的情况因此,我们有:
因此,我们已经设法获得,看起来像一个循环关系的东西。我们已经定义了 Raj 获胜的概率,假设他从₹100 开始递归,根据他从₹101 开始获胜的概率和他从₹99.开始获胜的概率剩下的是解决递归关系。为了解决这个递归问题,我们需要在时间上向后和/或向前扩展这个问题,直到我们达到一个基本条件,例如 P(Rᵢ已知。我们如何做到这一点?有两个基本条件可以帮助我们解决递归问题:
1.如果有人告诉我们拉吉是从₹0 开始的,那我们对拉吉获胜的可能性有什么看法呢?对,应该是 0!为什么?因为如果 Raj 一开始就没钱,他就已经输了。
2.如果有人告诉我们拉吉是从₹160 开始的,那我们对拉吉获胜的可能性有什么看法呢?对,应该是 1!为什么?因为如果 Raj 一开始就有所有的钱,他已经赢了这场比赛。
因此,我们有以下信息:
在解决这个递归问题之前,我们以稍微不同的方式写下递归关系,以简化我们的解决方案:
现在我们已经获得了一个递归关系和基本条件,我们可以通过回溯游戏来求解递归公式。太棒了。这给了我们:
我们将 I 的值从 1 到 159,因为我们的递归关系将允许 i + 1 = 159 + 1 =160 的最大值(Raj 可以拥有的最大金额)和 I-1 = 1–1 = 0 的最小值(Raj 可以拥有的最小金额)。我们代入 0°和 160°的基本情况解,得到以下结果:
如果我们加上前 99 个方程(1 ≤ i ≤ 99),我们得到:
请注意,书写递归关系的形式允许我们取消所有项,只留下其中的两项。另外,请注意,我们已经使用了几何级数公式来计算 2 的幂和。现在,如果我们知道 P(R₁),我们可以计算 P(R₁₀₀),这将解决我们的问题!为了找到 P(R₁),我们将前面显示的所有 159 个等式相加,并使用第二个基本条件,即 p(r₁₆₀= 1:
将该结果代入等式(I ),我们得到:
我们中的一些人可能会对这个结果感到惊讶,因为 Raj 的起点要高得多。但是,一旦你意识到 Raj 在每场比赛中获胜的几率大约是 Mir 获胜几率的一半,你很快就会明白为什么概率值开始呈指数衰减,这使得 Raj 赢得比赛的几率几乎为零。
事实上,您可以推广上述公式,假设 Raj 从某个₹A 开始,Mir 从某个₹B.开始。如果正面的概率为α,则您的递归关系和基本情况将由下式给出:
如前所述求解上述递归公式将给出广义赌徒破产问题的如下解:
注意,上述等式仅在α ≠ 0.5 时成立。如果α = 0.5,问题就变得很简单,因为钱多的游戏者更有可能赢,因为他们在每场游戏中的获胜概率是相同的。在这种情况下,解决方案很简单:
解决了这个版本的赌徒倾家荡产问题,我们再来看另一个版本的问题:
考虑两个赌徒 Raj 和 Mir。据了解,他们分别从₹100 和₹60 开始。赌徒们赌连续掷硬币的结果。如果硬币正面朝上,拉吉赢了,₹1.归他否则,米尔赢了,得到了₹1.游戏继续进行,直到其中一个玩家得到₹150.还假设硬币是倾斜的,正面着地的概率是 1/3。我们现在想确定 Raj 赢得比赛的概率。
这个问题和之前的问题有什么不同?
- 获胜条件:现在宣布获胜者是首先得到₹150 的人。
- In-variance:我们不再拥有游戏中总量的不变性。由于交易不再涉及从 Raj 到 Mir 的金额转移,在游戏中的任何点都可能有任何不确定的总金额。这意味着在游戏中的任何时候,我们都要记录 Raj 和 Mir 的钱数。
有两种方法可以解决这个赌徒破产的变种。Pascal 提出的第一种方法包括遵循我们之前使用的相同建模策略:根据第一次投掷的结果来确定 Raj 获胜的概率,以获得递归关系,确定基本情况,并求解递归公式以获得解决方案。另外,请注意,找到 Raj 获胜的概率与确定在 Mir 赢得 90 场比赛之前 Raj 赢得 50 场比赛的概率是一样的。因此,我们将概率建模如下:
我们让 R₍₅₀,₉₀₎表示 Raj 在 Mir 赢得 90 场比赛之前赢得 50 场比赛的事件。请注意我们是如何跟踪两者的:Raj 赢得的收益数和 Mir 赢得的收益数(由于我们之前拥有的方差属性的损失)。因此,
现在,我们观察到如果 Raj 赢了第一场比赛,那么 Raj 需要再赢 49 场比赛。同样,如果 Mir 在第一场比赛中获胜,那么 Mir 需要再赢 89 场比赛。因此,
和以前一样,我们可以确定递归问题的基本情况:
- 如果给我们米尔从₹150 开始,(注意,这对应于 R₍₁₅₀,₀₎)我们能说拉吉获胜的概率是多少?对,应该是 0!为什么?因为如果米尔有₹150 开始,米尔已经赢得了比赛。
- 如果我们知道拉吉是从₹150 开始的,(注意这对应于 R₍₀,₁₅₀₎),我们能说拉吉获胜的概率是多少?对,应该是 1!为什么?因为如果拉杰有了₹150,他就已经赢了。
因此,我们有以下信息:
就像以前一样,我们可以解决上述递归问题,以获得期望的概率。然而,现在解决上述问题要复杂得多,因为我们要处理两个变量来跟踪每个玩家要赢得整个赌博必须赢得的游戏数量。我们可以编写一个小的计算机程序来解决上述问题。我用 Python 编写了下面的递归解决方案来帮助我们找到解决方案(我还添加了一些记忆来加快速度):
def memoize(f):
table = {}
def helper(*args):
if args not in table:
table[args] = f(*args)
return table[args]
return helper
@memoize
def gambler_ruin(r, m, p):
if r == 0:
return 1
if m == 0:
return 0
else:
return p*gambler_ruin(r-1, m, p) + (1-p)*gambler_ruin(r, m-1, p)
print(gambler_ruin(50, 90, 1/3))
运行上面的程序给了我们问题的解 0.282,即 Raj 获胜的概率是 0.282,比上一个例子高得多。这是因为在前一个问题中,每次米尔赢了就意味着拉吉失去了₹1。但是,对于这个问题,即使 Mir 赢了一局,Raj 仍然需要赢相同数量的局来赢得整个赌博。
最后,让我们看看解决这个问题的另一种方法,这种方法早些时候由费马在他给帕斯卡的信中提出。这包括将问题建模为二项式分布。回想一下,二项式分布给出了 n 次试验中 k 次成功的概率,每次成功的概率为 p:
我们如何使用二项分布来模拟这个问题?我们被要求找出在 Mir 赢 90 场比赛之前 Raj 赢 50 场比赛的概率。因此,如果进行 90 + 50 -1 = 139 个游戏,我们保证会有一个赢家。请注意,这与之前的问题不同,因为之前对投掷次数没有限制。Raj 和 Mir 之间的连续变化意味着无限跑的可能性。然而,对于这个问题,由于我们在 139 场比赛中保证有一个赢家,所以我们可以使用二项分布来确定 Raj 获胜的概率。
假设,我们将游戏延长到 139 次投掷。如果 139 次投掷中有 50 次以上是正面,拉吉获胜。否则,米尔赢了。设 X 是 139 次投掷中投掷头数的随机变量,每次投掷头的概率为 1/3。因此:
因此,Raj 的获胜概率由下式给出:
上述总和可以使用任何计算器(或编写另一个计算机程序)进行评估。这两种方法给出相同的结果也就不足为奇了。
因此,我们已经讨论了赌徒破产问题的两个版本,理解了问题条件的含义,并查看了一些解决这些问题的建模策略。处理这些问题的第一步包括识别 apt 模型,该模型在计算上是可行的,并且利用了任何相关的不变性。接下来是建立模型,例如通过将期望概率取决于特定子事件的结果。一旦获得了模型,我们就开始使用该模型来获得最优解。这可以递归地完成(在确定合适的基本情况之后),或者通过利用一些众所周知的概率分布的属性来完成。最后,我们试图分析模型的结果,试图平衡直觉和实验结果之间的任何冲突。
感谢阅读!希望你喜欢这篇文章!
数据科学家的博弈论
基础:同时移动游戏&优势可解策略
很多人会从学习和/或流行文化中了解到博弈论。电影《美丽心灵》让许多人知道了约翰·纳西和他在这个领域的工作。对于数学家和经济学家来说,博弈论是一个迷人的研究领域,它作为一个研究领域的重要性怎么强调都不为过:53 个诺贝尔经济学奖中有 12 个授予了对博弈论做出贡献的经济学家。
那就是 22%!
我认为这在一定程度上表明了这一研究领域的重要性。虽然数据科学家不需要成为博弈论专家,但我确实认为对博弈论机制的理解是数据科学家工具箱中潜在的有用工具。
数据科学家可以用博弈论来结构化地分析竞争动态。将博弈论与数据科学和分析相结合可以产生更好的预测,从而产生战略决策的结果,这反过来将为企业、消费者、个人和社会产生更好的结果。博弈论是一个有用的额外概念,数据科学家可以应用它来预测理性的玩家/实体将如何做出决策。帮助分析数据驱动决策问题的主要组件包括:
- 可供选择的选项或选择的集合。
- 基于这些选择的一组结果。
- 对每个结局的估价。
博弈论与在动态、互动的环境中解决问题的数据科学家最为相关。这种背景可能是社会的、商业的、政治的、军事的或者完全是其他的。
只要上下文涉及 2 个或更多实体之间的战略互动,博弈论就可以帮助数据科学家产生更好的预测模型,并帮助实体做出更好的决策,以最大化战略互动结果的效用。
一些常见的成功结合博弈论和数据科学的例子包括:
- Armorway(现在的 Avata Intelligence)数据科学家开发了一种算法,以提高美国海岸警卫队巡逻队实时事件的效率。因此,美国海岸警卫队将巡逻效率提高了 60%。
- 内特·西尔弗运用博弈论和数据科学来预测选举结果,最著名的是奥巴马竞选
https://www.theguardian.com/world/2012/nov/17/nate-silver-interview-election-data-statistics
什么是博弈论?
博弈论试图根据一系列假设,对两个或更多参与者之间的战略互动进行建模。在最简单的层面上,由于大量明显不切实际的假设,博弈论可能会让人觉得非常做作。这种感觉没有错,但是这些人为的假设对于建立基础知识是必要的,在此基础上我们可以在以后建立更真实地代表现实世界战略互动的模型。
本文从最基础的层面开始,旨在为读者提供以下学习成果:
- 完全信息同时移动博弈的定义
- 对游戏关键特征的理解,如玩家、策略和回报。
- 识别游戏中的优势和劣势策略的工具,包括如何应用劣势策略的迭代删除以达到战略平衡。
- 理解纳什均衡解决方案的概念,以及如何在游戏中找到纳什均衡,包括手动和使用 NashPy 库。
我觉得有必要声明,虽然对博弈论的基础来说不重要,但我相信计算或算法博弈论对于更能代表真实世界的更复杂的游戏来说是必不可少的。因此,我们将很早就开始使用计算,而传统的游戏及其解决方案都是手工完成的。
让我们从一些基本定义开始:
- “战略”是互动的意思。
- 在战略游戏中,不止一个玩家做决定。
- 每个玩家对结果的满意度既取决于他们自己的决定,也取决于其他玩家的决定。
- 一个完全竞争的市场是非战略性的。
- 一个寡头垄断的市场,就像智能手机市场一样,由少数玩家主导。这是战略性的:每个公司决定供应多少和收费多少;产品差异化;玩家有限。
同样的逻辑也适用于消费者:
- 非战略性决策的一个例子是消费者支出可能是食品杂货。消费者只需要弄清楚什么样的商品能让他或她开心就行了。
- 另一方面,战略性消费者决策的一个例子可能是投资,它不仅受到消费者偏好的影响,还受到诸如该商品的价值是否会随着时间的推移而上升等因素的影响。
- 这些决定受到其他消费者决定的影响,他们可能会问自己类似的问题。
这只是许多例子中的两个。商业世界之外的一些其他例子可能包括:
- 政治活动和平台销售。
- 讨价还价。
- 比赛和竞赛。
- 合作。
- 军事和外交冲突。
- 人际交往。
我们需要考虑的一些关键术语
- 玩家以及他们采取行动的顺序。
- 每个玩家都可以使用的动作。在参与人 1 可以行动的博弈中,他有什么选择?参与人 2 和游戏中的其他参与人也一样。
- 收益:一个衡量每个玩家对游戏每种可能结果的快乐程度的数字。这个数字可以是美元,但不一定要分配一个客观的数字。重要的是,这些数字正确地给玩家的结果排序:较高的数字表示更喜欢的结果,较低的数字表示不太喜欢的结果。
- 一个玩家的策略规定了在游戏中他可能采取行动的每一个可能点他会做什么。
- 一个策略配置文件是一个策略列表——游戏中的每个玩家一个策略。
游戏类型
游戏属于一系列的类别,这取决于它们所代表的战略形势的类型。
同时移动博弈与顺序移动博弈
- 在同步博弈中,所有玩家同时决定采取什么行动。
- 单轮石头剪子布可以被建模为同时移动游戏。
- 在顺序移动博弈中,一个玩家必须先做出决定,然后其他人可以在做出决定之前看到这个玩家的选择。
- 国际象棋可以被建模为一个连续移动游戏。
一次性游戏与重复游戏
- 在单杆游戏中,玩家按照描述玩游戏,收集他们的奖金,然后再也不会彼此互动。游戏一旦结束,就真的结束了。对于任何给定的玩家来说,不需要担心其他玩家将来的惩罚或奖励。
- “真正的”一次性游戏在现实世界中很少见因为人们永远无法完全确定他们再也不会相互交流了。一个相对接近一次性游戏的战略情形是,高速公路上休息站的顾客和员工之间:双方玩家都认为他们不太可能在未来再次相遇。
- 在一个重复的游戏中,玩家多次按照所描述的方式进行游戏,因此原则上玩家有可能在明天通过自己的行为来惩罚或奖励其他玩家今天的行为。
- 要被建模为一个重复博弈,战略互动不一定要重复给定的次数。我们所需要的是,这个博弈有可能会重复。
- 许多现实世界的战略互动最好被建模为重复博弈。例如(与上述相反),在办公楼自助餐厅里,员工和顾客之间的互动最好被建模为一个重复游戏:互动双方的参与者都认为他们很有可能会再次相遇。
- 如果所有玩家都知道所有其他玩家的收益函数,那么这个博弈就是一个完全信息的博弈。换句话说,如果每个玩家都确切地知道其他玩家对游戏的每一个可能结果会有多高兴,那么这个游戏就是一个完全信息的游戏。
- 完全信息的“真实”游戏在现实世界中很少见,因为很少能完全了解另一个人的偏好。一个接近完全信息博弈的例子是一张 20 美元钞票的拍卖。一个竞标者可以合理地确定,所有其他参与者都乐意为低于 20 美元的任何金额买单,并且他们得到的价格越低就越高兴,如果他们得到的价格正好是 20 美元,他们就保持中立(他们既没有赚钱也没有赔钱),如果他们得到的价格高于 20 美元,他们就很难过(因此首先可能永远不会出价高于 20 美元)。
- 尽管如此,重要的是要注意到仅仅因为游戏中的收益可以用一些客观的术语(如美元)来考虑,并不意味着我们可以必然地假设这就是所有玩家关心的。例如,一个竞标者很可能仅仅从赢得拍卖这一事实中获得满足。在这种情况下,他很可能愿意为一张 20 美元的钞票出价超过 20 美元。
- 如果玩家没有其他支付函数的完整图片,这个博弈就是不完全信息的博弈。
- 大多数拍卖可以被认为是不完全信息的博弈。例如,考虑悉尼邦迪一套公寓的拍卖,最终以 200 万美元成交。其他竞标者不知道获胜者对这个结果有多高兴:也许 200 万美元远远低于他/她愿意支付的价格,因此他/她非常满意;也许 200 万美元正是她的最高限额,所以她对这个结果很矛盾。
优化:
一般来说,我们将考察战略情境中的参与者试图做出选择,以获得尽可能让他们富裕的结果。在我们之前遇到的博弈论术语中:游戏中的玩家试图选择策略最大化他们的收益。博弈论以数学的方式对这些选择进行建模,将一个给定玩家的收益表达为他自己以及其他玩家行为的函数。因此,我们需要一种方法来选择行动(由我们的问题玩家),使他的收益最大化。为此,我们需要一种叫做最优化的数学方法。
派生物
我们试图最大化的许多功能将具有以下“山丘”形状:
对于博弈论的大多数商业应用,我们希望最大化某样东西(也许是利润)或者最小化某样东西(也许是成本)。一个简单的表示可能是一个 sad 二次型的利润函数,它的最大值是导数= 0 的点。(来源:作者)
制作这个图非常简单:
这在 python 中建模相当简单。(来源:作者)
如果 x 是参与人的行动,F(x)是该行动的结果,选择最佳可用策略意味着选择使 F(x)最大化的 x 值。当我们的收益函数 F(x)有上面的形状时,这将是 x 值
用直观的术语来说,这是因为导数测量的是 F(x)的变化率,其中 x 的变化非常小。如果函数在某个特定的 x 值处增加,则该 x 值不是使函数最大化的值:通过增加 x 可以获得更高的 F(x)。如果函数在某个特定的 x 值处减少, x 值不是使函数最大化的值:通过减小 x 可以得到更高的值。只有当函数在 x 中既不增加也不减少时,该值才能是最大值,当导数为零时,函数既不增加也不减少。
警告一句:这个推理只适用于上述形状的函数:sad 二次函数。更一般来说,0 的导数不仅出现在最大值,而且最大值的导数可能是正的,也可能是负的。现在我们将只坚持悲伤的二次曲线,因为这简化了事情。
优化示例:
假设一个游戏中的玩家(姑且称他为玩家 1)试图选择他的策略(我们称之为 x1 )来最大化他的收益。但既然这是一个策略情况,参与人 1 的收益不仅仅取决于他自己的策略,还取决于博弈中另一个参与人的策略,参与人 2 的策略选择我们标为 x2 。因此,参与人 1 的收益将是一个依赖于和的函数。
囚徒困境
这是博弈论的一个经典例子,被用来
- 介绍表示同时移动游戏的方法
- 解决这类游戏的一种方法。
文森特·奈特关于经典囚徒困境的 YouTube 视频。提到这一点似乎是合适的,因为我们正在使用他的 nashpy 库(来源:文森特·奈特的 youtube 频道【https://www.youtube.com/watch?v=qcQMeiUnfVQ)
假设两个小偷,鲍勃和乔,被警察抓住并被分开审问。如果两个小偷合作,不泄露任何信息,他们将各获得一年的短期徒刑。如果一个人坦白,他将得到一笔交易,而另一个小偷将被判 5 年的长期徒刑。如果他们都坦白,他们都会被判三年中等刑期。
经典的囚徒困境(来源:作者)
假设两个囚犯都明白游戏的本质,彼此没有忠诚,在游戏之外也不会有报应或奖励的机会。不管对方怎么决定,每个囚犯都通过背叛对方(“坦白”)获得更高的奖励。推理包括分析两个玩家的最佳反应:
- 鲍勃要么否认,要么承认。如果鲍勃否认,乔应该坦白,因为获得自由比服刑一年要好。
- 如果鲍勃坦白,乔也应该坦白,因为服刑 3 年总比服刑 5 年好。所以不管怎样,乔应该坦白,因为不管鲍勃的策略是什么,坦白都是乔的最佳对策。平行推理会表明鲍勃应该坦白。
因为不管其他玩家的选择如何,坦白总是比合作带来更好的收益,所以这对乔和鲍勃来说都是一个严格优势策略。相互坦白是博弈中唯一的强纳什均衡(也就是说,唯一的结果是每个参与者通过单方面改变策略只会变得更糟)。因此,两难之处在于,相互否认比相互背叛产生更好的结果,但这不是理性的结果,因为从自利的角度来看,否认的选择是不理性的。因此,囚徒困境是一个纳什均衡不是帕累托有效的博弈。
报偿
关于收益的一个提示:我经常看到人们对囚徒困境得出错误的结论,并认为否认,否认是纳什均衡。我认为这是对收益误解的结果。这个博弈中的收益可以表示为
- 潜在刑期的年数,在这种情况下收益是负的。这应该是直观的,因为 10 年徒刑显然不如 1 年徒刑有吸引力。
- 代表玩家相对偏好的任意数字,在这种情况下,较高的正数对应较低的句子。
在任何一种情况下,结果都是一样的:严格优势策略是两个囚犯都坦白,因为任何一方都没有动力偏离这个策略(注意,这可能会在连续博弈中发生变化……)。Nashpy 库对游戏建模很有用:
用 nashpy 建模的经典囚徒困境(代码源:作者,包源: )
上面代码的输出(来源:作者)
支付矩阵
收益矩阵是一种便捷的方式来表示定义同时移动游戏的所有信息——玩家、策略和收益。因为我们会在整个课程中用它来表示这类博弈,这里有一些收益矩阵的性质:
- 行玩家为他的每个策略得到一个单独的行。因此,在一个博弈中,参与人 1(横列参与人)有 3 个策略,参与人 2(纵列参与人)有 2 个策略,这个博弈可能是这样的:
行玩家有 3 个策略,列玩家有 2 个策略(来源:作者)
如果有 2 个以上的玩家呢?在这种情况下,我们需要 3 个维度来表示收益矩阵,就像这样:
3 个玩家的收益矩阵(来源:作者)
为了使这个三维收益矩阵更易处理(并给我们空间来写所有的收益),这样的矩阵通常被一分为二,以便能够以二维表示:
三个玩家的收益矩阵(来源:作者)
最后,在完全信息博弈中,参与者不仅完全知道自己的收益,也完全知道博弈中所有其他参与者的收益。所以像囚徒困境这样的完全信息博弈的所有参与者都知道整个收益矩阵(包括所有收益)。
收益,最佳对策和收益矩阵
按照惯例,收益矩阵中的收益是这样写的,首先列出行参与者的收益,然后列出列参与者的收益。如果有两个以上的球员,季后赛名单将包含 3 个元素,像这样:(x,y,z)。
给定玩家的最佳反应函数是一个函数,它将游戏中所有其他玩家的策略作为自变量,并产生一个输出,即在所有其他玩家都采用指定策略的情况下,使玩家 I 的收益最大化的策略。
所以,在囚徒困境中,“如果乔希望鲍勃坦白,他也应该坦白”的正式说法是:
乔的最佳对策
这里“BR”代表“最佳反应”,下标“J”表示这是乔的最佳反应函数。
在收益矩阵中跟踪最佳回应的一个有用方法是强调与最佳回应相关的收益,如视频中所示。也就是说,在确定某个一般参与人 I 的最佳对策时,我们可以固定所有其他参与人的策略组合,然后确定参与人 I 的哪个策略给他的收益最高。一旦这个过程被博弈中所有其他参与人的所有可能的策略组合重复,我们就完全刻画了参与人 I 的最佳反应函数。
最后我们介绍了术语对称博弈: 如果一个人可以在不改变博弈的情况下改变参与者的身份,那么这个博弈就是对称的。
在两人博弈的情况下,就像囚徒困境,这意味着我们可以翻转谁是行中人,谁是列中人,不改变收益,改变后仍然代表同一个博弈。
例如,如果 Joe 的犯罪记录比 bob 长,那么他可能会被判处比 Bob 更多的刑期。会在同样的情况下。这将使游戏不对称。类似地,如果鲍勃关心乔,不想让他进监狱(而乔对鲍勃没有这种善意的感情),鲍勃的收益将不同于乔在相同情况下的收益,博弈将是不对称的。
主导策略
我们也遇到过术语优势策略: 如果对于一个给定的玩家,有一个策略比他可以选择的任何其他策略给他更高的收益,不管其他玩家选择什么策略组合,那么这个策略就是玩家的优势策略。
更正式的说法是,如果一个参与者的最佳反应函数对于任何可能的参数组合都有相同的输出,那么他就有一个优势策略(在这种情况下,他的优势策略就是输出)。
不太正式的说法是,如果不管其他玩家做什么,一个玩家的某个行动总是比其他所有行动都好,那么这个玩家就拥有优势策略。
如果有一种策略对一个玩家来说是“总是最好的”,那么对理性行为的合理预期就是他会一直使用这种策略。如果游戏中的每个玩家都有一个优势策略,那么这种逻辑可以将我们预期在游戏中发生的事情缩小到只有一个策略配置文件:也就是说,我们有一个策略列表(每个玩家一个)对应于我们认为可能的结果。
当一个玩家有一个优势策略时,这个博弈有一个优势策略的均衡:即。每个参与人都有自己的优势策略。囚徒困境就是这样一个游戏。
现实世界的囚犯困境
囚徒困境不仅仅是关于囚徒的。它也是一种代表许多现实生活情况的寓言。使战略局势成为囚徒困境的关键属性是,每个参与者都有一个优势战略——这种行动总是最好的,因此很难被认为是合理的选择——但所有参与者选择优势战略的结果是,双方最终都比他们选择不同的情况更糟糕。在占优策略均衡中,两人都要在监狱里度过 8 年,而如果他们都否认的话,他们可能只服刑 1 年。
例 1:军备竞赛
考虑两个强国争夺全球影响力的地缘政治背景,就像美国和苏联在冷战期间的情况一样。如果双方面临的选择是是否积累武器和军事能力,那么可以认为这样做是一种优势战略。
如果其他国家没有积累,你可以这样做,并最终决定性地占上风。如果另一个国家正在积累,那么你最好也这样做,否则会被认为是软弱的,被你的对手攻击。
结果是一场军备竞赛。
如果两个国家以大致相同的速度积累武器,任何一方都不会相对于另一方获得相对优势,因此他们都花费了资源,最终达到了他们之前的水平。
例 2:体育运动中的兴奋剂
通过与上述类似的推理,可以认为对于某些运动中的竞技运动员来说,服用提高成绩的药物(PED 氏症)是一种占优势的策略。
- 如果你的对手都没有服用,那么你自己服用兴奋剂几乎可以保证获胜。
- 如果你的所有对手都服用兴奋剂,那么你自己服用兴奋剂是保持竞争力的唯一方法。
- 如果所有运动员都服用兴奋剂,那么没有人会相对于其他人获得相对优势。
关于“合理性”的一点注记
我们在博弈论中假设每个参与者都寻求最大化自己的收益(这叫做理性)。这个假设有多合理?如果玩家是:
- 利他主义:关心其他玩家的收益,以及他们的收益有多高。
- 嫉妒:关心其他玩家的收益,希望他们的收益比自己的低。
- 平等主义者:希望所有的收益尽可能平等。
如果我们想反映这些偏好,我们可以相应地修改玩家的收益。例如,如果我们的一个囚犯非常在意不把另一个送进监狱,我们可以通过减少他认罪而另一个否认的结果来表示。
或者,如果一项运动中有很大比例的运动员宁愿不使用 ped 而输,也不愿使用 ped 而赢,那么这种战略情况可能不是囚徒困境。理性假设指出,玩家寻求最大化他们的收益,但没有限制或假设是什么决定了这些收益。
结论:
- 一个玩家有一个占优策略,如果这个策略给他们的收益比他们能做的任何事情都高,不管其他玩家在做什么。如果一个玩家有一个优势策略,期待他们使用它。
一路游戏到 AGI
原文:https://towardsdatascience.com/gaming-our-way-to-agi-2a0e44a5e691
播客
一路游戏到 AGI
丹尼尔·哈夫纳论强化学习代理的程序游戏生成
编者按:TDS 播客由杰雷米·哈里斯主持,他是人工智能安全初创公司墨丘利的联合创始人。每周,Jeremie 都会与该领域前沿的研究人员和商业领袖聊天,以解开围绕数据科学、机器学习和人工智能的最紧迫问题。
直到最近,人工智能系统还很狭窄——它们只能执行明确训练过的特定任务。虽然狭窄的系统显然是有用的,但人工智能的精髓是建立更灵活、更通用的系统。
但是,如果没有我们可以优化的良好的性能指标,或者我们至少可以用来衡量泛化能力的指标,这是不可能的。不知何故,我们需要找出需要增加的数字,以便让我们更接近一般能力的代理人。这就是我们在本期播客中与丹尼尔·哈夫纳一起探讨的问题。丹尼亚尔是多伦多大学人工智能专业的博士生,师从吉米巴和杰弗里辛顿以及谷歌大脑和矢量研究所的研究员。
Danijar 一直在研究具有泛化能力的 RL 代理的性能度量和基准问题。作为这项工作的一部分,他最近发布了 Crafter,这是一个可以在程序上生成复杂环境的工具,非常像《我的世界》,具有需要收集的资源,可以开发的工具,以及需要避免或击败的敌人。为了在手工艺环境中取得成功,代理需要稳健地计划、探索和测试不同的策略,这使他们能够解锁某些游戏中的成就。
Crafter 是研究人员正在探索的越来越多的策略的一部分,以找出我们如何能够基准化和测量通用人工智能的性能,它还告诉我们一些关于人工智能状态的有趣事情:我们定义需要正确概括能力的任务的能力越来越变得与人工智能模型架构的创新一样重要。在这一集的 TDS 播客中,Danijar 和我一起谈论了 Crafter、强化学习以及人工智能研究人员在致力于一般智能时面临的巨大挑战。
以下是我在对话中最喜欢的一些观点:
- Crafter 环境包括一系列可实现的目标,每一个目标都包括第一次执行一个特定的游戏任务。其中一些任务很简单,可以由当前最先进的 RL 代理相当一致地完成(例如,寻找食物来源并收获它们)。但有些更具挑战性,因为它们涉及到对其他任务的依赖:例如,一个“收集铁”任务只能在一个“制造石镐”任务已经完成后才能完成。结果是一个相当深的科技树,只能由学会计划的代理完成。
- 因为不同的任务需要不同的能力,Crafter 环境可以给开发者一种方法来描述他们的 RL 代理。通过测量智能体在可实现任务的全部分布中的平均表现,他们获得了智能体能力的指纹,该指纹表明智能体在多大程度上掌握了规划和探索等技能,这些技能与概括能力密切相关。
- 开发机器学习基准的一个挑战性方面是确保它们调整到正确的难度水平。好的基准测试很难掌握(这样他们就可以激励和指导这个领域的进展),但也很容易处理(这样开发人员就有足够的信号来迭代和改进他们的模型)。
- 关于人工智能的未来,一个有趣的辩论与算法的巨大改进或计算资源可用性的增加将在多大程度上带来进一步的进展有关。我们越来越多地看到,强化学习的前沿成果来自基于模型的系统,这些系统比无模型的系统更多地使用计算,RL 的进展与计算预算的增长密切相关(如 MuZero 和 EfficientZero)。对一些人来说,这表明当前的人工智能技术可能足以达到人类水平的性能,只需相对较小的调整,如果它们只是随着更多的计算而扩大的话。虽然 Danijar 看到了这一论点的一些优点,但他确实认为,在达到人类水平的人工智能之前,我们必须在算法设计方面取得根本性的进步,而不仅仅是利用原始的计算能力。
你可以点击这里在 Twitter 上关注丹尼尔。
章节:
- 0:00 介绍
- 2:25 测量概括
- 5:40 什么是工匠?
- 11:10 克拉夫特和《我的世界》的区别
- 20:10 代理行为
- 25:30 合并比例模型和强化学习
- 29:30 的数据效率
- 38:00 分层学习
- 43:20 人级系统
- 文化重叠
- 49:50 总结
伽马分布简单解释
原文:https://towardsdatascience.com/gamma-distribution-simply-explained-d95a9de16278
伽马分布及其起源的解释
介绍
在我之前的文章中,我们讨论并推导了指数分布,你可以在这里查看:
简而言之,指数分布计算泊松过程中事件之间等待时间的概率。
如果你不熟悉泊松过程/分布,我强烈推荐你在继续这篇文章之前阅读一下。
指数分布的问题在于,它只能推断出第一个事件的等待时间的概率。然而,伽马分布给出了等待时间的概率,直到第n事件。
在这篇文章中,我们将推导伽玛分布,并获得其背后的一些直觉。
衍生物
如你所料,伽马分布的推导类似于指数分布。
我们首先说我们想要等待时间第 n(n)事件发生。对于伽玛分布 T 是随机变量。这意味着我们需要 n-1 个事件及时发生**
作者在 LaTeX 中生成的方程。
现在我们需要使用泊松分布的* 概率质量函数(PMF): 来统计 t 时间段内发生0n-1事件的概率***
作者在 LaTeX 中生成的方程。
这里, n 是时间段 t 内发生的事件数,带有速率(泊松)参数 λ ,表示给定时间段内的预期事件数,例如每小时 5 个索赔。****
上面的公式是 累积分布函数(CDF)的定义。 要提取概率密度函数(PDF) 我们只需要找到CDF相对于 t 的导数,作为随机变量。
作者在 LaTeX 中生成的方程。
现在这个求导是相当棘手的,所以我在这里省略了整个过程。不过有兴趣的读者可以在这里找到的完整推导。
PDF 的最终版本如下:
作者在 LaTeX 中生成的方程。
我们做到了!
伽马函数
该分布被命名为伽马分布的原因是因为它包含了 伽马函数:
作者在 LaTeX 中生成的方程。
其中γ是希腊符号γ。因此,重写我们的 PDF:
作者在 LaTeX 中生成的方程。
情节
因此,伽马分布由两个值参数化:
- **λ,事件发生率
- n ,你所等待的事件的数量
下面是用 Python 创建的伽马分布图,其中我们有 n = 3 :
**# import packages
import numpy as np
import scipy.stats as stats
import matplotlib.pyplot as plt# get distributions
x = np.linspace(0, 20, 1000)
y1 = stats.gamma.pdf(x, a=3, scale=0.5)
y2 = stats.gamma.pdf(x, a=3, scale=1)
y3 = stats.gamma.pdf(x, a=3, scale=2)# plot
plt.figure(figsize=(15,7))
plt.plot(x, y1, label='λ=2')
plt.plot(x, y2, label='λ=1')
plt.plot(x, y3, label='λ=0.5')
plt.title('n=3, Waiting Till The 3rd Event', fontsize=24)
plt.xticks(fontsize=20)
plt.yticks(fontsize=20)
plt.legend(fontsize=20)
plt.ylabel('PDF', fontsize=20)
plt.xlabel('Random Variable T', fontsize=20)
plt.show()**
注:以上代码中 a = n 在本博客中刻度为 1/λ。****
作者用 Python 生成的图。
事件之间的预期等待时间等于 1/λ, 这是有意义的,因为当发生次数增加时,事件之间的预期时间应该减少。因此,随着 λ 变小,事件之间的预期时间增加。这在上面的图中可以观察到,我们看到第三个事件的平均等待时间随着 λ 变小而增加。
结论
在这篇博客中,我们对指数分布进行了归纳,得出伽玛分布,给出了等待时间的概率,直到第事件。在我的下一篇博客中,我们将讨论卡方分布,这是伽玛分布的一个子集,以及它在统计学中的许多用途!
和我联系!
- 要想在媒体上阅读无限的故事,请务必在这里注册! 💜
- 在我发布注册邮件通知时获得更新!T13😀
- 领英 👔
- 碎碎念 🖊
- github🖥
- https://www.kaggle.com/egorphysics🏅****
(所有表情符号由 OpenMoji 设计——开源表情符号和图标项目。许可证: CC BY-SA 4.0
GANfolk:使用人工智能创建虚构人物的肖像,作为非功能性物品出售
如何使用 StyleGAN2、VQGAN 和 GPT-3 从开源图像中合成不同的字符
GANfolk 样本,图片作者
我之前在 Medium 上写过关于使用人工智能创造视觉艺术的文章,比如抽象画和风景画。在我的研究中,我注意到生成敌对网络(GANs)似乎很难创建人的形象。我决定正面迎接这个挑战,所以我用画和人的照片训练了两只甘。然后我把这些图片作为 NFT 放在 OpenSea 上一个名为 GANfolk 的收藏中出售。
请注意,我是在知识共享署名共享许可下发布训练图像数据集、源代码和训练模型的。这意味着你可以使用我的代码来创建你自己的数字人画并出售它们,只要你给我署名。有关详细信息,请参见下面的源代码部分。
系统概况
这里是 GANfolk 系统的概述。您可以在下面的章节中找到组件和流程的详细信息。
赣方言成分,作者配图
我首先写了一个脚本来收集在 WikiArt 上公开的人们的画作,以及来自谷歌 Open Images 数据集的开源图像。我通过对齐面部特征和使用 LaMa 修复系统填充任何空白点来预处理图像[1]。然后我用 5400 张图片的 GANfolk 训练集训练了两个 GANs,StyleGAN 2 ADA [2]和 VQGAN [3]。我收集了 2700 幅旧的人物绘画和 2700 幅新的人物照片。
对于创建过程的第一步,经过训练的 StyleGAN 2 系统生成了 1000 幅图像作为基线集。我使用 OpenAI [4]的 GPT-3 为图片生成文本提示,比如“一个体贴的巴西女孩的画像”然后,我使用同样来自 OpenAI 的 CLIP 系统[5],找到与提示匹配的最佳图像。我选择了最好的图片,并将其输入经过训练的 VQGAN 系统进行进一步修改,以使图像与文本提示更加匹配。
我回到 GPT 3 号,让它为每张照片写一个名字和一个简短的背景故事。作为后期处理步骤,我添加了一个晕影效果,并将图像尺寸放大了四倍(从 512x512 到 2048x2048)。经过温和的编辑,我上传了照片和背景故事到 OpenSea 作为 GANfolk NFTs 出售。
先前的工作
在我进入 GANfolk 的细节之前,这里有一个简短的部分,关于其他人在生成人物肖像方面所做的工作。
Flickr-Faces-HQ 数据集
来自 Flickr-Faces-HQ 数据集的样本,图片来源: NVidia
在开发他们的 StyleGAN 系列生成网络的同时,NVidia 发布了一个名为 Flickr-Faces-HQ Dataset (FFHQ)的人物照片数据集。据英伟达称…
…[该]数据集由 70,000 张分辨率为 1024×1024 的高质量 PNG 图像组成,并且在年龄、种族和图像背景方面存在相当大的差异。
尽管 FFHQ 图像的质量和种类都非常好,但 NVidia 还是以非商业条款发布了数据集。此外,我发现这些脸似乎剪得太紧了,不适合拍出好的肖像。
MetFaces 数据集
来自 MetFaces 数据集的样本,图像来源: NVidia
NVidia 还发布了 MetFaces 数据集,这些人脸来自大都会艺术博物馆的画作
数据集由 1,336 张分辨率为 1024×1024 的高质量 PNG 图像组成。这些图像通过大都会艺术博物馆收藏 API 下载,并使用 dlib 自动对齐和裁剪。各种自动过滤器被用来修剪集合。
同样,NVidia 根据非商业条款发布了数据集,他们对人脸使用了类似的紧密裁剪。
下面是使用 StyleGAN 2 ADA 在 FFHQ 数据集上训练并使用 MetFaces 数据集微调后新生成的图像。
StyleGAN 2 ADA输出,图像来源: NVidia
虽然结果令人印象深刻,但毫不奇怪,结果似乎裁剪得太紧了。除了数据集,NVidia 还根据非商业条款发布了官方源代码,因此这些人脸不能作为 NFT 出售。此外,在生成的面孔中似乎明显缺乏文化多样性。
GANfolk 系统组件
我将在接下来的小节中讨论 GANfolk 系统中使用的组件和过程的细节。
采集和预处理训练图像
我写了两个脚本来为 GANfolk 收集源图像。第一个收集了 19 世纪和 20 世纪早期 WikiArt 上的公共领域绘画。第二个从谷歌的开放图像数据集中收集肖像。该数据集由 Flickr 上根据 CC-BY-SA 许可发布的照片组成,该许可允许商业使用。
为了找到并定位图像中的人脸,我使用了一个名为 DLIB 的包中的人脸识别算法。我修改了面部识别代码来更宽松地裁剪面部。这是一些来自 WikiArt 的画作的结果。
排列整齐的 WikiArt.com 肖像,图片由作者提供
虽然肖像周围有更多的负空间,但它确实在大多数图像周围拾取了许多空白的黑色区域。这是因为原始图片没有足够的背景区域来考虑脸部方向的旋转和缩放。
为了弥补这一点,我使用了罗曼·苏沃罗夫等人开发的修复人工智能系统,名为“大遮罩(LaMa)修复”。该系统将自动“填充”由第二个遮罩图像指定的图像部分。这是我写的和喇嘛一起修补的代码。下面是从 WikiArt 修复样本肖像的结果。
来自 WikiArt.com 的修复肖像,作者图片
这是一张经过相同预处理步骤后的图片样本。
修复公开图像中的肖像,作者图片
照片中似乎有各种各样的风格、年龄和种族。并且,在大多数情况下,修复的效果是不可见的。
两个甘的故事
我在这个项目中使用了两个 GAN,一个是由 Kim Seonghyeon(又名 rosinality)独立实现的 StyleGAN2,另一个是由 Patrick Esser 等人实现的 VQGAN。
在我之前的研究中,我发现 StyleGAN2 在生成的图像中创建一个与训练数据中的类型大致相似的全局结构方面做得非常好。然而,图像细节经常模糊或缺失。但是 VQGAN 是完全互补的。它不知道如何创建一个全局结构,但它在填充现实的图像细节方面做得很好。使用两种 gan 是两全其美的。
使用 GPT-3 生成图像提示
为了开始创建过程,我使用 GPT-3 来提示制作图像。我真的要求 GPT-3 达芬奇指令模型这样做:
**"Create prompts to render fictional people at different ages and nationalities."**
源代码这里是这里是。典型结果如下。
**drawing of a thoughtful Brazilian girl
acrylic painting of a sassy Mexican girl
charcoal sketch of an inquisitive Turkish boy
pencil drawing of a determined Indian woman
ink drawing of a playful Japanese girl
acrylic painting of an optimistic Spanish boy**
我使用这些提示有两个目的:(1)作为 CLIP 的输入,指导 GANs 生成相应的图像;(2)作为 NFT 的标题。
使用 StyleGAN2 和 CLIP 生成基线图像
生成提示后,我运行 StyleGAN2 来生成 1000 幅随机的人物画像。然后,我使用 OpenAI 的 CLIP 系统找到与提示最匹配的图像。正如我在早期文章中描述的,CLIP 系统有一个文本编码器和一个图像编码器,用于确定短语和图像之间的相似性。
以下是 StyleGAN 2 生成的最符合提示的前 14 幅图像,“绘制一个有思想的巴西女孩。”
结果来自 GANfolk StyleGAN2 生成的图片,图片作者
你可以在结果中看到各种各样的风格。有些看起来像图画;有些看起来像照片。虽然有些人看起来很体贴,但很难说她们看起来像来自巴西的女孩。所以我选择了系列中的第一个来看看 VQGAN 可以用它做什么。
使用 VQGAN 细化细节
为了找到与提示匹配的图像,我从 StyleGAN2 中提取了一个结果图像,并使用 VQGAN 对其进行了迭代优化。感谢凯瑟琳·克劳森所做的工作以及贾斯汀·约翰的进一步修改,我再次使用 CLIP 来分析图像的每次迭代,并引导 VQGAN 修改图像以更好地匹配提示。这是结果。
来自 GANfolk VQGAN 的结果提炼出一张图片,图片作者
上面的图像是使用 VQGAN 和 CLIP 来匹配提示“绘制一个有思想的巴西女孩”的迭代次数 0、25 和 50 你可以看到照片中的女人变得更年轻,或许“更巴西化”此外,肖像风格变得更像素描而不是油画。
图像后处理
我对每张图片进行了几个后期处理步骤,使它们看起来完美统一:
- 添加了一个晕影来淡化图像的角落;
- 使用模糊蒙版使细节更加清晰(可选);
- 执行超分辨率调整,将图像尺寸从 512x512 增加到 2048x2048
从左到右,原始图像、渐晕效果后的图像和模糊蒙版效果后的图像,作者的图像
这有点微妙,但你可以看到晕影效果如何强调了中心的主题,模糊蒙版效果锐化了细节。这里是晕影和反锐化掩模效果的源代码。
StyleGAN2 和 VQGAN 生成的图像分辨率为 512x512 像素。我使用了德国 Idealo 公司的超分辨率调整系统来提高分辨率。在调整尺寸之前,我给图像添加了一点随机噪声来创造一种绘画效果。这里的源代码是。下面是调整到 2048x2048 的样图。你可以点击图片放大,查看细节。
一个深思熟虑的巴西女孩的画像,作者图片
使用 GPT-3 创建虚构人物的名字和背景故事
在将图片发布到 OpenSea marketplace 之前,我决定通过使用 GPT-3 达芬奇指令系统为虚构的人创建名字和背景故事来“增加人们对 NFTs 的兴趣”。
我用来创建名字的提示如下。
**Create a name for a thoughtful Brazilian girl.****First name:**
GPT-3 系统的回应是:
**Sophia Last name: Santos**
我注意到系统有时会出现一个名人的名字,所以我写了一些代码来检查这个人是否有维基百科页面。然后我创建了一个循环来不断生成名字,直到它在维基百科上创建了一个而不是。这里的代码是这里是。
然后,我使用类似的技术为每个虚构的人创建一个背景故事。我使用的提示如下。
**Create a brief backstory for Sophia Santos, a thoughtful Brazilian girl.**
GPT 三号写道:
**Sofia Santos was born in the heart of Brazil to a Brazilian father and an American mother. Sofia grew up in a bilingual household and learned to appreciate both of her cultures. Sofia is a thoughtful and intelligent young woman who always looks for ways to help others.**
结果相当不错!我的合作者兼评论家珍妮弗花时间阅读了 GANfolk 的全部 100 篇背景故事,其中只有几篇需要编辑。
在 NFT 露天市场上铸造 GANfolk
对于我早期的 NFT 项目, GANshare ,我选择使用多边形区块链,因为它比 Etherium 更环保。出于同样的原因,我决定将 GANfolk 系列放在多边形链上。一旦我创建了所有 100 张带有名字和背景故事的图片,上传并把它们做成 NFT 就非常简单了。你现在可以在 OpenSea 上看到整个 GANfolk 系列。
结果
这里是前 5 个 GANfolk,显示了经过后处理的 StyleGAN2 和 VQGAN 的输出图像。
GANfolk #1,图片作者
一幅神秘的法国女人的画,玛蒂尔德·杜布瓦
玛蒂尔德·杜布瓦出生在法国南部的小镇圣·让·德·鲁兹。她是一位富有的航运巨头和一位歌剧演员的独生女。从年轻时起,玛蒂尔德就表现出对艺术的热爱。
上市OpenSea
GANfolk #2,图片作者
GANfolk # 2——专注的葡萄牙男子 joo Silva joo Silva 于 1984 年出生于葡萄牙里斯本。他从小就对了解周围的世界和不同的文化感兴趣。16 岁时,他和家人搬到了美国,他在加州完成了高中学业。
在上挂牌 OpenSea 上挂牌
GANfolk #3,图片作者
一张淘气的英国少年奈杰尔·拉克斯珀的照片奈杰尔·拉克斯珀出生于富裕的英国父母,他们把他宠坏了。他很快成为一个麻烦的少年,总是在学校和法律上惹麻烦。他的父母想尽一切办法让他守规矩,但都没用。奈杰尔喜欢制造麻烦,喜欢看到父母脸上失望的表情。
在上市 OpenSea 上市
GANfolk #4,图片作者
斯特凡·斯托伊切科夫是一名坚忍的保加利亚少年,斯特凡·斯托伊切科夫出生在保加利亚中心的一个小镇上。他的父母是勤劳的农民,他们教会了他诚实、正直和自力更生的重要性。斯特凡一直是一个安静、自省的孩子,他喜欢花时间阅读、学习和探索周围的自然世界。
上市上市 OpenSea 上市
GANfolk #5,图片作者
GANfolk # 5——一位忧心忡忡的韩国妇女的画像,金春熙 金春熙在首尔郊区长大。从小,她就被教导家庭和传统价值观的重要性。她的父母向她灌输了强烈的职业道德,她很快就树立了勤奋工作的名声。
上市OpenSea
你可以在这里看到所有的 100 个 GANfolk,https://opensea.io/collection/ganfolk
https://opensea.io/collection/ganfolk,图片作者
结论
在这个项目中,我学到了很多关于 GANs 的优点和缺点。正如我上面提到的,StyleGAN2 生成了整体形式良好的体面图像,尽管它们通常缺乏精细的细节。VQGAN 是互补的,因为它不知道如何创建具有全局形式的图像,但如果它从一张具有体面形式的图片开始,它在添加细节方面做得很好,主要是在使用剪辑系统时。
在这个项目中,我也注意到了对欧洲人的偏见。《StyleGAN2》在塑造不同国籍的人物形象时似乎有些吃力。这可能是由于训练图像缺乏多样性,尤其是来自 WikiArt 的绘画。但 CLIP 似乎知道世界各地的人长什么样,VQGAN 负责适当修改图像。
源代码
我收集的 5400 张图片可以在 Kaggle 上找到。这个项目的源代码可以在 GitHub 上获得。我在 CC BY-SA 许可下发布训练图像、源代码和训练模型。
知识共享署名共享
如果您使用这些资源来创建新的图像,请给出这样的归属:此图像是由 Robert A. Gonsalves 与 GANfolk 一起创建的。
感谢
我要感谢詹尼弗·林和奥利弗·斯特瑞普对本文的帮助。
参考
[1]R .苏沃罗夫等人的 LaMa,分辨率稳健的大型蒙版修复与傅里叶卷积 (2021)
[2]t . Karras 等人的 StyleGAN2 ADA,用有限的数据训练生成式对抗网络 (2020)
[3]p . Esser、R. Rombach 和 B. Ommer 的 VQGAN,驯服高分辨率图像合成的变形金刚 (2020 年)
[4]汤姆·布朗等著的《GPT-3》,语言模型是一次性学习者 (2020)
[5]a .拉德福德等人的剪辑,从自然语言监督中学习可转移的视觉模型 (2021)
为了无限制地访问 Medium 上的所有文章,成为的会员,每月支付 5 美元。非会员每月只能看三个锁定的故事。
GANomaly 论文综述:通过对抗训练的半监督异常检测
一种结合自动编码器和生成式对抗网络的异常检测模型
图片由 Unsplash 上的 Ine Carriquiry 拍摄
本文是 论文综述:视觉异常检测的修复重建 的续篇。在上一篇文章中,我回顾了一种新颖的方法,它通过将带有随机块的图像传递到 U-net 来提高异常检测性能。本教程有助于您理解如何使用自动编码器进行异常检测。我还对撰写专门研究视觉异常检测的论文的评论感兴趣,这是我在研究奖学金期间发现的。很少有帖子涉及异常检测和计算机视觉之间的交叉,因此,我想以某种方式填补这一空白。
异常检测是一个众所周知的问题,包括从正常样本中识别异常样本。这个问题很有挑战性,因为正常类的样本量大于异常类。在计算机视觉中,由于数据量小,图像种类有限,这就更难了。
在文献中,很少有模型是为这种类型的任务提出的。在自动编码器方面有相当多的工作,但是当数据集包含具有复杂模式的图像时,例如 CIFAR10,它们被证明是过于简单的模型。为此,GANomaly 被提议作为通用异常检测架构,其:
- 利用对抗训练,典型的 GANs,学习数据分布
- 使用编码器-解码器架构作为发生器网络
在这篇文章中,我将回顾介绍这种新颖的异常检测模型的文章。
概述
1.要求
当你阅读一篇论文时,有些概念是理所当然的。为了不迷失在这篇评论中,我建议您快速浏览这一部分。
自动编码器
自动编码器的体系结构。作者插图。
自动编码器是一种以无监督方式训练的神经网络结构。目标是将输入的最相关特征提取到输入的编码表示中,称为潜在空间表示,然后将其解压缩以获得原始输入。自动编码器由两个神经网络组成:
- ****编码器,将输入的信息压缩成低维的潜码。
- ****解码器从解压缩的潜在代码开始重建原始输入。
生成对抗网络
甘斯的建筑。作者插图。
生成式对抗网络是强大的算法,由以生成真实图像而闻名的 Ian Goodfellow 首次引入。它们以无人监督的方式进行训练,由两种神经网络结构组成:
- ****发生器接收随机输入并产生类似真实数据的样本。
- ****鉴别器试图将生成器生成的假样本与数据集的真实样本区分开来。
用来解释这些生成模型的常见类比是艺术伪造者和艺术研究者的例子。艺术伪造者(创造者)试图创作写实主义绘画,这些绘画类似于真实绘画的特征,而艺术调查员检查哪些绘画是真的,哪些绘画是假的。
主要思想是这两个神经网络互相竞争。在训练开始时,鉴别器比生成器工作得更好,生成器产生的数据明显是假的。随着历元数量的增加,发生器提高了其生成逼真图像的能力,可以欺骗鉴别器。主要目标是在生成器生成数据时达到平衡点,该数据与训练数据无法区分,在这种情况下,鉴别器将无法解决其任务。
对抗自动编码器
对抗性自动编码器的体系结构。作者插图。
对抗性自动编码器(AAE)是一种概率性自动编码器,它利用了 GANs 的思想,对抗性训练,将特定的先验分布(例如高斯分布)强加到自动编码器的潜在代码分布上。
对抗性自动编码器中有不同的组件:
- ****通过最小化重构误差来训练自动编码器,以从潜在代码 z 开始重构原始输入 x。
- ****发生器是自动编码器的编码器。
- ****鉴别器将利用先验产生的真样本与产生的样本(或假样本)区分开来,这些样本对应于自动编码器获得的隐藏代码。
训练对抗性自动编码器有两个主要阶段:
- ****重建阶段:自动编码器学习从潜在代码重建原始图像 x
- ****正则化阶段:第一步是训练鉴别器对隐藏码 z 和使用先验 z’生成的样本进行分类。之后,生成器(也是自动编码器的编码器)被更新以欺骗鉴别模型 d。主要目标是使鉴别器认为隐藏代码来自真实的先验分布。
2.加诺马利
GANomaly 是一种异常检测模型,它采用对抗性训练来捕获数据分布。在某些方面,它与对抗性自动编码器非常相似,因为它将传统的自动编码器与 GANs 相结合,但在 AAE 方面也有差异,这将在下面的段落中探讨。
- 如何应用于异常检测?
- GANomaly 的主要部件
- 目标函数
- 模型测试
它是如何应用于异常检测的?
异常检测有一个正式的问题定义:
- 训练数据集仅包含正常图像,而测试数据集包括正常和异常样本。
- 在训练期间,模型仅学习正态数据分布。
- 在训练阶段完成后,我们希望在测试样本上评估模型。假设在训练期间没有通过的异常样本应该比正常图像输出更高的异常分数。
GANomaly 的主要组件
加诺马利的建筑[1]。
与对抗性自动编码器类似,GANomaly 由不同的组件组成。该方法包括两个编码器、一个解码器和一个判别模型。
- Autoencoder ,其也是模型的生成器,通过使用编码器和解码器网络学习重构原始输入。
- ****鉴别器被训练来区分输入(真样本)和重建(假样本)。
- 第二编码器包括重构为潜在代码 z’。
目标函数
在模型的训练期间,目标函数组合了三个不同的损失函数,这些损失函数中的每一个都是通过不同的子网络获得的:
- ****对抗损失是原始图像 x 的特征表示和生成图像 G(x)的特征表示之间的 L2 距离。在这个损失函数中,f(x)被定义为对于给定的输入 x 输出鉴别器 D 的中间层的函数。对抗性损失用于用生成的图像欺骗鉴别模型 D。
- ****上下文损失是原始输入 x 和生成图像 G(x)之间的 L1 距离。这种损失对于添加关于输入的上下文信息很重要。
- ****编码器损耗是输入的瓶颈特征 z 和生成图像的编码特征 z’之间的 L2 距离。以这种方式,生成器学习如何对正常样本的生成图像的特征进行编码。
因此,目标函数考虑了这三种类型的损失:
其中 w_{adv}、w_{con}和 w_{enc}分别是对抗损失、上下文损失和编码器损失的权重参数。
模型检验
在评估期间,模型使用编码器损耗来输出每个测试图像的异常分数:
每个测试图像的异常分数[1]。
在计算异常分数之后,它们被归一化到 0 和 1 之间的范围内:
异常分数的最小-最大归一化[1]。
3.数据集
有四个数据集被认为是评估异常检测框架的基准。前两个数据集是众所周知的玩具数据集http://yann.lecun.com/exdb/mnist/和 CIFAR10 ,其中一个类被视为异常,其余被视为正常。剩下的数据集是大学行李异常数据集 (UBA)和全枪对操作良性 (FFOB)。****
UBA 是一个行李 x 光数据集,包含 230,275 个图像补片。目标是自动检测来自安全 X 射线扫描的威胁。在样本中,有三个异常类,刀、枪和枪组件,如下例所示。
FFOB 是英国政府的数据集,提供了 4,680 件火器全武器为异常类,67,672 件操作良性为正常类。
为了尊重问题的正式表述,所有这些数据集的正常样本被分成 80%用于训练,20%用于测试。只有在评估期间,模型才会看到异常样本。
4.结果
图 1:MNIST(a)和 CIFAR10 (b)数据集的 AUC 结果。他们还使用 3 种不同的随机种子来考虑变异[1]。
图 1 比较了 MNIST 和 CIFAR10 数据集的 AUC 结果。在这两种情况下,GANomaly 的 AUC 都高于 EGBAD、AnoGAN 和 VAE。
表 1:UBA 和 FFOB 数据集的 AUC 结果[1]。
从表 1 中,值得注意的是,在 UBA 数据集上,除了刀之外,GANomaly 在所有异常类上都表现得更好。此外,异常检测方法优于其他模型,在 UBA 和 FFOB 数据集上分别达到 0.643 和 0.882
图 2:MNIST 数据集上的结果,其中 2 是异常类。(a)通过改变潜在向量 z 的大小来分析模型性能;(b)加权损失对整体模型性能的影响[1]。
图 2 显示了超参数的变化,如潜在向量的大小和损失的权重,如何影响 MNIST 数据集上 GANomaly 的性能。z 等于 100 时获得最高 AUC,而在右侧,w_{bce}=1、w_{rec}=50 和 w_{enc}=1 时获得最佳性能。
图 3: (a)正常和异常测试实例的异常分数直方图。(b)使用t-SNE【1】从鉴别器的最后一个卷积层提取的特征的 3d 可视化。
图 3 突出显示了正常样品和异常样品之间的明显区别。
外卖食品
我希望你喜欢这篇关于 GANomaly 的评论。图像中的异常检测具有挑战性,简单的技术不足以识别异常。此外,缺乏专门用于异常检测的图像数据集,当数据集中没有足够的信息时,无监督方法最适合。
几个月前,我发现了一个适合异常检测的图像数据集,称为 MVTec AD 数据集。这是一个新颖而全面的工业数据集,由 15 个类别的 5354 幅高分辨率图像组成。训练集仅由正常图像组成,而测试集包含缺陷和无缺陷图像。我试图用这个数据集实现 GANomaly。这里的 GitHub 代码是这里是。
如果你想探索异常检测的其他技术,建议你去看看其他解释 Skip-GANomaly 和 AnoGAN 的论文。如果你有其他关于阅读的建议,请告诉我,分享知识是提高的最好方法。感谢阅读。祝您愉快!
参考文献:
[1] 加诺玛利:通过对抗训练进行半监督异常检测,S. Akcay,A. Atapour-Abarghouei 和 T. P. Breckon,(2018)
[2] 对抗性自动编码器,a .马克扎尼,j .施伦斯& N .贾伊特利,I .古德费勒,b .弗雷,(2016)
Github 知识库
你喜欢我的文章吗? 成为会员 每天无限获取数据科学新帖!这是一种间接的支持我的方式,不会给你带来任何额外的费用。如果您已经是会员, 订阅 每当我发布新的数据科学和 python 指南时,您都会收到电子邮件!
GANs:生成对抗网络——一种先进的数据生成解决方案
神经网络
对什么是 gan、它们如何工作以及如何用 Python 构建它们的全面解释
生成对抗网络。图片作者。
介绍
在数据科学社区中,对生成性对抗网络(gan)有太多的炒作。但是,当你开始了解它们时,你马上就会明白为什么了。GAN 架构是一个天才的设置,释放了现实数据生成和增强的潜力。
在本文中,我将带您了解 GANs 的基础知识,并向您展示如何使用 Keras/Tensorflow 库用 Python 编写一个 GANs。
内容
- 机器学习算法领域内的 gan
- GAN 架构及其工作原理的直观解释
- 一个详细的 Python 例子向您展示了如何从头构建一个 GAN
机器学习算法领域内的 gan
即使是经验丰富的数据科学家也很容易迷失在数百种不同的机器学习算法中。为了有助于这一点,我对一些最常见的算法进行了分类,并创建了一个可视化的旭日图,如下所示。
注意,一些算法足够灵活,可以应用于不同的任务。因此,任何分类的尝试都不会完美。尽管如此,能够看到高层视图仍然有巨大的价值。
下图是互动,请点击了解👇在不同的类别上揭示更多。
您会发现 GAN 是神经网络的一个子类,包含不同的变体,如基本 GAN(本文的重点)、条件 GAN(cGAN)、深度卷积 GAN(DCGAN)以及我将在未来文章中介绍的其他类型。
机器学习算法分类。由作者创作的互动图表。
如果你喜欢数据科学和机器学习 ,请 订阅 获取我的新文章的邮件。如果你不是中等会员,可以在这里加入https://bit.ly/36Mozgu。
GAN 架构及其工作原理的直观解释
生成敌对网络是深度学习机器,将两个独立的模型结合到一个架构中。这两个组件是:
- 发电机模型
- 鉴别器模型
这两种模式在零和游戏中相互竞争。生成器模型试图生成类似于问题域中的新数据样本。与此同时,鉴别器会尝试识别所呈现的示例是假的(来自生成器)还是真的(来自实际的数据域)。
发生器和鉴别器之间的竞争使它们成为对手,这就给了 GANs 这个名字。
发电机模型
让我们看看生成器模型,看看它是如何创建新的数据样本的。
发电机型号。图片由作者提供。
- 生成器模型从潜在空间中采样一个随机向量。这个空间遵循高斯分布,维数由我们指定。随机向量播种生成过程,因为我们使用它作为神经网络的输入。
- 输入沿着标准路径通过具有一个或多个隐藏层的网络。在简单 GAN 的情况下,这将是一堆密集连接的层,而深度卷积 GAN (DCGAN)也将包含卷积层。
- 数据流入输出层,我们可以在其中进行最终调整,以确保发生器输出具有所需的形状,能够馈入鉴频器。
- 最后,我们可以使用这些假的(生成的)样本来尝试和愚弄鉴别器。
鉴别器模型
接下来,我们来看看鉴别器模型是如何构造的。
鉴别器模型。图片 bu 作者。
- 鉴别器模型的输入是真实样本(从问题域中抽取)和虚假样本(由生成器模型创建)的组合。
- 数据通过一个或多个隐藏层的网络,就像你在任何其他神经网络中一样。
- 一旦我们到达输出层,鉴别器决定样本是真的还是假的(生成的)。
总之,鉴别器与标准的神经网络分类模型没有什么不同。
GAN 模型
生成性对抗网络结合了相互竞争的生成器和鉴别器模型。下面的 GAN 架构图说明了这两种模型是如何互连的。
GAN 模型架构。图片由作者提供。
正如您在图中看到的,我们向鉴别器模型提供了假的(生成的)和真实的例子,训练它来区分这两个类。
随着鉴别器在区分真实和虚假例子方面变得更好,生成器模型的权重和偏差得到更新,以使其产生更有说服力的假货。
该过程持续指定数量的时期,发生器和鉴别器试图更好地完成它们的特定任务。最后,在极限处,来自发电机模型的输出变得与真实的难以区分,鉴别器模型收敛到 0.5 的中性预测。
一个详细的 Python 例子向您展示了如何从头构建一个 GAN
这个例子的目的是让你对 GANs 的工作原理有一个基本的了解。因此,我们将把它应用于一个简单的问题。
设置
我们需要获得以下库:
- 熊猫、 Numpy 和 Math 用于数据生成和操作
- 用于数据可视化的 Matplotlib 、 Graphviz 和 Plotly (可选)
- Tensorflow/Keras 用于构建神经网络
让我们导入库:
上面的代码打印了本例中使用的包版本:
*Tensorflow/Keras: 2.7.0
numpy: 1.21.4
pandas: 1.3.4
matplotlib: 3.5.1
graphviz: 0.19.1
plotly: 5.4.0*
接下来,我们将创建一个圆,并获取其边缘(圆周)上的点的坐标。然后我们通过训练生成器和鉴别器来教甘“识别”和“产生”这样一个圆。
上面的代码生成 1,000 个点,并绘制一个显示我们的圆的图形。
由 1000 个点组成的圆。图片由作者提供。
创建 GAN 模型
现在我们已经准备好了数据,让我们定义和组装我们的模型。我们将从发电机开始:
发电机模型图。图片由作者提供。
如你所见,我们的生成器有三个输入节点,因为我们决定从一个三维潜在空间中绘制一个随机向量。注意,我们可以自由选择潜在的空间维度。
同时,输出有两个值,分别对应于一个点在 2D 空间中的 x 和 y 坐标。
接下来,我们建立鉴别器模型:
鉴别器模型图。图片由作者提供。
鉴频器输入有两个值,与发生器输出对齐。与此同时,鉴别器输出只是一个单一的值,告诉我们模型对数据是真/假有多有信心。
接下来,我们将这两个模型结合起来创建一个 GAN。下面代码中的一个关键细节是我们使鉴别器模型不可训练。我们这样做是因为我们希望使用真实和虚假(生成)数据的组合来分别训练鉴别器。稍后您将看到我们是如何做到这一点的。
GAN 模型图。图片由作者提供。
为发生器和鉴频器准备输入
我们将创建三个简单的函数来帮助我们为这两个模型采样/生成数据。
第一个从圆中采样真实点,第二个从潜在空间中绘制随机向量,第三个将潜在变量传递到生成器模型中以生成假样本。
模型训练和评估
最后两个函数将帮助我们训练模型,并在指定的时间间隔评估结果。
让我们首先创建模型性能评估函数:
正如您所看到的,上面的函数分别在真实和虚假(生成)点上评估鉴别器。然后绘制 2D 散点图,显示这些点在 2D 平面上的位置。
最后,培训功能:
如前所述,我们通过传递一批 50%真实和 50%虚假(生成)的样本来分别训练鉴别器。同时,发电机训练通过组合 GAN 模型进行。
结果
让我们调用训练函数来显示一些结果:
*# Train GAN model
train(gen_model, dis_model, gan_model, latent_dim)*
时段 0 的结果:
0 个时期后的 GAN 性能。图片由作者提供。
第 3,000 纪元时的结果:
3000 个周期后的 GAN 性能。图片由作者提供。
第 10,000 个纪元时的结果:
10,000 个时代后的 GAN 性能。图片由作者提供。
我们可以看到生成器在每一步都有所改进。然而,在 10,000 个时代之后,通过识别大多数真实的例子和相当一部分假的(生成的)例子,鉴别器仍然表现良好。因此,我们可以继续训练模型 10,000 个纪元,以获得更好的结果。
比较上述模型性能的另一种方法是查看真实和虚假点分布的汇总统计数据:
真实和虚假(生成的)点分布统计的比较。图片作者作者。
上述情况清楚地表明,分布的差异相对较小。
结束语
我希望我的文章已经让您很好地理解了 GANs 是如何工作的。如果您想了解更高级版本的 GAN,如条件 GAN (cGAN)或深度卷积 GAN (DCGAN),请订阅,以便在我发布文章后立即收到您的收件箱。
如果你还不是媒体会员,请考虑通过我下面的个性化链接加入,解锁包括我自己在内的成千上万伟大作家的文章。
最后,你可以在我的 GitHub 库 上找到本文使用的完整 Python 代码,作为 Jupyter 笔记本。
干杯!🤓
索尔·多比拉斯*
一些相关的文章你可能会感兴趣:
* *
使用计算机视觉目标检测的垃圾路线优化
变更数据
使用计算机视觉目标检测的垃圾路线优化
由我 10 岁的女儿 Isabella 合著,她的科学展项目变成了她的第一个动手机器学习项目
以下是我女儿五年级科学展期末报告的编辑版本。我们在一起工作了两个月,每次几个周末。就我个人而言,这是一个非常有益的机会来教她(我所知甚少)计算机视觉和 Python 编码。
从她执行第一个 Colab cell 2+2
的那一刻起,她就被迷住了——只看到它演变成一个完全成熟的系统,可以检测图像中的垃圾:
我女儿对我们第一个经过训练的模型在验证集图像中识别垃圾的最初反应。【图片由作者提供。]
我期待着与下一代人工智能工程师合作更多的项目!
问题动机
我对我在我的城市里看到的垃圾数量感到困扰。保持街道清洁很重要,因为垃圾会渗入我们的土壤和水源。这种环境影响对我们的健康是可怕的——更不用说动植物了。似乎这还不够,我们自然景观的美丽被垃圾大大削弱了。
为了提高对这一问题的认识,我假设市议会需要良好的数据来(1)确认问题存在于他们的地区,以及(2)确定哪些地区最需要垃圾收集资源。为了解决这个问题,我设想了一种安装在城市车辆(警车、垃圾车等)上的摄像机。)在车辆执行其日常公民职责时被动地收集视频数据。然后,摄像头数据由计算机视觉系统处理,该系统可以自动检测摄像头视野中的垃圾。最后,我设想向市议会提供一个数据产品(比如热图)。
德克萨斯州埃尔帕索垃圾检测热图。【图片由作者提供。]
有了这样一个系统,我相信城市会对最需要帮助的地区采取有针对性的行动。
蓝图
我开始我的项目时,头脑风暴了这个问题的多种解决方案。在一个解决方案中,我设想了一个简单的、非技术性的实现:
提议系统的蓝图。【图片由作者提供。]
解决方案 1 有许多限制。也就是说,它不可扩展。没有一个城市会让它的工人每天都盯着每条街上的垃圾。因此,我考虑了“解决方案 2”,它利用机器学习来对来自相机馈送的图像进行分类。这种解决方案很有吸引力,因为它是可行的,并且具有商业现成组件的成本效益。解决方案 2 有两个重要的局限性:它不能识别多个垃圾项目,也不能识别不同种类的垃圾。因此,我考虑了“解决方案 3”,一个被训练来识别每张图像的多种垃圾类型的物体检测系统。
以下是我如何准备和使用我的机器学习模型的蓝图:
机器学习工作流的系统图。【图片由作者提供。]
建筑物
以下摘录来自我的 Google Colab 笔记本。
首先,我在 Colab 中安装了 Tensorflow 对象检测库。
【图片由作者提供。]
然后我下载了 TACO 数据集,它包含了数以千计的 COCO 格式的标签垃圾图片。
[图片由作者提供, TACO Github 库在麻省理工学院许可下的截图。]
接下来,我将 COCO 数据格式从 XML 转换为适合 Tensorflow 对象检测器的 CSV 格式。然后我创建了一个数据框,其中包含文件名、类(“Trash”)和边界框坐标。
【图片由作者提供。]
然后,我创建了一个数据混洗和分割,将图像随机分配到训练、测试和验证组。
【图片由作者提供。]
我从 TensorFlow 库中配置了一个具有预训练权重的 efficientDet_d0_512x512 对象检测器。这有助于减少训练时间,因为模型已经配置了一些基本层来检测各种对象。这里的任务是用垃圾的例子“微调”基础模型,以便它也能学会检测我的例子。
然后,我对模型进行了训练,并观察了它的 total_loss 函数,以确定它的训练是否正确。请注意,随着模型在训练迭代中不断运行,损失函数会逐渐减小。
【图片由作者提供。]
我导出了模型权重,以便以后可以根据需要重新加载它们。这很重要,因为否则每次我想运行对象检测推理时,我都必须重新训练该模型,这并不好,因为花了 1.5 小时来完成训练该模型。
我还确认了我可以可靠地重新加载我导出的模型,如下所示:
【图片由作者提供。]
急于看到我的结果,我使用了一个脚本,该脚本将拍摄模型从未见过的新照片,以可视化其性能:
【图片由作者提供。]
以下是该步骤的一些输出示例:
【图片由作者提供;底层图片是麻省理工学院许可的来自 TACO 开源库的数据。]
模型完成后,我将注意力转向从图片的 EXIF 元数据中提取纬度和经度。我用这个脚本成功地完成了这个任务:
【图片由作者提供。]
另一天,我坐在父亲汽车的后座上,他开着车在德克萨斯州埃尔帕索的各个街区转悠,而我则用 iPad 拍照。这些图片包括地理位置数据,我可以使用上面的脚本提取。
拍完所有的照片后,我们将 iPad 连接到电脑上,下载照片。后来,我们将这些图像上传到 Colab 环境的“custom_data”文件夹中。
对于 custom_data 文件夹中的每个图像,以下脚本(1)执行对象检测推理,(2)提取纬度/经度,(3)将数据一起存储在 dataframe(表)中。
【图片由作者提供。]
下面是一个输出示例:
【图片由作者提供。]
接下来,我为 GeoPandas 安装了一个 lyum 插件,它提供了一个热图功能:
【图片由作者提供。]
从上面的脚本中输入数据帧创建了一个交互式热图,显示垃圾或多或少的区域。以下是德克萨斯州埃尔帕索不同地区的一些热图截图:
【每张图片由作者提供。]
回顾这些数据,我做了一些重要的观察。首先,沃尔玛的停车场出奇的干净。我曾假设那里会非常脏,因为人和车太多了。然而,经过思考,我认为这是一个干净的区域,因为企业有兴趣创造一个友好的购物环境,因此定期清洁停车场。第二,我假设 10 号州际公路附近的区域会因为交通流量而变脏。我的数据支持这一结论,埃尔帕索市应该分配更多的资源清理该地区。第三,农业区(又名“上谷”)脏得惊人。我推测更少的房屋和人会导致更少的垃圾。恰恰相反。
结论
总之,这是一个学习更多关于 Python、机器学习和对象检测的伟大项目——所有这些都是在为城市做好事的背景下进行的!对于最初的问题,这是一个很好的解决方案,因为如果没有自动化,人们执行这样的分析将会非常耗时。我提出的解决方案是一种经济高效的方法,可以快速识别城市中有大量垃圾的区域。我可以想象在城市车辆上配置我的系统,以便他们在执行其他日常任务时定期收集这些数据。
下一次,我会做一些不同的事情。例如,我会尝试为拍摄城市照片创造一种更平衡的采样方法。我担心我是如此渴望找到垃圾的照片,在这样做的时候,我可能在我的数据中过度采样了垃圾(偏见)。我记得在一个公园里,我可以看到许多垃圾,我特意跑过去给它们拍照。
至于改进我的设计,我会做两个重要的改变。首先,当同一块垃圾出现在多张照片中时,我会找到一种方法来处理垃圾计数过多的问题。这发生在汽车缓慢移动时,我快速拍摄了一系列照片,从而在一张照片的前半部分和第二张照片的后半部分包含了垃圾。在这种情况下,同一个垃圾在一个特定的区域被计数两次。改进这种设计的一种方法是创建一个地理过滤器,在每个地理半径内只取一个样本。例如,即使 10 张照片是在同一纬度/经度拍摄的,那么其中一张照片应该仅用于分析。
第二,该模型有时会将相同的垃圾项目识别为预测数组中的多个推断。这里有一个例子:
【图片由作者提供;底层图片是来自 TACO 开源库的 MIT 许可的数据。]
未来的改进包括处理推理以确定一个边界框是否实质上在另一个推理边界框内,这样我只从许多相似的预测中的一个得到结果。在这里,为了使项目在我分配的时间内易于处理,我采用了一系列预测中最有可能的对象检测推断。举例来说,我的系统在热图中只识别和绘制一个项目,即使物体检测器可以发现两个项目(见下图)。这导致了在特别脏的地区对垃圾的统计不足。
【图片由作者提供;底层图片是麻省理工学院许可的来自 TACO 开源库的数据。]
第三,我的第一个训练模型使用了 60+类的 TACO 数据集(铝箔,气溶胶罐,包装纸等。).但是我假设我没有足够的训练数据来准确地捕获这么多不同的类,在这种情况下,我正在微调一个现有的模型。相反,我使用了一个脚本,将所有垃圾类转换成一个名为“trash”的超类这样,我可以用大约 2400 个不同的垃圾样本来训练物体检测器。这一改变提高了我的模型持续检测上传图像中垃圾的能力。
我的想法在这个项目中不断发展。起初,我想建立一个对象检测器模型,因为它可以计算每张图像中的多块垃圾,并报告不同类型的垃圾(铝、纸、玻璃等)。).但是我遇到了一些挑战,例如(1)意识到当相同的垃圾出现在不同的图像中时,该模型会对其进行多次计数,以及(2)以不同的准确度对相同的项目进行多次分类。为了解决这些问题,我的设计必须改变。首先,我创建了一个只有一个类(“trash”)的对象检测器,并对每张图像只计数一个 Trash 实例。回想起来,我应该使用图像分类器。我怀疑这样训练会更快。
这个项目应该帮助其他人谁想要扩展我的工作。我希望有一天世界各地的城市都能实施这个项目。这样,他们可以识别最脏的区域并优化垃圾收集工作。我真心喜欢帮助地球和编码。这个项目是我结合这些激情的完美方式。
—
非常感谢佩德罗·普罗恩萨和佩德罗·西莫斯开源他们的项目, TACO:垃圾检测上下文中的垃圾注释,这使得这项工作成为可能。
这里使用的训练、测试和验证数据集是在麻省理工学院许可下的,可以在位于【https://github.com/pedropro/TACO】T2 的 GitHub 上获得。用于创建热图可视化的数据是作者拍摄的图像。
加托,来自 Deepmind 的最新作品。走向真 AI?
原文:https://towardsdatascience.com/gato-the-latest-from-deepmind-towards-true-ai-1ac06e1d18cd
迄今为止最先进的通才网络
加托可以玩游戏、生成文本、处理图像和控制机械臂。而且还不算太大。真 AI 上来了吗?
深度学习领域进展迅速,Deepmind 的最新作品就是一个很好的例子。他们的加托模型能够学习玩 Atari 游戏,生成逼真的文本,处理图像,控制机械臂等等,所有这些都是通过相同的神经网络实现的。受大规模语言模型的启发,Deepmind 应用了类似的方法,但扩展到了文本输出领域之外。
作者根据 Deepmind 在 arXiv (CC By 4.0)的预印本中的一张免费使用的图像组成的图。
加托是如何运作的
这个新的 AGI(在人工通用智能之后)作为一个多模态、多任务、多体现的网络工作,这意味着同一个网络(即具有单组权重的单个架构)可以执行所有任务,尽管涉及本质上不同种类的输入和输出。
虽然 Deepmind 展示加托的预印本不是很详细,但它足够清楚,因为它深深植根于用于自然语言处理和文本生成的变形金刚。然而,它不仅用文本训练,还用图像训练(已经有了像 Dall 这样的模型。e)、作用在机械臂上的扭矩、玩电脑游戏时的按钮按压等。实质上,加托处理所有类型的输入,并根据上下文决定是否输出可理解的文本(例如聊天、总结或翻译文本等)。)、或扭矩功率(用于机械臂的致动器)、或按钮按压(用于玩游戏)等。
因此,加托展示了基于变压器的机器学习架构的多功能性,并展示了它们如何适应各种任务。在过去的十年中,我们看到了神经网络在玩游戏、翻译文本、图像字幕等方面的惊人应用。但是加托足够通用,使用一组砝码和一个相对简单的架构,就可以自己完成所有这些任务。这与需要集成多个模块以便一起工作的专用网络相反,专用网络的集成取决于要解决的问题。
此外,令人印象深刻的是,加托甚至还没有接近我们所见过的最大的神经网络!“只有”12 亿权重,可与 OpenAI 的 GPT-2 语言模型、即相比,比 GPT-3(1750 亿权重)和其他现代语言处理网络小 2 个数量级以上。
在加托上的结果也支持先前的发现,即来自不同性质的数据的训练导致对所提供的信息的更好学习。就像人类从多个同步信息源了解他们的世界一样!这整个想法完全进入了近年来机器学习领域最有趣的趋势之一:多模态——处理和整合各种类型数据的能力。
论 AGIs 的潜力——走向真正的 AI?
我从来不喜欢人工智能这个词。我过去认为没有什么能打败人脑。然而…
与一年前相比,新兴 AGIs 背后的潜力更加有趣,当然也更加强大。这些模型能够用一个软件解决各种复杂的任务,使它们非常通用。如果一个这样的模型从现在起十年后发展起来,在类似机器人的硬件中运行,有移动的手段和适当的输入和输出外围设备,我们很可能会迈出坚实的步伐,创造出具有真正人工智能的真正的人造生物。毕竟,我们的大脑在某种程度上是非常复杂的神经网络,连接和整合感官信息以输出我们的行动。从虚无的角度来看,没有什么能阻止这种数据处理在计算机上进行,而不是有机地进行。
就在 3 年前,我绝对不会说这些,尤其是没有想到人工智能有一天会成为现实。现在,我不太确定,社区的情绪是相似的:他们现在估计,到 2030 年,我们可能会有基于机器的系统,具有与人类相同的通用推理和解决问题的任务。两年前预计的年份是 2200 年左右,并且一直在缓慢下降:
https://www.metaculus.com/questions/3479/date-weakly-general-ai-system-is-devised/
虽然这只是盲目的预测,背后没有坚实的模型,但这一趋势确实反映了该领域正在迈出的巨大步伐。我现在不认为一个机器人可以在某一天和你下棋,第二天玩拼字游戏,在你不在家的时候给你的植物浇水,甚至根据天气预报和你的植物看起来如何做出自己的决定,智能地为你总结新闻,为你做饭,甚至为什么不帮助你发展你的想法。多面手 AI 可能比我们想象的更快到达。
关键读数
Deepmind 在 arXiv 上关于加托的预印本:
https://arxiv.org/abs/2205.06175
在 Deepmind 的网站上:
https://www.deepmind.com/publications/a-generalist-agent
关于机器学习中的多模态:
我写的一些关于使用 GPT-3 和 VQGAN-CLIP 的文章,我用它们做了很多实验,重点是 web 开发:
www.lucianoabriata.com我写作并拍摄我广泛兴趣范围内的一切事物:自然、科学、技术、编程等等。 成为媒介会员 访问其所有故事(我免费获得小额收入的平台的附属链接)和 订阅获取我的新故事 通过电子邮件 。到 咨询关于小职位 查看我的 服务页面这里 。你可以 这里联系我 。
高斯过程核
原文:https://towardsdatascience.com/gaussian-process-kernels-96bafb4dd63e
不仅仅是径向基函数
在之前的文章中,我们探讨了高斯过程模型如何工作的基本原理——我们探讨了数学细节,并推导了高斯过程回归模型的解析解。我们还演示了如何使用高斯过程回归模型来模拟一些简单的数据。在本文中,我们将通过探索可用于现实生活数据建模的各种协方差核来深入探讨高斯过程模型的主题。本文假设您已经很好地理解了高斯过程模型——如果您还不太熟悉它们,请先阅读以前的文章!
阿布扎比的沙漠。由萨吉蒙·萨哈德万在 Unsplash 拍摄。
高斯过程模型的快速回顾
对于某目标的 φ 观测值y=y₁、 y ₂、……yᵩ]ᵀ对应t19】一组 φ 输入特性x=x₁、 x ₂、…xt3t
高斯过程模型对 yᵩ ₊₁平均值的预测为:
μ=kᵀc⁻y,
相应的预测方差为:
s=c-kᵀc⁻k,
其中协方差矩阵 C 具有元素:
C [ n ,m]=k(xₙ,xₘ)+σδₙₘ,
协方差向量 k 具有元素:
kn= k*(x*ₙ, x ᵩ ₊₁),
以及标量 c :
c=k(xᵩ₊₁,xᵩ₊₁)+σ,
其中 σ 是高斯噪声的标准差,k(xt20】ₙ,xt24】ₘ)是我们接下来要探讨的核函数。
高斯过程核
高斯过程模型中使用的核函数k(xₙ, x ₘ )是其核心——核函数本质上告诉模型两个数据点( x ₙ , x ₘ )有多相似。有几个内核函数可用于不同类型的数据,我们将在本节中研究其中的几个。
径向基函数核
一维高斯函数。作者创造的形象。
也许最广泛使用的核可能是径向基函数核(也称为二次指数核、平方指数核或高斯核):
k(xt48】ₙ,xₘ)= exp(-| |xₙ-xₘ| |/2l),
其中 L 为内核长度刻度。这个内核在很多机器学习库中默认使用,比如 scikit-learn 。
一般来说,高斯过程模型将不能外推超过距离训练数据 L 的距离,如下图所示——对于f(x)=x的模型外推(蓝色曲线)会随着 x 远离训练数据(黑色曲线)而很快失败,即使内插(红色曲线)在
from sklearn.gaussian_process import GaussianProcessRegressor
import numpy as np
import matplotlib.pyplot as plt# Create some toy data for the Gaussian process regressor model.
dx = 0.1
x = np.arange(-10, 10 + dx, dx)
X = x.reshape(-1, 1)
y = x * 1# Fit the model to the toy data.
gpr = GaussianProcessRegressor()
gpr.fit(X, y)# Perform interpolation prediction.
y_pred_in = gpr.predict(X)# Create some data for extrapolation prediction.
x_out = np.arange(10, 20 + dx, dx)
X_out = x_out.reshape(-1, 1)# Perform extrapolation prediction. The model should
# not perform very well here.
y_pred_out = gpr.predict(X_out)plt.plot(x, y, "k", linewidth = 6)
plt.plot(x, y_pred_in, "r")
plt.plot(x_out, y_pred_out, "b")
plt.legend(["Training data", "Interpolation", "Extrapolation"])
plt.xlabel("x")
plt.ylabel("f(x)")
plt.show()
函数的高斯过程模型(黑色曲线):f(x) = x 使用径向基函数核。插值(红色曲线)非常好,而外推(蓝色曲线)很快失败。作者创造的形象。
常数核
常数内核是最基本的内核,定义如下:
k(xt82】ₙ,xt86】ₘ)=κ,
其中 κ 是某个常数值。
白噪声核
不出所料,白噪声核用于模拟数据中的白噪声:
k(xt8】ₙ,xₘ)=νδₙₘ,
其中 ν 是某个噪声水平值。
指数正弦平方核
一维指数正弦平方函数。作者创造的形象。
指数正弦平方核本质上是正弦的,因此能够模拟周期性数据:
k(xt24】ₙ,xt28】ₘ)= exp(-2/l(sin(π|xₙ-xₘ|/p)),
其中 L 是内核长度刻度,而 p 是内核周期。
有理二次核
一维有理二次核。作者创造的形象。
有理二次核等价于多个不同长度尺度的径向基函数核的和。
k(xₙ,xt54】ₘ)=(1+| |xt58】ₙ-xₘ| |/(2al))⁻ᵃ,
其中 L 是内核长度标度,而 a 确定大标度和小标度变化的权重。
内核的组合
我们不仅可以在高斯过程模型中单独使用各种核函数,还可以将它们组合起来,以使模型更加强大!例如,我们将使用具有自定义内核的高斯过程模型来模拟在莫纳罗亚测量的大气二氧化碳的变化。这些数据是由 NOAA 提供的,公众可以免费使用。我只从下载的文件中提取了year
、month
和average
列。使用pandas
库的 csv 文件。
如下图所示,自 1958 年以来,测量的大气二氧化碳浓度一直在稳步上升,具有明显的周期性趋势。我们将尝试从今天开始推断未来 10 年(120 个月)的趋势。
莫纳罗亚的大气一氧化碳测量值(ppm)。作者创造的形象。
如果我们天真地使用 scikit-learn 中的默认径向基函数,我们会发现该模型能够很好地插值训练数据,但完全无法外推至未来,如下图所示。
使用默认设置(即使用径向基函数核)训练的高斯过程模型完全不能对未来进行任何种类的外推。作者创造的形象。
为了使用高斯过程模型有效地对数据建模,我们需要在制作模型时投入更多的精力!我们使用 Rasmussen 和 Williams (2006) 中概述的方程(5.15)—( 5.19)来创建定制的核函数,以便尽可能精确地模拟数据。
长期上升趋势
为了模拟长期上升趋势,我们使用具有大时间尺度的径向基函数核:
k₁(xₙ,xₘ)=aexp(-| |xₙ-xₘ| |/2l₁),
其中 A = 59.3, L ₁ = 390。
使用较大的时间尺度可确保模型能够捕捉时间序列中的长期趋势和变化。
周期性趋势
我们使用指数正弦平方核来模拟时间序列中的年度周期。此外,为了解决数据周期性的漂移,我们向周期性内核添加径向基函数因子:
k₂(xt32】ₙ,xt36】ₘ)=bexp(-| |xₙ-xₘ|/2l₂₁-2/l₂₂(sin
其中 B = 2.33, L ₂₁ = 2330, L ₂₂ = 1.26, p = 1.09。
中期和短期违规行为
时间序列数据中的中期和短期不规则性可以使用有理二次核来建模,该核能够捕捉大范围的长度尺度:
k₃(xₙ,xₘ)=c(1+| |xₙ-xₘ|/(2al₃))⁻
其中 C = 0.596, a = 0.145, L ₃ = 4.74。
噪音
使用径向基函数核和白噪声核的组合对数据中的噪声进行建模:
k₄(xₙ,xt8】ₘ)=dexp(-| |xₙ-xₘ| |/2l₄)+νδₙₘ,
其中 D = 0.183, L ₄ = 0.133, ν = 0.0111。
在高斯过程模型中组合核
用于模拟二氧化碳时间序列的自定义内核是:
CO2 _ kernel=k₁(xₙ,xₘ)+k₂(xₙ,xₘ+k₃(x
# Kernel of the trained sklearn Gaussian process regressor:
print(gpr.kernel_)59.3**2 * RBF(length_scale=390) + 2.33**2 * RBF(length_scale=2.33e+03) * ExpSineSquared(length_scale=1.26, periodicity=1.09) + 0.596**2 * RationalQuadratic(alpha=0.145, length_scale=4.74) + 0.183**2 * RBF(length_scale=0.133) + WhiteKernel(noise_level=0.0111)
如下图所示,由此产生的经过训练的高斯过程模型能够对大约 10 年后的大气二氧化碳浓度进行外推。与使用默认径向基函数核训练的原始模型相比,这是一个巨大的改进。
注意,随着预测日期和训练数据之间的距离增加,外推的方差变得更大。尽管如此,该模型已经能够捕捉长期上升趋势以及训练数据中的年度周期性!
使用包含长期、中期、短期长度尺度以及周期性的定制内核训练的高斯过程回归模型能够推断出大气二氧化碳在不久的将来的变化。作者创造的形象。
结束语
在今天的文章中,我们更详细地探讨了可用于高斯过程模型的各种内核。虽然径向基函数核被广泛使用,并且在大多数情况下都应该工作良好,但我们表明,使用精心制作的核将允许我们在不久的将来有效地对某些数据集进行预测。
对于机器学习从业者来说,很好地理解模型如何工作,以及如何调整模型确实是一项基本技能!如果你想更详细地了解高斯过程模型,我强烈推荐你同时阅读大卫·k·杜文瑙德(2014) 和拉斯姆森和威廉姆斯(2006) 。感谢您的阅读!
参考
[1] C. M. Bishop (2006), 模式识别与机器学习 ,【斯普林格】。
[2]大卫·k·杜文瑙德(2014)。 用高斯过程自动构建模型 ,剑桥大学博士论文。
【3】https://sci kit-learn . org/stable/modules/Gaussian _ process . html
【4】卡尔·e·拉斯姆森和克里斯托弗·k·I·威廉姆斯(2006)。 机器学习的高斯过程 ,麻省理工出版社。
[5]二氧化碳数据来自:Pieter Tans 博士、 NOAA/GML 、(gml.noaa.gov/ccgg/trends/)、T29)和斯克里普斯海洋研究所的 Ralph Keeling 博士。**
R 中的广义有序回归模型
原文:https://towardsdatascience.com/generalized-ordinal-regression-model-in-r-930834edae10
R 系列中的统计
安托万·道特里在 Unsplash 上拍摄的照片
简介
我们已经研究了几个逻辑回归模型,这些模型能够对二元和有序响应变量进行简单和多元回归分析。这些逻辑回归模型的结果通常是所有预测变量的系数。我们做的一个基本假设是比例奇假设。在这种假设下,我们假设预测变量的系数在不同水平的结果之间没有变化。例如,如果响应变量有三个不同的级别,我们假设该系数对于从第一级别到第二级别的转换以及从第二级别到第三级别的转换都有效。不同级别的系数可能不同,这是许多真实世界数据的情况。因此,我们需要拿出不同的模型,没有比例奇假设。
如果我们希望系数在响应变量的每个水平上变化,该模型通常被称为广义有序回归模型。另一方面,如果我们允许系数仅在某些系数上变化,则该模型称为部分比例奇数模型。在这篇文章中,我们将通过 r 中的广义有序回归模型的基础和实现。
想法的简化
比例奇模型的假设可以借助一个例子来阐明。假设我们收集了数百人的数据。这些数据包括他们的教育程度、年龄、婚姻状况、健康状况、性别、家庭收入和全职工作状况。我们想导出一个健康状况的回归模型,把教育程度、性别、婚姻状况、家庭收入作为预测变量。预测变量都是二进制的,这意味着除了教育之外,它们的值不是 0 就是 1。教育变量是连续的,代表个人完成教育的年数。
- 教育:数字和连续
- 婚姻状况:二进制(0 表示未婚,1 表示已婚)
- 性别:二进制(0 代表女性,1 代表男性)
- 家庭收入:二进制(0 代表平均或低于平均,1 代表高于平均)
- 健康状况:依次(1 表示差,2 表示一般,3 表示好,4 表示优秀)
如果我们执行一个有序的逻辑回归并保持比例奇数假设,我们将最终得到每个预测变量的单个系数值。假设家庭收入的系数是“x ”,这意味着家庭收入每增加一个单位(在这种情况下从 0 到 1,因为变量是二进制的),高于一个健康状态类别的对数概率或对数几率是“x”。因此,我们可以对这一模型得出如下结论。
- 如果家庭收入增加到高于平均水平,健康状况从差到一般的对数几率是 x。
- 如果家庭收入增加到高于平均水平,健康状况好于平均水平的对数几率是 x。
- 如果家庭收入增加到平均水平以上,那么从身体健康到身体健康的对数几率是 x。
这就是比例奇比模型的本质,在该模型中,对数奇比在所有水平的结果中保持不变。这种假设在现实世界的数据中经常被违反,因此我们不能继续使用比例奇模型假设。
解决方案
有两种可能方法来处理违反比例奇数假设的变量。一种方法是遵循广义有序 logit 模型,其中允许所有预测变量的影响发生变化。相比之下,可能会有这样的情况,其中一些解释变量是允许变化的,这种模型被认为是部分比例奇数模型。
- 广义有序回归模型->所有预测因子的效果都可能不同
- 部分比例奇数模型->某些预测因子的效果是允许变化的
在部分比例奇数模型中,只有那些违反比例奇数假设的变量才允许变化。其他的保持不变。
数据集
这个案例研究的数据源将是 UCI 机器学习库的成人数据集。在该数据集中,估计大约有 30000 个人基于他们的人口统计信息被识别,这些信息包括但不限于他们的种族、教育、职业、性别、工资、每周工作时间、就业状况以及他们的收入。
为了在 R 中实现,我们将对原始数据进行一些修改。事实上,修改与上面的“想法的简化”一节中的描述完全相同。
- 学历:数值型,连续型。教育对健康状况有很大影响。
- 婚姻状况:二进制(0 表示未婚,1 表示已婚)。我认为这个变量的影响会小一些,但还是包括在内了。
- 性别:二进制(0 代表女性,1 代表男性)。它的影响可能也较小,但发现它会很有趣。
- 家庭收入:二进制(0 代表平均或低于平均,1 代表高于平均)。这可能对健康状况有潜在影响。
- 健康状况:依次(1 表示差,2 表示一般,3 表示好,4 表示优秀)
要使用的修改数据
R 中的实现
首先,我们需要导入所需的库并加载数据。这里,我使用了 VGAM 软件包中的 vglm()命令来实现比例奇数模型和广义序数模型。命令中唯一不同的是
- 对于 PO 模型(比例奇数):平行=假
- 对于广义序数模型:parallel = True
使用命令“parallel = True”对序数模型使用 vglm()相当于使用我们在之前使用的 clm()命令。
结果解读
广义序数模型综述
从结果总结中,我们发现每个预测值有三个系数估计值。所有预测器的系数都是负的。该模型估计处于或低于特定类别的 logit 概率或对数几率。类别也显示在上面的结果中(最后一行)。由于健康状态有四种可能的结果(1 表示差,2 表示一般,3 表示好,4 表示优秀),因此有三种潜在的二元模型在幕后工作。
- logitlink(P[Y≤1])是处于或低于第一类的 logit 或 log 几率
- logitlink(P[Y≤2])是处于或低于第二类的 logit 或 log 几率
- logitlink(P[Y≤3])是处于或低于第三类的 logit 或 log 几率
例如,如果我们考虑婚姻状况,
- 对于婚姻状况的每一个单位变化(在这种情况下,从 0 到 1 或从未婚到已婚),处于健康状况类别 1 和处于健康状况类别 1 以上的 logit 或 log 比值比之间的差异变化-0.572。
- 处于或低于第 2 类健康状况和处于第 2 类以上健康状况的 logit 或 log 比值比之间的差异为-0.404,婚姻状况每改变一个单位(在这种情况下,从 0 到 1 或从未婚到已婚)。
- 对于婚姻状况的每一个单位变化(在这种情况下,从 0 到 1 或从未婚到已婚),处于或低于第 3 类健康状况和处于第 3 类以上健康状况的 logit 或 log 比值之间的差异变化-0.12。
我们可以通过执行下面的代码来获得奇数比率
exp(coef(model1,matrix = TRUE))
奇数比率
上述结果表明,婚姻状况的三个比值比分别为 0.564、0.667 和 0.886。从这里,我们可以得出结论,
- 已婚人士处于第 1 类(健康状况较差)健康状况的几率是未婚人士的 0.564 倍。
- 已婚人士处于或低于第 2 类(健康状况较差)健康状况与高于第 2 类(健康状况较好)健康状况的几率是未婚人士的 0.667 倍。
- 已婚人士处于或低于第 3 类(健康状况较差)健康状况与高于第 3 类(健康状况较好)健康状况的几率是未婚人士的 0.886 倍。
我们可以说,结婚降低了处于或低于健康状况类别的几率。另一方面,结婚与处于更好健康状态类别的更高机会相关。
我们还可以对“教育”预测值做出如下结论:
- 受教育年限每增加一个单位,处于 1 类(健康状况较差)健康状况的几率降低 0.839 倍。
- 教育年数每增加一个单位,处于或低于第 2 类(健康状况较差)健康状况的几率减少 0.857 倍。
- 教育年数每增加一个单位,处于或低于第 3 类(健康状况较差)健康状况的几率减少 0.878 倍。
因此,教育程度也与健康状况呈正相关。
结论
我们已经讨论了广义有序逻辑回归模型和部分比例奇数模型背后的基本思想。在这篇文章中,广义有序逻辑回归模型在 R 中的实现被证明和解释的结果。将来,我们将实施部分比例奇数模型,其中只有那些违反 PO 假设的变量才会发生变化。
数据集确认
杜瓦博士和格拉夫博士(2019 年)。UCI 机器学习知识库[http://archive . ics . UCI . edu/ml]。加州欧文:加州大学信息与计算机科学学院(CC BY 4.0)
感谢阅读。
https://mdsohel-mahmood.medium.com/membership https://mdsohel-mahmood.medium.com/subscribe
推广你的模型:EfficientNetV2 和猫和狗的例子
作者图片
考虑一下这个场景。你正在使用最新的 CNN 网络架构 EfficientNetV2 来训练一个图像分类器。您已经取得了令人印象深刻的训练准确性(> 95%),但模型学习评估样本的效果远不如训练样本。
作为机器学习工程师,我们明白我们的模型只有在看不见的数据上表现良好时才是好的。这就引出了一个问题:
我们如何提高网络在不可见数据上的性能?
当我们的模型过度拟合时,两个最常见的修正是:
- 在更多样本上训练我们的模型
- 改变我们模型的复杂性
由于获取数据是昂贵的(时间+成本),在这篇博客中,我们将专注于通过增强管道和改变模型复杂性来转换数据。在专注于建筑安全和安保的建筑科技初创公司 Forsight ,我们的机器学习团队使用这些策略来产生更好的通用模型。
你可以在 CNN 解读 这里 和 PPE 检测 这里 中了解我们的工作。
在本文中,我们将使用一个猫和狗的数据集,向您展示如何微调您的模型,并提高看不见的数据的性能。您可以很容易地扩展这个示例,并使用它来改进您自己的模型!让我们跳进来。
数据集和模型
我们将使用来自 kaggle 的高质量数据集,用一个经典的图像分类问题,保持简单。让我们拍摄 20,000 张图片,并在其中的 16,000 张图片上进行训练。剩余的 4,000 幅图像将用于评估。
我们将从基线 EfficientNetV2B0 模型体系结构开始。基础模型将位于输入/标准化层和二进制分类头之间。二进制分类头将包括一个全局平均池层、一个 35%下降的下降层和一个密集预测层。该模型将使用 Adam 优化器训练 100 个时期。为了更快地收敛,我们将使用 1 周期学习率计划,最大值为 0.001。1 周期学习率由两个阶段组成,以实现超收敛。在第一阶段,我们使用余弦退火将学习速率逐渐提高到最大。在第二阶段,我们使用余弦退火将学习率再次降低到 0。你可以在这里阅读更多相关信息。
结果(图片由作者提供)
在 100 个时期之后,该模型已经非常好地学习了训练数据,准确率接近 100%。但是验证损失正在增加,这表明我们过度适应训练数据,验证准确率停留在 85%左右,这还不够好!让我们开始微调模型,以提高图像增强管道在验证样本上的性能。
图像增强
数据扩充是一系列保留输出标签的输入转换。这是增加数据集大小和多样性的常用技术。对于影像数据集,常见的变换包括像素级操作,如更改颜色、亮度和添加噪声。像旋转和翻转这样的图像级变换也很常见。让我们在模型训练之前将图像增强管道插入到更大的数据管道中。在一些简单的二元分类问题中,降低模型复杂度也有助于防止过拟合。但在这个例子中,我们将使用图像增强管道来增加训练集的多样性,并有望减少过度拟合。
对于管道,我们将使用albuminations,这是一个快速灵活的库,广泛用于工业、研究、竞赛和项目。我们将实现像素级的变换:模糊、随机亮度对比、rgb 偏移和噪声。之后,我们将实现图像水平转换:水平翻转和随机旋转 90 度。最后,我们将添加图像压缩。您可以探索不同的增强,序列转换成管道,并用您自己的图像测试一切这里!下面是我们的完整管道,带有示例输入&输出图像:
增强管道(图片由作者提供)
让我们通过研究每个增强应用于每个训练样本的概率来试验管道。现在,我们将保持简单,并应用相同的概率,x,到每一个增加。但在未来,这种可能性可以微调。我们将把这个概率从 0%逐渐增加到 33%。我们将使用上面详述的相同 b0 模型,所有其他参数将保持不变。
增强管道实验结果(图片由作者提供)
增强管道提高了模型在不可见数据上的性能,每次应用增强的可能性只有 5%!当我们训练模型时,验证损失不再增加,表明模型不再过度拟合。将概率提高到 10%+会随着收益递减而进一步减少验证损失。另一方面,当我们增加增量%时,训练性能受到影响。这是因为当训练样本不断变化时,模型更难在训练样本中学习模式。但是模型对评价集的把握更好,这正是我们想要的!模型权重现在明显较少受训练集中的细节和噪声的影响。接下来,让我们将增强概率参数设置为 33%。既然模型没有过度拟合,我们就可以通过试验模型的复杂性来进一步提高看不见的数据的性能。
模型复杂性
2021 年,谭明星和郭怡广推出了更小、更高效的 EfficientNet 版本 EfficientNetV2。在研究了 EfficientNet 中的瓶颈之后,他们设计了一个新的参数搜索空间,这产生了一个改进的模型架构。你可以阅读更多关于模型架构和参数搜索过程的信息点击 ✎编辑。他们还引入了一种新的非均匀缩放策略,其中层逐渐添加到后期阶段,并通过深度参数(层)和宽度参数(通道)按比例放大,以创建更复杂的模型。作者使用他们改进的参数搜索空间创建了一个基线模型 EfficientNetV2B0,然后使用他们的缩放策略创建了更复杂的模型。我们将对更复杂的模型进行实验,但也会通过缩小深度参数来降低基线模型的复杂性。以下是我们将尝试的所有模型的概述:
作者图片
我们感兴趣的是模型复杂性和模型在看不见的数据上的表现之间的关系。这使我们提出以下问题:
基线模型对于手头的分类任务来说是否过于复杂?这会导致模型学习训练集中的噪声吗?
基线模型不够复杂吗?区分猫和狗需要更多的复杂性吗?
我们将尝试通过上表中详细列出的各种复杂性的训练模型来回答这些问题。我们将使用一个增强管道,每个转换有 33%的机会被应用。所有其他模型和数据集参数将保持不变。
作者图片
增加模型的复杂性会提高对不可见数据的处理能力!当将模型复杂性从 b0 基线模型中的 5.92E+6 个参数增加到 L 模型中的 1.18E+8 个参数时,我们能够将验证损失减少 45%。从直觉上讲,区分猫和狗将受益于更复杂的模型。不同品种的猫和狗的外貌差异很大。有些品种的狗看起来很像猫,反之亦然。此外,该模型不能使用颜色和大小等基本特征来区分。需要考虑更复杂的特征,如面部结构和爪子。让我们用 Grad-CAM 来分析模型看不到的样本。红色区域是导致模型将图像分类为猫或狗的区别特征。你可以在这里阅读更多关于 Grad-CAM 解释算法的内容。
作者图片
在这两个狗样本中,随着我们增加模型的复杂性,红色区域更加精确地符合狗的身体。在低复杂度下,红色区域包括部分背景,表明这些模型没有真正学会狗的区别特征。特别令人吃惊的是,对于右边的狗来说,随着我们逐渐增加复杂度,红色区域从背景中转移出来,并以狗的脸和鼻子为目标。
对于 cat 示例,关注点无处不在,尤其是在不太复杂的模型中。B0–3 模型强调图像的所有部分,除了猫的脸。但是,当我们继续添加更多的复杂性,红色区域在猫的脸上归零。最复杂的模型,L,强调猫的胡须,我们知道这是一个显著的特征!
结论
在这篇博客中,我们希望提供一些有用的见解和工具,来提高你的模型在看不见的数据上的性能。在猫和狗的帮助下,我们探索了一种图像增强管道来减少过度拟合。此外,我们还试验了不同的模型复杂性。在这个例子中,我们见证了更多的复杂性如何帮助我们的模型聚焦于更复杂的特性。最重要的是,我们使用这些工具将未知数据的模型准确率从大约 85%提高到大约 97%!即使有最好的模型和增强管道,我们的模型仍然会出现一些样本错误。正如你在下面看到的,一些狗的样本看起来像猫(左图),反之亦然(右图)。
狗(左)和猫(右)(图片由作者提供)
在机器学习中,我们只能通过在我们的架构和管道中试验参数来提高模型性能。我们希望这篇文章能为您提供关于如何充分利用数据的宝贵见解!
如果你对这个话题感兴趣,并且你愿意研究类似的问题,请联系我们https://forsight.ai/contact/。
参考
- 谭明星,郭诉乐,2021,“高效网络 2:更小的模型和更快的训练”,
- Jason Browlnee,“使用机器学习算法的过度拟合和欠拟合”,https://machineellingmastery . com/over fit-and-under fitting-With-Machine-Learning-Algorithms/
- 卡格尔猫狗数据集,https://www.microsoft.com/en-us/download/details.aspx?id=54765 。许可:社区数据许可协议-许可-版本 2.0。本协议对结果的使用、修改或共享不施加任何限制或义务
- “Tensorflow 2 中具有 1 周期策略的超级收敛,https://www . avan wyk . com/tensor flow-2-Super-convergence-with-1 cycle-Policy/
- Jason Brownlee,“如何避免深度学习神经网络中的过拟合”,https://machineellingmastery . com/introduction-to-regulatory-to-reduce-over fitting-and-improve-generalization-error/
用 Python 从图像生成三维网格
原文:https://towardsdatascience.com/generate-a-3d-mesh-from-an-image-with-python-12210c73e5cc
将深度学习与 3D 数据处理相结合以生成网格
阿尔瓦罗·皮诺在 Unsplash 拍摄的照片
几年前,从一张 2D 图片生成一个 3D 网格似乎是一项非常困难的任务。如今,由于深度学习的进步,已经开发了多个单目深度估计模型,它们可以从任何图像提供精确的深度图。通过这个贴图,可以通过执行表面重建来生成网格。
介绍
单目深度估计是在给定单个 RGB 图像的情况下,估计每个像素的深度值(相对于相机的距离)的任务。单目深度估计模型的输出是一个深度图,它基本上是一个矩阵,其中每个元素对应于输入图像中相关像素的预测深度。
深度图。图片由作者提供。
深度图中的点可以被视为具有 3 轴坐标的点的集合。由于映射是一个矩阵,每个元素都有 x 和 y 组件(它的列和行)。而 z 分量是其存储值,是点 (x,y) 的预测深度。在 3D 数据处理领域,一列 (x,y,z) 点被称为点云。
一个点云。原始文件由 Open3D 制作。
从非结构化的点云开始,可以获得一个网格。网格是由顶点和多边形集合组成的 3D 对象表示。最常见的网格类型是三角形网格,它由一组通过公共边或顶点连接的三维三角形组成。在文献中,有几种方法可以从点云中获得三角形网格,最流行的是 Alpha shape、Ball pivoting 和 Poisson 曲面重建。这些方法被称为表面重建算法。
三角形网格。原始文件由 Open3D 生成。
本指南中使用的从图像生成网格的过程由三个阶段组成:
- 深度估计 —使用单目深度估计模型生成输入图像的深度图。
- 点云构建 —深度图转换成点云。
- 网格生成—从点云中,使用表面重建算法生成一个网格。
要遵循指南中说明的不同步骤,您需要一个图像。如果你手边没有,你可以下载这个:
一间卧室。图片来自 NYU 深度 V2 。
1.深度估计
本指南选择的单目深度估计模型是 GLPN⁴.在抱脸模型轮毂上有。可以通过使用拥抱脸库变形金刚从这个中枢检索模型。
要安装 PyPI 最新版本的变压器,请使用:
pip install transformers
上面的代码用于估计输入图像的深度:
为了使用 GLPN,Transformers 库提供了两个类:GLPNFeatureExtractor
,用于预处理每个输入和GLPNForDepthEstimation
,即模型类。
由于其架构,模型输出大小为:
输出大小。使用 CodeCogs 生成的图像。
因此,image
的大小被调整为 32 的高度和宽度的倍数。否则,模型的输出将小于输入。这是必需的,因为点云将使用图像像素绘制,为此,输入图像和输出深度图必须具有相同的大小。
由于单目深度估计模型难以在边界附近获得高质量的预测,output
被中心裁剪(第 33 行)。为了保持输入和输出之间的相同尺寸,还将image
居中裁剪(第 34 行)。
以下是一些预测:
卧室的深度预测。来自 NYU 深度 V2 的输入图像。
游戏室的深度预测。来自 NYU 深度 V2 的输入图像。
办公室的深度预测。来自 NYU 深度 V2 的输入图像。
2.点云构建
对于本指南的 3D 处理部分,将使用 Open3D⁵。对于这种任务,它可能是最好的 Python 库。
要从 PyPI 安装最新版本的 Open3D,请使用:
pip install open3d
以下代码将估计的深度贴图转换为 Open3D 点云对象:
一个 RGBD 图像仅仅是一个 RGB 图像和它对应的深度图像的组合。PinholeCameraIntrinsic 类存储所谓的内在相机矩阵。通过这个矩阵,Open3D 可以从 RGBD 图像创建一个点云,点之间的间距正确。保持固有参数不变。有关更多详细信息,请参见指南末尾的附加资源。
要可视化点云,请使用:
o3d.visualization.draw_geometries([pcd])
3.网格生成
在文献中可用于此任务的各种方法中,该指南使用泊松表面重建算法。选择这种方法是因为它通常能提供更好、更平滑的结果。
此代码使用泊松算法从上一步中获得的点云生成网格:
首先,代码从点云中删除离群值。由于各种原因,点云可能包含噪声和伪像。在这种情况下,模型可能预测了一些深度,如果与其邻居相比,这些深度变化太大。
下一步是正常的估计。法线是垂直于表面或对象的向量(因此具有大小和方向),为了处理泊松算法,必须对其进行估计。有关这些向量的更多详细信息,请参见本指南末尾的附加资源。
最后,执行算法。depth
值定义了网格的详细程度。除了增加网格质量之外,更高的深度值也会增加输出尺寸。
如果你更喜欢使用程序来可视化网格,我建议下载 MeshLab ,因为有一些 3D 可视化程序不能渲染颜色。
这是最后的结果:
生成的网格。图片由作者提供。
生成的网格(从另一个角度)。图片由作者提供。
由于最终结果根据depth
值而变化,这是不同结果之间的比较:
不同深度值之间的比较。图片由作者提供。
使用depth=5
的算法得到 375 KB 网格,depth=6
到 1.2 MB,depth=7
到 5 MB,depth=8
到 19 MB,depth=9
到 70MB,而使用depth=10
到 86 MB。
结论
尽管只使用了一张图片,效果还是很不错的。借助一些 3D 编辑,你可以达到更好的效果。由于本指南不能完全涵盖 3D 数据处理的所有细节,我建议您阅读下面的其他资源,以更好地理解所涉及的所有方面。
附加资源
这个项目的演示可在这里获得。
感谢阅读,我希望你发现这是有用的。
参考
[1] H. Edelsbrunner 和 E. P. Mücke,三维阿尔法形状 (1994 年)
[2] F. Bernardini,J. Mittleman,H. Rushmeier,C. Silva 和 G. Taubin,[用于表面重建的球旋转算法](http://The ball-pivoting algorithm for surface reconstruction) (1999)
[3] M. Kazhdan,M. Bolitho 和 H. Hoppe,泊松表面重建 (2006)
[4] D. Kim,W. Ga,P. Ahn,D. Joo,S. Chun 和 J. Kim,利用垂直切割深度进行单目深度估计的全局-局部路径网络 (2022)
[5] Q. Zhou,J. Park 和 V. Koltun, Open3D:用于 3D 数据处理的现代图书馆 (2018)
[6] N. Silberman,D. Hoiem,P. Kohli 和 Rob Fergus,室内分割和支持从 RGBD 图像推断 (2012)
使用 Python 生成条形码
原文:https://towardsdatascience.com/generate-barcode-using-python-2da187f46954
根据您的各种需求创建条形码的过程指南
目录
- 介绍
- 支持的格式
- 使用 Python 创建条形码
- 结论
介绍
在本教程中,您将了解不同类型的条形码、它们存储的数据以及如何使用 Python 生成它们。
在当今的零售和电子商务领域,条形码已成为 POS 和仓储系统的必备条件。了解如何使用、生成和解码它们,将有助于数据科学家和数据工程师优化销售和物流流程。
条形码是以机器可读格式表示数据的方式之一。
条形码的概念最初是在 1951 年基于莫尔斯电码开发的。我们现在随处可见的竖线条形码格式是在 1973 年开发的。
条形码的成功故事始于多年后,当时它们开始被用于商店的结账系统,以帮助收银员自动获取每件产品的信息,而不是一件一件地查找。
条形码是二维码的前身,两者的主要区别在于其格式和存储的数据量。
条形码显示为带有几条垂直线的矩形,而二维码显示为内部形状各异的正方形。这种复杂程度使得二维码可以存储比条形码多得多的数据。
现在我们知道了什么是条形码以及如何使用它们,让我们深入条形码格式的细节,并使用 Python 实际创建我们的第一个条形码。
为了继续学习本教程,我们需要以下 Python 库: python-barcode 。
如果您没有安装它,请打开“命令提示符”(在 Windows 上)并使用以下代码安装它:
pip install python-barcode
支持的格式
python-barcode 库支持多种条形码格式:
- 代码 39 它包括大写字母、数字和一些特殊字符。它可以是可变长度的,最大为条形码的大小。
- 代码 128 它包括大写字母、小写字母、数字和一些特殊字符。它可以是可变长度的,最大为条形码的大小。
- PZN7 是代码 39 的变体,编码 6 位数字+ 1 位校验位。这种类型的条形码主要用于德国的药品。
- EAN-13 它包括 12 位数字+ 1 位校验位,用于编码主要在北美以外使用的 GTIN-13(全球贸易项目编号)。
- EAN-8
- JAN JAN(日本商品编号)是 EAN-13 的变体,其中前两位数字必须是 45 或 49(日本)。
- ISBN-13
ISBN-13(国际标准书号)与 EAN-13 相似,只是有一个特殊的前缀。 - ISBN-10(国际标准书号)是 ISBN-13 的一个子类型,一直使用到 2005 年 12 月 31 日。
- ISSN ISSN(国际标准序列号)使用一个 8 位数代码来唯一标识出版物、杂志等。
- UPC-A UPC-A 是 UPC(通用产品代码)的标准版本,由 12 位数字组成。
- EAN-14
- GS1–128
它是代码 128 的变体,用于商业和工业中的货物。这种类型的条形码可以包括多个数据字段。
该库还支持更多的格式。要使用 Python 检查受支持格式的完整列表,可以运行以下代码:
您应该得到:
['codabar', 'code128', 'code39', 'ean', 'ean13', 'ean13-guard', 'ean14', 'ean8', 'ean8-guard', 'gs1', 'gs1_128', 'gtin', 'isbn', 'isbn10', 'isbn13', 'issn', 'itf', 'jan', 'nw-7', 'pzn', 'upc', 'upca']
使用 Python 创建条形码
在本节中,我们将使用 Python 创建一个条形码。
作为一个例子,我们使用与可口可乐罐(12 盎司)相关的一个 UPC-A 。): 049000042511.
你可以在商店出售的一些可口可乐罐上看到这个条形码。
首先,让我们导入所需的依赖项:
接下来,我们将定义条形码内容,在我们的例子中是 UPC 号,它必须是字符串格式:
由于我们将创建一个 UPC 条形码,因此需要引用适当的条形码格式:
现在,我们可以创建条形码并将其呈现为 PNG 文件:
您应该会看到 generated_barcode.png 出现在与您的 main.py 文件相同的目录中,该文件应该是这个条形码图像:
作者图片
使用 Python 创建条形码的完整代码:
结论
在本文中,我们探讨了如何使用 Python 生成条形码。
任何零售组织当前传输的数据量都是数百万行,包含他们库存中每件商品的信息。了解如何生成条形码和 QR 码是实施自动化系统和改善许多零售和电子商务企业物流流程的一个重要优势。
如果你有任何问题或对一些编辑有建议,请随时在下面留下评论,并查看我的更多 Python 编程教程。
原载于 2022 年 6 月 6 日【https://pyshark.com】。
使用单词向量、句子转换器和 MMR 算法为 mcq 生成干扰项
使用 NLP 为 Edtech 中的 mcq 生成错误答案
图片来自 Pixabay
如果你是在 NLP 和 Edtech 的十字路口工作,你迟早会遇到给定问题和答案产生干扰子(错误答案选择)的问题,自动使用 NLP。
什么是干扰物?
干扰物是选择题的错误答案。
例如,如果一个给定的选择题的正确答案是巴拉克奥巴马,那么我们需要产生错误的选择(干扰物),比如乔治布什、比尔克林顿、唐纳德川普、等等。
作者图片
给出一个正确的答案,你如何找到干扰物?
基于知识图寻找干扰物的方法有几种,还有像 word2vec 、fastText 等无监督算法。其中一些可以在我之前的博文中找到。
让我们把重点放在 Sense2vec 上,它与 word2vec 在同一行,在 word 2 vec 中,您使用给定的单词或短语进行查询,并在向量空间中获得相似的单词/短语。
假设我们有一个问题和一个正确答案。
谁是美国第 44 任总统?巴拉克·奥巴马
现在的目标是为单词“巴拉克·奥巴马”找到错误的答案选项(干扰物)。那可能是其他总统或总统候选人。
但是有一个问题!如果您使用“巴拉克·奥巴马”查询 sense2vec,您将会遇到许多近似重复和拼写错误的单词,如巴拉克·奥巴马、奥巴马总统、奥巴马等,您需要过滤这些单词。你也会遇到其他的复制品,比如乔治·布什,乔治·W·布什等等,你只需要保留一个作为干扰物。
sense2vec 算法中与“巴拉克·奥巴马”相似的词:
巴拉克·奥巴马
奥巴马总统
奥巴马
乔治·布什
小布什
比尔·克林顿
约翰·麦凯恩
我们如何处理这个过滤近似重复的问题?我们可以有效地使用句子转换器嵌入和最大边际相关性来实现这一点。我们马上就会看到代码是如何实现的!
该算法
本教程完整代码的 colab 笔记本可以在这里找到。
让我们从安装 sense2vec 库并下载和初始化 sense2vec 模型开始。
让我们用搜索词“巴拉克·奥巴马”来看看 sense2vec 会输出什么
输出:
['巴拉克·奥巴马','乔治·W·布什','罗纳德·里根','乔治·布什','约翰·麦凯恩','吉米·卡特','奥巴马总统','比尔·克林顿','奥巴马','希拉里·克林顿','总统','莎拉·佩林','总统','南希·佩洛西']
现在,为了有效地过滤近似重复,让我们使用句子转换器将原始答案单词以及来自 sense2vec 中相似单词结果的每个单词转换为向量。
现在我们已经为每个单词/短语嵌入了一个向量,我们的目标是在嵌入空间中挑选前 N 个不同的(彼此远离的)单词。
我们选择题案例中的 n 可能是四个。因此,我们反复地想要为我们的搜索关键词(“巴拉克·奥巴马”)挑选三个最多样化的单词/短语。但是我们如何实现这一点呢?
我们有最大边际关联(MMR)来拯救我们!KeyBert 库中有 Maarten Grootendorst 很棒的 MMR 算法实现!
引用上面的 MMR 算法实现页面-
MMR 考虑关键字/关键短语与文档的相似性,以及已经选择的关键字和关键短语的相似性。这导致选择相对于文档最大化其多样性的关键词。
这里的文档可以用我们原来的查询关键字(巴拉克·奥巴马)来代替。我们将稍微修改 MMR 算法,以便选择的第一个单词是我们的原始关键字(巴拉克·奥巴马), MMR 算法顺序选择的其余单词应该与原始关键字不同,并且与现有列表中的单词不同。这确保了我们通过根据多样性标准不选择它们来消除近似重复。此外,该算法具有控制多样性的参数。
你可以看到输出是-
巴拉克·奥巴马
———
约翰·麦凯恩
乔治·W·布什
莎拉·佩林
比尔·克林顿
您可以清楚地看到,类似“巴拉克·奥巴马”、“乔治·w·布什”、“乔治·布什”、“奥巴马总统”、“奥巴马”等的近似重复项被过滤掉,不会出现在最终输出中。
结论
词向量算法可以被用作一种手段来为给定正确答案的多项选择问题生成干扰项(错误选择)。
但是由于单词向量算法中涉及的训练的非监督性质,许多相似的单词和近似重复的单词被返回。
由于这些单词有时语义相似,所以在短语中使用单词编辑距离或单词匹配效果不好。因此,我们使用基于转换器的算法 SBERT ,将任何短语或单词转换成向量,并过滤那里的邻居。这些算法被训练来将拼写错误的单词以及语义相似的单词放置在嵌入空间的附近。
从给定的一组可能的干扰物中,我们可以使用最大边际关联(MMR)算法有效地选择前四个不同的关键字/短语作为 MCQ 的干扰物。
祝 NLP 探索愉快,如果你喜欢它的内容,请随时在推特上找到我。
如果你想学习使用变形金刚的现代自然语言处理,看看我的课程使用自然语言处理的问题生成
使用 Keras 中的浅层和深层自动编码器生成 MNIST 数字
使用函数式 API —神经网络和深度学习课程:第 29 部分
原照片由 refargotohp 在 Unsplash 上拍摄,由作者编辑
如果我们忽略了算法的实际应用,那么算法是没有用的。
我们已经讨论了自动编码器背后的原理。是时候讨论它们的实际应用了。在此之前,您应该知道自动编码器是如何在 Keras 中实现的。
因此,在本文中,我将通过在 MNIST 数据上构建两个自动编码器模型来讨论自动编码器的 Keras 实现(参见最后的数据集引文)。还将定义一些与自动编码器模型架构相关的重要关键字。
先决条件
我推荐你阅读下面的文章作为这篇文章的先决条件。
- 深度学习中的自动编码器介绍 (推荐使用,因为你需要在实现之前了解自动编码器的原理)
- 构建 Keras 模型的两种不同方式:顺序 API 和函数 API (推荐使用,因为您将在这里使用 Keras 函数 API 来构建 autoencoder 模型)
- 获取、理解并准备 MNIST 数据集 (推荐使用,因为您将在此使用 MNIST 数据集构建自动编码器模型)
自动编码器的基本分类
让我们从自动编码器模型的一些基本分类和定义与自动编码器相关的重要关键字开始。
自动编码器的基本分类(图片由作者提供,用 draw.io 制作)
欠完整与过完整自动编码器
当自动编码器的潜在向量的维数小于输入的维数时,称为 欠完备自动编码器 。这种类型的自动编码器通常只尝试学习数据中最重要的特征。
相反,当自动编码器的潜在向量的维数大于或等于输入的维数时,它被称为 过完全自动编码器 ,它试图复制输入而不学习任何重要的特征。
我们将在这里构建的自动编码器模型属于欠完整自动编码器类别。
浅层自动编码器与深层自动编码器
只有一个隐藏层的自动编码器称为 浅层自动编码器 ,而有多个隐藏层的自动编码器称为 深层自动编码器 ,在某些情况下也称为 多层自动编码器 。
自动编码器中不同类型的层
自动编码器的编码器和解码器部分可以使用全连接(密集)层来构建。但是,它们不仅限于完全连接的层。诸如卷积层的其他层类型可以用于自动编码器的编码器和解码器部分。
设计 MNIST 数据自动编码器的体系结构
浅层自动编码器
首先,我们将为 MNIST 数据构建一个带有一个隐藏层的自动编码器,并查看模型的输出。我们将使用 Keras 函数式 API 方法。
浅层自动编码器架构(图片由作者提供,用 draw.io 制作)
- 步骤 1: 定义浅层自动编码器架构。
编码浅层自动编码器的架构(作者代码)
浅层自动编码器架构(图片由作者提供)
自动编码器输出层的大小应该与其输入层的大小相同。否则,输入的重建将是不可能的。
上面代码中的 autoencoder 变量包含编码器和解码器两部分。调用autoencoder.fit()
将输入压缩成潜向量,然后将给定的输入重构为输出。调用autoencoder.predict()
为新的输入数据执行这个过程。
上述代码中的 latent_model 变量输出输入的潜在表示。它包含输入的最重要的特征,并且一旦被训练就可以用于维数减少的目的,这在这里是可选的。调用latent_model.fit()
将输入映射到潜在空间。调用latent_model.predict()
编码新的输入数据。
- 步骤 2: 采集并准备 MNIST 数据集。
(作者代码)
注意,自动编码器不需要标签,因为它们是无监督的学习算法。
- 第三步:编译、训练、监控损失函数。
(作者代码)
(图片由作者提供)
调用compile()
方法为训练准备模型。也就是说,我们声明损失函数的类型和模型的优化器。
通常,我们使用(X_train, y_train)
训练我们的模型,使用(X_test, y_test)
验证我们的模型。但是,在这里,我们使用(X_train, X_train)
训练我们的自动编码器模型,并使用(X_test, X_test)
验证我们的自动编码器模型。我们忽略了y_train
和y_test
部分。这是因为自动编码器是不需要标签的无监督学习算法,输入和输出应该是相同的。
- 第四步:通过重构输入来测试模型。
我们可以绘制原始 MNIST 数字如下:
(作者代码)
原始 MNIST 图片(图片由作者提供)
我们还可以通过我们的浅层自动编码器模型来绘制重建的 MNIST 数字。
(作者代码)
浅层自动编码器重建 MNIST 数字(图片作者提供)
重建的图像与原始图像不太匹配,但它们仍然是可识别的。换句话说,重建图像的质量并不完美。原因是我们在自动编码器模型中只使用了一个隐藏层。这不足以捕捉 MNSIT 数字数据中复杂的非线性模式。因此,在下一节,我们将增加隐藏层的数量,并建立一个深度(多层)自动编码器。
深层(多层)自动编码器
现在,我们将为 MNIST 数据构建一个具有多个隐藏层的自动编码器,并查看模型的输出。我们将使用 Keras 函数式 API 方法。
深度自动编码器架构(图片由作者提供,用 draw.io 制作)
- 步骤 1: 定义深度自动编码器架构。
编码深度自动编码器的架构(作者代码)
深度自动编码器架构(图片由作者提供)
- 步骤 2: 如前所述采集并准备 MNIST 数据集。
- 第三步:编译、训练、监控损失函数。
(作者代码)
(图片由作者提供)
- 第四步:通过重构输入来测试模型。
我们可以像前面一样画出原始的 MNIST 数字。
原始 MNIST 图片(图片由作者提供)
我们也可以像前面一样通过我们的深度自动编码器模型来绘制重建的 MNIST 数字。
深度自动编码器重建的 MNIST 数字(图片由作者提供)
这一次,重建图像的质量有所提高!这是因为我们在自动编码器中使用了许多隐藏层。
下面是对比。
(图片由作者提供)
只有一个隐藏层的浅层自动编码器在生成 MNIST 数字上表现不佳。这是因为单个隐藏层不足以捕捉 MNIST 数据中存在的更复杂的非线性模式。具有多个隐藏层的深层(多层)自动编码器在产生 MNIST 数字方面表现良好。
随着我们增加隐藏层的数量(一个超参数!)在自动编码器中,模型倾向于很好地学习复杂的非线性关系。我们可以尝试不同数量的隐藏层。这是自动编码器中的一种超参数调整。自动编码器的其他重要超参数是潜在向量的维数、隐藏层中的节点数、损失函数的类型、优化器的类型、学习速率、批量大小、时期数以及隐藏层中使用的激活函数的类型。
相反,自动编码器的参数是与全连接层中的每个节点相关联的权重和偏差。
当训练自动编码器时,我们试图通过最小化损失函数来最小化重建误差,该损失函数在训练的每个时期多次更新权重和偏差(参数)的值。
今天的帖子到此结束。
如果您有任何问题或反馈,请告诉我。
阅读下一篇(推荐)
- 阅读我的“神经网络和深度学习课程”的所有剧集。
点击图片进入我的神经网络和深度学习课程(作者截图)*
支持我当作家
我希望你喜欢阅读这篇文章。如果你愿意支持我成为一名作家,请考虑注册成为会员,不受限制地接触媒体。它只需要每月 5 美元,我会收到你的会员费的一部分。
*https://rukshanpramoditha.medium.com/membership
或者,您也可以考虑通过点击本文底部的 给小费 按钮进行小额捐赠。
非常感谢你一直以来的支持!下一篇文章再见。祝大家学习愉快!*
MNIST 数据集信息
- 引用:邓,l,2012。用于机器学习研究的手写数字图像 mnist 数据库。 IEEE 信号处理杂志,29(6),第 141–142 页。
- 【http://yann.lecun.com/exdb/mnist/】来源:
- 许可:Yann le Cun(NYU 库朗研究所)和 Corinna Cortes (纽约谷歌实验室)持有 MNIST 数据集的版权,该数据集在知识共享署名-共享 4.0 国际许可(CC BY-SA)下可用。你可以在这里了解更多关于不同数据集许可类型的信息。
鲁克山普拉莫迪塔
2022–08–09
生成合成移动性数据
原文:https://towardsdatascience.com/generate-synthetic-mobility-data-a32894f1a253
合成移动性数据生成的简单解决方案建议
使用 Kepler.gl 可视化
移动性数据是通过正常活动被动产生的设备的地理位置。从交通规划到移民预测,它都有重要的应用。由于移动数据很少且难以收集,研究人员已经开始探索综合生成数据的解决方案。
在本文中,我将讨论一个生成合成移动数据的简单解决方案。该合成数据可用于研究目的和训练/微调算法。例如,可以综合生成标记的移动性数据,并训练模型来预测城市交通拥堵。然后,经过训练的模型可以应用于现实生活中的数据。
代码可以在这里找到,你也可以用这个 colab 笔记本自己试试。
数据
要合成生成的数据将代表从蜂窝电话设备收集的位置数据记录。通常,此类数据包含以下属性:
- phone_id —手机的唯一标识符
- phone_type —手机操作系统(iOS / Android)
- 时间戳(纪元时间)
- 纬度
- 经度
- 精确度(以米为单位)
方法学
A 部分—获取公共位置数据
在美国选择一个地点,创建一个 x 米的 bbox(边界框)。接下来,获取公共数据集:
- 居住地点—bbox 内的建筑样本( ArcGIS Rest API )
- 兴趣点(兴趣点)bbox 内的业务。( Kaggle 数据集)
- 道路—bbox 内的所有道路,以图形表示( OSM 立交桥 API ,使用 osmnx )
创建一个边界框
使用 Kepler.gl 可视化
获取 ArcGIS 居住地点
使用 arcgis_rest_url 获取 bbox 中建筑物的多边形。
*限于 2000 个多边形样本。
使用 Kepler.gl 可视化
获取 Kaggle POIs 数据集
使用 Kaggle API 下载 POIs 数据集。然后对其进行解析,将其加载到 geopandas,并将数据集过滤为 bbox 内的点。
使用 Kepler.gl 可视化
从天桥 API 获取 OSM 道路
使用 Kepler.gl 可视化
B 部分—生成合成时间线
现在,我们已经拥有了创建电话时间表所需的一切— 居住位置(将用于在家里、家人和朋友家的停留)、poi位置(将用于商店访问)和道路(将用于停留之间的驾驶)。在生成实际的移动性数据之前,我们将生成一个合成时间线来保存手机停留及其时间框架。
合成时间轴逻辑
综合时间线逻辑将在开始日期和结束日期之间的所有日期上迭代,并随机化在工作场所、居住位置和兴趣点的停留。为了保证正常的人类行为,逻辑将只产生工作日的工作停留,并确保用户晚上回家。
运行逻辑之前,请确保:
- 设置随机的家庭和工作地点
- 设定时间框架(开始日期和结束日期)
- 设置给定日期要访问的最大兴趣点和最大居住位置
下面的 gif 显示了我们合成时间线的第一天
使用 Kepler.gl 可视化
C 部分—生成合成流动性数据(信号)
我们的合成时间线已经准备好了,需要一个新的逻辑把它翻译成合成信号。我们的时间表中的第一个事件是寄宿家庭(00:00 -> 08:00),所以让我们从为这次住宿生成信号开始。
静态模式信号
以下脚本将生成停留开始和停留结束之间的信号数据帧。采样率(相邻信号之间的时间间隔)是一个可配置的参数。我把它设置为 600 秒(5 分钟)。每一个信号最近,lng 将被随机“噪声因子”所噪声化
在第一次停留时应用该逻辑将产生以下输出:
使用 Kepler.gl 可视化
驱动模式信号
我们时间线上的下一个事件是在“住所 1290”的停留,但是在为这次停留生成信号之前,我们需要为将我们的电话从其起点(家)带到其目的地(“住所 1290”)的驱动器生成信号。
为此,我们将使用道路图并寻找从起点到终点的最短路径。然后,我们将以 60 秒的采样率在有序路段上随机生成信号。
这就是合成驱动信号在地图上的样子:
使用 Kepler.gl 可视化
全合成移动性数据生成
在我们的最后一步,我们将迭代我们所有的合成时间线。对于每次停留,我们将生成静态模式信号,并且在每两次停留之间,我们将生成驱动模式信号。
轰!我们现在拥有由开源软件包和免费数据生成的完整的合成移动数据
使用 Kepler.gl 可视化
结论
生成合成移动性数据是可行,且不需要特殊的资源。我们需要的所有数据都在那里,可以免费使用。话虽如此,仍有改进的余地:
- 逻辑的幼稚——逻辑非常简单,所以输出并不真正代表完整的人类行为(例如出国旅行、移民等)..)
- 逻辑的效率——逻辑需要一些时间来运行。主要的瓶颈是驱动生成部分(最短路径计算)
- 逻辑的覆盖范围——逻辑仅支持美国,这是因为我使用了 USBuildingFootprints 非常准确,但不幸的是,它仅覆盖美国。
如前所述,代码可以在这里找到,你可以用这个 colab 笔记本自己试试。
*除非另有说明,所有图片均为作者所有。
感谢您的阅读!
为机器学习和软件测试生成合成数据集
使用 Python 生成统计上相似的虚拟数据集,用于代码开发和测试健壮性
据此
为测试生成真实的数据集是一个突出的问题。这在两个领域之一经常遇到:机器学习或软件工程。
创建更可靠的模型
在机器学习中,过拟合往往是一个非常突出的问题。如果一个模型过拟合一个小的/有限的/不平衡的数据集,那么它对未知数据产生准确预测的可靠性就会大大降低。对此的一个解决方案是使用合成数据集来扩展“训练”数据集,合成数据集是自动生成的数据集,具有与原始数据集相同的形式、大小和分布。
看不见或不可用的数据
类似地,如果我们正在编写一个应用程序来读取、查询或可视化数据(这还不可用),我们可能希望通过使用一个伪真实的虚拟数据集来测试它的健壮性。通过这样做,我们能够在数据的最新迭代被处理之前就开始开发全新的应用程序。
数据预测
虽然生成数据的方法很多,例如缩放、变换、标准化、添加噪声、混洗等。我们感兴趣的是保持形式,同时产生一个新的扰动样本集。为了做到这一点,我们依赖于 copulas——与传统的相关性计算相比,copulas 允许我们更好地捕捉诸如非线性尾部相关性等细微差别。
什么是系词?
联合分布解释了我们的n
随机变量(向量)是如何相互关联的。它由两件事组成:
- 边际:对边际行为的描述(每个分布的边缘是什么)
- 每个变量的边际(尾部)概率分布均匀的多元累积分布函数,赢得区间
[0,1]
。这是系词。
copula 描述了数据集中所有随机变量或组件之间的相关性结构。
将它们全部编码成 Python
装置
为了生成我们的合成数据集,我们使用了 Synthia 包。这可以与以下设备一起安装:
pip install synthia
加载和清理数据
我们首先加载数据,并提取数值列的子集,用于数据生成器。在这里,我还将任何nan (*not a number)*
项替换为零——这是因为我希望应用高斯连接函数,对于该函数,线性代数(sqrtm)计算需要对矩阵求逆。
如果使用独立系词(参见“代码”部分的片段),则不需要这一步。
*# Load the original data*
data = pd.read_csv(filename, index_col=0) *# Get file datatypes*
dtypes = data.dtypes *# Get the names of the columns with numeric types *
numeric = data.columns[dtypes.apply(pd.api.types.is_numeric_dtype)] *# Extract numeric subset*
subset = data.loc[:,numeric].replace(np.nan, 0)
构建数据生成器
使用 Synthia 包,我们创建了一个 copula 数据生成器。利用这一点,我们拟合高斯连接函数,并使用以下代码将数据参数化为分位数:
*# Create Generator *
generator = syn.CopulaDataGenerator() *# Define Coupla and Parameterizer *
parameterizer = syn.QuantileParameterizer(n_quantiles=100) generator.fit(subset, copula=syn.GaussianCopula(), parameterize_by=parameterizer)
创建新数据集
现在我们有了一个生成器,我们可以用它来创建一个与原始数据集大小相同的样本数据集。在 ML 任务的情况下,我们可能希望将样本量增加到一个任意大的数字。
samples = generator.generate(n_samples=len(subset), uniformization_ratio=0, stretch_factor=1) synthetic = pd.DataFrame(samples, columns = subset.columns, index = subset.index)
最后,我们可以复制原始数据帧,并用新生成的值替换这些值:
update = data.loc[:]
update.loc[:,numeric]= synthetic.loc[:,numeric]
update = update.astype(dtypes)
结论
我们现在能够使用 pandas 和 Syntia 包创建人工数据集,并在旧数据集的位置注入它们。这有许多应用,并不局限于取代测试的机密信息,在数据集可用之前进行软件开发,或扩展训练数据集作为提高人工智能性能的手段。
密码
这个项目中使用的脚本附后。
资源
在 R 中生成调色板
原文:https://towardsdatascience.com/generating-color-palettes-in-r-64394117a662
从图像生成调色板的 R 包列表
安德鲁·雷德利在 Unsplash 上的照片
https://abhinav-malasi.medium.com/membership
在过去的几个月里,我一直在尝试生成艺术,但我的大部分艺术作品都以灰色为结尾。我从来没有办法让我的作品看起来像你在网上看到的其他艺术家的作品一样漂亮。
所以,我开始从网上使用调色板提取工具。这让我思考,我在 R 本身有这些选项吗?因为从在线编辑器中复制十六进制代码是非常无聊的。我搜索并找到了一些 R 包,它们有能力从图像中提取调色板,并对结果进行充分的控制。
为了展示包的功能,我将使用来自imgpalr
包的图像作为参考。为了保持一致,调色板的大小固定为 10。要复制你的作品,别忘了播种。我们开始吧。
帕莱特
作者:安德烈亚·西里洛
一个简单而强大的工具,从图像中提取颜色并创建调色板。颜色提取器使用 k-means 算法。
这个包使用create_palette()
来创建调色板。开始工作的基本参数是提供文件路径、调色板的大小以及将变量的类型设置为分类。通过提供亮度和饱和度的临界值参数,可以进行进一步的微调。
提取的调色板(L),提取的调色板用于使用 ggplot (R)绘图。(图片由作者提供)
上述调色板的变体通过将参数filter_on_low_brightness
和filter_on_saturation
的值设置为FALSE
来实现。
提取的变体调色板(L),用于使用 ggplot (R)绘图的变体提取调色板。(图片由作者提供)
甚至连上帝都认可这个方案。
imgpalr
作者:马修·莱昂纳维茨
Imgpalr 还使用 k-means 算法来创建调色板。调色板生成器的image_pal()
功能可以更广泛地控制从图像创建调色板。基本参数包括文件路径和要识别的颜色数量。通过设置参数的范围,如saturation
、brightness
和bw
(用于黑白像素),可以调整生成的调色板。参数type
有助于定义顺序的、定性的或发散的调色板。此外,可以使用seq_by
参数通过hsv
、svh
或vhs
对连续调色板进行排序。此外,还可以调整 k-means 集群大小参数。
下面的调色板是用默认值生成的。
用默认值生成调色板(L),调色板用于绘图目的(R)。(图片由作者提供)
只需改变截止饱和度范围,调色板可以调整如下所示。
通过调整饱和度范围生成的调色板(L),调色板用于绘图目的(R)。(图片由作者提供)
colorfindr
作者:大卫·尊巴赫和玛拉·阿威克
该包使用get_colors()
函数提取颜色。除了使用基于频率的参数top_n
定义调色板的大小。也可以通过使用min_share
参数定义颜色共享来创建调色板。min_share
参数的取值范围在 0-1 之间。它还包含使用参数exclude_col
和exclude_rad
从调色板中排除颜色的选项。make_palette()
函数创建调色板。它可以选择设置两种流行的聚类技术:k-means 和中位数切割。可以使用参数clust_method
设置集群的选择。
使用默认设置生成调色板,并将clust_method
参数设置为 kmeans。
使用 k-means 的调色板生成。(图片由作者提供)
这是当clust_method
的参数设置为中间切割时生成的调色板。
使用中间切割生成调色板。(图片由作者提供)
边缘调色板
作者:乔尔·卡尔森
该软件包使用中值切割算法从图像中提取主色。image_palette()
函数用于提取颜色。像往常一样,参数是图像路径、调色板的颜色数量、choice
参数定义了如何选择颜色的函数。
该软件包具有其他与 ggplot2 集成的酷功能。函数scale_color_image()
和scale_fill_image()
可以直接使用 ggplot2 中生成的调色板。
下面的调色板是使用默认设置生成的,调色板的颜色数设置为 10。
使用默认设置生成的调色板。(图片由作者提供)
下面的图是使用 scale_color_image()和 scale_fill_image()函数生成调色板而生成的。这种变化是通过将choice
参数设置为 min 来实现的。
通过将 choice 参数设置为 min 生成的调色板变量。(图片由作者提供)
魔法
作者:rOpenSci
Magick 是 r 中用于图像处理的 goto 包。该包有一个名为image_quantize()
的函数,它为我们完成了这个任务,但不是一条简单的路径。Chisato 的博客对此做了很好的解释。
使用的代码:
这些是我发现在 r 中生成个性化调色板非常有用的一些包,我希望这对你们有些人有帮助。
https://abhinav-malasi.medium.com/subscribe
参考
2.https://github.com/leonawicz/imgpalr
3.https://github.com/zumbov2/colorfindr
4.https://cran . r-project . org/web/packages/magick/vignettes/intro . html
5.https://www . r-bloggers . com/2019/01/用图像量化从图像中提取颜色/
可以在 LinkedIn 和Twitter上与我连线,跟随我的数据科学和数据可视化之旅。或者您可以在 Medium 上关注我,了解有关 R 和 ggplot2 包的数据可视化的最新文章。
为回归模型生成置信区间
原文:https://towardsdatascience.com/generating-confidence-intervals-for-regression-models-2dd60026fbce
几种模型无关方法的解释和研究
数据科学家开发回归模型来预测日常工作中的一些连续变量是很常见的。可能不太常见的是,尤其是当我们学习回归方法时,如何为我们的模型的给定预测定义一个置信区间。
当然,有一些模型具有定义此区间的内置方式,并且在处理分类时,大多数模型会输出一个概率,该概率可以帮助我们处理预测的不确定性。
但是如果我们有一个没有内置输出方式的回归模型呢?我们需要一种不可知论的方法来生成回归变量的置信区间,这就是我们在这篇文章中要探索的。
为此,我们将研究论文使用刀切+ [1]的预测推理,该论文解释了生成这些区间的几种方法及其优缺点。我们将回顾主要的方法,并对它们进行编码,以更好地巩固概念。
这篇文章的代码也可以在 Kaggle 和 Github 上找到。
天真的方法
当我们试图生成置信区间时,最先想到的可能是天真的方法。这个想法是使用我们模型的残差来估计我们可以从新的预测中得到多少偏差。
算法如下:
- 在训练集上训练模型
- 计算训练集预测的残差
- 选择残差分布的(1-alpha)分位数
- 从该分位数中减去每个预测的总和,以获得置信区间的极限
人们期望,由于残差的分布是已知的,新的预测应该不会偏离它太多。
然而,这种天真的解决方案是有问题的,因为我们的模型可能会过拟合,即使没有过拟合,大多数时候训练集的误差也会小于测试集的误差,毕竟,这些点是模型已知的。
这可能会导致过于乐观的置信区间。因此,永远不要使用这种方法。
为了更好地理解它,让我们编写这个方法。对于这个方法和帖子上的其他方法,我们将使用糖尿病数据集[2],可以在商业用途的 sklearn 包中免费获得,也可以在这里找到。我们将应用随机森林回归来创建预测。
首先,让我们导入所需的库:
import numpy as np
import pandas as pdfrom sklearn.datasets import load_diabetes
from sklearn.ensemble import RandomForestRegressor
from sklearn.model_selection import train_test_split, KFold
现在,让我们导入数据集,定义 alpha,并将其分成训练集和测试集:
X, y = load_diabetes(return_X_y=True)
alpha = 0.05
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=42, test_size=0.1)
我们现在将定义一个函数,该函数将为我们生成结果数据集:
def generate_results_dataset(preds, ci):
df = pd.DataFrame()
df['prediction'] = preds
if ci >= 0:
df['upper'] = preds + ci
df['lower'] = preds - ci
else:
df['upper'] = preds - ci
df['lower'] = preds + ci
return df
该函数接收预测和 CI(残差分布的 1-alpha 分位数),并生成预测的下限和上限。
现在让我们生成简单的方法结果。为此,我们将拟合回归变量并计算训练残差,然后我们将获得测试集的分位数和预测,以使用我们的函数:
rf = RandomForestRegressor(random_state=42)
rf.fit(X_train, y_train)residuals = y_train - rf.predict(X_train)ci = np.quantile(residuals, 1 - alpha)
preds = rf.predict(X_test)df = generate_results_dataset(preds, ci)
人们可以使用 Kaggle 笔记本上的一个功能来可视化预测区间,以及预测值和真实值。
折叠法
刀切法试图克服朴素方法的过度乐观结果。为此,它不使用来自训练集的残差,而是使用来自留一预测的残差。
对于那些不知道的人来说,留一法包括为我们数据集中的每个数据点训练一个模型,在整个数据集上训练它,但一次移除一个样本。
通过这样做,残差是根据留一预测来计算的。如您所见,这是对测试集上每个点的预测,因为每个模型在训练阶段都没有看到该点。
然后,为了预测结果(或置信区间的中间点),我们在整个训练集上拟合最后一个模型,并使用它来进行预测。
让我们看看这在代码中是什么样子的:
kf = KFold(n_splits=len(y_train)-1, shuffle=True, random_state=42)
res = []
for train_index, test_index in kf.split(X_train):
X_train_, X_test_ = X_train[train_index], X_train[test_index]
y_train_, y_test_ = y_train[train_index], y_train[test_index]
rf.fit(X_train_, y_train_)
res.extend(list(y_test_ - rf.predict(X_test_)))
为此,我们将使用 sklearn 库中的 KFold 类,使用拆分数量等于实例数量减 1,这将为留一法过程生成拆分。
然后,对于每个训练好的模型,我们计算残值并将其保存在一个列表中。
现在,是时候在整个训练集上拟合模型并生成结果了:
rf.fit(X_train, y_train)
ci = np.quantile(res, 1 - alpha)preds = rf.predict(X_test)
df = generate_results_dataset(preds, ci)
这种方法比天真的方法更有效,但是,它仍然存在一些问题:
- 留一法成本很高,在许多应用中可能不可行
- 当我们的回归的维数接近实例数时,这种方法就失去了覆盖面。
这种覆盖范围的丢失很奇怪,不是吗?这种情况背后的直觉是,适合整个训练集的模型比用于生成残差的每个其他模型多使用了一个点。这使得它们不能直接比较。
折叠+方法
Jackknife+方法试图解决由于模型适合整个训练集而导致的 Jackknife 方法的覆盖范围损失。
为此,它将使用所有留一训练模型来生成预测。这样,对区间中点进行预测的模型将与残差相当。
但它将如何做到这一点呢?根据这篇论文,直觉是我们得到了所有模型预测的中间值。然而,如果希望对该预测或多或少地保守,可以使用预测的分位数。
然而,在实践中,请注意,通常情况下,模型不会随着单点的丢失而改变太多其行为,因此分布通常具有很小的方差,这使得该方法产生与重叠法非常相似的结果。这种方法的优点来自于退化的情况,其中一个点可能极大地改变模型。
让我们把它编码起来:
kf = KFold(n_splits=len(y_train)-1, shuffle=True, random_state=42)
res = []
estimators = []
for train_index, test_index in kf.split(X_train):
X_train_, X_test_ = X_train[train_index], X_train[test_index]
y_train_, y_test_ = y_train[train_index], y_train[test_index]
rf.fit(X_train_, y_train_)
estimators.append(rf)
res.extend(list(y_test_ - rf.predict(X_test_)))
这里的代码与我们在刀切法中使用的代码非常相似,但是,这一次我们还保存了用于生成残差的模型。
现在,我们的预测将有所不同,因为我们需要对每个模型进行预测:
y_pred_multi = np.column_stack([e.predict(X_test) for e in estimators])
现在,让我们来计算下限和上限:
ci = np.quantile(res, 1 - alpha)
top = []
bottom = []for i in range(y_pred_multi.shape[0]):
if ci > 0:
top.append(np.quantile(y_pred_multi[i] + ci, 1 - alpha))
bottom.append(np.quantile(y_pred_multi[i] - ci, 1 - alpha))
else:
top.append(np.quantile(y_pred_multi[i] - ci, 1 - alpha))
bottom.append(np.quantile(y_pred_multi[i] + ci, 1 - alpha))
最后,让我们使用中值预测来生成结果:
preds = np.median(y_pred_multi, axis=1)
df = pd.DataFrame()
df['pred'] = preds
df['upper'] = top
df['lower'] = bottom
现在,这种方法没有解决产生置信区间所花费的时间的问题。它只减少了一个拟合过程,但增加了每个模型的预测开销。
CV+方法
CV+方法试图解决生成间隔所需的时间问题。它与 Jackknife+完全相同,但是,它使用 K-Fold 交叉验证,而不是留一法。
论文模拟表明,这种方法比刀切法稍差,但速度更快。实际上,这可能是大多数情况下使用的方法。
它的实现与 Jackknife+相同,我们只需要更改 for 循环以使用小于数据集长度的折叠数:
kf = KFold(n_splits=5, shuffle=True, random_state=42)
这个简单的更改将实现 CV+方法。
结论
这个新工具可能对许多需要为回归模型生成预测区间的数据科学家有用。此外,这些方法在 MAPIE 库上是开源的。
折叠刀+太贵了,用不上。但是,如果您已经在方法上使用了交叉验证,那么您可以应用 CV+方法,而不会有开销或性能损失。
[1]巴伯、里娜&坎迪斯、埃马纽埃尔&拉姆达斯、阿迪亚&蒂布拉尼、瑞安。(2021).用刀切+进行预测推理。统计年鉴。49.486–507.10.1214/20-AOS1965。
[2] Bradley Efron,Trevor Hastie,Iain Johnstone 和 Robert Tibshirani (2004)“最小角度回归”,统计年鉴(附讨论),407–499
用人的步态生成数字签名
原文:https://towardsdatascience.com/generating-digital-signatures-with-the-gait-of-people-3a66f0c44b7b
用机器学习提高网络安全的创新尝试
早在 2018 年初,我们就预见到了人类手掌和手指上各种点的地标检测的有用性,并解释了我们如何在地标检测的基础上利用机器学习来理解手的各种信号。从那时起,我们已经见证了多种产品利用这个有用的想法来理解手势,主要是通过静态快照。在今天的文章中,我们想向前迈出一步,专注于一个更大的问题,即通过步态检测在短视频中利用人体上的更多标志来识别人。我们相信,随着人体标志检测和更快处理的不断进步,这类应用将占据中心位置。
为什么步态检测很重要?
当新冠肺炎疫情袭击世界的时候,人们不得不戴口罩遮住脸。突然之间,许多面部识别模型只能通过来自额头和眼睛的有限的标志点集来做出决定。一夜之间,通过面部识别生成的数字签名就失效了。我们还看到,随着 3D 打印机的出现,犯罪分子正在生成人脸来模仿他们的目标。更糟糕的是,今天,容易获得的 Deepfake 模型足以让观众相信目标受害者所说的一些陈述。
当今最受欢迎的技术中的所有这些漏洞和缺点将需要探索更新的技术。通过一个人走路的方式生成数字签名可能是一个有希望的开端。
人体标志检测模型
虽然从头开始开发步态检测模型可能是一项非常有趣和充满乐趣的工作,但它需要大量的数据、大量的计算能力和时间,而我们许多人都无法获得这些。因此,我们将努力在已经开展的一些良好工作的基础上再接再厉。作为一名机器学习应用程序开发人员,了解研究领域的所有发展非常重要。通过深度学习模型识别人体各种标志的工作将形成我们架构的基本组成部分。
TensorFlow Hub 为我们提供了许多有学问的模型,这些模型将帮助我们检测人体的地标。由于我们不会调整这些模型,相反,我们更感兴趣的是通过这些模型执行推理,我们甚至可以选择通过切换到 TensorFlow Lite 版本来改善我们的延迟,方法是牺牲一点模型的准确性。在我们的实验中,我们将选择 TensorFlow Hub 中的 MoveNet 模型的 Thunder 变体的 TensorFlow Lite 版本。MoveNet 模型将为我们提供 17 个 X-Y 坐标以及每个关键点的置信度得分。
地标关键点(图片来源: TensorFlow
数据类型
既然我们的人体地标检测模型已经定型,下一步就是关注数据。因为我们的目标是建立一个应用程序,我们将创建一种个人行走的数字签名,我们不太可能有大量的个人数据。在实际场景中,我们可能最多只能获得 6-10 秒的视频记录,我们将不得不继续工作。我们的目标非常类似于说金融机构可以收集最多 1 或 2 个客户的签名样本,而后者在他们那里开立新账户。
深入到我们的问题设置,我们将在视频的每一帧上运行我们的姿势估计模型,以生成图像中地标位置的时间序列数据,如下所示。
左:输入视频,中:检测,右:关键点(作者提供的 GIF)
现在,一个自然的问题出现了,我们如何在仅仅 10 秒钟的记录中描绘出一个人的行走模式。这是我们必须填补空白的关键之处,因为数据远远优于机器学习或深度学习模型。我们将尽可能多地模仿我们能想到的模式。
数据扩充
数据扩充是一种创建人工数据集以补充现有数据不足的技术。新生成的数据模拟了现有数据中模式的缺失部分,从而简化了训练期间 ML 模型的学习过程。我们已经在专门针对图像数据增强的上一篇文章中深入讨论了这个主题。
在我们试图解决的问题中,除了更少的数据量,我们甚至必须意识到这样一个事实,即个人的行走方式可能与我们的参考视频不同,期望个人总是保持类似的风格是不合逻辑的。尽管行走方式发生了变化,但总的来说,他/她身体各部分的运动和协调的整体动态应该在一段时间内(比如说过去几年)保持相当相似。然而,由于不同的情况,行走的缺失部分不同,从而产生不同的风格,我们将通过数据扩充来补充它。
a)快走:
拍摄参考视频时,个人可能不会为其他事情烦恼,但在现实生活中,他/她肯定会很匆忙。当人走得快时,帧之间的点对点过渡将不是平滑和连续的。为了模拟视频中的这种情况,我们可以定期随机跳过几帧。
快走(作者 GIF)
b)慢走:
与快走类似,一个人可能受伤了,或者可能有足够的时间,因此可能走得很慢。要模拟缓慢行走的情况,您需要在现有帧之间插入帧。这个慢速视频中的界标应该从其附近的帧中插入,使得人的运动看起来是连续的,并且在恒定 fps 速率下视频中帧数的增加使得人走得慢。
慢走(作者 GIF)
c)滞手:
很多时候,走路的时候,一个人可能一只手或者两只手拿着一个包。在这种情况下,拿着袋子的手不会移动太多,并且会在小范围内振动。我们通过保持对应于所选手的手腕、肘和肩的界标在小范围内移动来模拟这样的条件。
左:左手停滞,中:右手停滞,右:双手停滞(作者 GIF)
d)仅顶部姿势:
有时,记录目标运动的摄像机可能被安装在更高的高度或怪异的角度,这样它只能捕捉到目标的顶部姿势。我们需要通过向下移动人的标志来模拟这种情况,以便自然地看起来只有顶部姿势是可见的。
顶部姿态可见(GIF 由作者提供)
e)随机噪声:
记录目标运动的相机的另一个问题可能是它已经变旧了,并且其中的帧有故障。也有可能是拍摄对象的光线条件不合适。在这种情况下,一些标志可能随机消失,和/或姿态估计模型已经做出了非常低置信度的预测。这种类型的增强也比其他增强增加了更多的正则化效果,以防止我们的模型过度拟合。
故障关键点(作者提供的 GIF)
一个人可能不得不在中途停下来看看他/她的手机或者做一些其他的活动。在这种情况下,腿会保持静止,但身体的其他部位,如手和头可能会移动一点。
走走停停(作者 GIF)
g)左右行走:
到目前为止,在所有的视频中我们都假设拍摄对象会直线行走在镜头正前方。但是对象可能随机向左或向右移动。
左:向右移动,右:向左移动(作者 GIF)
h)混合组合:
无论录音多短,主题都不需要以一种方式改变他的常规风格。因此,我们必须将上述两个或多个条件组合起来,以串行甚至并行的方式模拟混合风格。
我)想更多的替代方案:
我们的模型只有在接受训练时看到的多样化数据量多,才会有好的表现。虽然上述一系列可供选择的行走方式将为我们的数据集增加多样性,但我们可以想到的提供更多数据扩充的场景类型从来没有硬性限制。
建筑模型
构建分类模型可能听起来很有趣,可以直接对主题进行分类,并将每个主题创建为一个类。但是这种方法有一个很大的缺点。我们正在考虑将这个解决方案扩展到几千个,分类器方法将产生一个大的密集矩阵(最后一层)。更重要的是,尽管我们已经做了所有的数据扩充,但我们仍然真的没有数据可以通过每个类别的表示学习来创建良好的边界。
为了解决上述问题,我们将使用一次性学习。通过一次性学习,我们将能够即时评估新人的视频,甚至无需将他们纳入培训。一次性学习利用相似性函数来比较两个实体彼此相似的程度。
我们的架构将包括[暹罗网络](https://en.wikipedia.org/wiki/Siamese_neural_network#:~:text=A Siamese neural network (sometimes,to compute comparable output vectors.)。由于我们的数据是基于时间序列的,我们将在暹罗网络中使用递归神经网络(GRU 或 LSTM)。我们将把一个参考视频的地标位置的时间序列数据和测试视频的地标位置的时间序列数据传递到暹罗网络中。然后,单独的潜在表示将通过密集层,以分别基于属于或不属于同一个人的视频来最大化或最小化分数。
高层架构(图片由作者提供)
结果
在我们的实验中,我们通过拍摄人们行走的视频来收集数据。我们甚至修剪了开源数据集中的免费可用的步行视频,我们还收集了几个视频,视频所有者允许我们使用他们的资源进行研究。
这是对一个测试视频的推断结果,该视频被提供给我们训练过的模型。与对象的参考视频的相似性分数显示在括号内。对于所有不正确的参考视频,该模型给出的相似性得分小于 0.5
左:测试输入视频,中:检测,右:带有相似性得分的结果(作者提供的 GIF)
这种方法是最好的吗?
不,一点也不。我们这个实验的全部目的是生成一种新型的数字签名。随着旨在加强安全性的创新技术的每一次进步,伪造者总会找到规避它的方法。目前,在 vid2vid 的帮助下,可以生成复制受害者步态的假视频。然而,结合面部识别的步态分析将是生成个人数字签名的良好机制。然而,我们相信这个实验中展示的简单方法可以形成一个很好的基线模型来推进事情的发展。
结论
在本文中,我们学习了如何在现有解决方案的基础上进行构建。我们利用姿态估计解决方案的优势,通过利用暹罗网络和数据增强来模拟许多可能的行走场景,来定制我们的步态检测。在本文中,我们展示了如何重新设计机器学习解决方案来解决其他用例。
密码
如果你想看看这个实验中使用的代码,你可以在这个 GitHub 库中找到它。
https://github.com/Prasad9/GaitDetection
你也可以看看我们以前的文章,解释高斯混合模型和期望最大化的算法。
生成催眠像素模式
原文:https://towardsdatascience.com/generating-hypnotizing-pixel-patterns-c1ab2a07d104
生成的像素模式。点击放大。图片作者。
一个关于对称、纤维艺术和埃舍尔的故事。
你还记得当你折叠一张纸,剪开它,展开后你会有一个漂亮的星星或雪花图案吗?当你在切割的时候,你没有很高的期望。但是展开后,对称就可以了,让你的纸看起来像魔术一样!重复相同的图案真的很强大,在艺术上做得非常好。在这篇文章中,我将与你分享如何用简单的技巧创造美丽的图案。
自制的“雪花”,我尝试了 20 年,真的很简单,它们看起来不漂亮吗?🤩图片作者。
对称
当某样东西在经过某种变换后保持不变或映射到自身时,称之为对称。下面是两种最常见的对称类型。
反射对称
当您可以通过线中的反射将对象映射到自身时,就会发生反射或线对称。
反射对称的例子,树在水中映射到它们自己。由亚当·伯基特拍摄。
旋转对称
如果您可以将一个对象围绕其中心点旋转 360 度,并且该对象映射到自身两次或更多次,这称为旋转对称。两次或更多,因为一次总是在 360 度完成。物体映射到自身的次数称为旋转对称的次数。你可以查一下序后图中的雪花,它们的旋转对称顺序等于 6。
一种特殊类型的旋转对称被称为点对称,当一个对象围绕其中心点旋转 180 度后映射到自身时,就会发生这种情况。检测点对称很容易:当一个物体上下看起来一样,像大写字母 H 或 N,它是点对称的。
你能认出这张图片中的反射、旋转和点对称吗?照片由巴迪·阿巴斯在 Unsplash 上拍摄
创建像素模式
像素模式被纤维艺术家广泛使用。你可以用它们来编织花边、缝纫和各种编织。你真的经常看到钻石图案,在下面的图片中,你可以发现一些不同形式的钻石。
analuisa gamboa 在 Unsplash 上拍摄的照片
手工创建像素模式需要花费大量时间。如果我们利用计算机的能力为我们工作会怎么样?有了计算机,理论上可以创造所有可能的像素模式!会不会带来新的有趣的模式?我尝试了许多不同的方法来创建模式。它们基于对称和重复的原则。可能性是无穷无尽的,因为它可以玩大小,颜色,以及旋转和重复的方式。
第一步很简单。在这一步中,从 n x n 个像素创建一个矩阵(在第一个例子中低于 6×6)。矩阵中随机填充了 0 和 1。零代表白色,一代表黑色。下面你可以看到一些生成的矩阵。他们没什么特别的,对吧?但是当矩阵被镜像、旋转和复制时会发生什么呢?
5 种不同的 6x6 随机黑白像素模式。在第一张图中,你可以看到从 0 和 1 矩阵到黑白矩阵的转换。
镜子,镜子…
如果我们取一个矩阵,向右镜像,然后向下镜像,然后水平和垂直复制几次结果,结果如下:
我们从左上角的 6x6 矩阵开始。然后向右镜像,向下镜像,复制。我们的第一个图案诞生了!
旋转,重复
而不是镜像(反射对称),当我们旋转矩阵时会发生什么(旋转对称)?我们可以用不同的方法来做这件事,每种方法都有不同的结果。下面,使用与上图相同的矩阵。旋转给了我们一个完全不同的模式。
矩阵向左旋转了 90 度。你也认得这种图案的花吗?
结合旋转和镜像也是可以的,旋转到另一边。下面一个 gif 用一些不同的方式生成图案。
以不同方式生成模式的示例。
使矩阵对角线对称
除了使用我们的方阵,我们还可以给它们增加一个“对称维度”。我们可以使矩阵对角对称。这给了我们这样的矩阵:
对角线具有反射对称性的 7x7 矩阵。
这给了我们这样的模式:
由 7×7 对角对称矩阵生成的模式。
通过反转颜色创建镶嵌
那埃舍尔呢。我希望你知道这位伟大的艺术家,他创作了最美丽、最迷人的画作。如果没有,那就去看看他的代表作(你不会后悔的)。他的特色镶嵌,在那里他完全用动物填充一个表面(大部分时间)真的很鼓舞人心。
我们能通过反转矩阵的颜色并复制它们来模仿这种效果吗?我尝试了两种不同的方法:水平切换矩阵的颜色,每一行都复制了相同的矩阵,第一行是黑/白,第二行是白/黑,第三行是黑/白,依此类推:
水平切换矩阵的颜色。带着一些幻想,我看到海马在左边,中世纪的军人在右边排成一行。
另一种方法是对角切换颜色,其中每个黑/白矩阵都被其自身的白/黑版本包围:
对角切换矩阵的颜色;我看到左边是飞龙,右边是公鸡。
它远非完美,但这些结果可以用作灵感和绘制自己的镶嵌图的起点。
使用模式
到目前为止,已经有两种模式出现了。一个在麦克拉梅,一个在框架编织。
macramé中使用的图案。图片作者。
使用左侧图案制作的框架编织。图片作者。
其他设置
玩不同数量的颜色、不同颜色的地图和不同大小的矩阵真的很有趣。下面是一些我特别喜欢的图案。正如我前面提到的,可能性是无穷无尽的!在这篇文章中,我只触及了皮毛。
结论
我希望你喜欢这个穿越模式和对称世界的小旅程。有了一些基本的 python(用于创建模式的编程语言)知识,就很容易受到启发,创建令人着迷的像素模式!而如果你没有任何 python 知识,就从剪刀和布开始吧!或者看一段分形的视频,其中的模式无限延续。
如果你想玩 python 代码,请联系我。目前,代码还没有公开。
如果你对我的纤维艺术感兴趣,在 Instagram 上关注我,看看我的作品。我不得不说大部分作品都不是由电脑程序激发的(目前还不是)😉).
感谢阅读!❤
本文是系列文章的第一篇。我以三种不同的方式使用了本文中生成的像素模式。第一种方法是在神经风格转移中回答这个问题:当我们将像素模式与图像和图片结合时会发生什么?我也把它们作为生成性对抗网络的输入。最后,我用它们来训练变分自动编码器,以发现潜在空间,并在生成的模式之间进行插值。点击链接阅读这些文章。(如果没有链接,我还没发表这些文章呢!)
别忘了 订阅 如果你想在我发表新文章时收到电子邮件。
利用 LSTM 神经网络生成新的卡尔纳蒂克音乐模式
卡纳蒂克音乐以一些积木的丰富循环模式为特色——使其成为探索基于人工智能的音乐生成的丰富游乐场。让我们看看 LSTM 是如何创作新的基本作品的
卡尔纳蒂克音乐中的积木
卡纳蒂克音乐是两种印度古典音乐形式之一(另一种是印度斯坦音乐)。与西方古典音乐不同,西方古典音乐中的作品被设定为一个特定的调(例如贝多芬的 C 小调第五交响曲)并且以多种调式为特征;卡尔纳蒂克的音乐作品大多被设定为一种独特的调式,称为 Raga。例如,Raga Sankarabharanam 对应于爱奥尼亚调式(通常称为大调音阶)。
由于这种对特定模式的关注,卡尔纳蒂克音乐以复杂的循环模式为特色,这通常可以被认为是建立在基本模式的基础上,几乎就像是重复片段的等价物。事实上,在卡纳蒂克音乐中,人们最先学到的东西之一就是萨拉里·瓦拉赛(Sarali varas ais),这是对基本的完整拉格(raga)的修改:
T2 [] S n d p | m g | r S
其中“s r g m p d n S”是组成完整音阶的 8 个音符。例如,如果我们谈论 c 大调音阶中的三卡拉巴拉那,对应的将是“C D E F G A B C”。“|”和“||”表示节拍小节。“||”代表一个周期的结束(这里是 8 拍),“|”代表一个特征音程。这个简单的上升和下降模式是第一个 Sarali Varisai,还有 13 个变化如下图所示:
Sarali Varisai 的另一个例子是:
S r g m | p d | S r | |
S r g m | p d | n S | |
S n d p | m g | S n | |
S n d p | m g | r S | |
本文的目标是训练一个人工智能模型,从 13 个 Sarali vari sai 中生成一个新的 Sarali Varasai,这些 Sarali vari sai 的长度都是 44 个字符(不包括第一个字符)。
LSTM 神经网络
长短期记忆(LSTM)神经网络被大量用于模拟循环模式和时间相关现象。LSTM 旨在提供长期和短期记忆来模拟序列模式。据报道,LSTM 架构是 20 世纪被引用最多的神经网络。
传统的神经网络在给定某个输入向量的情况下预测输出。例如,MNIST 数据集的特征是从 0 到 9 的数字化手写数字。人们可以开发一个基本的神经网络,以相当不错的精度来预测给定手写数字的数字,而不是在训练模型时看到的数字。
然而,这些神经网络没有对信息的持久性进行建模——这是一个缺点。例如,如果您想预测未来的一些事情,比如一天后的股票市场,如果您仅基于当前特征(相当于一幅图像中的所有像素)进行预测,您将会丢失有价值的信息。天气预报也是如此,甚至只是知道电影中接下来会发生什么。接下来的几分钟不仅仅取决于电影的当前位置,还取决于角色的历史。
这种持久性可以通过递归神经网络(RNNs)来建模。在一个简单的 RNN 中,有神经网络来模拟每一个时间步,其中 2 个输入是系统的先前状态;和一个考虑历史的向量。在下图中,您可以看到,在每一步 t,RNN 都接受一个输入 x_t,以及一个从过去的历史中累积的输入 v。这种模型的一个例子可以是例如一个句子的语言翻译——从英语到法语。在这种情况下,RNN 模型的想法是,不仅当前单词在建模相应的法语单词时有用,而且句子中前面单词的历史也有用。
rnn 的一个主要问题是它们不能处理大序列,这是由于在多次迭代中混合了非常小或非常大的数。LSTMs 通过为每个单元设置一个单独的“门”来克服这种限制,该门可以选择性地让信息通过。这样,他们可以通过忘记中间的一些无关紧要的连接,但同时跟踪一些重要的连接,来处理更长范围的依赖性。
用基本的 LSTM 细胞来模拟依赖于过去历史的循环模式|塞犍陀·维维克
为卡纳蒂克·萨拉里·瓦拉塞一代建立一个 LSTM 模式
首先,我定义了与 14 个不同的 Sarali Varasais 相对应的 14 个字符串,并创建了一个连接除第一个以外的所有字符串的列表(第一个字符串比其余的都小)。
x1=”s r g m | p d | n S || S n d p | m g | r s ||”
x2=”s r s r | s r | g m || s r g m | p d | n S || S n S n | S n | d p || S n d p | m g | r s ||”
x3=”s r g s | r g | s r || s r g m | p d | n S || S n d s | n d | s n || S n d p | m g | r s ||”
x4=”s r g m | s r | g m || s r g m | p d | n s || S n d p | S n | d p || S n d p | m g | r s ||”
x5=”s r g m | p , | s r || s r g m | p d | n S || S n d p | m , | S n || S n d p | m g | r s ||”
x6=”s r g m | p d | s r || s r g m | p d | n S || S n d p | m g | S n || S n d p | m g | r s ||”
x7=”s r g m | p d | n , || s r g m | p d | n S || S n d p | m g | r , || S n d p | m g | r s ||”
x8=”s r g m | p m | g r || s r g m | p d | n S || S n d p | m p | d n || S n d p | m g | r s ||”
x9=”s r g m | p m | d p || s r g m | p d | n S || S n d p | m p | g m || S n d p | m g | r s ||”x10=”s r g m | p , | g m || p , , , | p , | , , || g m p d | n d | p m || g m p g | m g | r s ||”
x11=”S , n d | n , | d p || d , p m | p , | p , || g m p d | n d | p m || g m p g | m g | r s ||”
x12=”S S n d | n n | d p || d d p m | p , | p , || g m p d | n d | p m || g m p g | m g | r s ||”
x13=”s r g r | g , | g m || p m p , | d p | d , || m p d p | d n | d p || m p d p | m g | r s ||”
x14=”s r g m | p , | p , || d d p , | m m | p , || d n S , | S n | d p || S n d p | m g | r s ||”l1=[]
l1.append([i for i in x2.split(" ") if i])
l1.append([i for i in x3.split(" ") if i])
l1.append([i for i in x4.split(" ") if i])
l1.append([i for i in x5.split(" ") if i])
l1.append([i for i in x6.split(" ") if i])
l1.append([i for i in x7.split(" ") if i])
l1.append([i for i in x8.split(" ") if i])
l1.append([i for i in x9.split(" ") if i])
l1.append([i for i in x10.split(" ") if i])
l1.append([i for i in x11.split(" ") if i])
l1.append([i for i in x12.split(" ") if i])
l1.append([i for i in x13.split(" ") if i])
l1.append([i for i in x14.split(" ") if i])
有 11 个不同的音符(' S ',' r ',' g ',' m ',' p ',' d ',' n ',' S ',' | ',',' ),其中' S '表示第一个音符的高音版本,' | '和' || '是特征音程,','只是一个延长音符的间隔。使用这个信息,我开发了一个热编码,其中 X 的维数是 X= (13,44,11),对应于 13 个萨拉里变奏,44 个音符组成每个萨拉里变奏(包括音程),11 对应于一个热编码维数。
y 是向左移动一步的 X;即时间步长 t 处的 Y 与时间步长 t+1 处的 X 相同。因此,游戏现在将预测给定前一个音符的下一个音符(LSTM 也考虑音符的历史)。
for i in range(0,len(l1)):
values = array(l1[i])
onehot_encoded = onehot_encoder.transform(values.reshape(len(values), 1))
X[i,:]=onehot_encoded
values=np.roll(values,-1)
onehot_encoded = onehot_encoder.transform(values.reshape(len(values), 1))
Y[:,i,:]=onehot_encoded
接下来,我们的想法是训练一个 LSTM 模型,根据之前的值(X)来预测后续的音符(Y)。
n_values = 11 # number of music values
reshaper = Reshape((1, n_values))
LSTM_cell = LSTM(n_a, return_state = True)
densor = Dense(n_values, activation=’softmax’)def djmodel(Tx, LSTM_cell, densor, reshaper):
"""
Implement the djmodel composed of Tx LSTM cells where each cell is responsible
for learning the following note based on the previous note and context.
Each cell has the following schema:
[X_{t}, a_{t-1}, c0_{t-1}] -> RESHAPE() -> LSTM() -> DENSE()
Arguments:
Tx -- length of the sequences in the corpus
LSTM_cell -- LSTM layer instance
densor -- Dense layer instance
reshaper -- Reshape layer instance
Returns:
model -- a keras instance model with inputs [X, a0, c0]
"""
# Get the shape of input values
n_values = densor.units
# Get the number of the hidden state vector
n_a = LSTM_cell.units
# Define the input layer and specify the shape
X = Input(shape=(Tx, n_values))
# Define the initial hidden state a0 and initial cell state c0
# using `Input`
a0 = Input(shape=(n_a,), name='a0')
c0 = Input(shape=(n_a,), name='c0')
a = a0
c = c0
outputs = []
# Step 2: Loop over tx
for t in range(Tx):
# Step 2.A: select the "t"th time step vector from X.
x = X[:,t,:]
# Step 2.B: Use reshaper to reshape x to be (1, n_values) (≈1 line)
x = reshaper(x)
#print(x.shape)
# Step 2.C: Perform one step of the LSTM_cell
a, _, c = LSTM_cell(inputs=x, initial_state=[a,c])
# Step 2.D: Apply densor to the hidden state output of LSTM_Cell
out = densor(a)
# Step 2.E: add the output to "outputs"
outputs=outputs+[out]
# Step 3: Create model instance
model = Model(inputs=[X, a0, c0], outputs=outputs)
return modelmodel = djmodel(Tx=44, LSTM_cell=LSTM_cell, densor=densor, reshaper=reshaper)history = model.fit([X, a0, c0], list(Y), epochs=200, verbose = 1)
如你所见,对于 200 个训练时期,损失下降了——这意味着该模型在 Saralivarisai 内更好地理解(并可能复制)模式。
在卡纳蒂克音乐序列上训练 LSTM |塞犍陀·维维克
经过训练后,LSTM 细胞可以用来产生音乐:
def music_inference_model(LSTM_cell, densor, Ty=44):
"""
Uses the trained "LSTM_cell" and "densor" from model() to generate a sequence of values.
Arguments:
LSTM_cell -- the trained "LSTM_cell" from model(), Keras layer object
densor -- the trained "densor" from model(), Keras layer object
Ty -- integer, number of time steps to generate
Returns:
inference_model -- Keras model instance
"""
# Get the shape of input values
n_values = densor.units
# Get the number of the hidden state vector
n_a = LSTM_cell.units
# Define the input of your model with a shape
x0 = Input(shape=(1, n_values))
# Define s0, initial hidden state for the decoder LSTM
a0 = Input(shape=(n_a,), name='a0')
c0 = Input(shape=(n_a,), name='c0')
a = a0
c = c0
x = x0 outputs = []
# Step 2: Loop over Ty and generate a value at every time step
for t in range(Ty):
# Step 2.A: Perform one step of LSTM_cell. Use "x", not "x0" (≈1 line)
a, _, c = LSTM_cell(x, initial_state=[a,c])
# Step 2.B: Apply Dense layer to the hidden state output of the LSTM_cell (≈1 line)
out = densor(a)
# Step 2.C: Append the prediction "out" to "outputs". out.shape = (None, 90) (≈1 line)
outputs.append(out)
#print(out.shape)
# Step 2.D:
# Select the next value according to "out",
# Set "x" to be the one-hot representation of the selected value
# See instructions above.
x = tf.math.argmax(out,axis=-1)
#print(x.shape)
x = tf.one_hot(x,n_values)
# Step 2.E:
# Use RepeatVector(1) to convert x into a tensor with shape=(None, 1, 90)
#print(x.shape)
x = RepeatVector(1)(x)
# Step 3: Create model instance with the correct "inputs" and "outputs" (≈1 line)
inference_model = Model(inputs=[x0, a0, c0], outputs=outputs)
return inference_modelinference_model = music_inference_model(LSTM_cell, densor, Ty = 44)
然后我们可以给推理模型随机初始化的输入:
def predict_and_sample(inference_model, x_initializer = x_initializer, a_initializer = a_initializer,
c_initializer = c_initializer):
"""
Predicts the next value of values using the inference model.
Arguments:
inference_model -- Keras model instance for inference time
x_initializer -- numpy array of shape (1, 1, 90), one-hot vector initializing the values generation
a_initializer -- numpy array of shape (1, n_a), initializing the hidden state of the LSTM_cell
c_initializer -- numpy array of shape (1, n_a), initializing the cell state of the LSTM_cel
Returns:
results -- numpy-array of shape (Ty, 90), matrix of one-hot vectors representing the values generated
indices -- numpy-array of shape (Ty, 1), matrix of indices representing the values generated
"""
n_values = x_initializer.shape[2]
given x_initializer, a_initializer and c_initializer.
pred = inference_model.predict([x_initializer,a_initializer,c_initializer])
# Step 2: Convert "pred" into an np.array() of indices with the maximum probabilities
indices = np.argmax(pred,axis=-1)
# Step 3: Convert indices to one-hot vectors, the shape of the results should be (Ty, n_values)
results = to_categorical(indices, num_classes=n_values)
return results, indicesx_initializer = np.random.rand(1, 1, n_values)
a_initializer = np.random.rand(1, n_a)
c_initializer = np.random.rand(1, n_a)results, indices = predict_and_sample(inference_model, x_initializer, a_initializer, c_initializer)notes=[]
for i in range(0,len(results[:,0])):
inverted = label_encoder.inverse_transform([argmax(results[i, :])])[0]
notes.append(inverted)print(' '.join(e for e in notes))
这是 LSTM 预测的:
d | n S | d p || d d p m | p,| p,|| g m p d | n d | p m || g m p g | m g | r s || S n d
有趣的是,它很好地生成了一个结构良好的 2 行组合,尽管是在一个周期的中间开始的。这可能是因为我们如何通过输入前一个音符来训练 LSTM 细胞——这有效地使用了周期性边界,并使其成为一个循环模型。
通过将“n d”放在前面,我们得到一个新的 Sarali Varisai,如下所示:
S n d d | n S | d p ||
d d p m | p,| p,| |
g m p d | n d | p m | |
g m p g | m g | r S | |
这是我唱这首歌的录音:
结论
在本文中,我将展示如何使用 LSTM——一种经典的递归神经网络架构来生成基本的 Carnatic Saralivarse 模式。由于这些基本模式构成了更复杂的卡尔纳蒂克音乐作品的基础,使用先进的人工智能算法来生成更复杂的作品可能会令人兴奋。
LSTMs 仍然受到递归神经网络固有的顺序性质的困扰——这也使得它们很难并行进行计算。最近,变形金刚席卷了自然语言处理领域。就像在阅读或翻译一个句子时,人们需要注意句子的各个部分一样,变形金刚被设计成使用自我注意来消除重复连接,并提供同时关注多个单词的能力。同样,如果有足够多的数据,变形金刚也可以用来理解和创作新的音乐。
如果你在音乐和 AI 的交汇点有其他想法,欢迎联系!
您可以在这个 GitHub repo 中找到代码:
https://github.com/skandavivek/LSTM-Carnatic-Music-Generation
如果你还不是中会员,想支持我这样的作家,可以通过我的推荐链接随意报名:【https://skanda-vivek.medium.com/membership】
关注我 如果你喜欢这篇文章——我经常在数据科学、音乐、安全和社会的界面上写作。
每周数据透视 订阅此处 !
参考文献:
利用机器学习生成科学论文标题
汉斯·雷尼尔斯在 Unsplash 拍摄的照片
动手操作笔记本关于一个生成式机器学习模型,它能够创建新的科学论文标题
作为一名研究人员(或者学生,或者两者兼而有之),我在过去的三年里花了很多时间阅读和研究的论文。
我的领域是机器学习和数据科学,当然这是一个巨大的领域,有很多不同的领域可以覆盖。我注意到一件有趣的事情,即使是最不同的文章也有相似的标题结构:
“某样东西某样东西某样东西某样东西”
当然,有很多反例(机器学习中一篇非常著名的文章叫做“注意力是你所需要的全部”)但这种设置标题的方式确实引人注目,而且具有明确性。
有一个写标题的“公式”的事实给了我一个想法。让我们建立一个机器学习方法,编造不存在的新标题。那就这么办吧!
附注:这种方法背后的技术可能很复杂,您可能不熟悉,但实现起来很容易。如果你想了解更多关于它是如何工作的,可以访问这个 网站 【原代码来源】或者看看我前段时间写的 这个中等故事 。
1.图书馆
好消息是这个图书馆很有名,你可能很熟悉。坏消息是,我们将使用 Tensorflow (j 只是开玩笑:))
公平地说,Tensorflow 是一个非常强大的库,但安装它并使它按照您想要的方式工作可能会很棘手。所以不要绝望,必要的时候使用 StackOverFlow。
无论如何,这是你需要的:
很简单,对吧?在某一点上,我们将拥有 Tensor.numpy(),这就是为什么您需要 tf.enable_eager_execution()。
注意:启动程序时需要运行 tf.enable_eager_execution(),一定要小心!现在就做!
2.数据集
当然,为了训练我们的机器学习算法来编写新标题,我们需要一堆旧标题来训练它。
我在这里 找到了一个 Arxiv 的书名和文章集 。下载并运行以下代码导入您的数据:
注意:这个模型当然是一个小例子,说明你可以用更强大的计算能力做些什么。为此,我使用了一个非常小的模型,只有几行(500)。如果你有足够的计算能力,并且接受你可能要等很久才能得到结果的事实,你可以随意使用更大的模型和更多的数据。
2.1 数据预处理
我们需要做一系列的操作来把这个字符串列表转换成机器可以真正读懂的东西。深入细节并不十分有趣,但简单地说,我们正在做的是向量化我们的文本,这意味着将字符串转换成值列表。
还是那句话,如果你想了解更多关于它是如何工作的,可以访问这个 网站 【原代码来源】或者看看我前段时间写的 这个中等故事 。
如果您想跳过这一部分,转到机器学习模型,只需运行此代码并继续:
3.机器学习
我们要用的技术叫做神经网络。具体来说,我们将使用一种称为递归神经网络的神经网络。这个想法是,给定一个特定的输入字符串,网络能够得到最有可能出现的字符串。
很不错吧?
可以使用下面几行代码构建模型:
当然,每个被定义为“机器学习”的算法都需要一些训练。每个训练与某个损失函数配对,在这种情况下,损失函数如下:
完美!现在一切都准备好了,让我们开始训练:
在训练过程中,我们定义的损失函数具有递减值,这很好!对于每个时期,模型存储在某个文件夹中,我们希望使用该文件夹中的最佳模型:
4.结果
虽然你们中的一些人可能喜欢所有关于机器学习的故事和事情,但我坚信你们大多数人是为了最终的结果而来。而且绝对公平!那我们走吧。
要生成新文本,您将需要此函数:
这正是我们所说的:挑选我们输入后更有可能被验证的字符。尽管如此,我们在函数中还有另一个输入,那就是温度。这个值给你产生的文本的“创造性”。换句话说,如果你使用温度=1,你会得到一个非常有创意的文本,尽管它可能没有任何意义。如果使用 temperature=0.1,您将得到一个可能更有意义的文本,但它可能与数据集中已有的文本非常相似。
让我们用 5 种不同的温度和 5 种不同的输入产生 5 种不同的文本:
让我们把它们打印出来!😃
因此,如果我们过滤掉一点,我们会得到以下结果:
“针对图像分割注意力的深度学习”
“深度学习的方法控制上下文”
“面向学习的图像深度强化学习”
“使用 Imagence 进行图形学习的机器学习”
一个新的隆布拉:为数据设置 giog-Prouge Impai”
我觉得这太棒了。最后一句纯属扯淡,因为我们设温度= 1。尽管如此,5 次中有 4 次我们有以下标题:
“某样东西某样东西换某样东西”
这正是我所期待的!
4.结论
我已经研究这个生成模型有一段时间了,我可以说它绝对是现象级的。同样,我们可以有比这好得多的结果!如果我们知道我们想要的结果是什么,我们可以尝试清理数据集并稍微更改我们拥有的文本,我们可以使用更多的文本和更强大的模型(例如增加 RNN 单位和图层的数量)。
尽管如此,我相信使用这个管道,您将在文本生成方面取得巨大的成果。
如果你喜欢这篇文章,你想知道更多关于机器学习的知识,或者你只是想问我一些你可以问的问题:
A.在 Linkedin 上关注我,我在那里发布我所有的故事
B .订阅我的 简讯 。这会让你了解新的故事,并给你机会发短信给我,让我收到你所有的更正或疑问。
C .成为 推荐会员 ,这样你就不会有任何“本月最大数量的故事”,你可以阅读我(以及成千上万其他机器学习和数据科学顶级作家)写的任何关于最新可用技术的文章。
再见。😃
从每一篇文本中提取最先进的见解
不需要 OpenAI GPT-3 和类似的工具来为你的自然语言处理任务获得最先进的语义文本洞察。
来源:由作者创作
在这篇文章中,我将展示大型语言模型,如 GPT-3,并不能通过其密集的文本嵌入为许多 NLP(自然语言处理)任务生成最佳的语义文本洞察,以及如何让每个人都可以用广泛可用的硬件生成最先进的嵌入。
密集文本嵌入是编码单词含义的文本的矢量表示。这样,意义相近的词在矢量表示中是相近的。这些嵌入对于复杂的任务非常有用,例如比较文本内容的相似性、语义搜索、释义挖掘等。在过去的几年中,语言模型的规模呈指数增长,由于资源的限制,许多从业者几乎不可能用最新的模型进行实验。我可能有一个解决方案给你😉。
来源:蒸馏伯特,伯特的蒸馏版本:更小、更快、更便宜、更轻——https://arxiv.org/pdf/1910.01108.pdf
问题陈述和前进方向
我们大多数人面临的限制是,我们没有权限或财力部署大型 GPU/TPU 集群来运行大型语言模型,如 T5、MPNet 或 GPT-3。为了克服这些限制并获得最先进的文本嵌入,我在以下两个步骤上花了很多时间,我想与你们分享:
- 基于所有相关语言模型的性能和模型参数的数量对其进行比较和排序,以找到最有效的预训练模型。
- 应用并比较不同的微调技术如何提高预训练模型的性能。
动机
当然,我有自己的理由生成高质量的文本嵌入。我的目标是使用文本嵌入作为我自己的模型的输入,以帮助决定我自己的金融资产选择。由于我收到了很多公开我的模型的请求,我发起了一个名为 PinkLion 的项目。
我设计了 PinkLion,通过提供对潜在预测模型的访问,实现动态投资组合优化和资产分析。这些模型可以访问成千上万只股票、基金/ETF 和加密货币的日常资产数据。
请随意尝试,并分享反馈🙏。(还处于粗略状态) www.pinklion.xyz
1.根据性能和规模对语言模型进行比较和排序
作为第一步,我们必须对现有的语言模型、它们的性能以及它们的规模有一个很好的了解。因此,目标是基于各种基准测试任务比较不同的语言模型,并确定它们相对于其参数数量的表现如何。
模型性能的定义—胶水和强力胶
尽管阅读了大量研究论文,但令人惊讶的是,很少发现跨多个基准任务(如 SentEval [1]、GLUE [2]、SQuAD[3]、RACE [4]或 SWAG [5])的完全透明的模型评估。在进一步研究这个话题后,我得出结论,认为【通用语言理解评测】【2】似乎是最广泛使用的评测语言模型性能的基准。
GLUE 是一个工具和数据集的集合,用于评估一个模型在一组九个自然语言任务中的性能。这九项任务集中在文本相似性和释义、分类和推理领域。
有了这个结论,我开始收集所有相关语言模型的 GLUE 评估结果。尽管 GLUE 被广泛使用,但是仍然很难获得所有相关的分数,尤其是对于较小的模型版本。本文中的所有后续数字都是按照以下顺序从 GLUE 排行榜、模型的研究论文或相应模型的 Github 页面获得的。
对于每个对如何详细组成 GLUE [2] 基准感兴趣的人来说,这里是组成基准的任务名称、缩写、描述和度量。
如果您对详细的粘合任务不感兴趣,请跳到下一节“模型比较和排名”。
- 语言可接受性语料库(CoLA) :判断一个句子的语法是否正确。— 指标:马修相关性
- 斯坦福情感树库(SST-2) : 判断句子是正面情感还是负面情感。——公制:* 准确度*
- 微软研究院释义语料库(MRPC) :判断两个句子是否是彼此的释义。— 指标:准确性/F1
- 语义文本相似度基准(STS-B) :确定两个句子的相似度,分值从 1 到 5。— 指标:皮尔森相关性/斯皮尔曼相关性
- 问答式自然语言推理(QNLI) :判断问题的答案是否在第二句话中。(这个数据集是从班数据集构建的。)— 度量:准确度
- Quora 问题对(QQP) :判断两个问题是否语义等价。— 指标:准确性/F1
- 多体裁自然语言推理(MNLI) :确定一个句子是否包含、反驳或与一个给定的假设无关。(该数据集有两个版本,一个版本的验证和测试集来自同一个发行版,另一个版本称为不匹配,其中验证和测试使用域外。— 指标:准确度
- 识别文本蕴涵(RTE) :确定一个句子是否包含给定的假设。— 指标:准确度
- Winograd 自然语言推理(WNLI) :判断含有匿名代词的句子和替换了该代词的句子是否包含。(该数据集是从 Winograd 模式挑战数据集构建的。)— 度量:准确度
通过取所有任务的平均值来计算最终的粘合分数。像 MRPC 和 STS-B 这样有两个跟踪指标的任务首先被平均,然后被认为是该任务的单一得分。
模型比较和排序
在定义了我们如何度量模型的性能之后,这里是收集到的不同语言模型家族的见解。下表显示了每个模型的参数数量、模型的内存存储量(这些数字已从 Tensorflow Hub 或相应模型的 Github 页面获得)以及上文所述的粘合分数。最后,**最后一列模型性能/大小比率显示了基于模型的粘合分数及其参数数量计算的比率。**
模型性能/尺寸比=粘合分数/模型参数数量
换句话说,如果一个模型在 GLUE benchmark 上用更少的参数表现得更好,那么这个比率就更高。因此,型号性能/尺寸比栏中的数字越高越好。
从整体来看,很明显,随着模型尺寸的增加,性能/尺寸比迅速降低。因此,模型中每个增加的参数都有很强的收益递减。简单看一下 Google【11】已经公布的 T5 车型家族,这一点就变得非常明显。
最小的 T5 型号 T5-small 拥有 6000 万个参数,粘合分数为 77.4,性能/尺寸比为 1.29。相比之下,最大的 T5 型号 T5–11B 的胶合分数为 90.3,比 T5-small 高 12.9 分。
为了实现 12.9 点或 16.6%的分数增加,模型大小必须从 6000 万个参数增加到 110 亿个参数,即增加了 18300%🤯。
现在你可能想知道为什么 GPT-3 没有出现在上表中。原因是 GPT-3 不是在普通胶水基准上正式评估的,而是在强力胶【6】基准上评估的。
SuperGLUE 是在 image GLUE 之后创建的一个新基准,它具有一组新的更难的语言理解任务。
幸运的是,强力胶排行榜列出了普通胶水基准测试中的 GPT-3 和多个型号(伯特-大号、罗伯塔-大号、T5–11B)。比较伯特-基地和 GPT-3,GPT-3 只有更高的强力胶分数 2.8 分(69.0 → 71.8)。尽管 GPT 3 号的参数是它的 1605 倍。同样,RoBERTa-large 和 T5–11B 的参数比 GPT-3 少,但得分更高,分别为 84.6 和 89.3。
了解到真正的大型模型,如 GPT-3 在强力胶上的表现优于其他模型(罗伯塔-大型和 T5–11B),我们可以假设 GPT-3 在普通胶水基准上也优于这些模型。
回到我们正常的 GLUE 比较,我们可以看到实现最佳性能/大小比的语言模型家族是 ALBERT (A Lite BERT) 模型。
ALBERT-base 模型实现了的 6.986 ,是所有测试语言模型中最高的比率。高比率可以解释为非常好的胶合分数 83.9,同时是所有测试模型中最小的,只有 1200 万个参数。因此,ALBERT-base 提供了每个部署参数的最佳性能。由于参数数量很少,艾伯特-基和艾伯特-大可以很容易地在 K80 或 P100 GPU 上训练。**
模型比较的结论
当用有限的资源生成密集文本嵌入时,最好考虑的模型族是 ALBERT 族。在所有评估的语言模型中,ALBERT-base 和 ALBERT-large 具有最好的性能/大小比。两者都有 1200 万和 1800 万个参数,胶合分数分别为 83.8 和 85.7。这使得这两种型号成为最小的型号之一,同时提供了与 T5 型号相当的性能,而 T5 型号则大得多。此外,GPT-3 在 SuperGLUE 基准测试中表现不佳,如果下游任务与文本生成无关,则不应该选择它作为模型。完整评价可以在 这里 找到。
在接下来的章节中,我将展示如何使用不同的微调技术来进一步提高 ALBERT-base 的性能。
2.通过应用和比较不同的微调技术来提高预训练模型的性能
在选择了 ALBERT-base 作为我们的语言模型之后,我们现在想要微调一般训练的模型,使它更适合我们自己的应用程序。有许多方法可以微调模型以生成文本嵌入。然而,不同的微调方法会导致非常不同的定性结果。因为比较不同的微调技术需要付出大量的努力,所以我想分享我自己的结果,以便将来其他人更容易理解。
在这一节中,我们将比较三种不同的微调过的 ALBERTs,它们都是用不同的微调技术改变的。这里是不同方法和结果的初步概述。
来源:比较 ALBERT-base 微调架构—全面评估此处 —由作者创建
- ****冻结艾伯特:冻结艾伯特由常规预训练艾伯特基础模型组成,该模型被冻结,因此在微调时不会更新其参数。此外,它还有一个可训练的分类头,包括一个 dense 和一个 softmax 分类层。该模型将作为我们的基线基准,因为它将代表 ALBERT-base 在没有重大微调的情况下的表现。
- 经过训练的艾伯特:经过训练的艾伯特和冰冻的艾伯特有着完全一样的架构,然而,所有的组件都是完全可以训练和微调的。这意味着 ALBERT-base 组件和分类头(密集层+ Softmax 层)可以针对分类微调任务完全调整它们的权重。
- ****暹罗阿尔伯特:暹罗阿尔伯特的灵感来自通用暹罗框架【7】,在我们的例子中,它包括三个相同的基于阿尔伯特的网络,这些网络使用共享权重。除了三个子网络之外,还为分类微调任务添加了由 softmax 层表示的分类头。
资料组
为了了解不同的微调方法在现实世界中的比较,我选择了 HuffPost 新闻分类数据集【8】作为文本语料库。该数据集包含 2012 年至 2018 年间从《赫芬顿邮报》收集的 200853 条新闻记录,标题和描述分布在 41 个独特的新闻类别中。因此,这个数据集非常适合于查看我们可以在多大程度上针对分类任务对模型进行微调。这是数据集的一个示例。
来源:赫芬顿邮报新闻分类数据样本——由作者创建
我们微调任务的目标是根据给定的新闻标题预测新闻类别。
数据准备
由于我们希望仅使用新闻标题作为输入来预测新闻类别,因此我们首先希望通过计算和绘制每个新闻标题的字符和字数分布来获得数据集的更好概述。
对于新闻标题的字数,最小字数为 0,最大字数为 44,平均一个新闻标题有 9 个字。然而,从分布情况来看,我们可以得出,由于许多标题有大量的单词,所以平均值被夸大了。
新闻标题的字符计数的分布显示了类似的情况,其中大量标题具有大量字符。对于字符计数,0 是最小计数,320 是最大计数,平均一个标题有 57 个字符。
来源:赫芬顿邮报新闻标题单词和字符的概率分布——由作者创建
为了提高输入数据的质量,我们删除了每个标题少于 10 个单词的所有新闻记录。应用此条件将记录数量从 200853 减少到 189339。****
删除标题少于 10 个单词的所有记录,并应用 80/20 训练和验证数据集拆分后,类别分布如下所示。
来源:赫芬顿邮报训练和验证拆分新闻类别——由作者创建
现在很明显,我们有一个高级别不平衡,与政治是占主导地位的阶级。这种偏差将在训练过程中通过给每个类分配不同的权重来纠正(稍后会有更多的介绍)。
比较微调技术
在对我们的 3 个模型(冰冻艾伯特、训练艾伯特、暹罗艾伯特)进行了训练之后,我们现在能够更详细地查看模型的性能,该训练数据集具有 160682 条对新闻类别进行分类的记录。
以下所有指标均来自验证数据集,该数据集显示,从整体来看, 暹罗阿尔伯特的性能最好,,其次是受过训练的阿尔伯特,然后是冷冻阿尔伯特。
**暹罗阿尔伯特几乎在所有指标上表现最佳
首先比较冻结的艾伯特和训练的艾伯特,前者使用基于艾伯特的网络的未改变的权重,后者由于赫芬顿邮报的训练过程而具有微调的权重,我们已经可以看到显著的性能提升。经过训练的艾伯特的准确率为 58%,F1 得分为 60%,AUC-micro 值为 0.996,而冷冻艾伯特的准确率为 32%,F1 得分为 35%,AUC-micro 值为 0.905。
来源:比较 ALBERT-base 微调指标—全面评估此处 —由作者创建
第二步比较siame ALBERT我们可以看到,siame 版本在几乎所有指标上都优于其他两款车型。暹罗阿尔伯特在验证数据集上的准确率为 63%,几乎是冷冻阿尔伯特准确率的两倍,也高于训练过的阿尔伯特的准确率。综上所述,Siamese ALBERT 的准确率为 63%,F1 分数为 64%,AUC-micro 值为 0.946,略低于训练过的 ALBERT 方法的同等分数。
因为我们最初的目标是创建最好的文本嵌入,这里是由 Siamese ALBERT 生成的嵌入。正如我们所看到的,我们确实为不同的新闻标题类别实现了不同的聚类。暹罗嵌入的一个有趣的特征是,它不仅为不同的类别创建聚类,而且具有类似内容的新闻标题类别,如 T 世界邮报、世界邮报和世界新闻在嵌入空间中彼此靠近。
来源:暹罗阿尔伯特新闻标题嵌入——由作者创建
不同评估步骤和嵌入代的完整代码可在我的Github上获得。****
暹罗阿尔伯特的实现
上面介绍的 Siamese ALBERT 是在硬件受限的情况下产生高质量文本嵌入的最佳方法。因此,我也想分享一下暹罗阿尔伯特是如何工作的,以及如何实现这个网络并重现上面的结果。**
让我们先来看看隐藏在单个连体阿尔伯特盒子后面的架构的高级表示。Siamese ALBERT 由 3 个基于 ALBERT 的子网组成,这些子网相互分担重量。**
暹罗系统的输入是文本标题和新闻类别的三元组。该三元组由来自相同类别的两个标题(例如,类别世界新闻)和来自不同类别的一个标题(例如,类别政治)组成。文本标题通过三个子网络,产生三个输出,这些输出被传递到三元半硬损失函数,该函数试图使两个相似类别的输入更接近,同时试图在嵌入空间内使具有不同类别的第三个输入的输入远离。尽管看起来三元连体结构有 3 个网络,但它只有一个网络为每个子网提供相同的权重。因此,子网彼此共享它们的权重。
三重损失
在 2015 年【9】的 Face Net 研究论文中引入了三重损失,以找到一种可扩展的方法来处理人脸识别等任务,这些任务的特点是类别数量多而每类样本数量少。
来源:三重损失嵌入图—由作者创作
一般来说,三联体损失-连体结构可以用于非常不同的目的,例如文本和图像相似性任务。这可以归因于这样的事实,即它允许直接学习从其输入到紧致欧几里得空间的映射,其中距离直接对应于相似性的度量。换句话说,三重损失最小化锚和正之间的距离,最大化锚和负之间的距离。锚和阳性具有相同的类别,而阴性是从不同的类别中取样的。三重态损失函数由欧拉距离函数表示。**
来源:三重损失图解—https://www.tensorflow.org/addons/tutorials/losses_triplet
暹罗阿尔伯特实施
但是在我们迷失在理论之前,让我们从实现开始。一如既往,第一步是加载数据集,在我们的例子中,它是《赫芬顿邮报》的数据集,随后,清理和准备数据,以便可以提供给我们的网络。这些步骤已经在前面的章节中简要描述过,用于数据准备的代码结合完整的实现可以在 这里 找到。
在清理和准备好我们的数据之后,我们加载 ALBERT tokenizer 并设置一个数据生成器函数,以实现一个灵活的实现,将我们的数据批量提供给 siamese 模型。
接下来,我们将从 Tensorflow Hub 加载预训练的 ALBERT-base 模型。在这个例子中,暹罗艾伯特网络采用三个输入(单词 _ id、掩码、类型 _ id)。这些不是之前三重损耗输入的锚、正和负,而是艾伯特模型需要的正常输入。在 3 个并行输入之后,加载的艾伯特基网络被合并到暹罗艾伯特中作为单一层,我们将从其中提取汇集输出权重并将它们馈送到 64 维密集层中。密集层之后是 L2 归一化层,而最后的 L2 层对于训练成功至关重要,否则网络会有不稳定的训练迭代和问题收敛。****
你现在可能想知道:为什么没有 3 个独立的子网络或不同的输入流来为网络提供一个锚定的、积极的和消极的类示例?
事实上,我分享的实现不需要配置由 3 个子网络组成的网络架构。在定义我们网络的损耗函数时,我们使用了 Tensorflow Addons 三重半硬损耗【10】函数实现,它在后台处理这个问题。损失函数确保在我们摄入的批次中找到三元组。这个过程被称为在线学习,并确保只训练(在我们的半硬损失函数的情况下)半硬三元组。
此外,我们定义了一个回调函数来保存我们的训练运行。最后,我们用 model.fit()启动训练运行。这些都是训练一个合理的连体网络所需的步骤。完整的代码可以在 这里 访问。
结论
当面临硬件限制时,ALBERT-base 和 ALBERT-large 是生成高质量文本嵌入的优秀语言模型。这两种模式都拥有目前所有可用的预训练语言网络中最好的性能/规模比之一。GPT-3 不是关于许多语言任务的最先进的模型,因此除了语言生成应用之外,不应该考虑使用它。
使用微调技术来进一步提高模型在各种指标上的性能,与传统的微调方法相比,siamese 架构在性能上有所提高,传统的微调方法只是在预训练网络之后添加微调头。
我希望我的发现可以帮助人们在未来建立自己的项目和产品。
感谢您的阅读!
-简·施密茨
参考资料:
[1] SentEval:句子嵌入评估工具包https://github.com/facebookresearch/SentEval
[2]胶水:通用语言理解评测https://gluebenchmark.com/
****[3]小队:史丹福问答数据集【https://rajpurkar.github.io/SQuAD-explorer/ ****
[4] RACE:来自考试的大规模阅读理解数据集https://arxiv.org/pdf/1704.04683.pdf
[5] SWAG:基于常识推理的大规模对抗性数据集https://arxiv.org/pdf/1808.05326.pdf
[6]强力胶:通用语言理解系统的一个更棘手的基准https://arxiv.org/pdf/1905.00537.pdfhttps://super.gluebenchmark.com/
[7]使用“暹罗”时间延迟神经网络的签名验证https://proceedings . neur IPS . cc/paper/1993/file/288 cc 0 ff 022877 BD 3d f 94 BC 9360 B9 c5d-paper . pdf
[8]米斯拉,里沙卜。(2018).新闻类别数据集。10.13140/rg . 2 . 2 . 20331.18729 .https://www . ka ggle . com/datasets/RMI SRA/news-category-dataset—Misra,Rishabh & Grover,Jigyasa。(2021).为 ML 雕刻数据:机器学习的第一步。
****[9] FaceNet:人脸识别和聚类的统一嵌入【https://arxiv.org/pdf/1503.03832.pdf ****
****[10]tensor flow Addons loss:TripletSemiHardLoss【https://www.tensorflow.org/addons/tutorials/losses_triplet ****
[11]用统一的文本到文本转换器探索迁移学习的极限https://arxiv.org/pdf/1910.10683.pdf
生成合成数据以训练 OCR 学习算法
编码和解释合成数据生成的过程,以提高 OCR 学习算法的准确性
数据扩充的效果。来源:作者。
提高低偏差学习算法性能的最可靠方法之一是在大量数据上对其进行训练。尽管获取额外数据的过程看起来可能是一项昂贵的努力,但存在一种令人着迷的技术,允许机器学习从业者生成潜在的无限量的人工数据: 人工数据合成 。
在本文中,我将展示用于生成合成数据的技术,并将它应用于一个众所周知的光学字符识别问题。
目录:
- 人工数据合成
- MNIST 光学字符识别
- 生成人造图像
- 模型选择和培训
- 评估数据扩充的效果
人工数据合成
人工数据合成是以编程方式生成新数据实例的过程,这些数据实例有效地近似底层真实数据。真实数据可以定义为通过真实生活调查或直接测量收集的数据,而人工数据是作为计算机模拟的结果收集的。基本要求是模拟数据反映原始数据的统计特性。截至目前,人工数据生成并不适合每一个机器学习问题,通常需要一定的创造力和洞察力才能将其应用于特定问题。
这种做法可以分为两种截然不同的变化:
- 从头开始生成数据:我们没有任何数据,我们希望创建一个近似真实数据的训练集
- 从小规模的训练集生成数据:我们已经拥有一些训练数据,但我们希望增加它的规模
在本文中,我将探讨后者,因为我已经可以访问数据集。
人工数据合成的基本原理是多样化的。如介绍中所述,一个目标是增加训练规模,作为提高算法性能的手段。另一方面,可以执行相同的技术来保护敏感数据,例如个人身份信息(PII)或个人健康信息(PHI)。最后,合成数据在那些数据稀缺或收集成本高昂的应用中发挥着重要作用。这方面的一个例子是异常检测或欺诈检测:欺诈交易的样本很少,人为伪造它们可能很方便。
马和人的人造图像。来源:谷歌开发者
合成数据经常被用于开发计算机视觉中的学习模型,因为在一些情况下,用计算机生成的数据训练的算法在现实世界中也是有效的。这方面的一个应用由的这篇研究论文来表示,其中使用动物的 CAD 模型来训练学习模型。在这个谷歌开发者笔记本中提供了一个实际应用,其中一个模型用马和人的人工图像进行训练。
最后但同样重要的是,数据扩充在避免过度拟合方面带来了有益的效果。过度拟合模型意味着算法在处理某种类型的数据时变得非常出色,但是,如果我们要求模型处理稍微不同类型的数据,模型的性能将会迅速下降。解决这个问题的一个简单方法是在学习阶段向模型提供更多不同的数据。
考虑这个简单的二进制分类任务:识别狗和猫。如果在训练数据中,所有示例都表示站立或坐着的猫,则模型可能很难正确分类下蛋猫的图像,如下图所示。
产卵猫。来源: Unisplash
一个简单的解决方案是将训练集中的图像旋转 90 °,使它们看起来像下蛋的猫。以这种方式,学习算法更有可能识别更广泛种类的猫图像的特征。
MNIST 光学字符识别
人工数据在像光学字符识别 (OCR)这样的应用中尤其有益,在这些应用中,训练集的大小在模型的广义准确性中起着重要作用。出于这个原因,我想探索合成数据可以做些什么来提高基于 MNIST 库的学习模型的性能。
光学字符识别是一种能够将印刷或手写字符转换为机器可读文本的技术。许多 OCR 模型的训练基于 MNIST 数据集,这是一个手写数字的大型数据库。它包含 70,000 个 28x28 像素的灰度色图。
在这篇文章中,我开发了一个基于 KNN 分类器算法的简单数字识别学习模型。现在的目标是评估一个更大的训练集如何对模型的准确性有价值。
生成人造图像
考虑到训练集由非常小的图像组成,它可以通过在 4 个主要方向(左、右、上、下)上移动每个图像 1 个像素来扩展。通过这种方式,原始数据集被复制了五份。用于图像数据扩充的其他技术是旋转、剪切、缩放、镜像等等。
第一步是导入数据集并检查数据样本的外观。
# Import the library
from sklearn.datasets import fetch_openml# Import the dataset
mnist = fetch_openml('mnist_784', version = 1)
mnist.keys()# Fetch the data
X = mnist["data"].values
y = mnist["target"].astype(np.uint8).values# Define function to print the images
def print_digit(index):
example = X[index]
example = example.reshape(28,28)
plt.imshow(example, cmap='viridis')
plt.axis('off')# Give a look at some images
print_digit(26)
输出应该如下所示:
在分割了训练集和测试集之后,我定义了数据扩充的函数。它将图像和定义移动方向和幅度的向量作为输入。移动的结果是,图像的一些像素将会是空白的。为了保持图像的原始形状,我们可以定义这些像素的填充方法。默认情况下,它们用 0.0 的浮点数填充。
from sklearn.model_selection import train_test_split
from scipy.ndimage import shift# Split train test set
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)# Define function to shift an image by a given vector
def shift_digit(img, vector, fill_with=0.0):
img = img.reshape(28,28)
img_shifted = shift(img, vector, cval=fill_with)
# Return unrolled image array
return img_shifted.flatten()
我们来看看将原图下移 5 个像素的效果。
shift1 = shift_digit(X[26], (5,0))
plt.imshow(shift1.reshape(28,28), cmap='viridis')
下移图像的效果。来源:作者。
接下来的步骤是获取训练集中的每幅图像,并在 4 个主要方向上移动 1 个像素。测试集保持不变。
# Convert dataset into lists
X_train_augment = list(X_train)
y_train_augment = list(y_train)# Loop through training images and shift them
for (dx, dy) in ((1,0), (0,1), (-1,0), (0,-1)):
for image, label in zip(X_train, y_train):
row_shifted = shift_digit(image, (dx, dy))
# Append shifted image and label
X_train_augment.append(row_shifted)
y_train_augment.append(label)# Convert back to np.array
X_train_augment= np.array(X_train_augment)
y_train_augment= np.array(y_train_augment)
原始训练集包含 56000 个训练样本,扩充后的训练集包含 280000 个样本。
模型选择和培训
为了衡量人工数据合成的好处,我将使用 2 个随机森林分类器分别在原始集和扩充集上训练它们。然后,我将在相同的测试集上比较它们的准确度分数。
由于随机森林分类器的工作原理超出了本文的范围,我建议阅读本介绍性指南。简而言之,随机森林是决策树的集合。为了确保多样性,通常在训练集的不同随机子集上训练树(有或没有替换)。在多数投票后,随机森林考虑每个弱分类器的决定,并输出聚合预测。
对于原始训练集和扩充集,我在相同的参数空间上执行交叉验证。
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import GridSearchCV# Initialize the classifier
clf = RandomForestClassifier(random_state=42)# Definte the parameter space
param_space = {'n_estimators': [100, 200], 'min_samples_split': range(2,8,2)}# Find best parameters in the parameter space without augmentation
grid_no_augment = GridSearchCV(clf, param_grid=param_space, scoring="accuracy", n_jobs=-1, cv=5, verbose=4)
grid_no_augment.fit(X_train, y_train)# Find best parameters in the parameter space with augmentation
grid_augment = GridSearchCV(clf, param_grid=param_space, scoring="accuracy", n_jobs=-1, cv=5, verbose=4)
grid_augment.fit(X_train_augment, y_train_augment)
评估数据扩充的效果
现在这两个模型都训练好了,我可以评估它们的准确性了。
from sklearn.metrics import accuracy_score# Get the models
model_no_augment = grid_no_augment.best_estimator_
model_augment = grid_augment.best_estimator_# Evaluate the models accuracies
accuracy_no_augment = accuracy_score(y_test, model_no_augment.predict(X_test))
accuracy_augment = accuracy_score(y_test, model_augment.predict(X_test))
结果显示,未经增强的准确率为 96.8%,而基于增强数据训练的相同模型的准确率为 97.8%。仅通过将训练集中的每幅图像移动 1 个像素,该模型的准确度就提高了整整 1%。原因之一是模型变得不太容易过度适应训练集。
对于最后一个分析,我将计算混淆矩阵,以了解哪些是最常见的分类错误。
混乱矩阵。来源:作者。
可以看出,最常见的错误分类是 9 被错误地归类为 4,2 被归类为 3。
结论
数据扩充被证明是计算机视觉的一种有效技术,因为它允许廉价而简单地提高模型的性能。
尽管如此,重要的是要记住,在某些情况下,数据扩充并不同样有效。一种情况是测试集与原始训练集非常相似。在这种情况下,增加训练集是没有帮助的,因为模型将根据与测试阶段遇到的数据不同的数据进行训练。
用人工智能生成维基百科文章
原文:https://towardsdatascience.com/generating-wikipedia-articles-with-ai-995436c9f95f
播客
Meta AI 研究员 Angela Fan 谈改进维基百科的挑战和重要性
编者按:TDS 播客由杰雷米·哈里斯主持,他是人工智能安全初创公司墨丘利的联合创始人。每周,Jeremie 都会与该领域前沿的研究人员和商业领袖聊天,以解开围绕数据科学、机器学习和人工智能的最紧迫问题。
生成引用良好且准确的维基百科文章一直是一个重要的问题:维基百科基本上已经成为互联网的记录百科全书,数亿人通过它了解世界。
但在过去十年中,维基百科也成为了数据饥渴的文本生成模型的重要训练数据来源。因此,维基百科内容中的任何缺点都有被未来的文本生成工具放大的风险。如果一种类型的主题或人物在维基百科的语料库中长期代表性不足,我们可以期待生成文本模型在其输出中反映甚至放大这种代表性不足。
从这个角度来看,维基百科文章生成的项目远比它看起来的要多——它实际上是为未来的语言生成系统设置场景,并授权人类以更强大的方式引导这些系统。
这就是为什么我想和 Meta AI 研究员 Angela Fan 谈谈,她的最新项目专注于生成可靠、准确和结构化的维基百科文章。在这一集的 TDS 播客中,她和我一起谈论了她的工作、高质量长格式文本生成的意义以及人类/人工智能合作的未来。
以下是我在对话中最喜欢的一些观点:
- 为了生成连贯和结构良好的维基百科文章,Angela 和她的合作者能够使用一个相当普通的基于 transformer 的架构,并做了两处有趣的修改。首先,维基百科的文章需要参考文献,而普通的变形金刚无法生成这些参考文献。因此,Angela 的系统转而查询公共抓取数据集,以找到贯穿生成的文章的主张的支持证据。该系统还使用缓存机制来确保在文章的前面部分中生成的信息可以被模型在生成新的部分时调用。这克服了 transformer 上下文窗口的有限大小,并确保模型总是有相关的信息,即使它来自文章的更早部分。
- 评估生成的文本可能是棘手的,公平地说,没有一个单一的指标能够完全捕捉到人类评估的所有细微差别。出于这个原因,虽然 Angela 和她的合作者确实使用传统的度量标准评估了生成的样本的质量,但他们用人工审查补充了这种分析。为了被人类评论者视为“准确”,一个句子需要事实正确,并且在语气和风格上与其他维基百科文章一致。这是一个很高的标准,但据 Angela 估计,50%的生成句子确实达到了这个标准——虽然这可能还不足以让她的系统推广到更广泛的世界,但它可以有意义地加快人类作家的工作,他们的责任可以从最初的研究转向事实检查。
- 人们常说,语言模型可以使它们的训练数据中的偏见永久化,事实的确如此。但是安吉拉指出,他们经常超越复制这些基础,实际上是放大它们。例如,如果文本语料库中 60%的代词是女性“她”,则很典型地会发现“她”在生成的文本中会被过度表示,因为语言模型通常喜欢“谨慎行事”,生成的文本片段单独来说更能反映训练集,但总的来说会放大现有趋势。
你可以在这里查看安吉拉的作品,或者在推特上关注我。
章节:
- 0:00 介绍
- 1:45 进入 Meta AI 的旅程
- 5:45 过渡到维基百科
- 11:30 文章如何生成
- 18:00 文本质量
- 21:30 准确度指标
- 25:30 出现幻觉的风险
- 30:45 跟上变化
- 36:15 用户界面/UX 问题
- 45:00 性别失衡的技术原因
- 51:00 总结
Python 中使用 Skip-Gram 算法和深度学习从文本数据生成单词嵌入
使用人工神经网络和 Gensim 介绍自然语言处理中的嵌入
莱昂纳多·大久保俊郎在 Unsplash 上的照片
1.介绍
NLP 中最大的挑战是设计一种方法来表示单词的意思。这是一个关键的组成部分,因为不同的人以不同的方式传达相同的信息。例如,“我喜欢这里的食物”和“食物质量似乎不错”。我爱死了!”在一天结束的时候,两个文本都反映了一个由一系列单词或短语代表的想法或思想。在分布语义学中,一个词的含义是由最频繁出现在附近的词给出的。当一个词(w) 出现在一个句子中时,其 上下文 是出现在附近(在固定的窗口大小内)的词的集合或序列。例如,
- “我们可以使用传统的机器 学习 算法来创建驱动程序模型。”
- “建深 学 框架是当务之急。”
- “我们能否使用传统的方法,将学习 转向 提高绩效?”
在 NLP 中,我们可以使用学习的上下文词(例如,(机器,算法),(深度,框架),(转移,朝向))来构建学习的向量表示。稍后我们会谈到单词向量。
2.什么是单词嵌入?
在自然语言处理(NLP)中使用单词嵌入来描述如何为文本分析表示单词。通常,这种表示采用实值向量的形式,该向量对单词的含义进行编码,期望在向量空间中彼此更接近的单词将具有相似的含义。在称为单词嵌入的过程中,每个单词被表示为预定向量空间中的实值向量。这种方法被称为深度学习,因为每个单词都被分配给一个向量,向量值就像神经网络一样被学习(Jason Browniee,2017)。
2.1 嵌入的示例
对每个单词采用密集分布的表示对于产生嵌入的方法是必要的。通常具有几十、几百甚至几千维的实值向量代表每个单词。相比之下,稀疏单词表示,如一键编码,需要数千或数百万维。
这是一个简单的例子,展示了两个不同的单词在三维空间中的表现。请注意,向量符号和假设值表示单词“Angel”和“Eugene”
天使= 3i + 4j + 5k,尤金= 2i + 4j + 4k
嵌入的妙处在于它允许我们计算单词的相似度。这里的相似代表语境,不一定代表意义。比如说,如果我们用一个余弦相似度,那么相似度(安吉尔,尤金)=(3 x2+4 x4+5 x4)/(sqrt((3+4+5))x sqrt(2+4+4))= 42/42.42 = 0.99 或者 99% 相似度。您可能想知道为什么这种相似性在 NLP 中很重要。这纯粹是为了让模型将单词上下文化,以便出现在相似上下文中的单词具有相似的含义。为了验证这一点,我们来看看下面的两句话。
- "安吉尔和尤金正在做一个项目提案,并计划明天推出."
- “Eugene 对模型验证的意见似乎是正确的。安吉尔,你能实现这些改变吗?”
虽然我们在这里看的是两句话,看起来安吉尔和尤金是队友;因此,相似性仍然很高。同样,这不是单词是否有相似的意思,而是它们是否出现在相似的上下文中或共现。
3.基本嵌入模型
3.1 简单概率模型
简单的概率模型可以使用链式规则来计算单词在文本中出现的概率。它可以使用组合关系来识别共现单词并计算它们的概率。下面的例子。
公式 1: 简单概率模型。作者使用 Latex & Jupyter 笔记本创建的图像。
p(天使玩的好)= p(天使)x p(玩|天使)x p(玩的好|天使)。计算这些概率需要计算单词和术语的出现次数。尽管越来越复杂和精确,像这样的直接概率模型还没有达到很好的性能。
3.2 Word2vec
学习单词向量的框架叫做 Word2vec。Word2vec 是一个神经网络,它使用单词“矢量化”来解析文本。它接受文本语料库作为输入,并产生代表语料库中的单词的一组特征向量。将相似的词向量在向量空间中分组在一起是 Word2vec 的目标和好处。Word2Vec 中有两种截然不同的架构:跳格和连续字包 (CBOW)。
假设我们有大量的文本。在固定的词汇表中,每个单词由一个向量表示。我们使用以下逐步方法计算嵌入:
- 浏览文本的每个位置 t ,每个位置都有一个 居中的单词 c
- 语境(或“外界”) 中的词表示为 o
- 使用 c 和 o 的词向量的相似度,计算给定 c 的 o 的可能性。(反之亦然)
- 继续修改单词向量到增加这种可能性
跳过克模型
在跳格模型中,我们试图对给定特定单词的上下文单词(例如,单词窗口大小)进行建模。
例如,如果我们有文本“Angel 踢足球踢得好”,考虑足球作为中心词(假设),剩余的词成为上下文。窗口大小表示我们想要考虑多少上下文单词。窗口大小示例和使用两个不同示例计算概率的过程。
图 1: 跳格模型示例。图表用两个不同的例子展示了中心词和上下文词的概念。这里 t 表示中心单词的位置,而 t-1 和 t+1 表示上下文单词的位置。作者使用 PowerPoint 创建的图像。
图二。说明了 Skip Gram 模型的似然函数。在真实的单词中,训练一种算法来最大化这种可能性,这将导致使用嵌入更准确地表示单词。图片由作者使用 Latex & Jupyter 笔记本创建。
使用 Gensim 生成基于跳格图的嵌入的 3.2.1.1 示例
Gensim 是一个开源框架,使用现代统计机器学习进行无监督的主题建模、文档索引、嵌入创建和其他 NLP 功能。我们将使用 Gensim 中的一些标准文本来创建单词嵌入。
#----------------------------------------Install and import necessary libraries-----------------------------------# !pip install contractionsimport re, string, unicodedata # Import Regex, string and unicodedata.
import contractions # Import contractions library.
from bs4 import BeautifulSoup # Import BeautifulSoup.import numpy as np # Import numpy.
import pandas as pd # Import pandas.
import nltk # Import Natural Language Tool-Kit.# nltk.download('stopwords') # Download Stopwords.
# nltk.download('punkt')
# nltk.download('wordnet')from nltk.corpus import stopwords # Import stopwords.
from nltk.tokenize import word_tokenize, sent_tokenize # Import Tokenizer.
from nltk.stem.wordnet import WordNetLemmatizer # Import Lemmatizer. For removing stem words
import unicodedata # Removing accented characters
from nltk.stem import LancasterStemmerfrom IPython.display import display
这些包对于数据清理和预处理非常有用。
import gensimfrom gensim.models import Word2Vecfrom gensim.test.utils import common_textscommon_texts[['human', 'interface', 'computer'],
['survey', 'user', 'computer', 'system', 'response', 'time'],
['eps', 'user', 'interface', 'system'],
['system', 'human', 'system', 'eps'],
['user', 'response', 'time'],
['trees'],
['graph', 'trees'],
['graph', 'minors', 'trees'],
['graph', 'minors', 'survey']]
在 Word2Vec()中使用 sg=1 创建一个跳格模型。向量大小表示由模型产生的每个单词的嵌入数量。窗口表示窗口大小,即用于生成嵌入的上下文单词的数量。
model = Word2Vec(sentences=common_texts, vector_size=3, window=5, min_count=1, workers=4, sg=1)
model.save(“word2vec.model”)print("Embeddings of a Word computer with 3 as Embedding Dimension")print(model.wv['computer'])print("Embeddings of a Word Tree with 3 as Embedding Dimension")print(model.wv['trees'])print("Embeddings of a Word Graph with 3 as Embedding Dimension")print(model.wv['graph'])Embeddings of a Word computer with 3 as Embedding Dimension
[ 0.19234174 -0.2507943 -0.13124168]
Embeddings of a Word Tree with 3 as Embedding Dimension
[ 0.21529572 0.2990996 -0.16718094]
Embeddings of a Word Graph with 3 as Embedding Dimension
[ 0.3003091 -0.31009832 -0.23722696]
我们现在将这些嵌入转换成一个列表,并使用 matplotlib()创建一个可视化。如果您检查嵌入的值,它们可能没有什么意义,但是当用图形表示时,它们应该看起来彼此更接近。
inter_array=model.wv['human']
comp_array=model.wv['user']
tree_array=model.wv['interface']from mpl_toolkits import mplot3d
import numpy as np
import matplotlib.pyplot as plt%matplotlib inline
c=[10,20,30]labels=['human', 'user', 'interface']fig = plt.figure(figsize=(10, 8))
ax = plt.axes(projection ='3d')
# defining all 3 axes
z = [comp_array[2], inter_array[2], tree_array[2]]
x = [comp_array[0], inter_array[0], tree_array[0]]
y = [comp_array[1], inter_array[1], tree_array[1]]
# plotting
ax.scatter(x, y, z, color=['red','green','blue'])ax.text(comp_array[0],comp_array[1],comp_array[2], "human")
ax.text(inter_array[0],inter_array[1],inter_array[2], "user")
ax.text(tree_array[0],tree_array[1],tree_array[2], "interface")ax.legend(loc="lower right")ax.set_title('3D Representation of words - computer, graph, and tree')
plt.show()
图 3。使用从 Gensim 生成的嵌入,单词“界面”、“用户”和“人”的三维表示。作者使用 Jupyter 笔记本创建的图像。
3.2.2 连续词袋(CBOW)
在连续词袋(CBOW)模型中,我们试图在给定其周围词(例如,词窗口大小)的情况下对中心词进行建模。
图 4:CBOW 模型的例子。与 Skip Gram 类似,CBOW 的可能性函数如上所示。作者使用 Jupyter 笔记本和 Latex 创建的图像。
4.从头开始实现 Skip Gram 模型
我们将使用随机生成的关于梅西和罗纳尔多的数据,并通过训练神经网络模型来尝试和开发单词嵌入(Bujokas,2020)。我们将把嵌入的大小保持为两个,允许我们用 Python 创建一个图,并直观地检查给定上下文中哪些单词是相似的。你可以在这里找到作者生成的样本数据。
4.1 基本 NLP 库
import itertools
import pandas as pd
import numpy as np
import re
import os
from tqdm import tqdm#----------------------Drawing the embeddings in Python
import matplotlib.pyplot as plt#-----------------------Deep learning: ANN to create and train embeddings
from tensorflow.keras.models import Modelfrom tensorflow.keras.layers import Input, Dense
4.2 用户自定义函数:从数据到字典的单词/记号
创建字典的用户定义函数,其中键代表唯一的单词,键值是索引或索引。
def create_unique_word_to_dict(sample):
"""
User defined function that creates a dictionary where the keys represents unique words
and key values are indices or indexes
"""
#-------Extracting all the unique words from our sample text and sorting them alphabetically
#-------set allows us to remove any duplicated words and extract only the unique ones
words_list = list(set(sample))
#-------------Sort applied here
words_list.sort()#------Creating a dictionary for each unique words in the sample text
unique_word_dict_containing_text = {}
for i, word in enumerate(words_list): #------For each word in the document
unique_word_dict_containing_text.update({
word: i
})return unique_word_dict_containing_text
4.3 用户自定义功能:数据清理或文本预处理
这是一个自定义的文本预处理函数。人们可以增强这里使用的停用词列表,以说明更频繁使用的不需要的词。我们使用该功能执行以下操作:
- 删除标点符号
- 删除号码
- 删除空白
- 删除停用词
- 返回清除的文本
def text_preprocessing_custom(sample_text):
#---------------Custome Text Preprocessing created as UDF-----------------------------
#----------------Regular expression for Punctuations------------------
punctuations = r''';:'"\,<>./?*_“~'''
#---------------Stop Words (Custom List)--------------------
stop_words=['i', 'dont', 'we', 'had','and', 'are', 'of', 'a', 'is', 'the', 'in', 'be',\
'will', 'this', 'with', 'if', 'be', 'as', 'to', 'is', 'don\'t']
"""
A method to preproces text
"""
for x in sample_text.lower():
if x in punctuations:
sample_text = sample_text.replace(x, "")#-------------Removing words that have numbers in them using regular expression---------
sample_text = re.sub(r'\w*\d\w*', '', sample_text)#-------------Removing whitespaces---------------------
sample_text = re.sub(r'\s+', ' ', sample_text).strip()# --------Convert to lower case----------------------
sample_text = sample_text.lower()#--------------Converting all our text to a list------------------
sample_text = sample_text.split(' ')#--------------Deleting empty strings---------------------
sample_text = [x for x in sample_text if x!='']# Stopword removal
sample_text = [x for x in sample_text if x not in stop_words]return sample_text
4.4 数据预处理和[中心,上下文]单词生成
牢记 Skip Gram 模型的概念,我们需要创建一个框架,允许我们使用大小为 2 的窗口从文本中识别中心词和上下文词的所有组合。请注意,给定文本中的每个单词(经过预处理后)在一个实例中可以是中心单词,在另一个实例中可以是上下文单词。理想情况下,我们允许文本中的每个单词作为中心单词,然后相应地找到相关的上下文单词。
from scipy import sparsesample = pd.read_csv('Text Football.csv') #-----Reading text datasample = [x for x in sample['Discussion']]print("--"*50)print("Text in the File")print("--"*50)print(sample)
#----------------------------------------Defining the window for context----------------------------------------
window_size = 2
#----------------------------------------Creating empty lists to store texts---------------
word_lists = []
all_text = []#----------------Combines preprocessed texts from the Sample Datafor text in sample:
#----------------------------------------Cleaning the text
text = text_preprocessing_custom(text)
all_text += text#-------------------------Iterating across each word in a text
for i, word in enumerate(text):for ws in range(window_size):if i + 1 + ws < len(text):
word_lists.append([word] + [text[(i + 1 + ws)]])if i - ws - 1 >= 0:
word_lists.append([word] + [text[(i - ws - 1)]])
unique_word_dict = create_unique_word_to_dict(all_text)print("--"*50)
print("Text to Sequence in post cleaning")
print("--"*50)
print(unique_word_dict)
print("--"*50)
print("Text to WordList [main word, context word]")
print("--"*50)
print(word_lists)
图 5: 如何生成中心(主要)和上下文单词的示例。作者使用 Jupyter 笔记本创建的图像。
4.5 为神经网络生成输入和输出数据
我们现在将使用主要单词(中心单词)作为神经网络的输入,使用上下文单词作为输出。输入和输出层之间的深层致密层将学习嵌入。想法是在单个隐藏层内训练一个 2 神经元神经网络,其中在训练模型时学习的权重将反映从数据中学习的嵌入。为了让我们的数据与 Tensorflow 架构兼容,我们对上面生成的主单词和上下文单词列表执行了一次热编码(OHE)。
def create_OHE_data(word_dict, word_lists):#----------------------------------------Defining the number of unique words----------------------------------------
n_unique_words = len(word_dict)#----------------------------------------Getting all the unique words----------------------------------------
words = list(word_dict.keys())#----------------------------------------Creating the X and Y matrices using one hot encoding---------------------------------
X = []
Y = []for i, word_list in tqdm(enumerate(word_lists)):#----------------------------------------Getting Indicies----------------------------------------index_main_word = word_dict.get(word_list[0])
index_context_word = word_dict.get(word_list[1])#----------------------------------------Creating the placeholders
Xrow = np.zeros(n_unique_words)
Yrow = np.zeros(n_unique_words)#----------------------------------------One hot encoding the main word, Input Matrix
Xrow[index_main_word] = 1#----------------------------------------One hot encoding the Y matrix words, Output Matrix
Yrow[index_context_word] = 1#----------------------------------------Appending to the main matrices
X.append(Xrow)
Y.append(Yrow)#------------------------Converting the matrices into a sparse format because the vast majority of the data are 0s
X_Matrix = sparse.csr_matrix(X)
Y_Matrix = sparse.csr_matrix(Y)print("--"*50)
print("Input Data [Showing the First Record]")
print("--"*50)print(X_Matrix.todense()[0])
print(X_Matrix.todense().shape)print("--"*50)
print("Output Data [Showing the first record]")
print("--"*50)print(Y_Matrix.todense()[0])
print(Y_Matrix.todense().shape)
return X_Matrix, Y_MatrixX, Y = create_OHE_data(unique_word_dict, word_lists)
图 6:OHE 生成输入输出数据的例子。作者使用 Jupyter 笔记本创建的图像。
4.6 训练神经网络以生成嵌入
下面展示了一个示例架构的外观。
图 7: 示出了用于训练嵌入的双神经元神经网络。作者使用 PowerPoint 开发的图像。
from tensorflow.keras.models import Sequentialdef embedding_model():
model=Sequential()
model.add(Dense(2, input_shape=(X.shape[1],), activation='relu'))
model.add(Dense(units=Y.shape[1], activation='softmax'))
model.compile(loss = 'categorical_crossentropy', optimizer = 'adam')
return modelmodel_embbedding=embedding_model()model_embbedding.summary()#----------------------------------------Optimizing the network weights----------------------------------------
model_embbedding.fit(
x=X.todense(),
y=Y.todense(),
batch_size=64,
epochs=1000,
verbose=0
)#----------------------------------------Obtaining the weights from the neural network----------------------------------------
#----------------------------------------These weights are equivalent to word embeddings--------------------------------------weights = model_embbedding.get_weights()[0] #---embeddings#----------------------------------------Creating Dictionary to Store Embeddings----------------------------------------
embedding_dict = {}
for word in words_list:
embedding_dict.update({
word: weights[unique_word_dict.get(word)]
})
embedding_dict
图 8: 从模型中生成的嵌入示例。作者使用 Jupyter 笔记本创建的图像。
# !pip install ann_visualizerfrom ann_visualizer.visualize import ann_vizann_viz(model_embbedding, view=True, filename='embed.pdf', title='ANN Architecture for embedding')
图九。说明了用于生成嵌入的人工神经网络架构。作者使用 Jupyter + ann_visualizer()开发的图片
4.7 直观地表示学习到的嵌入
#----------------------------------------Ploting the embeddings----------------------------------------
plt.figure(figsize=(15, 10))import seaborn as snssns.set(color_codes=True)plt.title("Embeddings")for word in list(unique_word_dict.keys()):
coord = embedding_dict.get(word) #------------------------Extracting Embeddings
plt.scatter(coord[0], coord[1]) #-----------------------Plotting Embeddings
plt.annotate(word, (coord[0], coord[1])) #----------------Annotate tokens
图 9: 从模型中生成的嵌入的可视化表示。作者使用 Jupyter 笔记本创建的图像。
4.8 观察
在足球界,梅西和罗纳尔多经常被全球球迷拿来比较。因此,它们更有可能在数据中同时出现。像“最伟大”、“球员”、“得分”和“得分外”这样的术语经常被用来评论球员的表现。使用随机生成的 12 个文本,我们在上图中捕捉到了这些变化和共现。注意少数停用词没有被删除;因此他们出现在情节中。这可以使用我们的 text_preprocessing_custom()函数来增强。正如前面所反映的,当我们研究数值时,图 8 中的嵌入可能没有多大意义。尽管如此,当绘制时,我们推断在相似上下文中使用的共现单词看起来彼此更接近。
5.结论
Tensorflow 的递归神经网络提供了一个嵌入层,可以在我们训练分类器模型时自动从文本中生成嵌入。这个过程包括使用文本矢量化将单词或句子映射到向量或一组数字。我们的独热向量——稀疏,因为它的大多数条目都是零——被嵌入层转换成密集的嵌入向量(密集,因为维度小得多,并且所有元素都是自然数)。它只需要一个完全连接的层来构成这个嵌入层(张量流,n.d .)。然而,当我们训练分类模型时,训练嵌入是耗时的。在这种情况下,如果我们有大量的数据,生成嵌入的灵活性允许我们减少创建分类模型所涉及的计算时间。嵌入也是比传统方法更好的单词表示,如单词袋和词频-逆文档频率。其他预先训练的模型,如伯特,生物伯特,手套,Edu 伯特等。,为用户提供使用预训练模型生成嵌入的灵活性。
6.参考
- 布约卡斯,E. (2020 年 5 月 30 日)。创建单词嵌入:使用深度学习在 Python 中编码 Word2Vec 算法。从中观网站检索:https://towards data science . com/creating-word-embeddings-coding-the-word 2 vec-algorithm-in-python-using-deep-learning-b 337d 0 ba 17 a 8
- 杰森·布朗尼。(2017 年 10 月 10 日)。什么是文本的单词嵌入?从机器学习大师网站检索:https://machinelementmastery . com/what-are-word-embedding/
- 张量流。(未注明)。基于 RNN 的文本分类。从 TensorFlow 网站检索:https://www . tensor flow . org/text/tutorials/text _ classification _ rnn
关于作者:高级分析专家和管理顾问,帮助公司通过对组织数据的商业、技术和数学的组合找到各种问题的解决方案。一个数据科学爱好者,在这里分享、学习、贡献;可以和我在 上联系 和 推特;
具有深度学习风格转移的合成微生物数据集的生成
生成用于训练深度学习检测器的带注释的合成数据集的有效策略
作者图片。
深度学习模型实现了比传统计算机视觉算法高得多的精确度。当使用传统方法进行图像识别时,特征提取算法是手动调整的,这在许多情况下是一个耗时的过程。相反,在深度卷积网络中,特征工程是自动执行的——网络学习如何自己提取最佳特征图,并在后续卷积中优化内核,以仅保留图像中的相关信息。
所以现在我们不需要花费数周时间来寻找最佳参数。但这是有代价的。为了通过复杂的深度学习模型获得足够的结果,我们需要足够大的数据集。收集和注释大数据集需要大量的时间和财力。此外,贴标过程本身也具有挑战性。合成数据是一种很有前途的替代方法,可以解决缺乏足够大的数据集的问题,并减少与收集此类数据相关的资源和成本[1]。此外,它可能有助于机构共享知识,例如高度专业化领域的数据集,同时保护个人隐私。
我们的目标是识别培养皿上的微生物菌落——这是微生物学中的典型任务。即使对训练有素的专业人员来说,这项任务也很难完成,因为有些菌落容易聚集和重叠,因此非专业人员很难分辨。在这篇文章中,我们将提出一种有效的策略来生成一个由微生物图像组成的带注释的合成数据集,我们已经在自然科学报告杂志【2】上发表了该数据集。然后,生成的数据集用于以完全监督的方式训练深度学习对象检测器。该生成器采用传统的计算机视觉算法以及神经类型的传输方法进行数据扩充。我们表明,该方法能够合成逼真图像的数据集,该数据集可用于训练能够定位、分割和分类五种不同微生物物种的神经网络模型。我们的方法比收集并标记一大组带有注释的真实图像需要更少的资源来获得有用的数据集。
我们表明,从 100 幅真实图像开始,我们可以生成数据来训练一个检测器,该检测器可以获得与同一检测器相当的结果[3],但要在一个真实的、大几十倍的微生物数据集 [4]上进行训练,该数据集包含超过 7k 幅图像。
生成合成数据集
现在让我们详细描述该方法。目标是生成带有微生物菌落的合成图像,这些图像将在以后用于训练深度学习检测和分割模型。管道如图 1 所示。注意,我们的生成框架的 Python 实现的源代码是公开可用的。
图一。一种合成数据集生成流水线方案。使用传统的计算机视觉算法从真实图像中分割出微生物菌落,然后随机排列在空盘子的碎片上,给出带有精确注释的合成补丁。为了提高生成的数据的真实性,然后使用神经风格转移方法对面片进行风格化。图改编自[2]。
我们从培养皿的标记真实图像开始,使用传统的计算机视觉算法进行菌落分割,包括适当的过滤、CIELab 颜色空间中的阈值处理和基于能量的分割——我们使用强大的 Chan-Vese 算法。为了获得一个平衡的工作数据集,我们从最近推出的琼脂数据集【4】的高分辨率子集中为 5 种微生物物种中的每一种随机选择 20 张图像(总共给出 100 张图像),这些图像可以根据请求从这个站点免费下载。
第二步,将分割的菌落和菌落簇随机排列在空培养皿的碎片上(我们称之为贴片)。我们从 10 个真实的空盘子图像中随机选择一个片段。我们重复这个步骤很多次,把随后的集群放在随机的地方,确保它们不重叠。与此同时,我们存储了放置在小块上的聚类中每个菌落的位置及其分割掩码,为该小块创建了一个注释字典。我们在图 2 中展示了生成的合成补丁的例子。
图二。在风格化步骤之前具有微生物菌落的合成贴片的实例。图改编自[2]。
正如我们在图 2 中所看到的,在某些情况下,菌落不能很好地与背景融合,它们的颜色与背景颜色不匹配。为了解决这个问题并提高生成数据的真实性,在第三步中,我们使用神经类型转移方法应用数据扩充。我们将风格从作为风格载体的选定实像之一转移到给定的原始面片。我们选择了 20 个具有显著不同光照条件的真实片段,以增加生成的碎片的多样性。风格化步骤后的示例性贴片如图 3 所示。我们使用[5]中介绍的快速有效的深度学习风格化算法。这种方法为我们的原始微生物图像提供了最真实的风格化,而不会引入任何不想要的伪像。
图 3。生成的微生物图像的风格化。五个合成补片(左)使用五个真实图像(上)的样式进行风格化。图改编自[2]。
训练深度学习模型
使用这种方法,我们生成了大约 50k 个补丁,然后进行了风格化处理。所进行的实验背后的想法是使用合成数据训练一个神经网络模型来检测微生物菌落,然后用培养皿上的细菌菌落在真实图像上测试其性能。我们使用我们的合成数据集训练流行的 R-CNN 检测器。图 4 给出了对琼脂数据集的真实斑块进行级联 R-CNN [6]检测器评估的示例。该模型在不同光照条件下检测各种大小的微生物菌落表现得相当好。
自动实例分割是在许多生物医学应用中有用的任务。在面片生成过程中,我们还为每个菌落存储一个像素级的分割掩模。我们使用这些额外的信息来训练深度学习实例分割模型——Mask R-CNN[7],它扩展了我们已经训练过的 R-CNN 检测器。真实样本的分割结果也呈现在图 4 中。获得的不同微生物菌落类型的实例分割正确地再现了菌落形状。
图四。真实数据上的微生物菌落检测示例(用绿色边框标记)—来自琼脂数据集的不同微生物种类的培养皿片段。例如分割的结果被呈现为颜色分割遮罩。图改编自[2]。
物体检测在微生物学中的主要应用之一是对培养皿上生长的微生物菌落进行自动计数。我们通过将其与标准方法进行比较来验证所提出的合成数据集生成方法,在标准方法中,我们收集大的真实数据集并训练检测器用于菌落识别和计数任务。
我们在使用来自高分辨率琼脂子集的 100 图像生成的 50k 大数据集上训练 R-CNN 检测器(Cascade ),并在与【4】中执行的相同任务中测试微生物菌落计数。结果如图 5(右)所示。结果表明,合成数据集的检测精度和计数误差仅比同一检测器稍差[3],但在包含超过 7k 真实图像的整个大数据集上进行训练,给出了大约 65k 个面片。同样清楚的是,引入风格转移增强大大提高了检测质量,并且在没有风格化步骤的情况下,结果相当差——参见图 5(左)中的原始数据集的结果,即在没有风格化步骤的情况下获得的结果。
图 5。对两个不同的合成训练数据集的真实数据进行微生物菌落计数测试:**raw——无风格化(左)和风格化(右)。风格化大大提高了检测性能。在理想的检测中,代表单个培养皿图像的每个黑点应该位于 y = x 黑线上。图改编自[2]。
摘要
我们引入了一种有效的策略来生成培养皿微生物图像的带注释的合成数据集,该数据集可用于以完全监督的方式训练深度学习模型。通过使用传统的计算机视觉技术,辅以深度神经类型转移算法,我们能够建立一个仅提供 100 幅真实图像的微生物数据生成器。与收集和标记包含数千幅真实图像的大型数据集相比,它需要的精力和资源要少得多。
我们证明了该方法在微生物检测和分割中的有效性,但是我们期望该方法具有灵活性和通用性,还可以应用于科学和工业的其他领域来检测各种对象。
参考
[1]https://blogs . NVIDIA . com/blog/2021/06/08/what-is-synthetic-data
[2]j . paw owski,S. Majchrowska 和 T. Golan,具有深度学习风格转移的微生物菌落数据集的生成,科学报告 12,5212 (2022)。https://doi.org/10.1038/s41598-022-09264-z
[3]检测 mAP = 0.416(越大越好),计数 MAE = 4.49(越小越好)指标,与 mAP = 0.520 和 MAE = 4.31 相比,获得了相同的检测器,但使用琼脂数据集进行了训练[4]。
[4] S. Majchrowska,J. Pawł owski,G. Guł,T. Bonus,A. Hanas,A. Loch,A. Pawlak,J. Roszkowiak,T. Golan 和 Z. Drulis-Kawa,琼脂微生物菌落数据集用于深度学习检测 (2021)。可在 arXiv[arXiv:2108.01234]获得预印本。
[5],叶春阳,,用于真实感风格传递的高分辨率网络 (2019)。预印本可从 atXiv[arXiv:1904.11617]获得。
[6]蔡志勇和,级联 R-CNN:深入研究高质量目标检测,IEEE/CVF 计算机视觉与模式识别会议,6154–6162(2018)。
[7] K. He,G. Gkioxari,P. Dollár,R. Girshick, Mask R-CNN ,IEEE 计算机视觉国际会议(ICCV),2980–2988(2017)。
作为精明增长运营计划的一部分,该项目由欧洲区域发展基金下的欧盟基金共同资助。作为国家研究与发展中心的一部分实施的项目:快速通道。
原载于 2022 年 5 月 06 日【https://neurosys.com】。
生成性对抗学习
原文:https://towardsdatascience.com/generative-adversarial-learning-b1d5b4f6b2bc
从生成性到“加对抗性”
由german Kaser在 Unsplash 上拍摄的照片
假设我们有一个真实图像的数据集。比如各种场景下的狮子图片。从这个数据集中,我们希望机器学习生成看起来像真实图像的新图像。
生成对抗网络,简称 GANs,是解决这一问题的一种令人信服的方法。
GAN 包括两个模型,一个发生器和一个鉴别器。生成器生成合成图像。鉴别器被训练来区分真实图像和合成图像。
生成器通过来自鉴别器的反馈进行学习。鉴别器识别它检测到的合成图像中的哪些是假的。发电机显然应该产生更少的能量。因为它们很容易被发现是假货。
发电机和鉴别器在战斗中被锁定。他们试图超越彼此。来回的竞争改善了这两者。
一旦(并且如果)发生器变得足够好以至于鉴别器不能可靠地区分真实图像和合成图像,则该过程停止。比乱猜要好。
让我们从生成开始
为了理解鉴别器所扮演的角色,首先让我们把它排除在外。
假设我们已经在真实图像数据集上训练了一个初始的无监督生成模型。它生成的图像有多真实?如果不够现实,我们如何通过机器学习来改善它?
评估生成器质量的一种自然方法是通过一种典型的模型优度的非监督测量。例如模型与验证集(即训练集的保留子集)的拟合程度。使用最大似然或更简单的标准,如平方和。
在现实环境中,这是不够有效的。
为什么不呢?
现实世界通常是巨大的。训练集是从复杂分布中抽取的微小部分。事实上,所有的实像可能只是宇宙中很小很小的一部分。来自宇宙的随机样本几乎肯定会被检测为赝品。
举个例子。一百万像素的二进制图像。宇宙的大小是“二的百万次方”。拥有十亿张图片的狮子真实图片训练集仍然只是很小的一部分。它们来自一个非常复杂的分布。也就是说,对真正的狮子图像进行表面上的小变形,就能让它看起来不真实。
让我们用一个不同的例子来阐述“表面的小失真”。
假设我们有一个非常大的实际手写 0 和 1 的数据集。说所有 1 的图像都是由直线组成的。假设在这个数据集上训练的生成器的第一个版本生成了许多轮廓弯曲的 1。(也许生成器被数据集中的 0 搞糊涂了。它们是弯曲的。)
我们如何判断发电机还不够好,因为它正在产生弯曲的1?
在验证集上评估我们的模型的适应性可能不会很好。验证集没有任何曲线 1。
这告诉我们以下几点。为了评估生成器的适用性,我们还应该查看它生成的合成图像样本,以及它们与真实图像的关系。
这里有一个来自教育环境的类比。要区分哪些学生已经掌握了某个主题,哪些学生还没有,并不总是那么容易。而不进行测试和评估不同学生的答案,因为它们与正确答案相关。
教育环境中的测试也可以揭示某个学生目前在哪些方面比较薄弱,以指导改进。这里也一样。
走上“正途”
所以我们得出结论,我们应该通过比较生成器生成的合成图像和真实图像来评估生成器的质量。我们究竟如何进行这种比较?
我们使用鉴别分类器。
我们有一组真实图像和一组合成图像。将这两者结合起来就产生了二元分类器的标记数据集。
在该数据集上训练二元分类器,并评估其预测准确性。(如有必要,使用列车测试分割。)
如果分类器比随机预测更好,挑出那些标记为合成的图像,它也预测为合成的。这些是很容易与真货区分开来的假货。使用此反馈重新训练发电机。
在我们的例子中,我们预计鉴别器将揭示生成器正在生成许多曲线 1,这些曲线 1 很容易被预测为合成的。
为什么要有鉴别力鉴别力?
用我们的例子来推理吧。如果我们的分类器能够以某种方式自动学习到一幅有曲线和其他一些区别于 0 的特征的图像不是真实的,那就太好了。
鉴别分类器有相当大的机会学习能够冲洗掉这种类型的假货的特征。生成分类器可能不能。无法学习在两个类之间区分的特征。尤其是不要“图像有曲线和一些其他特征,使其区别于 0”。
线是“弯曲的”是高阶特征。与数据世界相比,高阶特征的世界超级庞大。我们已经注意到,后者是巨大的。生成模型很难找到“正确的”高阶特征,因为它们只能处理模型适应性。鉴于判别学习的使用,判别模型有更好的机会。
菜谱
好了,现在我们有了基本的配方。我们重复以下步骤。
1\. Train the generator.
2\. Generate synthetic images from it.
3\. Train the discriminator on the real+synthetic images.
4\. Identify the regime (if any) in which the generator is weak. Use
this as feedback when going back to step 1.
请注意,训练是分轮进行的,发电机训练和鉴别器训练在一轮中交替进行。
实现这个方法的一种自然方式是由这四个步骤组成的重复管道。每一步的输入和输出都是明确定义的。我们有一些自由来选择它的实现。例如,我们可以为步骤 3 使用任何二元分类器。
增量学习和损失函数
在这一部分,我们以不同的方式来看待培训。从迭代训练和损失函数的角度。这一视角也让我们得以一瞥生殖对抗网络是如何运作的。
生成器和鉴别器都通过来自鉴别器最新实例的反馈来学习。我们首先讨论这种反馈的形式。
设 D ( x )表示鉴别器 D 分配给一个数据 x 的分数。 D ( x )如果 D 认为 x 是假的就为低,如果 x 是真的或者 D 认为是真的就为高。
现在开始发电机 G 的训练。假设鉴别器已经从与真实数据集结合的初始生成器的输出中被训练。
让我们想象重复做下面的事情。
1\. Sample a synthetic image *s* from *G*.
2\. If *D*(*s*) is low, update *G*’s parameters so as to reduce the likelihood that *G* will sample this particular *s* subsequently.
而在第 2 步中,我们说this particular *s*
,我们真正的意思是with the same characteristics as this particular *s*
。换句话说,我们希望增量训练能够一般化,不仅降低对这个特定的 s 进行采样的可能性,而且降低对那些具有与这个 s 相似特征的样本进行采样的可能性。
接下来,是鉴别训练。为此,我们假设除了真实图像的数据集之外,我们还有一个来自生成器最新版本的合成图像。让我们把两者结合起来。设 x 表示一个数据,y 表示其标签:真实或合成。现在想象将标记数据集中的每个实例( x , y )逐一呈现给鉴别器。如果 y 为真,我们寻求更新 D 的参数,以便增加 D ( x )。如果 y 是合成的,我们寻求更新 D 的参数,以便减少 D ( x )。
生成器仅从合成图像上的鉴别器分数中学习。相比之下,鉴别器从鉴别器在真实图像和合成图像上的分数中学习。如果鉴别器学习仅限于从合成图像中学习,那就不好了。它可能会决定给所有的图像分配低分,甚至是真实的图像,这对于在下一轮训练生成器来说是不好的反馈。
模式崩溃
没有什么可以阻止生成器偏爱数据中的某些模式,在极端情况下只有一个模式。考虑我们的数字示例,扩展到 0 到 9。如果生成器难以生成真实的 8,它可以完全放弃生成 8。这个鉴别器只能辨别真假。它无法判断合成图像中是否缺少 8。这些模式甚至没有在真实数据集中标注。
总结
在这篇文章中,我们讨论了在学习生成与给定数据集中的数据非常相似的数据的背景下的生成性对抗学习。
我们从生成学习开始,并解释了为什么它不太可能在这项任务中充分发挥作用。然后,我们在混合中引入了判别学习。
生成器和鉴别器相互竞争。这迫使双方都要改进。
延伸阅读
稳定扩散,梦想融合,制作视频,图像视频,下一步是什么
原文:https://towardsdatascience.com/generative-ai-878909fb7868
生成式人工智能概述
用于文本到图像、文本到 3D 和文本到视频的生成式人工智能
星夜(作者利用稳定扩散)
新一代人工智能还处于萌芽状态,但正在呈指数级增长。自从 OpenAI 首次亮相 GPT-3 和 DALL E 以来,它一直在人工智能领域抢尽风头
2022 年是文本到内容的时代(又名 AIGC)。2022 年 4 月,OpenAI 发布了 DALL E 2,在的论文中描述了关于剪辑和扩散模型。这是第一次从自然语言的文本描述中创造出逼真的图像和艺术。
四个月后,初创公司 StabilityAI 宣布发布 stability Diffusion,这是一款开源的文本到图像生成器,可以在几秒钟内创作出令人惊叹的艺术作品。它可以在消费级 GPU 上运行,速度和质量都有所突破。它如此炙手可热,以至于在 2022 年 10 月 17 日的种子轮中成为了独角兽。
2022 年 9 月 29 日,谷歌宣布 DreamFusion 使用 2D 扩散技术进行文本到 3D 的生成。同一天,Meta 宣布制作一个视频用于没有文本视频数据的文本到视频生成。
不到一周,谷歌似乎回应了 Meta 的视频制作,推出了文本转视频的图片视频。
在过去半年的这段激动人心的旅程中, Midjourney 和 CogVideo 同样重要。Midjourney 是一个独立的研究实验室,提供 Midjourney Bot 来从文本生成图像。CogVideo 是第一个开源的大规模预训练文本转视频模型,拥有 94 亿个参数。
在这里,我将描述他们是如何工作的稳定扩散,文本到 3D,文本到视频。此外,让我们体验一下无需编码的文本到图像的美妙体验,看看接下来会发生什么。
稳定扩散和不稳定扩散
稳定扩散引入了条件潜在扩散模型(LDM ),以实现图像修复和类别条件图像合成的最新技术水平,以及在各种任务上极具竞争力的性能,包括文本到图像合成、无条件图像生成和超分辨率,同时与基于像素的 LDM 相比显著降低了计算要求。该方法可以在不降低质量的前提下,显著提高去噪扩散模型的训练和采样效率。
条件潜在扩散模型通过串联或更一般的交叉注意机制来解释(来源:潜在扩散模型
在释放稳定扩散的同时,StabilityAI 开发了一个基于 AI 的安全分类器,默认启用。它理解几代人的概念和其他因素,以消除用户不想要的输出。但是对于强大的图像生成模型,可以很容易地调整它的参数。
基于稳定的扩散,法师出现在浏览器中生成 NSFW 内容。这是直接和免费使用,没有 NSFW 滤波。
不要混淆。不稳定扩散是一个使用稳定扩散支持 AI 生成 NSFW 内容的社区。毫无疑问,这些模特可以在帕特伦和拥抱脸上找到。
文本转 3D 的 DreamFusion
谷歌和 UCB 共同推出了使用 2D 扩散的文本到 3D 生成的 DreamFusion。
DreamFusion 通过一种新颖的 SDS(分数提取采样)方法和一种新颖的 NeRF(神经辐射场)类渲染引擎,将可扩展的高质量 2D 图像扩散模型转移到 3D 域。DreamFusion 不需要 3D 或多视图训练数据,仅使用预训练的 2D 扩散模型(仅在 2D 图像上训练)来执行 3D 合成。[3]
DreamFusion 演示了从自然语言字幕生成 3D 对象(来源: DreamFusion )
如上所示,场景由针对每个字幕随机初始化并从头开始训练的 NeRF 来表示。NeRF 使用 MLP 参数化体积密度和反照率(颜色)。DreamFusion 从随机相机渲染 NeRF,使用从密度梯度计算的法线以随机照明方向对场景进行着色。阴影揭示了从单个视点看不清楚的几何细节。为了计算参数更新,DreamFusion 扩散渲染并使用(冻结的)条件 Imagen 模型重建渲染,以预测注入的噪声。
尽管 DreamFusion 产生了引人注目的结果,但它仍处于文本到 3D 的早期阶段。当应用于图像采样时,SDS 不是完美的损失函数。因此,在 NeRF 环境中,它经常产生过度饱和和过度平滑的结果,并且缺乏多样性。此外,DreamFusion 使用 64 × 64 Imagen 模型来平衡质量和速度。
为文本到视频制作视频
梅塔(又名脸书)在人工智能进化中从未落后。在 DreamFusion 宣布的同一天,Meta 推出了文本到视频生成的 Make-A-Video。
元制作视频高层架构(来源:制作视频)
根据上面的高层架构,制作视频主要有三层:1)。在文本-图像对上训练的基础 T2I(文本-图像)模型;2).时空卷积和注意力层将网络的构建模块扩展到时间维度;和 3)。时空网络由时空层和 T2V 生成所需的另一个关键元素组成,即用于高帧率生成的帧插值网络。[4]
因此,制作视频是建立在 T2I 模型上的,具有新颖实用的时空模块。它加速了 T2V 模型的训练,而不需要从头开始学习视觉和多模态表示。它不需要成对的文本-视频数据。并且生成的视频继承了广阔性(审美的多样性,奇幻的描绘等。)
谷歌的图像视频
Google 的 Imagen Video 是一个基于视频扩散模型级联的文本条件视频生成系统。
给定文本提示, Imagen Video 使用基本视频生成模型和一系列交错的空间和时间视频超分辨率模型生成高清视频。
它包括七个子模型来执行文本条件视频生成、空间超分辨率和时间超分辨率。整个级联以每秒 24 帧的速度生成 128 帧(~ 5.3 秒)的高清 1280×768(宽×高)视频,约为 1.26 亿像素。
Imagen 视频示例为"一束秋叶落在平静的湖面上,形成文字' Imagen 视频'。平稳。“生成的视频分辨率为 1280×768,时长 5.3 秒,每秒 24 帧(来源:成像视频)
稳定扩散无代码 AI
如上所述,我们可以看到扩散模型是文本到图像、文本到 3D 和文本到视频的基础。我们用稳定扩散来体验一下。
使用建议文本:亚马逊雨林中的高科技太阳能乌托邦
(作者使用稳定扩散)
正文:动漫女机器人头上长出花朵
(作者使用稳定扩散)
正文:从天空中看到的瑞尼尔山近景
由 DreamStudio 生成(左)与作者拍摄的照片(右)
你可能等不及了。下面是许多不用任何代码就可以尝试的方法。
下一步是什么?
生殖人工智能令人惊叹,发展迅速。当我们还沉浸在文本到图像的真实图像和艺术中时,我们现在正在进入下一个前沿领域:文本到视频和文本到 3D。
但是它是新生的,充满了关于相关性、高保真度、广阔性和效率的挑战。
- 相关性:我们注意到在相同的输入文本下,模型产生不同的(甚至显著不同的)结果,以及一些不相关的结果。在生成艺术的时候,如何用自然语言描述输入的内容似乎成了一门艺术。
- 高保真:来自 DALL-E 2 和稳定扩散的许多逼真图像给我们留下了深刻的印象,但它们都仍然有很大的高保真空间。
- 广阔:广阔是关于审美的多样性,荒诞的描绘,等等。它可以为各种输入提供丰富的结果。
- 效率:生成一张图像需要几秒几分钟。3D 和视频需要更长时间。例如,DreamFusion 使用更小的 64 × 64 Imagen 模型,通过牺牲质量来加快速度。
好消息是,它开启了许多令人兴奋的机会:人工智能工程、基础模型和生成性人工智能应用。
- 人工智能工程:人工智能工程对于实现 MLOps 自动化、提高数据质量、增强 ML 可观察性和自生成应用内容至关重要。
- 基础模型:独立训练和操作很多大型模型,成本很高,变得不现实。最终,它将统一或集成到几个基础模型中。这些大规模模型运行在云中,为上面的不同领域和应用服务。
- 生成式人工智能应用:有了人工智能工程和基础模型,这是一个巨大的应用机会,包括元宇宙和 NFT 空间的数字内容。例如,初创公司 Rosebud 专注于多样化的照片生成。
到 2025 年,生成式人工智能模型将产生 10%的数据。按照目前人工智能进化的速度,我们可以预计未来几年将会发生显著的变化。
参考
- 具有剪辑潜在性的分层文本条件图像生成:https://arxiv.org/abs/2204.06125
- 用潜在扩散模型合成高分辨率图像:https://arxiv.org/abs/2112.10752
- DreamFusion:使用 2D 扩散的文本到 3D:https://arxiv.org/abs/2209.14988
- 制作视频:没有文本-视频数据的文本-视频生成:https://arxiv.org/abs/2209.14792
- Imagen 视频:采用扩散模型的高清视频生成:https://arxiv.org/abs/2210.02303
遗传算法及其在机器学习中的实用性
原文:https://towardsdatascience.com/genetic-algorithm-6aefd897f1ac
GA 是什么,如何用它来训练机器学习模型?
在 Unsplash 上 Sangharsh Lohakare 拍摄的照片
介绍
遗传算法(GA)是一种自然计算算法,是通过复制自然界中存在的现象和行为来尝试解决问题的算法。
文献中有几种自然算法,如粒子群优化算法(PSO),它试图以数学方式模拟鱼群或鸟群的社会行为,以及蚁群优化算法(ACO),它试图在模拟蚂蚁移动行为的图中找到最佳路径。
这些类型的算法用于解决优化问题,其中您希望在搜索空间内找到最优值(最大值或最小值,取决于所分析的问题)。研究最多的优化问题之一就是所谓的 旅行商问题 。
在所有的自然计算算法中,最著名的是遗传算法。
概述
Pankaj Patel 在 Unsplash 上拍摄的照片
遗传算法是基于遗传学的概念,其中变换被应用于旨在试图复制诸如突变、自然选择和交叉等事件的数据。
简而言之,遗传算法迭代执行,并具有以下步骤:
- 初始种群的生成
对于每次迭代:
- 子群体(亲本)的选择
- 应用交叉和变异操作产生新的种群(子代)
- 对产生的新群体的评估
- 用新群体替换初始群体
算法将一直运行,直到达到停止条件,通常是最大迭代次数或评估生成的群体时获得的某个值。
重要的是要记住,要正确地执行算法,必须要有一个明确定义的问题要解决,并且要使用评估度量。
概念
Joshua Hoehne 在 Unsplash 上拍摄的照片
为了理解算法,有必要理解它背后的概念。
- 个体:代表问题唯一可能解决方案的群体成员。在数学上,它可以表示为一个值数组,其中数组中的每个值都是一个与所分析问题的响应方程相关联的系数。
- 群体:作为所分析问题答案候选人的一组个体。
- 比武:选择种群中最适个体的方法。它包括比较群体中两个(或更多)个体的结果,并选择获得最佳结果的一个。
- 交换:基因重组的操作,父母的遗传密码结合在一起产生孩子。定义一个(或多个)截止点,其中子代从父代 1 接收从遗传密码的开始到截止点的所有基因,并从父代 2 接收从截止点到遗传密码的结束的所有基因。下图(图 1)显示了一个具有一个截止点的交叉运算示例,其中每个单元格代表一个与响应方程相关的系数。
图 1 —交叉操作示例—作者图片
- 突变:将基因的值修改为不同于原始值的操作。图 2 显示了一个应用于个体的突变的例子。
图 2——第二个基因的突变操作示例——作者图片
既然已经定义了基本概念,那么让我们来看一个使用遗传算法来解决问题的例子。
这个例子
在机器学习中,一种广泛使用的算法是人工神经网络,它试图模拟人脑的行为进行决策。一种非常简单的网络被称为多层感知器(MLP ),它是由一个输入层、一个或多个中间层和一个输出层组成的网络。
下图(图 3)显示了一个 MLP 前馈神经网络的示例,其中间层包含四个神经元,隐藏层也包含四个神经元,输出层包含一个神经元。
图 3-带有一个隐藏层的前馈神经网络示例-作者图片
网络如何工作的细节不是本文的重点,知道输出 y 是通过使用网络各层和输入数据之间的权重的运算来计算的就足够了。
层权重是向量集,其中每个值对应于与不同层中的神经元之间的链接相关联的权重。目的是优化这些权重,以便网络可以解决问题。
让我们从定义图 3 中描述的 MLP 前馈的类开始。
现在,让我们一步一步地了解算法的功能。第一步是创建初始种群。下面的函数接收所需的个体数量(n_indiv ),并使用 MLPFeedforward 类返回随机初始化的个体总数(神经网络的权重)。
下面的函数执行 GA 的锦标赛步骤,从初始群体中随机选择两个父代,并将两个父代中的最佳父代(具有最大准确度的一个)存储在父代列表中。它接收人口和数据作为参数:特征()X)和标签( y )。
下一步是创建执行交叉和变异操作的函数。该函数的参数是双亲列表、决定是否会发生交叉(cross_thr)和突变(mutation_thr)操作的阈值以及限制操作中基因变化范围的突变限制。
最后,实现了一个包含整个算法的类。它还具有 run 方法,该方法多次执行算法,存储每次运行时获得的精度,并保存生成的最佳 MLP 及其性能。
现在,为了展示该算法,创建了一个具有 4 个特征、10.000 行和 2 个类的通用数据集,将其分为训练和测试数据(70%为训练样本,其余 30%为测试样本),并使用标准缩放器对其进行标准化。然后,使用 GA_MLPFeedforward 类来展示模型的性能如何随着遗传算法的使用而提高。算法中使用的参数是:
- 人口规模= 50
- 迭代次数= 200
- 交叉阈值= 0.5
- 突变阈值= 0.7
- 突变极限= [-5,5]
重要的是要说,所使用的参数没有优化,参数的优化对算法的结果起着非常重要的作用。
在执行算法之后,绘制了训练数据的精度演变,如下图 4 所示。
图 4——算法准确性的演变——作者提供的图片
最后,评估测试数据时模型的性能如图 5 所示。
测试数据的混淆矩阵和获得的准确度—图片由作者提供
Joshua Hoehne 在 Unsplash 上拍摄的照片
希望这篇文章能够让你快速理解遗传算法,以及如何在机器学习算法的开发中使用它。学习新的技能和做同样事情的方法总是好的。
欢迎任何意见和建议。
请随时通过我的 Linkedin 联系我,并查看我的 GitHub。
遗传算法——一种优化方法
原文:https://towardsdatascience.com/genetic-algorithm-an-optimization-approach-dd60e23261e6
使用遗传算法和 R 语言实现优化的介绍
照片:Unsplash
优化在任何商业领域都是一个非常重要的概念,无论是零售、金融、汽车还是医疗保健。简而言之,优化的目的是通过最小化/最大化损失/成本函数在搜索空间中找到一个点或一组点,这为我们提供了手头问题的最优解决方案。这里,我们试图最小化/最大化目标函数 f(x ),其服从一个/多个约束,例如,
Minimize f(x)
Subject to:
g(x) = 0 (equality constraint)
h(x) <= 0 (inequality constraint)
lb <= x <= ub (lb: lower bound, ub: upper bound)
本文将帮助您了解我们如何使用遗传算法(GA)优化问题陈述,遗传算法是最简单的进化算法(EAs)之一。
什么是遗传算法,它是如何工作的?
进化算法的基本直觉是从群体中选择最好的个体作为父母,要求他们繁殖以延长世代。在这个繁殖过程中,来自父母双方的基因交叉,在某种程度上,一个非遗传的错误发生了,这就是所谓的突变。然后下一代被要求繁殖他们的后代,这个过程继续下去。进化算法受交叉和变异理论的启发,其中交叉用于从群体的遗传信息中创建新的解决方案,而变异用于带来新的信息或保持群体中的多样性,并防止过早收敛以使解决方案更通用。
遗传算法是一种基于遗传和自然选择的生物进化原理的搜索优化技术。它通常用于从搜索空间中找到问题的最优或接近最优的解决方案,否则将花费大量时间来解决这些问题。现在,让我们来谈谈工作算法的基本原理。
作者图片
初始化人口
让我们先了解一下 GA 中人口是什么意思。
种群(或世代):它是所有可能解(候选解)的集合,用来发起搜索。遗传算法将迭代多代,直到找到最优解。
染色体:它代表群体中存在的一个特定(候选)解。
基因:是包含特定染色体的值(等位基因)和位置(基因座)的决策变量的单个元素。
作者图片
第一代随机初始化(随机初始化)。该算法通常从随机生成的群体开始。群体的大小取决于问题的性质,并由决策变量的大小决定。在所有世代中,种群的规模保持不变。此外,还有另一种选择,使用已知的启发式算法对问题(启发式初始化)初始化群体,但通常不推荐,因为这可能导致群体具有相似的解和非常小的多样性。
评估体能
适合度函数评估候选解决方案对目标的适合程度。它给每个个体一个适合度分数(概率值),基于这个分数,该个体将被选择用于繁殖。
通常,适应度函数可以被认为是一个目标函数,但是在存在多个目标和约束的复杂问题的情况下,适应度函数可以被不同地设计。
一个适应度函数应该具有以下特征:
适应度函数的计算速度应该足够快。
它必须定量测量给定解决方案的适合程度,或者从给定解决方案中产生的适合个体的程度。
选择父母
父代选择是从群体中识别最适合的解决方案的过程,它复制下一代解决方案。它有助于下一代自然地继承“好”的特征。在第 0 代中,我们没有任何后代,我们从初始群体中选择父母。
这种选择是非常关键的,因为它驱使个体找到更好和更合适的解决方案,从而导致算法的收敛。此外,亲本的关键特征之一应该是在群体中保持良好的多样性。选择亲本有不同的方法(这里看这里看)。
交叉
交叉有助于产生新的后代。由于遗传算法是基于随机的进化算法,从每个亲本携带的基因数量是随机的。来自双亲染色体的某些基因重叠或混合产生新的一代。由于后代是双亲染色体交叉的结果,它继承了双亲的特征。有时后代从父母一方获得一半基因,从另一方获得另一半基因,有时这种百分比会发生变化。
有不同的方法,如单点交叉,双点交叉,均匀交叉。均匀交叉是执行交叉的常用方法。在这里,基因是从亲代染色体中随机选取的。这里,遗传的基因被标记为 1,其他的被标记为 0,这来自于均匀分布(假设这些被称为 unif_value)。现在 unif_value 与每个父染色体的基因值相乘。父代选择和交叉操作重复多次,直到下一代中的解的数量达到 population_size(在所有代中保持相同的群体大小)。
作者图片
突变
突变有助于将新的特征包含到基因中,这基本上保持了种群内的多样性,并防止了过程的过早收敛。变异是遗传算法中与搜索空间的“探索”相关的部分。因此,变异对遗传算法的收敛至关重要,而交叉则不然。
满足标准
当算法达到其收敛时,即它不会产生与上一代明显不同的后代时,算法终止。通常,如果满足以下任一标准,算法就会终止:
如果在接下来的 X 次迭代中群体中没有适应度提高
如果目标函数已经达到最佳值或预定义值
如果已经达到最大代数
为什么选择遗传算法
在传统算法可能无法提供解决方案的大规模问题中,遗传算法(GA)能够“足够快地”提供“足够好”的解决方案。它为解决复杂的优化问题提供了一个通用的框架。以下是使用遗传算法的几个优点:
a)克服了传统优化算法的不足
如果我们能计算任意给定点的梯度,那么函数就是可微的。传统的算法如梯度下降法、牛顿法都是用导数的方法来获得最优解。它从一个随机的点开始,沿着梯度的方向移动,直到我们到达顶点。这种技术是有效的,并且对于我们具有单峰目标函数的线性回归类型的问题非常有效。但是,在现实世界中,我们有一个复杂的问题,有多个峰值和许多谷值(非凸目标函数)。在这里,传统的算法有陷入局部最优的固有趋势。
作者图片
然而,遗传算法不需要目标函数的梯度。它可用于目标函数不连续、不可微、随机或高度非线性的各种优化问题。
这可以很容易地并行化。
c) GA 相当快,在相对较短的计算时间内探索搜索空间。
d)可以包含多个复杂的优化目标
限制
a)如果执行不当,GA 可能不会收敛到最优解
b)可能计算量大,因为适应度函数被重复计算。
c)有时 GA 不允许硬约束,所以需要在目标函数中传递它们作为惩罚。惩罚函数降低不可行解的适应度,使得适应度随着违反的约束的数量或与可行域的距离成比例地降低。
作为用例的几个例子
a)库存优化:这有助于补货决策,从而避免缺货情况并因此导致销售损失。
b)商店空间优化:这可以通过重新安排空间来帮助增加商店的销售额,同时保留一些约束因素。
c)电源优化
实现:R 中的代码片段
# Define the functions:
pred_eval_fun_P <- function(x) {}: # Defining fuction for calculating predicted demand
orig_eval_fun_P <- function(x) {}: # Defining fuction for calculating actual demand
eval_g_ineq1 <- function(x) {}: # Creating 1st constraint
eval_g_ineq2 <- function(x) {}: # Creating 2nd constraint
fitness_P <- function(x) {}: # Creating the objective function and adding penalty as it can't take hard constraint. As example like below:
{
f <- pred_eval_fun_P(x) # maximise f(x)
p <- sqrt(.Machine$double.xmax) # penalty term
pel_int <- abs(eval_g_ineq1(x))
pel_int1 <- abs(eval_g_ineq2(x))
penalty1 <- # define as needed
penalty2 <- # define as needed
f-penalty1-penalty2 # fitness function value
}
lb: # Defining the lower bound
ub: # Defining the upper bound
no_cores <- detectCores() - 1
# Also define starting, max_gen, hard_gen as needed
# Run the optimization:
library(snow)
library(rgenoud)
cl1 <- makeCluster(no_cores, type="SOCK")
clusterExport(cl1, list("fitness_P","lb","ub","pred_eval_fun_P","eval_g_ineq1","eval_g_ineq2","starting","max_gen","hard_gen"))
GA_P1 <- genoud(fitness_P, Domains=cbind(lb,ub), nvars=length(lb), pop.size=100, max=TRUE,wait.generations = 50, max.generations = max_gen, hard.generation.limit=hard_gen, data.type.int=TRUE, cluster = cl1,print.level = 1, solution.tolerance = 0.001)
final_reco <- as.numeric(as.character(GA_P1$par))
stopCluster(cl1)
免责声明:本文中表达的观点是作者以个人身份发表的意见,而非其雇主的意见
基因组学——终极数据科学
原文:https://towardsdatascience.com/genomics-the-ultimate-data-science-part-i-ec91683d1e18
思想和理论
基因组学——终极数据科学
从数据科学的角度概述基因组学,强调数据和数据解释在理解生物学中的重要作用
首先,让我们解决所有权要求。二十多年前,当我开始上大学并上第一堂生物课时,我第一次翻开坎贝尔的《生物学》(第四版),生物学就成了我的主要兴趣。显然,我的观点是主观的,所以其他人可能会觉得金融、运筹学或推荐系统是数据科学的终极主题。
然而对我来说,生物学是一个完整的包裹。我对它了解得越多,它就越让我吃惊。它渗透了我对一切的想法和观点,从生孩子意味着什么,到对政治的看法,到理解不同社会如何互动,到理解当前的问题,如新型冠状病毒病毒的传播和气候变化。
这篇文章是介绍数据科学在基因组学中的作用的第一部分。它证明了我的标题声明,并介绍了基因组学中的一些基本概念,使其成为最终的数据科学。随后的帖子将对此进行扩展,每个帖子都讨论了一个特定的主题,并在相关时提供了代码示例。我的总体目标是与他人分享我对生物学和数据科学的热爱,尤其是它们的交集。希望这将帮助生物学家和数据科学家理解这些领域是如何交叉的。
我花了几年时间才真正理解,但是为什么基因组学是终极数据科学的关键来自生物学的三个不同特征,每一个都是‘啊哈!’属于我自己的时刻。
首先,我最终意识到的最重要的事情之一是,你必须抛弃所有关于生活会有多复杂的先入为主的观念。生活的复杂程度令人震惊。人们不得不怀疑它是否是无限的。这可以在所有尺度上看到,快速瞥一眼的生物数量可能会有助于说服你。
查尔斯·达尔文 1837 年绘制的进化树草图。图片来自维基共享资源。
第二个关键的洞见是理解为什么会这样。生物学中最重要的主题是进化。进化是生命如此复杂的原因。虽然达尔文早期关于进化的思想,包括他著名的草图,是基于合理的推理和一些逻辑跳跃,进化的研究现在是一门定量科学,也是基因组学是终极数据科学的另一个原因。
第三,由于进化是生物复杂性的驱动力,而且这种复杂性似乎是无限的,人们最终必须问自己,人类是否有能力理解它。人类没有任何理由能够理解生命是如何运作的。对我们来说,假设我们能够或者应该能够做到这一点是幼稚的。然而,人类擅长简化论,事实证明——非常幸运——地球上的生命有一个关键特征,它允许我们在生物学上站稳脚跟,形成基因组学领域,并创建最终的数据科学。
解开线——线是通往基因组学的大门
进化的要素之一是信息存储和复制。生命不是靠魔法运行的,所以生物学家知道这些信息必须在细胞内的某个地方找到。
DNA 的结构,显示了组成其结构的 4 个核苷酸的例子。用户 Zephyris 的图片来自维基共享
每个人现在都知道生命以某种方式编码在 DNA 中,尽管如何做到这一点并不广为人知。众所周知,DNA 是由一系列被称为核苷酸的化学单体组成的。它们是 4 种相关的小化学物质,每种都用一个字母表示:A(腺嘌呤)、C(胞嘧啶)、G(鸟嘌呤)和 T(胸腺嘧啶)。现在这被认为是理所当然的,但这是 1953 年的一个重大发现,它确实令人惊讶,信息存储机制简单到足以被人类理解。这种存储机制打开了基因组学领域的大门,几乎所有生物学的复杂性都以某种方式植根于这 4 个字母的字符串中。
尽管 DNA 占据了媒体的大部分版面,并渗透到了流行文化中,但它实际上并不是第一个被认为是由可以用字符串表示的不同化学物质的线性排序组成的生命成分。这个奖项颁给了蛋白质。
细胞的大部分特性是由其组成蛋白决定的。蛋白质是细胞的主力,负责许多细胞活动。许多人可能只在包装背面的营养标签上听说过与饮食有关的蛋白质,但这掩盖了蛋白质是什么以及它们是如何运作的。
大卫·s·古德塞尔 对大肠杆菌横切面中蛋白质的艺术再现。 RCSB 蛋白质数据库。doi:10.2210/rcsb _ pdb/good sell-gallery-028。
蛋白质真的是原始的乐高积木,尽管更灵活和迷人。大肠杆菌蛋白质的手绘图像是 T2 的大卫·古德塞尔的许多美丽画作中的一幅,这些画作表现了蛋白质的活动。我推荐看看他的其他画作,他的月度分子系列,当然还有他令人惊叹的书生命的机器。这些肯定会改变你对蛋白质是什么以及它们能做什么的印象。该图像代表了大肠杆菌细胞的一部分,并成功地传达了细胞不是无定形的斑点,而是由许多称为蛋白质的精致小机器人组成,每个机器人都有一个特定的三维结构来决定其功能。它们一起工作来协调细胞的活动。一些执行代谢过程并产生能量为细胞提供动力,一些聚集形成驱动细胞运动的复杂马达,一些发挥结构作用以使细胞保持结构完整性,等等。
Digizyme 公司的 Evan Ingersoll & Gael McGill 对细胞横截面中蛋白质的艺术再现。作者授予的许可。
对于大多数人来说,很容易在生活中没有真正思考过蛋白质的性质和功能,以及它们如何相互作用来执行细胞的功能,或者仅仅是它们的存在是多么多样和惊人。我希望上面的图片可以帮助说服你,但我必须首先提到上面的一点:你必须放弃所有关于生活可以有多复杂的先入为主的观念。上面的图像是细胞的美丽艺术渲染,但一个细胞中可能有数万种不同的蛋白质,它们不是静态的,它们经常在物理上相互作用以形成更大的功能结构,并且它们根据化学梯度、相邻的蛋白质、温度等不断反应和调整它们的行为。事实上,Digizyme 艺术家必须将场景中的蛋白质数量减少 80%以上,才能真正描绘出供人类消费的景观。
牛胰岛素的氨基酸组成。胰岛素由两条不同的氨基酸链组成,A 链和 B 链。每种氨基酸都有特定的化学结构,并用字母表中的一个字符来表示。图片作者。
当然,这些关于蛋白质的细节在 20 世纪上半叶都不为人知。众所周知,蛋白质执行特定的功能,但是确切地说,它们是如何执行这些功能的,以及它们采用了什么样的结构(如果有的话)却是未知的。正是在 20 世纪 40 年代末到 50 年代,蛋白质的大部分性质才被研究出来。由弗雷德·桑格领导的工作表明,这些神秘的蛋白质由一系列被称为氨基酸的化学物质组成,每个氨基酸都有一个单字母符号。这些氨基酸各有不同的结构,就像不同的 DNA 核苷酸有不同的结构一样。Sanger 指出牛胰岛素由两条独立的链组成,每条链都有特定的线性氨基酸序列。尽管在 1953 年发现 DNA 的双螺旋性质后胰岛素的全序列被发表,但是 A 链和 B 链的序列分别在 1952 年和 1951 年被确定。因此,在确定 DNA 可以表示为字符串之前不久,就确定了蛋白质可以表示为字符串的事实。当时,氨基酸排序的重要性还不为人知,因为不知道蛋白质是什么样子,也不知道它们是如何运作的。
在弗雷德·桑格表明蛋白质由线性氨基酸序列组成后不久,第一个蛋白质结构被确定,并于 1958 年发表。约翰·肯德鲁和他的同事确定了肌红蛋白(来自抹香鲸)的三维结构,提供了组成蛋白质的氨基酸线性序列决定其结构和功能的早期证据。
肌红蛋白的带状图,第一个发现的蛋白质结构。图片来自维基共享资源。
字符串和哈希——从 DNA 到蛋白质
伊曼纽尔·埃克斯特伦在 Unsplash 上拍摄的照片
到 20 世纪 50 年代中期,人们知道 DNA 和蛋白质分别是由 4 个不同的核苷酸(字符)和氨基酸(20 个字符)组成的聚合物(字符串),但它们之间关系的性质实际上在 20 世纪 40 年代就已经确定了。1949 年,奥斯瓦尔德·艾弗里和他的同事们决定性地证明了 DNA——而不是蛋白质——是将一种无害细菌“转化”成一种可能导致疾病的细菌所必需的,并且这是一种可遗传的变化。蛋白质是不够的。这表明 DNA 负责可遗传的变化,并且首次证明细胞中不同的蛋白质集合可能在 DNA 中的某个地方以某种方式编码。当然,细节决定成败。
到了 20 世纪 60 年代,人们意识到 DNA 编码蛋白质,但研究人员还没有真正测序基因组的技术。然而,他们确实有能力生成基因图谱,这使他们能够大致定位基因内的一些突变,即使他们不知道基因的实际序列甚至长度。此外,他们能够突变 DNA,但由于他们实际上不能读取产生的基因组来识别突变,他们不得不通过检测它的影响,或者用生物学的说法,它的表型来推断发生了突变。因此,当时的策略是突变基因和基因组(当然不是人类的),并确定基因或基因组中突变的大致位置。我们将在 python 中而不是在实验室中近似这种早期工作,以“重新发现”遗传密码的各个方面。
我们从随机突变一个基因开始,插入或删除一个碱基。这在基因的某处可能是这样的:ATCCG → AT G CCG 或 CCGAGT → CCGA T GT 用于插入,或 CG T CCG → CGCCG 或 ACG T AGT → ACGAGT 用于删除。这些只是假设的例子;实际的插入或删除操作(统称为插入)可以在基因的任何地方。在下面的图片中,每条水平线代表一个生物体的 20 个不同个体中长度未知的相同基因,每个圆圈代表随机插入一个碱基(红色带“+”)或随机删除原始基因中的一个碱基(灰色带“-”)。只显示保留基因功能的突变。在这种情况下,“功能性”意味着没有无义突变,即极有可能破坏基因功能的突变。这是一个过于简化的问题,但仍然是这个练习的一个很好的代理。为了节省空间,我省略了生成这些图像的大部分代码,但是大部分工作是由 MutatableSeqRecord 类完成的,该类继承了 biopython 的 SeqRecord 类,增加了向原始序列添加突变的能力:
这导致了下面的遗传图谱:
在 20 种不同的生物体中,沿着基因长度随机产生的可容忍的插入(+)或缺失(-)的位置,显示了沿着基因长度的突变的大致位置(0-开始,1-结束)。图片作者。
以上大致描述了 20 世纪中期遗传学是如何发展的。也就是说,已知基因由一长串有序的核苷酸组成(例如 ACTCGAGAGCCGATTA…或 ATGACACAGTTAGA),可以延伸数百或数千个核苷酸(字符),突变的大致位置可以沿其长度测量。除此之外,没有任何细节是已知的。然而,即使在上面的例子中,我们也可以获得一些启示。特别是,这些功能突变体中的所有突变都是在基因的右侧发现的,但不管是突变插入了还是缺失了一个碱基。这本身就很能说明问题,但可以有多种解释。让我们注意这一点:
- 靠近基因左侧的单碱基突变比靠近基因右侧的单碱基突变更有可能破坏基因的功能。
一个基因中也可以产生多个单碱基突变。我们将会看到我们可以从保留具有两个独立 indels 的功能基因中学到什么,使用与上面类似的代码,但为了节省空间,将它省去:
在 20 种不同的生物体中,沿着基因长度随机产生的 2 个容许的插入(+)或缺失(-)的位置,显示了沿着基因长度的突变的大致位置(0-开始,1-结束)。图片作者。
这是黄金。一个类似的模式出现在单一的 indels 中,但是有一些非常有趣的新信息。这些双重突变的功能基因中的 indels 通常仍然位于基因的右侧。然而,少数突变体(在这种情况下为 3/20)在基因左侧具有 indel,这些 indel 非常接近并且是相反的类型:一个插入和一个缺失。注意:突变体#2 具有相同的模式,但是 indel 位置如此接近以至于点是重叠的。我们在列表中添加了一个新的总体趋势:
- 如果靠近相反类型的 indel(插入与缺失相反),基因左侧的单碱基插入更可能被容忍
最后,让我们尝试 3 个独立的 indels,看看是否有更多我们可以提取的见解。根据上述趋势,我们已经知道这些可能会是什么样子。大多数功能突变体可能在基因的右侧有三个任何类型的突变位点,但少数在基因的左侧有两个相邻的相对突变位点,第三个突变位点在基因的右侧。让我们检查一下。这里有 20 个三重突变的功能基因:
在 20 种不同的生物体中,沿着基因长度随机产生的 3 个耐受的插入(+)或缺失(-)的位置,显示了沿着基因长度的突变的大致位置(0-开始,1-结束)。图片作者。
我们的假设被证实了。大多数突变是在基因的右侧发现的,但是在基因左侧突变的突变体与附近相反类型的 indel 配对。这并不能提供更多的见解,所以我们将通过生成三个突变体来缩小我们的关注范围,其中所有三个 indels 都位于左侧。我将继续使用蛮力方法来生成这些,因为它不会花很长时间来计算只有 20 个突变体。这将导致以下结果:
在 20 种不同的生物体中,沿着基因长度随机产生的 3 个容许的插入(+)或缺失(-)的位置,将突变体限制在所有 indels 都在基因前半部分的那些。x 轴显示沿着基因长度的突变的大致位置(0-开始,1-结束)。图片作者。
如你所见,功能性三重突变体中的 indel 位置有一个引人注目的模式,这是这个谜题的最后一块。在所有情况下,三个独立体紧密地聚集在一起,并且总是属于同一类型。我们将在列表中添加另一个观察结果:
- 如果在一个功能性三重突变基因的左边发现三个 indels,它们紧密地聚集在一起,属于同一类型
总之,我们有以下观察结果:
- 靠近基因左侧的单碱基突变比靠近基因右侧的单碱基突变更有可能破坏基因的功能。
- 如果靠近相反类型的 indel(插入与缺失相反),基因左侧的单碱基插入更可能被容忍
- 如果在一个功能性三重突变基因的左侧发现三个 indels,它们紧密聚集在一起,属于同一类型。
为了了解这里发生了什么,我们将使用一个类比。假设你的朋友给你发了一条短信,我们会认为这些短信在通信网络上很容易被破坏。消息存储为内存中连续的字节数组,这些数组通常会因内存连续块中某个位置的随机添加或删除而被破坏。在这种情况下,我们不使用前导位,因为在 ASCII 字符编码中它总是零,所以我们使用七位来表示每个字节。考虑到这一点,你看着你的手机阅读来电信息,内容如下:
Meet me on!)kKgICrACe_k]HAgKmK\AChAiQJA[CYXA]KCdAiQJAM__HAG_ke
这可不好。这句话几乎完全没有意义。你应该在某个时候去见你的朋友,但是你不知道时间和地点。很明显,该消息在靠近消息开头的某处被插入或删除的位所破坏。如果这发生在消息的结尾就好了。那样的话,你也许就能破译它了。照目前的情况来看,这条消息基本上没有什么作用。
假设您真的很不幸,消息被七个不同的位插入或删除破坏了。你现在绝对不走运。信息内容如下:
[KPA[JA_9SWOeK?W;OmK\AChAiQJA[CYXA]KAr the food g_ke
但是,如果这七种突变都是同一类型,并且发生在很近的地方,会怎么样呢?
Meet me on Tuesday a|U.{ seven at the mall near the food court
我们现在可以读了。虽然插入了七位,但消息仍然清晰可辨,因为只有插入附近的位受到影响,有效地将所有下游位向下游移动了一个字节的位。你可能还会想,在购物中心见面不再是任何人真正会做的事情,但不管怎样,希望这个例子能达到它的目的。
这与上述基因突变体中发生的现象完全相同,但它们的“字节”在计算机中相当于 3 个碱基,而不是 8 位。通过在附近插入(或删除)3 个碱基的倍数,只有蛋白质的局部受到影响,而蛋白质的其余部分保持完整。让我们停下来惊叹,地球上所有形式的生命都使用与人类数字存储信息完全相同的信息存储方法,这是多么令人惊讶。进化和人类——他们自己也是进化的产物——都发现了同样的储存信息的方法。
因此,概括地说,我们都知道数字信息可以存储为字节,每个字节由 2 字符字母表(位)中的 8 个字符组成,用于存储 ASCII 字符。这些 ASCII 字符被连续解码以拼写单词和组成句子。这些句子的作用是传达意思。类似地,生物信息可以存储为密码子而不是字节,每个密码子由 4 字符字母表(而不是 2 字符 0/1 字母表)中的 3 个字符(而不是 8 个)组成,以编码氨基酸。这些氨基酸被连续解码,以确定氨基酸的线性序列,该序列决定蛋白质的结构,从而决定其功能。这些蛋白质可能有许多功能。
地球生命中英文的数字位编码(ASCII)和通过 DNA 的蛋白质化学编码的比较。注意,尽管在 ASCII 编码中存在标点符号和其他非英语字符,但为了简单起见,这里只考虑了英语字母表的 26 个字母。图片作者。
这里唯一缺少的一块拼图是,我们知道将字节映射到字符的 ASCII 字符编码,因为人类开发了编码方案,但科学家在 19 世纪中期不知道遗传编码方案。他们只知道它的存在。不幸的是,他们没有谷歌或我们目前的生物学知识库。他们必须在实验室里解决。
在本文的第二部分,我将讨论遗传密码是如何被破译的,并继续详述由此产生的信息到底是做什么的。我会继续将它与英语进行比较,因为这样更直观,而且有许多相似之处。这样做,我将为理解基因组学作为一门数据科学奠定基础,并阐述为什么我相信它是终极数据科学。
请继续关注第二部分…
张量流概率简介:分布对象
概率深度学习
介绍
本文属于“概率深度学习”系列。这个每周系列涵盖了深度学习的概率方法。主要目标是扩展深度学习模型,以量化不确定性,即知道他们不知道的东西。
我们使用张量流和张量流概率(TFP)开发我们的模型。TFP 是构建在 TensorFlow 之上的 Python 库。我们将从可以在 TFP 中找到的基本对象开始,并了解如何操作它们。我们将在接下来的几周内逐步增加复杂性,并将我们的概率模型与现代硬件(如 GPU)上的深度学习相结合。
迄今发表的文章:
- 张量流概率简介:分布对象
- 张量流概率简介:可训练参数
- 张量流概率中从零开始的最大似然估计
- tensor flow 中从头开始的概率线性回归
- 使用 Tensorflow 进行概率回归与确定性回归
- Frequentist 与 Tensorflow 的贝叶斯统计
图 1:我们在这个系列中结合了两个世界:概率模型和深度学习(来源)
像往常一样,代码可以在我的 GitHub 中找到。
分发对象
单变量分布
分布对象捕捉概率分布的基本操作。让我们从最简单的形式开始——单变量分布。顾名思义,这些分布只有一个随机变量。高斯分布是完全由其均值和标准差定义的连续概率分布。它的标准型是μ = 0,σ = 1 的特例。
normal = tfd.Normal(loc=0, scale=1)
normal
<tfp.distributions.Normal 'Normal' batch_shape=[] event_shape=[] dtype=float32>
注意属性batch_shape
和event_shape
。event_shape
属性捕捉随机变量的维度。因为我们定义了一个单变量分布,所以event_shape
是空的。batch_shape
表示我们存储在对象中的不同独立分布对象的数量。在这种情况下,我们只存储一个发行版,因此它是空的。
我们可以用这个分布对象做什么?例如,我们可以从中取样。
# Draw one sample from the normal distribution
normal.sample()
<tf.Tensor: shape=(), dtype=float32, numpy=1.5117241>
# Draw 3 samples from the normal distribution
normal.sample(3)
<tf.Tensor: shape=(3,), dtype=float32, numpy=array([-1.2282027 , -0.01802123, 0.2748567 ], dtype=float32)>
在连续随机变量的情况下,我们还可以评估给定输入的概率密度函数(PDF)。
normal.prob(0.2)
<tf.Tensor: shape=(), dtype=float32, numpy=0.3910427>
当我们开始实施机器学习和深度学习算法时,我们经常会发现自己面临一个共同的问题:乘以概率并不有趣。整个产品开始迅速接近零,你将用尽精度来存储这么小的数字。
解决这个问题的常见方法是用对数概率代替概率。首先,通过应用对数变换,我们将域从[0,1]更改为(-∞,0)。这是相关的,因为我们可以在对数标度中存储大的负数,而不是必须存储非常小的数字(我们会很快用完精度)。第二,对数函数是一个单调递增的函数,这确保我们可以像比较原始对数概率一样比较两个转换后的对数概率。最后,对数转换将所有乘法运算转换为加法运算。因此,概率不再相乘,而是将对数概率相加。
在 TFP 中,处理对数概率是很简单的,我们只需要调用一个不同的方法。
normal.log_prob(0.2)
<tf.Tensor: shape=(), dtype=float32, numpy=-0.9389385>
注意,上面只是由prob
方法返回的值的自然对数。
np.log(normal.prob(0.2))
-0.9389385
最后,我们可以绘制一个直方图,它近似于分布的密度。
sns.histplot(data=normal.sample(10000).numpy(), kde=True);
图 2:标准高斯分布的直方图和核密度估计。
现在,让我们探索如何在单个对象中存储一批发行版。让我们创建两个单变量正态分布,一个μ=0,σ=1,另一个μ=1,σ=1。
normal_batch = tfd.Normal([0,1],[1,2])
normal_batch
<tfp.distributions.Normal 'Normal' batch_shape=[2] event_shape=[] dtype=float32>
请注意,这两个分布需要是相同的类型(在这种情况下,它们都是高斯分布),并且它们被认为是独立的,即这不是创建多元分布的方法。在上面的输出中,您可以看到batch_shape
现在是预期的 2。
我们可以很容易地从两种分布中抽取样本。
normal_batch.sample(3)
<tf.Tensor: shape=(3, 2), dtype=float32, numpy=
array([[-1.5531085 , 1.4462973 ],
[-0.5346463 , 0.63747466],
[-2.2433918 , -0.4113649 ]], dtype=float32)>
我们样品的形状不同。现在是(3,2),因为我们从每个正态分布中抽取 3 个样本。我们可以绘制这两种分布,只是为了确保它们没有任何关联。
samples = normal_batch.sample(10000).numpy()
ax = sns.scatterplot(x = samples[:,0], y = samples[:,1])
ax.set(ylim=(-10, 10), xlim=(-10, 10), xlabel='N(0,1)', ylabel='N(1,2)');
图 3:两个独立高斯分布的样本。
同样,我们可以得到两种分布的 pdf 值。
normal_batch.prob([1,2])
<tf.Tensor: shape=(2,), dtype=float32, numpy=array([0.24197073, 0.17603266], dtype=float32)>
我们得到一个返回的形状为(2)的张量,因为我们得到了两个不同点的分布的 PDF 值。
为了更好地理解形状的行为,让我们增加batch_shape
的等级。
normal_batch_D = tfd.Normal([[[0., 2],
[0.5, 1],
[5, 2]]], scale=1)
normal_batch_D
<tfp.distributions.Normal 'Normal' batch_shape=[1, 3, 2] event_shape=[] dtype=float32>
我们现在有 6 个不同的高斯分布存储在同一个对象中。不运行代码能说出normal_batch_D[0,2,1]
的均值和标准差是多少吗?
像往常一样,我们可以从中取样。
normal_batch_D.sample(10)
<tf.Tensor: shape=(10, 1, 3, 2), dtype=float32, numpy=
array([[[[-0.94896364, 2.1813042 ],
[ 0.14763275, 0.22235268],
[ 4.8377185 , -0.19643283]]],
[[[ 0.6483533 , 2.3491006 ],
[-0.11504221, 0.13614637],
[ 5.2141023 , 1.9243499 ]]],
[[[ 0.14039962, 1.5450974 ],
[ 1.134828 , 2.2807612 ],
[ 5.892858 , 0.6847892 ]]],
[[[ 1.3779826 , 2.0819554 ],
[ 1.0930698 , 0.5755873 ],
[ 4.71762 , 2.248595 ]]],
[[[ 0.21968068, 1.2137487 ],
[ 1.3834007 , -0.17327452],
[ 5.6132197 , 2.4669297 ]]],
[[[-0.7178315 , 1.1999301 ],
[-0.19561946, 0.14512819],
[ 3.7195773 , 1.3497605 ]]],
[[[ 0.03890136, 2.9174664 ],
[ 0.37707615, -1.6925879 ],
[ 4.0377812 , 2.6682882 ]]],
[[[ 1.4869312 , 2.2919848 ],
[ 1.1833754 , 0.78834504],
[ 4.746928 , 2.398845 ]]],
[[[-2.2711177 , 1.9751831 ],
[ 2.855303 , -0.51687765],
[ 5.6308627 , 0.96069396]]],
[[[-0.5072157 , 1.7689023 ],
[ 0.67927694, 0.30969065],
[ 3.8056169 , 3.4717598 ]]]], dtype=float32)>
我们得到一个形状为(10,1,3,2)的张量,这意味着我们有 10 个样本(第一维)用于 6 个高斯分布中的每一个。
多元分布
现在,是时候探索我们的分配对象的event_shape
属性了。创建多元分布有多种方法,让我们从最简单的一种开始。我们可以定义一个二维高斯分布,并且不包括两个维度之间的任何相关性,这意味着协方差矩阵的非对角项为 0。
mv_normal = tfd.MultivariateNormalDiag(loc=[0, 1], scale_diag=[1., 2])
mv_normal
<tfp.distributions.MultivariateNormalDiag 'MultivariateNormalDiag' batch_shape=[] event_shape=[2] dtype=float32>
最后,我们看到填充了一个event_shape
,在本例中值为 2。正如我们所说的,这是我们上面定义的随机变量的维数。
是时候从中抽取样本,并查看与多个批处理分布相比的差异了。
mv_normal.sample(3)
<tf.Tensor: shape=(3, 2), dtype=float32, numpy=
array([[ 1.4743712 , -0.77387524],
[ 1.7382311 , 2.747313 ],
[-1.3609515 , 4.8874683 ]], dtype=float32)>
形状与我们从两个分批分布中采样 3 次的示例相同。由于我们已经定义了一个具有非对角协方差矩阵的多元分布(维度是独立的),我们得到了类似的结果——比较图 4 和图 3。
图 4:具有独立维度的多元高斯分布的样本。
是时候将这两个概念结合起来,定义一批多元分布了。
normal_diag_batch = tfd.MultivariateNormalDiag(loc=[[0,0], [1,1], [0,0]],
scale_diag=[[1,2], [1,1], [2,10]])
normal_diag_batch
<tfp.distributions.MultivariateNormalDiag 'MultivariateNormalDiag' batch_shape=[3] event_shape=[2] dtype=float32>
我们现在可以看到,batch_shape
和event_shape
都被填充了,值分别为 3 和 2。这意味着我们已经创建了 3 个二维高斯分布。
samples = normal_diag_batch.sample(10000).numpy()
samples.shape
(10000, 3, 2)
当从上面的对象中采样时,我们得到一个具有以下维度的输出:(number_samples, batch_shape, event_shape)
。
让我们探索计算这个对象的对数概率。您希望看到的输出是什么样的?
normal_diag_batch.log_prob(samples)
<tf.Tensor: shape=(10000, 3), dtype=float32, numpy=
array([[-2.5595174, -1.8528149, -5.7963095],
[-3.219818 , -2.9775417, -9.757326 ],
[-5.3475537, -1.8487425, -6.300317 ],
...,
[-3.3621025, -3.5017567, -6.5536766],
[-2.7969153, -2.8572762, -5.0501986],
[-3.2498784, -2.2819252, -7.9784765]], dtype=float32)>
因为我们有 3 个分布(尽管它们是二维的),所以我们得到一个输出(number_samples, batch_shape)
。为什么?
我们可以绘制 3 个分布的样本,以比较协方差矩阵中不同对角线值的影响程度。
plt_sample_batch = normal_diag_batch.sample(10000).numpy()
fig, axs = (plt.subplots(1, 3, sharex=True, sharey=True, figsize=(10, 3)))
titles = ['cov_diag=[1,2]', 'cov_diag=[1,1]', f'cov_diag=[2,10]']
for i, (ax, title) in enumerate(zip(axs, titles)):
samples = plt_sample_batch[:,i,:]
sns.scatterplot(x=samples[:,0], y=samples[:,1], ax=ax)
ax.set_title(title)
axs[i].set_ylim(-25, 25)
axs[i].set_xlim(-25, 25)
plt.show()
图 5:具有不同对角协方差矩阵的 3 个多元高斯分布的样本。
请注意,在此图中,任何分布都没有出现相关性。即便如此,第三分布在 y 轴方向上被极度拉长。这是因为对角线上的第二个值比第一个值大,第二个维度的标准偏差比第一个维度的标准偏差大得多,因此我们看到了 y 方向上的分布。
结论
本文介绍了理解 TFP 中的核心对象——分布对象的第一步。我们首先定义单变量分布,操作对象的batch_shape
属性,并测试不同的方法:sample
、prob
和log_prob
。接下来,我们增加了随机变量的维数,并引入了多元分布。在这种情况下,我们探索了处理对象的batch_shape
和event_shape
属性的方法,允许我们在单个对象中存储多个多元分布。
下周,我们将探讨如何训练这些分布对象的参数。到时候见!
保持联系: LinkedIn
参考资料和材料
[1] — Coursera:深度学习专业化
[2] — Coursera:深度学习的 tensor flow 2专业化
[3] — 张量流概率指南和教程
[4] — TensorFlow 博客中的 TensorFlow 概率帖子
张量流概率简介——可训练参数
概率深度学习
介绍
本文属于“概率深度学习”系列。这个每周系列涵盖了深度学习的概率方法。主要目标是扩展深度学习模型,以量化不确定性,即知道他们不知道的东西。
我们使用张量流和张量流概率(TFP)开发我们的模型。TFP 是构建在 TensorFlow 之上的 Python 库。我们将从可以在 TFP 中找到的基本对象开始,并了解如何操作它们。我们将在接下来的几周内逐步增加复杂性,并将我们的概率模型与现代硬件(如 GPU)上的深度学习相结合。
迄今发表的文章:
- 张量流概率简介:分布对象
- 张量流概率简介:可训练参数
- 张量流概率中从零开始的最大似然估计
- tensor flow 中从头开始的概率线性回归
- 使用 Tensorflow 进行概率回归与确定性回归
- Frequentist 与 Tensorflow 的贝叶斯统计
图 1:我们在这个系列中结合了两个世界:概率模型和深度学习(来源)
像往常一样,代码可以在我的 GitHub 上找到。
分发对象
在上一篇文章中,我们看到了如何操作 TFP 分布对象。记住分布对象捕捉概率分布的基本操作。我们从单变量分布开始,即只有一个随机变量的分布。然后,我们将我们的理解扩展到如何用分布对象属性来表示多元分布。我们保持简单,因为我们定义了一个 2 维高斯分布,并且没有包括两个维度之间的任何相关性。要回忆的最重要的属性是batch_shape
和event_shape
。如果您对它们还不满意,请查看我以前的文章。我们将在本系列中广泛使用它们。
在介绍可训练的分布参数之前,我们将再介绍一个关于分布对象的概念。
独立分布
有些情况下,我们希望将事件空间上的一批独立分布解释为事件空间乘积上的单个联合分布。这影响了我们处理batch_shape
和event_shape
属性的方式。当我们开始构建一些众所周知的算法(如朴素贝叶斯分类器)时,独立分布将非常有用。原因是在朴素贝叶斯的情况下,给定一个类标签,特征是独立的。
为了说明,让我们定义两个正态分布。
第一个是以下形式的多元常态:
为了定义第一个,我们将像以前一样使用MultivariateNormalDiag
,因为,再一次,它们之间的维度是不相关的。
mv_normal = tfd.MultivariateNormalDiag(loc=[0, 1], scale_diag=[1,2])
<tfp.distributions.MultivariateNormalDiag 'MultivariateNormalDiag' batch_shape=[] event_shape=[2] dtype=float32>
我们对形状属性越来越满意,因此毫不奇怪我们的event_shape
为 2。
像往常一样,我们可以计算对数概率:
mv_normal.log_prob([0.2, 1.5])
<tf.Tensor: shape=(), dtype=float32, numpy=-2.5822742>
我们得到一个单一的值,因为我们有一个单一的分布,即使它是多维的。
让我们从独立的多元高斯分布中取样,并绘制联合分布图。我们以前做过类似的事情。
samples = mv_normal.sample(10000).numpy()
x1 = samples[:,0]
x2 = samples[:,1]
sns.jointplot(x = x1, y = x2, kind='kde', xlim=[-6, 7], ylim=[-6, 7]);
图 2:上面定义的多元高斯分布的近似联合分布图。还显示了单个维度的单变量图。
正如所料,多元高斯分布的维数之间没有相关性。
表示批量高斯分布对象的时间。
locs = [0,1]
scales = [1, 2]
batched_normal = tfd.Normal(loc=locs, scale=scales)
batched_normal
<tfp.distributions.Normal 'Normal' batch_shape=[2] event_shape=[] dtype=float32>
注意batch_shape
等于 2。
batched_normal.log_prob([0.2, 1.5])
<tf.Tensor: shape=(2,), dtype=float32, numpy=array([-0.9389385, -1.6433357], dtype=float32)>
由于我们在同一个对象中存储了两个独立的分布,因此计算对数概率会产生两个值。
我们可以绘制两个单变量分布的概率密度函数(PDF)。
x = np.linspace(-4, 4, 10000)
densities = batched_normal.prob(np.repeat(x[:, np.newaxis], 2, axis=1))
sns.lineplot(x=x, y=densities[:, 0], label=f'loc={locs[0]}, scale={scales[0]}')
sns.lineplot(x=x, y=densities[:, 1], label=f'loc={locs[1]}, scale={scales[1]}')
plt.ylabel('Probability Density')
plt.xlabel('Value')
plt.legend()
plt.show()
图 3:作为单个分布对象批处理的两个单变量高斯分布的 PDF。
让我们总结一下上面的内容,这样我们就可以介绍独立分布对象了。很明显,当第一个分布对象返回一个对数概率时,第二个返回 2。不同之处在于,我们传递给第一个的数组被解释为一个二维随机变量的单一实现。在第二种情况下,数组被解释为每个随机变量(批次)的不同输入。
为了帮助我们理解什么是独立分布以及它是如何有帮助的,我们来玩一些概率术语:
- 独立分布是我们从单变量分布转移到单个多变量分布的一种简化方法;
- 独立分布允许我们从单个随机变量的几个分布转移到一组随机变量的联合分布;
- 独立分布提供了从几个批量分布转移到一个多维分布的能力;
- 独立分布是一个接口,将我们想要吸收的任何维度吸收到事件维度;
- 最后,更实用和 TFP 的描述方式——独立分布是一种将分布的
batch_shape
维度移动到新分布对象的event_shape
的方式。
希望用这么多不同的方式来描述它能使所有这些概率概念以及它们被转化为 TFP 抽象的方式更加清晰。
是时候应用理论概念并查看实际实现了。
independent_normal = tfd.Independent(batched_normal, reinterpreted_batch_ndims=1)
independent_normal
<tfp.distributions.Independent 'IndependentNormal' batch_shape=[] event_shape=[2] dtype=float32>
批量高斯分布现在是一个IndependentNormal
分布对象,它是一个独立的多元高斯分布,如我们上面定义的。我们可以通过 2 的event_shape
看出来。类似地,对数概率现在应该产生一个值。
<tf.Tensor: shape=(), dtype=float32, numpy=-2.5822742>
最后,让我们将独立高斯分布图与上面显示的图进行比较。
samples = independent_normal.sample(10000).numpy()
x1 = samples[:,0]
x2 = samples[:,1]
sns.jointplot(x = x1, y = x2, kind='kde', space=0, color='b', xlim=[-6, 7], ylim=[-6, 7]);
图 4:独立高斯分布对象的近似联合分布图。还显示了单个维度的单变量图。
可训练参数
变量
现在我们知道了什么是张量流概率对象,是时候了解如何为这些分布训练参数了。这是我们开始应用我们所学的知识和构建算法时所缺少的联系。
在 TensorFlow 中,Variable
对象是我们用来捕捉深度学习模型的参数值的东西。这些对象在训练期间通过例如应用从损失函数和数据获得的梯度来更新。
我们来定义一个。注意,要创建一个新变量,我们必须提供一个初始值。
init_vals = tf.constant([[1.0, 2.0], [3.0, 4.0]])
new_variable = tf.Variable(init_vals)
new_variable
<tf.Variable 'Variable:0' shape=(2, 2) dtype=float32, numpy=
array([[1., 2.],
[3., 4.]], dtype=float32)>
A Variable
非常类似于张量。它们具有相似的属性,例如形状和数据类型,以及方法/操作,例如导出到 NumPy。虽然他们有一些不同,例如,他们不能被重塑。
print("shape: ", new_variable.shape)
print("dType: ", new_variable.dtype)
print("as NumPy: ", new_variable.numpy())
print("Index of highest value:", tf.math.argmax(new_variable))
shape: (2, 2)
dType: <dtype: 'float32'>
as NumPy: [[1\. 2.]
[3\. 4.]]
Index of highest value: tf.Tensor([1 1], shape=(2,), dtype=int64)
请注意,如果出于某种原因,您不希望在训练过程中对某个变量进行微分,您可以使用参数trainable
来定义它。
variable_not_diff = tf.Variable(1, trainable=False)
<tf.Variable 'Variable:0' shape=() dtype=int32, numpy=1>
总之,通常,我们希望我们的变量是可微的。张量流允许自动微分,这是用于训练神经网络的反向传播算法的基础。
我们将使用一个 API 来完成自动微分— tf.GradientTape
。连接回Variable
对象,这个 API 让我们能够计算操作相对于输入的梯度,即一个或多个Variable
对象。
让我们用tf.GradientTape
API 和Variable
对象做一个简单的例子。
x = tf.Variable(3.0)
with tf.GradientTape() as tape:
y = x**2
一旦我们在tf.GradientTape
上下文中定义了一个操作,我们就可以调用gradient
方法并传递损失和输入变量。
dy_dx = tape.gradient(y, x)
dy_dx.numpy()
6.0
是时候把这些概念应用到我们的问题上了。回想一下,我们对学习分布的参数感兴趣。
normal = tfd.Normal(loc=tf.Variable(0., name='loc'), scale=5)
normal.trainable_variables
(<tf.Variable 'loc:0' shape=() dtype=float32, numpy=0.0>,)
在这种情况下,上面定义的高斯分布的均值不再是一个简单的值,而是一个可以学习的Variable
对象。
对于训练过程,最大似然是深度学习模型中通常的疑点。简而言之,我们正在寻找使数据概率最大化的模型参数。
连续随机变量的概率密度函数粗略地表示样本取特定值的概率。我们将把这个函数表示为𝑃(𝑥|𝜃),其中 𝑥 是样本值, 𝜃 是描述概率分布的参数:
tfd.Normal(0, 1).prob(0)
<tf.Tensor: shape=(), dtype=float32, numpy=0.3989423>
这可能看起来很奇怪,但事实上,我们已经计算高斯分布的 PDF 有一段时间了,所以这里没有什么特别新的东西。
为了完成对训练参数的介绍,让我们将这个概念与我们在上面分享的独立分布对象联系起来。当从同一个分布中独立地抽取多个样本时(我们通常假设),样本值 𝑥 1、…、 𝑥𝑛 的 PDF 是每个个体 𝑥𝑖 的 pdf 的乘积。我们可以把它写成:
希望您能在上面的定义中看到这两个概念是如何重叠的。
结论
本文继续探讨 TFP 中的分布对象,但这次我们更进一步,将它们与 TensforFlow 中的Variable
对象联系起来。我们从定义什么是独立分布以及它如何帮助我们定义独立联合概率开始。它允许我们从单变量分布转移到独立的多变量分布,从而将我们想要的任何维度吸收到事件维度中。接下来,我们介绍了Variable
对象,以及如何利用自动微分来区分它们。有了这些知识,我们将它们与 TFP 中的一个分发对象结合使用。最后,我们讨论了最大似然法,以及当我们从同一个分布中独立抽样时,它与独立联合分布的关系。
下周,我们将更详细地探讨发行版的培训过程。到时候见!
保持联系: LinkedIn
参考资料和材料
[1] — Coursera:深度学习专业化
[2] — Coursera:深度学习的 tensor flow 2专业化
[3] — 张量流概率指南和教程
[4] — TensorFlow 博客中的 TensorFlow 概率帖子
地球同步轨道提升试验 I:理解差异中的差异
这是 2 部分系列的第 1 部分,着眼于产品环境中的 geo lift 实验:
- 理解差异中的差异
- Spotify Blend 案例研究
在这一部分,我们将探讨为什么以及何时进行 geo lift 实验。我们还将探索差异中的潜在差异因果推断方法。
最近 AB 测试已经成为产品开发团队的黄金标准。在产品上运行实验有助于团队理解每个变化对他们的关键度量的增量效应,并逐渐改进他们的产品。然而,有时随机地向一些用户推出一个特性,而不向另一些用户推出,会导致不准确的结果。这可能是由于网络效应、库存溢出,或者更普遍的情况,即稳定单位处理值假设 (SUTVA)不成立。这是假设一个随机单元参与产品变更的方式独立于任何其他单元参与相同产品变更的方式。随机化单元是我们对每个变量进行随机分配的层次。在大多数情况下,这是个人用户,但也可以是设备、会话或社交群等。让我们看一些例子来更好地理解这个概念。
特别是对于社交产品,许多功能需要双向参与才能发挥作用。随机地向用户显示一个特征变化可能导致那些用户中的一些没有适当的机会参与其中,因为不是他们所有的朋友都可以访问相同的特征变化。因此,测试中衡量的成功标准可能无法真正代表将变更推广到所有用户的效果。
同样,违反 SUTVA 是电子商务和旅游产品在试验决定向用户显示哪些项目的算法时面临的问题之一,如个性化和搜索算法。例如,在 Airbnb 上,当一种算法向用户显示他们继续预订的房间时,该房间不再向任何被分配了不同算法的用户显示。因此,这两个算法的参与度指标是不一致的,因为其中一个算法的参与度并不独立于另一个算法。
通过改变实验的设计,我们有多种方法可以控制这些。例如,在社交网络的情况下,我们可以将网络分成彼此交互频繁的用户群,并在群集级别执行随机分配。然而,实现更复杂的实验设计会有很大的开发成本。因此,有时我们可能没有资源来进行实验。
这个问题的一个潜在解决方案是以 geo lift 实验的形式运行一个准实验。这个想法是向特定地区的所有用户发布特性更改。然后,将该地区的关键成功指标与未发布功能变更的控制地区的成功指标进行比较。然后,我们使用这种比较来理解该特性对我们的关键成功指标的因果影响。值得注意的是,这只能作为一种解决方案,前提是假设将实验限制在特定的地理区域代表了向您的整个用户群发布该功能。以社交产品为例,这只能作为一种解决方案,前提是假设对于大多数用户来说,他们在产品上与之交互的所有其他用户都来自相同的地理位置。
这种方法是基于差异的因果推断方法。使用这种方法测量的关键成功指标的提升不像运行一个设计良好的实验那样是因果影响的有力指标,但它更足智多谋,并且(在大多数情况下)设置和推出更快。
这种方法也是控制基于时间的混杂因素的好方法。例如,在进行实验的同时开展普遍获得活动或销售。或者,对于样本量较小的公司,基于时间的混杂因素可能只是自然变异。然而,该方法的进一步假设是,任何基于时间的混杂因素都适用于两个地理区域。例如,新的普遍获得营销活动必须在控制和测试地区同时进行。为了理解为什么需要这样,让我们更深入地研究一下这种方法背后的因果推理技术。
差异中的差异
差异中的差异方法使用两个相似的用户群,并在功能变更发布前后对关键成功指标进行建模。我们的一个用户群被作为对照,在准实验期间没有接收到特征变化。另一个用户部分进行处理,并接收特征变化。对于每项成功指标,我们建立了以下模型:
- 控制预发布(
a
) - 控制后释放(
x
) - 治疗前释放(
b
- 治疗后释放(
y
两个地区之间的发布前差异由b — a
给出,两个地区之间的发布后差异由y — x
给出。然后,我们可以将由于特征变化导致的整体抬升建模为这些差异之间的差异,因此该方法得名。
uplift = post_release_diff - pre_release_diff
= (y - x) - (b - a)
此图显示了不同方法的不同之处。geo 之间的发布后差异等于发布前差异+由于特征变化而产生的隆起。(图片由作者提供)
现在我们对 geo lift 实验的工作原理有了更好的了解,让我们来看看在第 2 部分中使用这种方法的真实产品示例。我们将使用一个名为 PyMC 的 python 包,使用我们观察到的数据对我们的成功指标进行建模。它使用贝叶斯统计和蒙特卡罗马尔可夫链 (MCMC)方法来建模指标。我们将把这个应用到 Spotify Blend 功能中。
感谢阅读这篇文章!我希望它能帮助你更好地理解什么时候使用这种因果推理方法是有用的。
如果你喜欢阅读我的文章,愿意支持我的写作,并且正在考虑订阅一个媒体,请随时使用我下面的推荐链接。我会从你的订阅费中提成。
https://medium.com/@kaushsk12/membership
Geo Lift 实验 II: Spotify Blend 案例研究
原文:https://towardsdatascience.com/geo-lift-experiments-ii-spotify-blend-case-study-476a81099744
这是两部分系列的第 2 部分,介绍产品环境中的 geo lift 实验:
- 理解差异中的差异
- Spotify Blend 案例研究
在这一部分,我们将把我们在第一部分中探索的内容应用到案例研究中。让我们深入案例研究。
产品背景:Spotify Blend
假设我们是 Spotify 播放列表体验团队的项目经理。对于任何不熟悉这个功能的人来说,这是一个允许用户与他们选择的朋友创建播放列表的功能。然后,播放列表会自动生成用户及其朋友都喜欢的歌曲。它还向用户显示他们的音乐品味和朋友的音乐品味之间的匹配分数。当用户收听新音乐时,该品味匹配分数改变,并且播放列表提供的推荐变得更好。
作为这个特性的项目经理,让我们假设我们发现用户在设置混合的体验上有一些摩擦。邀请朋友加入 blend 时,会向用户显示一个本机共享模式,以便与朋友共享邀请链接(如下所示)。当朋友们点击与他们分享的链接时,他们就会被添加到混合中。让我们假设,查看数据,我们看到在用户点击邀请按钮和他们的朋友成功接受他们的邀请之间有相当大的落差。我们没有太多的数据来说明这到底是为什么,因为这可能是由于用户没有通过共享模式发送邀请。也可能是朋友没有回复邀请信息。
点击 Spotify Blend 功能上的邀请按钮时的原生共享模式。(图片由作者提供)
假设
我们假设,通过在 Spotify 应用程序中保持这种体验,用户更有可能成功邀请他们的朋友,并创建一个混合。我们希望更改这部分功能,允许用户通过用户名(或从关注者下拉列表中选择)搜索用户,以便将他们添加到混合中。作为一个数据驱动的项目经理,我们希望将此作为一个实验来验证我们的假设和假说。
问题
由于多个用户之间的交互对该功能至关重要,因此一个用户使用该功能的方式并不独立于另一个用户使用该功能的方式。因此,如果我们将此作为传统的 AB 测试来运行,将每个用户作为随机单元,那么我们将违反 SUTVA。从用户的角度来看,如果用户可以在 Spotify 中搜索并邀请他们的朋友,但他们的朋友在控制组中,看不到邀请,这将中断他们的体验,并影响他们使用该功能的方式。
解决这个问题的一个方法是将网络分成互相跟随的用户群,并在群集级别执行随机分配。然而,由于这是 Spotify 的首批多用户体验之一,让我们假设没有任何基础设施来运行这种类型的实验。就开发资源而言,建立基础设施的成本很高,我们希望更有资源来更快地迭代。
让我们假设,通过一些探索性的数据分析,我们发现 98%的用户只创建与来自他们自己地理位置的其他用户的混合。通过将我们的特征变化限制在特定的地理区域,我们非常接近于重建 SUTVA。因此,这似乎是一个地球同步轨道提升实验的绝佳候选。我们承认,地理提升实验的结果并不像我们要进行的社交聚类实验或如果我们 100%的用户只与来自他们自己地理的用户创建混合那样是一个强有力的因果关系指标。但是我们认为,如果把这个实验作为一个地理提升实验来运行,我们会得到这个变化的影响的一个很好的指示。
我们的控制地理位置将为geo_1
,我们的治疗地理位置将为geo_2
。我们将只在geo_2
发布改变,我们相信这将增加点击邀请按钮的用户被至少一个朋友接受邀请的可能性。因此,我们的成功度量将是表示用户是否成功邀请 1 个朋友加入混合的转换度量。
混杂因素
让我们假设我们已经仔细地选择了geo_1
和geo_2
,这样我们可以控制的任何混杂因素都可以在两个地理区域一致地应用。任何可能对此功能的指标产生影响的普遍获得活动都适用于两个地区。
现在我们已经清楚了上下文,让我们假设我们已经运行了这个实验一段时间,并且已经收集了足够的数据。所以让我们深入分析一下。
分析
在开始分析之前,值得注意的是,这里使用的数据是人工生成的,因此已经是本练习的理想格式。在现实世界中,我们可能需要执行一些 ETL 操作来获得这种格式的数据。然而,这超出了本文的范围。这些数据也不代表 Spotify 的实际用户参与度数据。
让我们导入依赖项并研究实验数据。
import pandas as pd
import numpy as np
import pymc3 as pm
import theano.tensor as tt
import matplotlib.pyplot as plt
import arviz as azexperiment_data = pd.read_csv('experiment_data.csv')experiment_data.head()
我们看到数据包含一个散列来表示用户 Id,用户在哪个组中,转换后的列表示用户的邀请是否成功。让我们继续总结不同组的转化率。
我们将把每组的转换率建模为一个具有 Beta 分布的变量,就像我们之前在做转换率的贝叶斯建模时所做的那样。我们不会通过检查来估计每个 Beta 分布的参数,而是将它们建模为 0 和相应组样本大小之间的均匀分布变量。所以让我们开始这样做:
diff_model = pm.Model()with diff_model:
geo_1_pre_a = pm.Uniform('geo_1_pre_a', lower=0, upper=1367)
geo_1_pre_b = pm.Uniform('geo_1_pre_b', lower=0, upper=1367)
geo_1_post_a = pm.Uniform('geo_1_post_a', lower=0, upper=1893)
geo_1_post_b = pm.Uniform('geo_1_post_b', lower=0, upper=1893)
geo_2_pre_a = pm.Uniform('geo_2_pre_a', lower=0, upper=1522)
geo_2_pre_b = pm.Uniform('geo_2_pre_b', lower=0, upper=1522)
geo_2_post_a = pm.Uniform('geo_2_post_a', lower=0, upper=1408)
geo_2_post_b = pm.Uniform('geo_2_post_b', lower=0, upper=1408)
既然我们已经对分布参数进行了建模,我们就可以对每个转换率的分布进行建模了。
with diff_model:
geo_1_pre_cr = pm.Beta('geo_1_pre_cr', alpha=geo_1_pre_a, beta=geo_1_pre_b)
geo_1_post_cr = pm.Beta('geo_1_post_cr', alpha=geo_1_post_a, beta=geo_1_post_b)
geo_2_pre_cr = pm.Beta('geo_2_pre_cr', alpha=geo_2_pre_a, beta=geo_2_pre_b)
geo_2_post_cr = pm.Beta('geo_2_post_cr', alpha=geo_2_post_a, beta=geo_2_post_b)
在对每个转换率进行建模后,我们现在可以对特性发布前后的差异进行建模,同时对差异中的差异进行建模,这就是我们的提升。
with diff_model:
diff_pre = pm.Deterministic('diff_pre', (geo_2_pre_cr - geo_1_pre_cr))
diff_post = pm.Deterministic('diff_post', (geo_2_post_cr - geo_1_post_cr))
diff_in_diff = pm.Deterministic('diff_in_diff', diff_post - diff_pre)
既然我们已经对差异进行了分层建模,我们可以添加我们的观察值。但在此之前,我们不妨也对每个 geo 中的提升进行建模,以帮助我们更好地理解不同混杂因素导致的提升。
with diff_model:
diff_geo_1 = pm.Deterministic('diff_geo_1', (geo_1_post_cr - geo_1_pre_cr))
diff_geo_2 = pm.Deterministic('diff_geo_2', (geo_2_post_cr - geo_2_pre_cr))
最后,我们将观察到的转化率添加到模型中,然后对其进行采样。我们将这些转化率建模为伯努利变量,该变量使用之前建模的转化率作为转化率的概率。
with diff_model:
geo_1_pre_conversions = pm.Bernoulli('geo_1_pre_conversions', p=geo_1_pre_cr, observed=conversion_values['geo_1_pre'])
geo_1_post_conversions = pm.Bernoulli('geo_1_post_conversions', p=geo_1_post_cr, observed=conversion_values['geo_1_post'])
geo_2_pre_conversions = pm.Bernoulli('geo_2_pre_conversions', p=geo_2_pre_cr, observed=conversion_values['geo_2_pre'])
geo_2_post_conversions = pm.Bernoulli('geo_2_post_conversions', p=geo_2_post_cr, observed=conversion_values['geo_2_post'])trace = pm.sample()
一旦模型被采样,我们就可以绘制采样分布图。
with diff_model:
az.plot_trace(trace, compact=False)
查看diff_in_diff
图,隆起很可能大于 0,隆起的最高概率约为 5%。
我们现在可以检查所有模型的统计数据,并了解它们的 95%可信区间在哪里。
with diff_model:
display(az.summary(trace, kind='stats', round_to=2, hdi_prob=0.95))
特别是,我们想看看最近 3 个模型的统计数据— diff_in_diff
、diff_geo_1
和diff_geo_2
。
看起来我们的diff_in_diff
平均值为 5 %, 95%的可信区间在 0-9%之间。因此,虽然这一变化很可能确实影响了用户邀请的成功程度,但也很可能只是很小的影响。
查看我们的控制地理,diff_geo_1
表明在geo_2
发布变更前后平均有 5%的差异,95%可信区间位于 2–9%之间。由于这个原因,我们没有发布特性变化,这种变化很可能是由于混杂因素造成的。
结论
虽然看起来我们想要测试的变化可能对用户的邀请有多成功产生了影响,但这种影响可能非常小。如果我们对此次升级感到满意,我们可以将该功能推广到多个地区,并持续监控成功指标。然而,如果我们觉得影响太小,我们可以重新评估我们的假设,想出其他方法来改善用户体验。
感谢阅读这篇文章!我希望它能帮助你更好地理解当传统的实验不起作用时,如何用这种方法来测试真实的产品特性。
如果你喜欢阅读我的文章,愿意支持我的写作,并且正在考虑订阅一个媒体,请随时使用我下面的推荐链接。我会从你的订阅费中提成。
https://medium.com/@kaushsk12/membership
自行车共享系统日常运行的地理可视化:赫尔辛基和塔尔图
与伊莱亚斯·威尔伯格合作
图片由 Elias Wilberg 提供。从 2021 年的一个夏季工作日开始,赫尔辛基和埃斯波的自行车共享自行车
最近,我在 GIS4 Wildlife 开始了与大规模运动数据集地理可视化相关的项目。我刚刚想起,在 30 日地图挑战赛期间,我的一位同事 Elias Wilberg 创作了一幅精彩的可视化作品,展示了赫尔辛基自行车共享系统的日常活动。可视化渲染的环境是 PostgreSQL/PostGIS+Qgis+time manager 之间的连接。因此,我决定向 Ellias 询问渲染过程的体验,并将他的回答包含在这篇文章中。
Elias 专注于研究城市和主动移动性,尤其是自行车共享系统数据的使用。我在一篇关于赫尔辛基地区自行车共享运动聚合的旧文章中提到过他的研究。
但是我发现 Elias 在 30DayMapChallenge 中的可视化很吸引人,它代表了自行车共享系统用户在他们的轨迹中从起点到终点的移动。我记得当他在处理它的时候,由于数据集的大小,他在晚上离开了计算机渲染。运行脚本 12 个小时,希望在处理过程中没有错误,这种体验对于大型数据集来说是常见的。因此,在开始我未来与大规模运动数据集可视化相关的项目之前,Elias 的经历对我来说非常有价值,很高兴与地理空间社区分享它。
总的来说,我询问了地理可视化产品的使用体验,Elias 回答道:
“我对自行车共享数据产生了兴趣,因为这是为数不多的关于自行车的详尽且开放可用的数据源之一。通过帮助揭示城市地区人们的时空骑行模式,自行车共享数据可以帮助理解如何让更多的人骑自行车;尤其是由于气候危机,这是城市迫切需要的东西。赫尔辛基地区的当地自行车共享系统已经存在多年,在市民中非常受欢迎,每年约有 350 万人次。这意味着该系统每年都提供易于获取和使用的有趣数据
在我看来,成功分析和可视化数据的关键是使用正确的工具。当你有合适的工具时,即使使用非常简单的硬件,也有可能从大型数据集进行动态可视化和高级分析,就我而言,使用的是一台 5 年前的笔记本电脑。
以自行车共享可视化为例,我使用 Python 进行路由,使用 PostGIS 进行几何运算,使用 QGIS 进行可视化,使用 GIMP 将地图框组合成视频格式。从路线开始,路线规划工具的可用性和效率在最近几年有了很大的提高。有了 Pandana 图书馆,赫尔辛基所有自行车共享站(大约有 450 个)之间的最短路线搜索只需不到 5 分钟。虽然通常是空间操作需要最大的处理能力,但 PostGIS 是一个很好的工具。我能够将最短的路线分成超过 1000 万个节点,并使用 PostGIS 为每个节点插入时间戳,同样在不到 5 分钟的处理时间内。虽然 QGIS 本身可能会塞满那么多点,但根据时间戳将点以更小的块从 PostGIS 输入到时态控制器使可视化成为可能。在这一阶段,QGIS 在 24 小时内每秒生成一个地图框,但正如 Bryan 所提到的,这需要更多的时间。最后,使用 GIMP,可以将帧组合成动态可视化,并优化视频文件的大小。最棒的是所有的事情都是用开源工具完成的。开放万岁!
最后,非常感谢布莱恩。如果没有你的兴趣和热情,我就不会花时间制作这个可视化的东西。很高兴您能和我们一起来到数字地理实验室!"
灵感和伟大的想法不仅仅来自埃利亚斯。由蒂格兰T2【Khachatryan撰写的关于赫尔辛基地区自行车网络的网络分析非常有价值。中型简介包含非常有趣的文章,有许多见解。如果你在 Medium 上,查看他的个人资料,然后关注蒂格兰·哈恰特良。尤其是他的上一篇文章。
https://geometrein.medium.com/wolt-delivery-network-analysis-cccdc4cb50e3
关于自行车共享系统的许多灵感及其数据集分析的许多可能性。所以,我决定加入我自己的。我可以更像 Elias 那样描述我的可视化,因为我想创建一个地图动画,重点关注自行车共享系统用户在他们的轨迹中的运动。查看我这篇关于爱沙尼亚塔尔图自行车数据地理可视化的文章。
地理可视化
图片由作者提供。自行车街道网络上的自行车运动
塔尔图智能自行车系统由 750 辆自行车和 80 多个站点组成。该网站包含有关自行车使用情况的更新指标,例如当年行驶的距离等。此外,考虑到三分之二的自行车都配备了电动辅助马达,以在踩踏板时提供额外的动力,它为自行车的使用提供了可接受的价格。
我将从 OSM 数据中提取的自行车街道网络添加到地理可视化中。获取数据集的代码可以在本文的知识库中找到:
关于数据集和许可
爱沙尼亚提供了一个开放数据门户,在那里你可以找到自行车共享系统的数据。对于这个地理可视化,我使用了 2019 年 7 月 18 日的每日数据集。
- 自行车共享系统数据:知识共享署名许可-共享 3.0 未发布。
- OSM 自行车网络:获得开放数据共享开放数据库许可(ODbl) 或归属许可。用户可以自由地复制、分发、传输和改编这些数据,只要这些数据是像 OpenStreetMap 贡献者这样的作者所拥有的。
代码和 Web 地图
按下播放并检查移动。建议在“火箭”按钮上减速。
在这里找到代码:储存库
可视化在这里: Tartu 智能自行车
图片由作者提供。塔尔图智能自行车路线
一旦运行了代码并保存了文件。
将它们拖至 KeplerGl 演示。
一条重要的建议是,您可能需要准备时间戳:
data['route_code'] **=** [f'B-{code}' **for** code **in** data['route_code']]
data['timestamp'] **=** data['coord_date'] **+** ' ' **+** data['coord_time']
检查整个笔记本,它有完整的工作流程和添加 OSM 的自行车路线层的过程。
结论
本文旨在强调大规模运动数据集需要支持可视化的环境。目前最好的组合是 PostgreSQL/PostGIS+Qgis+time manager。自行车共享系统数据集是运动数据集的很好的例子,Elias 的经历对未来的工作是有价值的建议。如果你知道更好的环境或集成(当然有),请让我知道或联系我合作。祝愿你未来的可视化。
带四键的地理围栏
原文:https://towardsdatascience.com/geofencing-with-quadkeys-7c5b9866ff98
本文介绍了如何创建带有方形分区的地理围栏
上图显示了使用此处描述的算法对葡萄牙大陆进行的等级方形离散化。图片由作者用叶子包和 OpenStreetMap 图像制作。
地理围栏是划定任意地理区域的虚拟边界。我们使用这些构造来分类某个对象是否位于感兴趣的区域内,例如当我们想要确定一辆汽车是否进入了特定的停车场时。地理围栏不必与特定的物理位置相对应,可以传达更微妙的概念,如龙卷风走廊或禁飞区。
为了让地理围栏发挥作用,我们需要一些地理定位设备来报告感兴趣的对象在世界上的位置,并需要一种方法来定义围栏本身。我们通常使用地理空间多边形来处理这最后一点,其顶点是在纬度和经度空间中定义的位置。简单地说,当物体的跟踪设备报告一个我们可以用数学方法确定的位于多边形内部的位置时,物体就在围栏内部。在某种意义上,geofence 是一个二元分类器,用于确定对象相对于 geofence 内部的位置。
地理围栏系统传统上使用特定的存储和查询工具,这些工具允许从地理多边形到透明地映射地理空间顶点的内部表示的直接转换。这些系统是精确的,但是需要定制的存储和索引格式,这些额外的需求通常会降低存储空间和查询性能。例如,想想 PostgreSQL 扩展 PostGIS ,它扩展了基本功能以添加地理空间处理特性,比如函数、数据类型和索引。
通常情况下,应用不需要高精度的地理围栏检测。想一想需要什么来确定你的一辆车是否进入了加油站。没有必要编码位置边界的所有细节,一个粗略的草图可能就是你所需要的,特别是如果它将使位置的编码更简单和检测更快。此外,如果您可以使用更轻便的地理围栏系统,您可能希望在您的 edge 物联网设备上实现它。
哈希备选方案
那么我们有什么选择呢?有趣的是,一些选项使用固定的分层地理空间网格,六边形或正方形。这些替代方法可以将 geofence 区域缩小到 64 位整数索引的列表,您可以轻松地存储和快速查询这些索引。您只需要一个常规的数据库或内存存储以及基本的集合包含逻辑就可以让这些替代方案发挥作用。
这些算法根据它们选择的几何形状将纬度和经度空间中的位置散列成唯一的整数。两者都是通过建立一个固定的层次地理空间网格来工作的,所以你必须接受一些近似误差。幸运的是,您可以使用这两种方法来离散大约 5 米或 16 英尺。
优步·H3 的选择
在其他文章中,我探索了优步的 H3(T1)的潜力,这是一种分层的六边形空间索引。这个空间索引系统是强大而轻便的,因为它是用 C 语言实现的,允许在边缘设备上使用。
由于其哈希特性,我们还可以使用 H3 来实现高速地理围栏查询。正如我在另一篇文章中所展示的,我们可以将地理围栏设想为一组 H3 指数,每个指数都有一个相关的空间六边形。下图显示了这个概念,其中我们用一组相连的 H3 指数来定义地理围栏。包含测试只是将位置转换为相关的 H3 指数,然后运行集合包含测试。
上图显示了如何从一组 H3 六边形设计地理围栏。地理围栏包含所有六边形和所有红点。为了测试任意点的包含性,我们需要将其转换为适当的 H3 指数,并执行集合包含测试。图片由作者用叶子包和 OpenStreetMap 图片制作。
请注意,存储这样的地理围栏是非常便宜的,每个六边形作为一个 64 位整数进行加权。上面描述的地理围栏只有 45 个这样的整数。但是当地理围栏变大时会发生什么呢?我们可以不断将六边形添加到不断增长的 geofence 中,但这种方法有一定的局限性,因为 H3 表示可能会比其他 geo-polygon 表示更加重要。
我们可能会选择使用更大的六边形尺寸来替代日益严重的地理围栏问题。正如你可以从优步的文档中看到的,H3 六边形等级意味着当增长到最接近的级别时,每个六边形代表较低级别的七个六边形。但是这种选择是以丢失细节为代价的。
然而,优步·H3 API 确实提供了一个解决方案,允许我们在层次结构中混合和匹配不同级别的六边形。下面两张葡萄牙大陆的图片展示了这一过程是如何进行的。
上图显示了离散为 2,769 个 6 级 H3 六边形的葡萄牙大陆地图。图片由作者用叶子包和 OpenStreetMap 图像制作。
上图显示了将葡萄牙大陆地图离散为 2,769 个六级六边形。优步提供了将这些六边形合并到更高级别的六边形以填充整个地理围栏形状的选项。我们可以在下图中看到这个过程的结果。
通过压缩前一组 H3 六边形,我们将计数减少到 501。图片由作者用叶子包和 OpenStreetMap 图像制作。
这种方法的显著优点是,它将六边形的数量减少到 501,或整整一个数量级。缺点是,由于其固有的几何形状,六边形不会完全重叠,并且会有一些小区域未被覆盖。正如优步的文献所解释的那样,这种效应对于许多统计应用来说并不重要。但是当涉及到地理围栏时,上面的简化杀死了它。
下图更详细地说明了这种效果。
上图显示了葡萄牙大陆中央区域的 H3 压缩离散化。不同层次的六边形之间的差距非常明显。图片由作者用叶子包和 OpenStreetMap 图片制作。
这是地理围栏的相关问题吗?正如我在上面和之前的一篇文章中所展示的,如果你能忍受这种参差不齐的边缘,这种方法会非常有效。geofence 仅包含无缝连接的相同大小的六边形,因此不会出现包含检测错误。另一方面,对于较大的形状,压缩后的案例显示的间隙可能大到足以导致 geofence 包含检测错误。
四键选择
四键的几何结构确实解决了间隙问题。正如你从开始的图像中看到的,正如四键数学所暗示的,正方形的层次在不同层次的四键之间提供了紧密的配合。你可以从它的来源、微软在线文档和下面的文章中读到更多关于这个主题的内容。
如下图所示,与 H3 不同,四键层次结构允许你紧密地适应不同层次的方块,没有重叠或死区。请注意,方形图块的固定位置意味着我们只能在层次结构中从较低到较高的级别(较低的缩放比例)合并特定的图块集。
上图展示了 Bing 地图切片系统的层次结构。图片由作者用叶子包和 OpenStreetMap 图片制作。
上图揭示了四键的两个优秀特性。首先,四键代码是以四进制表示的整数,这意味着每个“缩放”级别需要两个额外的位来表示。第二,每个四键对应于一个更大的地图上的一个图块,我们可以把它想象成一个位图,每个图块作为一个“像素”我们将在这里使用这两个特性。
数字四键编码
让我们注意,它需要一个数字(0、1、2 或 3)来表示缩放级别。我们在将四键描述为字符串时使用这种方法,因为它方便地打包了图块位置信息和缩放级别(字符串长度)。
通过认识到每个数字只需要两位来编码,我们也可以将这种表示转换成二进制整数。如果我们保持在线地图通常可用的最大 23 个缩放级别,我们需要 48 位来存储完整的四键哈希。有足够的空间来保存缩放级别,只需要五位。这是一个可能的编码的公开提议:
https://github.com/joekarl/binary-quadkey
就个人而言,我会颠倒编码设计,在低位保留散列,在高位保留缩放级别。这种方法会使下面需要的一些数学运算变得更容易。
为了简单起见,我们将使用基本编码,仅使用低 48 位来存储散列,并在其他地方保持缩放级别。这种方法使得代码更容易实现和遵循。
隐式位图
如前所述,我们可以认为四键仅仅是地理空间散列,地图上的方块,或者是位图上的像素。下图显示了 EaEarth 的 sap 是一组 8x8 的瓦片。为什么不是 8x8 的位图,我们可以在上面画线和多边形?
上图显示了缩放级别为三的 Bing 地图切片。请注意,我们可以将这些看作数组或位图。图片由作者用叶子包和 OpenStreetMap 图像制作。
位图概念在高缩放级别时变得非常有用,在这种情况下,每个图块都像我们喜欢的那样小,我们可以使用它们来“绘制”对应于地理围栏区域的填充多边形。当然,这幅画是一个虚拟的概念。通过“绘制”一个像素,代码将向给定缩放级别的现有集合添加一个四键代码。
将附近的瓷砖合并成更大的瓷砖是一个简单的过程。请参考前面说明缩放操作的图像。请注意,当绘制索引值以数字 3 结束的四键(最后两位设置)时,我们可以检查相邻三个图块的存在。当绘制图块“213”时,我们可以检查是否存在“212”、“211”和“210”如果这些图块存在,代码可以用图块“21”替换它们,并将其放置在适当的缩放级别设置上。如果适用,重复该过程。
现在我们可以看看在位图上绘制填充多边形的过程。
在位图上绘制填充多边形
我们首先为多边形顶点选择一个合适的表示。您可以使用可用的 GitHub 库来跟踪实现。我们从通用多边形填充脚本的代码开始。
上面的类存储多边形顶点的每个实例。(图片来源:作者)
该算法的第一步包括从多边形顶点到边的转换。下面的代码显示了如何存储多边形的边。
多边形边需要特定的顺序,填充算法才能发挥最佳效果。(图片来源:作者)
暂时不要担心“活动边缘”。我们将在后面讨论这个概念。
对于每条边,我们收集 y 坐标的最小值和最大值、与最小值 y 相关的 x值以及边斜率的倒数。存储多边形边信息的类对填充算法所需的特殊排序方法进行编码。此功能与选择接下来要加工的边相关。
为了创建多边形边,我们使用一个专用函数来计算所需的属性,并将它们存储在目标存储对象上。所有这些值将帮助多边形填充算法决定下一条水平线的起点和终点。
上面的代码使用连续的顶点对创建边。(图片来源:作者)
每对连续的顶点都转换成一条多边形边,如以下代码所示:
上面的函数将顶点序列转换成相应的边序列。注意代码是如何复制第一个点来闭合多边形的。(图片来源:作者)
请注意,该函数会过滤掉任何水平边缘(参见第 9 行)。该测试明确地避免了生成边的代码被零除的可能性,并反映了多边形填充算法是如何工作的:通过顺序填充水平线。增加这样的边是多余的。
我们现在可以看看绘制填充多边形的代码。这里我们使用一个 NumPy 数组作为位图。我们首先确定多边形的边,创建支持数组和活动边列表。这个列表是算法工作的基础。
上面的代码包含了位图的多边形填充算法,这里实现为 NumPy 数组。(图片来源:作者)
在第 12 行,代码进入一个循环,遍历所有扫描行,并为每一行设置一个活动边列表。活动边缘与当前绘制的扫描线相交。我们使用一个不同的数据结构来保存这些,因为算法强加了可变性(见第 30 行)。
上面的类定义了活动边的属性。(图片来源:作者)
该算法使用两个函数来管理活动边缘的列表,一个函数插入其最小值 y 等于当前扫描线的边缘,另一个函数移除不再与其相交的活动边缘。
请注意,第一个函数在插入时将边转换为活动边。它对边列表进行操作,并返回活动边列表。第二个函数仅适用于活动边。(图片来源:作者)
在更新和排序活动多边形边的列表后,算法可以绘制所需的水平线。内部循环处理成对的活动多边形边,并在它们之间绘制一条水平线。在外循环的最后,代码更新所有有效边沿的 x 位置。
下面是调用多边形填充函数的方法:
上面的代码定义了一个任意多边形,将其转换成 NumPy 数组,最后显示出来。(图片来源:作者)
这是结果:
上图显示了运行前一幅图像的代码的结果。(图片来源:作者)
现在,我们已经了解了如何在位图上绘制填充多边形,让我们将该过程扩展到使用不同缩放级别的四键生成 geofence。
用四键绘制填充多边形
可以想象,将地理多边形转换为一组四键类似于上面的算法。这个过程在概念上非常简单。我们从要编码的地理多边形开始,使用提供的比例表确定目标细节级别,将所有地理多边形顶点转换为图块坐标,然后绘制填充的多边形。在此过程中,我们递归地将图块聚集到较高的细节级别,从而减少最终的图块数量。请遵循地理多边形四键填充脚本上的代码。这个脚本类似于前面的脚本,但是它不是显示位图,而是将生成的四键保存到数据库中。关于这个问题的更多信息,请参见库文档。
我们首先将一个多边形定义为一组有序的空间坐标,并对源多边形使用纬度和经度空间坐标。这个空间是连续的,纬度从-90 到 90 度,经度从-180 到 180 度。
下一步需要将这些坐标离散到一个等距空间,其粒度取决于我们想要达到的细节水平。根据经验,我使用微软提供的地面分辨率表来粗略估计每个“像素”的大小。选择名为“地面分辨率(米/像素)的第三列的值,并将其值乘以 256,这是“滑动地图”图块的标准大小。得到的值是赤道上“像素”宽度的合理估计,单位为米。在 20 级和零纬度,每块瓷砖的尺寸为 38 米(约 125 英尺)。随着远离这些纬度(向北或向南),像素大小将会减小。
该算法首先将纬度和经度上的所有源数据点转换到目标离散空间。每个坐标现在是一个整数对,而不是实数对。如前所述,离散化取决于“缩放级别”
坐标之间的转换是一个两步过程。首先,我们将纬度和经度转换为所需级别的四键。接下来,我们提取四键瓦片坐标。
上面的函数实现了从纬度和经度到指定细节级别的图块坐标的两步转换过程。(图片来源:作者)
在按顺序通过所有地理多边形顶点运行此转换后,我们就可以使用栅格多边形填充算法了,该算法与上一节中的算法非常相似。它通过从最小(顶部)到最大(底部)循环通过所有离散扫描线( y 坐标)来工作。该函数绘制的“画布”是一个字典,它将一个细节级别映射到同一级别的整数编码四键的集合。让我们通过下面的代码来遵循算法逻辑。
上面的代码显示了四键多多边形填充功能。主循环从上到下遍历扫描线,同时管理活动边。(图片来源:作者)
该函数首先使用下面的代码从多边形中收集边的列表。注意除了边缘列表,该函数如何返回扫描线 y 坐标范围。
(图片来源:作者)
接下来,我们进入主循环,遍历离散化的 y 坐标范围。除了像素绘制(第 20 到 24 行)之外,代码与我们之前看到的类似。如你所见,我们使用了两种不同的绘图功能,一种用于偶数扫描线,另一种用于奇数扫描线。原因是偶数编号的扫描线没有提供机会来合并附近的图块以生成较低分辨率的图块(请参见上面的讨论)。
上面的代码显示了偶数扫描线生成函数。请注意,它只是用四键范围更新缩放级别设置。(图片来源:作者)
下面的函数将图块坐标转换为四键索引,如上所述。
上面的代码显示了如何在给定的缩放级别将图块坐标转换为四键索引。注意,我们生成的位序列没有添加电平数据。(图片来源:作者)
奇数扫描线绘图功能更有趣,因为它必须处理合并图块的可能性。
上图包含了在奇数扫描线上绘图的函数。这些扫描线是图块合并的候选。(图片来源:作者)
它不仅仅更新缩放级别集,而是遍历生成的四键并调用一个专门的函数。对于每个要插入的四键,该函数测试一个终止数字“3”,这是潜在扩展的签名。如果是,它还会测试相邻四键的缩放级别设置。请注意简化的四键代码二进制编码是如何有用的:
上面的代码执行隐式递归图块扩展和插入。(图片来源:作者)
扩展四键包括移除相邻键,并在上面的缩放级别中插入一个更大的键。代码遍历缩放级别,直到无法再找到要展开的四键。
结果是一个 Python 字典,其中缩放级别作为键,缩放级别集作为值。下面的函数显示了如何将新生成的围栏插入数据库。
上面的代码在数据库中以相同的名称插入了一个区域列表。这种方法对于具有多个多边形的国家边界非常有用。(图片来源:作者)
询问
我们现在可以开始查询生成的数据,在这种情况下,通过填充的数据库。这个想法很简单:给定纬度和经度空间中的任意位置,我们想要确定它属于哪个地理围栏(如果有的话)。但是首先要克服一个障碍。我们已经生成了各种缩放级别的四键,那么我们如何知道对任意位置使用哪一个呢?
简单的回答是我们不知道,所以我们查询所有的缩放级别。这个解决方案并不像看起来那么复杂,而且,如果有合适的索引,它也很快。让我们看看如何进行查询。
我们的第一步是确定我们的数据库包含哪些缩放级别。这里,我们在实例化数据库对象时这样做。
数据库查询示例代码。(图片来源:作者)
查询过程从使用存储的最大缩放级别将纬度和经度坐标转换为四键开始。接下来,代码将四键转换为编码整数,包含值和缩放级别。我们去掉最后一个,然后把散列转移到适当的位置。接下来,对于所有当前缩放级别,代码生成一个相应四键值的列表。我们确信所有这些四键都包含输入位置。最终的数据库查询非常简单。只需使用一个集合包含子句。
请注意,为了让这个查询在大规模运行时表现良好,我们需要向表中添加一个适当的索引。对于大规模数据集,我们甚至可以考虑发出并行查询,通过四键前缀分割表以限制索引大小,等等。
结论
地理围栏是地理空间分析行业的工具之一。我们已经看到了它们的常规实现,并讨论了它们的缺点。我们还提出了基于散列或网格的替代方案:基于六边形的优步 H3 和基于正方形的四键。我们看到了 H3 如何合并不同缩放级别的六边形,以及该解决方案如何将一些区域排除在地理围栏之外,从而导致潜在的检测错误。最后,我们看到了基于四键分层正方形的替代方案如何通过生成无缝瓦片集来工作,以及如何高效地查询这些瓦片集。
我希望你喜欢这篇文章!
参考
Git 储存库
https://github.com/joaofig/quadkey-geofence
地理空间哈希
🌐微软(github.com)提出的使用四键的地理平铺的 Python 实现
多边形填充
国界
https://github.com/datasets/geo-countries
国家多边形作为 GeoJSON —数据集—数据中心—无摩擦数据
joo Paulo Figueira 在葡萄牙里斯本的TB . LX by Daimler Trucks and bus担任数据科学家。
球形数据的几何深度学习
原文:https://towardsdatascience.com/geometric-deep-learning-for-spherical-data-55612742d05f
球形 CNN
通过对物理世界平移对称性的理解进行编码,卷积神经网络(CNN)彻底改变了计算机视觉。在这篇博文中,我们研究了 CNN 成功背后的原理是如何转移到一系列问题上的,这些问题的数据表现出复杂的几何形状,例如球体。
这篇博文由来自 Kagenova 的 Oliver Cobb 和 Augustine Mavor-Parker 共同撰写。
球形数据的一个例子。[图片由 NASA 在 Unsplash 拍摄]
在过去的十年中,CNN 从传统(平面)图像和视频中提取语义的能力已经得到了快速的提高。如果有足够的数据,通常可以达到人的水平。然而,对具有空间结构的数据进行分析远不是一个已解决的问题。有许多问题的数据表现出空间的但非平面的结构。示例包括虚拟现实中的 360°图像、大爆炸的宇宙微波背景(CMB)辐射、医学成像中的 3D 扫描以及计算机图形中的网格表面,仅举几例。
对于这些问题中的每一个,我们都希望利用我们对数据结构的了解,特别是它们所涉及的对称变换。正如在 之前的一篇博文 中所讨论的,将对对称性的理解编码到机器学习模型中可以是一种限制所考虑的模型空间的强大方法,允许更有效地学习模型。
对于平面上的图像,平移对称可以通过应用在图像上平移的一堆卷积滤波器来容易且有效地编码。因为相同的卷积滤波器被应用于所有位置,所以结果操作在平移上是等变的,即它尊重平移对称性。这意味着无论特征位于图像中的哪个位置,它都会以相同的方式刺激相应位置的激活神经元。
不幸的是,对于非平面结构的问题,通常不存在这种简单的程序来编码对对称性的理解。然而,对于这些问题,几何深度学习新兴领域的研究人员正在制定新的方法,这些方法利用数据的几何形式的属性,并尊重对称性。最近取得重大进展的一组问题是那些在球面上定义数据的问题。
球形数据的对称性
许多字段涉及到固有地存在于球体上的数据。
当在球面上的每个点进行观测时,会产生球面数据,例如地球的地形图。然而,当在多个方向上进行观测时,也会出现这种情况,例如宇宙学中的宇宙微波背景(CMB)或虚拟现实和计算机视觉中的 360°图像(见下图)。在 Kagenova,我们正在努力为这些问题和其他涉及复杂几何数据的问题(如球体)解锁深度学习的非凡成功。
球形数据的例子。【原创人物由作者创作。]
对于平面图像,CNN 规定,定义特定特征如何变换的规则不应取决于该特征恰好位于平面中的位置。对于在球体上定义的数据,我们希望规定规则不应该依赖于特征如何以及在哪里碰巧在球体上定向。变换特征然后旋转其变换后的形式应该等同于旋转特征并变换其旋转后的形式。关于此属性的操作称为旋转等变(见下图)。
旋转等方差图。给定球形数据(左上),应用变换(𝒜)以获得特征图(右上),然后旋转(ℛᵨ)特征图(右下),相当于首先旋转数据(左下),然后应用变换(右下)。【原创人物由作者创作。 ]
在物理学中,规定控制系统行为的物理定律不应该取决于系统的取向,这就产生了角动量守恒定律。因此,毫不奇怪,在量子物理中用于研究角动量的一些相同的机器对于在深度学习中定义旋转等变层是有用的(正如我们将在后面看到的)。
标准(平面)CNN 的局限性
在深入球形深度学习的想法之前,也许很自然地会想为什么平面 CNN 的有效性不能被直接利用。我们能不能不把我们的球形数据投射到某个平面表示上,然后简单地用通常的方式应用 CNN?毕竟,我们对球形世界的平面投影(地图)很熟悉。
投影的问题是不存在从球面到平面的既保持形状又保持面积的投影。换句话说,扭曲是不可避免的。
这就是为什么格陵兰岛在地球地图上看起来和非洲差不多大,而实际上还不到非洲的十分之一(见下图)。
球体到平面的投影引入了不可避免的扭曲,这与所用的投影方法是不可避免的。因此,在地球地图上,格陵兰岛看起来和非洲差不多大,但实际上还不到非洲的十分之一。[图片来源于维基共享资源。]
这些失真意味着,当将传统的 CNN 应用于球形图像的平面投影时,特征根据它们所处的位置而出现不同。当应用于球面图像的平面投影时,平面 CNN 的平移等方差不编码旋转等方差。对旋转等变进行编码需要卷积的概念,这是专门为球体的几何形状设计的。
卷积复杂性
不幸的是,由平面 CNN 实现的简单卷积过程不能应用于球形设置。
要了解为什么会出现这种情况,首先考虑平面数据的形式。平面数据被表示为像素值的 2D 阵列。对于定义在平面上的数据,我们可以在水平和垂直方向上均匀地间隔像素位置。平面的这种均匀采样意味着每个像素具有相关的邻居,并且所有像素在相同的相对位置(北、东北、东等)具有邻居。这意味着在相同样本位置定义的任何过滤器都可以通过平移以输入中的任何像素为中心,从而使样本精确对齐。
不幸的是,没有办法对球体进行采样,使得所有像素在相同的相对位置具有邻居。球体上的位置通常使用球面坐标来描述,θ测量极角,ϕ测量方位角。相对于θ和ϕ均匀间隔采样会产生下图左侧所示的球体采样。如果我们使用这些样本位置来定义过滤器,然后旋转过滤器,我们会发现样本位置并不对齐(见下图)。不管我们选择如何对球体进行采样,这都是正确的。
假设我们使用与球形数据相同的样本位置定义一个过滤器。由于样本不对齐,因此无法评估滤波器在各种旋转下与数据的匹配程度。这适用于球体的所有样本。【原创人物由作者创作。]
众所周知,不可能以旋转不变的方式离散化球体。因此,不可能构造出严格旋转等变的纯离散球面卷积。
为了构造一个能捕捉旋转等变性质的卷积的球面概念,我们必须考虑一个连续的表示。值得庆幸的是,存在这样一种表示,对于这种表示,可以执行卷积的自然概念。
考虑球面上连续信号的表示。这些是函数 f: 𝕊 → ℝ将一个值与球面𝕊上的每一点(θ,ϕ)相关联,而不仅仅是在选择的样本位置。正如圆上的连续信号(即周期函数)可以分解为正弦和余弦函数的加权和,球体上的连续信号同样可以分解为谐波基信号的加权和(见下图)。在这两种情况下,权重(系数)可用于表示信号,产生圆上信号的傅立叶级数表示和球上信号的球谐表示。
球面调和函数。[图片来源于维基共享资源。]
虽然这种表示是无限的,但是通过适当地截断系数向量,可以非常精确地近似真实世界的信号。从上图可以看出,低次球谐函数只能捕捉低频变化,而高次球谐函数可以捕捉较高频率的变化。我们截断的点决定了我们的数据表示的分辨率。
球形卷积
回想一下,我们想要执行满足旋转等方差属性的球形数据的转换。
有一个非常自然的球形卷积的概念,在连续设置中类似于在平面情况下执行。
这是取球面信号 f : 𝕊 → ℝ,定义一个球面滤波器 g : 𝕊 → ℝ,计算卷积信号 f * g 定义为
这里我们使用了旋转算子ℛᵨ ,定义为(ℛᵨg)(ω)=g(ρ⁻ω)。换句话说,它具有将相应的反向旋转应用于函数的定义域的效果,类似于我们如何考虑平移定义在 1D 线或 2D 平面上的函数。
对上述等式的解释是,卷积信号 f * g 捕捉到信号 f 在任何给定旋转ρ下与滤波器 g 的匹配程度(见下图)。这类似于平面情况,我们考虑滤波器在各种平移下与输入的匹配程度。
数据(左)与过滤器(中)的球形卷积的可视化,以产生特征图(右)。【原创人物由作者创作。]
这里的主要区别在于,定义卷积信号的空间,即旋转空间(3D 空间),不同于定义被卷积的信号和滤波器的空间(2D 球)。在上面所示的说明性例子中,滤波器对于方位角旋转是不变的,因此输出保持在球体上。
然而,将输入信号从球体提升到 3D 旋转空间并不是特别有问题。随后可以在信号和定义在旋转空间上的滤波器之间执行卷积的类似概念。因此,给定球形输入,为了分层学习特征,我们可以执行一次球形卷积,从而在 3D 旋转组上产生激活图,然后执行我们想要的任意多的旋转组卷积。
要了解为什么上述卷积概念是旋转等变的,请注意,将输入旋转ρ相当于对积分内的滤波器应用额外的ρ⁻旋转。反过来,这具有旋转由ρ⁻定义的卷积信号的域的效果。换句话说,在执行卷积之前旋转输入相当于先执行卷积,然后旋转输出。
两个球形信号的卷积似乎需要对三维空间中的每个值计算二维积分。不过好在 f * g 和 f 和 g 的调和表示之间的关系很简单。通过在 f 和 g 的调和系数之间执行矩阵乘法,可以在调和空间中计算球形卷积。鉴于深度学习实践者非常习惯于利用 GPU 来高效地执行矩阵乘法,这尤其方便。
球形回旋是不够的
配备了可以有效实现的旋转等变线性运算,看起来我们已经拥有了重复应用该运算和分级学习特征所需的一切。
然而,有一个重要的组成部分,我们迄今为止忽略了提到——非线性的引入。
在平面网络中,非线性是由逐点激活函数引入的,即通过将选择的非线性函数分别应用于每个样本位置的值。由于平面采样方案的均匀性,这实际上是平移等变操作。然而,我们已经过渡到使用没有相关样本位置或值的谐波表示。虽然可以获得基于样本的表示,但是我们不能对球体进行均匀采样(如上所述),这意味着对每个样本应用相同的非线性函数不是严格的旋转等变操作。
然而,以这种方式引入非线性是可能的,并且如科恩等人(2018 年)和埃斯特韦斯等人(2018 年)所示,通常相当有效。然而,为了执行卷积和非线性运算,在谐波和基于样本的表示之间重复转换是麻烦的。此外,人们很自然会怀疑损失的等方差在多大程度上阻碍了性能。
在我们的下一篇文章中,我们将看到如何利用量子物理学的思想在调和空间中直接引入非线性,而不损害我们对旋转对称的尊重程度。
参考
[1]科恩,盖革,克勒,韦林,球形 CNN,ICLR (2018), arxiv:1801.10130 。
[2]埃斯特韦斯,艾伦-布兰切特,马卡迪亚,达尼利迪斯,学习 SO(3)用球面 CNN 的等变表示,ECCV (2018), arXiv:1711.06721 。
简单解释了几何分布
原文:https://towardsdatascience.com/geometric-distribution-simply-explained-9177c816794f
几何分布的简单描述和用途
莫里茨·金德勒在 Unsplash 上拍摄的照片
介绍
在这篇文章中我想讨论一个统计学中常见且容易理解的分布, 几何分布 。这种分布被用于许多行业,如金融、体育和商业。因此,如果您是一名数据科学家,了解这一点非常重要。
在本帖中,我们将详细介绍它的定义、直觉、一点数学知识,最后在一个例题中使用它。
定义、属性和类型
几何分布是一种离散的概率分布,它推断出我们在获得成功之前需要进行的 伯努利试验 的次数的概率。伯努利试验是指单个事件只有两种结果:以一定的固定概率成功或失败。
几何分布通常被称为离散版的 指数分布 。如果你想了解指数分布,我之前写过一篇关于它的短文,你可以在这里找到:
实际上有两种不同类型的几何分布:
- 首先是获得成功所需的试验次数。
- 二是成功前的失败次数。
第一种被称为移位几何分布。
实际上,这两种方法都可以使用,但需要从一开始就明确区别,以确保结果的一致性。
在本文中,我们将使用移位版本,因为我觉得它更容易用数学和直觉来处理。
几何分布的一个关键性质是它是https://en.wikipedia.org/wiki/Memorylessness无记忆的。这意味着,仅仅因为上一个结果是失败的,下一个就不太可能成功。这是因为每个伯努利轨迹都是独立的https://en.wikipedia.org/wiki/Independence_(probability_theory)。****
数学细节
由于几何分布与伯努利分布和二项式分布 密切相关,其 概率质量函数(PMF) 呈现类似的形式:
作者在 LaTeX 中生成的方程。
其中 p 是成功的概率, n 是获得成功所需的事件数。
【均值】 简单来说就是:
作者在 LaTeX 中生成的方程。
其中 p 是再次试验成功的概率。
让我们通过一个例子让这个理论更具体。
示例和图表
在常规六面模具上第五辊出一个 4 的概率有多大?
在这种情况下我们有:n = 5p = 1/6。 把这个代入上面的 PMF,我们发现概率为:**
作者在 LaTeX 中生成的方程。
这种情况下的期望值是:
作者在 LaTeX 中生成的方程。
这意味着我们希望使用模具在第六个辊上轧制一个 4 。
我们还可以使用 Python 为一系列试验绘制这个场景,【n】***😗****
***# Import packages
from scipy.stats import geom
import matplotlib.pyplot as plt# Probability and Number of Trials
n = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
p = 1/6# Generate the PMF
dist = geom.pmf(X, p)# Plot the distribution
plt.figure(figsize=(12, 7))
plt.scatter(n, dist, linewidth=2, color='black')
plt.xticks(fontsize=18)
plt.yticks(fontsize=18)
plt.ylabel('Probability', fontsize=20)
plt.xlabel('Number of Rolls', fontsize=20)
plt.vlines(n, 0, dist, colors='black', linewidth=2, alpha=0.7)
plt.savefig('plot.png')
plt.show()***
作者用 Python 生成的图。
我们观察到掷出 4 的概率随着掷骰数的增加而呈指数下降。这是有道理的,因为我们的第一个 4 不太可能发生在第 100 次滚动中。
这种概率相对于成功所需试验次数的指数下降是几何分布 PMF 的一般形式。
结论
在本文中,我们讨论、解释并绘制了几何分布图。要记住的关键点是,几何分布计算连续伯努利试验失败指定次数后的成功概率。
和我联系!
- 要在媒体上阅读无限的故事,请务必在此注册!T3💜
- 当我在这里发布注册邮件通知时,可以获得更新! 😀
- 领英 👔
- 推特 🖊
- github🖥
- https://www.kaggle.com/egorphysics🏅****
(所有表情符号由 OpenMoji 设计——开源表情符号和图标项目。许可证: CC BY-SA 4.0
几何先验 I
原文:https://towardsdatascience.com/geometric-priors-i-cc9dc748f08
几何深度学习
群、表示、不变性和等方差
一系列博文,总结了 AMMI 计划的 几何深度学习(GDL)课程; 非洲机器智能硕士 ,授课老师 迈克尔·布朗斯坦 , 琼·布鲁纳 , 塔科·科恩 ,以及佩塔尔·韦利奇科维奇
维数灾难是高维学习中的一个重大挑战。这篇帖子讨论了几何深度学习(GDL)中的一个主要概念,叫做几何先验 ( 对称性和尺度分离)。几何先验让我们能够解决维数灾难。我们将看到域和信号在这些域上的概念。我们将讨论对称以及它们是如何出现在 ML 和 GDL 中的。我们还将看看一些潜在的数学概念,如群表示、不变量和等价变量。最后,我们将看到如何在深度学习中使用不变&等变网络的概念。
立方体的旋转对称性(Oₕ).群图片来自 GDL 课程第三讲。
参见我上一篇文章 s 上一篇 Erlangen 程序的 ML 和 高维学习 。这些博文是根据四位导师的 GDL 原书 和 GDL 课程atAMMI改编的。
在上一篇关于高维度学习的文章 中,我们看到,由于维数灾难,在没有假设的情况下,高维度的学习是不可能的,也就是说,我们的学习问题所需的样本数量随着维数呈指数增长。我们还介绍了主要的几何函数空间,其中我们在高维空间中的点可以认为是低维几何域上的信号。从这个假设出发,并且为了使学习易于理解,我将呈现对称性(在这篇文章中)和标度分离(在下一篇文章中)。
维度的诅咒。图片来自 GDL 课程第三讲。
此外,我们还讨论了我们需要注意的三种误差,即近似误差、统计误差和优化误差。如果我们的函数类减少(我们试图估计的真实函数远在这个类之外),近似误差会增加,这表明有一个大的函数类。相比之下,统计误差意味着我们不可能基于有限数量的数据点找到真正的函数。随着函数类的增长,这种错误会增加。最后,我们有优化误差,它与我们在假设类中找到最优假设的程度有关(当经验风险最小化可以有效地解决时,误差很小)。
我们将会看到,通过使用几何先验和等变网络:尊重我们问题的对称性的网络,我们可以减少我们假设类的大小或复杂性。通过这样做,我们可以在不放弃有用假设的情况下减少统计误差,这意味着近似误差不会更差。(普适性结果:如果我们的函数类是等变函数的普适逼近器)。
几何域
在几何深度学习(GDL)中,数据存在于由ω表示的域中。域包括网格、组、图和流形(量规&测地线)。定义域也是一个集合(在典型的数学中),但是它可能有一种不同的结构。我们可以有一个邻域结构(在网格和图形中)。我们也可以有一个出现在流形中的度量结构(点之间的距离),因为我们可以测量表面上点之间的距离。
几何领域:5 Gs。图片来自 GDL 课程第三讲。
GDL 的关键信息是,我们希望设计处理几何数据的神经网络,这些网络应该尊重域结构,无论它可能有许多选择。
几何域上的信号空间
在大多数情况下数据不是域本身,而是域上的信号。ω域上的信号是一个函数𝓍,它输入ω域上的一个元素,并将其作为向量空间𝒞中的一个向量输出,即𝓍∶ω→𝒞(𝒞的维数称为通道)。我们也可以把ω上的𝒞-valued 信号空间定义为所有函数(该空间上的各种信号)的集合,即𝒳(ω,𝒞)={𝓍∶ω→𝒞}.
例如,如果我们的域是一个网格,数学上ω=ℤₙ×ℤₙ,其中ℤₙ是从 0 到 n-1 的整数集合。该信号是采用诸如(0,1)的网格点并将其映射到 rgb 值的空间,即信号𝓍∶ω→ℝ)的函数。类似地,在图中,我们的域是从 1 到 n 的节点集。对于分子图,信号将是从该图到𝑚维度的向量空间的函数,其中𝑚是我们关心的原子的数量(对于每个节点,我们分配一个热向量,该向量告诉该节点的原子类型)。
网格和图形域中的信号。图片来自 GDL 课程第三讲。
希尔伯特空间结构
正如所见,域可以是一个向量空间 n ,比如用于建模图像的连续平面ℝ。所以我们可以在这个域里加点。但是在某些领域,我们没有这个向量空间(例如,在图中,我们没有任何节点添加的概念)。尽管如此,信号空间总是有一个向量空间结构,在这个结构中,我们可以将两个信号相加并乘以标量。从数学上讲,这由以下等式定义:
信号的加法和标量乘法。来自 GDL 原型书第三章的方程式。
换句话说,如果我们有两个信号𝓍和𝓎(两个图像),并且我们有两个标量 α & β ,我们可以形成信号 α 𝓍 + β 𝓎,并在点𝑢.对其求值这种加法是逐点定义的(在𝑢评估第一个函数,在𝑢评估第二个函数,将这些数与 α 和 β 线性组合,并将它们相加)。
两个信号的相加。图片来自 GDL 课程第三讲。
如果信号空间有无穷多个点,那么它就是一个向量空间。那么,这将是一个无限维的向量空间(就计算而言,我们必须处理有限的东西)。而且,在这个向量空间中定义一个内积会给我们希尔伯特空间。
给定一个内积〈 𝓋,𝓌〉on 𝒞 (𝓋,𝓌 ∈ 𝒞)和ω上的一个测度𝜇(关于它我们将定义积分),我们通过下式得到𝒞𝒳(ω上的一个内积:
𝒞𝒳(ω上的内积定义)(关于测度𝜇).的积分)来自 GDL 原型书第三章的方程式。
测量𝜇允许测量ω的集合和子集的大小。在大多数情况下,这种数学设置归结起来相当简单。具体来说,如果ω是一个有限集,那么 计数度量 可以用来计算积分,这意味着我们对ω上的点求和,所以当我们做内积时,我们只对向量空间的维数求和。
我们需要内积的原因是我们想做模式匹配当我们使用线性/conv 层时,我们需要以某种方式比较我们的信号𝓍和滤波器𝓎.
几何特征领域
到目前为止,我们已经看到信号𝓍是𝒞).域ω上的函数(取ω的一个元素𝑢并将其映射到向量𝑥(𝑢)稍微一般一点的东西,在物理学上叫做场,在数学上叫做(“丛”)。字段是一个函数的略微一般化,其中不是将ω中的𝑢映射到固定空间𝒞中的向量𝓍(𝑢,而是将其映射到向量空间𝒞的元素,该元素由𝑢进行子索引,即𝓍(𝑢) ∈ 𝒞 ᵤ 。例如,网格上的向量场为流形中的每个点分配一个向量,该向量位于该点的切平面上。所以流形上的每个点都有一个切面。
流形的向量场和切空间。图片来自 GDL 课程第三讲。
为了简单起见,我们现在只讨论𝒞𝒳(ω的函数空间。
域为数据
我们有时候域上没有信号,但是域本身就是数据。这出现在网格、没有节点或边特征的图形以及点云中。有一些方法可以处理这个问题,比如我们可以用图邻接矩阵作为ω×ω上的信号。在流形或网格的情况下,我们可以使用度量张量,网格固有的量,它可以被视为信号。
域本身就是数据。图片来自 GDL 课程第三讲。
对称性
这里是我们岗位的核心。为了解释物体的对称性,让我们以三角形为例。我们可以对三角形做几个不改变它的变换。我们可以将它旋转 120 度,这将只是用另一个点替换三角形中的每个点,而不会改变整体形状。我们也可以在垂直线翻转它,它不改变三角形。这些变换叫做对称。
三角形的对称性(由 120 度旋转 R 和翻转 F 产生)。图片来自 GDL 课程第三讲。
一个对象的对称性是该对象保持不变的变换。
我们将看看 ML 中出现的两种对称,参数化的对称和标签函数的对称。在参数化的对称性中,设输入空间为𝒳,输出空间为𝒴(一个标签空间),权重空间为𝒲(一个参数空间),模型为𝑓,一个从输入空间𝒳和权重空间𝒲到标签空间𝒴的映射,即f:𝒳×𝒲→1。我们可以说,将权重𝓌映射到其自身的变换𝔤,即𝔤: 𝒲 → 𝒲,是参数化的对称,如果:
参数化的对称性。来自 GDL 课程的方程式,第三讲。
比如交换神经网络同一层的两个神经元的进出连接,并不会改变输入输出映射 f (。, 𝑤).
参数化的 S 对称性示例(s 交换两个神经元的输入和输出连接)。图片来自 GDL 课程第三讲。
ML 中出现的第二种对称,也是最会讲的,是标签函数的对称。再次假设输入空间𝒳、输出空间𝒴和标签函数 L 是地面实况标签函数,即从输入空间𝒳到输出空间𝒴.的映射变换𝔤是对称的,如果:
标签函数的对称性。来自 GDL 课程第三讲的方程。
换句话说,如果我们进行转换,然后计算标签,这与只计算标签是一样的。例如,在图像分类任务中,𝔤可以是图像的旋转、平移或缩放。
标签函数的 S 对称性示例(如果我们旋转图像,该类不会改变)。GDL 课程,第三讲。
学习类≅学习对称性
我们可以把所有的学习问题,或者至少是所有的监督学习,看作是关于对称性的学习。假设我们有两个类的输入空间𝒳。我们可以看到,𝒳中任何尊重类边界的可逆映射(它将某类中的一个点映射到该类中的另一个点)都是标号函数 L 的对称。这意味着如果我们知道了所有的对称,我们只需要为每一个类标注一个标签!因为我们可以从一个点开始,应用一个对称很多次,到达这个类中的每一个点。
具有两个类的输入空间𝒳(如果我们知道所有l 的对称性,从某个类中的一点,我们到达该类中的每一个其他点)。图片来自 GDL 课程第三讲。
如果学习问题是非平凡的(如果我们相信我们需要学习来解决我们的问题),我们不应该期望能够先验地找到完全对称群。
结构域的对称性
在一大类问题中,我们称之为几何深度学习问题,对称性的来源来自于数据赖以生存的域。设ω表示一个几何域。变换𝔤∶ω→ω是对称的,如果它保持ω的结构。这些对称性的一些例子可能是:
- 集合元素的排列保持了集合的成员关系。如果我们的定义域是一个集合,那么这个集合的结构就是一个集合成员关系(无论元素是不是集合的一部分,都不考虑集合中元素之间的顺序或关系)。然后,这个集合的元素的排列保持了这里的结构。
- 欧氏等距(旋转、平移、反射)保持了欧氏空间中的距离和角度ω=ℝᵈ。如果我们有欧几里得空间,比方说一个平面, ℝᵈ是一个度量空间(点与点之间的距离是空间结构的一部分)。然后,对称可以是欧几里得等距,即,诸如旋转、平移和反射的距离保持映射。
- 一般微分同胚保持流形ω的光滑结构。如果我们把 ℝ 或者任何流形仅仅看作一个光滑流形,我们不会以一个度量结束,所以域的任何微分同胚或者光滑翘曲都可以认为是一个对称性。
几何深度学习中对称性的例子。GDL 课程,第三讲。
对称群
给定物体的所有对称的集合称为对称群。
现在我们可以观察到一些关于对称性的东西:
- 恒等变换永远是对称的。
- 给定两个对称变换,它们的合成(一前一后做)也是对称的。
- 给定任何对称,它的逆也是对称。
抽象群体
群是一个集合 𝔊 连同一个二元运算 ⚬:𝔊×𝔊→𝔊 称为组成,表示为 𝔤⚬𝔥 = 𝔤𝔥(为简单起见),并满足以下公理:
- 关联性 : (𝔤𝔥) 𝔨 = 𝔤 (𝔥𝔨)适用于所有𝔤、𝔥、𝔨 ∈ 𝔊.
- 恒等式:存在唯一的𝔢 ∈ 𝔊满足𝔤𝔢 = 𝔢𝔤 = 𝔤.
- 逆:对于每个𝔤 ∈ 𝔊都有一个唯一的逆𝔤⁻ ∈ 𝔊,这样。𝔤𝔤⁻ = 𝔤⁻ 𝔤 = 𝔢.
- 结束:对于每一个𝔤,𝔥 ∈ 𝔊,我们有𝔤𝔥 ∈ 𝔊.
为了简单起见,通常使用𝔊来指代组,而不仅仅是其元素的集合。注意,一个群是而不是必然可换的,即𝔤𝔥≠𝔥𝔤一般。具有交换运算的群称为阿贝尔群。
立方体的旋转对称性(Oₕ).群图片来自 GDL 课程第三讲。
对称组,抽象组&组动作
在这一节,我们定义群论中的几个概念:
- 对称群是其元素为变换𝔤∶ω→ω的群。
- 分组运算是贴图(函数)的合成。
- 抽象组是一组元素以及复合规则,满足组公理(如上定义)。
- 组动作是一个映射或函数,它获取组元素和域ω的元素,并将其映射到域ω中的新元素。,即𝔊×ω→ω(表示为(𝔤,𝑢) ↦ 𝔤𝑢).群作用应满足:𝔤(𝔥𝑢) = (𝔤𝔥) 𝑢 和 𝔢𝑢 = 𝑢,对于所有𝔤,𝔥 ∈ 𝔊和∈ω。
群体作用的例子:作用于ℝ的欧几里得平面运动。 ℝ 中的欧几里得群具有带角度𝜃的旋转分量以及 x 和 y 方向的平移分量(tₓ,tᵧ),可应用于二维点(𝓍,𝓎).
作用于ℝ的欧几里得平面运动。 GDL 课程第三讲。
凯莱图表&表格
如果我们的群是有限且离散的,我们有两种方式来表示这个群的结构:凯莱图&表。让我们以我们以前讨论过的三角形的对称群为例。在 Cayley Diagrams 中,每个节点都是对象(三角形)的一个配置,但是我们也可以用一个组元素来标识它。我们可以对任何节点应用旋转或翻转,并移动到其他节点。相比之下,我们可以把群结构表示成一个乘法表。组中的每个元素都列在一行和一列中,表中的条目将是组元素的乘积。
凯莱图和乘法表(三角形的对称群)。GDL 课程,第三讲。
组的种类
这里有一些不是有限的也不是离散的群。大体上,我们可以将群定义为两种类型,离散群和连续群。 离散组可以是:
- 有限的,如旋转对称的三角形。
- 无限,或可数无限,如整数集合的平移和无限扩展网格。
另一方面,我们有连续组,它们可以是:
- 紧凑组,如二维旋转。
- 局部紧群,如连续平移和旋转平移。
- 非局部紧群,如微分同胚的无限维群(作为数学句柄最难得到)。
所有这些类型的群都可以是可交换的,例如顺序无关紧要的二维旋转群,或者是不可交换的,例如顺序有关的三维旋转群。
各种团体。GDL 课程,第三讲。
ω作用于信号𝒳(ω的对称性,𝒞)
作用在域ω上的对称性也作用在ω上的信号上。对于群元素𝔤和信号𝓍,我们将𝔤对𝓍的新群作用定义为:
𝔊对𝒞).𝒳(ω信号的行动 GDL 原型书,第 3.1 节。
简而言之,在域中的点𝑢评估的变换信号等于在𝔤⁻ 𝑢.评估的原始信号𝓍
这方面的一个例子可以是翻译图像。𝔊是一个翻译小组,由𝑥和 y 两部分组成。在下图中,我们看到 bug(在右边的原图中)从左边移到了右边( t 是一个正数,沿 x 方向平移)。𝔤.翻译形象的价值在点𝑢的𝓍将是在点𝑢 - t,即 𝔤.的原始图像中的相同值𝓍(𝑢) = 𝓍(𝑢 - t 。注意,如果没有逆(减),我们就无法满足群体行动的公理。
翻译图像(右边:原始图像,左边:翻译图像)。GDL 课程,第三讲。
如前所述,域可能缺少向量空间结构,但信号的空间总是具有向量空间结构。此外,我们刚刚在信号上定义的组动作是线性(如果我们有信号的线性组合,那么我们应用变换𝔤,这与对单个信号应用𝔤并线性组合它们是一样的)。
信号群作用的线性。GDL 原型书,第 3.1 节。
信号上的群组动作的线性示例(两个图像)。GDL 课程,第三讲。
小组陈述
群𝔊的 n 维实表示是一个映射𝜌 ∶ 𝔊 → ℝⁿ*ⁿ,给每个𝔤 ∈ 𝔊指定一个可逆矩阵𝜌(𝔤,并且满足𝜌
例如,设群𝔊是 1 维中整数平移的集合,即𝔊 = (ℤ,+),定义域ω=ℤ₅= { 0,1,2,3,4}(如短音频信号),𝔤 = 𝑛对 u∈ω的作用定义为:𝑛,𝑢↦𝑛+5(mod 5)。那么,在𝒳(ω(5 维空间)上的表示就是移位算子:
团体代表的例子。GDL 课程,第三讲。
要验证这是群表示,我们得证明矩阵𝜌( n 可逆,𝜌 (n+m) = 𝜌 (n)P(m)(群表示的条件)。很明显,𝜌(n 的行列式),即 det(𝜌( n ),不等于 0;(det(𝜌(n)=det(𝜌(1))ⁿ,det(𝜌(1))不能为零,因为单位矩阵是𝜌(1)).的子矩阵因此,𝜌( n 是可逆的。𝜌(n+m)= 𝜌(n)𝜌(m)直截了当。
重要的是要注意,如果一个组满足条件,我们可以对它有多个表示。例如,如果我们有旋转群 SO(2),我们可以有一个作用于 ℝ 中的点、位于圆上的点、平面上的函数/信号等的表示。在 GDL 蓝图中,每个特征空间都有一个组表示,每个组表示都可能不同。
在下面几节中,我们将讨论不同域中对称群的一些例子。
对称性:集合&图形
S 从集合和图开始,对称群的一个例子是置换 s 的群,即𝔊 = 𝕊ₙ(“对称群”),定义域是顶点的集合,即ω=𝑉,(可能还有边)。图形的特征可以是标量、矢量或张量(𝕊ₙ).的三组表示
三种图形特征(𝕊ₙ).的表示 GDL 课程,第三讲。
此外,我们有两种作用于图和集合的对称;描述的对称性和对象本身的对称性。首先,请注意:
- 一个图(甚至一个集合)是一个抽象对象(之前定义过)。
- 实际上,我们所能访问的只是计算机内存中的图/集合的描述(它有一些无关的属性,比如图/集合元素的节点顺序)。
- 描述具有属性(例如顺序),这些属性不是对象的固有属性。
通常,我们感兴趣的是描述的对称性,而不是物体本身的对称性。为了解释这一点,让我们讨论下图。对于中间的图,如果我们为每个节点选择一个标签,我们可以将图写成邻接矩阵。如果我们应用一个置换,我们可以得到一个不同的邻接矩阵(右边的那个)。这里是描述的对称性;我们对同一个图有不同的描述(我们希望我们的网络是不变的或等变的)。我们也可以看到这个图形本身有一种对称性。如果我们做另一个置换,例如交换节点 3 和 4,邻接矩阵将是相同的(左边的那个)。
D 描述的对称性和 O 对象的对称性的例子。GDL 课程,第三讲。
对称性:网格
在网格中,定义域是网格点的集合,和对称群𝔊可以是离散 平移、旋转、翻转。底层的连续空间可能有更多的对称性(例如缩放)。群体表征是有规律的;我们可以移动和旋转图像。
常规组代表。GDL 课程,第三讲。
图像的旋转和平移。GDL 课程,第三讲。
对称性:群&齐次空间
我们可以将对称性推广到一般群和齐次空间的集合,其中域ω≅𝔊/ℌ,对于空间中的每两个点,至少有一个对称将一个点映射到另一个点(将在后面的帖子中涉及)。群𝔊是一个局部紧群。这个群的作用是传递的:对于任何𝓍,𝓎∈ω,存在𝔤 ∈ 𝔊使得𝔤.𝓍 = 𝓎.小组代表是正规的(𝜌(𝔤) 𝑥(𝑢) = 𝑥(𝔤⁻ 𝑢)).
一般群和齐次空间。GDL 课程,第三讲。
对称性:流形(测地线&量规)
最后,我们有流形(测地线&量规),这也将在后面的帖子中详细介绍。群𝔊是群规范变换,它指的是特征空间的参考系中的变化。为了解释这一点,让我们看看下图。我们有一个向量场,它给流形上的每一个点分配一个切平面上的向量。如果我们想用数字表示矢量,我们必须在切平面上选择一个坐标系。改变这个框架将改变矢量的数值表示(这被称为规范变换)。制图表达𝜌取决于要素类型。
流形和网格。GDL 课程,第三讲。
到目前为止的总结
到目前为止,我们已经讨论了各种几何域上的信号空间及其希尔伯特空间结构。我们还看了几何先验中的几个概念,如对称性、对称群和群表示。有趣的问题将是我们如何在深度学习中应用这些概念,以及我们如何建立包含它们的模型?
不变表示法
A 假设我们想对字母“A”、“B”和“C”进行分类,它们可能以不同的字体和不同的方向出现(见下图)。然后,如果我们知道标签函数的对称性(在这种情况下,数字的旋转),我们可以形成一个不变表示,其中同一字母的每个旋转版本由一种特征向量表示。因此,我们的学习问题变得更容易。
数字分类问题中的不变表示。GDL 课程,第三讲。
然而,在深度学习中,过早地构建不变表示是不明智的。原因是:为了识别整个对象,我们需要首先识别这个对象的部分(这就是为什么神经网络应该是深度的)。但是如果我们让的中间表示不变,我们将丢失关键信息(看下图)。
零件的不变表示将丢失关键信息(见右图)。GDL 课程,第三讲。
等变网络
T 上述问题的解决方案是使用等变网络,其具有以下成分:
- 特征向量空间𝒳ᵢ。
- 网络图层(地图)𝑓ᵢ。
- 对称群𝔊。
- 𝔊的𝜌ᵢ代表每个𝒳ᵢ。
最简单的例子可能是平面图像、RGB 输入( n × n 像素和 3 个通道)。特征空间𝒳₀将是 n × n × 3 。所以我们有一个 n × n × 3 维的𝔊群表示,它可以是平移。𝒳₁可能是具有 64 个通道的卷积层𝑓₁的输出(我们的特征空间将是 n × n × 64 )。这将是同一组𝔊的不同表现(翻译)。直到层数为止。如果满足下面的等式,我们说网络是等变的
等方差方程。GDL 课程,第三讲。
我们可以从下图了解一下。如果我们从𝒳₀开始,使用𝜌₀进行变换(例如,移动图像),然后应用第一层。如果我们首先应用图层,然后应用相同的变换𝜌₀,我们应该会得到相同的结果,但是现在通过特征空间中的表示来产生𝒳₁.如果所有的网络层都满足这个性质,我们说这个网络是等变。我们还可以证明,如果每一层都满足等方差,那么它们的合成也满足等方差。
等变网络图。GDL 课程,第三讲。
等方差作为对称一致的推广
L et 重复一下我们之前讨论过的分类问题(对数字“A”、“B”、“C”进行分类)。输入空间𝓍和𝓎(在下图中)是位于轨道上的两个信号(如果我们考虑字母“a”的所有旋转副本,这将形成称为“a”的轨道的流形)。网络将该输入映射到由通用字母“A”符号化的特征向量。在非等变网络中,旋转版本可以被发送到特征空间中的不同点(这不可能发生,等变网络必须在整个轨道上一致地概括)。此外,等变网络已经以一种方式被一般化,即符合对称性。换句话说,如果𝓍和𝓎应该被映射到同一点,那么它们的变换版本也应该被映射到同一点。
非等变网络(这不可能发生,等变网络必须在整个轨道上一致地推广)。GDL 课程,第三讲。
当我们深入研究对称和等变网络时,你可能想知道为什么不使用数据增强,它可以取代对称?
当然,数据增强在 ML 中应用广泛,简单易行,非常流行,但也有各种优缺点。支持等方差的最突出的论点,在某类问题中,我们有对称性,比如医学图像,真的有平移和旋转。在这些情况下,我们看到等变网有更好的性能。另一个相关的例子是,如果我们有一个大的对称群(如置换群),实际上不可能使用数据扩充。
T 他的帖子讨论了几何域、信号,以及几何先验的第一要素(对称性)。我们还看到了对称背后的几个数学思想,以及几何领域的例子,以及它们如何用于深度学习。下一篇文章将介绍几何先验的第二个要素(尺度分离)。然后,我们将看到完整的几何深度学习 ( GDL)蓝图。
参考
- GDL 球场,( AMMI ,2021 年夏季)。
- T. Cohen 的第 3 讲[ 视频 | 幻灯片。
- 米(meter 的缩写))m .布朗斯坦、j .布鲁纳、t .科恩和 p .韦利奇科维奇,几何深度学习:网格、组、图形、测地线和量规 (2021)。
我很感谢拉米·阿迈德、汉尼斯·斯特尔克和 M·埃尔法提赫·萨拉赫审阅了这篇文章并提供了有益的评论。
几何先验 II
原文:https://towardsdatascience.com/geometric-priors-ii-6cd359dfbcf4
几何深度学习
GDL 蓝图
一系列博文,总结了 AMMI 计划的 几何深度学习(GDL)课程; 非洲机器智能硕士 ,授课老师 迈克尔·布朗斯坦 , 琼·布鲁纳 , 塔科·科恩 ,以及佩塔尔·韦利奇科维奇
维数灾难是高维学习中最具挑战性的问题之一。在之前的一篇帖子 ( 几何先验 I ) 中,我们讨论了机器学习和几何深度学习中的各种概念,包括对称、不变和等变网络。在这篇文章中,我们将这些数学概念与维数灾难联系起来。然后,我们引入第二个几何先验;尺度分离,代表了战胜维数灾难的有效工具。最后,我们将看到如何将这两个几何先验(对称性和尺度分离)结合起来,构建几何深度学习(GDL)蓝图,它可以作为不同几何域的通用架构。
尺度分离先验(图像分类)。图片来自 GDL 原型书,第 3.4 节。
本帖与M . Elfatih Salah合著。另见上一篇 s 关于 Erlangen 程序的 ML , 高维学习 , 几何先验 I 。这些博文是根据四位导师的 GDL 原书 和 GDL 课程atAMMI改编的。
R 调用以前帖子中的学习设置,在统计学习中,我们有一个目标函数 f *,一个从输入空间𝒳到输出空间 ℝ 的映射; f * : 𝒳 → ℝ.我们也把输入空间𝒳写成一组信号𝒳(ω,𝒞)={𝓍∶ω→𝒞},其中ω是几何域,𝒞是定义在ω上的向量空间。在 几何先验 I 中,我们已经看到了几何域的各种例子,以及如何使用域ω来捕捉与该域中信号的性质密切相关的各种变换。(本帖与 几何先验 I ) 。
这样,从前面介绍的内容来看,目标函数 f 定义为f**:𝒳(ω,𝒞) → ℝ 。我们的目标是在假设类𝓕中学习这个函数,我们可以按照我们想要的方式参数化它,例如使用神经网络。我们现在的承诺是目标 f *是𝔊-invariant ( f *保持不变,如果考虑变换群𝔊的任何元素和任何信号‘输入’的话)。数学上,
𝔊-Invariant 函数。GDL 课程,第四讲。
因此,自然的问题是:我们如何在假设课上利用这个承诺?这足以打破维度的诅咒吗?
为了回答第一个问题,让我们定义一个组平滑算子,它是一个采用任何假设 f 并用组𝔊.中所有变换的平均值替换它的算子在一个数学设置中,如果𝔊是一个离散的有限群并且 f 是我们的假设,我们将𝑓的𝔊-smoothing 算子定义为,
函数𝑓.的𝔊-Smoothing 算子来自 GDL 课程的方程式,第四讲。
如果我们仔细观察,可以发现函数 f 的这个平滑算子有一个有趣的性质;它也是𝔊-invariant(如果𝑓已经是𝔊-invariant,那么应用平滑运算符不会改变它)。因此,给定假设类𝓕,我们可以通过应用平滑算子使其成为𝔊-invariant,并且更正式地如下:
假设类𝓕.的𝔊-Smoothing 算子来自 GDL 课程的方程式,第四讲。
(同样,如果我们扩展不变性群𝔊,不变性类将变得更小,这似乎是直观的)。
不变性下的学习
通过将平滑算子应用于我们的假设类,我们可以证明近似误差不受该平滑操作的影响,并且我们可以这样正式表述:
近似误差不受平滑操作的影响。GDL 课程,第四讲。
为了证明,我们可以注意到平滑算子是在 L (𝒳).)中的正交投影算子所以预测中产生的 L 误差可以分解为该算子图像中的误差加上正交补中的误差,如下所示,
近似误差不受平滑操作的影响(证明)。GDL 课程,第四讲。
然后,用 f *替换𝔤,知道 f *不受平滑操作影响,我们得到,
近似误差不受平滑操作的影响(证明)。GDL 课程,第四讲。
另一方面,由于平滑后假设类更小,统计误差减少,但问题是:多少?
看一看假设类之一,我们假设它是 Lipschitz 类,我们知道它具有以下属性:
李普希茨函数类。GDL 课程,第四讲。
然后,其平滑版本具有以下属性:
应用𝔊-Smoothing 算子时的李普希茨函数类。GDL 课程,第四讲。
平滑版本使得 Lipschitz 假设更强,因为距离更弱,并且主要结果:
定理[ Bietti,Venturi,B.'21 ]:利用一个𝔊-invariant 核岭回归,学习一个李普希兹,𝔊-invariant 函数 f 的泛化误差满足𝔼𝓡(f̂)≲*θ(|𝔊|n)⁻/ᵈ,其中 n 是样本数,d 是维数。
该定理导致样本复杂度的量化增益,该增益与组的大小成比例,因为泛化误差由样本的数量乘以组的大小来限定,都是 -1/d. 的幂
此外,组大小|𝔊|在维度上可以是指数的,例如,如果我们考虑局部转换(可能的局部转换的数量在域的大小上是指数的)。然而,正如我们所看到的,速率仍然受到维数的诅咒,这表明引入群不变性不太可能打破维数的诅咒。
到目前为止的结论:
到目前为止,我们已经看到使用全局对称性或不变性先验减少了统计误差并保持近似误差不变,但是仍然不足以打破维数灾难。所以问题来了:打破维度诅咒所必须的对称性之外的假设有哪些,我们遗漏了什么?
还有,我们一直在讨论理论问题,没有讨论如何高效地构建不变类。我们所描述的平滑操作符并不是我们想要在计算机中直接实现的东西(因为计算所有转换组的平均值是非常不切实际的)。从基本原则出发,我们如何高效地做到这一点?
深度学习“归纳偏差”:组合性
深度学习研究领域有影响力的人可能会在不同的演讲中提到,比如 Yann LeCun 和 Y oshua Bengio ,深度学习之所以有效,是因为 组合性 原理。此外,有来自大脑的证据表明,大脑也是分层组织的,这种想法并不是深度学习研究人员开发的,但它已经存在很长时间了。
深度学习为什么有效?Y 奥舒亚·本吉奥(上)和扬·勒村(下)。GDL 课程,第四讲。
既然我们似乎都同意组合性对深度学习的成功至关重要,我们应该问我们如何才能形式化这种直觉?
多尺度结构
形式化这种直觉的一个方法是观察物理学和计算科学的不同领域。在不同的领域,我们可以看到多尺度结构是一个基本原则和许多学科的基础。
在计算生物学中,为了知道生物学是如何工作的,我们必须理解不同尺度下的过程。有些事情我们可以用分子动力学或者功能图来理解。 比如湍流 ,展现了数学物理中不同尺度之间的交流。 [逾渗](https://en.wikipedia.org/wiki/Percolation#:~:text=Percolation (from Latin percolare%2C ",is described by Darcy's law.) 另一种模型,描述流体通过多孔材料的运动。因此,我们必须考虑不同的尺度。你可能也有自己的例子,在不同的尺度下,系统有不同的行为。
逾渗模型。来自[维基百科](https://en.wikipedia.org/wiki/Percolation#:~:text=Percolation (from Latin percolare, ",is described by Darcy's law.)的动画。
多分辨率分析基础
在我们的上下文中,我们希望将多尺度结构的概念与几何域(网格、组、图形和流形)联系起来。另一个非常相关的概念是多分辨率分析(MRA)。(为了简单起见,我们将假设一个网格域,即ω是一个 2D 网格,但大多数东西可以更一般地描述,然后应用于所有的几何域)。
多分辨率分析是一种方案,该方案试图根据存在于较小域 ( 较粗域 域)中的信号来分解存在于该域中的信号。例如,在网格域的情况下,我们想要更少的像素。在多分辨率分析中,我们试图理解如何从一个分辨率(特定数量的像素)到下一个分辨率(减去数量的像素),以允许我们保留信息。一般来说,我们可以将一幅图像分解成不同分辨率(分辨率更低)的同一幅图像,加上我们需要返回到第一幅图像的细节(见下图)。
图像分解。GDL 课程,第四讲。
在这里,我们必须声明,通过降低网格的分辨率,我们正在攻击维度的诅咒。这是由于在这种情况下像素数量的指数依赖性。我们可以保证,在某一分辨率下,由于样本数量与新分辨率成正比,因此将避免维数灾难。因此,我们的问题将是如何将多尺度结构的想法与学习中的先验结合起来?
秤分离之前
L 让我们讨论一些如何在学习中结合多尺度结构和先验知识的例子。想象一下,目标函数 f 可以在图像的粗糙版本上定义。在下图所示的分类问题中,我们只想知道图像是“海滩还是“山*”而不是它的分辨率。
两幅图像的原始版本(左)和粗糙版本(右)(分类问题)。GDL 课程,第四讲。
在这个问题中,我们可以将图像的分辨率降低到能够给出正确分类的某个水平。如前所述,这将减少网格的大小(像素数)。因此,通过粗化,我们可以避免维数灾难。然而,这通常是不正确的,因为我们需要非常小心粗化的程度。如下图所示,太粗化会丢失太多信息,我们无法分辨图像中到底有什么。
过度粗化会丢失图像信息的示例。GDL 课程,第四讲。
让我们看看多尺度结构也能有所帮助的另一个例子。假设目标函数 f* 可以通过局部项的和来近似,
用局部项的和来逼近函数 f* 。GDL 课程,第四讲。
局部术语 x(u) 是指提取以像素 u 为中心的补丁图像。比如在识别一个纹理(下图所示的分类问题)时,如果我们在一堵砖墙或者一堆草里。我们当地的一个术语就属于这一类。因此,如果我们想要一个好的分类,我们可以只取局部描述符的平均值,因为在粗尺度上没有很多可变性(因为纹理是一个具有空间同质性的结构)。
本地术语的示例:黄色块显示的补丁图像。GDL 课程,第四讲。
我们可以看到,维数灾难也被避免了,因为这个问题中的相关维数减少到了面片维数。
我们还必须声明,这是一个强有力的假设,通常是不充分的,如下图所示;所有的本地术语都可能给出错误的分类。
当本地术语可能给出错误分类时的例子。GDL 课程,第四讲。
重要的是要说明这样一个事实,我们可能有原始尺度和粗略尺度的信息,但不知何故它们以一种不太极端的方式相互作用。因此,在某种意义上,我们必须将这两种尺度结合起来。
让我们考虑一个通用的组合模型,
具有两个算子(非线性局部粗化和非线性假设)的一般成分模型。GDL 课程,第四讲。
函数 f 由两个运算符组成;非线性局部粗化结合非线性假设在粗尺度上提取一些信息(比单独平均小块强得多),两者都是可以学习的。你可能会问为什么这个组合和什么时候可以论证这个组合模型更有效率。当我们考虑一些具体的例子时,比如动态编程和分治算法,我们是从更小的问题的角度来看待这个问题的。在这些情况下,我们可以认为这种组合模型更有效。然而,这仍然是一个未解决的问题*,并且从理论的角度来看,这个组成模型还没有被完全理解。
接下来,我们结合尺度分离和群不变量这两个先验,从第一性原理给出一个强有力的模型。然后,我们介绍完整的几何深度学习(GDL)蓝图。
不变性与尺度分离的结合
S 由于我们有一个函数类是对群体行为不变的(𝔊-invariant 函数),我们想把它和上面描述的多尺度结构(尺度分离之前)合并成一个架构。因为我们讨论过的这些几何先验给了我们模型中应该有的必要条件,而不是一个特定架构的所有要素。
如果我们从一个对群作用不变的线性算符(线性𝔊-invariant 函数)开始,使用前面介绍的𝔊-smoothing 算符,我们可以写成,
带有𝔊-Smoothing 算子的线性𝔊-Invariant 函数。GDL 课程,第四讲。
其中 x̅ 是群轨道上的平均值(我们将 x 的群平均值称为 A x )。(第一个不等式是因为 f 是𝔊-invariant,我们在应用𝔊-smoothing 算子,第二个不等式是因为 f) 的线性。
然而,如果我们仅仅依靠这个假设,我们会丢失很多信息。因为如果我们有一个平移组,例如,一个图像的轨道将由该图像所有可能的平移组成,然后我们只需将所有这些图像平均为一个。
作为补充,我们可以使用线性𝔊-equivariant 函数,我们在 几何先验 I 中讨论过。(如果𝔊同时作用于𝒳和𝒴,那么映射𝐵 : 𝒳 → 𝒴就是𝔊-equivariant。𝑥) = 𝔤.𝐵(𝑥)).为了实现一个更强大的模型,我们可以用一个元素式非线性函数 𝜎合成这个等变函数𝐵;𝜎 : 𝒳 → 𝒳,𝑤𝑖𝑡ℎ𝜎𝑥(u)= 𝜎(𝑥(u),以给出一个非线性𝔊-equivariant 函数 u,即 U := 𝜎 ⚬ 𝐵(由于非线性是逐元素应用的, U 将保持𝔊-equivariant).
然后,我们可以通过组合组平均值 A 和非线性𝔊-equivariant 函数u来提供强大的𝔊-invariant 函数
一个迫切的问题是,这种组合是否会产生一个富裕的阶层?换句话说,我们能很好地逼近任何一个目标函数,𝔊-invariant,并且有足够的 b 和𝜎选择吗?
参考 通用逼近定理 ,这种组平均与非线性等变映射的组合产生了通用逼近器。然而,为了更稳定的表示,我们需要使用一个局部等变图。另外,如果我们有一个具有长程相互作用的函数,我们不能用单层的局部𝔊-equivariant 映射来近似它;相反,我们必须组合几个局部等变函数,使区域变粗。(为证明, GDL 原典 ,第 3.5 节)。
最后,为了构建一个丰富的𝔊-invariant 函数类,我们需要一个局部等变映射、一个全局不变映射和一个粗化操作符。
几何深度学习(GDL)蓝图
L 很快,我们可以在所谓的 GDL 蓝图中,为学习目标函数 f 所需的模型勾勒出上面讨论的所有成分。GDL 蓝图是一个建设性的方法,我们可以用它来定义一个通用架构*,它可以应用于各种几何领域(网格、组、图形和流形)。
设ω是一个定义域,𝔊是ω上的对称群,ω′是ω的一个更粗的定义域,即ω′⊆ω。几何深度学习(GDL)蓝图的构建模块是:
- 线性𝔊-equivariant 层𝐵∶𝒞𝒳(ω)→𝒞′𝒳(ω′),满足𝐵(𝔤. x ) = 𝔤.𝐵(𝑥)适用于所有𝔤 ∈ 𝔊和𝑥∈𝒳(ω、𝒞).
- 非线性𝜎∶𝒞→𝒞′按元素应用 as(𝝈(x)(u)= 𝜎(𝑥(u))。
- 局部汇集(粗化)𝑃∶𝒳(ω,𝒞)→𝒳(ω′,𝒞),使得ω′⊆ω。
- 𝔊-invariant 层(全球统筹)𝐴∶𝒳(ω,𝒞) → 𝒴,满足𝐴(𝔤.𝑥) = 𝐴(𝑥)适用于所有𝔤 ∈ 𝔊和𝑥∈𝒳(ω、𝒞).
几何深度学习(GDL)蓝图(图形输入)。图片来自 GDL 原型书,第 3.5 节。
把这些块拼在一起,我们就能创造出一个丰富的 𝔊-invariant 函数 f:𝒳(ω,𝒞) → 𝒴这种采取形式的
丰富的 𝔊-Invariant 函数类。 GDL 原型书,第 3.5 节。
其中选择每个块,使得其输出尺寸与下一个块的输入尺寸相匹配。另外,每个块可能具有不同的对称群𝔊.
我们在这篇文章中已经看到,仅仅对称可能不足以打破维度的诅咒。然后我们提出了第二个几何先验,称为尺度分离。基于多尺度结构的假设,我们讨论了尺度分离如何抵消维数灾难。我们以 GDL 蓝图作为几何领域的**框架来结束,它是由两个几何先验构建的。我们将在随后的文章中详细讨论这个 GDL 蓝图,以及它如何应用于各种几何领域。我们将在这个框架中看到我们最喜欢的架构,如 CNN、transformers 和 GNNs。
参考
- GDL 球场,( AMMI ,2021 年夏季)。
- j .布鲁纳第四讲[ 视频 | 幻灯片。
- 米(meter 的缩写))m .布朗斯坦、j .布鲁纳、t .科恩和 p .韦利奇科维奇,几何深度学习:网格、组、图形、测地线和量规 (2021)。
我们感谢汉尼斯·斯特尔克对草案提出的有益意见。
带有谷歌地球引擎和 Greppo 的地理空间应用程序
原文:https://towardsdatascience.com/geospatial-app-with-google-earth-engine-and-greppo-2c166b373382
如果没有丰富的 JavaScript 经验,使用 Google Earth 引擎是很困难的。Greppo 让您在 Python 中克服了这个问题。
使用 Greppo 和 GEE 的最终 web 应用程序。图片作者。
谷歌地球引擎是数据科学家工具箱中处理地理空间数据的一个神奇工具。然而,用 GEE 代码编辑器构建 web 应用程序需要很高的学习曲线。基于 JavaScript 的应用创建器需要专门从事 Python 开发的数据科学家投入大量时间
Greppo 是弥合这一差距的完美工具。
在这篇博文中,我将使用一个流行的 GEE 用例 DEM(数字高程模型)来构建一个 web 应用程序。我将带你了解 GEE 的基础知识、客户机-服务器模型、API 如何工作以及 GEE 数据模型。在这种背景下,这篇文章将使用 Greppo 创建一个使用 GEE 的 Python 接口的应用程序,并强调 Greppo 的心智模型和易于使用的界面。
注:这里所有代码都是用 Python 写的。它们是从 文档 中移植来的 GEE 的样本 JavaScript 代码。
入门指南
在我们开始之前,你需要访问谷歌地球引擎。按照此处的说明注册并获得访问权限。
以下是关于 Greppo 及其使用方法的快速教程:
接下来,让我们设置 Python 环境来安装依赖项。要理解什么是 Python 环境以及如何设置它,请阅读这篇。将以下包安装到 Python 环境中。
pip install earthengine-api greppo
web-app 的代码将放入 app.py、 中,通过命令行使用命令greppo serve app.py
服务并运行 app。
注意:要在命令行中运行
greppo
命令,需要激活安装了 greppo 的 python 环境。app.py 文件可以被重命名为任何名称,但是在运行命令greppo serve app.py
时一定要在这个文件夹中,或者在一个相对的文件夹结构greppo serve /demo/folder/app.py
中。
Greppo 的 GitHub 库:https://github.com/greppo-io/greppo
如有任何问题,请使用“问题”在 GitHub 上联系我们,或在 Discord 频道中点击。
GEE 认证和初始化
为了能够使用谷歌地球引擎,你需要创建一个服务帐户,并获得与该帐户相关的访问密钥文件。这只需要几分钟的时间,但是请确保按照说明正确操作。遵循此处的说明。要使用服务帐户和密钥文件,请使用以下代码进行初始化。
注意:确保将 key-file.json 保存在另一个位置,最好安全地保存在您计算机的根文件夹中,不要提交到公共存储库中。
了解 GEE 的客户机-服务器模型
正如 GEE 的开发者文档所说,Earth Engine 不像你以前用过的任何 GIS 或地理空间工具。GEE 主要是一个云平台,所有的处理都在云中完成,而不是在你的机器上。您将与 GEE 进行的交互仅仅是翻译并发送到 GEE 云平台的指令。为了更好地理解这一点,我们必须通过 GEE 的客户端与服务器及其懒惰计算模型。
客户端与服务器
先说我之前提到的, GEE 主要是一个云平台 。它让你在云中完成所有的处理。那么,你如何访问这个处理功能呢?
这里是earthengine-api
库派上用场的地方。Python 包earthengine-api
向客户机(也就是您)提供对象,作为在云中传递和处理的服务器对象的代理。
为了更好地理解客户机-服务器模型,让我们以客户机中的一个字符串变量和服务器中的一个字符串变量为例。在客户端创建一个字符串并打印它的类型时,我们得到 Python class str
对象来表示一个字符串。如果我们想将一个字符串发送到服务器,以便在云中使用或操作,我们可以使用ee.String
将数据包装在一个代理容器中,该容器可以在服务器中读取。更具体地说,ee. objects
是一个ee.computedObject
,它是代理对象的父类。
>> # Client side string
>> client_string = 'I am a Python String object'
>> print(type(client_string))
<class 'str'>>> # Server side string
>> server_string = ee.String('I am proxy ee String object!');
>> print(type(server_string))
<class 'ee.ee_string.String'>
代理对象不包含任何实际数据或处理功能/算法。它们只是服务器(云平台)上对象的句柄,仅仅是传达要在服务器上执行的指令。可以把它想象成一种使用代码与服务器通信的方式,要做到这一点,您需要将数据和指令包装在特定类型的ee.computedObject
容器中。
当对数据执行循环或使用条件语句时,这种理解变得更加重要。为了执行这些,指令将被发送到服务器来执行它们。要了解这些是如何实现的查看本页了解更多细节。
惰性计算模型(延迟执行)
因此,从上面我们知道earthengine-api
包仅仅是向服务器发送指令。那么,死刑是如何以及何时执行的呢?
客户端库earthengine-api
将所有指令编译成一个 JSON 对象并发送给服务器。但是,这不会立即执行。执行被推迟直到有对结果的请求。对结果的请求可以是一个print
语句,或者是要显示的image
对象。
这种随需应变的计算会影响返回给客户端(您是用户)的内容。来自 earthengine-api 的结果是一个指向要获取数据的 GEE tile 服务器的 url。因此,在提到的感兴趣区域内的图像被选择性地处理。感兴趣区域由客户端显示中地图的缩放级别和中心位置决定。而且,当您移动和缩放时,图像会被处理并发送到客户端进行查看。因此,图像是延迟计算的。
使用 Greppo 和 GEE
使用 Greppo 显示和可视化地球引擎图像对象是相当直接的,你需要使用的是:app.ee_layer()
。在 GEE 中存储地理空间数据的基本数据类型是,
Image
:地球引擎中的基础栅格数据类型。ImageCollection
:图像的堆叠或时间序列。Geometry
:地球引擎中的基本矢量数据类型。Feature
:带属性的Geometry
。FeatureCollection
:一套Feature
。
在理解了 GEE 的客户机-服务器和惰性计算模型之后,我们可以推断,这些数据类型是根据对其可视化的请求按需处理的。
那么,如何配合 GEE 使用 Greppo 呢?
最好用一个例子来解释。先说 app 的脚手架。你必须首先从Greppo
导入app
对象,因为这将是你与前端通信的入口点。然后你将不得不import ee
,向地球引擎认证你自己,并用你的上述服务账户的凭证初始化你的会话。
接下来,让我们从从目录中选择数据集开始。这里,我们使用USGS/SRTMGL1_003
来获取数字高程图。我们需要首先为 DEM 图像数据中所有大于 0 的值获取一个土地掩膜,为此我们使用dem.get(0)
。接下来,我们需要在 DEM 上应用蒙版,只显示陆地,为此我们使用dem.updateMask(dem.gt(0))
,并将结果指定为我们要显示的ee_dem
。由于所有数据都存储为 int 16(32767 和-32768 之间的值的矩阵),我们必须使用调色板来显示矩阵。
要添加调色板,我们创建一个可视化参数对象,其中包含生成 RGM 或灰度图像的指令。这里我们使用包含Hex values:[‘006633’, ‘E5FFCC’, ‘662A00’, ‘D8D8D8’, ‘F5F5F5’]
的调色板,并将其线性映射到与指定的min -> #006633
和max -> #F5F5F5
相对应的值。
注意:存储在 DEM 中的数据是栅格,表示为矩阵,每个像元包含表示像元的点的高程(米)。
然后使用 Greppo 在 web 应用程序中可视化该地图,您只需使用app.ee_layer()
。ee_object
是地球引擎图像对象,vis_param
是可视化参数字典,name
对应于将在 web-app 前端使用的唯一标识符,description
是可选的,为 app 用户提供额外的指导。关于这一点的更多内容可以在文档 这里 中找到。
上述步骤中的 web-app 视图。图片作者。
端到端通信:完整的网络应用
到目前为止,我们已经看到了如何只在 Greppo 可视化地球引擎对象。然而,Greppo 能够在前端和后端之间进行复杂的交互。让我们用一个例子来寻找用户指定的一个点的高程。我们将使用 Greppo 的三个 API 特性。
app.display()
:在前端显示文本或减价。app.number()
:前端的数字输入功能,供用户输入数值。它在后端绑定到的变量将被更新为用户指定的值。app.text()
:前端的文本输入功能,供用户输入数值。它在后端绑定到的变量将被更新为用户指定的值。
更多详情请参考 文档 。
让我们从使用app.display
( name
是惟一的标识符,值是显示的文本,可以是多行字符串)显示一些文本来指导 web 应用程序用户开始。之后,让我们使用app.number()
创建两个数字输入,分别代表该点的经度和纬度。
app.number()
接受 name、显示在前端的标识符和 value,value 是这个元素的默认值。接下来,我们也创建一个文本输入,使用app.text()
和name
和value
获得点的名称,如前面提到的app.number()
一样。
使用该点的纬度和经度,我们现在可以用可视化参数color: ‘red’
为该点创建一个地球引擎几何图形对象。我们现在可以使用上面提到的app.ee_layer()
来显示。
为了找到该点的高程,我们在 DEM 对象上使用地球引擎方法sample
。我们对 DEM 中的点进行采样,以从 DEM 中获取属性。我们从输出中取出第一个点,并使用.get
方法找到与属性elevation
相关联的值。最后,我们编写了一个多行字符串来显示输出。
注意:要将地图居中到一个点,并在初始加载时缩放,使用
app.map(center=[lat, lon], zoom=level)
。
具有交互功能的网络应用视图。图片作者。
结论
我们的目标是使用 google earth engine 的数据和计算功能以及 Greppo 的 web 应用程序开发库,完全用 Python 创建 web 应用程序。我们了解了 GEE 的工作原理,了解了如何将 Greppo 与 GEE 集成。学会使用app.ee_layer()
、app.display()
、app.number()
和app.text()
创建一个完整的 web 应用程序,与前端和后端进行端到端的通信。
所有的演示文件都可以在这里找到:【https://github.com/greppo-io/greppo-demo/tree/main/ee-demo
查看一下 GitHub 资源库:此处为 Greppo 上最新更新。如果您的用例有错误、问题或功能请求,请联系 Discord channel 或在 GitHub 上提出问题。用 Greppo 造了什么东西?贴 GitHub。
注:文章原载 此处 。
熊猫专家的地理空间数据争论
原文:https://towardsdatascience.com/geospatial-data-wrangling-for-pandas-experts-96c130c78bd8
熟悉 Python 知识的地理空间数据分析
安德鲁·斯图特斯曼在 Unsplash 上的照片
这个笑话在我高中时代(很多年前)就流传了。一个男孩只记住了一篇考试作文,题目是“牛”。然而,在考试中,他失望地发现作文题目变成了“河流”。不知道该做什么,他想出了一个绝妙的主意。他写了这样一篇文章——从前有一条河。。然后迅速切换到 …..有一头奶牛坐在河边。那是一头中年的黑白条纹奶牛,长着长长的尾巴。男孩继续写文章,就像他熟悉的领域“牛”那样,最后又回到“河”上。
我们很快就会谈到这个故事的寓意。
地理空间数据产品同时具有丰富的信息和华丽的外观。如果你只是给某人看一张地图,什么都不说也不写,它仍然传递了一个故事。然而,对于数据科学家来说,学习地理空间数据分析的前景可能是可怕的。至少发生在我身上。我没有受过这方面的训练,我做过的最令人兴奋的“地理空间”工作是一张我学习地点的地图——用 Microsoft Paint 创建的。我总是被其他人的地理空间工作所吸引,尽管从来没有想过我自己会尝试这样做。我没有时间投入大量精力从头开始学习另一种工具。第二个障碍是我必须购买专有的 GIS 软件许可证(我还不知道 QGIS,它是免费的)。
当我了解到地理空间数据可以表示为 dataframe 对象时,事情很快发生了变化。当我了解到这一点时,我知道我不必从头开始,我可以在 Python 基础上构建我的地理空间能力。
想法很简单:1)使用合适的 Python 库如geopandas
、GDAL
将地理空间数据导入到您的笔记本环境中;2)然后将其转换成一个pandas
dataframe 对象;3)继续分析和操作pandas
中的数据;4)最后,使用matplotlib
可视化地图。
地理空间数据有多种形式,如多边形、线、点和栅格,这种方法适用于所有这些形式。今天我将讨论多边形,这样你就可以知道如何使用其他的了。作为参考,下面是不同形式的空间数据的可视化表示:
左图表示多边形(湖)、线段(河)和点(井位)。右图代表光栅图像(图像来源:维基百科)
您可以将多边形视为一个州的县/区边界。同样,河流可以表示为线段;和所有的杂货店一样分。另一方面,在栅格数据集中,区域被划分为正方形而不是多边形,每个正方形包含与特定位置相关联的值/变量/要素(例如,气温、人口密度)。
好了,让我们深入研究 dataframe 对象中的地理空间数据(多边形)。
载入库和数据
您只需要两个库就可以开始了。geopandas
用于数据争论,而matplotlib
用于数据可视化。顾名思义,geopandas
将pandas
功能的能力用于地理空间数据。
您可以使用您喜欢的软件包管理器(pip、conda、conda-forge)安装 geopandas:
pip install geopandas
安装完成后,让我们导入这个库。
import geopandas as gpd
该库带有内置数据集,因此您可以立即开始使用。稍后您可以随意试验自己的数据,但是现在,让我们使用内置数据集。我们现在将加载数据集,它包含世界上每个国家的多边形。
# load in dataset
dataSource = gpd.datasets.get_path('naturalearth_lowres')
gdf = gpd.read_file(dataSource)
我们现在将检查刚刚创建的对象的数据类型:
type(gdf)
>> geopandas.geodataframe.GeoDataFrame
这是一个地理数据框架,我们很快会看到它只是一个常规的数据框架,但有一个额外的“几何”列。
你可以用matplotlib
的本地命令.plot()
快速可视化多边形
gdf.plot()
可视化地理数据框架-世界各国的多边形。x 轴和 Y 轴值分别代表经度和纬度(图片由作者生成)
使用 pandas 功能进行数据操作
在上面,我们已经可视化了地理空间数据,其中每个多边形都是一个国家。
每个面(国家)都带有一些以地理数据框架格式存储的属性。这意味着您可以立即开始使用pandas
功能。让我们来看看数据框的前几行:
gdf.head()
(图片由作者生成)
这就是地理数据框架的样子。它就像一个常规的数据帧,但有一个特殊的“几何”列存储地理空间信息(这个几何列有助于绘制多边形)。
通过像对待数据帧一样对待这个表,您现在可以应用许多pandas
功能。让我们尝试一些我们通常在数据科学项目中作为探索性数据分析的一部分使用的熟悉方法:
# getting information about the data
gdf.info()
的输出。应用于地理数据框(由作者生成的图像)的 info()方法
使用上面的.info()
方法,我们得到了看起来熟悉的输出。它显示有 177 行(每一行代表一个国家)和 6 列(即每个国家的属性)。我们可以用熊猫.shape
进一步证实这一点。
# number of rows and columns
gdf.shape
>> (177, 6)
现在让我们再次使用pandas
方法,通过调用unique()
方法来检查数据集中有多少个洲。
# unique values of a columns
gdf['continent'].unique()
>>array(['Oceania', 'Africa', 'North America', 'Asia', 'South America',
'Europe', 'Seven seas (open ocean)', 'Antarctica'], dtype=object)
您也可以对行进行条件过滤。让我们只选择位于非洲大陆的国家。
# filtering rows
gdf[gdf['continent']=='Africa'].head()
(图片由作者生成)
不出所料,您还可以操作列,比如创建新的计算字段。让我们基于两个现有的列创建一个名为GDP _ per _ head的新列: gdp_md_est 和 pop_est。
# create calculated column
gdf['gdp_per_capita'] = gdf['gdp_md_est']/gdf['pop_est']
gdf.head()
(图片由作者生成)
我们现在为数据集中的每个国家增加了一个属性列。
这些只是数据操作的几个例子,您可以尝试一些您感兴趣的其他例子。除了这些数据操作技术之外,您还可以生成汇总统计数据,进行高级统计分析等等。让我们生成一些汇总统计数据:
# generate summary statistics
gdf.describe().T
(图片由作者生成)
总结这一部分,首先,我们使用geopandas
库导入地理空间数据(多边形,或者用更专业的术语来说是“shapefile”),然后使用pandas
功能来操作和分析地理数据框架。在下一节中,我们将使用另一个熟悉的 Python 库— matplotlib
来研究可视化数据。
形象化
地理空间数据的真正威力在于它能够可视化地理数据框架中包含的不同属性。与数据操作的pandas
类似,我们将使用matplotlib
来可视化地图中的属性。让我们从一个基本的开始——仅仅可视化形状。
# visualizing the polygons
gdf.plot()
用 Python 绘制多边形。plot()方法。它使用几何列来可视化多边形(由作者生成的图像)
上面的地图显示了多边形。在引擎盖下,这些多边形是从数据框的几何尺寸栏创建的。然而,它还没有显示任何数据,但是我们可以通过指定我们感兴趣的数据列来实现:
# visualize a data column
gdf.plot(column = 'pop_est')
显示每个国家估计人口的世界地图(图片由作者生成)
这张地图现在变得有趣且信息丰富,它用颜色渐变显示了世界上每个国家的估计人口。
但是如果你只想放大非洲呢?这很简单,只需在数据框中过滤非洲大陆,然后以类似的方式创建绘图。
# filter Africa data from the dataframe
africa = gdf[gdf['continent']=='Africa']
# plot
africa.plot(column = 'pop_est')
显示非洲国家估计人口的地图(图片由作者生成)
您还可以访问额外的matplotlib
功能来定制地图——例如,删除 x 和 y 轴,在右侧添加图形标题和颜色条。让我们做所有这些。
import matplotlib.pyplot as plot
# use matplotlib functionalities to customize maps
africa.plot(column='pop_est', legend=True)
plt.axis('off')
plt.title("Population in the continent of Africa");
使用 pandas(用于过滤)和 matplotlib(用于绘图)的组合来可视化地理空间数据。(图片由作者生成)
这就是了。您刚刚使用两个库:pandas
和matplotlib
从地理空间数据中创建了一幅漂亮的地图,这完全符合您的 Python 知识。
这只是起点,从这里开始,天空才是极限!
前进
从这里去哪里?您可以尝试我们刚刚使用的库的一些其他功能,创建更多的地图并进一步定制它们。然后尝试使用外部数据集,网上有很多可用的(如果你需要指点,请在评论中告诉我)。接下来,运用你的想象力和创造性思维,提出一个问题,试着回答。举个例子,你可以问——人口密度排名前 10 的国家是哪些?你应该能够使用pandas
方法过滤这些信息,然后使用matplotlib
在地图上显示它们。
我在 Python 环境中实现了这个练习,但是如果您对其他编程语言感兴趣,它将以类似的方式工作。如果你喜欢 R 编程,试着用tidyverse
和ggplot
重现我们在这里所做的。
感谢阅读。请随意订阅以获得我即将在媒体上发表的文章的通知,或者通过 LinkedIn 或 Twitter 与我联系。下次见!
地理空间索引 101
原文:https://towardsdatascience.com/geospatial-index-101-df2c011da04b
如何有效管理地理空间数据
介绍
由于移动设备、物联网和地球观测技术的进步,如今位置数据变得更加可用。大量的数据对开发定位系统和应用程序的人提出了挑战。本文将简要介绍管理地理空间数据最常用的技术地理空间索引。
什么是索引?
索引是一种优化数据检索和搜索的数据管理方法。使用索引的好处是可以在不扫描所有现有数据的情况下定位特定的数据。每个人都熟悉的索引的真实例子是图书馆。
在图书馆里,书是按照一些标准如书名来分组和排序的。所有以 A 开头的书都在第一书架上,而以 B 开头的书在第二书架上,依此类推。当人们去图书馆时,他们只是去与他们找到的书名相对应的书架,而不是浏览所有可用的书籍,这将花费大量的时间。
索引对于当今所有可用的数据库技术都是至关重要的。有了索引,数据库可以加快查询速度,因为要处理的数据量大大减少了,这类似于图书馆帮助用户找到他们想要的书。
地理空间数据
地理空间数据是记录其地理位置的数据。可以把它想象成一个普通的数据库记录,它有一个附加的列来指定这个数据关联到地球上的什么地方。以下是地理空间数据的一个示例:
地理空间数据示例(图片由作者提供)。
除了看起来像数学函数的位置之外,表上的所有记录看起来都像正常的结构化数据。这种数据类型称为几何,其中用于存储地理数据。下面是 4 个最常见的几何对象:
- 点 :地球上单个位置
- 线 :连接的一组点
- 多边形 :表示特定区域边界的一组点
- 多多边形 :具有相同属性的多边形的集合
(注:T 这里有两种地理空间数据:矢量和栅格。点、线和面是表示位置或区域的矢量数据,而栅格数据是特定场景的图像,其中还包含其位置信息,如卫星和无人机图像。本文只关注矢量数据。)
地理空间索引
与可以应用传统索引算法 的原始数据类型(例如整数)不同,索引地理空间数据要复杂得多,因为它有两个维度: x 和 y,更不用说来自一组点的复杂性,例如线和多边形。因此,它需要不同的索引方法。
一种可能的方法是将整个空间分成一组子空间,如下所示。每个子空间都有一个指定的名称,以便我们以后可以正确地引用它。
图片由作者提供。
假设您有 12 条数据记录,在地理上看起来如下:
图片由作者提供。
通过将数据与预定义的区域相结合,这就是您所得到的结果。
图片由作者提供。
现在,如果您按记录所属的区域对记录进行分组,结果如下表所示:
图片作者。
假设您想要检索所有记录,例如,与澳大利亚相关联的记录。第一种方法是查找与国家边界重叠的所有像元。在这种情况下, L 和 P 。
图片由作者提供。
然后,查找位于两个单元格中的所有记录,得到 X ₄、 X ₅和 X ₁₂.
图片由作者提供。
最后,浏览剩余的记录,我们发现只有 X ₄和 X ₅符合搜索条件。
图片由作者提供。
请注意,尽管总共有 12 条记录,但我们只扫描了其中的 3 条。这个例子中减少的运算数量听起来可能很小,但实际上,对于大量的数据,它可以产生巨大的影响。
地理哈希
上面的例子是对 Geohash 的简化解释。Geohash 是一种算法,它将任何地理位置编码为文本,方法是将世界分成大小相等的单元,称为单元(或网格 ) ,其中每个单元都分配有特定的哈希(但是,它使用的不是字母,而是以 32 为基数的数字)。上一个关于 geohash 的例子中没有提到的是它的层次结构。
假设我们像前面的例子一样将整个空间分成网格。
图片由作者提供。
我们不再停留在这里,而是进一步将每个细胞分割成更小的子空间。例如,在下图中,单元格 A 被分割成 16 个更小的单元格。
图片由作者提供。
然后,我们通过在 A 的一个子节点上重复它,比如说 AF ,更深入一层。
图片由作者提供。
应用此过程越多,每个单元格代表的区域就越小,哈希字符串就越长。名称的长度表示哈希的精度级别。
geohash 的一个有趣特性是,分配给每个像元的名称都有其父名称作为前缀。例如,细胞 AFA 到 AFP 都是 AF 的子细胞。
Geohash 的层次结构(图片由作者提供)。
当您想要验证一个区域是否是另一个区域的子区域时,prefix 属性非常方便。它可以被视为关系数据库中的ST_Contains
( spatial contain )函数的近似版本,具有更低的计算成本,因为它只处理文本,而不处理点、线或多边形。
您可能还会注意到,每个单元格的名称从左到右按字母顺序排序。这种机制允许我们使用小区名称来估计它们之间的地理距离。例如,假设有 3 个哈希: AA 、 AB 、 AK 。由于按字母顺序 AA 比 AK 更靠近 AB (字母‘A’比‘K’更靠近‘B’),所以 AA 在地理上也比 AK 更靠近 AB 。尽管该属性在新行开始的地方有一个例外(例如 AE 比 AC 更接近于 AA ,但是所有单元格的间距相等的事实使得这种边缘情况易于管理。
注意 G eohash 只是可用的地理空间索引之一。其他的索引系统,如 四叉树 和 H3 ,即使基于相同的基础,应用不同的划分策略,所以也有不同的优缺点。
地理空间索引使用
除了作为索引,地理空间索引还可以应用于各种用例。
数据整理
Geospatial data is very different from primitive data like Integer , Float , and String therefore relational databases need a special data type for them: Geography . The geography data type, however, is only a string in the format of Well-Known Text (WKT) that looks like the following:
POINT(103.14411713327522 15.553626958655387)
LINESTRING(103.14261509622688 15.5550326423843,103.14400984491462 15.552738065460904,103.14585520471687 15.552944785150965,103.14594103540534 15.554040396042701)
POLYGON((103.1434304877674 15.553296208147678,103.14435316766853 15.5547639094724,103.14585520471687 15.553482255373645,103.1448896094715 15.552138577185888,103.1434304877674 15.553296208147678))
Notice that the text is very long due to the decimal precision of the coordinate reference system. On numerous occasions, nevertheless, this precision is unnecessary. For instance, in a ride-sharing application where millions of drivers pick up and drop off millions of passengers a day, it would be impossible(and absurd) to analyze all the points these activities occur. Aggregating the data per zone is much more sensible.
Using an index to aggregate geospatial data(Image by author).
A geohash can be an identifier of each zone that can be used to not only reference the area but also infer its spatial properties. Moreover, since there are finite numbers of cells, using geohash can keep the amount of data in the system constant and more manageable.
Data Partitioning
The vast amount of data available nowadays makes the trend of using distributed data stores grows rapidly. Unlike traditional databases, distributed databases split data into chunks and spread them across nodes in the cluster. Designing a data partitioning strategy is necessary (or at l east recommended) for the system owners to utilize distributed data stores effectively.
In developing geographic-oriented systems and applications, partitioning data by its geographical location is very sensible. The data associated with a similar neighborhood should also be in the same chunk and, therefore, stay in the same node in the database cluster. This way, when users retrieve the data, it will come from a minimal number of nodes and files which saves the computational cost significantly.
Geospatial indexes provide a convenient way to split your data geographically. As you can see from the above examples, the closer the coordinates are, the higher chance they will be in the same geohash(or any other index system) cell. Therefore, you can use geohash as a partition key for the distributed database of your choice.
Using geospatial index for data partitioning(Image by author).
Conclusion
Managing geolocation data is critical for developing location-based applications. A geospatial index provides a systematic and elegant approach to inde x geospatial data. Therefore, it’s a key component that any robust geographic software system must have.
基于余弦相似度的地理空间选址分析
根据邻里关系识别地理区域之间的相似性
简介
L 对于经营物理位置的企业来说,位置至关重要,靠近目标市场至关重要。
对于正在向新领域扩张的特许经营来说,这种挑战是常有的,在新领域中理解业务的是很重要的。本文的目的是更详细地探讨这一想法,根据现有特许经营所在地区的特点,评估新特许经营地点的适宜性。
为了实现这一点,我们将从西雅图一家受欢迎的咖啡店特许经营店的 OpenStreetMap 中获取数据,使用关于周围社区的信息来识别类似的新的潜在位置。
框定问题
要完成这项任务,需要考虑几个步骤:
- 寻找现有的特许经营点
- 确定这些地点周围的便利设施(我们假设这能让我们了解附近的情况)
- 寻找新的潜在地点及其附近的便利设施(重复步骤 1 & 2)
- 评估潜在位置和现有位置之间的相似性
由于这个任务是地理空间的,使用 OpenStreetMap 和类似 OSMNX 和 Geopandas 这样的包会很有用。
收集数据
寻找现有加盟店
如上所述,我们将使用一个受欢迎的咖啡店来定义现有的感兴趣的位置。使用 OSMNX 收集这些信息非常简单,我们可以定义感兴趣的地理位置。我已经将名胜设置为西雅图(美国),并使用 OpenStreetMap 中的 name/brand 标签定义了特许店的名称。
*import osmnx
place = 'Seattle, USA'
gdf = osmnx.geocode_to_gdf(place)#Getting the bounding box of the gdf
bounding = gdf.bounds
north, south, east, west = bounding.iloc[0,3], bounding.iloc[0,1], bounding.iloc[0,2], bounding.iloc[0,0]
location = gdf.geometry.unary_union#Finding the points within the area polygon
point = osmnx.geometries_from_bbox(north, south, east, west, tags={brand_name : 'coffee shop'})
point.set_crs(crs=4326)
point = point[point.geometry.within(location)]#Making sure we are dealing with points
point['geometry'] = point['geometry'].apply(lambda x : x.centroid if type(x) == Polygon else x)
point = point[point.geom_type != 'MultiPolygon']
point = point[point.geom_type != 'Polygon']*
这为我们提供了本地区现有特许经营点的位置:
已定义的西雅图区域(紫色多边形)内现有特许经营点(蓝点)的地图。图片作者。
查看现有位置会让我们对以下问题产生疑问:
- 这个地区的特许经营密度如何?
- 这些位置的空间分布是怎样的(密集分布还是均匀分布)?
要回答这些问题,我们可以使用定义的区域多边形和现有特许经营的数量来计算特许经营密度,这样我们可以得到每平方公里 0.262。注意:多边形中的大部分区域是水,因此这里的密度看起来比实际要低很多……**
为了测量这些位置相对于彼此的分散程度,我们可以使用 Sklearn 的 球树来计算到最近邻居的距离
在规定的西雅图区域内(紫色多边形),现有特许经营位置的地图,以到最近邻居的距离(以公里为单位)进行着色。图片作者。
最近邻也可以显示为直方图:
最近邻直方图。图片作者。
看起来大多数位置彼此相距 800 米,当查看地图并看到市中心现有位置的高密度时,这一点很明显。
这些地点周边的生活设施怎么样?
我们首先需要获得感兴趣区域内的所有便利设施,并定义每个现有位置周围的半径,该半径可用于识别附近的便利设施。这可以通过使用另一个循圆测试仪来实现,但是基于指定的半径(我设置为 250 米)来查询点:
*from sklearn.neighbors import BallTree#Defining the tree based on lat/lon values converted to radians
ball = BallTree(amenities_points[["lat_rad", "lon_rad"]].values, metric='haversine')#Querying the tree of amenities using a radius around existing locations
radius = k / 6371000
indices = ball.query_radius(target_points[["lat_rad", "lon_rad"]], r = radius)
indices = pd.DataFrame(indices, columns={'indices'})*
当我们查询 OSM 并使用 BallTree 查找附近的便利设施时,我们只剩下现有特许经营店半径范围内的每个便利设施的指数。因此,我们需要提取设施类型(例如,餐馆)并对每次出现进行计数,以获得如下所示的已处理数据帧:
总结现有位置周围半径范围内的便利设施的示例数据框架。每行代表一个现有的特许经营点。图片作者。
现在,我们可以在分类条形图中看到现有特许经营店附近最受欢迎的设施:
现有特许经营店半径范围内的便利设施条形图。图片作者。
我们的咖啡店特许经营店似乎主要位于其他提供食品/饮料的位置附近,还有一些其他的少数便利设施,如“充电站”。这给了我们所有现有位置的总数, 但是设施的分布是一样的吗?
我们可以应用快速 PCA 和 DBSCAN 聚类来查看现有特许经营点如何相互聚类(使用 min_sample 值 3):
DBSCAN 集群加盟店,由集群标签着色。图片作者。
向左有一个主要的星团,然而也有其他较小的星团存在。这一点很重要,因为它告诉我们,现有的特许经营地点根据其周围的便利设施而变化,并且不符合 单一“类型”的街区。
寻找新地点
现在,我们已经为现有的特许经营场所创建了一个数据集,我们现在可以为新的潜在场所生成一个类似的数据集。我们可以使用 OSMNX 提取的图随机选择感兴趣区域中的节点,因为点将被限制在可步行的现有路径上:
*G = osmnx.graph_from_place(place, network_type='walk', simplify=True)
nodes = pd.DataFrame(osmnx.graph_to_gdfs(G, edges=False)).sample(n = 500, replace = False)*
随机抽样的地点(蓝色),我们将与现有的特许经营地点进行比较。图片作者。
重复前面的步骤,就可以找到这些潜在地点附近的便利设施…
一张略显凌乱的位置图,突出显示了现有的特许经营区(黑色)、新的预期位置(深绿色)和该区域的所有设施(按设施类型着色)。
测量现有和潜在位置之间的相似性
这是我们一直在等待的部分;衡量现有地点和潜在地点的相似性。我们将使用成对余弦相似度来实现这一点,其中每个位置都由一个基于附近设施的多样性和数量的向量组成。在这种地理空间环境中,使用余弦相似度有两个好处:
- 矢量长度不需要匹配 =我们仍然可以测量具有不同类型设施的位置之间的相似性。
- 相似性不仅仅基于娱乐设施的频率 =因为我们也关心娱乐设施的多样性,而不仅仅是数量。
我们计算潜在新位置相对于所有其他现有位置的余弦相似性,这意味着我们有多个相似性得分。
*max_similarities = []
for j in range(len(new_locations)):
similarity = []
for i in range(len(existing_locations)):
cos_similarity = cosine_similarity(new_locations.iloc[[j]].values, existing_locations.iloc[[i]].values).tolist()
similarity.extend(cos_similarity)
similarity = [np.max(list(chain(*similarity)))]
average_similarities.extend(similarity)
node_amenities['averaged similarity score'] = max_similarities*
那么我们如何定义什么是相似呢?
对于这一点,我们可以记住现有的位置并没有形成一个单一的集群,意味着存在异质性。一个很好的类比可以是在评价一个人能否加入一个友谊团体时:
- 一个友谊群通常包含不同的人,他们有不同的特点,而不是一群有相同特点的人。
- 在一个群体中,人们会与群体中的不同成员或多或少地分享一些特征。
- 一个新的潜在成员不一定需要与团队中的每个人都相似才能被认为是合适的。
因此,在与现有位置比较时,我们选择了最大相似性分数,因为这告诉我们潜在位置与至少一个其他现有特许经营位置相似。取平均值会导致较低的分数,因为特许经营位置之间附近的便利设施存在差异。我们现在可以绘制结果,用相似性分数着色,看看哪些区域可能成为新的特许经营点:
潜在位置按相似性得分着色(黄色代表高相似性百分比,紫色代表无相似性),旁边是现有特许经营位置(黑色)。图片作者。
我们在市中心的位置的相似性得分都显示出与现有位置有很强的相似性,这是有道理的,因此我们真正感兴趣的是具有高相似性得分的位置,这些位置位于远离现有特许经营位置(地图上的黑点)。
最终想法
我们已经使用地理空间技术来评估咖啡店特许经营扩展到新位置的潜在位置,使用基于附近便利设施的余弦相似度分数。这只是一幅大图的一小部分,人口密度、可达性等因素。也应该被考虑在内。
请关注下一篇文章中的,在那里,我们将通过一些建模来进一步完善这个想法。感谢阅读!
如何使用数据分析来理解数据
原文:https://towardsdatascience.com/german-credit-data-part-1-exploratory-data-analysis-8f5f266b2426
使用德国信用数据集对贷款申请人进行探索性分析
由 Ibrahim Boran 在 Unsplash 上拍摄的照片
介绍
数据分析是分析原始数据以获取信息并对数据做出结论的过程。数据分析是数据科学中的一个重要领域,因为它可以帮助企业优化绩效。数据分析帮助企业降低成本,提高企业的整体效率。
当银行收到贷款申请时,它必须决定是否批准贷款。银行根据申请人的资料做出贷款决定。两种类型的风险与银行的决策相关联
- 如果申请人的信用风险为良好,即有可能偿还贷款,那么不批准此人的贷款会导致银行的业务损失
- 如果申请人有不良信用风险,即不太可能偿还贷款,那么批准此人的贷款会给银行带来财务损失
目标
德国信贷数据的目的是最大限度地减少向申请人发放高风险贷款的机会,同时最大限度地增加从优质贷款中获利的机会。在对申请人的贷款申请做出决定之前,贷款经理会考虑申请人的人口和社会经济概况。
德国信用数据集是从 UCI 机器学习库下载的公开可用数据集。德国信贷数据包含 20 个变量的数据,以及对 1000 名贷款申请人的申请人是否被视为良好或不良信贷风险的分类。该任务需要研究数据并建立预测模型,以便为银行经理提供指导,从而根据潜在申请人的个人资料决定是否批准向其提供贷款。
探索性数据分析
探索性数据分析(EDA)用于对数据集进行彻底的检查。EDA 揭示了数据集中可能不明显的趋势、模式和关系。在这个案例研究中,我使用了原始数据集的简化版本,它只有 10 个变量:
- 年龄(数字)
- 性别(字符串:男性,女性)
- 工作(数字:0 —非熟练和非居民,1 —非熟练和居民,2 —熟练,3 —高技能)
- 住房(字符串:自有、租赁或免费)
- 储蓄账户(字符串—少量、中等、相当丰富、丰富)
- 支票账户(字符串—小额、中等、大额)
- 信贷金额(数字,单位为德国马克)
- 持续时间(数字,以月为单位)
- 用途(正文:汽车、家具/设备、收音机/电视、家用电器、维修、教育、商务、度假/其他)
- 风险(字符串—好的,坏的)
导入库并读取数据集
作者图片
作者图片
- 数据集包含 10 列和 1000 行(4 个要素为 int64,6 个要素为对象数据类型)
- 支票和储蓄账户是仅有的两个缺少值的列
数据的统计摘要
作者图片
- 平均年龄是 36 岁
- 取整后的平均持续时间是 21
- 年龄范围从 19 岁到 75 岁
- 德国马克的信用范围是从 250 到 18424
清除丢失的值
作者图片
- “储蓄帐户”列中有 183 个缺失值
- 支票帐户栏中有 394 个缺失值
Checking 和 Saving accounts 列中的值是分类的,所以我决定仔细看看这些值是什么。
作者图片
通过删除没有值的行,数据集将丢失 577 个实例,超过一半。这是一个重大的数据损失。为了避免这种情况,我用 none 替换了空值。空值的申请人可能在申请时没有储蓄或支票账户。
作者图片
可视化数据
目标变量分布
作者图片
- 有 700 例申请人被归类为良好
- 有 300 例申请人被归类为不良
数值变量的配对图
作者图片
- 年龄、信用额度和持续时间的分布图具有正的偏斜
年龄分布
作者图片
- 所有的图形都有一个正偏差,表示平均值大于中值
- 20 至 30 岁的申请人更有可能申请贷款
- 申请人不太可能申请高信用贷款
- 更多的贷款在发放后 20 个月左右就已经还清了
- 该银行更有可能接受 20 至 30 岁的申请人,并要求 250 至 2500 德国马克的贷款
作者图片
- 信贷额低于 5000 德国马克的申请人中有 50%以上被列为良好
- 贷款信用超过 5000 马克的成年人更有可能被归类为不良贷款
- 学生和青年申请人最有可能申请信贷额低于 5000 马克的贷款
住房分配
作者图片
- 大多数申请人拥有一套房子
- 一半以上拥有被归类为好的房子的申请人
作者图片
- 小提琴图的密度曲线表明,申请人的信贷额低于 5000 德国马克的频率较高
按性别分布
作者图片
- 数据中男性申请者比女性多两倍
- 大多数申请人都属于技术工种
- 大约 2/5 的男性申请人和 1/3 的女性申请人被归类为不良
- 小提琴图的密度分布遵循相同的趋势,表明大多数男性和女性申请人被归类为熟练工人
按工作类别分布
作者图片
- 拥有技能工作的申请者有两倍多被归类为好
- 超过 50%的申请人属于熟练和非熟练和常驻工作类别
- 高技能的申请者更有可能获得更大的贷款
检查和保存帐户分配
作者图片
- 储蓄账户很少或没有储蓄账户的申请人更有可能申请贷款
- 大多数申请者都属于“小人物”一类
- 在“小 T21”类别中,50%的申请者年龄在 25 至 45 岁之间
- 拥有中等储蓄账户、相当富有储蓄账户和富有储蓄账户的申请者更有可能被归类为优秀
- 拥有很少和没有储蓄账户且贷款信用额超过 5000 德国马克的申请人更有可能被归类为不良
作者图片
- 300 多名申请者没有支票账户
- 超过 3 倍没有支票账户的申请人被归类为好
- 拥有适度支票账户的申请人中,50%的人年龄在 25 岁到 40 岁之间
- 拥有丰富支票账户的申请人分布更广,他们被归类为好账户,年龄在 25 到 45 岁之间
- 信用额度高且支票账户中的 T42 很少的申请人更有可能被归类为不良
按目的分配
作者图片
- 很大一部分申请人要求贷款购买汽车、收音机/电视机
- 一半以上的申请人申请的贷款少于 5000 德国马克
- 高信用贷款的申请人更有可能被归类为不良贷款
按持续时间分布
作者图片
- 发放的大多数贷款期限为 12 至 24 个月
- 大多数在 24 个月内偿还贷款的申请人被归类为良好
- 大多数贷款期限超过 24 个月的申请人被归类为不良
分析得出的结论
- 期限少于 24 个月的贷款更有可能被偿还
- 发放信贷额少于 5,000 德国马克、期限少于 24 个月的贷款比较保险
- 拥有房产的申请人表明他们经济独立,是更好的贷款候选人
- 拥有高技能和高技能工作的申请人是发放贷款的更安全的候选人
- 汽车贷款是银行发放的最常见的贷款,损益比率很高(最赚钱的贷款)
- 发放 2,500 马克以下的贷款比不太可能偿还的高信用贷款更有利可图
银行经理可以利用上面的信息做出是否继续贷款申请的明智决定。使用数据集建立一个预测模型将有助于增加发放好贷款(财务收益)的机会,同时最小化坏贷款(财务损失)。本文的第二部分将介绍预测建模和交叉验证,以便根据数据选择最佳模型。
项目可访问性
数据集引用
汉斯霍夫曼。(1994).Statlog(德国信用数据)。UCI 机器学习知识库
这个数据集是在知识共享署名 4.0 国际 (CC BY 4.0)许可下许可的。
让你的机器学习模型性能提升 10%
原文:https://towardsdatascience.com/get-10-jump-in-your-machine-learning-model-performance-fa8bddf590d1
超越超参数调整提高模型性能的逐步指南
假设您已经确定了一个机器学习模型和相应的超参数,这些模型和超参数为您提供了最佳性能,但模型的精度仍然低于基线/预期精度。对你来说是穷途末路还是可以进一步改善?
在这篇博客中,我将带你经历一个超越超参数调整的提高模型性能的 3 步过程。我们开始吧!
模型改进生命周期(图片由作者提供)
第一步:错误分析
错误分析的目的是使用以下方法确定改进的空间:
- 确定模型哪里出了问题
总体准确性数字就像一个总结,它并不指向模型出错的具体区域。因此,找出模型出错的地方非常重要,这样我们就可以设计一些改进策略。
示例:使用 X 射线图像检测肺炎。
图片来源:Unsplash
假设我们正在构建一个使用胸部 X 射线检测肺炎的模型,我们希望提高模型的准确性。
分析样品
我们可以从模型预测出错的地方挑选 200 个样本,并试图理解错误预测的原因。当我们分析这些案例时,我们发现一些图像是模糊的,一些有暗/亮背景,反射,等等。我们可以使用这些类别来创建一个汇总表(如下所示)来捕捉错误预测的原因。
捕捉预测错误的原因(图片由作者提供)
总结分析
现在,我们可以创建一个汇总表来查找标签之间的错误分布(错误计数/总错误)以及这些标签的错误率(该标签的错误计数/总图像)。
错误标签的分布(图片由作者提供)
为了找到“带有该标签的全部图像”,你可以训练一些模型来识别标签,或者你可以选取一个样本,找到标签的分布,并将其扩展到人群。
查看上表,我们可以发现大多数错误都集中在“暗背景”标签中(40%),并且错误率在该标签中也是最大的(16%)。这是否意味着我们应该关注这个标签来提高我们的模型性能?
不尽然,我们需要将这些错误率与基线性能进行比较。
2。离基线多远
为了找到我们应该集中精力提高我们的准确性的地方,我们需要比较模型错误率和基线错误率。
现在,您的基线可能是以下之一:
- 该任务的人类水平的性能。
- 为您的用例提供最先进的/开源的性能。
- 您计划替换的以前部署的模型。
在下表中,我们添加了人因水平的绩效(或错误率)作为基线。
将模型错误率与基线错误率进行比较(图片由作者提供)
我们将发现我们的模型的错误率和原因标签的人类水平错误之间的差距/差异。
模型误差和人为误差之间的差距(图片由作者提供)
3。确定工作的优先顺序
为了区分工作的优先顺序,我们需要增加一个角度:找到带有标签的数据的百分比。
现在结合(乘以)“间隙”和“该标签的数据百分比”,如果我们对不同的标签达到人类水平的准确性,我们可以获得潜在的准确性提升。
使用差距和数据分布来寻找改进的潜力(图片由作者提供)
“暗背景”和“反射”具有提高整体准确度的最大潜力。因此,我们可以根据实现的难易程度来确定其中一个的优先级。
第二步:完善输入数据
假设我们正致力于提高“暗背景”标签/数据的准确性。
现在,我们将尝试生成更多的数据点,以捕捉我们的算法表现不佳但人类(或其他基线)表现良好的现实例子。
我们可以遵循以下策略之一来生成更多数据点:
- 收集更多具有暗背景的 X 射线图像。
- 创建合成数据点:我们可以选择没有暗背景的 X 射线图像,并为这些图像合成添加暗背景。
第三步:模特训练
第 3 步是在这个更新的输入数据集(预先存在的数据点+新创建的数据点)上训练您的模型。
重复所有 3 个步骤,以达到人类水平(或基线)的表现。
我们(我和我的团队)目前正在一个文本分类用例中遵循这种 3 步方法,我们已经能够将分类准确率从 64%提高到 75%。
结论
为了提高超参数调整之外的模型性能,我们可以使用误差分析技术来识别与基线相比模型表现不佳的类别/标签。一旦我们确定了要关注的标签,我们就可以改进与这些标签相对应的输入数据,并重新训练我们的模型。我们可以重复这个循环,以达到预期的准确性(或性能指标)。
如果你觉得我的博客有用,那么你可以 关注我 每当我发布一个故事时,你都可以直接得到通知。
如果你自己喜欢体验媒介,可以考虑通过 注册会员 来支持我和其他成千上万的作家。它每个月只需要 5 美元,它极大地支持了我们,作家,而且你也有机会通过你的写作赚钱。
借助分析微服务从您的数据中获得更多价值
原文:https://towardsdatascience.com/get-more-out-of-your-data-with-analytics-microservices-9a5a34a3ad2f
凯尔·辛克森在 Unsplash 上的照片
数据的速度、种类和数量都在快速增长。根据 IDC 最近的研究,到 2025 年,全球创建的数据将激增至 181 吉字节,而企业将捕获约 16 吉字节(全球创建的数据中约 9%由企业捕获。)而且这个差距预计还会扩大。使用当前的实践,一些公司可能能力较弱,无法利用数据的增长。
消费者体验的数字化是数据泛滥的一个驱动因素。
一些公司正在寻求使用敏捷开发方法来缩小差距,这种方法允许他们尝试新想法,快速转向,并通过挖掘消费者行为数据来响应消费者需求。
成功弥合这一差距的公司将为自己创造竞争优势。他们能够收集数据,并以某种方式利用这些数据,从而获得独特的消费者洞察力。这些见解可以为他们的客户带来新的创新和体验,最终为他们自己带来新的商机。
就像他们服务的客户一样,商业服务公司需要缩小数据创建和使用的差距。像营销机构和咨询公司这样的服务公司将从采用新的设计模式中受益,这些设计模式代表客户促进了数据实践的创新。这些公司应该考虑分析微服务。
什么是微服务?
Sam Newman 是“构建微服务”和“整体到微服务”一书的作者,他将微服务描述为一种开发模式,这种开发模式“致力于为您提供许多选项来解决您可能面临的问题。”这是一个非常宽泛的定义,但对于处理模式不断演变的分析问题来说,这非常有吸引力。让我们潜入更深的地方…
他进一步将微服务描述为围绕特定业务领域构建的可独立部署的、与技术无关的应用服务的集合。
那很有帮助;并呼吁分析应用程序…
《微服务模式》的作者 Chris Richardson 将这些服务描述为一组松散耦合的协作服务。他也同意它们是可独立部署的,并且他通过描述为它们工作的团队来扩展这个想法。他解释说,微服务团队是较小的团队,他们独立工作,独立于下游服务/用户/需求设计和构建服务,但在开发服务时要记住那些最终用户。
当我们考虑分析服务时,在快速发展的大数据环境中,微服务开发架构非常诱人。让我们探索它们的优缺点,以了解它们是否真正适合分析用例。
微服务的利弊。
当考虑在您的组织内部署微服务开发模式时,您将希望确定这些问题是否值得为保持微服务平稳运行而进行额外的工作。让我们首先看看这种架构模式的好处,并将其与支持它所需的额外工作进行比较。
分析微服务的优势
上市速度
微服务方法具有灵活性,因为您可以围绕新功能/服务进行设计,以满足该服务消费者的需求,而不是考虑它将如何影响其他应用程序。
敏捷性带来了满足快速发展的数据存储/分析领域中引入的新技术要求的能力。此外,当新数据上线时,我们可以开始使用新数据挖掘洞察力,从而使公司能够创新并利用额外数据可用性带来的商机。
组件的独立性
在使用单个数据库的典型分析方法中,一行新代码可能需要整个应用程序的完全重新部署,以促进更改。下游流程需要考虑对模式的任何更改;而上游流程将需要促进总体分析应用程序的新引入部分。
在微服务架构中,我们分离组件并独立构建它们。当他们一起工作时,我们不会让他们依赖其他服务或技术。这使得微服务成为探索新想法和开发与现有服务协同工作的新服务的有用方式。
面向 B2B 的复制/扩展服务
由于它们的独立性,微服务可以被复制以促进客户/代理关系。当新客户需要微服务时,服务提供商可以在客户环境中启动微服务,与其他客户的服务分开。这使得计费和会计更容易管理。额外的安全性也是这一变化的一个有益的副作用。
定价有利于客户,因为他们只需要为他们使用的东西付费,这在客户/代理关系中可能是一种有吸引力的模式。
此外,DevOps 和数据工程团队可以开发微服务的新功能和增强功能,并向所有现有用户推出更新,从而加快最新更新在客户群中的采用,而无需重新构建整个整体解决方案的平台。更新、补丁和功能变得可用并可在整个分析客户端范围内部署。
技术灵活性
微服务架构模式的另一个重要特征是快速探索和采用新技术的能力。因为每个微服务都是独立的,所以我们可以选择最好的工具来交付服务。
例如,我们可以通过集成一个合作伙伴服务来设计一个 ETL 服务,这个服务很好地为我们提供了对数据的访问。也许我们可以在这个服务的基础上使用 Go 作为我们的面向对象编程语言来转换和加载数据到我们的数据库。
Johannes Plenio 在 Unsplash 上拍摄的照片
另一个服务可以用 Python 编写,以利用其强大的数据科学功能和现成的机器学习库。也许这种微服务为利用它的客户建立了预测重要业务结果的预测模型,并为他们的可用分析服务增加了另一层。
第三种服务可能需要分布式计算环境来处理极其大量的数据(可能是以每天有数十亿行数据的 web 日志的形式。)该服务可以利用 Scala 和 Apache Spark 来处理非常大的数据,并将其转换成更易于管理的报告,供下游流程使用。
微服务让您能够灵活地为特定的业务应用选择合适的软件、硬件、容器和云服务,并将其与您的堆栈中的其他服务分开,以便进行维护、增强和扩展。
分析微服务的缺点
连接服务的复杂性
微服务是独立的,连接它们是这种设计模式面临的一个挑战。从总体价值和最终用户的角度来看,微服务通常不是独立的。它被集成到一个产品中,并最终将其结果交付给下游流程中的另一个微服务,或者是更广泛的交付或体验的一部分。
构建微服务的团队必须考虑移交,并确保它经过测试、高效,并交付服务用户所期望的价值。他们还必须考虑新特性的发布,以及这会如何影响没有为新特性做好准备的下游流程。解决这个问题越来越常见的方法是使用特性标志,这是一种打开和关闭特性的方法。
让我们用一个分析示例来演示这种复杂性。考虑一个对数据进行地理编码并向报告和其他分析服务提供纬度和经度的流程。如果在未来,开发团队决定转移到点坐标(将纬度和经度合并成一个数据点),他们可能需要在一段时间内支持点坐标和纬度/经度,直到下游微服务(或客户)可以切换到使用点坐标。只有在那时,他们才能拒绝纬度/经度数据点。这需要强有力的沟通——这让我想到了微服务的下一个话题。
协调
沟通和协调是微服务设计模式的关键。每项服务都需要向服务的内部和外部用户提供详细的文档。他们需要交流变化;并促进关于如何利用变化的培训。采用微服务设计模式的团队会花时间在服务之间的通信层。
在微服务之间的通信接口上的投资是微服务设计模式的一个不小的成本。
一个有沟通挑战的团队会和微服务斗争。因此,我不建议在客户可能与不止一家机构合作的跨机构或控股公司环境中使用微服务。微服务最好由处理大部分(如果不是全部)客户工作的完全集成的机构采用。事实上,微服务可能会使完全集成的代理模式更强大,对客户更具粘性,因为协调被强调为成功的关键。
人员配备和开发运维支持
由于开发运维团队可以利用的技术种类繁多,以及对敏捷性的强调,微服务设计模式将在开发运维文化中发挥最大作用。团队成员和人员配备应达到支持有效开发运维所需的水平。每个微服务都应该包含构建服务所需的所有人员。每个微服务都绑定到一个特定的业务运营,这些组织需要部署一个围绕该运营工作的团队,了解其流程、集成点和技术。
例如,分析微服务可能有一名设计数据集成的工程师,一名负责业务逻辑的解决方案顾问,以及一名负责底层硬件/技术的基础架构团队成员。该团队将在整个产品生命周期内负责微服务,管理新功能的开发、维护和部署。
采用微服务设计模式的分析团队也应该能够访问支持自动化所需的工具。微服务设计强调持续集成和交付,因此这些服务器需要启动并运行。像 AWS、Azure 和 GCP 这样的云提供商可以促进这些工具的采用。
与整体分析应用程序的比较。
与微服务相比,单片分析应用程序将所有功能放在一个流程中,并为每个部署复制整个流程。对于代理关系,考虑一个为所有客户服务的完整应用程序。每个客户端都以相同的方式部署,并利用所有的整体功能,因为它们被迫承担整个应用程序。
作者图片
独石确实有一席之地,但在寻找构建新功能时,它们可能会变得令人沮丧。对应用程序的一小部分进行更改需要为所有客户端重新构建和部署整个整体。
此外,如果不对整个服务进行重新设计和重新部署,分析团队就无法在其分析产品中探索新技术,因此“独石”可能会扼杀创新。
微服务采用建议。
对于考虑在其分析功能中采用微服务的组织来说,重要的是要记住分析产品不是项目。产品没有开始和结束日期。它们将继续存在,直到被新产品取代或因缺乏需求而被淘汰。他们必须投资。他们必须发展以解决用户的需求。微服务方法也是一种产品方法,每个服务为最终用户/客户解决一个分析问题。
组织可以考虑 MonolithFirst(参见 Martin Fowler 的推荐)。)对于分析团队来说,这可以从一个单一的应用程序开始,为企业数据仓库提供动力,供所有客户使用。也许该解决方案解决了组织及其客户最常见的一些问题。随着分析服务的发展,对分析用户提出了更多的问题——测试整体应用程序的能力。
我认为下一个合乎逻辑的步骤——在这种情况下——是开发微服务来扩展 monolith。也许一项新的分析服务会作为微服务分离出来,满足特定的业务需求。该产品会自行发展。它从上游的其他服务(或单片本身)获取输入,并与下游的其他服务和/或单片应用程序进行通信。
总之,微服务使企业能够缩小数据使用/数据创建的差距。
数据洪流为企业创造了新的机会,让他们找到创新的新方法来创建数据驱动的应用程序,为他们的客户带来新的体验。这些新的机会将从有足够能力部署新的数据工程和开发来支持新的分析服务的团队中体现出来。
微服务设计模式使团队能够探索新技术,快速失败,并交付新服务,而无需重新构建他们的整个分析基础架构。企业应将微服务视为一个帮助数据和分析创新的流程。
关于此主题的进一步探索资源:
- 纽曼,S. (2021)。构建微服务:设计细粒度系统。奥莱利媒体。
- 纽曼,S. (2020)。整体到微服务:改变你的整体的进化模式。奥赖利媒体公司。
- c .理查森(2018)。微服务模式:以 Java 为例。曼宁。
- 克莱普曼博士(2021)。设计数据密集型应用程序:可靠、可伸缩和可维护系统背后的重要思想。奥赖利。
- 亨布尔,j .,,法利,D. (2015)。持续交付:通过构建、测试和部署自动化实现可靠的软件发布。艾迪森-韦斯利。
使用 D3js 和 Python 使散点图具有交互性,从而充分利用散点图。
散点图对于可视化两组数值变量之间的关系非常有用。当它与缩放和刷功能交互时,它甚至更有洞察力。
使用 D3Blocks 的散点图示例(图片由作者提供)。
散点图可能是最著名的可视化数字变量的图表。这样的基本图表有时非常有用,尤其是当它们与刷和缩放功能交互时。 在这篇博文中,我将演示如何创建交互式散点图,使用不同的颜色、大小、工具提示以及在两组或三组坐标之间滑动数据点。散点图是 D3Blocks 库 的一部分,可以使用 Python 创建。后端用 D3js 开发,输出封装成一个 HTML。这使您可以轻松地将图表嵌入到一个更大的框架中,或者您可以直接共享/发布它,只需要一个互联网浏览器。
如果你觉得这篇文章很有帮助,可以使用我的 推荐链接 继续无限制学习,并注册成为中级会员。另外, 关注我 关注我的最新内容!
散点图是 D3Blocks 的一部分。
D3Blocks 是一个包含各种图表的库,其可视化后端构建在(d3) javascript 上,但可使用 Python 进行配置。通过这种方式, D3Blocks 库将 d3-javascript 的优势(如速度、可伸缩性、灵活性和无限创造力)与 Python 结合在一起,以便快速轻松地访问广泛的社区,如数据科学领域。在 D3Blocks 中的每个图表的输出,比如散点图,被封装到一个 HTML 文件中。这使得在网站上分享或发布或将其嵌入仪表板变得非常容易。此外,除了浏览器之外,它不需要任何其他技术来发布或共享图表。更多关于D3 blocks的信息可以在本博客 1 中找到。
创建散点图的原因。
**散点图是可视化数字变量和观察这种关系的本质的理想选择。例如,散点图可以帮助显示两个变量之间的线性关系的强度(例如相关性),但也可以用作质量控制,用于勘探目的,或获得对底层分布的更好理解。
数据点的标签属性和移动。
尽管散点图可能很有见地,但由于数据点的重叠,防止过度绘制并不总是直截了当的。这使得正确识别变量之间的关系具有挑战性。为了克服一些挑战, D3Blocks 的散点图包含各种 标签属性 ,具有 刷 和缩放功能。标签属性有*****颜色工具提示信息不透明度*** ,以及 描边颜色 (图 1) 。 另外, a 渐变 可以应用在数据点高度重叠的情况下。****
图一。可以为每个点设置多个属性,例如颜色、笔画颜色、不透明度、大小和工具提示(图片由作者提供)
另一个挑战是两组或三组变量之间的比较。这通常会导致创建多个散点图,然后将它们并排可视化。虽然可以用这种方式观察全球结构,但描述局部差异并不容易。为了克服这个挑战, D3Blocks 中的散点图可以让数据点在不同坐标之间移动。此博客中显示了一个用例,但为了演示,我将创建一个小示例:
首先,安装 D3Blocks 库 :
**pip install d3blocks**
在下面的代码部分,我们将创建 3 个数据点,并让它们在三组坐标之间移动。此外,我们将为数据点添加一些标签属性,如颜色、大小和不透明度。第一个数据点的坐标为: (x,y) = (1,1),(x1,y1) = (1,10),(x2,y2) = (5,5)。其他坐标见下面的代码部分。
**from d3blocks import D3Blocks
# Initialize
d3 = D3Blocks()
# Import example
x=[1, 1, 1]
y=[1, 2, 3]
x1=[1, 1, 1]
y1=[10, 9, 5]
x2=[5, 6, 7]
y2=[5, 5, 5]**
为三个数据点中的每一个指定标签属性:
**size = [15, 20, 25]
color = ['#FF0000', '#0000FF', '#00FF00']
stroke = ['#FFFFFF', '#FFFFFF', '#FFFFFF']
opacity = [0.7, 0.8, 0.8]
tooltip = ['1st datapoint', '2nd datapoint', '3th datapoint']**
现在只需向散布函数提供输入参数:
**# Set all propreties
d3.scatter(x, # x-coordinates
y, # y-coordinates
x1=x1, # x1-coordinates
y1=y1, # y1-coordinates
x2=x2, # x2-coordinates
y2=y2, # y2-coordinates
size=size, # Size
color=color, # Hex-colors
stroke=stroke, # Edge color
opacity=opacity, # Opacity
tooltip=tooltip, # Tooltip
scale=False, # Scale the datapoints
label_radio=['(x, y)', '(x1, y1)', '(x2, y2)'],
figsize=[1024, 768],
filepath='scatter_demo.html',
)**
最终的散点图如图 2 所示:
图二。散点图中的运动演示。通过单击单选按钮(左下角),数据点开始向预定义的坐标集移动(图片由作者提供)。
构建您自己的交互式散点图。
让我们加载【MNIST】数据集【4】,这是一个众所周知的手写数字数据集,免费使用,非常适合检查和展示散布功能。使用 clustimage 库 很容易执行原始数据集向低维空间的映射。这里,我们将加载预先计算的主成分和 t-SNE 坐标,它们代表了数位到数位的相似性(参见下面的代码部分)。使用散点图,我们现在可以通过让数据点在两个映射之间移动,轻松地可视化和比较两个映射之间的关系。比例选项设置为真以确保两组坐标在同一范围内。此外,标签属性如工具提示、大小、不透明度和(笔画)颜色也被设置。颜色的输入可用十六进制颜色手动指定,但也可以是字符串标签。在这个例子中,我使用了数字标签,它通过使用输入色图 (cmap) 自动转换成十六进制颜色。缩放和刷图功能始终可用,如图图 3 所示。这里的显示了一些交互示例。
**# Load libraries
from d3blocks import D3Blocks
import numpy as np
# Initialize
d3 = D3Blocks()
# Load PC and tSNE coordinates
df = d3.import_example('mnist')
# Set random sizes, and opacity
size=np.random.randint(0, 8, df.shape[0])
opacity=np.random.randint(0, 8, df.shape[0])/10
# Tooltip are the digit labels
tooltip = df['y'].values.astype(str)
# Set all propreties
d3.scatter(df['PC1'].values, # PC1 x-coordinates
df['PC2'].values, # PC2 y-coordinates
x1=df['tsne_1'].values, # tSNE x-coordinates
y1=df['tsne_2'].values, # tSNE y-coordinates
color=df['y'].values.astype(str), # Hex-colors or classlabels
tooltip=tooltip, # Tooltip
size=size, # Node size
opacity=opacity, # Opacity
stroke='#000000', # Edge color
cmap='tab20', # Colormap
scale=True, # Scale the datapoints
label_radio=['PCA', 'tSNE'],
figsize=[1024, 768],
filepath='scatter_demo.html',
)**
图 3。散点图。缩放是通过选择一个区域。双击将返回主屏幕。(图片由作者提供)
如果您想对任何标签属性进行更改,您可以更改数据框中的值,如代码段 2 所示。编辑完成后,散点图可以通过 show() 功能再次可视化。
**# Make dits in the dataframe
d3.edge_properties
# label x y x1 .. size stroke opacity tooltip
# 0 0 0.472107 0.871347 0.294228 .. 0 #000000 0.1 0
# 1 1 0.624696 0.116735 0.497958 .. 0 #000000 0.5 1
# 2 2 0.608419 0.305549 0.428529 .. 4 #000000 0.6 2
# 3 3 0.226929 0.532931 0.555316 .. 4 #000000 0.0 3
# 4 4 0.866292 0.553489 0.589746 .. 1 #000000 0.6 4
# ... ... ... ... ... .. ... ... ...
# 1792 9 0.262069 0.709428 0.693593 .. 5 #000000 0.5 9
# 1793 0 0.595571 0.837987 0.352114 .. 6 #000000 0.5 0
# 1794 8 0.668742 0.359209 0.520301 .. 6 #000000 0.4 8
# 1795 9 0.416983 0.694063 0.683949 .. 6 #000000 0.4 9
# 1796 8 0.489814 0.588109 0.529971 .. 1 #000000 0.4 8
# [1797 rows x 12 columns]
# Show the updated chart
d3.show(filepath='scatter_demo.html', label_radio=['PCA', 'tSNE'])**
最后的话
我演示了如何使用 Python 创建自己的交互式独立散点图。散点图对于可视化数字变量非常有用,当它是交互式的时,变得更加有洞察力。散点图是D3 图块 中的一个图块,D3js 的使用显示了它的力量和优势,例如速度和灵活性。喜欢这个的话,还有更多好用的互动 D3js 块,比如D3 graphSankey chart【5】moving bubbles【6】等等。你可以随意使用这个库!
注意安全。保持冷静。
欢呼,E.
如果你觉得这篇文章很有帮助,可以使用我的 推荐链接 继续无限制学习,并注册成为中级会员。另外, 关注我 关注我的最新内容!
软件
我们连线吧!
- 我们在 LinkedIn 上连线
- 在 Github 上关注我
- 在媒体上跟随我
参考
- D3Blocks:创建交互式和独立 D3js 图表的 Python 库。 中等,2022 年 9 月
- t-SNE、UMAP、PCA 等映射之间的定量比较。 中等,2022 年 5 月
- 用 Python 创作精美单机互动 D3 图表, 2022
- E, 用 Python 在 d3js 中创建漂亮的 Sankey 图表的动手指南。中 ,2022 年 10 月
- 如何用 Python 在 d3js 中创建讲故事的移动气泡图 ,2022 年 9 月
- https://keras.io/api/datasets/mnist/ MNIST 数据集(CC BY-SA 3.0)
了解 Python 中的音频特征提取
原文:https://towardsdatascience.com/get-to-know-audio-feature-extraction-in-python-a499fdaefe42
理查德·霍瓦特在 Unsplash 上的照片
了解 Python 中的音频特征提取
探索音频文件的波形相关特征,以便在分析和 ML 中进一步使用
在大数据时代的兴起,我们可以收集比以往更多的数据。数据收集不再局限于数字格式的交易数据,而是出现了其他格式和结构,包括文本、图像、音频甚至视频格式。如果进行相应的处理和分析,我们可以在现有表格数字数据的基础上,使用这些多媒体数据生成更有价值的见解。在本文中,我将分享我们如何从音频文件中提取一些突出的特征,以便进一步处理和分析。
本分析中使用的完整代码共享在 本 Github 项目 下。
音频文件和概念
在音频数据分析中,我们处理和转换由数字设备捕获的音频信号。根据它们被捕获的方式,它们可以是多种不同的格式,如 wav、mp3、m4a、aiff 和 flac。
引用Izotope.com的话,波形(wav)是最流行的数字音频格式之一。它是一种无损文件格式,这意味着它捕捉原始音频的最接近的数学表示,没有明显的音频质量损失。在 mp3 或 m4a(苹果的 mp3 格式)中,数据以这种方式被压缩,因此它可以更容易地分发,尽管质量较低。在音频数据分析中,大多数库支持 wav 文件处理。
作为波的一种形式,声音/音频信号具有以下一般属性:
- 频率:单位时间内振动的发生次数
- 振幅:波浪上一点从其平衡位置测得的最大位移或移动距离;影响声音强度
- 声速:声波在单位时间内传播的距离
从音频文件中提取的信息只是上述主要属性的变换。
音频文件的探索性分析
在这个分析中,我使用三个不同的音频文件来比较不同音频类型的不同数字音频特征。它们在 Chosic.com 和 Freesound.org 有售。
这些文件源的详细信息可以在本文末尾找到(参考资料部分)。
这些文件将主要用这些 Python 包来分析: librosa 用于音频信号提取和可视化, pydub 用于音频文件操作, wave 用于读取 wav 文件。
通用音频参数
就像我们通常通过获取数据的统计摘要(即使用" Dataframe.describe "方法)来开始评估表格数据一样,在音频分析中,我们可以从获取音频元数据摘要开始。我们可以利用 pydub 中的 audiosegment 模块来实现。
以下是一些可以提取的一般特征:
- 通道:通道数量;1 个单声道,2 个立体声
- 样本宽度:每个样本的字节数;1 表示 8 位,2 表示 16 位
- 帧率/采样率:使用样本的频率(赫兹)
- 帧宽:每个“帧”的字节数。一帧包含每个通道的样本。
- 长度:音频文件长度(毫秒)
- 帧数:样本的帧数
- 强度:以 dBFS 为单位的响度(相对于最大可能响度的 dB)
from pydub import AudioSegment# Load files
audio_segment = AudioSegment.from_file("Downloads/Warm-Memories-Emotional-Inspiring-Piano.wav")# Print attributes
print(f"Channels: {audio_segment.channels}")
print(f"Sample width: {audio_segment.sample_width}")
print(f"Frame rate (sample rate): {audio_segment.frame_rate}")
print(f"Frame width: {audio_segment.frame_width}")
print(f"Length (ms): {len(audio_segment)}")
print(f"Frame count: {audio_segment.frame_count()}")
print(f"Intensity: {audio_segment.dBFS}")
上述三个文件的参数值可以在下面找到。它们都是立体声文件,采样率为 44100Hz。由于它们具有相同的采样率,因此长度较长的文件也具有较高的帧数。这里有一个有趣的发现,即“动作摇滚”文件比其他文件有更高的强度值,因为与其他文件相比,它是具有明显的更高响度的摇滚音乐。
分析的三个音频文件的通用音频参数(图片由作者提供)
我们还可以将这些文件的振幅随时间的变化可视化,以了解波浪运动。
import wave# Open wav file and read frames as bytes
sf_filewave = wave.open('Downloads/Warm-Memories-Emotional-Inspiring-Piano.wav', 'r')
signal_sf = sf_filewave.readframes(-1)# Convert audio bytes to integers
soundwave_sf = np.frombuffer(signal_sf, dtype='int16')# Get the sound wave frame rate
framerate_sf = sf_filewave.getframerate()# Find the sound wave timestamps
time_sf = np.linspace(start=0,
stop=len(soundwave_sf)/framerate_sf,
num=len(soundwave_sf))# Set up plot
f, ax = plt.subplots(figsize=(15, 3))# Setup the title and axis titles
plt.title('Amplitude over Time')
plt.ylabel('Amplitude')
plt.xlabel('Time (seconds)')# Add the audio data to the plot
ax[0] = plt.plot(time_sf, soundwave_sf, label='Warm Memories', alpha=0.5)plt.legend()
plt.show()
振幅随时间变化的可视化(图片由作者提供)
衍生音频特征
转到更有趣的(尽管可能有点令人困惑:))特性。使用 librosa 分析音频特征可以提取和可视化许多高级特征。
声谱图
提取的音频特征可以在声谱图上可视化。引用维基的话,频谱图是信号的 频率 随时间变化的 频谱 的直观表示。它通常被描述为热图,强度以不同的颜色梯度显示。****
import librosax, sr = librosa.load('Downloads/Warm-Memories-Emotional-Inspiring-Piano.wav')# Spectrogram of frequency
X = librosa.stft(x)
Xdb = librosa.amplitude_to_db(abs(X))
plt.figure(figsize=(15, 3))
librosa.display.specshow(Xdb, sr=sr, x_axis='time', y_axis='hz')
plt.colorbar()
温馨回忆音频文件声谱图(图片由作者提供)
垂直轴显示频率,水平轴显示剪辑的时间,颜色变化显示音频波的强度。
均方根(RMS)
这里的均方根是指信号的总幅度,通俗地说可以解释为音频文件的 响度或能量 参数。**
y, sr = librosa.load(audio_data)# Get RMS value from each frame's magnitude value
S, phase = librosa.magphase(librosa.stft(y))
rms = librosa.feature.rms(S=S)# Plot the RMS energy
fig, ax = plt.subplots(figsize=(15, 6), nrows=2, sharex=True)
times = librosa.times_like(rms)
ax[0].semilogy(times, rms[0], label='RMS Energy')
ax[0].set(xticks=[])
ax[0].legend()
ax[0].label_outer()
librosa.display.specshow(librosa.amplitude_to_db(S, ref=np.max),
y_axis='log', x_axis='time', ax=ax[1])
ax[1].set(title='log Power spectrogram')
动作摇滚和暴躁老头文件的可视化结果如下所示。在这里,我们可以看到动作摇滚文件的 RMS 值始终很高,因为这种摇滚音乐始终响亮而强烈。另一方面,脾气暴躁的老人文件在响度上有平滑的上下波动,因为人类的语音自然会根据语音重点而有移动的音高和音量。
动作摇滚和暴躁老头文件的 RMS/能量谱图(图片由作者提供)
过零率
引用维基百科,过零率(ZCR)是信号从正到零再到负或者从负到零再到正的速率。它的值已经广泛用于语音识别和音乐信息检索,是对敲击声进行分类的关键特征。像摇滚、金属、emo 或朋克音乐这样的高度打击乐声音往往具有较高的过零率值。
我们可以通过放大幅度时间序列中的某一帧,统计它在 y 轴上通过零值的次数,并对整个音频进行外推,来手动得到这个数据。或者,librosa 中有一个函数,我们可以用它来获得过零状态和速率。
y, sr = librosa.load('Downloads/Action-Rock.wav')
zcrs = librosa.feature.zero_crossing_rate(y)print(f"Zero crossing rate: {sum(librosa.zero_crossings(y))}")plt.figure(figsize=(15, 3))
plt.plot(zcrs[0])
plt.title('Action Rock')
以下是样本音频文件的过零点值和速率。在这里,我们可以看到动作摇滚文件的过零率明显高于温暖记忆文件,因为它是一首高度打击乐的摇滚歌曲,而温暖记忆是一首更平静的原声歌曲。
温暖记忆和动作摇滚文件的过零率(图片由作者提供)
梅尔频率倒谱系数(MFCC)
引用 Analytics Vidhya 的话,人类不会线性感知频率。与高频相比,我们更擅长检测低频中的差异,即使差距是相同的(即“50 和 1,000 赫兹”对“10,000 和 10,500 赫兹”)。在的梅尔音阶、中,等距离的音高在听者听来是等距离的。
Mel 频率倒谱系数(MFCC)是声音的短期功率谱的表示,基于 Mel 尺度中的一些变换。它通常用于语音识别,因为人们的声音通常在一定的频率范围内,并且彼此不同。在 Librosa 中,获取和显示 MFCCs 非常简单。
x, sr = librosa.load('Downloads/131652__ecfike__grumpy-old-man-3.wav')
mfccs = librosa.feature.mfcc(x, sr=sr)# Displaying the MFCCs:
plt.figure(figsize=(15, 3))
librosa.display.specshow(mfccs, sr=sr, x_axis='time')
人类语音的 MFCCs 值似乎比音乐文件更低且更动态。在下面的截图中,我们可以看到,与音乐文件相比,人类语音文件上有更多的深蓝色斑点以及不断变化的深红色和浅红色阵列。
音乐文件与人类语音的 MFCCs 可视化(图片由作者提供)
色度
我们可以使用色度特征可视化来了解某个音高的特征 {C,C♯,d,D♯,e,f,F♯,g,G♯,a,A♯,B}在采样帧中呈现的主导程度。
在下面的示例中,我们可以看到动作摇滚音乐文件有很强的 D 音阶,偶尔有 A 音阶。
x, sr = librosa.load('Downloads/Action-Rock.wav')hop_length = 512chromagram = librosa.feature.chroma_stft(x, sr=sr, hop_length=hop_length)
fig, ax = plt.subplots(figsize=(15, 3))
img = librosa.display.specshow(chromagram, x_axis='time', y_axis='chroma', hop_length=hop_length, cmap='coolwarm')
fig.colorbar(img, ax=ax)
动作摇滚音频文件的色度可视化(图片由作者提供)
温度图
Tempo 指的是一段音频的速度,通常以每分钟节拍(bpm)为单位来衡量。与古典音乐相比,像 hip-hop、techno 或 rock 这样的乐观音乐通常具有更高的节奏,因此节奏图特征对于音乐流派分类是有用的。
可以使用下面的命令在 librosa 中计算它。
y, sr = librosa.load('Downloads/Warm-Memories-Emotional-Inspiring-Piano.wav')
hop_length = 512# Compute local onset autocorrelation
oenv = librosa.onset.onset_strength(y=y, sr=sr, hop_length=hop_length)
times = librosa.times_like(oenv, sr=sr, hop_length=hop_length)
tempogram = librosa.feature.tempogram(onset_envelope=oenv, sr=sr,
hop_length=hop_length)# Estimate the global tempo for display purposes
tempo = librosa.beat.tempo(onset_envelope=oenv, sr=sr,
hop_length=hop_length)[0]
我们可以把节奏图中的速度想象成如下。在这里,我们可以看到一个摇滚音乐文件,它在整首歌中始终有一个高节奏,而与之相比的是一首结合了一些强节奏和弱节奏的舒缓歌曲。摇滚歌曲文件的整体速度约为 172bpm,而平静歌曲文件的速度约为 161bpm。
体温图可视化(图片由作者提供)
结束语
音频数据可能包含有价值的信息,这取决于分析师/工程师如何发现它们。这里分享的功能大多是技术音乐功能,可以用于机器学习模型而不是商业/产品分析。它们可以用于许多应用,从娱乐(对音乐流派进行分类)到商业(从客户电话中清除非人类语音数据)和医疗保健(识别心跳异常)。
本分析中使用的完整代码共享在 本 Github 项目 下。
资源
要了解有关音频/音乐特征提取的更多信息,您可以浏览下面的资源。
https://link.springer.com/book/10.1007/978-3-540-74048-3 https://www.kdnuggets.com/2020/02/audio-data-analysis-deep-learning-python-part-1.html
使用的音频参考
[1]温暖的回忆——月之琴键的情感激励钢琴|https://soundcloud.com/keysofmoon
归属 4.0 国际(CC BY 4.0)
音乐推广https://www.chosic.com/free-music/all/
[2]动作摇滚由 LesFM |https://lesfm.net/motivational-background-music/
音乐由https://www.chosic.com/free-music/all/
创作共用 CC 由 3.0 推广
[3]坏脾气老头包坏脾气老头 3.wav 由 ecfike |音乐推广由https://freesound.org/people/ecfike/sounds/131652/创作共用 0
免费获取回归神经网络中的不确定性估计
原文:https://towardsdatascience.com/get-uncertainty-estimates-in-neural-networks-for-free-48f2edb82c8f
给定正确的损失函数,标准的神经网络也可以输出不确定性
Christina Deravedisian 在 Unsplash 上拍摄的照片
W 当我们建立一个机器学习模型时,我们通常以这样的方式设计它,它输出一个单一的数字作为预测。来自 scikit-learn 的大多数模型是这样工作的:基于树的模型、线性模型、最近邻算法等等。同样适用于 XGBoost 和其他 boosting 算法,以及深度学习框架,如 Tensorflow 或 PyTorch 。
虽然这通常是好的,但最好也有一个围绕这个点估计的不确定性度量。这是因为“我们将销售 1000 辆 50 型汽车”和“我们将销售 1000 辆 5000 辆汽车”之间的差异是巨大的:从第一个陈述中,你可以得出该公司将销售 1000 辆左右的汽车,或多或少,而第二个陈述告诉你该车型根本没有任何线索。
注: 较低的不确定性并不意味着模型是正确的。作为人,它也可以对一些完全错误的事情非常固执己见。因此,像往常一样,评估模型的质量是至关重要的,在这种情况下也是如此。
贝叶斯推理
如果你读过我关于贝叶斯推理的文章(谢谢!你已经知道如何创建模型,不仅输出一个点,而是输出一个完整的目标分布。
图片由作者提供。
正因为如此,我们可以通过观察预测的分布或一些导出的数字(如标准差)来了解模型何时不确定。分布越窄(标准差越小),模型越确定。
不过,这一次我们不会去贝氏这里。虽然贝叶斯推理是您应该在某个时候学习的一个很好的领域,但它有几个缺点:
- 它在计算上甚至比神经网络更复杂
- 数学上更难理解,而且
- 你必须了解新的图书馆。
因此,这篇文章是为那些了解他们的深度学习框架,并希望在没有太多麻烦的情况下包括一些不确定性估计的人准备的。然而,如果你想在某个时候进入完全贝叶斯神经网络的领域,试试 PyTorch 的 Tensorflow Probability 或 Pyro 之类的库。
使神经网络揭示它们的不确定性
免责声明:同样,我不知道以下方法是否出现在任何论文或书籍中。它刚刚出现在我的脑海里,我想把它写下来。如果你知道任何来源,请在评论中给我一个提示,我会添加到文章中。谢谢!
更新: 马蒂亚斯·瓦尔德内格罗·托罗 指出我即将介绍的损失在他们的论文 中被称为方差衰减。
从现在开始,让我们坚持使用我们最喜欢的面包和黄油神经网络。在本文中,我将使用 Tensorflow 代码,但是您也可以轻松地将所有代码都用于 PyTorch 或其他框架。这里我们将考虑一个回归问题,但是类似的论点也可以用于分类任务。
推导均方误差
为了理解如何得到不确定性估计,我们必须先理解如何得到点估计。然后,我们将以一种简单的方式概括这一思想。因此,简单回顾一下,下面是均方误差 (MSE)损失函数:
图片由作者提供。
这很直观:某些真实值 yᵢ 和模型预测值 ŷᵢ 之间的差距越大,亏损就越高。但是,当指数中的 2 替换为 4 时,我们可以用同样的方式进行讨论。或者去掉 2,用绝对值|yᵢ–ŷᵢ|代替(平均绝对误差,MAE)。
那么,MSE 有什么特别之处呢?其中包含哪些假设?让我们来找出答案。⚠️ 危险:数学领先。如果这太多,直接跳到实现部分。这个结果很容易应用,即使你还不能理解这个理论。 ⚠️
假设如下:
给定输入特征 x ,真标 y 按正态分布用 表示μ(x)标准差,即y~N(μ ( x ), σ)。这意味着观察到的标签来自某个真值 μ ( x ),但被某个误差破坏,标准偏差为 σ。这个误差也叫噪声。 注意,很多时候我们写 ŷ 而不是 μ ( x )。**
神经网络(和大多数其他模型)的任务是预测这个μ(x)给定 x. 这使得平均预测正确,这是我们能做的最好的事情,因为我们不能预测噪声。现在的表达y~N(μ(x),σ* )只是表示以下意思:*****
图片由作者提供。
这只是用均值 ŷ=μ( x ) 和标准差 σ 描述单个标签 y. 分布的正态分布的密度函数,现在我们没有单个观测值 y 及其对应的预测值 ŷ ,而是几个,比如说 n 。假设所有的观察都是随机独立的,我们得到
图片由作者提供。
现在训练一个神经网络基本上意味着统计学家所谓的最大似然估计。这是一种比较花哨的说法我们要最大化上面的密度函数,也叫似然函数。****
现在,我们可以将最大似然估计与 MSE 最小化联系起来,如下所示:
- 最大化似然函数
- 意味着最大化最右边的项
- 意味着最大化 e 的指数
- 意味着最小化指数中的和,
- 意味着最小化 MSE(除以 n 不会改变最佳参数)。
或者作为一张图片:
图片由作者提供。
概化 MSE 损失
如果你熬过了最后一节,那么恭喜你,你已经走得很远了,你几乎达到了你的目标!我们只需要做一个简单的观察:
我们将 σ视为常数,在进行最大似然法时基本上将其忽略。
但是 σ 也正是我们想要估计的!这是因为它从定义上抓住了预测中的不确定性。那么,我们让我们的模型在 μ ( x ) 之外再输出一个值 σ ( x )怎么样?这意味着,即使对于简单的回归,模型也将有两个输出:一个是对真实值 μ ( x )的估计,另一个是给定 x 的不确定性估计 σ ( x )。****
图片由作者提供。
现在,我们可以用 σ ( xᵢ )代替上面等式中的所有 σ ,我们最终得到如下陈述:最大化似然函数意味着最大化该项
图片由作者提供。
这反过来意味着最小化指数中的巨大总和,这是我们最新导出的损失函数(没有吸引人的名字,请在评论中提出建议)😉):
图片由作者提供。
注意,我在中加入了 1/ n ,但是这并没有改变最优解,就像 MSE 的情况一样。
注: 这种损失有一些有趣的性质。首先,它仍然包含 MSE 位(yᵢ–μ(xᵢ))。此外,还有两个涉及 σ 的术语:ln( σ ( x ))以及 1/ σ ( x )。
为了保持较低的损耗, 型号不能输出非常大的值给(×63),因为随着σ(×T70)的增长,ln(σ(×)增加该模型也不能输出接近于零的非常小的值,因为这样一来项 1/ σ ( x )就变大了。这样,模型被迫输出一个合理的猜测为σ(x)到* 平衡两项的罚值 。***
只有当(yᵢ—μ(xᵢ))很小,即预测值非常接近真实值时,模型才能够输出很小的标准差 σ ( x )。在这种情况下,模型对其预测相当有把握。
好了,理论到此为止。我们现在应该编码了!
Tensorflow 中的实现
好了,我们已经知道,我们需要两样东西来制造一个标准的神经网络输出不确定性:
- 包含预测标准偏差(=不确定性)的第二个输出节点,以及
- 如上所述的客户损失函数。
在你选择的任何深度学习框架中实现这两件事应该都很容易。我们将在 Tensorflow 中进行,因为上次我已经选择了 PyTorch 来解释可解释的神经网络。😎
*****
让我们从一个简单的例子开始。
不断的噪音
首先,我们将创建一个由 1000 个点组成的玩具数据集,通过
import tensorflow as tf
tf.random.set_seed(0)
X = tf.random.uniform(minval=-1, maxval=7, shape=(1000,))
y = tf.sin(X) + tf.random.normal(mean=0, stddev=0.3, shape=(1000,))
我们可以将这个数据集可视化:
图片由作者提供。
好的,所以它仅仅是一个正弦波加上了 N (0,0.3)分布噪声。在最好的情况下,模型的实际预测遵循正弦波,而每个不确定性估计在 0.3 左右。我们通过以下方式建立一个简单的前馈网络
model = tf.keras.Sequential([
tf.keras.layers.Dense(32, activation='relu'),
tf.keras.layers.Dense(32, activation='relu'),
tf.keras.layers.Dense(32, activation='relu'),
tf.keras.layers.Dense(2) # Output = (μ, ln(σ))
])
好了,我们已经通过定义一个有两个输出的神经网络处理了第一个要素。*****
为了简化计算,我们直接假设第二个输出不是 σ ( xᵢ ),而是 ln( σ ( xᵢ ))。我们这样做是因为最后一层的两个神经元可以输出任意的实数值,尤其是比零小的数值,这对标准差没有意义。但是标准偏差的对数可以是任何实数,因此域匹配。而且我们无论如何都需要损失函数中的 ln( σ ( xᵢ )),那就去争取吧。说到损失函数,我们可以通过
*****def loss(y_true, y_pred):
mu = y_pred[:, :1] # first output neuron
log_sig = y_pred[:, 1:] # second output neuron
sig = tf.exp(log_sig) # undo the log
return tf.reduce_mean(2*log_sig + ((y_true-mu)/sig)**2)*****
剩下的就是一切照旧了。你用这个损失函数和拟合来编译模型。
*****model.compile(loss=loss)
model.fit(
tf.reshape(X, shape=(1000, 1)),
tf.reshape(y, shape=(1000, 1)),
batch_size=32,
epochs=100
)*****
让我们检查模型已经学习的不确定性估计:
*****print(tf.exp(model(X)[:20, 1]))
# Output:
# tf.Tensor(
# [0.29860803 0.27371496 0.32216415 0.32288837 0.31084406 0.30166912
# 0.32059005 0.3331769 0.31244662 0.31863096 0.30940703 0.32042852
# 0.3231969 0.29584357 0.31141806 0.32493973 0.3169802 0.32060665
# 0.30542135 0.31733593], shape=(20,), dtype=float32)*****
我看不错!该模型了解到噪声具有大约 0.3 的标准偏差。这是模型所学内容的可视化:
图片由作者提供。
我们就喜欢这样。实际预测 μ 遵循数据,而不确定性 σ 刚好高到足以捕捉标签 y 中的噪声。
变化的噪声
我们现在通过引入非恒定噪声来增加一点趣味,统计学家称之为异方差。看看这个:
*****tf.random.set_seed(0)
X = tf.random.uniform(minval=-1, maxval=7, shape=(1000,))
sig = 0.1*(X+1)
y = tf.sin(X) + tf.random.normal(mean=0, stddev=sig, shape=(1000,))*****
这创建了一个数据集,其特征 X 中的噪声增加。
图片由作者提供。
基本事实仍然是一样的:它是一个正弦波,模型应该能够捕捉到这一点。然而,该模型还应该知道, X 的值越高,意味着不确定性越高。
剧透:如果你在新的数据集上重新训练如上相同的模型,这正是你将看到的。
图片由作者提供。
在我看来很可爱。
结论
在本文中,您了解了如何调整神经网络,使其能够输出对不确定性的估计以及实际预测。它只需要一个额外的输出神经元和一个比 MSE 稍微复杂一点的损失函数。
不确定性估计的好处是,它们让您评估模型对其预测的信心,您知道是否可以信任模型的预测。它们还允许您报告估计的下限或上限,这在计算最好或最坏的情况时很有价值。
另一种获得不确定性估计的流行方法是使用贝叶斯推理。然而,数学更复杂,而且比我在这里向你展示的解决方案要慢得多。此外,我发现(深度)贝叶斯学习的软件包目前不像 Tensorflow 或 PyTorch 那样容易使用,尽管这可能会在贝叶斯方法获得更多牵引力时发生变化。不过,我喜欢这个话题,所以也来看看吧!😉
我在这里给你的是一个简单的工具,让你避开贝叶斯的争论,不需要你改变太多的日常行为,同时仍然给你一个贝叶斯世界的巨大好处。
奖金(感谢 的巨大投入)卡洛斯阿雅-莫雷诺:获得不确定性估计的另一种方法是使用自举。基本上,如果你使用随机森林,这就是正在发生的事情:你通过子采样从你的原始数据集创建 b 个更小的数据集,在每个数据集上训练一个模型,然后你得到 b 个输入的不同预测。这些 b 预测的平均值是你的最终预测,而这些 b 预测的标准差是不确定性的度量。例如,如果所有 b 模型的输出都差不多,那么不确定性就会很小。
然而,这种方法的问题是,你需要训练不同的车型,这可能相当昂贵。在随机森林中,它工作得很好,因为单个决策树快速且容易适应。对于神经网络来说,事情看起来更黑暗。杰里米·尼克松(Jeremy Nixon)等人也做了研究,即使抛开计算问题,自举神经网络可能也不会太有益。
我希望你今天学到了新的、有趣的、有用的东西。感谢阅读!
作为最后一点,如果你
- 想支持我多写点机器学习和
- 无论如何都计划获得一个中等订阅,
为什么不做 通过这个环节 ?这将对我帮助很大!😊
说白了,给你的价格不变,但大约一半的订阅费直接归我。
非常感谢,如果你考虑支持我的话!
如有问题,在LinkedIn上写我!**
使用 Azure 机器学习控制数据和模型漂移
以自动化的方式检测、分析和减轻数据和模型漂移
变化是生活中唯一不变的。在机器学习中,如果不小心管理,它会表现为数据漂移、模型预测和性能下降。
照片由 serjan midili 在 Unsplash 上拍摄
在本文中,我们将讨论数据和模型漂移,以及它如何影响生产模型的性能。您将学习识别和减轻漂移的方法,以及使用 Azure 机器学习从静态模型过渡到常青人工智能服务的最佳实践。
如果您想在实际例子中尝试这些概念,我们还提供了一个样本笔记本。
了解数据和模型漂移
许多机器学习项目在广泛的数据和特征工程、建模、训练和评估阶段之后,以部署到生产的令人满意的模型结束。然而,一个模型运行的时间越长,就有越多的问题可能在很长一段时间内未被发现。
模型漂移导致的数据漂移和性能下降
数据漂移是指输入数据的分布随时间变化。漂移会导致模型最初从训练数据中学习到的内容与生产过程中的推理观察结果之间存在差距。让我们看几个数据漂移的例子:
- 现实世界的变化:一个原本很小的人口群体越来越多地出现在劳动力市场上(例如战争难民);新的监管框架开始影响用户的同意(如 GDPR)
- 数据采集问题:物联网传感器损坏导致测量错误;出于隐私原因,web 表单的初始强制输入字段变成了可选字段
- 数据工程问题:意外的编码或缩放变化或变量交换
模型漂移伴随着模型性能随时间的下降(例如,监督分类用例中的精度下降)。模型漂移有两个主要来源:
- 现实世界的变化也被称为概念漂移:特征和目标变量之间的关系在现实世界中发生了变化。例子:在疫情期间旅游活动的崩溃;通货膨胀的上升影响购买行为。
- 数据漂移:上述输入数据的漂移也可能影响模型质量。然而,并不是每次出现数据漂移都一定是问题。当漂移发生在不太重要的特征上时,模型可能会做出稳健的响应,并且性能不受影响。让我们假设人口群组(年龄、性别和收入的特定组合)在推理过程中比在训练过程中更经常出现。如果模型仍能正确预测这一群体的结果,这不会引起头痛。如果漂移导致模型进入特征空间的更少填充和/或更易出错的区域,则问题更大。
模型漂移通常保持不被发现,直到新的地面真相标签可用。原始测试数据不再是可靠的基准,因为真实世界的函数已经发生了变化。
下图总结了各种漂移:
漂移的类型。摘自机器学习中的数据和概念漂移|走向数据科学
从正常行为到随波逐流的转变可能大不相同。现实世界中的人口统计变化通常会导致数据或模型的逐渐漂移。然而,传感器损坏可能会导致突然偏离正常范围。购买行为的季节性波动(如圣诞节)表现为周期性漂移。
如果我们的观察有时间戳(或者数据点至少是按时间顺序排列的),则可以采取以下措施来检测、分析和减轻漂移。
我们将在下面更详细地描述这些方法,并通过预测性维护案例研究对它们进行试验。
从静态到常青模式
从静态到常青模式
分析和减轻数据和模型漂移的选项取决于机器学习模型生命周期中当前数据的可用性。
让我们假设一家银行收集历史数据来训练一个模型,以支持信贷决策。目标是预测贷款申请应该被批准还是拒绝。标记的训练数据是在 2020 年 1 月至 12 月期间收集的。
该银行的数据科学家在 2021 年第一季度对该模型进行了培训和评估,并决定在 2021 年 4 月将其投入生产。让我们看一下团队可以用来收集生产数据的三个选项:
良好的漂移管理取决于数据的可用性
场景 1:静态模型
在这里,团队不收集任何生产数据。也许他们根本没有考虑到这一点,因为他们的项目范围仅仅包括交付初始模型。另一个原因可能是公开数据隐私问题(存储受监管的个人数据)。
显然,除了分析历史训练数据之外,对于检测数据或模型漂移没有太多可做的。只有当模型用户开始抱怨模型预测越来越不适合商业决策时,漂移才可能被发现。然而,由于没有系统地收集反馈,逐渐漂移可能会在很长一段时间内不被发现。
有趣的是,今天许多高效的机器学习模型都是以这种模式运行的。然而,像 MLOps 这样的机器学习生命周期管理程序在实践中正在获得更多的牵引力来解决这样的问题。
如果模型是根据代表性数据训练的,并且特征/目标关系是随时间稳定的(例如,以进化速度变化的生物现象),则静态模型方法可能是可接受的。
场景 2:收集生产数据
团队决定从生产阶段收集观察到的输入数据(特征)以及相应的模型预测。
如果没有数据保护问题或其他组织障碍,这种方法很容易实施。通过将最近的生产数据与原始训练观察值进行比较,可以发现特征和预测标签的漂移。关键特征的显著变化(就特征重要性而言)可用作进一步调查的触发因素。
然而,如果模型有问题,关键的信息丢失了:我们没有新的地面真实标签来评估产量预测。这可能会导致以下情况:
- 虚拟漂移(假阳性):我们观察到数据漂移,但是模型仍然按照预期工作。这可以让团队获取新的标记数据用于再训练,尽管这是不必要的(从模型漂移的角度来看)。
- 概念漂移(假阴性):虽然输入数据没有漂移,但真实世界的函数已经偏离了模型所学的内容。因此,日益过时的模型会导致不准确的业务决策。
场景三:常青树模式
在这种情况下,银行不仅分析生产输入和潜在漂移预测,还收集标记数据。根据业务环境,这可以通过以下方式之一实现:
- 业务单位贡献新标记的数据点(如初始培训所做的那样)
- 人在回路中的反馈:来自生产阶段的模型预测被系统地回顾。特别是由领域专家发现的错误批准和错误拒绝,并且收集具有正确标签的相应特征用于再训练。
纳入人在回路中的反馈需要调整流程和系统(例如,业务用户可以在他们的应用程序中覆盖或标记不正确的预测)。
主要优点是概念漂移可以以高可靠性被识别,并且模型可以通过再训练被定期刷新。
整合业务反馈和定期再培训是成熟的 MLOps 实践的重要组成部分(参见下面 Azure 机器学习的参考架构示例)。
实践中的数据和模型漂移管理
必须有一个检测机制来系统地测量漂移。理想地,这样的机制是集成的 MLOps 工作流的一部分,该工作流在连续的基础上比较训练和推理分布。我们已经编译了几个支持数据和模型漂移管理的机制。
我们在样本笔记本中使用基于合成数据集的预测性维护用例。目标是根据速度或热量偏差、操作员、装配线、自上次服务以来的天数等特征预测设备故障。
为了识别漂移,我们结合了统计技术和分布重叠(数据漂移)以及预测技术(模型/概念漂移)。对于这两种漂移类型,我们将简要介绍所用的方法。
漂移检测始于将按时间顺序排序的观察数据集划分为参考和当前窗口。参考(或基线)窗口表示较旧的观察,并且通常与初始训练数据相同。当前窗口通常反映生产阶段看到的更近的数据点。这不是严格的 1:1 映射,因为可能需要调整窗口以更好地定位何时发生漂移。
将时序数据集划分为参考和当前窗口
我们首先需要区分数字数据和分类/离散数据。对于统计检验,这两种类型的数据将经历不同的非参数检验,以提供 p 值。我们处理不同的样本大小,并且不对我们数据的实际分布做出假设。因此,非参数方法是检验两个样本相似性的一种简便方法,不需要知道实际的概率分布。
这些检验允许我们以一定的置信度接受或拒绝零假设,如 p 值所定义的。因此,您可以通过调整 p 值的阈值来控制测试的灵敏度。默认情况下,我们建议使用更保守的 p 值,如 0.01。样本越大,就越容易拾取噪声。其他常用的计算分布之间漂移的方法是连续分布的 Wasserstein 距离和概率分布的 JS 散度。
以下是限制漂移检测中误报警数量的一些最佳实践:
- 如果数据集中有许多变量,则将漂移分析的范围限定为关键要素的候选列表
- 如果数据集很大,请使用子样本而不是所有数据点
- 进一步降低 p 值阈值,或者为更大的数据量选择替代测试
虽然统计测试有助于识别漂移,但是很难解释漂移的大小以及漂移发生的方向。给定一个像年龄这样的变量,样本是变老了还是变年轻了,年龄是如何分布的?要回答这些问题,可视化分布是很有用的。为此,我们增加了另一种非参数方法:【KDE】。由于我们有两种不同的数据类型,我们将对分类数据执行预处理步骤,通过对变量进行编码将其转换为伪数字符号。相同的序号编码器对象用于参考和当前分布,以确保一致性:
现在我们已经对数据进行了编码,我们可以直观地检查整个数据集或感兴趣的选定变量。我们使用参考样本和当前样本的核密度估计函数来计算它们的百分比交集。以下步骤改编自该样本:
- 用带宽方法“scott”将数据传递给 KDE 函数,如scipy . stats . Gaussian _ kde()所示。该参数决定了分布的平滑程度。
- 取范围(两个分布的最小值和最大值),并通过使用两个函数的微分计算两个 KDE 函数在此范围内的交点。
- 对具有常数值的变量执行预处理,以避免错误并调整两种分布的比例。
- 根据 numpy.trapz() 用合成梯形法则近似求交点下的面积。
- 绘制参考和当前分布、交叉点以及交叉面积和重叠百分比。
- 将相应的统计检验(KS 或卡方检验)添加到标题中,并提供漂移指示(漂移与无漂移)
通过查看代码示例,可以更深入地理解这些步骤。KDE 交集的结果如下所示:
比较 KDE 交点以识别数据漂移
对这些地块的简要考察告诉我们:
- 哪些变量在参考样本和当前样本之间具有显著不同的分布?
- 漂移的幅度和方向是什么?
如果我们看一下“操作员”这个变量,我们可以看到,在安装模型时与今天相比,哪个员工在操作机器方面发生了很大的变化。例如,可能发生的情况是,一名操作员已经退休,因此不再操作任何机器(操作员 2)。相反,我们看到之前不存在的新操作者已经加入(例如,操作者 6)。
既然我们已经看到了如何发现特征和标签的漂移,那么让我们看看模型是否受到数据或概念漂移的影响。
与之前类似,我们将历史(训练)观察值和最近标记的数据点一起堆叠在一个时序数据集中。然后,我们根据最近的观察比较模型的性能,如下图所示。
预测模型漂移检测
核心问题是,我们想回答这样一个问题:一个新的模型在预测最新数据时,是否比一个根据旧的观察数据训练的模型表现得更好?
我们很可能事先不知道漂移是否以及在哪里发生了。
因此,在我们的第一次尝试中,我们可以使用原始训练数据作为参考,使用推断观察作为当前窗口。如果我们由此发现漂移的存在,我们很可能会尝试不同的参考和当前窗口,以精确定位漂移的确切位置。
预测模型漂移检测有两个选项:
每个选项都有特定的优势。作为第一种选择的结果,如果模型 2 优于模型 1,您已经有一个训练有素的候选人准备部署。第二种选择可能会减少误报,代价是对漂移不太敏感。在没有确定漂移的情况下,备选方案 3 减少了不必要的训练周期。
让我们看看如何使用选项 1 在我们的预测性维护用例中查找漂移。聚合数据集由 45,000 个带时间戳的观察值组成,我们将其分为 20,000 个引用、20,000 个当前观察值和 5,000 个最近观察值,用于测试。
我们定义了一个 scikit-learn 管道来预处理数字和分类特征,并训练两个 LightGBM 分类器进行比较:
对当前分类器重复最后一步后,我们比较性能指标,以确定模型之间是否存在明显差距:
目前的模式比参考模式好得多。因此,我们可以得出结论,我们确实已经确定了模型漂移,当前模型是替代生产模型的有希望的候选模型。一种直观的检查方式是比较两个分类器的置信度得分的分布:
模型漂移对预测类别概率的影响(左)与 KDE 交集(右)
左边的直方图显示了参考模型和当前模型的预测类别概率之间的明显差异,因此也证实了模型漂移的存在。
最后,我们再次使用数据漂移部分的 KDE 图和统计测试来测量漂移的程度。两个分类器的 KDE 图之间的交点仅占 85%。此外,KS 检验的结果表明分布并不相同。
在本例中,结果在意料之中,因为我们有意将漂移纳入我们的综合预测维护数据集中。对于真实世界的数据集,结果不会总是那么明显。此外,可能有必要尝试不同的参考和当前窗口分割,以可靠地找到模型漂移。
常青树模型的 MLOps 参考架构
我们现在将重点关注在具有 Azure 机器学习的 MLOps 架构中嵌入漂移检测和缓解。以下部分利用了 Azure ML 数据集、模型和管道等概念。演示库提供了一个用于生成数据漂移检测图的 Azure ML 管道的例子。为了自动化模型的重新训练,我们建议使用最新的 Azure MLOPs 代码示例和文档。下图提供了一个示例体系结构,包括我们到目前为止了解到的关于数据和模型漂移的所有内容。
常青树模型的 MLOps 架构
通过将漂移缓解视为自动化 Azure MLOps 工作流的一部分,我们可以用可管理的努力维护 evergreen ML 服务。为此,我们执行以下步骤:
- 在 Azure 机器学习 中摄取和版本化数据这一步对于维护训练数据、机器学习实验和结果模型之间的血统至关重要。对于自动化,我们使用 Azure 机器学习管道,它使用托管数据集。通过指定版本参数(version="latest "),可以确保获得最新的数据。
- 训练模型
在这一步中,对源数据进行模型训练。这个活动也可以是自动化 Azure 机器学习管道的一部分。我们建议添加一些参数,如数据集名称和版本,以便在多个数据集版本中重用同一个管道对象。通过这样做,在出现模型漂移的情况下,可以触发相同的流水线。一旦训练完成,模型就会在 Azure 机器学习模型注册中心注册。 - 评估模型
模型评估是培训/再培训流程的一部分。除了查看性能指标以了解模型有多好之外,彻底的评估还包括审查解释、检查偏见和公平性问题、查看模型在哪里出错等。它通常包括人工验证。 - 部署模型
这是您部署模型的特定版本的地方。在 evergreen 模型的情况下,我们将部署适合最新数据集的模型。 - 监控模型
收集关于部署模型的遥测信息。例如, Azure AppInsights 工作簿可用于收集对模型实例的请求数量以及服务可用性和其他用户定义的指标。 - 收集推理数据和标签
作为服务持续改进的一部分,模型做出的所有推理都应与基础事实(如果可用)一起保存到存储库中(例如 Azure Data Lake)。这是至关重要的一步,因为它允许我们计算出推论和参考数据之间的偏差量。如果地面实况标签不可用,我们可以监测数据漂移,但不能监测模型漂移。 - 测量数据漂移
基于上一步,我们可以通过使用参考数据并使用以上章节中介绍的方法将其与当前数据进行对比来开始数据漂移检测。 - 测量模型漂移
在这一步,我们确定模型是否受到数据或概念漂移的影响。这是使用上面介绍的方法之一完成的。 - 触发重新训练
在模型或概念漂移的情况下,我们可以利用我们用于初始训练的相同 Azure ML 管道来触发完整的重新训练和部署管道。这是静态模型和常青树模型之间闭环的最后一步。重新训练触发器可以是:
【自动】T5——比较参考模型和当前模型的性能,如果当前模型性能优于参考模型,则自动部署。
人在回路 —检查数据漂移可视化以及参考模型和当前模型之间的性能指标,并在回路中部署数据科学家/模型所有者。这种情况适合高度管制的行业。这可以使用 PowerApps、Azure DevOps 管道或 GitHub 操作来完成。
后续步骤
在本文中,我们研究了一些实用的概念,以发现和减轻表格用例的数据和机器学习模型的漂移。我们已经使用我们的演示笔记本和预测性维护示例看到了这些概念的实际应用。我们鼓励您将这些方法应用到您自己的用例中,并感谢任何反馈。
能够系统地识别和管理生产模型的漂移是迈向成熟的 MLOps 实践的一大步。因此,我们建议将这些概念集成到端到端的生产解决方案中,如上面介绍的 MLOps 参考架构。我们的笔记本的最后一部分包括如何使用自动化 Azure 机器学习管道实现漂移检测的例子。
数据和模型漂移管理只是整体 MLOps 愿景和架构的一个组成部分。请随意查看文档,了解关于 MLOps 以及如何使用 Azure Machine Learning 实现它的一般信息。
在我们的例子中,我们已经查看了表格形式的机器学习用例。针对图像和自然语言等非结构化数据的漂移缓解方法也正在出现。医学成像 AI 存储库中的漂移检测提供了一种结合元数据分析医学图像以检测模型漂移的有前途的方法。
除非另有说明,所有图片均由作者所有。
利用数据发挥创造力
原文:https://towardsdatascience.com/getting-creative-with-data-bd74a98d2a61
放大任何一个数据科学项目,你会看到看起来呆板、执行仔细的元素:查询、代码片段、公式和图表。这些煞费苦心的细节往往掩盖了为解决复杂挑战而整合各种要素的创造性能量。
本周,我们将庆祝我们最近分享的一些最具创意的项目和方法。我们认为你也会喜欢的。
- 地理空间科学的未来已经到来了吗? 在与 Bryan R. Vallejo 的生动对话中,我们了解到数据科学和云 GIS 的交叉领域正在形成令人兴奋的发展,以及实时数据和可扩展计算解决方案可能如何改变生态保护领域。
- 用复杂数据创建有意义的可视化 。停留在地理空间表示的一般领域, Lan Chu 向我们展示了如何使用 Geopandas、Plotly Express 和 Folium 通过地图讲述引人入胜的故事。她的教程生动展现了非洲的幸福指数和冲突数据。
- 时间序列能否应用于一个国家的精神状态?Hannah Roos 的最新工作尤其具有挑战性:她旨在将公共数据与时间序列分析结合起来,以检测新冠肺炎对德国民众的心理影响。其结果是一个迷人的项目演练,以及对数据质量检查重要性的深思熟虑的思考。
Cathy Mü 在 Unsplash 上的照片
- AI 的下一个前沿:编剧?! 在之前的一篇文章中展示了我们如何使用 GPT-J——大规模的开源语言模型——来生成俳句之后,罗伯特·a·贡萨尔维斯将目光投向了一个更加雄心勃勃的目标。利用几个公共数据集,他训练该模型生成电影创意、描述和对话。输出可能不是奥斯卡级别的,只是还没有,但仍然非常有趣。
- 把数据和声音放在一起,我们能学到什么? 我们都知道数据可视化;Pavle Marinkovic 在 TDS 上的第一篇文章中,他转向了新兴的数据发音领域。这是一种新颖的方法,使从业者能够通过新的感官输入来检测模式并获得洞察力:“声音给原本休眠的物体、地方和时刻带来了生命。当你对数据进行语音处理时,也会发生同样的情况。
- 你不仅仅可以用 gif 来搞笑 。凯蒂·哈格蒂的新教程简单快捷,展示了如何用 Python 制作 gif(包括完整的实现)。一路上,Katy 还展示了在传达关键见解时,动画可视化有时会比静态图表更有效。
- AI 最终会让跨物种交流成为可能吗?不,人类和黑猩猩不会热烈地谈论政治和天气。但是正如克里斯蒂安·蒙森指出的,人工智能检测和复制模式的能力可能会帮助我们更加接近那个时刻:“理解人类语言和动物交流系统之间的微妙差异和惊人的相似之处可以帮助我们专注于人工语言系统到底需要完成什么。”
- 如何让自己团队的价值可见可衡量 。暂且抛开动物不谈,我们甚至可以成功地与互相交流?正如 Louise de Leyritz 指出的那样,数据团队通常很难准确定位他们对更广泛的组织的价值。然而,通过一些创造性的思考,他们可以建立一个清晰的指标框架,让其他利益相关者直观地理解。
感谢您对我们作者工作的支持——这使得我们能够就如此广泛和多样化的主题分享伟大的作品。如果你喜欢本周的选择,考虑成为中级会员——它将让你无限制地访问我们的全部档案。
直到下一个变量,
TDS 编辑
动手使用 DBT —数据构建工具
原文:https://towardsdatascience.com/getting-hands-on-with-dbt-data-build-tool-a157d4151bbc
与 DBT 一起运行您的第一个项目的分步指南
Volodymyr Hryshchenko 在 Unsplash 上的照片
DBT 现在风靡一时!当我第一次读到关于它的时候,我想好吧,这是一个配置驱动的 ETL ,还有什么新的吗?
后来我读了更多,我就像嗯..多适配器支持这很好,我们不必再编写通用库并与他人共享。
阅读越来越多,我喜欢哇!这是一个与云捆绑在一起的完整平台,测试基本上使我们的生活更加轻松。
好了,赞够了!让我们用dbt
来运行我们的第一个项目
安装指南
步骤 1:设置 Postgres
DBT 支持多适配器如红移、雪花、BigQuery、Spark、Postgres 等。因为这是我们的项目,让我们从简单的postgres
开始,但是如果你有任何其他列出的适配器的连接器,请随意对特定的适配器进行适当的修改。
回到postgres
,如果你已经有了postgres
设置,你可以跳过这一步,否则你可以按照这个指南来设置。设置完成后,验证它是否已设置。
~ % postgres -V
postgres (PostgreSQL) 13.3
步骤 2:创建一个新的虚拟 env 并激活它
DBT 运行在一个 python 生态系统上,如果你没有本地的python
设置,先这样做(python 设置指南)!(拜托,没有 Python,真的?)
为dbt
创建一个新的虚拟环境
~ % virtualenv dbt~ % source dbt/bin/activate
(dbt) ~ %
步骤 3:设置 Postgres dbt 适配器
这将为使用postgres
作为适配器安装必要的包+为dbt
安装核心包。
pip install dbt-postgres
这将只安装dbt-core
和dbt-postgres
和:
*$ dbt --version
installed version: 1.0.0
latest version: 1.0.0
Up to date!
Plugins:
- postgres: 1.0.0*
出于某种原因,如果你没有使用postgres
作为适配器,例如为dbt-<youradaptor>
安装软件包,你想为 spark 安装dbt
适配器,你可以
*pip install dbt-spark*
运行您的第一个项目
步骤 1:初始化您的第一个 dbt 项目
好了,所有东西都装好了。选择你最喜欢的文件夹并运行dbt init
,它会要求你输入项目名称,添加相同的项目并选择你的适配器。既然我们用的是postgres
,我们可以选择【1】
*(dbt) % dbt init
16:43:39 Running with dbt=1.1.1
Enter a name for your project (letters, digits, underscore): dbt_for_medium
Which database would you like to use?
[1] postgres
[2] sparkEnter a number: 1
16:43:46
Your new dbt project "dbt_for_medium" was created!Happy modeling!*
步骤 2:为 dbt 配置概要文件
很好,看看这个!DBT 维护环境的细节,如this
项目,this
环境与此相关。
这些配置文件需要在/Users/jyotidhiman/.dbt/profiles.yml
更新,请在此配置您的适配器详情:
*dbt_for_medium:
outputs:dev:
type: postgres
threads: 1
host: localhost
port: 5432
user: dbt
pass: dbtpass
dbname: postgres
schema: postgres
prod:
type: postgres
threads: 1
host: localhost
port: 5432
user: dbt
pass: dbtpass
dbname: postgres
schema: postgrestarget: dev*
步骤 3:验证配置文件设置是否正确
配置文件配置完成后,转到您的dbt
文件夹并运行以下命令来验证设置是否已正确完成。如果出现任何适配器问题,此处会突出显示一个错误。
*(dbt) dbt_for_medium % dbt debug
16:50:06 Running with dbt=1.1.1All checks passed!*
第四步:运行你的 DBT 项目!!
DBT init 附带了一些虚拟模型和 SQL,可用于验证和运行设置,可以使用以下命令调用它:
*(dbt) dbt_for_medium % dbt run
16:51:02 Completed successfully
16:51:02
16:51:02 Done. PASS=2 WARN=0 ERROR=0 SKIP=0 TOTAL=2*
步骤 5:验证 Postgres 中是否创建了数据
由于我们在仓库中使用了postgres
,虚拟模型(我们的转换)创建的数据可以在postgres
中得到验证。要了解它是如何创建的,请查看项目中的my_first_dbt_model.sql
。
*postgres=# select * from my_first_dbt_model;
id
— —
1
(1 row)*
步骤 5:运行测试
DBT 提供了开箱即用的测试集成,并附带了一些可以使用命令dbt test
调用的虚拟测试
*(dbt) dbt_for_medium % dbt test
16:52:00 Running with dbt=1.1.1
16:52:00 Found 2 models, 4 tests, 0 snapshots, 0 analyses, 167 macros, 0 operations, 0 seed files, 0 sources, 0 exposures, 0 metrics
16:52:00
16:52:00 Concurrency: 1 threads (target='dev')
16:52:00 Finished running 4 tests in 0.16s.
16:52:00
16:52:00 Completed with 1 error and 0 warnings:
16:52:00
16:52:00 Failure in test not_null_my_first_dbt_model_id (models/example/schema.yml)
16:52:00 Got 1 result, configured to fail if != 0
16:52:00
16:52:00 compiled SQL at target/compiled/dbt_for_medium/models/example/schema.yml/not_null_my_first_dbt_model_id.sql
16:52:00*
现在你看到一个测试失败了,这是 DBT 故意弄脏你的手。要修复测试,取消my_first_dbt_model.sql
中该行的注释
作者图片
再次运行dbt run
和dbt test
作者图片
嗯,就这样吧!!全部成功完成:)
您的基本 DBT 设置已经准备就绪,可以工作了!耶!!
现在您可以开始在它的基础上构建您的 DBT 项目了。关于 DBT 的更多细节,请查看这篇令人惊叹的文章。
如果你在设置上遇到任何问题或者有任何疑问,请告诉我。
希望这有所帮助!
JD
SML 入门:一种起源于 1983 年的怪异多态语言
尝试用 SML 语言编写和编译一些基本代码
前言
在我作为一名计算机程序员的经历中,我爱上了计算机编程中的许多概念,这些概念现在已经成为通用编程概念。在我的生命中,有一段时间我爱上了面向对象编程,以及类的子类化,比如 C++中的子类化。在河的另一边,我也喜欢许多函数式编程语言,有些语言有独特的类型系统,比如标准的 ML,或者我今天第一次尝试的 SML 编程语言。需要说明的是,我一生中从未写过这种语言,我也不打算继续写下去——尽管,如果它激发了我的兴趣,你永远不会知道像我这样的大脑会发生什么。
无论如何,我有过许多不同编程概念的美好经历,但仍然有一个是我最喜欢的:
多态性。
多态只是一个书呆子气的词,意思是我们正在基于类型改变事物。在大多数例子中,这是函数调用。更简单地说,我们的方法附加在我们的类型上。我遇到的第一种利用这种编程概念的语言是 Julia 编程语言。如果你今天看一下我的博客,你可能会发现上面有很多关于朱莉娅的内容,而且很多时候我都在写朱莉娅。虽然 Julia 不仅仅有多态和多重分派,但这是 Julia 的一个重要部分,而且该语言的范例围绕着多重分派的使用。如果您想了解更多,我将在下面的文章中详细介绍 Julia 中多重分派的伟大之处:
也就是说,我爱上了朱莉娅,因为她身上所有的优点。在许多方面,朱莉娅在精神上有点像曼梯·里的继承者。ML 最初是通过多分派概念实现参数多态性的,在认识到它对计算世界的贡献后,我想拜访一下这种我最喜欢的语言的精神继承者。这正是我今天要做的,同时将结果与 Julia 进行比较,也许是我所经历过的其他种类的语言。最后一件事,我为向自己介绍这种令人敬畏的语言而写的所有代码都可以在我在 Github 上的 Random_Code 库中找到,这里有一个链接:
https://github.com/emmettgb/Random_Code/blob/main/tryingsml/src/trying.sml
第一部分:建立 SML
[emmac@fedora ~]$ sudo dnf install sml
[sudo] password for emmac:
Fedora 34 - x86_64 - Updates 29 kB/s | 7.9 kB 00:00
Fedora 34 - x86_64 - Updates 444 kB/s | 444 kB 00:00
Fedora Modular 34 - x86_64 - Updates 58 kB/s | 12 kB 00:00
No match for argument: sml
Error: Unable to find a match: sml
好吧,看起来不会那么容易。
[emmac@fedora ~]$ sudo dnf search sml
Last metadata expiration check: 0:01:20 ago on Tue 18 Jan 2022 06:48:04 AM EST.
================================================ Name & Summary Matched: sml =================================================
perl-Net-LDAP-DSML.noarch : DSML Writer for Net::LDAP
原来这个包实际上叫做 smlnj,但是它只在一些不知名的个人包存档中可用,而且鉴于我使用 Fedora——这对我来说是不可行的。相反,我找到了这些指令,它们详细描述了如何使用 Bash 脚本运行某种安装程序
手指交叉。
好了,原来 Bash 脚本是对一堆 make 文件的调用,所以显然在 Fedora 上安装 SML 的唯一方法是从源代码构建并安装它。我能够做到这一切,现在我面临的唯一问题是,现在它已经安装了,我的路径中没有它,也不知道这个安装中的任何文件到哪里去了。真的,没有任何关于这方面的文档,除了我刚刚提到的那些细节,http://smlnj.org网站在资源方面完全没有用。
好了,过了一会儿,我回到了我的~/下载目录,在我的下载文件夹里看到了像“bin”这样的目录和类似的东西,我感到非常困惑。我通常不会注意到这一点,但是我按照 last modified 进行了排序,并且正在寻找上面我最初使用 wget 下载的 tarball。请记住,由于这些文件是在之后修改的,它们一定是在之后被放在那里的。足够充分的是,这并不构成一个“安装”,至少不是在 Unix 类系统上,这是我们在 Unix 中使用语言的更典型的方式。考虑到这一点,我将这些目录移到了~/。sml,然后我像这样导出我的 SML bin 的路径:
nano ~/.bashrcexport PATH="$PATH:~/.sml/bin"
获取新信息。bashrc 文件,
source ~/.bashrc
现在我们可以从我们的终端调用 sml:
[emmac@fedora config]$ sml
Standard ML of New Jersey (64-bit) v110.98.1 [built: Tue Jan 18 06:53:10 2022]
-
耶!
此外,对于那些对该软件的许可感到好奇的人:
================================================================================
The new code is covered under the following license:
====================
STANDARD ML OF NEW JERSEY COPYRIGHT NOTICE, LICENSE AND DISCLAIMER.Copyright (c) 2016 by The Fellowship of SML/NJPermission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without restriction,
including without limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of the Software,
and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
第二部分:尝试 SML
虽然 SML 语言的安装可能不完全正常,但代码看起来相对容易理解。在实际编写和尝试设计一个包之前,我想在 REPL 中尝试一些基本操作,
[emmac@fedora src]$ sml
Standard ML of New Jersey (64-bit) v110.98.1 [built: Tue Jan 18 06:53:10 2022]
-
不确定这是否只在类 Unix 系统上,因为我相信这种语言很可能是为 Windows 系统设计的,所以也许这就是这个 REPL 如此糟糕的原因…相反,REPL 并不可怕,除非你想说,以任何方式移动光标。
Standard ML of New Jersey (64-bit) v110.98.1 [built: Tue Jan 18 06:53:10 2022]
- hello^[[D^[[D^[[D^[[D
同样是的,我确实在早上 6:53 构建了这个编译器。
也许这是有益的怀疑,但我认为这很可能是 REPL 的设计与 Windows 铭记,和一些关于移动光标的系统调用方式是不同的,并没有组装适合我的系统。当然,这只是推测——我也使用过其他有这个问题的 REPLs 最著名的是我的编程语言的 REPL。
罪名成立。
(图片由作者提供)
让我们试着添加一些数字!
- val x = 5 + 5;
val x = 10 : int
好了,现在我有了一个值,只需要弄清楚如何打印它,以及函数调用是如何构造的…耐心等待…
- print(x);
stdIn:2.1-2.9 Error: operator and operand do not agree [tycon mismatch]
operator domain: string
operand: int
in expression:
print x
我的天啊。
我相信这里发生的事情是我向操作员提供了错误的类型。我相信“域”代表了这个方法应该应用于哪种类型。记住这一点,因为现在我只是想做 hello world,让我们切换到一个字符串——然后我们可以弄清楚我们到底如何打印一个整数。
- y = "hello"
= ;
Yes, I forgot the semi-colon- print y;
helloval it = () : unit
这里一个有趣的方面是我们使用的语言类型。ML 是一种静态类型语言。我也必须承认,我从来没有像这样使用过 REPL 的静态类型语言,我不这么认为。整个事情感觉非常奇怪,但同时真的很酷,我很高兴能了解更多。顺便说一句,对于那些像我一样有兴趣实际学习这种语言,或窥视其范式思想以获得一些赞赏的人,我会推荐维基百科页面,因为它确实是唯一好的信息来源,因为 sml 网站似乎是一堆 403、404 和 HTML,没有任何 CSS。在看了这些例子之后,我想向你们展示这种语言实际上有多酷。
我真正喜欢这门语言的是,它有一种处理函数和类型的方式,我们可以在 Julia 的更高层次上看到这种方式。编写这种语言实际上围绕着两个领域。人们可以主要在函数式编程范式中工作,使用声明性方法调用,或者可以使用命令式等效方法工作,后者更多地基于使用结构和事物,而不是纯粹的高级操作符调用。下面是维基百科页面上列出的两者的例子:
**fun** factorial 0 = 1
| factorial n = n * factorial (n - 1)
以上是该函数的声明版本。我必须承认,我不确定 0 在这个上下文中是什么意思,这有点粗略。另一个重要的注意事项,正如我所检查的,在 SML 语中,|绝对是一个基本操作符,因为它被用来表示这些额外的调用——但是我想在我看这个的同时,我发现了什么
factorial 0 = 1
是。
阶乘 0 = 1 仅仅意味着如果提供的值是 0,那么我们得到 1——我想..也许我们应该尝试使用运算符来查看:
- val x = factorial 0
= ;
val x = 1 : int
现在让我写这个函数的另一个版本,如果它是-1,就返回 3,只是为了确保这绝对是这个意思:
- fun factorial 0 = 1
= | factorial -1 = 3
= | factorial n = n * factorial (n - 1)
= ;
stdIn:23.5-25.38 Error: clauses do not all have same function namestdIn:23.5-25.38 Error: parameter or result constraints of clauses do not agree [overload - bad instantiation]
this clause: 'Z * 'Y[INT] -> 'X
previous clauses: 'W[INT] -> 'X
in declaration:
factorial =
(fn 0 => 1 | (factorial,1) => 3 | n => n * factorial (n - 1))
我将来可能会理解,但现在不会,因为负值-1,我们不再使用与其他类型相同的类型…记住,我将尝试用 1 来代替…
fun factorial 0 = 1
| factorial 1 = 3
| factorial n = n * factorial (n - 1);val factorial = fn : int -> int - factorial 1;
val it = 3 : int
这与更迫切的召唤相反,鉴于我迄今为止的新知识,我认为现在对我来说更容易理解。
**fun** factorial n = **let** **val** i = ref n **and** acc = ref 1 **in**
**while** !i > 0 **do** (acc := !acc * !i; i := !i - 1); !acc
**end**
这里有一个很酷的小函数,这绝对是一个更好的实现,因为它不使用递归。我们在结尾语法上也达到了一个高峰,这确实让这门语言更接近 Julia。看一下这两种语言之间的比较:
**function** factorial(n)
**let** **val** i = ref n && acc = ref 1 **in**
**while** !i > 0
(acc := !acc * !i; i := !i - 1) !acc **end**!acc
**end**
非常相似,Julia 甚至看起来更像 ML,带有更多的声明性内联函数声明:
factorial(n) = let...
在我开始用 ML 做一个小项目之前,我还想了解三件事情:
- 类型
- 索引
- 环
这些都将是我决定创建的包的基本要素,这是一个有趣的包,但同时在一种新的语言中执行将是具有挑战性的。此外,在寻找关于这个概念的信息时,我偶然发现了一个很酷的可以用 ML 完成的小东西,我想分享一下。这是一个 In 保持不变的尾递归紧循环的封装,在一个不变的外部函数中有一个或多个累加器参数。这是什么意思?我们把这个被递归调用的函数变成了一个常规的循环。不变量只是“在程序状态下返回真值的东西”的一个花哨的说法这就是为什么我们需要在定义这个函数之前限制它的范围。
**local**
**fun** loop (0, acc) = acc
| loop (m, acc) = loop (m - 1, m * acc)
**in**
**fun** factorial n = loop (n, 1)
**end**
显然,虽然尾部递归不是我想在这里学习的,但是我们在这里有一个关于如何使用累加器循环的突出例子。这里最重要的事情是,我们实际上需要从传统的循环语法中构造迭代循环。换句话说,在这种语言中,我们需要编写自己的 break。我最初想为我的第一个 SML 项目做一些疯狂的事情,但是考虑到现在我将不得不编写比最初预期的更多的函数,我可能会做一些更简单的事情。
第 3 部分:构建我的第一个 ML 包
对于我的第一个 SML 套餐,我决定做一些简单的事情。最大的原因是,使用列表的每个依赖项都需要被编程。我不希望整天坐在这里编写基本列表操作的循环,所以我决定只列出几个要加载的文件,然后我们将在 REPL 中使用它们。第一个是总结,我们肯定需要。我要用求和来计算一个数组的平均值。这听起来可能很简单,但实际上并不简单,因为我同时通过尝试、错误和研究来学习这种语言的语法——老实说,对我来说,语法的某些部分可能需要花很多时间来适应。也就是说,我用过的最接近的语言是 Lisp。然而,这种断言主要是在类型中,这种语言也有多态性扳手扔在,所以它真的仍然是不一样的。考虑到这一点,在花了大约 20 分钟的时间试图写出一些有用的东西之后,
fun sum (x::y::xs) = x :: sum (x+y::xs)
| sum xs = xs
奇怪的旁注:用这种语言和 REPL 人一起工作会非常令人恼火。在不能移动光标和这些奇怪的事情之间,无效语法只是等待更多的分号,这是一个相当大的斗争。
也没有我需要的标准长度函数。
fun len(xs) =
case xs of
[] => 0
| (_::xs') => 1 + len(xs')
在这里,我还学习了 case 语法,这是一种非常命令式的语法,我很高兴在这种语言中看到它。length 函数没有问题,但是 sum 函数最终给我带来了一些问题。主要问题是,每当我对列表求和时,它都是分布在每个值上的累积和。换句话说,累加器将每个循环值加在一起,但只有最后一个索引才是我们想要的实际整数。我尝试编写一个新的函数来返回一个整数——但是使用空列表构造函数,无论何时我们返回一个整数,这个函数都会变得非常混乱。我必须承认,我不知道这到底是为什么,但不管怎样,我写了一个快速的最大值函数,试图减轻这一点,所以我可以解决它,并得到我的平均值。
fun maximum(xs)=
case xs of
[] => NONE
| (head::[]) => SOME head
| (head::neck::rest) => if head > neck
then maximum (head::rest)
else maximum (neck::rest)
现在我有了这三个函数,我将继续定义两个新值:
(*An interesting way to leave comments*)
val x = [4, 7, 12, 18, 22];
另外,看看 SML 的评论。
现在让我们试着运行这个应用程序。
[emmac@fedora src]$ sml trying.sml
Standard ML of New Jersey (64-bit) v110.98.1 [built: Tue Jan 18 06:53:10 2022]
[opening trying.sml]
val len = fn : 'a list -> int
val maximum = fn : int list -> int option
val sum = fn : int list -> int list
val x = [4,7,12,18,22] : int list-
嘿,成功了!我们需要做的最后一件事是将所有这些函数的使用结合到 x 上。我将在 REPL 中完成剩余的工作。我已经找到了关于这种语言的更多资源,我想今后我可能会在某个时候再尝试这种语言,也许会在其中开发一些东西,因为迭代的水平在这里实际上是非常棒的。我们实际上可以使用堆栈,从堆栈中弹出和推送数据——尽管我们也不一定要这样做。我认为能够选择你的方法真的很棒;每个特定场景的声明性或命令性。然而,我确实觉得语法有点限制,函数以一种奇怪的方式工作,并以一种奇怪的方式成为参数属性。实际上,这归结为多重分派的实现与 Julia 的完全不同。例如,abs 函数是实类型下的一个调用:
Real.abs(-1)
= ;
[autoloading]unexpected exception (bug?) in SML/NJ: Io [Io: openIn failed on "/home/emmac/Downloads/sml.boot.amd64-unix/smlnj/basis/.cm/amd64-unix/basis.cm", No such file or directory]
raised at: Basis/Implementation/IO/bin-io-fn.sml:626.25-626.71
../cm/util/safeio.sml:30.11
../compiler/TopLevel/interact/evalloop.sml:45.54
好吧,也许我在这里的另一个问题是,我没有任何 SML 基地..?但是我不确定…不管怎样,我真的不在乎,我可以写我自己的绝对函数…
**fun** abs(r: real):real =
**if** r < 0.0 **then** ~r **else** r
让我们最终用我的小包裹开始这个 REPL,并尝试得到平均…
[emmac@fedora src]$ sml trying.sml
Standard ML of New Jersey (64-bit) v110.98.1 [built: Tue Jan 18 06:53:10 2022]
[opening trying.sml]
val len = fn : 'a list -> int
val maximum = fn : int list -> int option
val sum = fn : int list -> int list
val x = [4,7,12,18,22] : int list
我要做的第一件事是得到数组的和,
- val cumsum = sum x;
val cumsum = [4,11,23,41,63] : int list
现在要得到实际的总和,我要得到新的累计列表的最大值。
- val summation = maximum cumsum;
val summation = SOME 63 : int option
最后,我会得到 x 的长度:
- val n = len x;
val n = 5 : int
还有…分…
- val mu = summation / n
= ;
stdIn:4.5-4.23 Error: operator and operand do not agree [tycon mismatch]
operator domain: real * real
operand: int option * int
in expression:
summation / n
美味的肉汁。
我相信 real 可能只是一个整数的抽象,所以我认为我们在这里面临的问题是在我们的最大函数中,因为它返回“int option”,不管那是什么意思。我找到了关于此选项类型的以下信息,以便找出它被退回的原因:
在里面,我发现了这个函数:
**val** [getOpt](https://smlfamily.github.io/Basis/option.html#SIG:OPTION.getOpt:VAL) **:** *'a* option ***** *'a* **->** *'a*
我想那可能会有用。
- val realsum = getOpt summation;
stdIn:1.6-7.19 Error: operator and operand do not agree [tycon mismatch]
operator domain: 'Z option * 'Z
operand: int option
in expression:
getOpt summation
号码
你知道,我甚至没有真正读过那个函数的描述符…读完之后,我意识到我们真正需要的是 valOf 函数:
- val realsum = valOf summation;
val realsum = 63 : int
耶!
- val mu = realsum / n;
stdIn:8.5-8.21 Error: operator and operand do not agree [tycon mismatch]
operator domain: real * real
operand: int * int
in expression:
realsum / n
什么?!
好吧,那就更奇怪了。我期望能够分割整数(多么罪恶!)但很明显,这不是 SML 的做事方式…
事实证明,所有这些都很简单,但也很复杂。运算符/不是 SML 的除法运算符。除法运算符是 div。还有,我还是不知道/本身是干什么用的。这里有一个我找到的数学运算符表的链接,它不在上面。
虽然/肯定做了一些事情,有两个实际值,我现在的任务只是这个意思——当我不写文章时,有一个完整的兔子洞可以钻进去。考虑到这一点,我们只需要稍微修改一下代码。
- val mu = realsum div n;
val mu = 12 : int
结束语
所以这些第一步可能有些基础,但与我使用过的其他语言相比,这种语言肯定有很大的不同。这是 20 世纪 60 年代和 80 年代之间开发的语言的伟大之处——我们通常在计算领域期望的许多标准完全空缺。我个人持保留意见,认为这是一件很棒的事情,因为我们可以学到更多关于语言、语言结构和范式的知识。
在这个特定的应用程序中,我在一些函数式编程语言(比如 Lisp)方面的经验很有帮助。尽管这种语言和 Lisp 一点也不像。不管怎样,不管出于什么原因,它真的让我回到了那种语言。这种语言真的是独一无二的,我认为任何人开始接触这种语言都会有一些困难。如果你是一名 SML 程序员,我很抱歉还不知道如何写这种语言。也就是说,我绝对认为这种对概念和编程语言的探索是一个好主意。此外,C++编程语言的创始人 Bjourne Stroustup 也是如此,他引用了下面这段话:
自然,每个专业程序员都应该知道几种语言,并且应该意识到不同语言之间的编程和设计技术有很大的不同。stroustrup.com,比约恩·斯特劳斯特鲁普
走出你的舒适区,体验代码中不同的语法和范例,这当然是一个好主意——因为我认为这可以让你成为一名更好的程序员,无论是在一般情况下,还是回家后使用你通常使用的语言。非常感谢你阅读我的 SML 之旅。虽然有一些小问题,和一些奇怪的/学习的经历,但是看着这种建立了现代形式的多重调度的语言是很有趣的。我会说,我肯定认为朱莉娅做得更好,当然,但这总是主观的——当然,朱莉娅只是在某些方面做得更好,而不是其他方面。所有这些设计都有优点和缺点,我认为是应用程序真正让两种语言之间的选择有效。话虽如此,我真的不知道 SML 技能可能有多大用处,或者它们可能有多受欢迎,但我的猜测是,它可能不是最好的学习语言之一,除非你和我出于同样的目的学习它;教育。我确信这种语言是古老的、经过测试的,并且肯定可以在许多环境中使用,我只是不太确定是否有更好的选择。祝你有美好的一天,保持好奇,快乐编程!
使用 GroupBERT 从预先训练的语言模型中获得更多回报
在 Graphcore IPU 上实现 2 倍的训练速度
图片作者 Graphcore
ERT 已经成为当今最流行、最通用的人工智能模型之一。然而,BERT 对密集运算的依赖意味着它的准确性和灵活性需要很高的计算成本。
为了应对这一挑战,Graphcore Research 开发了 GroupBERT,这是一种基于 BERT 的新模型,它使用分组转换,非常适合我们的情报处理单元(IPU)。GroupBERT 将增强的转换器结构与高效的分组卷积和矩阵乘法相结合,使 IPU 用户能够有效地将模型中的参数数量减半,并将训练时间减少 50%,同时保持相同的精度水平。
由 IPU 解锁的增强型 BERT
图 GroupBERT 模型结构示意图。图片作者。
我们的论文“ GroupBERT:具有高效分组结构的增强型 Transformer Architecture”[1]展示了 IPU 如何允许 Graphcore Research 探索非常高效和轻量级的构建块,以推导出在非常大的文本语料库上更有效地进行掩蔽预训练的 Transformer 编码器结构。
GroupBERT 利用分组转换。我们用分组矩阵乘法来扩充全连接模块,并在变换器的结构中引入一个新的卷积模块。因此,每个 GroupBERT 层都扩展到四个模块,而不是像最初的 BERT 中那样扩展到两个模块。
使用 GroupBERT,我们可以看到通过验证损失衡量的 FLOPs 和任务性能之间的权衡得到了显著改善。为了达到相同的损失值,GroupBERT 只需要不到常规 BERT 模型一半的 FLOPs,后者只使用密集运算,没有利用其深度潜力。
增加的深度和减少的运算强度【2】的组伯特的组成部分强调了内存访问。与密集操作相比,对于给定的输入激活张量,分组操作执行较少的 FLOPs。为了利用计算,这些低算术强度运算需要比密集计算更快的数据访问。由于 IPU 允许所有权重和激活存储在具有极高 47.5 TB/s 带宽的片内 SRAM 中,因此它可以将 GroupBERT 的理论效率优势转化为各种型号的实际训练时间减少。
分组的完全连接的层
原变压器编码器层由两个模块组成:多头关注(MHA)和全连接网络(FFN)。许多工作都致力于提高其效率。 Tay 等人(2020)【3】概述了各种不同的方法,其中大多数修改集中在减少 MHA 模块对序列长度的二次计算依赖性。然而,在 BERT 中,大部分计算是以相对适中的序列长度 128 来执行的,并且 FFN 模块消耗了迄今为止最多的资源,在模型执行期间贡献了近三分之二的 FLOPs。
FFN 模块的结构非常简单:它由两个矩阵和一个非线性组成。第一个矩阵将表示投影到更高维度,通常比模型隐藏表示大四倍。这个维度扩展操作之后是非饱和激活函数,其执行表示的非线性变换并将其稀疏化。最后,通过下投影矩阵将稀疏表示缩减到模型维数。
GroupBERT 介绍了一种新颖的方案,使 FFN 计算更便宜、更快。一个关键的观察结果是,由分组引起的稀疏性最好应用于接收稀疏输入的矩阵。因此,我们只在第二个下投影矩阵中引入分组矩阵乘法,使其分块对角化。然而,分组操作在传入的隐藏表示上引入了局部性约束,限制了转换期间的通道信息传播。为了解决这个问题,我们将组与输出投影混合在一起,类似于 MHA 块,它也为多个头部划分隐藏表示。
总的来说,这种分组 FFN (GFFN)方案允许 GroupBERT 将 FFN 层的参数数量减少 25%,而任务性能的降低最小。这与所有以前尝试使用分组变换的方法形成对比,但是将它应用于两个矩阵。这导致不相交的隐藏表示,并导致显著的性能下降。
图 2: GFFN 使用一种新颖的方案来应用分组矩阵乘法,既保持了性能又节省了计算。图片作者。
作为注意力补充的分组卷积
计算 BERT 中使用的序列长度的全部到全部的注意力不会导致显著的计算开销。然而,最近的一项研究( Cordonnier et al. 2020 ) [4]表明,首先,对于语言模型来说,只使用多头注意力可能是多余的。变换器层中注意力头的子集折叠成卷积模式,以单独模拟本地令牌交互。
为了减少计算密集注意力图来模拟序列中的局部相互作用的冗余,GroupBERT 引入了专用的卷积模块。分组卷积充当滑动窗口,混合彼此接近的单词标记之间的信息。然后,我们用一个附加的 GFFN 来扩展编码器,以跟随卷积模块,确保每个令牌处理模块都与一个特征处理 GFFN 模块相耦合。
有了这些附加模块,本地令牌交互就有了一个专用的轻量级模型元素。这反过来使得 MHA 注意力仅在模拟远程交互时表现得更好,因为花费在本地令牌交互上的注意力容量更少。图 3 显示了训练前验证集上 BERT 和 GroupBERT 的注意力图。这是一个清晰的可视化,说明了利用回旋如何让注意力在长距离互动中更有效,因为这种模式更平滑、更分散。
图 3:**BERT(左)和 GroupBERT(右)从验证集数据中生成的注意力地图。GroupBERT 注意力较少依赖于相对位置,这使它能够更好地专注于长期互动。图片作者。
让模型参数发挥作用
标准化
许多工作已经考虑了在变压器中应用标准化的最佳方法。虽然层规格化总是规格化的首选方法,但它有两种应用方式:前规格化和后规格化。
PostNorm 对跳过连接和残差之和的输出进行归一化,而 PreNorm 在应用任何投影之前对残差分支的表示进行归一化,如图 4 所示。
图 4:后置规格化(左)和前置规格化(右)将层规格化(LN)函数定位在相对于剩余和跳过连接的不同位置。图片作者。
BERT 的标准实现使用 PostNorm,在使用默认学习率的设置下,它的好处是提供比 PreNorm 更强的任务性能。然而,我们发现前范式配置明显更稳定,可以使用更高的学习率,这是后范式模型所不具备的。
GroupBERT 使用 PreNorm 并实现了优异的任务性能,因为该模型现在可以稳定地适应 4 倍于 PostNorm 基线的学习速率增长,而 post norm 基线在如此高的学习速率下会出现偏差。更高的学习速率对于增加模型泛化和实现更好的收敛速率是重要的。
更高的学习率不会直接带来计算上的节省。然而,实现更高的任务性能将需要更大的模型,从而导致更高的计算成本。因此,通过增加模型稳定性来实现更高的学习率增加了利用模型参数的效率。
辍学
许多基于 Transformer 架构的语言模型受益于使用 dropout,因为它减少了对训练数据集的过度拟合,并有助于泛化。然而,BERT 是在非常大的数据集上预先训练的,在这种情况下,过度拟合通常不是问题。
出于这个原因,GroupBERT 在预培训期间取消了辍学。丢弃掩码乘法中涉及的触发器可以忽略不计,因此也可以将其视为触发器中立优化。然而,生成丢弃掩码会有很大的吞吐量开销,因此在 BERT 中删除这种正则化方法有利于更快的模型执行和更好的任务性能。
虽然在没有退出的情况下对维基百科数据集执行预训练是有利的,但退出仍然是微调 GroupBERT 期间的重要工具,因为微调数据集比预训练语料库小几个数量级。
IPU 变压器
在将所有概述的修改组合到一个模型中之前,我们必须验证每个组件的有效性。然而,成组的 ffn 减少了模型 flop 的数量,而添加卷积模块增加了 flop。因此,直接与 BERT 进行比较是不公平的,因为模型会消耗不同数量的资源。为了确定不同大小的模型增强的质量,我们必须将它们与不同大小的 BERT 模型之间的对数插值进行比较:中等、基本和大。
图 5 显示了引入 GroupBERT 的每个增强的局部消融研究。与基线 BERT 相比,模型的每一次添加都会提高帕累托效率。当组合时,GroupBERT 基本模型实现了与 BERT 大模型相同的 MLM 验证损失,即使它只有不到 50%的参数。
图 5: GroupBERT 消融研究。GroupBERT 中引入的所有增强提高了 BERT 的帕累托效率,因为它们实现了更低的验证 MLM 损失。图片作者。
为了明确地得出 GroupBERT 优于 BERT 的结论,我们证明了 GroupBERT 基相对于 BERT Large 的优势在大范围的模型尺度上持续存在。我们用两个额外的模型尺寸来补充 GroupBERT 基本模型,以创建一个连续的 Pareto 前沿。这种方法,如图 6 所示,使我们能够展示 GroupBERT 相对于 BERT 以及其他试图在 Transformer 中使用分组转换的模型的持续优势。
图 6:针对 FLOPs 的训练前验证准确性。图片作者。
由于 GroupBERT 是 IPU 本地模型,理论上在 FLOPs 方面实现的节省也转化为达到给定验证准确度水平所需的更好的端到端预训练时间。
图 7:在 IPU-POD16 上,不同优化器的 GroupBERT Base 与 BERT Large 的训练时间。所有模型都达到了同等的训练前验证精度。图片作者。
关键要点
我们的结果表明:
- 分组转换可以创建更有效的模型
- 将卷积与密集的全部对全部的注意力结合起来,即使对于需要长距离交互的任务也有有益的影响
- IPU 将理论上的浮点运算节省转化为计算时间的实际节省
- 通过架构变化,而不是简单的模型缩放,可以实现显著的性能提升
总之,在预训练性能方面,GroupBERT 可以实现比原始 BERT 高达 2 倍的改进,从而实现更快、更高效的计算。
谢谢你
感谢 Daniel Justus、Douglas Orr、Anastasia Dietrich、Frithjof Gressmann、Alexandros Koliousis、Carlo Luschi 和 David Svantesson,他们也为这项研究做出了贡献,感谢 Graphcore 的其他同事给予的支持和见解。
参考
[1] I. Chelombiev,D. Justus,D. Orr,A. Dietrich,F. Gressmann,A. Koliousis,C. Luschi, GroupBERT:具有高效分组结构的增强型变压器架构 (2021),arXiv
[2] D. Masters,深入研究现代计算机视觉模型 (2020),graphcore.ai
[3] Y. Tay,M. Dehghani,D. Bahri,D. Metzler,高效变压器:调查 (2020 年),arXiv
[4] J.B. Cordonnier,A. Loukas,M. Jaggi,论自我注意与卷积层的关系 (2020),arXiv
认真对待时间序列
原文:https://towardsdatascience.com/getting-serious-with-time-series-837836eb751e
时间序列是数据科学家工具箱中的一个关键元素,它是事件或值的序列,我们可以通过分析来发现有意义的模式。从环境研究到市场营销,它们在众多数据丰富的领域中发挥着至关重要的作用,并使从业者能够洞察过去并对未来做出明智的预测。
本周,我们选择了四篇具有启发性的文章,它们从不同的角度涵盖了时间序列预测。不管你的经验水平如何,你都有可能学到新的东西。
- https://medium.com/towards-data-science/what-you-need-to-know-for-your-arima-time-series-project-804713b8f00d做好基础很重要。如果你刚刚开始时间序列分析, Sameeha Afrulbasha 对这个主题的介绍是一个很好的起点。它提供了基本概念的明确定义,侧重于 ARIMA(自回归综合移动平均)模型,并解释了不同的组成部分如何一起工作。
- 在 Python 中赋予时间序列生命 。如果你准备好卷起袖子用一些真实的数据工作,Leonie Monigatti 会让你从理论到实践的转变变得平稳而不费力。这个有用的资源提供了您开始操作和可视化时间序列数据所需的所有细节和代码片段,并特别关注有时难以处理的日期时间格式。
乔西·韦斯在 Unsplash 上拍摄的照片
- 如何创建完整的时序工作流 。一旦您对时间序列预测的构建块感到满意,您可能希望将它们实现到一个端到端的项目中。 Marco Peixeiro 的综合指南将引导你走上正确的道路:它解释了如何将时间序列问题构建为监督学习问题,然后允许你使用任何 scikit-learn 模型。
- 当深度学习遇上时间序列 。我们如何将神经网络的能力应用于预测任务? Gabriele Orlandi 探索了前沿模型在时间序列分析领域的潜在应用,并展示了最新研究如何动摇传统的预测方法。
如果说历史教会了我们什么的话,那就是 TDS 的读者总是好奇的想要了解更多。我们将为您提供帮助,为您提供其他主题的必读书目:
- 在我们最新的作者聚焦中,我们很高兴与 NLP 专家 Julia Turc 就多模态机器学习以及早期职业实践者应该如何选择正确的项目(以及其他主题)进行了交谈。
- 作家开发者们,欢呼吧:我们在 Medium 的朋友们宣布了带有语法高亮的代码块的到来!
- 在他们的第一篇 TDS 文章中,安娜·阿拉克里扬和合著者德米特罗·卡拉巴什以深入研究标签编码的“彩虹方法”而引起轰动。
- 如果你想了解更多关于 TinyML 的新兴领域但不确定从哪里开始, Rafael Tappe Maestro 的第一篇 TDS 文章(与 Nikolas Rieder 合著)是一篇容易理解且引人入胜的介绍。
- 涵盖主题建模的理论和实践, Lan Chu 和 Robert Jan Sokolewicz 向我们展示了潜在 Dirichlet 分配(LDA) 的完整实现。
- 在编程语言之间切换很不容易,这就是为什么 Madison Hunter 关于在 Python 和 R 之间平滑切换的技巧是数据科学家的必读之作。
谢谢你一如既往的支持。如果你想产生最大的影响,考虑成为的中层成员。
直到下一个变量,
TDS 编辑
入门:图形数据库| Neo4j
原文:https://towardsdatascience.com/getting-started-graph-database-neo4j-df6ebc9ccb5b
作者图片:复杂关系图可视化(neo4j Dashboard)
传统上,各种基于 SQL 的数据库,也称为关系数据库管理系统(RDBMS ),如 MySQL、SQLite、PostgreSQL 等。为各种网络和移动应用提供动力。鉴于这种数据库在社区中的流行和熟悉程度,数据工程师和科学家已经将产品需求调整到关系数据库中,即使 NoSQL 数据库或图形数据库可能更适合这些需求。
即使对于数据分析师/科学家来说,SQL 也是最需要的数据提取和操作技能,而其他查询语言,如 MongoDB 查询语言( MQL )或 Cypher 查询语言(Neo4j Graph)对社区来说大多是新的。因此,这篇博客的重点是提供一个循序渐进的入门指南,并从头开始构建一个基于图形的数据库。
在我们进入使用 Neo4j 的细节之前,让我们也了解一下为什么以及什么时候我们应该探索图形数据库的适用性——一般来说,如果数据集中的各种实体之间存在复杂的关系、层次或连接,以图形的形式存储数据不仅是可扩展的,而且还简化了各种复杂的分析。
使用图表的其他一般好处是-
- 有效利用关系
- 复杂而直观的数据存储方式
- 可扩展性和更好的性能
- 强大的分析和仪表板
- 根据业务需求的灵活模式
让我们深入研究使用 Neo4j 设置图形数据库
虽然现在有各种图形数据库,如 neo4j、Amazon Neptune、Tiger graph 等,但 Neo4j 是该领域历史最悠久的领先提供商之一,并且还具有特定于数据科学应用的功能。有趣的是,它是高度可扩展的,并且已经为其客户管理了具有数十亿个节点的图表。而且,它是开源的,可以免费使用。
虽然我们可以在其云版本 Neo4j AuraDB 上设置 Neo4j,并使用 python 驱动程序来读/写数据,但这篇博客更侧重于在本地系统上设置数据库,使我们也能够执行批量操作。
图表的关键组件:
- 节点—图表中表示各种实体的各种节点
- 关系—各种节点之间的连接
- 属性—每个节点或关系存储的详细信息
- 标签和索引—主要是为了编写更快、更简单的查询而添加的
第一步:下载并设置 Neo4j
第二步:启动 Neo4j 控制台
cd neo4j-community-4.4.4
bin/neo4j console
作者图片
现在,我们可以使用http://localhost:7474/browser/访问 Neo4j 控制台
图片作者:neo4j dashboard
步骤 3:使用密码创建一些节点和关系
- Neo4j 支持 cypher 查询数据库,我们可以从控制台使用 cypher 创建一些节点
CREATE (p:PEOPLE {name: 'Person 1', location: 'Noida'}), (b:BOOK {name: 'Book 1', author: 'XYZ'})MATCH (p:PEOPLE), (b:BOOK)
WHERE p.name = 'Person 1' AND b.name = 'Book 1'
CREATE (p)-[r:READ]->(b)
作者图片:使用 cypher 创建的简单的 2 个节点和 1 个关系图
步骤 4:使用 neo4j-admin import
批量创建节点和关系
- 虽然我们可以使用 cypher 创建节点,但对于大量节点来说,这可能非常耗时。因此,我们可以使用
neo4j-admin import
在几秒钟内从 CSV 导入数百万行。但是这只能在创建数据库时从控制台执行一次。 - 这里的是一个示例 python 代码,可以用来生成示例数据。大型 CSV 文件可以按照此处指定的格式创建/重构。
- Neo4j 支持节点/关系属性的
int
、long
、float
、double
、byte
、short
、char
、string
、point
、date
、localtime
、time
、localdatetime
、datetime
、duration
数据类型。默认值是string
,数组类型可以通过在类型后追加[]
来指定。此外,我们可以有效地向图中添加标签和索引,以便更快地进行扩展查询。
# To import csvs generated from the code abovebin/neo4j-admin import --nodes=import/nodes_roots.csv --nodes=import/nodes_people.csv --nodes=import/nodes_books.csv --relationships=import/relations_roots.csv --relationships=import/relations_read.csv --relationships=import/relations_favourite.csv --force
最后,我们的样本图形数据库看起来像这样—
作者图片:使用 neo4j-admin 导入的示例图
步骤 4:使用 Cypher 运行分析
例 1:通过Person 1
得到Books
即READ
和FAVOURITE
。
MATCH (p:PEOPLE), (b:BOOKS)
WHERE p.name='Person 1' and (p)-[]->(b)
RETURN p, b
例 2:得到被People
评为 5 的Books
MATCH (p:PEOPLE)-[k:READ]->(b:BOOKS)
WHERE k.rating=5
RETURN p, b
这里的是学习编写高级查询的 Cypher 文档。
第五步:部署
Neo4j 数据库可以从 AWS 市场部署。Neo4j 甚至提供了一个 Django 框架[neomodel](https://neomodel.readthedocs.io/en/latest/)
用于在数据库顶部编写 API 服务。
如果你有兴趣了解更多,请在评论中告诉我:)
这只是“开始”,但图可以是巨大的,有数十亿个节点和关系,甚至在这种规模下表现良好。想象脸书网络图,数十亿人作为节点,相互连接。甚至像谷歌、LinkedIn 和 PayPal 这样的科技巨头也利用图形数据库的力量创造了蓬勃发展的业务。他们的秘密?他们每个人都使用图形数据库技术来利用数据连接的力量。
只要有关系,图数据库就可以成为更有效的数据库选择!
Apache Spark 入门
原文:https://towardsdatascience.com/getting-started-with-apache-spark-cb703e1b3ee9
探索与 Spark 相关的一些关键概念,以及是什么决定了它在大数据领域的成功
伊恩·施耐德在 Unsplash 上拍摄的照片
介绍
正如我们今天所知,互联网发展初期的一个主要问题是无法扩展(例如,能够在短时间内搜索大量数据,支持不断变化的用户数量而不停机,等等)。
这最终是因为在一个网络世界中,不断产生和处理各种形状和大小的新数据(关于大数据分析的更多信息可在我之前的文章中找到)。
试图解决这个问题的第一个方法是 Apache Hadoop(高可用性分布式面向对象平台)。Hadoop 是一个开源平台,旨在将数据作业分解为小块,并将它们作为作业分布在一个计算节点集群中,以便能够并行处理它们。Hadoop 可以分为 3 个关键组件:
- HDFS (Hadoop 分布式文件系统):为大数据支持和容错而设计的分布式文件系统。
- MapReduce :一个框架,旨在促进集群中数据作业的并行化。在映射过程中,以键-值对的形式获取并处理输入,然后将输入传递给 reduce 操作,以便聚合输出并提供最终结果。
- YARN(又一个资源协商者):管理集群的不同节点(例如调度作业、任务等)并平均分配工作负载。
因此,使用 Hadoop,可以通过水平扩展我们的基础设施(添加更多机器)和并行执行来处理大量数据。由于摩尔定律的放缓以及全球范围内云存储成本的同步降低,水平扩展操作事实上已经成为大多数应用程序中事实上的标准方法。
另一方面,Hadoop 也有许多限制:
- 没有完全设计为支持批处理以外的任务(例如流、机器学习等)
- 难以管理和优化。
- 过于冗长的 API 需要大量样板代码。
- 作为批处理过程的一部分,中间计算被写入,然后从磁盘读取(因此导致性能瓶颈)。
为了尝试解决所有这些限制,Apache Spark 应运而生。
阿帕奇火花
Apache Spark 始于 2009 年加州大学伯克利分校 Matei Zaharia 的一个研究项目,今天被公认为处理大量数据的最受欢迎的计算引擎之一(在某些工作中比 Hadoop MapReduce 快几个数量级)。Spark over Hadoop 的一些关键改进包括:
- 一套支持不同类型工作流的库(如 Spark ML、结构化流、Spark SQL、Graph X)。
- 高度容错和并行的生态系统。
- 多种编程语言的 API(例如 Scala、Java、Python、SQL、R)。独立于所使用的语言,程序被分解成字节码,并在集群的工人上执行。
- 用于中间计算的内存存储。
- 专注于计算(例如,Hadoop 同时包含计算和存储系统,而 Spark 几乎对任何存储选项都是开放的)。
Spark 应用程序可以分解成许多不同的组件(图 1)。使用驱动程序来实例化 Spark 会话,并使用它来与集群的不同组件(例如,集群管理器和执行器)进行交互。作为程序执行的一部分,驱动程序负责向集群管理器请求必要的资源(例如内存、CPU),以便让执行器(例如 Java 虚拟机)继续执行它们的任务,并直接与执行器通信以优化执行。Spark 目前能够支持 4 种不同类型的集群管理器:standalone、Hadoop YARN、Apache Mesos 和 Kubernetes。
图 1: Apache Spark 架构(图片由作者提供)。
在整个过程中,数据被存储为分区,并且执行者优选地被分配需要处理离他们最近的数据的任务(以最小化网络带宽)。
Spark 可以对数据执行两种操作:转换和动作。变换会创建一个新的 Spark 数据框,而不会改变原始数据框,并且总是被延迟评估(它们不会立即执行,而是作为沿袭的一部分被记住)。这使得创建转换的有向无环图(DAG) 成为可能,然后可以利用 Spark Catalyst optimizer 基于规则和基于成本的优化,以最有效的方式运行。拥有一系列的转换和不可变的数据帧可以让 Spark 创建一个高度容错的系统。每当一个动作被调用时,转换最终被执行。
变换还可以分为窄或宽。窄转换采用单个输入分区并返回单个输出分区(例如过滤操作),而宽转换使用多个分区并要求数据的重新洗牌(例如分组操作)。
在过去的几年里,为了让 Spark 更容易开发,在 Spark API 上开发了不同形式的结构。rdd(弹性分布式数据集)是 Spark 中最基本的抽象形式,如今主要被 Spark 的结构化 API(数据集、数据帧 API)等效命令所取代。事实上,Spark 结构化 API 完全构建在 rdd 之上,旨在为数据处理领域的大多数常见任务提供易于使用的预优化代码。此外,Spark 还可以通过UDF(用户定义函数)创建自定义函数。Spark UDFs 可以用不同的编程语言编写(例如 Python、Java、Scala 等),尽管目前建议用 Java 或 Scala 编写 UDF,因为它们可以提供更好的整体性能(例如 UDF 就像 Catalyst Optimizer 的黑盒,因此如果用 Python 编写,就无法完全优化)。
最后,Spark 还使得使用 Spark ML 库运行机器学习工作流成为可能(图 2)。该库可分为 4 个关键组成部分:
- 转换器:主要用于执行数据工程/预处理任务(如缩放、特征选择等),它们只应用基于规则的转换(没有从数据中学习)。它们将一个数据帧作为输入,并返回一个新的数据帧。
- 估计器:从数据中学习参数,并返回一个训练好的 ML 模型(它是一个变换器)。
- 管道:在单个对象中组织一系列转换器和估算器。
- 评估者:用于通过各种分类、回归等指标评估我们的机器学习模型。
图 2: Spark ML 工作流程(图片由作者提供)。
此外,Spark ML 可以与各种其他开源包集成,如 Hyperopt、Horovod、MLFlow 等,以提供完整的用户体验。
结论
总体而言,Apache Spark 是目前业内用于处理大数据的最受欢迎的技术之一,由 Databricks 和 Palantir 等公司提供支持。由于最近生态系统的加入,Spark 现在也变得越来越与传统数据科学用户相关(例如 Spark 上的 Pandas API),使其成为最有趣的学习技术之一。
联系人
如果你想了解我最新的文章和项目,请通过媒体关注我,并订阅我的邮件列表。以下是我的一些联系人详细信息:
天文学家气流入门:数据工程的主力
借助由 Python 支持的托管气流服务天文学家构建强大且可扩展的数据管道
什么是天体气流?
我们将从气流开始。 Apache Airflow 是一个开源的工作流管理平台,帮助您构建数据工程管道。Airflow 的最大优势之一,也是它如此受欢迎的原因,是你可以用 Python 以 DAG ( 有向无环图)的形式编写你的配置。使用 Python 编写 DAG 的强大功能意味着您可以利用强大的 Python 库套件来做几乎任何您想做的事情。
接下来是天文学家。天文学家是一个受管理的气流服务,允许你在云环境中编排工作流程。Airflow 的一个常见用例是从一个数据源获取数据,通过几个步骤对其进行转换,然后将其加载到数据仓库中。您甚至可以利用气流进行特性工程,在这里您可以在数据仓库中应用数据转换,创建新的数据视图。在这篇文章中,我将介绍气流的基础知识以及如何开始使用天文学家。
我们开始吧!
安装自制软件
自制软件将是安装天文学家 CLI 最简单的方法。如果你没有安装家酿软件,你可以通过访问家酿网站并运行他们提供的命令来安装。
安装 Docker
接下来,我们需要安装 Docker 。Docker 是一个容器管理系统,允许你在本地计算机和云中运行虚拟环境。Docker 是极其轻量级和强大的。Airflow 运行在 docker 容器中,并安装所需的一切,如 web 服务器和本地数据库。前往 Docker 网站,为你的操作系统安装 Docker 桌面。
注意:在大型企业(超过 250 名员工或年收入超过 1000 万美元)和政府机构中商业使用 Docker Desktop 需要付费订阅。
天文学家 CLI
最后,让我们安装天文学家 CLI (命令行界面)。CLI 是一个命令行工具,允许您与天文学家服务进行交互。您可以使用 CLI 创建新项目、部署代码和管理用户。
brew install astro
注意:对于这个例子,我们不打算将我们的任何 Dag 发布到云中;我们将在本地运行它们,感受一下天文学家是如何工作的。
创建一个 Python 虚拟环境
虚拟环境是隔离您将用于运行 Airflow 的包的一种很好的方式,允许您拥有多个具有不同依赖关系的 Airflow 项目。您可以通过运行以下命令来创建虚拟环境:
python3 -m venv venv
然后在必要时激活环境。如果您使用的是 Visual Studio 代码,可以使用命令面板来激活环境,或者它应该在您打开项目时激活。
source venv/bin/activate
初始化项目
为您的项目创建一个新文件夹,并启动您选择的 IDE 我的工具是 Visual Studio 代码。在这个例子中,我将我的作业命名为Astro
,但是用一个更具描述性的名称来描述您正在运行的作业会更合适。
运行命令astro dev init
,用几个文件和文件夹创建一个新项目。最重要的文件是docker 文件。该文件用于构建 docker 映像,您将使用该映像来运行 Airflow 实例。Dockerfile 是一个文本文件,包含用户可以在命令行上调用的所有命令,以组合一个图像。使用 docker build,用户可以创建一个连续执行几个命令行指令的自动化构建。
您还会在文件夹中找到一个 requirements.txt 文件,其中包含运行 Dag 所需的所有 python 包。您可以根据需要向该文件添加其他包。当我们到达这里,我们将没有任何额外的依赖;唯一出席的将是apache-airflow
。
作者图片
狗的解剖
放置在/dags
目录中的 Dag 会自动出现在气流 UI 中。重要的是,包含 DAG 逻辑的.py
文件具有 DAG 上下文管理器定义;你可以看到下面这条线with DAG('example_dag'
。新版本的 Airflow 使用 decorator(@dag(
)来完成同样的事情。
让我们看一个 DAG 示例,并讨论各种组件。
from airflow import DAG
from airflow.operators.dummy_operator import DummyOperator
from airflow.operators.bash_operator import BashOperator
from airflow.operators.python_operator import PythonOperator
from airflow.version import version
from datetime import datetime, timedelta
def my_custom_function(ts,**kwargs):
"""
This can be any python code you want and is called from the python
operator. The code is not executed until the task is run by the
airflow scheduler.
"""
print(f"Task number: {kwargs['task_number']}.")
print(f"DAG Run execution date: {ts}.")
print(f"Current time: {datetime.now()}")
print("Full DAG Run context.")
print(kwargs)
# Default settings applied to all tasks
default_args = {
'owner': 'airflow',
'depends_on_past': False,
'email_on_failure': False,
'email_on_retry': False,
'retries': 1,
'retry_delay': timedelta(minutes=5)
}
# Using a DAG context manager, you don't have to
# specify the dag property of each task
with DAG('example_dag',
start_date=datetime(2019, 1, 1),
max_active_runs=3,
schedule_interval=timedelta(minutes=30),
default_args=default_args,
# catchup=False
) as dag:
t0 = DummyOperator(
task_id='start'
)
t1 = DummyOperator(
task_id='group_bash_tasks'
)
t2 = BashOperator(
task_id='bash_print_date1',
bash_command='sleep $[ ( $RANDOM % 30 ) + 1 ]s && date')
t3 = BashOperator(
task_id='bash_print_date2',
bash_command='sleep $[ ( $RANDOM % 30 ) + 1 ]s && date')
# generate tasks with a loop. task_id must be unique
for task in range(5):
tn = PythonOperator(
task_id=f'python_print_date_{task}',
python_callable=my_custom_function,
op_kwargs={'task_number': task},
)
t0 >> tn # inside loop so each task is added downstream of t0
t0 >> t1
t1 >> [t2, t3] # lists can be used to specify multiple tasks
积木
- Imports :这只是您希望在项目中使用的 python 库的列表。像使用任何 python 项目一样使用它们。
- 自定义函数:自定义函数位于上下文管理器之外,由 Python 操作符调用。该函数可以是您想要的任何 python 代码,并从 python 操作符中调用。一旦气流调度程序运行任务,就会执行该代码。
- 默认参数:适用于所有任务的默认设置字典。这些可以在任务级别覆盖。
- DAG 上下文管理器:更多关于下一个!
- 任务:工作流程中的各个步骤
- 执行顺序:气流运行各任务的顺序
DAG 上下文管理器
DAG 上下文管理器是构建图形的逻辑所在。让我们浏览一下上面的 DAG 示例,并讨论各个组件。还有其他方法来构建 DAG,包括新的任务流 API 。但是,DAG 上下文管理器是构建 DAG 的常用方法。上下文管理器允许轻松地将任务分配给 DAG,并能够添加任务之间的依赖关系。
首先是 dag ( example_dag
)的名称,稍后你会在 Astro UI 中看到。然后是一些全局设置,包括之前定义的default_args
。
任务
接下来是我们的第一个任务,t0 = DummyOperator
。虚拟操作符什么也不做,只是允许您对任务进行分组,以设置图中将要执行的顺序。接下来,我们有另一个虚拟操作符,后面是下一个任务t2 = BashOperator
。Bash 操作符允许您执行 Bash 脚本。
最后,我们看到了循环中的下一组任务,tn = PythonOperator
利用了 Python 操作符,它可以执行 Python 代码,甚至像在本例中一样,调用我们在文件开头定义的函数。
op_kwargs
参数用于将task_number
变量传递给函数。在上面的函数中,除了“ts”变量之外,还传递了**kwargs
变量。此变量允许函数访问 DAG 运行的完整上下文。
我们的函数用print(kwargs)
打印完整的上下文变量。我们可以查看日志,看看我们的kwargs
。
执行次序
现在,把所有这些都带回家,我们设置任务执行的顺序。三行代码告诉 DAG 如何执行。首先是循环内部的t0 >> tn
。这个位置告诉 DAGtn
在循环的每次迭代中运行,从上面我们知道生成了五个任务。
接下来是t0 >> t1
。虚拟操作符允许下一行t1 >> [t2, t3]
执行并行任务。这里的逻辑是,我们有一个起始虚拟操作符,下一步有六个任务,最后一步有两个并行任务。
开始项目
运行astro dev start
来启动项目和 docker 容器。
[+] Running 4/4
⠿ Container astro_cf4ada-postgres-1 Started 0.7s
⠿ Container astro_cf4ada-scheduler-1 Started 1.5s
⠿ Container astro_cf4ada-triggerer-1 Started 1.5s
⠿ Container astro_cf4ada-webserver-1 Started 2.2s
Airflow is starting up! This might take a few minutes…
打开 Docker 仪表板,查看不同的容器运行情况。像上面的控制台输出一样,我们可以看到四个容器正在运行。
作者图片
在它完成启动过程之后,您将在您的 shell 中看到以下内容。请注意 URL 和凭据。当它准备好的时候,你的浏览器应该自动启动 Astro UI,但是你可以随时使用 URL 来访问它。
Airflow Webserver: http://localhost:8080
Postgres Database: localhost:5432/postgres
The default Airflow UI credentials are: admin:admin
The default Postgres DB credentials are: postgres:postgres
打开并浏览气流用户界面
看一下气流 UI。Dag页面将向您显示您的 Astro 项目中所有可用 Dag 的列表。您可以使用最左侧的滑块控制来启动 DAG。这样做之后,您将立即开始看到 DAG 运行!
作者图片
当您点击 DAG 的名称时,您将被带到网格视图。该页面显示了一个非常酷的彩色编码图,显示了每个任务运行的状态。您可以点击每一个来获得更多的细节,包括该任务的日志。如果出现故障,您可以点击失败的任务来查看日志。
作者图片
接下来是图形视图。图形视图向您显示了执行 DAG 的顺序。请查看我们任务执行的输出,并将其映射回我们上面写的代码。您可以看到执行的顺序和任务之间的依赖关系。
作者图片
停止项目
最后,当你完成了一天的开发,你可以用astro dev stop
停止一切运行。
❯ astro dev stop
[+] Running 4/4
⠿ Container astro_cf4ada-triggerer-1 Stopped
⠿ Container astro_cf4ada-webserver-1 Stopped
⠿ Container astro_cf4ada-scheduler-1 Stopped
⠿ Container astro_cf4ada-postgres-1 Stopped
结论
天文学家是开源的阿帕奇气流服务的商业产品。它提供了高度可靠的托管服务,允许您为数据工程和数据科学构建强大的工作流编排。我们今天介绍了入门的基础知识,从安装到创建项目、运行 DAG 和停止项目。我们还讲述了 DAG 上下文管理器的基础知识以及如何构建 DAG。从这里,您可以开始尝试自己的 Dag 和工作流,并实现数据工程和数据科学工作流的现代化。编码快乐!
一如既往,本文的代码可以在 GitHub 上找到
如果你喜欢阅读这样的故事,并想支持我成为一名作家,考虑注册成为一名媒体成员。一个月 5 美元,无限量访问数千篇文章。如果你使用 【我的链接】 注册,我将免费获得一小笔佣金。
BigQuery SQL 用户定义函数(SQL UDFs)入门
一个强大的基本构建块,支持核心 BigQuery 平台功能的自定义扩展
迷茫?不要害怕。照片由 charlesdeluvio 在 Unsplash 上拍摄
动机
可扩展性是 BigQuery 的关键特性之一——伴随着超级大国般的新功能的不断发展——这使得它成为一个强大的平台,可以处理任何类型的数据工作,无论大小。
然而,任何平台的功能集都可能是压倒性的,甚至很难弄清楚从哪里开始。这一系列的目的是揭开一些基本 BigQuery 特性的神秘面纱,并让您以更快的速度朝着您的特定目标前进。
情况
BigQuery 中有几个不同种类的“函数”,所以如果没有通过全面的(但有时是压倒性的)官方文档弄清楚每个函数的细微差别,请原谅。
首先,User-Defined Functions (UDFs)
是被称为Routines
的大查询资源类型家族的一部分,该家族还包括Stored Procedures
、(也就是通常所说的脚本)、Remote Functions
和Table Functions
。
每一种都有不同的种类,每一种都有自己的特质。下面这篇文章是对开发Routines
的一个温和的介绍:
为了简单起见,让我们从最简单的开始:SQL 用户定义函数。
解决办法
在深入函数构造和语法的细节之前,一个有用的起点是展示如何实际使用它。从 SQL UDF 获得结果的语法非常简单且一致:
SELECT project_id.dataset_id.function_name([OPTIONAL PARAMETERS])
如果这个函数没有参数,那么你仍然需要包括括号,因此:
SELECT project_id.dataset_id.function_name()
您可以将它用作普通查询的一部分(在这种情况下,该函数将为查询响应的每一行返回一个值),或者用作工作流的一部分,其中只需要一个响应,并且您希望将一些计算复杂性封装在一个易于使用的函数中。
在本例中,我们将构建一个 SQL UDF,使表引用(通常缩写为 refs)的工作变得非常简单,这对开发 BigQuery 自动化脚本和其他函数很有帮助。将使用以下语法调用该函数:
SELECT flowfunctions.ref.build('project_id', 'dataset_id', 'table_name')**RETURNS:**
'project_id.dataset_id.table_name'
我们按照地理位置(美国:flowfunctions
)将所有Routines
分组到单个项目中,并按照功能类型分组到数据集中。
UDF 最简单的开发工作流程实际上是而不是从开发实际功能开始,它是编写底层查询。通过在开始时声明您将用作参数的变量,将查询转换成函数就变成了微不足道的最后一步。
在这种情况下,我期望三个输入参数,project_id
、dataset_id
和table_name
,都是STRINGs
。我像在 BigQuery 脚本中一样声明它们,在这种情况下,使用DECLARE
关键字在声明点设置它们的值:
DECLARE project_id STRING DEFAULT 'my-project';
DECLARE dataset_id STRING DEFAULT 'my_dataset';
DECLARE table_name STRING DEFAULT 'my_view';
我现在甚至可以运行这个脚本,它会成功运行——但是实际上什么也不会发生,因为我没有选择任何东西。如果我想检查这些值,我可以执行以下查询:
SELECT project_id, dataset_id, table_name
然而,这很无聊,让我们把他们建成一个参考!这并不难——然而,即使对于这个简单的例子,也有几种不同的方法可以实现这一点,或者使用 CONCAT 函数:
SELECT CONCAT(project_id, ".", dataset_id, '.', table_name) AS ref
或者使用 串联运算符 :
SELECT project_id||"."||dataset_id||'.'||table_name AS ref;
这两个例子都很可读,但是因为第二个例子有点短,我们就用它吧。执行这个(连同变量声明),我们将得到我们想要的结果:
**RETURNS:**
my-project.my_dataset.my_view
太好了!现在把它放到一个函数中,这样我们可以重用和简化我们未来的代码。User-Defined Functions
的官方文档是这里是,然而我们只需要一个非常简单的语法来实现我们的简单函数:
CREATE OR REPLACE FUNCTION flowfunctions.ref.build (
project_id STRING,
dataset_id STRING,
table_name STRING
) AS ((
SELECT project_id||"."||dataset_id||'.'||table_name AS ref
))
没有必要将参数定义放在不同的行上,但是如果有很多这样的定义,会有助于提高可读性。其他需要注意的事项有:
- 您不能在查询末尾使用分号,否则会出错
- 您需要双括号,否则您会得到以下错误消息:
*The body of each CREATE FUNCTION statement is an expression, not a query; to use a query as an expression, the query must be wrapped with additional parentheses to make it a scalar subquery expression at [y:x]*
这听起来很奇怪,但却是非常重要的!您可以更改函数体以删除SELECT
和AS ref
来消除错误,但是将 SQL 查询放在双括号中(使其成为“标量子查询表达式”)现在使您能够编写任意复杂的 SQL 查询,并将它们封装到可重用的函数中。这意味着您可以以连续的方式编写美观、可读的查询(即使用通用的表表达式,而不求助于无法理解的嵌套子查询)。这是一件非常好的事情。
限制是您需要从查询中返回一个(且只有一个)“对象”,然而实际上您可以简单地将多列构建到STRUCTs
中,将多行构建到ARRAYs
中,这应该可以解决任何用例。
当然,那时你必须UNNEST
他们才能使用他们,但那肯定是另一天的主题。
执行
要执行此功能,只需在控制台中执行以下查询:
SELECT flowfunctions.ref.build('project_id', 'dataset_id', 'table_name')
拥有BigQuery Data Viewer
和BigQuery Metadata Viewer
权限的allAuthenticatedUsers
也可以使用包含该函数的数据集,因此只需几次点击,就可以对全世界开放。相当神奇!
如果您觉得这(以及其他相关材料)有用和/或有趣,请跟我来!
如果你还不是会员,那么加入 Medium 吧,每月只需 5 美元,就能从这个活跃、充满活力和激情的数据人社区中获得无限的故事。也有很多其他人,但是如果你对数据感兴趣,那么这里就是你要去的地方…
Bloom 入门
原文:https://towardsdatascience.com/getting-started-with-bloom-9e3295459b65
用 Bloom 生成文本的概述和 Codelab
帕特里克·托马索在 Unsplash 上的照片
目录
什么是盛开,为什么我们要小心行事
Bloom 是来自 BigScience 的一个新的 176B 参数多语言 LLM(大型语言模型),由 Huggingface 主持,与全球数百名研究人员和机构进行开放合作。除了贡献者的多样性之外,Bloom 最值得注意的一点是,Bloom 是完全开源的,Huggingface 通过其 transformers API 向公众提供了他们完整的(以及一些较小的)预训练模型。对 LLM 进行研究的其他组织,包括 OpenAI、Meta 和 Google,已经选择将他们的 LLM 主要保留在内部,或者限制访问严格控制的封闭测试组。
作者提供的图片;布鲁姆发的短信。提示:“爱丽丝穿过小门”
关于在现实世界中使用这些模型的危险,已经有了一场对话,更不用说让他们公开化了。担忧的范围从强化不公平和系统性偏见,到加速网上错误信息的传播。比我更有能力的声音,并继续倡导更多的人负责,透明和公平的发展和使用这一技术。如果你不熟悉,我鼓励你在这里停下来,花一些时间了解一下像蒂姆尼特·格布鲁 ( DAIR 研究所)、玛格丽特·米歇尔和人工智能合作伙伴团队等许多人的工作。
因此,我鼓励每个人坚持预期用途,并在继续本Hello World
风格的介绍性教程时,注意布鲁姆的模型卡上列出的风险和限制。
设置您的环境
我们将在 PyTorch 中使用通用 Bloom 模型的 1.3B 参数版本,仅使用 CPU 运行推理。当我在 Google Cloud 的 Vertex 服务上使用 Python 3 Jupyter Lab VM 时,你应该能够在几乎任何本地或托管的*nix Jupyter 环境上使用。
首先,我们需要设置一个虚拟环境作为洁净室,以安装所有正确版本的依赖项。我们将创建一个名为.venv
的环境(它也产生一个同名的隐藏目录),然后激活它开始工作:
pip install venv
python -m venv .venv
source .venv/bin/activate
接下来,我们将把需要的包安装到我们的.venv
环境中:
pip install transformers
pip install torch torchvision torchaudio --extra-index-url [https://download.pytorch.org/whl/cpu](https://download.pytorch.org/whl/cpu)
最后,我们需要退出我们的 venv,向 Jupyter Lab 注册我们的新环境作为内核,并重新启动它:
deactivate
ipython kernel install --user --name=venv
source .venv/bin/activate
当你在 Jupyter Lab 中选择一个内核选项时,你应该会看到一个选项venv
。让我们选择并连接到它。
下载预先训练的标记器和模型
启动我们的示例笔记本(在 GitHub 上也有),我们首先从之前安装到venv
的包中导入一些模块:
import transformers
from transformers import BloomForCausalLM
from transformers import BloomTokenizerFast
import torch
现在到主事件,我们下载预训练的 Bloom 1.3B 参数通用 LLM。虽然我还没有准确地确定它的大小,但似乎这个版本的模型的权重和偏差占用了大约 1.5Gb 的空间。关键的是,我们还需要获取布鲁姆的记号赋予器。这将允许我们将输入文本(“提示”)转换成 Bloom 可以理解的嵌入文本:
model = BloomForCausalLM.from_pretrained("bigscience/bloom-1b3")
tokenizer = BloomTokenizerFast.from_pretrained("bigscience/bloom-1b3")
说到这里,我们来设置一些全局,包括我们的提示文字:
prompt = "It was a dark and stormy night"
result_length = 50
inputs = tokenizer(prompt, return_tensors="pt")
一些注意事项:
result_length
校准我们从模型得到的提示响应的大小(以令牌为单位)。inputs
包含prompt
的嵌入表示,专门由 PyTorch 编码使用。如果我们使用 TensorFlow,我们会通过return_tensors="tf"
。
运行推理:更好反应的策略
在我们向模型发送提示之前,我们需要考虑哪些解码/搜索策略可能最适合我们的用例。使用自回归转换器(为下一个令牌预测而训练),我们有许多选项来搜索答案空间以获得最“合理”的输出。Patrick von Platen(hugging face)的这篇好文章很好地解释了我们将要尝试的 3 种技术背后的细节和数学,所以我不会在这里重新发明轮子。然而,我会给你 TL;每个的灾难恢复版本:
- 贪婪搜索简单地在每个时间步长 t+1 选择下一个单词,该单词在 t 处跟随该单词的预测概率最高。这里的一个主要问题是,如果贪婪搜索前面有一个在 t 处概率低的单词,则贪婪搜索将错过在 t+1 处概率高的单词
- 波束搜索跟踪第 n(
num_beams
)个最可能的字序列并输出最可能的序列。听起来不错,但是当输出长度变化很大时,这种方法就失效了——就像开放式文本生成一样。贪婪搜索和波束搜索也产生输出,其分布与人类可能执行相同任务的方式不太一致(即,两者都容易产生相当重复、乏味的文本)。 - 用 Top-k + Top-p 采样是三种方法的组合。通过采样,我们的意思是基于其条件概率分布随机选择下一个单词( von Platen,2020 )。在 Top-k 中,我们选择
k
个最有可能的单词,然后在下一次抽签之前在它们之间重新分配概率质量。 Top-p 为 top-k 增加了一个额外的约束,因为我们从累积概率超过p
的最小单词集中进行选择。
现在,我们将尝试所有 3 种策略,以便比较结果。:
# Greedy Search
print(tokenizer.decode(model.generate(inputs["input_ids"],
max_length=result_length
)[0]))
那是一个漆黑的暴风雨之夜,风刮得很大。雪下得很大,地上覆盖着雪。所有的马都冻僵在地上,男人们蜷缩成一团
# Beam Search
print(tokenizer.decode(model.generate(inputs["input_ids"],
max_length=result_length,
num_beams=2,
no_repeat_ngram_size=2,
early_stopping=True
)[0]))
那是一个漆黑的暴风雨之夜,风刮得很大。我在路中间,这时我听到一声巨响。它来自我马路对面的房子。一个男人
# Sampling Top-k + Top-p
print(tokenizer.decode(model.generate(inputs["input_ids"],
max_length=result_length,
do_sample=True,
top_k=50,
top_p=0.9
)[0]))
那是一个漆黑的暴风雨之夜。快到中午了。当我下车脱鞋时,一个男人走到我面前坐下。他留着小胡子,浓密的头发和棕色的眼睛。男性
结论和后续步骤
就个人而言,所有这些结果似乎大多是合理的。您会发现,当您迭代并调整参数和提示时,一些策略可能会为您的特定用例产生更优的输出。事实上,构造提示来哄 LLM 做一些有用的事情本身就有点像艺术和科学。
另外,在采样 top-k + top-p 输出中,术语“夜晚”和输出“几乎中午”之间的不一致说明了一个有价值的观点,即很容易将LLM 误认为是推理机器,它们使用世界的内部模型来构建自己的响应(像人类一样)。事实上,我们不需要深度学习、大数据或者 LLMs 来证明人类会拟人化任何东西。相反,我们应该看到 LLM 的本来面目:语法上可信的句子生成器,应该睁大眼睛(以及大量减轻工程和包容性设计)部署它们的局限性。
考虑到这一点,我自己与布鲁姆的旅程将沿着几条线索前进;很大程度上侧重于使文本生成和分类标题适应现代审计中的问题。具体来说:
- 代码摘要。布鲁姆能用通俗易懂的英语概括一个代码块的逻辑吗?
- 令牌分类的迁移学习。Bloom 能被训练识别过程文件中的风险和/或控制吗?
- 可靠性。对于生成的摘要和分类的事实准确性,我们可以在 Bloom 预测中构建什么保证(如果有的话)?
快乐发电!
参考
- 布鲁姆模特卡,2022,拥抱脸
- 绽放
[transformers](https://huggingface.co/docs/transformers/model_doc/bloom)
文档,2022,拥抱脸 - 如何生成文本:用变形金刚使用不同的解码方法进行语言生成,2020,Patrick von Platen
[venv](https://docs.python.org/3/library/venv.html#module-venv)
模块文档,2022,Python.org- 提示 GPT-3 的工程技巧和诀窍,2021,安德鲁·坎蒂诺
- 布鲁姆入门:样本笔记本,2022,丹妮·塞隆
因果推理入门
原文:https://towardsdatascience.com/getting-started-with-causal-inference-5cb61b707740
超越“相关性不是因果关系”,理解“为什么”
在 Unsplash 上由 Carlos Muza 拍摄的照片
在整个技术创新的旅程中,公司最梦寐以求的目标是找到改变人类行为的方法,方法是回答诸如“我如何才能让访问者成为顾客?”、“这个打折券有帮助吗?”、“哪项活动能带来更好的参与度和利润?”等。这需要使用数据来制定可靠的可操作方案,更具体地说,需要了解应该做些什么来将我们的关键指标推向所需的方向。
数据科学家一直在使用预测建模来生成可操作的见解,这些模型非常擅长做它们应该做的事情,即生成预测,但不是为我们提供可靠的可操作的见解。他们中的许多人遭受虚假的相关性和不同种类的偏见,我们将在本文中讨论。
那么,我们为什么需要因果推理呢?答案就在问题本身,因为我们需要知道【为什么】,为什么事情会发生?,例如,“销售是否因为优惠券代码而增加?”。因果推理是可操作处方的教父。这不是什么新鲜事,经济学家多年来一直用它来回答诸如“移民会导致失业吗?”、“高等教育对收入有什么影响?”等等。
联想 VS 因果?
我们经常听到类似“联想不是因果关系”的说法,这也是很直观的理解。现在让我们从数学的角度来看这个问题。假设,有人告诉你,在社交媒体网站上有个人资料图片的人有更好的参与度,那么为了增加参与度,我们应该要求用户上传一张个人资料图片。人们可以很快指出,有更多朋友的用户可能有一张个人资料照片,用“你可能认识的人”选项来提示用户是增加参与度的一种更好的方式。在这种情况下,个人资料图片不会引起参与,它只是与参与相关联。
作者图片
在我们开始学习数学之前,让我们先理解几个贯穿本文的术语,治疗和控制。治疗不过是某种我们想要知道效果的干预。在我们的情况下,有一张个人资料照片就是治疗。
处理={1,如果单元 I 被处理,否则为 0 }
Yᵢ是单位 I 的结果,Y₁ᵢ是单位 I 治疗的结果,Y₀ᵢ是未治疗的结果。因果推断的根本问题是,我们没有观察治疗和对照的易。因此,个体治疗效果是无法观察到的,有多种方法可以对其进行估计,但由于我们才刚刚开始,所以让我们关注平均治疗效果(ATE)
E[Y₁-Y₀]
ATT = E[Y₁-Y₀|T=1],对被治疗者的平均治疗效果。
直觉上,我们理解在某些情况下,是什么让关联不同于因果关系,但对此有一个词的答案,它被称为偏差。在我们的例子中,没有个人资料照片的人可能有较少的朋友和/或来自较高的年龄组。因为我们只能观察到一种潜在的结果,也就是说,接受治疗的 Y₀不同于未接受治疗的 Y₀。治疗后的 Y₀高于未治疗的 Y₀,也就是说,有个人资料照片(治疗)的人在没有照片的情况下会有更高的参与度,因为他们可能更年轻,更懂技术,也有更多的朋友。被治疗的 Y₀也被称为反事实。这是我们没有观察到的东西,但却是唯一的理由。有了这样的理解,让我们再举一个例子,“与公立学校相比,私立学校教育能带来更高的工资吗?”。我们的理解是,上私立学校的学生有更富裕的父母,他们可能受教育程度更高,因为教育与财富有关,而不仅仅是暗示因果关系,因为高工资和私立学校教育之间似乎有关联。
关联由=E[Y | T = 1]-E[Y | T = 0]给出
因果关系由=【e[y₁-y₀】
用关联方程式中观察到的结果替换
e[y | t = 1]-e[y | t = 0]= e[y₁|t=1]-e[y₀|t=0
加上和减去治疗单位的反事实结果 EY₀|T=1
e[y | t = 1]-e[y | t = 0]= e[y₁|t=1]-e[y₀|t=0]+e[y₀|t=1]-e[y₀|t=1】
e[y | t = 1]-e[y | t = 0]= e[y₁-y₀|t=1]+{e[y₀|t=1]-e[y₀|t=0]}bias
联想= ATT+偏见
这是上帝的因果宇宙方程式。它告诉我们为什么关联不是因果关系,并且当治疗组和对照组都没有接受治疗时,治疗组和对照组在治疗前的差异是多少。如果 E[Y₀|T=1]=E[Y₀|T=0],即在接受治疗之前,治疗和控制之间没有区别,那么我们可以推断关联是因果关系。
因果推理技术旨在使治疗组和对照组在除治疗外的所有方面都相似。随机对照试验(RCT)或随机实验就是这样一种确保偏倚为零的方法。它包括随机分配单位到治疗组或对照组,如 E[Y₀|T=1]=E[Y₀|T=0]. A/B 测试是一项随机实验,是谷歌和微软等公司广泛使用的一种技术,旨在发现网站在用户体验或算法方面的变化所带来的影响。但这并不总是可能的,在我们的例子中,我们不能随机给用户上传个人资料图片的选项,或者我们不能要求人们吸烟以量化吸烟对怀孕的影响等。此外,它也不能用于非随机观察数据。
在我们进入一些方法来确保治疗组和对照组除了治疗之外在所有方面都是相似的之前,让我们先看看因果图,因果关系的语言。
因果图:因果关系的语言
因果图的基本思想是表示数据生成过程。听起来很简单?不过,也不尽然!它要求我们事先知道所有可能影响结果的因素,治疗是其中之一。
一个很常见的例子就是医学对病人存活率的影响。如果药物用于受疾病严重影响的人,也就是说,如果治疗包括重症患者,而对照组有不太严重的患者,这将在我们的设计中引入偏差。
减少这种偏差的一种方法是控制其他因素,如疾病的严重程度(随机分配不同严重程度的治疗)。通过对严重病例进行调理,治疗机制变得随机。让我们用因果图来表示这个。
图 A 和图 B(作者图片)
因果图由节点和边组成。节点代表不同的因素和结果,边代表因果关系的方向。在上面的图 A 中,药物导致患者存活,在图 B 中,严重程度和药物都导致患者存活,而严重程度也导致治疗。这些图表是有用的,因为它们告诉我们两个变量是否彼此相关,路径(节点之间的箭头)给了我们关系的方向
这让我们想到了好路径和坏路径的概念。好的路径或前面 门是治疗与结果相关的原因,这也是我们分析感兴趣的。通常,这些是箭头背离治疗的路径,其余所有路径是坏路径或后门,我们需要对其进行控制,通常是箭头指向治疗的路径。在上面的例子中非常明显。不是吗?
关闭后门,或控制或调节影响治疗的变量只是确保偏差为零和我们的因果推断结果更有效的一些其他词语。我们称这种偏见为混杂偏见,这是因果宇宙中的大坏蛋。当有一个共同因素影响治疗和结果时,就会发生混杂,所以为了确定因果关系,我们需要通过控制共同原因来关闭后门。在我们上面的例子中,严重程度引入了混杂因素,因此除非我们控制严重程度,否则我们无法得到医学对患者存活率的因果估计(T - > Y)。
混杂偏倚(图片由作者提供)
我们是否应该控制一切,以确保我们的模型不会遭受混淆的偏见?但愿因果宇宙中的生命也是如此简单!
想象一下,我们想知道练习国际象棋对工作记忆的影响。我们以某种方式随机化了实践的度量,使得 E[Y0|T=1]-E[Y0|T=0] = 0。由于实践的测量是随机的,我们可以直接测量因果影响,而无需控制任何变量。然而,如果我们决定控制实际游戏中的性能,我们会将选择偏差添加到我们的估计中。通过调节表现,我们正在寻找表现相似的球员群体,同时不允许工作记忆发生太大变化,结果,我们没有得到练习对工作记忆的可靠影响。我们有这样一个例子,E[Y₀|T=0,Per = 1] > E[Y₀|T=1,Per = 1】,也就是说,那些不需要太多练习就能有更好表现的人可能有更好的工作记忆。
选择偏差(图片由作者提供)
当我们未能控制治疗和结果的共同原因时,就会出现混杂偏倚。当我们控制治疗和结果的共同影响时(如上面的例子),或者当我们控制从原因到结果的路径中的变量时(在下面的回归案例 3 中用例子讨论),就会发生选择偏倚
我们已经在理论上谈了很多,但我们如何实现这一点呢?在数据已经收集后,我们如何控制变量以获得治疗效果?
回归救援
回归就像数据科学宇宙的标题美国,在集成和神经网络成为主流之前很久就从数据中解决问题和创造价值。回归是识别因果关系、量化变量之间的关系以及通过控制其他变量来关闭后门的最常见方式。
如果你不熟悉回归,我建议你先经历一下。有数百(如果不是数千)本关于回归的书籍和博客,只需要一次谷歌搜索。我们的重点将更多地放在使用回归来识别因果影响上。
让我们看看这在实践中是如何工作的。我们必须了解电子邮件活动对销售的影响,更具体地说,我们希望评估模型
销售额= B₀+B₁*Email +误差。
数据是随机的,也就是说没有偏见,E[Y₀|T=1]-E[Y₀|T=0]=0.
我们可以直接计算出 ate
(df.groupby("Email")["Sales"].mean())
由于这是随机数据,我们可以直接计算出 ATE = 101.21–95.72 = 5.487。现在让我们试着回归一下。
result = smf.ols('Sales ~ Email', data=df).fit()result.summary().tables[1]
这很酷,对吧?我们不仅得到了作为电子邮件系数的 ATE,还得到置信区间和 P 值。截距给出了 Email=0 时的销售额,即 E[Y|T=0],Email 系数给出了 ATE E[Y|T=1]-E[Y|T=0]。
对随机数据进行因果估计更容易,因为我们不必控制偏差。正如我们上面讨论的,控制混杂因素是一个好主意,但是我们应该小心选择偏差,因为我们不断增加我们控制的变量的数量。在接下来的几节中,我们将讨论在我们的因果估计工作中应该控制哪些变量,并使用回归来实现它们。
案例 1:控制混杂因素。
在缺乏随机数据的情况下,我们无法在不控制混杂因素的情况下获得无偏的因果影响。假设我们想量化多受一年教育对时薪的影响。这是我们的数据
让我们看看这个简单模型的结果
对数(lhwage) = B₀+ B₁*educ +误差
result = smf.ols('lhwage ~ educ', data=wage).fit()
result.summary().tables[1]
由于这是一个对数模型,教育程度每增加 1 年,小时工资预计增加 5.3%。但是这个模型有偏见吗?E[Y₀|T=1]-E[Y₀|T=0] =0 吗?由于这是来自实验的非随机数据,我们可以认为受教育年限越长的人父母越富有,网络越好,或者他们属于一个享有特权的社会阶层。我们也可以认为,年龄和经验年限也会影响工资和受教育年限。这些影响我们治疗和教育的混杂变量引入了偏见(E[Y₀|T=1]-E[Y₀|T=0)!=0),我们需要对其进行控制。让我们将这些控件添加到我们的模型中,看看结果。
controls = ['IQ', 'exper', 'tenure', 'age', 'married', 'black', 'sibs', 'meduc']result = smf.ols('lhwage ~ educ +' + '+'.join(controls), data=wage).fit()result.summary().tables[1]
正如你在上面看到的,在考虑控制因素后,教育对时薪的影响降低到了 4.78%。这证实了第一个没有任何控制的模型是有偏差的,高估了多年教育的影响。这个模型并不完美,可能还有一些我们没有控制的混杂变量,但它肯定比前一个要好。
观察数据(非随机)的因果推断需要内部有效性,我们必须确保我们已经控制了可能导致偏差的变量。这不同于预测模型所需的外部有效性,我们使用训练测试分割来实现预测模型,以获得可靠的预测(不是因果估计)。
案例 2:控制预测因子以减少标准误差
我们已经在上面看到了如何增加混杂因素的控制可以给我们更可靠的因果估计。但是我们还应该在模型中加入什么其他的变量呢?我们是否应该在结果中加入那些善于捕捉方差但不是混杂因素的变量?让我们用一个例子来回答这个问题
假设我们开展了一个电子邮件活动来宣传我们的杂货店。电子邮件是随机发送的,不存在偏见,即没有其他变量影响我们的治疗分配,E[Y₀|T=1]-E[Y₀|T=0] =0。让我们算出电子邮件对销售的影响。
model = smf.ols('sales ~ email', data=email).fit()model.summary().tables[1]
看起来电子邮件活动对销售有负面影响。该系数具有较高的标准误差,并且不显著。让我们更深入地了解一下治疗(电子邮件)和控制(无电子邮件)销售的可变性
作者图片
由于该数据来自随机实验,电子邮件的分配通过抛硬币来完成,没有其他变量影响治疗。但是我们看到销售有很大的差异。这可能是因为电子邮件对销售的影响很小,而那些最近的、频繁的、大量购买的人(RFM 价值观)决定了销售。也就是说,如果产品像冬装一样是季节性的,我们可以说销售的可变性受到其他因素的影响,如用户的 rfm 得分或季节性。因此,我们需要控制这些影响,对我们的电子邮件活动的因果影响进行可靠的估计。让我们在模型中引入 rfm 分数。
model = smf.ols('sales ~ email + rfm', data=email).fit()model.summary().tables[1]
添加 rfm 后,电子邮件对销售产生了积极的影响,电子邮件变量也达到了 95%的显著水平。因此,控制具有高预测能力的变量有助于捕捉方差,并提供更可靠的因果估计。基本上,在上面的例子中,当我们观察相似的客户(rfm 得分水平相似)时,结果变量的方差较小。
总结案例 1 和案例 2,我们应该始终控制在我们的因果设计中具有强大预测能力的混杂因素和变量。
良好的控制(图片由作者提供)
案例 3:不良控制导致选择偏差
在数据繁荣的时代,我们不缺少可以直接放入模型的变量。我们在上面讨论过,我们不应该把所有的变量都加到模型中,否则我们会引入另一种偏差,称为选择偏差。
假设,我们想知道大学学历对工资的影响。我们设法随机分配了大学学位(大学教育 1 分,其他情况下 0 分)。如果我们现在在设计中控制白领工作,我们就引入了选择偏差。我们关闭了大学教育影响工资的前门路径。
model = smf.ols('wage ~ educ', data=df).fit()
model.summary().tables[1]
大学教育对工资有积极影响,受过大学教育的人平均比没有受过大学教育的人多挣 0.46 个单位。
model = smf.ols('wage ~ educ + whitecollar', data=df).fit()
model.summary().tables[1]
对白领工作的控制导致了选择偏差,关闭了大学学位影响工资的渠道之一。这导致低估了大学学位对教育的影响。由于随机化的原因,我们预计 E[Y₀|T=1]-E[Y₀|T=0] =0,然而在控制了白领工作 E[Y₀|T=0,WC=1] > E[Y₀|T=1,WC=1]之后,也就是说,即使没有大学学位也有白领工作的人可能比那些需要大学学位的人更勤奋。
回归分析在控制混杂因素方面非常有效,而且有点神奇。其方法是将数据划分为混杂单元格,计算每个单元格中的影响,并使用加权平均值合并它们,其中权重是单元格中处理的方差。
但是每个人都有自己的问题,回归也是如此。它不适用于非线性,并且由于它使用组中治疗的方差作为权重,ATE 受高方差因子的影响更大。但是这不是我们所拥有的唯一工具,还有其他方法都旨在使治疗组和对照组相似,我们将在本系列的第 2 部分讨论这些方法。
参考文献
- 【https://matheusfacure.github.io/python-causality-handbook/
- https://www.bradyneal.com/causal-inference-course
- https://theeffectbook.net/
- https://www.masteringmetrics.com/
- https://www . Amazon . in/Applied-Data-Science-Transforming-Actionable/DP/0135258529
Python Pandas 中的数据清理入门
原文:https://towardsdatascience.com/getting-started-with-data-cleaning-in-python-pandas-76d977f95b57
数据清理
使用流行的 Python 库执行数据清理的实际例子。
Mick Haupt 在 Unsplash 上拍摄的照片
数据清理是处理数据时必不可少的步骤之一。事实上,在大多数情况下,您的数据集是脏的,因为它可能包含丢失的值、重复的值、错误的格式等等。在没有清理数据的情况下运行数据分析可能会导致错误的结果,并且在大多数情况下,您甚至无法训练您的模型。
为了说明执行数据清理所需的步骤,我使用了一个非常有趣的数据集,由 Open Africa 提供,其中包含维多利亚湖 4 个子区域的历史和预测降雨量和径流量。该数据集在知识共享许可下发布,可通过此链接获得。我发现这个数据集非常有趣,因为虽然它很小,但它是故意脏的,因此它可以用来说明如何执行数据清理。
所以,我们继续吧!
文章组织如下:
- 加载数据集
- 缺少值
- 数据标准化
- 初步探索性数据分析。
1 加载数据集
数据集以 Excel 文件的形式提供,所以我通过read_excel()
函数加载它。为了正常工作,这个函数需要安装openpyxl
库。要安装它,你可以运行pip install openpyxl
。
import pandas as pddf = pd.read_excel('source/rainfall.xlsx')
df.head()
作者图片
数据集未正确加载,因为列名不正确。因此,我需要再次读取数据集,跳过前两行:
df = pd.read_excel('source/rainfall.xlsx', skiprows=2)
df.head()
作者图片
还有一些额外的列,我可以通过usecols
参数删除它们:
df = pd.read_excel('source/rainfall.xlsx', skiprows=2, usecols='B:D')
df.head(20)
作者图片
2 个缺失值
有两排完全空着。我可以通过dropna()
功能放下它们:
df.dropna(inplace=True, axis=0)
df.head(20)
作者图片
3 数据标准化
第一列包含两列。我使用split()
功能将它们分开。
splitted_columns = df['Month, period'].str.split(',',expand=True)
splitted_columns
作者图片
现在,我将每个新列分配给原始数据帧中的一个新列:
df['Month'] = splitted_columns[0]
df['Period'] = splitted_columns[1]
df.head(15)
作者图片
我放下Month,period
栏:
df.drop('Month, period', axis=1, inplace=True)
df.head(15)
作者图片
有些列包含字符串mm
,所以我定义了一个函数来消除它。
def remove_mm(x):
if type(x) is str:
return x.replace('mm', '')
else:
return x
我将前面的函数应用于列Lake Victoria
和Simiyu
:
df['Lake Victoria'] = df['Lake Victoria'].apply(lambda x: remove_mm(x))
df['Simiyu'] = df['Simiyu'].apply(lambda x: remove_mm(x))
df.head(20)
作者图片
我描述了每一列的类型
df.dtypes
作者图片
Lake Victoria
和Simiyu
列应该是浮动的。所以我把它们转换成浮点数:
df["Lake Victoria"] = pd.to_numeric(df["Lake Victoria"])
df["Simiyu"] = pd.to_numeric(df["Simiyu"])
df.dtypes
作者图片
4 初步探索性数据分析
为了执行探索性数据分析(EDA),我使用 pandas profiling 库。我可以这样安装它:
pip install pandas-profiling
然后,我用它来构建一个报告:
from pandas_profiling import ProfileReportprofile = ProfileReport(df, title="rainfall")
profile.to_file("rainfall.html")
该库构建一个 HTML 文件,其中包含每一列的所有统计数据,并计算每对数字列之间的相关性。下图显示了生成的报告的示例:
作者图片
摘要
恭喜你!您刚刚清理了 Python Pandas 中的第一个数据集!过程简单快捷!
您可以从我的 Github 库下载本文使用的代码。
如果你读到这里,对我来说,今天已经很多了。谢谢!你可以在这个链接阅读我的趋势文章。
相关文章
你知道 scikit-learn 也提供数据清理功能吗?
阅读这篇文章来学习如何处理 scikit-learn 中的缺失值。
数据湖入门
原文:https://towardsdatascience.com/getting-started-with-data-lakes-6761e0317442
数据仓库补充背后的想法和解释
照片由 Aaron Burden 在 Unsplash
数据湖以原始格式存储大量来自不同来源的数据,以使它们可用于大数据分析。这些数据可以是结构化的、半结构化的或非结构化的。这些信息一直存储在那里,直到需要进行分析评估。
为什么公司要使用数据湖?
存储在数据湖中的数据可以是结构化的、半结构化的或非结构化的。这种数据格式不适合关系数据库,而关系数据库是许多 T4 数据仓库的基础。
此外,关系数据库也不是水平可伸缩的,这意味着存储在数据仓库中的数据在超过某个级别后会变得非常昂贵。水平可伸缩性意味着系统可以在几台不同的计算机之间进行划分,因此负载不会落在一台计算机上。这样做的好处是,在压力很大的情况下,可以根据需要临时添加新系统,例如有许多同时进行的查询。
根据定义,关系数据库不能在许多计算机之间划分数据集,否则,数据一致性将会丧失。因此,在数据仓库中存储大量数据是非常昂贵的。大多数用于数据湖的系统,如 NoSQL 数据库或 Hadoop ,都是水平可伸缩的,因此可以更容易地伸缩。这就是为什么数据湖和数据仓库是许多公司数据架构的基石之一的主要原因。
体系结构
数据湖的具体实现因公司而异。但是,有一些基本原则应该适用于数据湖的数据架构:
- 可以包含所有数据。基本上,来自源系统的所有信息都可以加载到数据湖中。
- 不必首先处理数据,但可以以原始形式加载和存储数据。
- 只有在特殊的用例对数据有需求时,才会处理和准备数据。这个过程也称为模式读取。
否则,不管系统如何,储存时仍应考虑一些基本要点:
- 具有统一命名约定的通用文件夹结构,以便可以快速轻松地找到数据。
- 创建数据目录,命名文件的来源并简要说明各个数据。
- 数据筛选工具,这样就可以很快发现数据质量有多好,以及是否有任何缺失值。
- 标准化的数据访问,以便可以分配授权,并明确谁可以访问数据。
数据仓库的差异
数据仓库还可以通过数据湖来补充,非结构化的原始数据以较低的成本临时存储在数据湖中,以便以后使用。这两个概念的主要区别在于它们存储的数据和存储信息的方式。
数据仓库和数据湖的比较|图片:作者
这是你应该带走的东西
- 数据湖是指一个大型数据存储,它以原始格式存储来自源系统的数据,以便供以后分析使用。
- 它可以存储结构化、半结构化和非结构化数据。
- 它从根本上不同于数据仓库,因为它存储未处理的数据,并且直到有特定的用例时才准备数据。
- 这主要是数据保留的问题。
如果你喜欢我的作品,请在这里订阅https://medium.com/subscribe/@niklas_lang或者查看我的网站* 数据大本营 !还有,medium 允许你每月免费阅读 3 篇 。如果你希望有无限制的 访问我的文章和数以千计的精彩文章,请不要犹豫,点击我的推荐链接:【https://medium.com/@niklas_lang/membership】每月花$5***获得会员资格**
* https://medium.com/@niklas_lang/membership
最初发布于https://database camp . de*。**
在 AWS 中开始使用 Delta Lake & Spark 简单的方法
在 AWS 中配置 EC2 上的 Apache Spark 和 Delta Lake 的分步教程,以及 Python 中的代码示例
约书亚·索蒂诺在 Unsplash 上拍摄的照片
如果您从事过数据湖或湖屋解决方案的工程设计,那么您可能已经针对数据湖平台的可伸缩存储层采用了(或听说过)解耦和分布式计算框架。尽管这种计算框架的列表在不断增长,但 Apache Spark 一直在不断发展,并证明了它在大数据处理领域的稳健性。随着 Apache Spark 越来越成功,许多供应商也在提供各种风格的基于 Apache Spark 的解决方案(如 Databricks、Amazon EMR)。此外,在解决现有数据湖解决方案的酸性限制方面出现了激增,在这种情况下,您可能听说过像 D 埃尔塔湖、胡迪、冰山这样的解决方案。所有这些看起来都很吸引人,但是对于初学者来说,有时会有点不知所措。有一种可能性是,您可能希望从小处着手,在 ACID 兼容的数据湖中利用 Apache Spark 的潜力(例如,通过在 AWS 中的 VM (EC2)中正确配置它)。或者,您可能希望解决特定的用例,这些用例不需要基于供应商的产品,或者不需要大量实例来进行大规模分布式处理。此类特定用例的一些示例可能是:
- 您发现您现有的 ETL 过程没有类似 SQL 的处理能力。例如,假设您正在使用 Python,您主要依赖 Python 原生数据结构(如列表、字典)及其模块(如 csv、pandas)来实现所需的转换。您认为,能够运行 SQL 表达式并与更抽象和可扩展的数据结构(如 dataframes)互换以实现您想要的转换,可以加速和简化您的开发,例如,而不是求助于多行代码来从登录目录读取数据并根据列的值将其拆分到多个目标目录;您可以通过 Spark SQL 中的几行代码方便地实现它。
- 你发现 S3 选择是相当有限的,并且想要一个更好的解决方案来运行除雅典娜之外的 S3 上的 SQL。
- 如果您的数据速度接近实时,并且在任何时间点,正在处理的数据量可以在资源范围内,即虚拟机的 CPU 和 RAM,因此它不需要分布式计算。
- 您希望构建基于 Apache Spark 和 Delta Lake 的解决方案原型,通过从小处着手(即在您选择的 EC2 实例中运行)来实现其业务价值。您不希望为您的初始原型运行一个成熟的基于供应商的解决方案。如果你想学习这些新技术,同样的启发法也适用。
如果以上任何一个条款引起了您的共鸣,那么您可能会发现这篇文章非常有帮助,因为它以简单/容易的方式解释了如何在 AWS 中的 EC2 实例上开始使用 Apache Spark 和 Delta Lake。
先决条件
要跟进,您将需要以下内容:
- AWS 帐户
- EC2 实例(可以是任何大小,但建议至少是具有 2 个 vCPUs 的实例)配置如下:
- Python(理想情况下是> 3.8)(可选地配置了虚拟环境)
- JDK(理想情况下是 Oracle 的,但 OpenJDK 也工作得很好。我特别发现 Oracle JDK 11 对于 Apache Spark 3 来说相当可靠
- S3 桶(您将在那里为您的三角洲湖水合/存储数据)
- 附加到 EC2 实例的 IAM 角色,允许对 S3 存储桶进行读/写操作
步伐
第一步是在您的(虚拟)环境中安装 PySpark。在撰写本文时,我发现 pyspark 3.2.2 在与 Delta Lake dependencies 结合使用时相当稳定。所以我将在本文中使用它。
如果您使用 pip 在您的环境中安装依赖项,请运行以下命令:
pip install pyspark==3.2.2
如果一切顺利,PySpark 模块将安装在您的 Python 环境中!现在是时候使用它了。
下面是代码片段中发生的事情的概要分析:
- 第 1 行—我们从 pyspark.sql 模块导入 SparkSession 类
- 第 2 行—我们指定 Spark 工作所需的依赖关系,例如允许 Spark 与 AWS(在我们的例子中为 S3)交互、使用 Delta Lake 核心等
- 第 3 行——我们实例化了 SparkSession 对象,该对象标记为在脚本中使用 Spark 的入口点。在创建对象时发生了很多事情,您可能希望仔细阅读 spark 文档以了解每一个的基本原理,但有几个是关键的:
- spark.sql.extensions:扩展 Spark 会话对象以使用 Delta Lake 功能
- spark.jars.packages:以逗号分隔的 jar Maven 坐标列表以包含在驱动程序和执行器类路径中
-Spark . SQL . sources . partitionoverwritemode:允许更好地/动态写入分区数据,即不删除现有分区,只插入当前数据帧中的分区
提示:如果您使用的是公司 HTTP/HTTPS 代理,您可以添加以下配置来允许遍历代理,从而允许 Spark 从 Maven repo 下载指定的 jars 包:
.config("spark.driver.extraJavaOptions", "-Duser.timezone=UTC -Dhttp.proxyHost=proxy_hostname -Dhttp.proxyPort=port -Dhttps.proxyHost=proxy_hostname -Dhttps.proxyPort=port")
一旦完成,Spark 会话对象就可以使用了。
从 S3 读取数据
让我们使用一个样本数据集。对于本文,我选择了来自新西兰统计局网站的就业数据(请参考本文末尾关于数据集许可方面的参考资料部分)。
我也将 CSV 文件上传到我的帐户中的一个桶中。
以下是如何在 Spark 中读取带头文件的 CSV 文件:
df = spark.read.option("recursiveFileLookup", "true").option("header","true").csv("s3://your_bucket/test/machine-readable-business-employment-data-mar-2022-quarter.csv")
需要注意的是,由于 Spark 遵循惰性执行模型,所以在这个阶段它不会在内存中加载任何数据。仅当您在 Spark 中执行某个操作时(例如。count(),。show()函数),然后它会将所需的数据加载到内存中进行处理。
让我们先看一下数据是什么样子的:
df.show()
输出如下所示:
基于 SQL 的数据转换
Spark 最强大的特性之一是能够对数据运行 SQL 查询。这大大加速和简化了 ETL,因为您可以通过 SQL 快速表达非常强大的数据转换逻辑,而不是编写冗长/复杂的代码来实现相同的功能。
要运行 SQL 查询,您需要在数据上创建一个“临时视图”。此视图未注册到集成目录/hive-metastore(例如 Glue ),并且将仅位于当前会话的本地。
要创建临时视图:
df.createOrReplaceTempView("employment_tbl")
完成后,您可以对 S3 上的数据运行 SQL 查询。你可以通过使用 spark.sql()函数来完成:
spark.sql("select Series_title_2,count(*) as count from employment_tbl group by Series_title_2 order by 2 desc").show(truncate=False)
输出如下所示:
在这里,我们运行一个 group by SQL 查询来计算唯一值在 series_title_2 列中出现的次数。
您还可以根据以下转换逻辑创建新的数据帧:
df_groupby = spark.sql("select Series_title_2,count(*) as count from employment_tbl group by Series_title_2 order by 2 desc")
将增量格式的数据写入 S3
让我们将这个转换后的数据帧以 delta 格式写入 S3,以实现我们的“delta-lake”
df_groupby.write.format("delta").save("s3://your_bucket/test/sample_data/")
完成后,您可以从 S3(例如通过管理控制台)验证数据是否以增量格式写入:
如果你想在 Spark 中读取 delta 格式的数据:
df = spark.read.format("delta").load("s3://your_bucket/test/sample_data/")
如果你想看数据:
df.show()
这是它看起来的样子:
对数据执行更新
像 Delta Lake 这样的技术的独特主张之一是能够对 data-lake 上的数据执行符合 ACID 的更新/删除。传统上,在受 Hadoop 影响的大数据范式中,这是一个严重的限制,数据工程师不得不求助于各种变通办法来实现数据集中所需的更新/删除效果。但是现在,这个功能在 Delta Lake(和胡迪的 Iceberg)中得到了原生支持。
假设您想要删除 series_title_2 = '其他服务'的所有记录。您可以这样做的方法之一如下。
在上面的代码片段中,我们在表数据以增量格式存储的路径上创建了对增量表的引用。然后,我们指定了如何更新数据的逻辑,即将 Series_title_2 列值从“其他服务”更改为“其他”。执行时,这将直接将更新应用于 S3 上的底层增量文件。
为了验证更新是否发生,让我们读回 dataframe 中的数据并运行一些 SQL 查询:
df = spark.read.format("delta").load("s3://your_bucket/test/delta_format/")df.createOrReplaceTempView("employment_tbl")spark.sql("select count(*) from employment_tbl where Series_title_2 = 'Other Services'").show()
这个计数是零:
让我们运行另一个执行 Group By 的 SQL 查询来验证“Other Services”不存在,并且已被替换为“Others”:
如您所见,数据帧中倒数第四个条目是“Others”,如果您将它与我们之前运行的 group by query 的输出进行比较,您会发现它是“Other Services”。
使用类似的方法,您也可以删除数据。此外,我没有使用 SQL 表达式来执行更新和删除,但是您也可以轻松地做到这一点。
因此,如果您一直遵循本文,那么您已经完成了整个工作流程,即读取原始数据(CSV 格式)、进行一些转换、以 delta 格式写入数据、更新数据,然后再次读取 delta 格式的数据。这是通过 Spark 可以实现的所有高级转换的基础。
值得指出的是,本文中的方法确实有一些限制:
- 该设置未与集中式 Hive Metastore 或目录(如 Glue)集成。因此,如果您运行 CREATE TABLE 语句,这些语句将不会在中央目录中注册,因此其他 Spark 实例或工具(如 Athena)将无法访问这些表。虽然你可以自己配置与 Glue catalog 集成,但是在这种情况下,EMR 确实提供了一些好处,因为它提供了与 Glue 的开箱即用集成。
- 该设置不是分布式的,即它不利用多个虚拟机来执行分布式计算。
符合 ACID 标准的 datalake 解决方案,如 Delta Lake 和胡迪等,以及像 Databricks 这样提供基于它的商业解决方案的相关公司,在数据行业正获得相当好的发展势头,因此了解这项技术将是一项很好的投资。最好的学习方法是通过实践,如果你发现了一个有价值的用例,就从小处着手,做实验,打破常规,构建东西,然后进化!
参考
- 三角洲湖泊:https://delta.io/
- 文章中使用的数据集:https://stats . govt . NZ/assets/Uploads/Business-employment-data/Business-employment-data-March-2022-quarter/Download-data/Business-employment-data-March-2022-quarter-CSV . zip
(根据知识共享 4.0 国际许可授权重用)
嵌入入门比您想象的要容易
原文:https://towardsdatascience.com/getting-started-with-embeddings-is-easier-than-you-think-e88b7b10bed1
作者图片
理解嵌入的快速指南,包括真实世界的应用和如何计算它们
与 Arize AI 的数据科学家Francisco Castillo carr ASCO合作撰写。
想象一下,你是一家有前途的聊天机器人初创公司的工程师,致力于帮助人们快速找到他们需要的医疗服务。客户和医务人员之间有数百万次聊天互动。您正在构建一个模型,将问题发送到医院和诊所的不同部门。由于机器学习是数学,而不是魔术,你需要以某种方式向你的模型“解释”,“扭伤的脚踝”和“肿胀的脚”可能是类似的查询。你是怎么做到的?
输入嵌入。
什么是嵌入?
嵌入是数据的矢量(数学)表示,其中线性距离捕获原始数据集中的结构。
这个数据可以由单词组成,就像上面描述的场景一样,在这种情况下,我们称之为单词嵌入。但是嵌入也可以表示图像、音频信号,甚至是大块的结构化数据。
嵌入在现代深度学习中无处不在,例如变压器、推荐引擎、SVD 矩阵分解、深度神经网络层、编码器和解码器。
嵌入是基础性的,因为:
- 它们为您的数据提供了一种通用的数学表示
- 他们压缩你的数据
- 它们保留了数据中的关系
- 它们是深度学习层的输出,为模型学习的复杂非线性关系提供可理解的线性视图
我们来看一个经典的琐碎例子。如果您的数据集只有四个词——女王、国王、公主和王子——您可以使用一个带有三维稀疏向量的热编码。这意味着您需要三列,其中大部分包含零。
作者图片
然而,如果你观察这些单词,它们在两个方面有所不同:年龄和性别。因此,您可以将数据表示如下:
作者图片
你不仅去掉了一个专栏,还保留了有价值的信息。很明显,在这样一个简单的例子中,你不会得到太多——但是如果你的语料库包含了英语中的每一个单词呢?如果它包含了每种语言的每一个单词呢?后一种表示显然不可能手工构建,但如果它存在的话,它将是非常有价值的。坚持这个想法。
在实践中,嵌入并没有真正给你 1 和 0,而是介于两者之间的东西,并且逐列解释它们要困难得多(如果不是不可能的话)。尽管如此,重要的信息可以以这种紧凑的形式保存下来。
经典例子,作者图片
嵌入现实世界
推荐系统
可以说今天推动最大商业价值的机器学习产品之一是推荐系统。从如何保持用户参与到推荐什么产品到什么新闻可能与你相关,推荐系统无处不在。推荐系统的一种常见方法是协同过滤(例如,与你品味相似的人喜欢什么?).现代推荐系统中的协同过滤几乎总是使用嵌入技术。因此,许多数据科学家第一次接触嵌入是在推荐系统中。
几年前,嵌入也开始出现在其他类型的商业模型中,比如最初的 Word2vec。Word2vec 中生成嵌入的方式明显不同于推荐系统中使用的矩阵分解方法;它们基于将单词关系训练成线性向量。Word2vec 拒绝许多团队去确定关系可以超越文字多远,以及嵌入可以表示什么关系。快进到今天,变形金刚——许多现代人工智能技艺背后的魔法——可以被视为一个复杂的概率调整嵌入层次。
简而言之,嵌入在现代人工智能中无处不在。除了无处不在之外,将数据表示为嵌入还有另一个优点:它可以作为模型、团队甚至组织之间的接口。
这里有几个其他的例子来说明嵌入在现实世界中的应用。
无人驾驶汽车
使用嵌入式的另一个重要且具有挑战性的问题是自动驾驶汽车。假设你的团队正在训练汽车刹车系统的模型。你想要的一个重要的模型特征是“停止标志”考虑到这一点,你在你所在区域的一堆停车标志上进行训练,但不幸的是在现实世界中你可能会遇到不同语言甚至不同形状的停车标志。不用担心这个就好了。幸运的是,贵公司的另一个团队有一个嵌入的停止标志供您使用。
现在,您可以专注于问题的一部分,而另一个团队可以负责交通标志嵌入,并作为输入提供给您。嵌入成为模型之间的接口,就像不同微服务之间的 REST 接口。您可能需要在维度上达成一致,但除此之外,下游模型可能是一个黑盒。
图片作者:艾瑞泽·艾
文件分类
如果你花时间关注机器学习的最新发展,其中许多都围绕着自然语言处理。这些任务可以包括翻译、情感分析、主题建模、摘要等等。该领域最近爆发的核心是一种特殊类型的神经网络,称为变压器。今天的变压器在整个架构的多个地方使用嵌入,包括输入和输出。就数学数据而言,语言是非结构化的,因此为嵌入技术的发展提供了绝佳的机会。甚至更简单的架构也依赖嵌入来表示输入数据。
因为根据定义,嵌入更紧凑地表示数据,所以它们也可以用于压缩目的。例如,ImageNet 是 150GB。如果嵌入可以帮助你用 1/50 的空间来表示它,那会使你的许多任务更简单。
嵌入的核心价值是线性关系,如距离、投影、平均值、加法和减法在向量空间中都有意义。非常简单的线性操作可以提供很多价值。点积可以告诉你两个项目有多相似。不同城市的平均值可以创建一个平均“城市”的代表向量投影可以显示一个概念如何与另一个概念相关联。
你如何计算嵌入?
今天,关于如何创建嵌入的许多讨论都围绕着深度神经网络(DNN)。由于 DNN 训练的嵌入在业界如此普遍,这篇文章将主要关注它们。然而,需要指出的是,生成嵌入并不需要 DNN。比如 GloVe 就是一个很重要的不使用 DNNs 的单词嵌入。
奇异值分解(SVD)和主成分分析(PCA)是获得不依赖于神经网络的嵌入的常用方法。这两种方法都来自于降维和矩阵分解技术家族,可以有效地处理大量数据。
假设有许多模型架构应用于不同的用例,有许多方法可以从 DNN 模型中提取嵌入向量。让我们来看一种方法。
假设您正在寻找一个用于翻译系统的单词嵌入。对于这项技术来说,这是一个非常自然的应用,因为例如,英语中的“cat”和西班牙语中的“gato”之间的相似性可能会被保留下来。您可以在多种语言的大量文本上训练一个 transformer 模型。
该架构可能非常复杂,也可能非常简单,但让我们假设一个输入层(编码器)、许多前馈形式的隐藏层和一个输出层(解码器)。暂且忽略变形金刚的位置注意力,当你的网络在某个单词位置看到“猫”时,它在相关的隐藏层上有一组激活值——当它看到“狗”时,它有另一组激活值。太好了!那是你的嵌入。您可以简单地获取该层的激活值。
这当然只是表现猫的一种方式。其他可能包括:
- 取最后 N 层的激活值的平均值
- 取单词位置嵌入的平均值来创建上下文
- 从编码器和解码器中提取嵌入
- 仅取第一层值
模型架构和您选择的方法将影响模型维度(一个向量有多少个值)以及层次信息。由于维数取决于作为机器学习实践者的你,你可能想知道:嵌入应该有多少维?
有时模型和架构已经是板上钉钉的了,你所做的只是提取一个嵌入层来理解内部。在这种情况下,你的维度已经为你设计好了。然而,如果您构建模型本身来生成嵌入,则存在一个很大的设计权衡。
参数越少,嵌入操作就越简单,在下游也越有用,但是参数太少可能会错过值得保存的重要信息。另一方面,原始数据大小的嵌入不是嵌入!您选择保留的每个维度都会损失一些压缩优势。
保持嵌入越大的另一个好处是:嵌入越大,可以使用的距离度量就越简单。更复杂的距离度量通常难以描述和理解。这是嵌入通常使用几百到几千个参数的主要原因之一。
结论
嵌入是高维数据的密集、低维表示。它们是输入数据表示、压缩和跨团队协作的极其强大的工具。
虽然有许多方法可以获得这样的表示,但是作为一名工程师,你必须注意你产生的表示的大小、准确性和可用性。这就像机器学习中的许多其他任务一样,是一个迭代问题,正确的版本控制既具有挑战性,也是必不可少的。
尽管嵌入极大地降低了输入特征的维数,但是如果不通过像 UMAP 这样的技术进一步降低维数,它们仍然是难以理解的。敬请关注本内容系列中关于可视化和故障诊断嵌入的最佳实践的下一次更新。
联系我们
如果这个博客引起了你的注意,并且你渴望了解更多关于机器学习可观察性和模型监控,请查看我们其他的博客和 ML 监控上的资源!如果您有兴趣加入一个有趣的 rockstar 工程团队来帮助模型成功生产,请随时联系我们,注册一个免费帐户,或者在这里找到我们的空缺职位!
Python 中的 Google APIs 入门
原文:https://towardsdatascience.com/getting-started-with-google-apis-in-python-7f07b5d8b806
使用 Python 和 Google API 自动完成 Gmail、Google Drive、Calendar 或任何其他 Google API 的速成课程
产生稳定的扩散
谷歌有数百个 API,包括 Gmail、Drive、地图、翻译、分析等等。所有这些共享相同的概念,如授权、分页或媒体上传/下载。在本文中,我们将探索所有这些概念,并尝试使用上述一些 API,了解您可以使用任何或所有 Google API 做的所有很酷的事情。
安装
为了能够访问 API,我们需要首先在 Google Cloud 中创建一个项目。为此,请前往云资源管理器并点击创建项目。或者,您也可以通过 CLI 使用gcloud projects create $PROJECT_ID
来完成。几秒钟后,你会在列表中看到新项目。
接下来,我们需要为这个项目启用 API。你可以在 API 库中找到所有可用的 API。我们将在接下来的部分中使用的功能包括 Gmail、Drive 和 Forms,但是您可以选择您想要启用的功能。
启用 Google API
API 现在可以使用了,但是我们需要凭证来访问它。根据应用程序的类型,有几种不同类型的凭据。它们中的大多数适合于需要/要求用户同意的应用,例如客户端(桌面)或 Android/iOS 应用。对我们来说,更好的凭证类型是服务帐户,因为它适合自动化——也就是说——无需用户交互即可访问。服务帐户凭证然而,不能与许多 API 一起工作——例如,照片 API 不支持它,并且所有 Google Workspace(g suite)API(例如,Gmail、Sheets、Docs…)仅在您拥有自己的带有域范围访问委托的工作空间时才允许它。因此,我们唯一的选择是使用 OAuth 凭证。
要创建 OAuth 凭证,请转到凭证页面,点击创建凭证并选择 OAuth 客户端 ID 。您将被要求配置 OAuth 同意屏幕,然后创建它——在第一个屏幕中,您将被要求选择用户类型,我们将选择外部。
在下一个屏幕中,您将在开发者联系信息下指定应用名称、用户支持电子邮件和电子邮件地址。之后您可以点击保存并继续,跳过范围部分。在测试用户部分,点击添加用户并添加您将用于测试的 Google 帐户的电子邮件地址(您可能需要为此创建额外的 Google 帐户),点击保存并再次继续。总之,配置应该如下所示:
Google 同意屏幕摘要 1
Google 同意屏幕摘要 2
现在我们需要返回到凭证页面点击创建凭证并选择 OAuth 客户端 ID 。接下来,我们将选择应用程序类型作为桌面应用程序,并为其命名:
Google API OAuth 应用程序
您将看到客户端 ID 和客户端密码——单击下载 JSON 按钮,以名称credentials.json
保存它们——我们稍后会用到它们。
完成后,我们现在有了启用 API 的项目和 OAuth 申请/同意屏幕,这将允许我们授权访问我们选择的 API 和范围。
最后,我们需要安装 Python 客户端库来使用 API。我们需要两个,一个用于认证,一个用于实际的 Google APIs:
访问 API
现在我们准备开始编写一些 Python 代码。我们需要做的第一件事是进行身份验证并获得用户同意:
我们从定义访问 API 的访问范围开始。要找到你需要的,在特定的 API 文档页面中搜索“范围”,第一个结果应该会导致类似于 Gmail 或照片的页面。选择您需要的所有范围,并将它们添加到SCOPES
列表中。这里我们选择gmail.readonly
进行测试。
接下来,我们检查token.json
是否存在,它是一个存储用户访问和刷新令牌的文件。该文件是在授权流程首次完成时自动创建的。如果文件在那里,但是它存储的凭证无效,我们尝试刷新它们。如果文件不存在,我们让用户通过 OAuth 流登录。这将打开浏览器选项卡,要求您登录。在这里,您应该使用您在 OAuth 屏幕设置期间指定的测试用户。
在接受应用程序请求的作用域后,将创建token.json
文件。当您再次运行此身份验证代码时,将使用该令牌(假设它没有过期),因此您不必再次在浏览器中批准它。
如果您决定在某个时候改变作用域,一定要删除token.json
,因为应用程序需要重新授权。
基础知识
Google API Python 客户端库包含了所有 Google API 的函数,这使得它变得非常复杂和难以导航。大部分也是生成的代码(包括文档),这对用户体验没有帮助。现在让我们看看所有各种 Google APIs 的基本模式,它们可以帮助我们更好地理解客户端库,从而更有效地导航和使用它。
无论您选择使用哪种 API,它们都有一个共同点——创建 API 对象和发出请求的方式:
无论你想调用 Gmail API 还是任何其他 API,你总是会通过提供 API 名称、版本和先前创建的凭证,从googleapiclient.discovery
使用build
函数来【构建】服务。然后,使用返回的service
对象,您可以调用该 API 的任何函数。因为这是生成的代码,你不会得到自动完成,所以最好的方法是找到你的选项,是去文档页面。URL 有点难找到,不过一般来说,你可以浏览https://Google apis . github . io/Google-API-python-client/docs/dyn/来找到你需要的 API 组和方法。例如,对于上面的service.users().messages()
,你可以从转到。
最后,不要忘记调用.execute()
方法,否则得不到任何真正的响应。说到这里,响应将只是 Python dictionary 形式的 JSON,您必须遍历它以获得您想要的数据——要了解您可以预期的字段,请查看上面生成的文档,其中包括示例响应对象。
当您需要弄清楚单个方法的各个参数时,基于上述 API 文档构建方法链会很好地工作,但是当您试图导航或学习一般的 API 选项时,可能不太方便。为此,你最好搜索 REST API 参考,比如 Gmail的参考。浏览一下,您会很快注意到它遵循了与 Python 方法链(.users().messages().list
)相同的结构:
Gmail API 参考
踢轮胎
既然我们知道了如何导航 API 并调用它的端点,那么让我们试试它的轮胎,看看各种各样的 Google APIs 能为我们做些什么。
我们已经介绍了 Gmail API,所以在这里,我们可以看看它的更多方法:
上面的片段显示了我们如何从登录的帐户发送电子邮件。这是通过创建一个消息对象来实现的,该对象随后被 base64 编码,并使用body
参数传递给.send(...)
。还要注意,我们使用了"me"
作为userId
的值——这是一个特殊的值,用于标识当前登录的用户。
假设您有一堆带有附件的电子邮件,您希望提取这些附件以进行备份或进一步处理。您可以使用 Gmail API 抓取这些附件:
这里我们假设您已经有了一些消息及其 ID。我们使用这个message_info
来查询消息对象的 API。然后,我们遍历消息有效负载的各个部分,寻找引用附件的部分——也就是具有非空filename
属性的部分。然后,我们使用在消息部分找到的 ID 向 API 请求每个附件。返回的附件数据是 base64 编码的,这里我们假设它是明文数据,但也可能是 PDF、图像等。
Gmail API 是最广泛的 API 之一,人们可以用它做更多的事情,所以请确保查看它的 API 参考。
从 Gmail 继续,另一个受欢迎的谷歌服务是 Drive ,让我们看看如何从它上面搜索和下载文件:
我们使用.files().list()
方法来搜索文件。为了便于搜索,我们在q
参数中指定了一个查询,其格式为query_term operator values
——在本例中——mimeType
为query_term
, =
为operator
,'image/png'
为values
。这个查询确保我们只接收 MIME 类型为image/png
的文件。您可以构建许多查询,在文档中有一个完整的列表。在我们的搜索中,我们还指定了fields
参数,它告诉 API 只返回文件的 ID 和名称,而不是完整的有效负载。
从 API 获得文件列表后,我们使用.files().get_media
获得文件元数据,然后使用该信息创建downloader
对象,用它下载实际文件。最后,我们将下载的数据写入一个文件。
除了管理文件,使用其他 API 时也需要 Google Drive API。例如,如果您想访问您的 Google 表单,您需要使用 Drive API 来搜索它们:
为了搜索表单,我们使用与搜索图像时相同的.files().list
方法,唯一的不同是我们改变了q
参数来搜索 Google 表单。
然后,我们可以使用返回的信息来处理我们的表单,例如获取所有表单响应。注意,在这个例子中,我们将static_discovery
指定为False
,这是因为目前静态发现文档不是 Python 客户端库的一部分,因此我们需要从互联网上获取它,否则我们会得到UnknownApiNameOrVersion
异常。
上面的例子还演示了在初始调用list
后使用list_next
进行分页。这很方便,因为我们不必处理存储在响应对象中的“下一页令牌”,相反,我们只需将请求和响应对象传递给list_next
方法,让库来处理剩下的部分。
另一个你可能想要检查的 API 是照片 API ,然而这个需要更多的努力来使它工作:
与 Forms API 一样,对于 Photos API,我们也需要将static_discovery
指定为False
。然而,这个问题更多——如果你试图在前面提到的生成的 API 参考中寻找它,它根本不存在。这是因为它还没有像这个 GitHub 问题中提到的那样发布,所以在此期间,如果我们想看看有哪些方法可供我们使用,我们需要通过如上所示的私有属性做一点挖掘。
虽然不太方便,但该 API 仍可用于——例如——将图像上传到相册:
我们首先创建一个样本相册,我们将在其中上传我们的图像—这是必要的,因为您只允许将图像上传到您的应用程序创建的相册。之后,我们将上传图像,这是一个两步过程—首先我们必须上传图像字节,然后我们将上传的数据与图像相关联。
第一步,我们必须使用 raw REST API,将图像数据发布到 Photos API uploads
端点。之后,我们使用相册 ID、图像名称和从原始数据上传返回的上传令牌构建一个请求体,然后使用batchCreate
方法将其发送给 API。在这个例子中,我们只上传了一个图像,但是您可以使用一个batchCreate
调用上传多个图像,同时将多个项目传递给请求体中的newMediaItems
条目。
回到 Google Workspace APIs,让我们看看日历 API。假设我们希望在日历中创建新活动时收到通知:
从 Calendar API 的角度来看,我们需要做的就是建立一个事件将要到来的通道。我们通过使用包含 ID、地址和通道到期(生存时间)的主体调用.events().watch()
来实现这一点。这里的地址必须是互联网上的一个 HTTPS 网站(不是localhost
)。在开发过程中,最简单的设置方法是使用像 ngrok 这样的服务,它会将请求转发给你的localhost
。
在设置了转发(使用 ngrok 或任何其他工具)之后,您应该开始看到 HTTP POST 请求进来,您可以在其中开始处理。示例请求可能如下所示:
我们要看的最后一个 API 是事实检查工具 API ,我们可以用它来验证/反驳几乎任何事情:
这个和以前的没什么不同。以前,我们只使用 OAuth 进行身份验证,但是事实检查工具 API 是一个公共 API,不需要访问/授权用户数据,因此可以使用基本的 API 密钥身份验证。要获得谷歌云 API 密匙,请点击这里的指南。
我们在上面的例子中还演示了一个新的东西,那就是使用批处理请求——而不是直接单独调用.claims().search()
,这里我们使用new_batch_http_request
创建一个批处理,并将所有我们想要的搜索请求添加到其中。在将请求添加到批处理中时,我们还提供了一个callback
函数,在特定的请求被执行后会调用这个函数。在这种情况下,回调函数- print_claim_review
-通过打印我们搜索的原始索赔及其文本评级来处理响应。
如果您像上面的代码片段那样只发出几个请求,这种批处理可能没有意义,但是如果您必须发出数千个 API 调用,则可能通过最小化 HTTP 请求开销来提高性能。
结论
在本文中,我试图捕捉大多数常见的工作流和概念,比如批处理、分页、搜索或认证,这些都是您在使用 Google APIs 时可能会遇到的。然而,这并不是使用它们可以完成的所有事情的详尽指南,所以一定要查看其他资源,例如 Python API 客户端示例或 Google Workspace 示例和片段。此外,对于任何严肃的应用程序,你肯定也应该研究测试,API 客户端文档包括关于模拟的章节。
最后,还有很多本文没有涉及的 API。一些值得探索的是翻译 API 、语音转文本或路由 API(地图),它们都包含一个免费层。
成为会员阅读媒体上的每一个故事。你的会员费直接支持我和你看的其他作家。你还可以在媒体上看到所有的故事。
https://medium.com/@martin.heinz/membership
你可能也喜欢…
JSON 入门
原文:https://towardsdatascience.com/getting-started-with-json-4c94bb4df113
介绍用于数据交换的文件格式
费伦茨·阿尔马西在 Unsplash 上的照片
JSON 是JavaSscriptOobjectNrotation 文件格式的缩写。它描述了存储数据的标准化数据格式。它是文本文件格式之一,因为数据是以人类可读的自然语言存储的。
这些文件是如何组织的?
简单地说,JSON 文件的结构是一个无序的键值对集合。对于 Python 程序员来说,这种结构比 Python 字典更好。
键“city”、“country”和“population”的顺序不是预定义的,所以与这里显示的顺序不同的 JSON 文件仍然与显示的文件相同。
在 JavaScript 中,对象只能有表示为字符串的键。由于 JSON 文件格式源自 JavaScript,因此只有字符串可以用作键。
一对中的值可以采用不同的数据类型。允许以下类型:
- 字符串,例如“城市”:“柏林”。
- 数字,例如“人口”:3645000
- 对象,例如另一个 JSON 对象
- 数组,例如“区”:[“Kreuzberg”、“Pankow”、“Reinickendorf”]
- 布尔值,例如“大写”:真
- 空
这种多种多样的数据类型使得 JSON 成为一种非常流行的文件格式。只有少数例外,它们不能作为值存储在 JavaScript 对象符号中。
在 JSON 中,不能存储任何函数。同时,日期格式不能以本机方式存储。但是,这不是主要问题,因为您可以将日期存储为字符串,然后在读取文件时将它们转换回日期。
JavaScript 对象符号的优点和缺点
由于结构简单,这种文件的使用非常普遍。对数据结构的要求相对较低,并且文件对于许多用户来说是快速和容易理解的。
这种广泛使用也可以解释为,现在所有常见的编程语言都有自己的解析器,这使得 JSON 的使用更加容易。
这种文件格式的主要缺点是模糊的数字定义和缺乏解释不支持的数据类型的标准。JavaScript 对象符号不区分常见的数字格式,如整数或小数。这使得数字的解释依赖于实现。
如前所述,有些数据类型在默认情况下不受支持,比如日期。这可以通过转换为字符串来避免。然而,有不同的可能性和库,可用于这一点,它没有商定任何统一的标准。
用 JSON 可以实现哪些应用?
JavaScript 对象符号在许多不同的应用程序和编程语言中使用。NoSQL 和各种关系数据库都有连接器来存储这些文件类型。
此外,它还适用于以下用例:
- 数据传输:当通过 API 请求信息时,JavaScript 对象符号在很多情况下被用作响应格式。
- 清晰的数据存储:由于数据格式要求不高,灵活的数据结构可以轻松存储。
- 临时数据存储:JavaScript 对象符号也常用于在程序中短期存储信息。由于与其他编程语言的高度兼容性,这些文件也可以由不同的应用程序使用,而无需更多的麻烦。
如何用 Python 编辑 JSON 文件?
Python 有自己的库来处理 JavaScript 对象符号。必须先用“打开”打开相应的文件。然后可以用“load”将存储的 JSON 对象转换成一个由 Python 字典组成的数组。
从现在开始,您可以像使用 Python 字典一样使用数据。
这是你应该带走的东西
- JSON 是JavaSscriptOobjectN旋转文件格式的缩写。
- 它描述了存储数据的标准化数据格式。
- 该文件格式用于各种应用程序,因为它非常容易阅读和理解。
- 此外,所有常见的编程语言都提供了用 JavaScript 对象符号简化工作的模块。
- 模糊的数字定义是使用这种文件格式的最大缺点。
如果你喜欢我的作品,请在这里订阅https://medium.com/subscribe/@niklas_lang或者查看我的网站* 数据大本营 !还有,medium 允许你每月免费阅读 3 篇 。如果你希望有无限制的 访问我的文章和数以千计的精彩文章,不要犹豫,点击我的推荐链接:【https://medium.com/@niklas_lang/membership】每月花$5***获得会员资格**
* *
Python 中的 NLTK 入门
原文:https://towardsdatascience.com/getting-started-with-nltk-eb4ed6eb7a37
探索一些我们可以用来开发基本 NLP 管道的最常见的函数和技术。
照片由亚伦·伯顿 @unsplash.com 拍摄
N LTK ( 自然语言工具包)是自然语言处理技术在 Python 中的首批实现之一。尽管它可能看起来有点过时,并且面临来自其他库的竞争(例如, spaCy ),但我仍然认为 NLTK 是对 Python 中文本方法的一个非常温和的介绍。
起初,使用 NLTK 可能看起来有点奇怪,因为它有很多方法,特别是对于 Python 初学者来说。但实际上,它是从简单的 NLP 任务开始更方便的库之一,因为它有简单的单行方法,人们可以调用这些方法来执行一些很酷的文本转换。
当然,你不应该指望使用 NLTK 来训练最先进的模型。然而,这个库给了你很多工具,用于小的宠物项目和开发小规模的 NLP 项目。此外,它也是第一次接触 NLP 技术的最好的库之一。
这篇文章会给你一些简短的解释和例子,你可以马上用在 NLTK 中。我们将做一些 NLTK 函数的代码示例,并讨论一些我们可以使用的替代方法。
为了使这段代码易于复制,我们将使用来自 Python 的维基百科页面的前几段:
python_wiki = '''
Python is a high-level, interpreted, general-purpose programming language. Its design philosophy emphasizes code readability with the use of significant indentation.
Python is dynamically-typed and garbage-collected. It supports multiple programming paradigms, including structured (particularly procedural), object-oriented and functional programming. It is often described as a “batteries included” language due to its comprehensive standard library.
Guido van Rossum began working on Python in the late 1980s as a successor to the ABC programming language and first released it in 1991 as Python 0.9.0.[33] Python 2.0 was released in 2000 and introduced new features such as list comprehensions, cycle-detecting garbage collection, reference counting, and Unicode support. Python 3.0, released in 2008, was a major revision that is not completely backward-compatible with earlier versions. Python 2 was discontinued with version 2.7.18 in 2020.
Python consistently ranks as one of the most popular programming languages.
'''
单词分词器
自然,如果没有某种类型的转换,计算机无法真正理解大量的文本。在处理 NLP 管道时,标记化是第一个想到的转换。
“令牌”是 NLP 行业中一个非常常见的表达。令牌是某个特定文本的子集或整个文本的分解。例如,孤立的单词!
由于有多种方法可以将单词从文本中分离出来,nltk
有一些关于记号赋予器的很酷的不同实现,即:
- 空白符号化器
- tree bank分词器
记号赋予者把我们的句子分割成记号。然后,这些标记可以被送入多种单词表示算法,如 tf-idf ,二进制或计数矢量器。让我们从最简单的开始, whitespace tokenizer,它根据单词之间的空格分割文本:
*from nltk import tokenizews_tok = tokenize.WhitespaceTokenizer()
token_list = ws_tok.tokenize(python_wiki)
print(token_list[0:10])*
分解上面的代码:
from nltk import tokenize
—我们从导入通用的tokenize
模块开始,该模块包含标记化器的不同实现。- 我们在
ws_tok
中定义了一个WhitespaceTokenizer
的实例。 - 我们使用
ws_tok
实例来标记我们的python_wiki
文本。
print
语句产生以下结果:
*['Python', 'is', 'a', 'high-level,', 'interpreted,', 'general-purpose', 'programming', 'language.', 'Its', 'design']*
WhitespaceTokenizer
的一个主要问题是,它最终通过每个空格分割文本,形成一些由单词和标点组成的记号。
例如,“interpreted,
”被认为是一个单独的标记,注意这个单词上的逗号。如果你有另一个没有逗号的interpreted
,这个单词会被认为是一个完全不同的单词。这是一个重大的挫折,因为如果单词在逗号、句号或其他标点符号旁边,它们的意思不会真正改变。
幸运的是,我们有其他可用的记号赋予器,比如 TreeBank 记号赋予器。这个记号赋予器的一些特性是(摘自官方文档):
该记号赋予器执行以下步骤:
-拆分标准缩写,例如:“不要”、“不要”和“他们会”、“他们会”
-将大多数标点符号视为单独的符号-当逗号和单引号后跟空格时,将其分开
-在行尾出现单独的句点
让我们用我们的python_wiki
文本做同样的测试:
*tb_tokenizer = tokenize.treebank.TreebankWordTokenizer()
token_list = tb_tokenizer.tokenize(python_wiki)
print(token_list[0:10])*
让我们检查一下印刷品:
*['Python', 'is', 'a', 'high-level', ',', 'interpreted', ',', 'general-purpose', 'programming', 'language.']*
酷!一些问题已经解决。注意language.
还是一个单词和标点结合的令牌。幸运的是,这个问题用默认的混合了TreebankWordTokenizer
和PunkSentenceTokenizer
的word_tokenize
函数解决了(我们没有测试的一个记号赋予器)。
*from nltk import word_tokenize
token_list = word_tokenize(python_wiki)
print(token_list[0:10])*
让我们看看我们的前十个令牌:
*['Python', 'is', 'a', 'high-level', ',', 'interpreted', ',', 'general-purpose', 'programming', 'language']*
酷!NLTK 的默认word_tokenize
似乎很好地隔离了句子中的标记,因为它没有在单个标记中附加任何标点符号。
总的来说,这些记号化器是将我们的文本分割成记号的好方法,然后将它们输入到其他应用程序中,如简单的情感分析或单词向量。我们的实验有助于你理解不存在单一的“记号赋予者”。虽然有人可能会说word_tokenize
产生了最好的结果,但是在nltk
中可以尝试其他的选择和实现。
堵塞物
减少文本的变化可能是您在处理文本数据时首先要做的实验之一。
文本数据本质上是高维的——英语中有成千上万的单词,其维数与其他语言相似。
词干可能是减小文本大小的一个好选择——词干的作用是从单词中去掉一些后缀。词干是一种有点“蛮力”的技术,在从文本中剪切字符时被认为是相当激进的。像tokenizers
一样,词干分析器有不同的风格——让我们看看 NLTK 中的一些实现,从PorterStemmer
开始:
*from nltk.stem import PorterStemmer
porter = PorterStemmer()
porter_tokens = [porter.stem(token) for token in token_list]*
分解我们代码的每个指令:
- 我们从从
nltk.stem
模块导入PorterStemmer
开始。 - 我们在
porter
变量中定义了一个PorterStemmer
的实例。 - 我们使用列表理解并在
porter.stem
函数中传递每个token
来阻止每个标记。
让我们打印我们的第一个 10 个词干标记:
*['python', 'is', 'a', 'high-level', ',', 'interpret', ',', 'general-purpos', 'program', 'languag']*
在某些情况下,我们的词干与我们的原始单词不同——例如,原始单词interpreted
变成了interpret
。词干意味着首字母相同的单词将被认为是相同的,例如:
interpretation
变为interpret
interpreted
变为interpret
interpret
停留interpret
根据定义,有些单词没有词干。当我们通过词干算法时,python
保持python
。
更具侵略性的斯特梅尔是 T20——我们也可以用它来表达
*from nltk.stem import LancasterStemmer
lanc = LancasterStemmer()
lanc_tokens = [lanc.stem(token) for token in token_list]*
检查我们的lanc_tokens
:
*['python', 'is', 'a', 'high-level', ',', 'interpret', ',', 'general-purpose', 'program', 'langu']*
一般来说,LancasterStemmer
会比PorterStemmer
从文本中删除更多的字符,但这并不意味着,在某些情况下,与Lancaster
相比,Porter
可以进一步阻止单词。从上面的例子来看:
language
在Lancaster
中变成langu
,在Porter
中变成languag
。- 反过来,
general-purpose
在Porter
中有词干,但在Lancaster
中没有。
如果你检查全文,你会发现Lancaster
减少了更多的文本,进一步规范化。
文本规范化是一项很酷的技术,可以减少输入的差异,并规范化可能(或可能不)表达相同意思的相似单词。然而,要注意的是,每次你应用Stemmer
,你都在减少来自语料库的信息。
目前,人们正在讨论Stemmers
或Lemmatizers
(接下来将详细介绍)是否应该成为大多数数据管道的一部分,尤其是在新的神经网络技术应用于文本的情况下。有人可能会说,它们将来可能会过时,但是,在下列情况下,它们仍然有效:
- 你缺乏训练大量参数的计算能力;
- 由于可解释性或其他潜在原因,您需要应用更简单的模型。
如果你想了解更多关于Stemmers
的事情,你可以在这里查看我关于此事的博文!
词汇化
与词干化类似,词汇化是另一种标准化技术,目的是减少文本的变化。
主要区别在于,它不是将单词切割成后缀,而是试图找到单词的词根,通常称为 lemma。
NLTK 还包含一个现成的 WordNet Lemmatizer 实现,这是一个非常著名的词汇数据库,包含关于单词关系的数据。让我们检查一下WordNetLemmatizer
模块:
*['Python', 'is', 'a', 'high-level', ',', 'interpreted', ',', 'general-purpose', 'programming', 'language']*
那很有趣..看来我们的令牌和我们的句子完全一样!解释很简单——我们需要给lemmatizer
输入我们想要减少的单词的词性标签。让我们从我们的代币中选择单词programming
:
lemmatizer.lemmatize('programming')
产量programming
lemmatizer.lemmatize('programming', pos = 'v')
产量program
当我们给出论点pos
时,引理者能够理解单词 programming 是一个与词根“program”相关的动词。NLTKwordnetlemmatizer接收 5 个pos
标签:**
- 名词的“n”——这是默认值。实际上,如果我们只是调用我们的
lemmatizer.lemmatize
而没有pos
参数,我们将把所有的单词都看作名词; - 动词用“v ”;
- 形容词用“a ”;
- r 代表副词;
- s 代表卫星形容词——不常使用;
当然,手动输入这些pos
标签是非常低效的。幸运的是,nltk
有一个超级酷的实现来自动检索 POS 标签!
词性标注
正如我们在词汇化一节中看到的,词性标注包括根据单词在句子中的功能对其进行分类。
一个特定的词在一个句子中有一定的功能(通常称为词性)——例如,句子“I like learning Python
”包含 4 个词和下面的词性:
- 代词
I
- 动词
like
和learning
- 名词
Python
棘手的是,某些单词在句法上可能是相同的,但在不同的句子中有不同的“功能”:
- 我正在洗水槽。
- 我要把沉入水中**
单词sink
的写法完全相同,但有不同的词性标签。在第一句中,sink
是名词。第二个,是一个verb
。
在NLTK
中,我们有现成的 POS tagger 可供使用,幸运的是,它避免了这些问题:
*import nltk
pos_tags = nltk.pos_tag(token_list)
print(pos_tags[0:10])*
第 10 期pos_tags
:
*[('Python', 'NNP'), ('is', 'VBZ'), ('a', 'DT'), ('high-level', 'JJ'), (',', ','), ('interpreted', 'JJ'), (',', ','), ('general-purpose', 'JJ'), ('programming', 'NN'), ('language', 'NN')]*
与上面的一些例子相反,nltk.pos_tag
返回一个元组列表。每个元组包含单词及其对应的pos_tag
。让我们看几个例子:
Python
被归类为NNP
——专有单数名词;is
是一个VBZ
——现在时态的动词。
从预告中,我们看到很多标签!我们必须记住他们吗?当然不是!我们有两种方法来理解每个标签的含义:
- 查找 Penn Treebank POS 表。
- 使用您想要检查的标签运行
nltk.help.upenn_tagset()
。例如,nltk.help.upenn_tagset('NN')
返回标签NN
的完整 wiki。
让我们从第一句话开始,看看这位经过预先训练的 pos 标记员是否能够处理我们的“下沉”问题:
*print(nltk.pos_tag(['I','am','washing','the','sink']))*
这会产生:
*[('I', 'PRP'), ('am', 'VBP'), ('washing', 'VBG'), ('the', 'DT'), ('sink', 'NN')]*
爽,这里沉的是一只NN
!让我们看看另一句话:
*print(nltk.pos_tag([‘I’,’am’,’going’,’to’,’sink’,’in’,’the’,’water’]))*
这会产生:
*[('I', 'PRP'), ('am', 'VBP'), ('going', 'VBG'), ('to', 'TO'), ('sink', 'VB'), ('in', 'IN'), ('the', 'DT'), ('water', 'NN')]*
厉害!sink
是vb
—一个动词!
这是因为这个预先训练的词性标注器考虑了单词的上下文。由于sink
紧挨着to
,标记者立即意识到它应该标记为动词,因为没有其他场景中这些单词一起出现,而sink
是名词。
你也可以在我在 Udemy 上的 NLP 基础课程中了解更多关于训练你自己的 pos 标签员的信息。
…
但是,等等..这些标签与我们必须输入到词汇化过程中的标签不匹配!这是怎么回事?
幸运的是,我们可以用下面的函数很容易地转换它们:
*def get_lemma_tag(pos_tag):
if pos_tag.startswith('J'):
return 'a'
elif pos_tag.startswith('V'):
return 'v'
elif pos_tag.startswith('N'):
return 'n'
elif pos_tag.startswith('R'):
return 'r'
else:
return ''*
您只需检查pos_tag
函数的起始字母是什么,并将其转换为输入到 lemmatizer 的单字母版本——让我们测试一下:
*lemmatizer.lemmatize(‘programming’, pos = get_lemma_tag(‘VBZ’))*
这输出program
。成功了!
为了简单起见,我们在函数中忽略了“附属形容词”——它们不常使用,需要更复杂的规则。
POS 标签是 NLP 中一个非常重要的概念。你可能最终会使用你自己训练过的标签或者更高级的标签,比如 spacy 的实现。尽管如此, NLTK 的版本仍然被广泛使用,并且可以实现很好的性能,足以完成一些 NLP 任务。
N-Grams
到目前为止,我们只把我们的记号看作孤立的单词。在许多 NLP 应用程序中,将单词耦合在一起作为单个“令牌”来考虑是很重要的。
例如,否定是解释的基本要素。Bi-grams 是“两个连续单词”的自然语言处理方式。让我们来考虑这个句子:
- 我不喜欢剧院。
在典型的单字符方式中,我们的令牌是:
- 我没有去剧院
在这种情况下,将“不像”视为一个单独的标记是有意义的。想象一个情感分析应用程序,它将检查相关的标记来理解我们文本的整体“情感”。在这种情况下,它显然是一个否定的句子,因为“代理人”不喜欢剧院。
对于这个用例,了解我们的文本包含多少“不喜欢”的表达是有用的。如果我们只考虑一个孤立的单词“like”,我们的算法将很难拾取这种“负面情绪”,因为单词“而不是”也将是孤立的。此外,一个孤立的“ not ”本身并不一定代表一种负面情绪——例如在句子“我不认为这个剧院不好。**
在更高级的模型中,如神经网络,算法能够自己挑选这些二元模型、三元模型(三个令牌)——这实际上是它们的优势之一。但是,对于更简单的模型(朴素贝叶斯、回归、基于树的模型),必须显式地给出 n-grams 作为特征。
NLTK 有一个我们可以获取的二元模型和三元模型的快速实现:
*print(list(nltk.bigrams(token_list)))*
超级容易!我们只是把我们原来的token_list
喂给nltk.bigrams
然后…
*[('Python', 'is'),
('is', 'a'),
('a', 'high-level'),
('high-level', ','),
(',', 'interpreted'),
('interpreted', ','),
(',', 'general-purpose'),
('general-purpose', 'programming'),
('programming', 'language'),
('language', '.'),
('.', 'Its'),*
这是我们课文的前 10 个双字母组合的预览。我们现在有了一个元组列表,每个元组包含来自python_wiki
文本的两个单词。
如果我们想要三个词呢?使用nltk
很简单:
*list(nltk.trigrams(token_list))*
我们有一个功能叫做trigrams
!让我们看看前 10 个:
*[('Python', 'is', 'a'),
('is', 'a', 'high-level'),
('a', 'high-level', ','),
('high-level', ',', 'interpreted'),
(',', 'interpreted', ','),
('interpreted', ',', 'general-purpose'),
(',', 'general-purpose', 'programming'),
('general-purpose', 'programming', 'language'),
('programming', 'language', '.'),
('language', '.', 'Its'),*
酷!现在我们有了一个元组列表,其中每个元组包含文本中三个连续单词的每一对。我们能有更多的三字母组合吗?没错。使用带额外参数的通用ngrams
函数:
*list(nltk.ngrams(token_list, 4))*
这产生了四个字母:**
*[('Python', 'is', 'a', 'high-level'),
('is', 'a', 'high-level', ','),
('a', 'high-level', ',', 'interpreted'),
('high-level', ',', 'interpreted', ','),
(',', 'interpreted', ',', 'general-purpose'),
('interpreted', ',', 'general-purpose', 'programming'),
(',', 'general-purpose', 'programming', 'language'),
('general-purpose', 'programming', 'language', '.'),
('programming', 'language', '.', 'Its'),
('language', '.', 'Its', 'design'),*
对于你的 NLP 管道来说,N-grams 是一个非常重要的概念,尤其是当你处理可能需要一些聪明的特征工程的更简单的模型的时候。
我们完事了。如果你想开始使用自然语言处理管道,NLTK 是一个非常酷的库。虽然你不应该期望使用 NLTK 来构建最先进的模型,但是熟悉这个库将是向你介绍很酷的 NLP 概念的一个极好的方式。**
我在Udemy上开设了一门学习自然语言处理基础知识的课程,在这里我向学生们介绍 nltk、词向量和神经网络!这门课程是为初学者量身定做的,我希望你能在我身边。**
Python 中的自然语言处理基础 —图片作者
*https://ivopbernardo.medium.com/membership
这篇文章的代码有一个小要点:*
规范化流程入门:线性代数与概率
变易法则、双射与微分同胚
平静的流动(鸣谢:作者)
生成模型的基础是理解数据样本的来源分布。在我以前的一篇文章中,通过完成期望最大化算法的步骤,展示了一个生成模型的例子,其中我们假设潜在变量产生了期望的数据。我们还知道其他一些基于神经网络的方法,如变分自动编码器(VAE)和生成对抗网络(GAN ),它们显示了(它们的变体)引人注目的应用,但是它们缺少一个关键特征;它们不允许对新点的概率密度进行精确评估。通过标准化流量,可以通过易处理的分布进行采样和密度估计。
为了理解标准化流的基本原理,我们在这里讨论必要的本科水平的概率和线性代数,这将有助于逐步掌握将简单分布转换为复杂分布的概念。你能从这篇文章中学到什么?
- 什么是双射和微分同胚?
- 线性代数和雅可比矩阵基础。
- 转换概率分布和雅可比矩阵。
- 规范化流程在前面的概念中处于什么位置?
- 通过使用张量流概率库检查我们的理解。
1.双射与微分同胚;
一个函数 f : A → B 如果这 2 个集合( A , B )的元素有完美的一一对应,那么这个函数就是双射的。双射函数最重要的性质是存在一个反函数,它可以撤销函数的作用。
例:由公式f(x)=x+7 定义的从集合{1,2,3,4}到集合{8,9,10,11}的函数是一个双射。
微分同胚: 把 x 看作一个 D 维向量,我们想定义一个 x 上的联合分布。基于流建模的主要思想是将 x 表示为从 p_u ( u )采样的实向量 u 的变换 ϕ :
x=ϕ(u)其中,u∽p _ u(u)……(eq:1)
这里 p_u(u) 称为流量模型的基分布。基于流的模型的定义属性是变换 ϕ 必须可逆,并且 ϕ 和 Inv( ϕ) 必须可微。这些变换被称为微分同胚,并且要求 u 也必须是 D 维的[1]。
2.变量和雅可比矩阵的变化:
在这里,我们得到了大学线性代数课的帮助。
考虑一个线性变换 T : R →R(在数学中,维数为 n 的实坐标空间,表示为 Rⁿ).这样的线性变换可以用一个 2×2 矩阵 A 来描述。将有序对作为列向量,我们可以写出:
情商。2.1:线性变换和雅可比矩阵
矩阵 A 将单位正方形(两边为标准单位向量 i 和 j 的正方形)发送到两边为 A 的列的平行四边形,即 a c 、 b d 。偏导数矩阵 A (如果 T 是线性变换,它将是常数矩阵)有一个行列式;这叫做 T 的雅可比。这个平行四边形的面积是|det(A)|A的行列式的绝对值。
一般来说,如果 D 是 R 中的任意区域,D _1 =T(D)是它在这种线性变换下的像,那么:Area(D _1)= |det(A)| Area(D)。我们举一个在 R 空间从笛卡尔坐标转换到极坐标的例子,就可以很容易地验证这一点。我们知道变换规则由x=rcosθ,y=rsinθ给出,那么变换的雅可比矩阵为:
情商。2.2:用于从笛卡尔坐标系转换到极坐标系统的雅可比矩阵
利用上面雅可比矩阵的定义我们可以得到面积元素为: dA ( x ,y)= |det**J|dA(r, θ ) = r dr dθ。最后,变量公式的变换在多元微积分中非常有用,我们可以总结如下:
设 T 是从 D_ 1 到 D 的一个 C 微分同胚(若 t 是 r 次连续可微, T 称为一个 C ʳ微分同胚)。对于 D: 中的任何连续功能 f
情商。2.3:使用雅可比的变换规则
这个表达式可以推广到 n 维。
3.1.变量的概率和变化:
我们将上述概念从单/多变量函数扩展到概率分布。考虑一个简单的变换,一个均匀分布在单位立方体上的随机变量 U ,U∈【0,1】。我们可以将 U 缩放一个因子 n 得到一个新的随机变量 X (如下图 1 所示)。
情商。3.1.1:用因子 n 缩放随机变量
由于总概率是守恒的,我们可以写成:
情商。3.1.2:概率总和为 1
由于我们从单位体积的立方体上的随机变量( V_u )开始,缩放随机变量也将缩放概率密度,如下所示:
情商。3.1.3:当 n>1 时,概率密度缩小
前面关于从 u → x 的变换的讨论(图 1)告诉我们,由于映射函数(可逆)【ϕ】,从p(u)→p(x)的变换,有多少 du 被收缩/拉伸到 dx
为什么这很重要?因为这是流量正常化的起点。我们就是这样从情商开始的。1 在微分同胚一节之前。我们考虑从概率分布 p_u 采样的实向量 u 和改变 u→x 的变换函数 ϕ 。对于基于流动的模型, ϕ 是一个微分同胚。
图 1:使用微分同胚变换概率分布:由于总概率总是 1,我们在变换后得到一个收缩/拉伸的空间。(鸣谢:作者幻灯片)
3.2.转换概率分布:
为了推导一些转换概率分布的简单公式,我们从方程开始。3.1.2.上一节中又考虑了一个单一的区域,所以我们可以去掉积分:
情商。3.2.1:概率守恒导致第一对数概率公式。
我们记得我们通过映射函数 ϕ 从 u 到 x 的转换为x=ϕ(u);u= inv {ϕ}(x)。从前面关于变换的讨论(在线性代数部分)中,我们知道由线性/非线性变换引起的拉伸/收缩可以通过雅可比矩阵的行列式来量化。非常类似地,这里的变换规则也变成了:
情商。3.2.2:根据之前的线性代数讨论,用雅可比矩阵写出之前的方程。
R.这个方程的 H.S 现在完全取决于基本变量 u 。雅可比矩阵定义保持不变:
情商。3.2.3:强大的雅各比派:它所代表的…
取上式两边的对数,我们可以写成如下:
情商。3.2.4:取等式的对数。3.2.2.
由于变换函数 ϕ 是可逆的,我们也可以写出如下等式:
情商。3.2.5:由于可逆变换,我们可以增加一些步骤并重写等式。3.2.4 以不同的形式。
我们稍后将使用这些转换规则来转换概率分布。
4.标准化流程:
在介绍了所有必要的基础知识和数学公式之后,我们准备介绍标准化流程。
之前我们只讨论了一种通过可逆函数 ϕ 的变换。微分同胚(通过函数变换使得函数及其逆均可微)的一个非常重要的性质是它们是可复合的即给定两个这样的变换ϕ1,ϕ2,它们的复合ϕ1⋅ϕ2 也是可逆的和可微的。逆矩阵和雅可比行列式由下式给出
情商。4:多重微分同胚的组合。
规范化流程背后的想法是将多个微分同胚 ϕ 1、 ϕ 2、⋯ ϕk 链接在一起,以逐渐获得来自复杂分布 p_X ( x )的 x【原始数据】,从 u 开始,来自简单基础分布p _ u(u**
图 2:标准化流程做什么?通过一系列微分同胚将简单分布(例如:N(0,I))转换为复杂分布。(鸣谢:作者幻灯片)
张量流概率库的简单例子:
我们将从使用 TensorFlow 概率库的一些简单的正向和反向转换的例子开始,该概率库具有双投影器模块。让我们加载必要的库:
*import tensorflow as tfimport tensorflow_probability as tfptfd = tfp.distributionstfb = tfp.bijectors*
变换张量: 让我们使用两个最简单的双射运算(缩放和移位)并将它们组合起来,以“正向”变换一个张量,并执行反向运算以再次检索原始张量:
我们从一个常数张量[1]开始。, 2., 3.]然后我们通过tfb.Chain
链接两个双射操作(移动 3 个单位和缩放 2 个单位)。然后,我们可以很容易地执行正向和反向操作,我们验证,在正向变换后,使用反向变换,我们得到了原始张量。
变换分布: 在前面的例子中我们变换了一个张量,但这里我们来变换一个分布。我们从正态分布中抽取样本,并使用我们之前用于变换的相同链式双射运算:
仅考虑正态分布中的 5 个随机样本,我们应用链式双投影器(scale & shift ),下面是正向变换的结果图(直方图)。
图 3:正态分布的样本(左)和正向变换后的结果(3 个单位移位+ 2 个单位标度)。
我们还检查了等式 2 中描述的对数概率规则。3.2.4 & 3.2.5.这些很容易检查,因为双投影器在张量流概率中有方法forward_log_det_jacobian
和inverse_log_det_jacobian
。
如果考虑 2D 分布,而不是 1D 分布,会发生什么?让我们看一个从 2D 均匀分布开始的例子,并应用如下的缩放双射运算:
2D 均匀分布在 x 方向上缩放-2 个单位,在 y 方向上缩放 0.5 个单位。我们可以对缩放双射前后的 100 个样本进行散点图绘制:
图 4:通过正向运算变换 2D 分布(变换函数是双射的)。
对角矩阵将始终独立地在两个维度上执行缩放操作,即矩形分布保持矩形,我们在上一个示例中看到了这一点。
如果要从矩形分布变成四边形分布呢?这里我们可以使用下三角矩阵进行缩放双射运算。让我们看看:
从矩形分布开始,我们最终得到如下四边形分布:
图 5:与图 4 相同,但是这里我们将矩形分布转换为四边形分布。这里显示了这些分布的示例。
就像以前一样,我们可以通过应用如下的逆变换来恢复到原始分布:
*y2_inv = scale_bijector2.inverse(scale_bijector2(x))*
图 6:我们总是可以执行逆变换(只要变换是双射的)并取回原始样本(来自均匀的 2D 分布)。
我们讨论了标准化流程的核心概念和构建模块。从线性代数、雅可比行列式和概率分布变换开始,我们已经看到了线性代数中雅可比行列式的概念如何无缝地用于变换概率分布。最后,我们使用了张量流概率库中的双射器模块来验证我们推导的概念和公式。
在下一篇文章中,我将更多地关注复杂分布的转换(例如,从正态分布到双峰分布等)。)使用双射函数,但是这里使用的概念和公式在研究标准化流的任何阶段都将非常有用。
干杯,保持坚强!!
参考资料:
[1]“用于概率建模和推理的标准化流”:G. Papamakarios 等人[arXiv: 1912.02762 。
[2] 正常化流动:分布和决定因素 : Eric Jang。
[3]本帖使用的笔记本: GitHub 链接。
Docker 中的 Postgres 入门
原文:https://towardsdatascience.com/getting-started-with-postgres-in-docker-616127e2e46d
面向数据科学家的 Python 入门
原文:https://towardsdatascience.com/getting-started-with-python-for-data-scientists-a9b20058b46e
开始时你应该学什么
对于任何数据科学初学者来说,一个关键问题是使用哪种语言。有很多选择,做出选择可能很困难,尤其是当你在 Twitter(或其他知名来源)上看到一种语言比另一种语言更好的争论时。我可以很自信的说,无论你从哪种语言开始,都比浪费时间去挑选一种语言要好!一旦你学会了一种语言,如果有必要的话,学习另一种语言就变得容易多了,现在大多数数据科学语言都有完整的工具包可供使用。
尽管如此,大多数人最终还是从 Python 开始。这是因为 Python 中围绕数据科学发展起来的深层生态系统,包括 sklearn、statsmodels、pandas、matplotlib、tensorflow 等库。这意味着你在数据科学职业生涯中可能遇到的任何工作流,你都可能在 Python 中完成。然而,选择 Python 的另一个好处是,除了数据科学之外,它还有广泛的适用性。这包括通过 Django 和 Flask 等框架在 web 开发中使用,以及即将成为 Pyscript 的框架,它在自动化和测试中的使用,因为它的易用性和简单性以及 Beautiful Soup 等包,以及在构建产品的一般软件工程领域的广泛使用。因此,学习 Python 不仅可以为学习数据科学做准备,还可以为软件工程中更广阔的职业生涯做准备。
Python 基础
学习任何编码语言的第一步是学习基础知识。作为其中的一部分,你还需要设置你的计算机,这样你就可以真正地编写和运行代码。在数据科学中,这通常是通过使用 Anaconda 和 Jupyter 笔记本来完成的,这是一种用于数据数据科学工作流的常见环境。对于初学者来说,这样做的好处是,您可以清楚地运行单独的小代码片段,并且 Anaconda 可以帮助您处理 Python 中经常出现的混乱的包冲突现实。虽然许多人后来开始使用实际的 Python 脚本并使用虚拟环境,但 Anaconda 和 Jupyter 笔记本是很好的起点。
学习语言本身通常从理解变量和数据类型如何工作开始。就 Python 而言,在大多数语言中,变量被用来存储信息,这些信息允许您稍后在程序中调用和使用这些信息。这可以通过 Python 中的=
操作符简单地完成,它将信息赋给一个变量。接下来要学习的第二件事是该语言支持什么数据类型。在 Python 的情况下,四个主要的基本数据类型包括int
、float
、str
和bool
,它们代表一个整数(没有小数位的整数值)、一个浮点数(有小数位的数值)、一个字符串值(键入的单词)和一个布尔值(只能取True
和false
)。虽然您可能会遇到其他数据类型,但这些是让您开始旅程的基本构件。
接下来的事情是学习语言中的操作符。这是用于执行数学或比较运算等操作的符号。在前一种情况下,我们使用的符号如+
表示加法,-
表示减法,*
表示乘法,/
表示除法。然而,我们也可以执行比较操作,然后形成控制流的基础。在 Python 中,这可以包括诸如用于检查值是否相等的==
、用于不相等的!=
以及分别用于小于和大于的<
、>
的比较。
Python 逻辑
接下来要讨论的是逻辑和流程流在 Python 中是如何工作的。这是为了让您可以创建更复杂的程序,其中内置了一些逻辑,以便在满足给定条件时触发某些操作。在 Python 中,构建这些复杂的程序通常涉及到条件语句、逻辑语句、循环和函数的使用。
在这方面,首先要讨论的是条件语句。虽然您已经讨论了比较运算符,但这涉及到如何使用它们来检查是否满足某个条件,然后触发一些代码来响应。一个例子是检查变量a
是否等于b
使得a == b
或者a
大于b
使得a > b
将响应为真。然后,这些比较运算符可用于使用条件语句if
、else
、elif
触发代码。这些允许你触发代码if
条件被满足,或者else
否则会发生什么。这些条件可以通过使用and
、or
和not
构建成更复杂的语句,这允许您一次检查多个条件。
我们还需要知道如何基于条件或者通过创建可重用的代码片段来重复代码片段。前者可以使用循环来触发,只要满足条件,循环实际上就运行同一段代码。这分为while
和for
循环,前者在条件仍然为真时执行给定的动作,而 for 循环将在已经定义的组上循环。当我们需要在代码的不同区域反复使用代码时,我们还有一些有用的函数。这可能是当您希望执行相同的操作,但输入不同或处于工作流的不同阶段时,通过定义一个可在代码中稍后调用的函数来完成。
Python 序列
一旦你掌握了这门语言的基础和逻辑,下一步就是理解如何存储不同形式的数据。这在数据科学中非常重要,因为您不太可能一次存储单个信息,而是存储多个数据块,每个数据块都需要特定的格式。为此,我们需要能够选择正确的数据格式,以实现最高效的存储和访问。
在 python 中,有四个主要的内建序列,你可以经常利用它们。这包括列表、元组、集合和字典。了解如何使用这些信息及其关键特征以确保以正确的方式存储数据是非常重要的。在这种情况下:
- 列表:可变、有序、可索引,并且可以包含重复记录
- 元组:不可变、有序、可索引,并且可以包含重复记录
- 集合:可变、无序、不可索引,不允许重复记录
- 字典:可变、有序、可索引,并且不能包含重复值(至少在它们的键中)
了解这些特征将决定您将选择哪种数据结构/序列来存储数据,以便在您想要执行分析时可以轻松访问。
编程范例
除了学习语言,理解不同的编程范例如何工作也很重要。在学习上述大部分内容时,您将会遇到过程式和函数式编程范例。前者是指代码以过程化的方式展开,代码基本上按照编写时的样子“进行”。而后者通常使用过程化编程,但也利用了将可重复的代码片段抽象成函数的优势。这减少了您必须编写的代码总量,也允许某种形式的抽象。
另一种选择是面向对象编程,这也是您在深入研究 Python 中的库时会遇到的。与前两个范例相反,这一个范例构造代码,以便数据的特征和行为可以捆绑在一起成为一个单一的结构。它通过创建被称为类的“蓝图”来做到这一点,这些“蓝图”允许您创建可以呈现代码中早期定义的某些特征和行为的对象。理解这种范式对于能够与将成为任何数据科学工作流一部分的许多库进行交互是很重要的。这种范式的好处是,它有助于编写可重复使用的代码,并将特征和行为捆绑到一个结构中,使得在与库交互时更容易使用和理解。
结论
学习一门新的编码语言可能会很困难,尤其是对那些学习母语的人来说。Python 在这方面对数据科学家是有益的,因为它相对容易地从简单的语法开始,足够容易阅读和理解。在学习数据科学语言时,建议您涵盖大部分基础知识,包括:变量、数据结构、序列、操作、逻辑、函数和面向对象编程。一旦掌握了这些基础知识,您就可以更加自信地开始您的 Python 数据科学之旅,并转向更复杂的主题和构建您的数据科学工作流。祝你好运!
如果你喜欢你所读的,并且还不是 medium 会员,请使用下面我的推荐链接注册 Medium,来支持我和这个平台上其他了不起的作家!提前感谢。
https://philip-wilkinson.medium.com/membership
或者随意查看我在 Medium 上的其他文章:
PyTorch 图像模型(timm)入门:实践者指南
如何在自己的培训脚本中使用这个神奇的库
PyTorch Image Models (timm) 是一个用于最先进的图像分类的库,包含图像模型、优化器、调度器、增强器等等的集合;最近,它被命名为2021 年最热门的图书馆!
虽然越来越多的低代码和无代码解决方案使得开始将深度学习应用于计算机视觉问题变得容易,但在我目前作为微软 CSE 的一部分的角色中,我们经常与希望寻求针对其特定问题定制解决方案的客户接触;利用最新和最大的创新超越这些服务提供的性能水平。由于新的架构和培训技术被引入这个快速发展的领域的速度,无论您是初学者还是专家,都很难跟上最新的实践,并且在处理新的愿景任务时很难知道从哪里开始,以便重现与学术基准中呈现的结果类似的结果。
无论我是从头开始训练,还是对现有模型进行微调以适应新任务,以及希望利用现有组件来加快我的工作流程,timm 都是 PyTorch 中我最喜欢的计算机视觉库之一。然而,尽管 timm 包含参考培训和验证脚本,用于再现 ImageNet 培训结果,并且在官方文档和timm docs 项目中包含涵盖核心组件的文档,但是由于该库提供的特性数量庞大,在定制用例中应用这些特性时,可能很难知道从哪里开始。
本指南的目的是从从业者的角度探索 timm,重点是如何在定制培训脚本中使用 timm 中包含的一些功能和组件。重点不是探究这些概念是如何或为什么工作的,或者它们是如何在 timm 中实现的;为此,在适当的地方会提供原始论文的链接,我会推荐 timmdocs 去了解更多关于 timm 的内部情况。此外,本文决不是详尽的,所选择的领域是基于我使用这个库的个人经验。
这里的所有信息都是基于撰写本文时最近发布的timm==0.5.4
。
目录
虽然这篇文章可以按顺序阅读,但它也可以作为图书馆特定部分的参考。为了便于导航,下面提供了一个目录。
- 模型 <#983b> - 定制模型-
-特征提取-
-导出不同格式 - 数据增强-rand augment
-cut mix 和 MixUp - 数据集
- 优化器
- 调度器- 使用示例
- 指数移动平均模型
- 把这一切联系在一起!
Tl;dr: 如果你只是想看一些可以直接使用的工作代码,复制这篇文章所需的所有代码都可以在这里GitHub gist中找到。
模型
timm 最受欢迎的特性之一是其庞大且不断增长的模型架构集合。这些模型中的许多都包含预先训练的权重——要么在 PyTorch 中进行本机训练,要么从 Jax 和 TensorFlow 等其他库移植而来——可以轻松下载和使用。
我们可以列出并查询可用模型集合,如下所示:
我们还可以使用预训练参数将此选择过滤到具有预训练权重的模型:
这仍然是一个令人印象深刻的数字!如果此时你正经历一点点选项麻痹,不要绝望!一个有用的资源可用于探索一些可用的模型,并了解它们的性能,这是由代码为的论文的摘要页,其中包含 timm 中许多模型的基准和原始论文的链接。
为了简单起见,让我们继续使用熟悉的、经过测试的 ResNet 模型系列。我们可以通过提供一个通配符字符串来列出不同的 ResNet 变体,该字符串将用作基于模型名称的过滤器:
正如我们所看到的,还有很多选择!现在,让我们探索如何从这个列表中创建一个模型。
一般用法
创建模型最简单的方法是使用create_model
;可用于在 timm 库中创建任何模型的工厂函数。
让我们通过创建一个 Resnet-D 模型来演示这一点,如 卷积神经网络图像分类锦囊论文中所介绍的;这是对 ResNet 体系结构的一个修改,它利用一个平均池来进行下采样。这在很大程度上是一个任意的选择,这里演示的特性应该可以在 timm 中包含的大多数模型上工作。
正如我们所见,这只是一个普通的 PyTorch 模型。
为了帮助我们更好地了解如何使用该模型,我们可以访问其配置,其中包含的信息包括用于归一化输入数据的统计数据、输出类的数量以及网络分类部分的名称。
具有不同数量输入通道的图像的预训练模型
timm 模型的一个不太为人所知但非常有用的特性是,它们能够处理具有不同数量通道的输入图像,这对大多数其他库来说是个问题;在这里有一个关于这是如何工作的精彩解释。直观上,timm 通过对小于 3 的信道的初始卷积层的权重求和,或者智能地将这些权重复制到期望数量的信道来实现这一点。
我们可以通过将 in_chans 参数传递给create_model
来指定输入图像的通道数。
在这种情况下,使用随机张量来表示单通道图像,我们可以看到模型已经处理了图像并返回了预期的输出形状。
值得注意的是,虽然这使我们能够使用预训练的模型,但输入与模型训练的图像明显不同。因此,我们不应该期望相同级别的性能,并在将模型用于任务之前对新数据集进行微调!
定制模型
除了用股票架构创建模型之外,create_model
还支持许多参数,使我们能够为我们的任务定制一个模型。
受支持的参数可能取决于底层模型架构,其中一些参数如下:
- global_pool :确定最终分类层之前要使用的全局池的类型
因型号而异。在这种情况下,它取决于架构是否采用全局池层。因此,虽然我们可以在类似 ResNet 的模型中使用它,但是在不使用平均池的 ViT 中使用它就没有意义了。
虽然有些论点是特定于模型的,但诸如以下论点:
- drop_rate :设置训练的辍学率(默认为‘0 ’)
- num_classes :类别对应的输出神经元的数量
几乎可用于所有型号。
在我们探索实现这一点的一些方法之前,让我们检查一下当前模型的默认架构。
改变班级数量
检查我们之前看到的模型配置,我们可以看到我们网络的分类头的名称是 fc 。我们可以用它来直接访问相应的模块。
然而,这个名称可能会根据所使用的模型架构而改变。为了给不同的模型提供一致的接口,timm 模型有get_classifier
方法,我们可以用它来检索分类头,而不必查找模块名。
正如所料,这将返回与之前相同的线性图层。
由于这个模型是在 ImageNet 上预训练的,我们可以看到最终的层输出了 1000 个类。我们可以用 num_classes 参数来改变这一点:
检查分类器,我们可以看到,timm 已经用一个新的、未经训练的、具有所需类别数的线性层替换了最后一层;准备好微调我们的数据集!
如果我们想完全避免创建最后一层,我们可以设置类的数量等于 0,这将创建一个具有身份函数的模型作为最后一层;这对于检查倒数第二层的输出非常有用。
全局池选项
从我们模型的配置中,我们还可以看到设置了 pool_size ,通知我们在分类器之前使用了一个全局池层。我们可以按如下方式对此进行检查:
这里我们可以看到这返回了一个SelectAdaptivePool2d
的实例,它是 timm 提供的自定义层,支持不同的池化和扁平化配置。在撰写本文时,支持的池选项有:
- 平均值:平均池
- 最大:最大池
- avgmax: 平均池和最大池之和,按 0.5 重新调整
- catavgmax: 沿特征维度的平均和最大池输出的串联。请注意,这将使特征尺寸加倍。
- “”:不使用池,池层被一个标识操作替换
我们可以看到不同池选项的输出形状,如下所示:
修改现有模型
我们还可以使用reset_classifier
方法修改现有模型的分类器和池层:
创建新的分类头
虽然已经证明使用单一线性层作为我们的分类器足以获得良好的结果,但当在下游任务上微调模型时,我经常发现使用稍大的头部可以提高性能。让我们探索如何进一步修改我们的 ResNet 模型。
首先,让我们像以前一样创建我们的 ResNet 模型,指定我们想要 10 个类。因为我们使用了一个更大的头,所以让我们使用 catavgmax 进行池化,这样我们就可以提供更多的信息作为分类器的输入。
从现有的分类器中,我们可以得到输入特征的数量:
现在,我们可以通过直接访问分类器,用修改后的分类头替换最后一层。这里,分类标题的选择有些随意。
用虚拟输入测试模型,我们得到预期形状的输出。现在,我们的改装模型可以开始训练了!
特征抽出
timm 模型还具有用于获得各种类型的中间特征的一致机制,这对于将架构用作下游任务的特征提取器是有用的;比如在物体检测中创建特征金字塔。
让我们通过使用来自牛津宠物数据集的图像来想象这是如何工作的。
我们可以将其转换为张量,并将通道转换为 PyTorch 预期的格式:
让我们再次创建我们的 ResNet-D 模型:
如果我们只对最终的特征图感兴趣——在这种情况下,这是合并之前最终卷积层的输出——我们可以使用forward_features
方法绕过全局合并和分类层。
我们可以将它形象化如下:
多特征输出
虽然正向特征方法可以方便地检索最终特征图,但 timm 还提供了使我们能够将模型用作输出选定级别的特征图的特征主干的功能。
当创建一个模型时,我们可以通过使用参数 features_only=True 来指定我们想要使用一个模型作为特征主干。默认情况下,大多数模型将输出 5 步(并非所有模型都有这么多),第一步从 2 开始(但有些从 1 或 4 开始)。
可以使用*out _ indexes*和
output_stride 参数修改特征级别的指数和步数,如文档中的所示。
让我们看看这是如何与我们的 ResNet-D 模型一起工作的。
如下所示,我们可以获得关于返回的特性的更多信息,例如特定的模块名称、特性的减少和通道的数量:
现在,让我们通过我们的特征提取器传递一个图像,并研究输出。
正如所料,已返回 5 个特征地图。检查形状,我们可以看到通道的数量与我们预期的一致:
可视化每个特征图,我们可以看到,图像是逐步下降采样,正如我们所期望的。
使用火炬特效
TorchVision 最近发布了一个名为 FX 的新工具,它使得在 PyTorch 模块向前传递期间访问输入的中间转换变得更加容易。这是通过象征性地跟踪 forward 方法来产生一个图来完成的,其中每个节点代表一个操作。由于节点被赋予了人类可读的名称,因此很容易准确地指定我们想要访问的节点。FX 在文件和博客中有更详细的描述。
注:在撰写本文时,使用 FX 时,动态控制流还不能用静态图来表示。
由于 timm 中几乎所有的模型都是象征性可追溯的,我们可以使用 FX 来操纵它们。让我们探索一下如何使用 FX 从 timm 模型中提取特征。
首先,让我们从 TorchVision 导入一些 helper 方法:
现在,我们重新创建我们的 ResNet-D 模型,使用分类头,并使用 exportable 参数来确保模型是可跟踪的。
现在,我们可以使用get_graph_nodes
方法按照执行顺序返回节点名称。由于模型被跟踪了两次,在 train 和 eval 模式下,两组节点名都被返回。
使用 FX,可以很容易地从任何节点访问输出。让我们在层 1 中选择第二次激活。
使用create_feature_extractor
,我们可以在该点“切割”模型,如下图所示:
现在,通过我们的特征提取器传递一个图像,这将返回一个张量字典。我们可以像以前一样想象这个:
导出到不同的格式
训练之后,通常建议将您的模型导出到一个优化的格式,以便进行推理;PyTorch 有多种方法可以做到这一点。由于几乎所有的 timm 模型都是可脚本化和可追踪的,我们可以利用这些格式。
让我们检查一些可用的选项。
导出到 TorchScript
TorchScript 是一种从 PyTorch 代码创建可序列化和可优化模型的方法;任何 TorchScript 程序都可以从 Python 进程中保存,并在没有 Python 依赖的进程中加载。
我们可以通过两种不同的方式将模型转换为 TorchScript:
- 跟踪:运行代码,记录发生的操作,构建包含这些操作的 ScriptModule。控制流或动态行为(如 if/else 语句)被删除。
- 脚本:使用脚本编译器对 Python 源代码进行直接分析,将其转换成 TorchScript。这保留了动态控制流,并且对不同大小的输入有效。
关于 TorchScript 的更多信息可以在文档中的和本教程中的中看到。
由于大多数 timm 模型都是可脚本化的,所以让我们使用脚本来导出我们的 ResNet-D 模型。我们可以设置层配置,以便在创建模型时使用可脚本化的参数使模型是 jit 可脚本化的。
在导出模型之前调用model.eval()
将模型置于推理模式是很重要的,因为像 dropout 和 batchnorm 这样的操作符根据模式的不同会有不同的行为。
我们现在可以验证我们能够编写脚本并使用我们的模型。
导出到 ONNX
开放神经网络交换(ONNX) 是一种用于表示机器学习模型的开放标准格式。
我们可以使用torch.onnx
模块将 timm 模型导出到 ONNX 使它们能够被支持 ONNX 的许多运行时所使用。如果用一个还不是 ScriptModule 的模块调用torch.onnx.export()
,它首先执行与torch.jit.trace()
等效的操作;它使用给定的参数执行模型一次,并记录执行过程中发生的所有操作。这意味着,如果模型是动态的,例如,根据输入数据改变行为,则导出的模型将不会捕捉这种动态行为。类似地,跟踪可能只对特定的输入大小有效。
关于 ONNX 的更多细节可以在文档中找到。
为了能够以 ONNX 格式导出 timm 模型,我们可以在创建模型时使用 exportable 参数,以确保模型是可追踪的。
我们现在可以使用torch.onnx.export
来跟踪和导出我们的模型:
我们现在可以使用check_model
函数来验证我们的模型是有效的。
由于我们指定了我们的模型应该是可追踪的,我们也可以手动执行追踪,如下所示。
数据扩充
timm 包括许多数据扩充转换,它们可以链接在一起形成扩充管道;与火炬视觉类似,这些管道期望 PIL 图像作为输入。
最简单的开始方式是使用create_transform
工厂函数,让我们在下面探索如何使用它。
在这里,我们可以看到这已经创建了一些基本的增强管道,包括调整大小,规范化和转换图像为张量。正如我们所料,我们可以看到,当我们设置 is_training=True 时,包括了额外的变换,如水平翻转和颜色抖动。这些增强的幅度可以用诸如 hflip 、v lip和 color_jitter 之类的参数来控制。
我们还可以看到,根据我们是否在训练,用于调整图像大小的方法也有所不同。虽然在验证过程中使用了标准的 Resize 和 CenterCrop ,但是在训练过程中,使用了RandomResizedCropAndInterpolation,让我们看看它在下面做了什么。因为在 timm 中实现这种变换使我们能够设置不同的图像插值方法;这里我们选择插值是随机选择的。
运行几次转换,我们可以观察到不同的作物已采取的形象。虽然这在培训期间是有益的,但在评估期间可能会使任务变得更加困难。
取决于图像的类型,这种类型的变换可能导致图片的主题从图像中被裁剪掉;如果我们看第一行的第二张图片,我们可以看到这样的例子!虽然如果不经常发生,这应该不是一个大问题,但我们可以通过调整比例参数来避免这种情况:
RandAugment
当开始一项新任务时,可能很难知道使用哪些增强,以及以何种顺序使用;随着现在可用的增强数量的增加,组合的数量是巨大的!
通常,一个好的起点是使用在其他任务中表现良好的增强管道。一种这样的策略是 RandAugment,这是一种自动化的数据增强方法,它从一组增强中统一采样操作,如均衡、旋转、曝光、颜色抖动、色调分离、改变对比度、改变亮度、改变锐度、剪切和平移,并顺序应用其中的一些操作;更多信息,请参见原始文件。
然而,在 timm 中提供的实现中有几个关键的差异,timm 的创建者 Ross Wightman 在ResNets Strike Back paper的附录中对此进行了最好的描述,我在下面解释了这些差异:
原始 RandAugment 规范有两个超参数,M 和 N;其中 M 是失真幅度,N 是每幅图像均匀采样和应用的失真数量。RandAugment 的目标是 M 和 N 都是人类可以解释的。
然而,对于 M[在最初的实现中]来说,情况并非如此。几个增强的尺度在该范围内是向后的或者不是单调增加的,因此增加 M 不会增加所有增强的强度。
在最初的实现中,虽然随着 M 的增加,一些增强的强度上升,但是其他增强的强度下降或者被完全移除,使得每个 M 本质上代表其自己的策略。
timm 中的实现试图通过添加“增加”模式(默认启用)来改善这种情况,在该模式中,所有增强强度都随着幅度增加。
这使得增加 M 更直观,因为所有的强化现在应该随着 M 的相应减少/增加而减少/增加强度。
[此外,] timm 添加了一个 MSTD 参数,该参数将具有指定标准偏差的高斯噪声添加到每个失真应用的 M 值中。如果 MSTD 设置为'-inf ',则对于每个失真,M 从 0-M 均匀采样。
在 timm 的 RandAugment 中,为了减少对图像均值的影响,归一化参数可以作为一个参数传递,以便所有可能引入边界像素的增强都可以使用指定的均值,而不是像在其他实现中那样默认为 0 或硬编码元组。
[最后,]默认情况下不包括剪切,以支持单独使用 timm 的随机擦除实施*,这对增强图像的平均值和标准偏差的
影响较小。
- timm 中随机擦除的实现在这里详细探讨。
现在我们已经了解了什么是 RandAugment,让我们看看如何在增强管道中使用它!
在 timm 中,我们通过使用一个配置字符串来定义 RandAugment 策略的参数;它由用破折号(-
)分隔的多个部分组成
第一部分定义了 rand augment 的具体变体(目前只支持rand
)。其余部分可以按任意顺序排列,它们是:
-
m ( 整数):rand 增大的幅度
-
n ( integer ):每个图像选择的变换操作数,这是可选的,默认设置为 2
-
mstd ( float ):应用的噪声幅度的标准偏差
-
mmax ( integer ):将幅度的上限设置为默认值 10 以外的值
-
w ( integer ):概率权重指数(影响操作选择的一组权重的指数)
-
inc ( bool — {0,1} ):使用严重性随幅度增加的增强,这是可选的,默认值为 0
例如:
-
rand-m9-n3-mstd0.5
:产生 9 级随机增强,每幅图像 3 次增强,mstd 0.5 -
rand-mstd1-w0
:mstd 1.0,权重 0,默认 m 值 10,每幅图像放大 2 倍
向create_transform
传递一个配置字符串,我们可以看到这是由RandAugment
对象处理的,我们可以看到所有可用操作的名称:
我们还可以通过使用rand_augment_transform
函数来创建这个对象,以便在自定义管道中使用,如下所示:
让我们将这个策略应用到一个图像上,来可视化一些转换。
由此,我们可以看到使用 RandAugment 给了我们很多不同的图像!
剪切和混合
timm 使用Mixup
类提供了剪切混合和混合增强的灵活实现;它处理这两种扩充并提供在它们之间切换的选项。
使用Mixup,
,我们可以从各种不同的混音策略中进行选择:
- 批次:每批次执行切割混合与混合选择、λ和切割混合区域采样
- pair :对一个批次内的采样对进行混合、lambda 和区域采样
- elem :对批次内的每幅图像进行混合、λ和区域采样
- half :与元素方面相同,但是每个混合对中的一个被丢弃,以便每个样本在每个时期被看到一次
让我们想象一下这是如何工作的。要做到这一点,我们需要创建一个数据加载器,遍历它并将扩充应用到批处理中。我们将再次使用来自 Pets 数据集的图像。
使用 TorchVision 和 timmdocs 的帮助函数,我们可以在没有应用增强的情况下可视化我们批次中的图像:
现在,让我们创建我们的混音转换!Mixup
支持以下参数:
- mixup _ alpha(float):mix up alpha 值,如果>为 0,则 mix up 有效。,(默认值:1)
- cutmix _ alpha(float):cut mix alpha 值,如果>为 0,则 cut mix 有效。(默认值:0)
- cutmix _ minmax(List【float】):cut mix 最小/最大图像比率,cut mix 处于活动状态,如果不是无,则使用此 vs alpha。
- prob ( float ):每个批次或元素应用 mix 或 cutmix 的概率(默认值:1)
- switch _ prob(float):当两者都激活时,切换到 cutmix 而不是 mix 的概率(默认值:0.5)
- 模式 ( str ):如何应用 mixup/cutmix 参数(默认:批次)
- label _ smoothing(float):应用于混合目标张量的标签平滑量(默认值:0.1)
- num _ classes(int):目标变量的类的数量
让我们定义一组参数,以便我们将 mixup 或 cutmix 应用于一批图像,并且以概率 1 交替,并且使用这些来创建我们的“Mixup”变换:
由于 mixup 和 cutmix 发生在一批图像上,所以我们可以在应用增强来加速之前将该批图像放在 GPU 上!在这里,我们可以看到 mixup 已经应用于这批图像。
再次运行增强,我们可以看到,这一次,应用了 CutMix。
从彼此上面打印的标签可以观察到,我们也可以使用Mixup
进行标签平滑!
数据集
timm 为处理不同类型的数据集提供了许多有用的工具。最简单的开始方式是使用create_dataset
函数,它将为我们创建一个合适的数据集。
create_dataset
总是期望两种说法:
- 名称:我们要加载的数据集的名称
- root: 本地文件系统上数据集的根文件夹
但是有额外的关键字参数,可用于指定选项,如我们是否希望加载定型集或验证集。
我们还可以使用create_dataset
,从几个不同的地方加载数据:
- 火炬视觉中可用的数据集
- 张量流数据集中可用的数据集
- 存储在本地文件夹中的数据集
让我们探索其中的一些选项。
从 TorchVision 加载数据集
要加载 TorchVision 包含的数据集,我们只需在希望加载的数据集名称前指定前缀torch/
。如果文件系统上不存在这些数据,我们可以通过设置 download=True 来下载这些数据。此外,我们在这里指定我们希望加载带有 split 参数的训练数据集。
检查类型,我们可以看到这是一个 TorchVision 数据集。我们可以像往常一样通过索引来访问它:
从 TensorFlow 数据集加载数据集
除了通过 TorchVision 使用 PyTorch 时通常可用的数据集,timm 还使我们能够从 TensorFlow 数据集下载和使用数据集;为我们包装底层的tfds
对象。
从 TensorFlow 数据集加载时,建议我们设置几个额外的参数,本地或 TorchVision 数据集不需要这些参数:
- batch_size :用于保证分布式训练时,批量大小除以所有节点的样本总数
- is_training :如果设置,数据集将被混洗。注意,这不同于设置分割
虽然这个包装器从 TFDS 数据集中返回解压缩的图像示例,但是我们需要的任何扩充和批处理仍然由 PyTorch 处理。
在这种情况下,我们用tfds/
作为数据集名称的前缀。可用于图像分类的数据集列表可在这里找到。对于这个例子,我们将任意选择bean数据集。
我们还可以看到,对于拆分参数,我们指定了一个tfds
拆分字符串,如这里的所述。
检查我们的数据集,我们可以看到底层的 TensorFlow 数据集已经被包装在一个IterableImageDataset
对象中。作为一个可迭代的数据集,它不支持索引——参见这里的差异和——所以为了查看来自这个数据集的图像,我们必须首先创建一个迭代器。
我们现在可以使用这个迭代器来依次检查图像和标签,如下所示。
我们可以看到我们的图像已经正确加载!
从本地文件夹加载数据
我们还可以从本地文件夹加载数据,在这种情况下,我们只需使用空字符串(''
)作为数据集名称。
除了能够从 ImageNet 风格的文件夹层次结构中加载之外,create_dataset
还允许我们从一个或多个 tar 文档中提取;我们可以利用这一点来避免必须解压缩存档!作为一个例子,我们可以在 Imagenette 数据集上进行试验。
此外,到目前为止,我们一直在加载原始图像,所以让我们也使用 transform 参数来应用一些转换;这里,我们可以使用前面看到的create_transform
函数快速创建一些合适的转换!
通过检查图像的羞耻,我们可以看到我们的变换已经被应用。
ImageDataset 类
正如我们所见,create_dataset
函数为处理不同类型的数据提供了许多选项。timm 能够提供这种灵活性的原因是通过尽可能使用 TorchVision 中提供的现有数据集类,以及提供一些额外的实现— ImageDataset
和IterableImageDataset
,它们可以在广泛的场景中使用。
本质上,create_dataset
通过选择一个合适的类为我们简化了这个过程,但是有时我们可能希望直接使用底层组件。
我最常使用的实现是ImageDataset
,它类似于torch vision . datasets . image folder,但是增加了一些额外的功能。让我们探索一下如何使用它来加载解压缩的 imagenette 数据集。
ImageDataset
灵活性的关键在于它索引和加载样本的方式被抽象成了一个Parser
对象。
timm 包含了几个解析器,包括从文件夹、tar 文件和 TensorFlow 数据集读取图像解析器。解析器可以作为参数传递给数据集,我们可以直接访问解析器。
这里,我们可以看到默认解析器是ParserImageFolder
的一个实例。解析器还包含有用的信息,比如类查找,我们可以像下面这样访问它。
我们可以看到,这个解析器已经将原始标签转换成整数,可以输入到我们的模型中。
手动选择解析器— tar 示例
因此,除了选择合适的类,create_dataset
还负责选择正确的解析器。再次考虑压缩的 Imagenette 数据集,我们可以通过手动选择ParserImageInTar
解析器并覆盖ImageDataset
的默认解析器来获得相同的结果。
检查第一个样本,我们可以验证这已经正确加载。
创建自定义解析器
不幸的是,数据集并不总是像 ImageNet 那样结构化;也就是说,具有以下结构:
root/class_1/xx1.jpg
root/class_1/xx2.jpg
root/class_2/xx1.jpg
root/class_2/xx2.jpg
对于这些数据集,ImageDataset
不能开箱即用。虽然我们总是可以实现一个自定义数据集来处理这一点,但这可能是一个挑战,取决于数据是如何存储的。另一种选择是编写一个定制的解析器来使用ImageDataset
。
作为一个例子,让我们考虑一下牛津宠物数据集,其中所有图像都位于一个文件夹中,文件名中包含类名——在本例中是每个品种的名称。
在这种情况下,由于我们仍然从本地文件系统加载图像,所以对ParserImageFolder
只做了一点小小的调整。让我们看看这是如何实现的灵感。
由此,我们可以看到“ParserImageFolder”做了几件事:
- 为类创建映射
- 执行
__len__
返回样品数量 - 实现
_filename
来返回样本的文件名,并带有选项来确定它应该是绝对路径还是相对路径 - 执行
__getitem__
返回样品和目标。
现在我们已经了解了我们必须实现的方法,我们可以在此基础上创建我们自己的实现!这里,我使用了 pathlib ,从标准库中提取类名并处理我们的路径;因为我发现它比os
更容易使用。
我们现在可以将解析器的一个实例传递给ImageDataset
,这将使它能够正确加载 pets 数据集!
让我们通过检查第一个样本来验证我们的解析器已经工作。
由此看来,我们的解析器起作用了!此外,与默认解析器一样,我们可以检查已经执行的类映射。
在这个简单的例子中,创建一个自定义数据集实现只需要稍微多做一点工作。然而,希望这有助于说明编写自定义解析器并使其与ImageDataset
一起工作是多么容易!
优化者
timm 具有大量的优化器,其中一些不是 PyTorch 的一部分。除了方便访问熟悉的优化器,如 SGD 、亚当和 AdamW ,一些值得注意的内容包括:
- AdamP :本文所述
- rms propf:基于原始 TensorFlow 实现的 RMSProp 的实现,此处讨论其他小调整。根据我的经验,这通常会比 PyTorch 版本带来更稳定的训练
- LAMB:Apex 的 FusedLAMB 优化器的纯 pytorch 变体,在使用 PyTorch XLA 时与 TPU 兼容
- adabelieve:本文所述。关于设置超参数的指南可在此处获得
- 马德格拉德:本文所述
- AdaHessian :自适应二阶优化器,在本文中描述
timm 中的优化器支持与 torch.optim 中相同的接口,并且在大多数情况下可以简单地放入一个训练脚本中,而无需进行任何更改。
要查看 timm 实现的所有优化器,我们可以检查 timm.optim 模块。
创建优化器最简单的方法是使用create_optimizer_v2
工厂函数,该函数需要:
- 一个模型或一组参数
- 优化程序的名称
- 要传递给优化器的任何参数
我们可以使用这个函数来创建 timm 中包含的任何优化器实现,以及 torch.optim 中流行的优化器和 Apex 中的融合优化器(如果安装的话)。
让我们来看一些例子。
在这里,我们可以看到,由于 timm 不包含 SGD 的实现,它已经使用“torch.optim”中的实现创建了我们的优化器。
让我们尝试创建一个在 timm 中实现的优化器。
我们可以验证已经使用了 timm 的Lamb
实现,并且我们的权重衰减已经应用于参数组 1。
手动创建优化器
当然,如果我们不想使用create_optimizer_v2
,所有这些优化器都可以用通常的方式创建。
optimizer = timm.optim.RMSpropTF(model.parameters(), lr=0.01)
用法示例
现在,我们可以使用大多数优化器,如下所示:
*# replace
# optimizer = torch.optim.Adam(model.parameters(), lr=0.01)
# with* optimizer = timm.optim.AdamP(model.parameters(), lr=0.01)
for epoch in num_epochs:
for batch in training_dataloader:
inputs, targets = batch
outputs = model(inputs)
loss = loss_function(outputs, targets)
loss.backward()
optimizer.step()
optimizer.zero_grad()
在撰写本文时,唯一的例外是二阶Adahessian
优化器,它在执行backward
步骤时需要一个小的调整;类似的调整可能需要额外的二阶优化器,它们可能会在未来添加。
这将在下面演示。
optimizer = timm.optim.Adahessian(model.parameters(), lr=0.01)
is_second_order = (
hasattr(optimizer, **"is_second_order"**) and optimizer.is_second_order
) *# True* for epoch in num_epochs:
for batch in training_dataloader:
inputs, targets = batch
outputs = model(inputs)
loss = loss_function(outputs, targets)
loss.backward(create_graph=second_order)
optimizer.step()
optimizer.zero_grad()
向前看
timm 还使我们能够将前瞻算法应用于优化器;此处介绍和此处精彩讲解。前瞻可以提高学习的稳定性,降低内部优化器的方差,而计算和存储开销可以忽略不计。
我们可以通过在优化器名称前面加上lookahead_
来对优化器应用前瞻。
optimizer = timm.optim.create_optimizer_v2(model.parameters(), opt='lookahead_adam', lr=0.01)
或者由 timm 的 Lookahead 类中的优化器实例进行包装:
timm.optim.Lookahead(optimizer, alpha=0.5, k=6)
当使用 Lookahead 时,我们需要更新我们的训练脚本以包括下面的行,来更新慢速权重。
optimizer.sync_lookahead()
下面是如何使用它的一个例子:
optimizer = timm.optim.AdamP(model.parameters(), lr=0.01)
optimizer = timm.optim.Lookahead(optimizer)
for epoch in num_epochs:
for batch in training_dataloader:
inputs, targets = batch
outputs = model(inputs)
loss = loss_function(outputs, targets)
loss.backward()
optimizer.step()
optimizer.zero_grad()
optimizer.sync_lookahead()
调度程序
在撰写本文时,timm 包含以下调度程序:
- StepLRScheduler :学习率每 n 步衰减一次;类似于 torch.optim.lr_scheduler。StepLR
- multi step scheduler:一个步骤调度器,支持多个里程碑,在这些里程碑上降低学习速率;类似 torch.optim.lr_scheduler。多步
- PlateauLRScheduler :每次指定的指标达到稳定状态时,以指定的因子降低学习率;类似于 torch.optim.lr_scheduler。ReduceLROnPlateau
- 余弦调度器:带重启的余弦衰减调度,如本文所述;类似于 torch.optim.lr_scheduler。CosineAnnealingWarmRestarts】
- tanhlr scheduler:hyberbolic-tangent 衰变纲图,重启,如本文所述
- PolyLRScheduler :多项式衰减时间表,如本文所述
虽然在 timm 中实现的许多调度程序在 PyTorch 中都有对应的程序,但 timm 版本通常有不同的默认超参数,并提供额外的选项和灵活性;所有 timm 调度程序都有预热时期,并且可以选择向调度添加随机噪声。此外,CosineLRScheduler
和PolyLRScheduler
支持称为 k-decay 的衰减选项,如这里的所介绍的。
在研究这些调度器提供的一些选项之前,让我们首先探索如何在定制的训练脚本中使用来自 timm 的调度器。
用法示例
与 PyTorch 中包含的调度程序不同,每个时期更新两次 timm 调度程序是一种很好的做法:
- 在每次优化器更新之后,应该调用
.step_update
方法,使用下一次更新的 Iindex;在这里,我们将为 PyTorch 调度程序调用.step
- 在每个时段的末尾,应该调用
.step
方法,并使用下一个时段的索引
通过明确地提供更新的数量和时期索引,这使得 timm 调度器能够消除在 PyTorch 调度器中观察到的混淆的“最后时期”和“1”行为。
下面是我们如何使用 timm 调度程序的一个例子:
training_epochs = 300
cooldown_epochs = 10
num_epochs = training_epochs + cooldown_epochs
optimizer = timm.optim.AdamP(my_model.parameters(), lr=0.01)
scheduler = timm.scheduler.CosineLRScheduler(optimizer, t_initial=training_epochs)
for epoch in range(num_epochs):
num_steps_per_epoch = len(train_dataloader)
num_updates = epoch * num_steps_per_epoch
for batch in training_dataloader:
inputs, targets = batch
outputs = model(inputs)
loss = loss_function(outputs, targets)
loss.backward()
optimizer.step()
scheduler.step_update(num_updates=num_updates)
optimizer.zero_grad()
scheduler.step(epoch + 1)
调整学习率计划
为了演示 timm 提供的一些选项,让我们探讨一些可用的超参数,以及修改这些参数对学习率计划的影响。
这里,我们将重点关注CosineLRScheduler
,因为这是 timm 的培训脚本中默认使用的调度程序。然而,如上所述,在上面列出的所有调度器中都存在诸如添加预热和噪声之类的特性。
为了让我们能够可视化学习率计划,让我们定义一个函数来创建一个模型和优化器,以便与我们的计划程序一起使用。注意,由于我们将只更新调度程序,模型实际上并没有被优化,但是我们需要一个优化器实例来与我们的调度程序一起工作,而优化器需要一个模型。
def create_model_and_optimizer():
model = torch.nn.Linear(2, 1)
optimizer = torch.optim.SGD(model.parameters(), lr=0.05)
return model, optimizer
使用 PyTorch 中的“CosineAnnealingWarmRestarts”调度程序
为了说明 timm 的余弦调度器不同于 PyTorch 中包含的调度器,我们先来看看如何使用ConsineAnnealingWarmRestarts
的 Torch 实现。
该类支持以下参数:
- T_0 ( int ):第一次重启的迭代次数。
- T_mult ( int ):重启后增加 T_{i} 的因子。(默认值:
1
) - eta _ min(float):最小学习率。(默认值:
0 .
) - last _ epoch(int)—最后一个 epoch 的索引。(默认值:
-1
)
为了设置我们的时间表,我们需要定义以下内容:时期的数量、每个时期发生的更新的数量,以及——如果我们想要允许重新启动——学习速率应该返回到其初始值的步骤的数量。因为我们在这里没有使用任何数据,所以我们可以任意设置这些数据。
num_epochs=300
num_epoch_repeat = num_epochs//2
num_steps_per_epoch = 10
注意:在这里,我们已经指定我们希望学习率在训练运行的中途“重启”。这主要是出于可视化的目的而选择的,这样我们可以了解该调度程序的重启情况,而不是在实际训练运行中使用该调度程序的推荐方式。
现在,让我们创建我们的学习率调度程序。由于 T_0 需要根据迭代次数来指定直到第一次重启的时间——其中每次迭代是一批——我们通过将我们希望重启发生的时期的索引乘以每个时期的步数来计算。这里,我们还指定学习率永远不应该低于“1e-6”。
现在,我们可以在训练循环中模拟使用这个调度程序。因为我们使用 PyTorch 实现,所以我们只需要在每次优化器更新后调用step
,这是每批一次。在这里,我们记录每一步后的学习率值,这样我们可以直观地看到学习率值在整个训练过程中是如何调整的。
从该图中,我们可以看到,学习率衰减到第 150 个时期,在第 150 个时期,学习率在再次衰减之前被重置为初始值;正如我们所料。
使用 timm 中的“CosineLRScheduler”调度程序
现在我们已经了解了如何使用 PyTorch 的余弦调度器,让我们来看看它与 timm 中包含的实现以及提供的其他选项有何不同。首先,让我们使用 timm 实现的余弦学习率调度器— CosineLRScheduler
来复制前面的图。
我们需要这样做的一些论据与我们之前看到的相似:
- t_initial ( int ):第一次重启的迭代次数,相当于 torch 实现中的‘t _ 0’
- lr_min ( float ):最小学习率,相当于 torch 实现中的 eta_min (默认:
0 .
) - cycle _ mul(float):重启后增加 T_{i} 的因子,相当于 torch 实现中的 T_mult (默认:
1
)
但是,为了观察与 Torch 一致的行为,我们还需要设置:
- cycle _ Limit(int):限制一个周期内的重启次数(默认:
1
) - t _ in _ epochs(bool):迭代次数是否按照 epoch 而不是批量更新的次数给出(默认:
True
)
首先,让我们像以前一样定义相同的时间表。
num_epochs=300
num_epoch_repeat = num_epochs/2
num_steps_per_epoch = 10
现在,我们可以创建调度程序实例了。这里,我们用更新步骤的数量来表示迭代的次数,并将周期限制增加到超过我们期望的重新启动次数;以便参数与我们之前在 torch 实现中使用的参数相同。
现在,让我们定义一个新函数来模拟在训练运行中使用 timm 调度程序,并记录学习率的更新。
def plot_lrs_for_timm_scheduler(scheduler):
lrs = []
for epoch in range(num_epochs):
num_updates = epoch * num_steps_per_epoch
for i in range(num_steps_per_epoch):
num_updates += 1
scheduler.step_update(num_updates=num_updates)
scheduler.step(epoch + 1)
lrs.append(optimizer.param_groups[0][**"lr"**])
plt.plot(lrs)
我们现在可以用它来绘制我们的学习进度计划!
正如所料,我们的图表看起来与我们之前看到的一模一样。
既然我们已经复制了我们在 torch 中看到的行为,让我们更详细地看看 timm 提供的一些附加功能。
到目前为止,我们已经用优化器更新来表示迭代次数;这需要我们使用num_epoch_repeat * num_steps_per_epoch
来计算第一次重复的迭代次数,但是,通过根据历元指定迭代次数(这是 timm 中的默认设置),我们可以避免进行这种计算。使用默认设置,我们可以简单地传递我们希望第一次重启发生的时期的索引,如下所示。
我们可以看到,我们的时间表没有改变,我们只是稍微不同地表达了我们的论点。
添加预热和噪音
所有 timm 优化器的另一个特点是,它们支持在学习率计划中增加热身和噪音。我们可以使用 warmup_t 和 warmup_lr_init 参数指定预热时期的数量以及预热期间使用的初始学习率。如果我们指定想要 20 个预热时期,让我们看看我们的时间表是如何变化的。
在这里,我们可以看到,这导致了我们的最低学习率的逐渐增加,而不是像我们之前看到的那样从那个点开始。
我们还可以使用 noise_range_t 和 noise_pct 参数向一系列时期添加噪声。让我们给前 150 个纪元添加少量噪声:
我们可以看到,直到纪元 150,添加的噪声影响我们的时间表,因此学习率不会以平滑的曲线下降。我们可以通过增加 noise_pct 来使其更加极端。
“cosinelrscheduler”的附加选项
虽然预热和噪声可用于任何调度程序,但还有一些附加功能是CosineLRScheduler
特有的。让我们探讨一下这些是如何影响我们的学习率周期的。
我们可以使用 cycle_mul ,增加到下一次重启的时间,如下图所示。
此外,timm 提供了使用 cycle_limit 限制重启次数的选项。默认情况下,该值设置为“1 ”,这将产生以下计划。
CosineLRScheduler
还支持不同类型的衰变。我们可以使用 cycle_decay 来减少(或增加)将在每次连续重启期间设置的学习率值。
注意:这里我们增加了重启次数的频率,以更好地说明衰减。
为了控制曲线本身,我们可以使用 k_decay 参数,学习率的变化率由其 k 阶导数改变,如本文所述。
该选项提供了对该调度程序执行的退火的更多控制!
timm 培训脚本中的默认设置
如果我们使用 timm 培训脚本中的默认设置来设置这个调度程序,我们会看到下面的调度程序。
注:在训练脚本中,训练将继续额外的 10 个周期,而不会进一步修改学习率作为“冷却”。
正如我们所看到的,在默认设置下根本没有重启!
其他学习率计划
虽然 timm 中我最喜欢的调度器是CosineLRScheduler
,但是可视化一些其他调度器的调度可能会有帮助,这些调度器在 PyTorch 中没有对应的调度器。这两种调度器都类似于余弦调度器,即学习率在指定数量的时期后重置(假设没有设置周期限制),但退火的方式略有不同。
对于TanhLRScheduler
,使用双曲正切函数进行退火,如下所示。
timm 还提供了PolyLRScheduler
,它使用多项式衰减:
与CosineLRScheduler
类似,PolyLRScheduler
调度器也支持 k_decay 参数,如下所示:
指数移动平均模型
在训练模型时,通过对在整个训练运行中观察到的参数进行移动平均来设置模型权重值可能是有益的,这与使用在最后一次增量更新之后获得的参数相反。实际上,这通常是通过维护一个 EMA 模型来实现的,它是我们正在训练的模型的副本。然而,不是在每个更新步骤之后更新该模型的所有参数,而是使用现有参数值和更新值的线性组合来设置这些参数。这是使用以下公式完成的:
已更新 _EMA_model_weights =
衰变* EMA_model_weights + (1。—衰变)*更新 _ 模型 _ 权重
其中 decay 是我们设置的参数。例如,如果我们设置衰变=0.99 ,我们有:
已更新 _EMA_model_weights =
0.99 * EMA_model_weights + 0.01 *更新的 _model_weights
我们可以看到它保留了 99%的现有状态,只保留了 1%的新状态!
为了理解为什么这可能是有益的,让我们考虑这样的情况,我们的模型在训练的早期阶段,在一批数据上表现得非常差。这可能导致对我们的参数进行大量更新,过度补偿所获得的高损失,这对即将到来的批次是不利的。通过仅并入最新参数的一小部分,大的更新将被“平滑”,并且对模型的权重具有较小的整体影响。
有时,这些平均参数有时可以在评估期间产生明显更好的结果,并且这种技术已经在流行模型的几个训练方案中使用,例如训练 MNASNet 、 MobileNet-V3 和efficient net;使用 TensorFlow 中包含的实现。使用 timm 中实现的ModelEmaV2
模块,我们可以复制这种行为,并将相同的实践应用到我们自己的训练脚本中。
ModelEmaV2
的实现需要以下参数:
- 模型:nn的子类。我们正在培训的模块。这是将在我们的训练循环中正常更新的模型
- 衰减 : ( float )要使用的衰减量,它决定了之前的状态会保持多少。TensorFlow 文档建议衰减的合理值接近 1.0,通常在多个 9 的范围内:0.999、0.9999 等。(默认值:
0.9999
) - 设备:应该用来评估 EMA 模型的设备。如果未设置此项,EMA 模型将在用于该模型的同一设备上创建。
让我们探索一下如何将这一点融入到培训循环中。
model = create_model().to(gpu_device)
ema_model = ModelEmaV2(model, decay=0.9998)
for epoch in num_epochs:
for batch in training_dataloader:
inputs, targets = batch
outputs = model(inputs)
loss = loss_function(outputs, targets)
loss.backward()
optimizer.step()
optimizer.zero_grad()
model_ema.update(model)
for batch in validation_dataloader:
inputs, targets = batch
outputs = model(inputs)
validation_loss = loss_function(outputs, targets)
ema_model_outputs = model_ema.module(inputs)
ema_model_validation_loss = loss_function(ema_model_outputs, targets)
正如我们看到的,要更新 EMA 模型的参数,我们需要在每次参数更新后调用.update
。由于 EMA 模型和被训练的模型有不同的参数,我们必须单独评估。
值得注意的是,这个类对它的初始化位置很敏感。在分布式训练期间,应该在转换到 SyncBatchNorm 之前和使用分布式数据并行包装器之前应用它!
此外,当保存 EMA 模型时, state_dict 中的键将与被训练模型的键相同,所以应该使用不同的检查点!
把所有的东西放在一起!
虽然整篇文章中的伪代码片段说明了如何在训练循环中单独使用每个组件,但是让我们探索一下一次使用许多不同组件的示例!
在这里,我们将看看如何在 Imagenette 上训练一个模型。注意,由于 Imagenette 是 Imagenet 的子集,如果我们使用预训练的模型,我们会稍微作弊,因为只有新的分类头会用随机权重初始化;因此,在本例中,我们将从头开始训练。
注意:这个例子的目的是演示如何一起使用 timm 的多个组件。因此,所选择的特征以及所使用的超参数在某种程度上是任意选择的;因此,通过一些仔细的调整,性能可能会得到提高!
为了去除我们通常在 PyTorch 训练循环中看到的样板文件,比如遍历数据加载器和在设备之间移动数据,我们将使用 PyTorch-accelerated 来处理我们的训练;这使我们能够只关注使用 timm 组件时所需的差异。
如果您对 PyTorch-accelerated 不熟悉,并且希望在阅读本文之前了解更多相关信息,请查看 介绍性博客文章 或 文档;或者,这很简单,缺乏这方面的知识不会影响您对本文所探讨内容的理解!
在 PyTorch-accelerated 中,训练循环由“训练者”类处理;我们可以覆盖特定的方法来改变特定步骤的行为。在伪代码中,PyTorch 加速训练器中训练运行的执行可以描述为:
train_dl = create_train_dataloader()
eval_dl = create_eval_dataloader()
scheduler = create_scheduler()
training_run_start()
on_training_run_start()
**for** epoch **in** num_epochs:
train_epoch_start()
on_train_epoch_start()
**for** batch **in** train_dl:
on_train_step_start()
batch_output = calculate_train_batch_loss(batch)
on_train_step_end(batch, batch_output)
backward_step(batch_output["loss"])
optimizer_step()
scheduler_step()
optimizer_zero_grad()
train_epoch_end()
on_train_epoch_end()
eval_epoch_start()
on_eval_epoch_start()
**for** batch **in** eval_dl:
on_eval_step_start()
batch_output = calculate_eval_batch_loss(batch)
on_eval_step_end(batch, batch_output)
eval_epoch_end()
on_eval_epoch_end()
training_run_epoch_end()
on_training_run_epoch_end()
training_run_end()
on_training_run_end()
关于训练器如何工作的更多细节可以在文档中找到。
我们可以对默认教练进行子类化,并在培训脚本中使用,如下所示:
在使用 2 个 GPU 的 Imagenette 上使用此培训脚本,按照此处的说明,我获得了以下指标:
- 精度 : 0.89
- ema_model_accuracy : 0.85
34 个纪元后;考虑到超参数还没有调优,这并不坏!
结论
希望这已经提供了 timm 中包含的一些特性的全面概述,以及如何将这些特性应用到定制的培训脚本中。
最后,我想花点时间感谢 timm 的创造者 Ross Wightman 为创建这个令人敬畏的图书馆所付出的巨大努力。Ross 致力于为整个数据科学界提供易于访问的最先进的计算机视觉模型的实现,这是首屈一指的。如果你还没有,那就去加星星吧!
复制这篇文章所需的所有代码都可以在 GitHub gist 这里 找到。
克里斯·休斯正在上LinkedIn。
参考
- rwightman/py torch-image-models:py torch 图像模型、脚本、预训练权重— ResNet、ResNeXT、EfficientNet、EfficientNetV2、NFNet、Vision Transformer、MixNet、MobileNet-V3/V2、RegNet、DPN、CSPNet 等等(github.com)
- 代码为 2021 的论文:一年回顾|作者 Elvis | Papers with Code | 2021 年 12 月| Medium
- ImageNet(image-net.org)
- Pytorch 图像模型(rwightman.github.io)
- Pytorch 图像模型(timm)| timm docs(fastai . github . io)
- PyTorch 图像模型|论文代码
- 【1812.01187】卷积神经网络图像分类的锦囊妙计(arxiv.org)
- 【2010.11929 v2】一张图像抵得上 16x16 字:大规模图像识别的变形金刚(arxiv.org)
- 用于目标检测的特征金字塔网络| IEEE 会议出版物| IEEE Xplore
- 牛津大学视觉几何组
- 火炬视觉—火炬视觉 0.11.0 文档(pytorch.org)
- torch.fx — PyTorch 1.10.1 文档
- 火炬视觉中使用火炬 FX | PyTorch 的特征提取
- TorchScript — PyTorch 1.10.1 文档
- torch script 简介— PyTorch 教程 1.10.1+cu102 文档
- ONNX | Home
- torch.onnx — PyTorch 主文档
- 【1909.13719】rand augment:缩小搜索空间的实用自动数据扩充(arxiv.org)
- 【2110.00476 v1】雷斯内特反击:蒂姆(arxiv.org)的改进训练程序
- 【1905.04899】cut mix:训练具有可本地化特征的强分类器的正则化策略(arxiv.org)
- 【1710.09412】混乱:超越经验风险最小化(arxiv.org)
- torch vision . datasets—torch vision 0 . 11 . 0 文档(pytorch.org)
- 张量流数据集
- torch . utils . data—py torch 1 . 10 . 1 文档
- path lib——面向对象的文件系统路径——Python 3 . 10 . 2 文档
- torch.optim — PyTorch 1.10.1 文档
- 【2006.08217】AdamP:在尺度不变权重上减缓动量优化器的减速(arxiv.org)
- 讲座 _ 幻灯片 _ LEC 6 . pdf(toronto.edu)
- Apex(py torch 扩展)— Apex 0.1.0 文档(nvidia.github.io)
- 【2010.07468】AdaBelief 优化器:根据观测梯度的置信度调整步长(arxiv.org)
- 庄/Adabelief-Optimizer:neur IPS 2020 聚焦“AdaBelief Optimizer:通过对观测梯度的信念来调整步长”(github.com)
- 【2101.11075】不妥协的适应性:随机优化的动量化、适应性、双平均梯度法(arxiv.org)
- 【2006.00719】ADAHESSIAN:用于机器学习的自适应二阶优化器(arxiv.org)
- 【1907.08610】前瞻优化器:向前 k 步,向后 1 步(arxiv.org)
- 前瞻优化器:向前 k 步,向后 1 步| Michael Zhang — YouTube
- torch.optim — PyTorch 1.10.1 文档
- 【1806.01593】分类上具有双曲正切衰减的随机梯度下降(arxiv.org)
- 【2004.05909】k-decay:一种学习速率表的新方法(arxiv.org)
- 【1807.11626 v3】mnas net:面向移动的平台感知神经架构搜索(arxiv.org)
- 【arxiv.org 【1905.11946 V5】efficient net:卷积神经网络模型缩放的再思考
- 【arxiv.org 【1905.02244 V5】搜索 MobileNetV3
- tf.train .指数移动平均| TensorFlow Core v2.7.0
- 介绍 PyTorch 加速|克里斯·休斯| 2021 年 11 月|迈向数据科学
- 欢迎阅读 pytorch-accelerated 的文档!— pytorch 加速 0.1.3 文档
递归神经网络(RNNs)入门
原文:https://towardsdatascience.com/getting-started-with-recurrent-neural-network-rnns-ad1791206412
使用 RNNs 进行情感分析
照片由 Unsplash 的 Nishaan Ahmed 拍摄
本文将讨论一组称为递归神经网络(RNNs)的独立网络,用于解决序列或时间序列问题。
让我们开始吧!
什么是递归神经网络?
一个循环神经网络是一种特殊类别的神经网络,允许信息双向流动。RNN 具有短期记忆,这使它能够在产生输出时考虑先前的输入。短期记忆允许网络保留过去的信息,从而揭示彼此远离的数据点之间的关系。rnn 非常适合处理时间序列和序列数据,如音频和文本。
RNN 结构
下图显示了 RNN 的结构。
展开的网络产生于为每个时间步长 t 创建 RNN 的副本。ht 表示网络在时间 t,的输出,而 Xt 是网络在时间 t 的输入。
rnn 的类型
现在让我们简单地提一下各种类型的 rnn。
rnn 主要有四种类型:
- 单输入单输出的一对一。
- 单输入多输出的一对多,例如图像字幕。
- 多对一多输入单输出,例如情感分析。
- 多对多多输入多输出,例如机器翻译。
递归神经网络是如何工作的?
正如你在上面展开的 RNN 中看到的,RNNs 通过应用时间 (BPPTT)反向传播来工作。以这种方式,当前和先前输入的权重被更新。通过将误差从最后一个时间步长传播到第一个时间步长来更新权重。因此,计算每个时间步长的误差。
rnn 不能处理长期依赖。在很长的时间步长中会出现两个主要问题:消失梯度和爆炸梯度。
RNN 挑战——消失的梯度问题
当用于计算权重的梯度开始消失时,消失梯度出现,意味着它们变成接近零的小数字。结果就是网络不学习。与此相反的是爆炸梯度问题。
爆炸梯度问题通过剪切梯度来解决。消失梯度问题由某种类型的 rnn 解决,这种 rnn 可以处理长期依赖性,例如,长短期记忆(LSTMs) 。
LSTMs
递归神经网络有几种变体。这里有几个例子:
- 长短期记忆(LSTM) 通过引入输入、输出、和忘记门解决了消失梯度问题。这些网关控制着网络中保留的信息。
- 门控循环单元(GRU) 通过引入重置和更新门来处理消失梯度问题,重置和更新门决定在整个网络中保留哪些信息。
在本教程中,我们主要关注 LSTMs。LSTMs 可以处理长期依赖关系,更适合处理长序列问题,如情感分析。例如,在一个长的电影评论中,在决定评论的情绪时,记住评论的开始是很重要的。想想评论,“这部电影相当慢,有些角色相当无聊,但总的来说,这是一部非常有趣的电影。”如果你只考虑这篇评论的第一部分,你会给它贴上负面评论的标签。然而,在评论快结束时,观众说这部电影很有趣。
为情感分析实现 LSTMs
接下来,让我们在 TensorFlow 中实现用于情感分析的 LSTM。我们将使用 Kaggle 上著名的 50K 电影评论的 IMDB 数据集。该数据集在斯坦福大学网站上公开发布。
我们将使用层获取数据并运行项目。因此,第一步是验证图层帐户并初始化项目。
get_dataset
函数可用于获取数据集。
接下来,让我们定义一个从评论中删除常用词的函数。
现在让我们使用上面的函数来清理评论。我们可以保存清理后的数据集,这样就不会再次重复这个过程。
将数据集保存到层是通过用 @dataset 装饰器装饰函数来完成的。pip_requirements
装饰器用于传递运行该函数所需的包。
执行上述函数将保存数据并输出一个可用于查看的链接。
文本预处理
由于数据是文本形式的,我们需要将其转换成数字表示。此外,我们必须做到以下几点:
- 删除特殊字符和标点符号。
- 将评论转换成小写。
我们可以使用来自 TensorFlow 的 Tokenizer 类来执行上述所有操作。这是通过创建该类的一个实例并在训练集上安装它的fit_on_texts
方法来实现的。在这样做的时候,我们定义了一个词汇表之外的标记,在以后将文本转换为序列时,这个标记将用于替换词汇表之外的单词。默认情况下,分词器会保留最常用的单词,但是,这可以使用num_words
参数来覆盖。
此时,每个单词都被映射为一个整数表示。这被称为单词索引,可以通过调用分词器上的*word_index*
来查看。
如果我们可以保存这个标记器,这样我们就不必在这个数据集上再次训练标记器,那就太好了。让我们创建一个函数来保存这个记号赋予器。我们使用 @model decorator ,因为该函数返回一个模型。 @fabric decorator 决定了函数执行的环境类型。
执行上述函数将训练记号赋予器并保存它。
创建文本序列
既然每个单词都有一个整数表示,我们需要为每个评论创建一个序列表示。这是由texts_to_sequences
函数完成的。我们获取刚刚保存的记号赋予器,并将其用于该操作。
下图显示了一个评论及其数字表示:
填充文本序列
处理这些评论的挑战之一是它们的长度不同。但是,我们稍后要构建的 LSTM 神经网络希望数据长度相同。我们通过定义每个评论的最大长度并截断它们来解决这个问题。之后,我们用零填充最大长度以下的评论。填充可以在评论的开始(前)或结束(后)进行。pad_sequences
是从 TensorFlow 中得到的预处理方法。
填充后的数据如下所示:
在创建 LSTM 模型之前,让我们将数据捆绑到一个 TensorFlow 数据集中。
定义 LSTM 模式
准备好数据后,我们可以创建一个简单的 LSTM 网络。为此,我们将使用 Keras 顺序 API 。该网络将由以下主要构件组成:
- 一个嵌入层 。单词嵌入是单词在密集向量空间中的表示。在该空间中,语义相似的单词一起出现。例如,这有助于情感分类,因为负面词汇可以捆绑在一起。
Keras 嵌入层希望我们传递词汇表的大小、密集嵌入的大小和输入序列的长度。该层还在迁移学习中加载预训练的单词嵌入权重。
- 双向 lstm允许数据从两边通过,即从左到右和从右到左。两个方向的输出连接在一起,但可以选择求和、求平均值或相乘。双向 LSTMs 帮助网络学习过去和未来单词之间的关系。
当如下定义两个 LSTM 时,第一个必须返回将传递给下一个 LSTM 的序列。
现在让我们将上述操作捆绑到一个单一的训练函数中。该函数将返回 LSTM 模型。为了保存这个模型、它的度量和参数,我们用@model decorator 包装它。
执行上述函数训练并保存模型。它还记录函数中定义的所有项目,如训练和验证准确性。
经过训练的模型现在可以对新数据进行预测了。
最后的想法
在本文中,我们已经讨论了递归神经网络及其变体。特别是,我们涵盖了:
- 什么是递归神经网络?。
- 一种递归神经网络的结构。
- 与注册护士合作的挑战。
- 如何在 TensorFlow 和层实现 LSTM?
在 LinkedIn 上关注我,了解更多技术资源。
资源
图片经许可使用。
数据集引用
@InProceedings{maas-EtAl:2011:ACL-HLT2011,
author = {Maas, Andrew L. and Daly, Raymond E. and Pham, Peter T. and Huang, Dan and Ng, Andrew Y. and Potts, Christopher},
title = {Learning Word Vectors for Sentiment Analysis},
booktitle = {Proceedings of the 49th Annual Meeting of the Association for Computational Linguistics: Human Language Technologies},
month = {June},
year = {2011},
address = {Portland, Oregon, USA},
publisher = {Association for Computational Linguistics},
pages = {142--150},
url = {http://www.aclweb.org/anthology/P11-1015}
}
开始使用 Streamlit:开始使用时需要知道的 5 个功能
利用这 5 项功能简化您的简化学习
克里斯蒂娜·莫里路摄于 Pexels。
Streamlit 是一个基于 Python 的开源框架,允许您轻松创建和部署交互式数据科学仪表盘和机器学习模型。
该库和公司由一群前谷歌工程师于 2018 年成立,他们认识到数据科学家和机器学习专业人员在开发和部署交互式仪表盘和模型时面临的挑战。作为回应,他们创建了 Streamlit 作为解决方案来简化流程,并使其更容易被更广泛的用户使用。
Streamlit 建立在 Python 之上,它支持许多常用于数据科学和机器学习的主流 Python 库,如 matplotlib 、 plotly 和 pandas 。
要全面了解 Streamlit 的功能,请务必点击下面的链接查看其全面的文档。
https://docs.streamlit.io/library/get-started
在本文中,我们将了解开始使用 Streamlit 必不可少的 5 个功能。使用下面的目录跳转到您感兴趣的部分。
最通用的细流功能:st.write()
用 st.columns()
∘ 控制细流列的宽度
用 st.siderbar()
整理你的 App 用 ST . file _ uploader()
∘用 st.file_uploader()
∘上传多个文件
安装 Streamlit
在运行 Streamlit 应用程序之前,我们首先需要安装库。如果您使用的是 PIP,只需使用下面的命令即可。
pip install streamlit
一旦安装了 Streamlit,我们可以创建一个名为app.py
的新文件,并开始向其中添加我们的代码。
要运行代码,我们需要在终端中输入以下内容:
streamlit run app.py
这将在您的默认浏览器中启动 Streamlit。
最通用的 Streamlit 函数:st.write()
这是 Streamlit 中最通用的功能之一,也是您开始使用时首先遇到的功能之一。
在帮助文档中,该函数被描述为“Streamlit 命令的瑞士军刀”。当你开始使用它的时候,你会发现它是多么的万能。
有了st.write()
我们就可以进去了:
- 文本
- 减价
- 乳液
- 熊猫数据框
- 带有文本和变量的多个参数
- 图表对象
- 表情符号
- 计算
下面的代码演示了st.write()
函数的不同应用。
import streamlit as st
import pandas as pd
import numpy as np
# Simple Text
st.header('Simple Text')
st.write('Here is some simple text')
# Latex
st.header('LaTeX Equations')
st.write("""Here is a simple equation using LaTeX
$$
ax^2 + bx + c
$$
""")
# Markdown
st.write('## Markdown')
st.write('Here is some more **markdown** text. *And here is some more in italics*')
# Emojis
st.header('Emojis')
st.write('And an emoji or two :smile: :thumbsup:')
# Calculations
st.header('Calculations')
a = 3
b = 3
st.write('And calculations:', a + b )
# Dataframes
st.header('Dataframes')
arr_data = np.random.default_rng().uniform(0, 100, size=(5,5))
df = pd.DataFrame(arr_data, columns=list('ABCDE'))
st.write(df)
当我们运行上面的代码时,我们得到下面的页面。
使用 Streamlit st.write()函数可以实现的功能示例。图片由作者提供。
使用st.columns()
将列添加到您的 Streamlit 应用程序
开始使用 Streamlit 应用程序时,您显示的任何内容都包含在一列中。这对于一些简单的应用程序来说可能没问题,但是如果您想要创建稍微复杂一点的布局呢?
这就是[st.columns()](https://docs.streamlit.io/library/api-reference/layout/st.columns)
功能发挥作用的地方。
由于 Streamlit 没有创建网格的本地方法,我们可以使用这个函数开始向我们的应用程序添加列。
# Creating a dataframe with random values in 5 columns
arr_data = np.random.default_rng().uniform(0, 100, size=(5,5))
df = pd.DataFrame(arr_data, columns=list('ABCDE'))
# Creating a simple figure
fig, ax = plt.subplots(1,1)
ax.scatter(x=df['A'], y=df['B'])
col1, col2 = st.columns(2)
with col1:
st.write('## Column 1')
st.write("""This is column 1 and we can create a description or
explanation for the plot on the right""")
with col2:
st.write('## Column 2')
st.write('This is column 2 and it contains a plot')
st.pyplot(fig)
当我们运行我们的应用程序时,我们现在看到两列:
使用 st.columns()创建的包含两列的 Streamlit 应用程序。图片由作者提供。
或者,我们可以删除with
语句,直接在列对象上添加方法,如下所示:
col1.write('## Column 1')
col1.write("""This is column 1 and we can create a description or
explanation for the plot on the right""")
col2.write('## Column 2')
col2.write('This is column 2 and it contains a plot')
col2.pyplot(fig)
控制细流列的宽度
如果我们想要控制这些列的宽度,我们可以将一个列表传递给st.columns()
函数。在这个例子中,我使用了[1,3]
,这意味着 Streamlit 将生成 2 列。第二列的宽度将是第一列的三倍。
col1, col2 = st.columns([1,3])
with col1:
st.write('## Column 1')
st.write("""This is column 1 and we can create a description or
explanation for the plot on the right""")
with col2:
st.write('## Column 2')
st.write('This is column 2 and it contains a plot')
st.pyplot(fig)
使用 st.columns()创建的具有 2 个不同宽度列的 Streamlit 应用程序。图片由作者提供。
使用 st.siderbar()整理您的应用程序
当你开始用 Streamlit 构建更大的应用程序时,你可能会发现它变得杂乱,并且很难找到你放在页面上的任何小部件。
在我们的 Streamlit 应用程序上有一个侧边栏(以类似于传统网页的方式),允许我们将关键输入参数、文件上传器和导航全部放在 Streamlit 应用程序的左侧。
要为我们的 Streamlit 应用程序创建侧边栏,我们只需调用st.sidebar
,然后调用任何标准的 Streamlit 函数。
# Sidebar
st.sidebar.header('Sidebar')
st.sidebar.write('Add your parameters or file uploaders here')
st.sidebar.button('Magic Button')
# Main Section
st.header('Simple Text')
st.write('Here is some simple text')
当这段代码运行时,我们可以看到我们已经将应用程序分成了两部分。左侧包含侧边栏,右侧是我们的主要部分,我们可以在这里显示图表、数据等等。
Streamlit 应用程序中的侧边栏。图片由作者提供。
如果你想知道如何使用侧边栏来创建导航菜单,那么看看我几个月前分享的下面这篇文章:
使用 st.file_uploader()上传文件
Streamlit 为您提供了允许应用程序用户上传文件的功能。与对加载过程进行硬编码相比,这是一种更好的加载数据的方式。
要创建一个文件上传小部件,我们可以调用st.file_uploader()
并传入一些参数。在这个例子中,我们可以使用 label 参数来提供关于用户应该上传什么的信息。
st.header('File Uploader')
uploaded_file = st.file_uploader(label='Please upload your file')
运行上面的代码将创建一个基本的文件上传器小部件。
Streamlit 应用程序中的文件上传器小部件。图片由作者提供。
Streamlit 中的文件上传程序已经预先格式化,因此您不需要使用任何 CSS 或 HTML 来增强其外观。
使用 st.file_uploader()上传多个文件
如果我们想允许上传多个文件,我们可以添加一个accept_multiple_files
参数,并将其设置为真:
st.header('File Uploader')
uploaded_file = st.file_uploader(label='Please upload your file',
accept_multiple_files=True)
使用 st.file_uploader()限制文件格式
如果我们想将文件上传限制到特定的文件扩展名,我们可以添加type
参数并传入一个文件扩展名列表:
st.header('File Uploader')
uploaded_file = st.file_uploader(label='Please upload your file',
type=['.png', '.jpg', '.gif'])
使用 st.metric()显示关键指标
构建仪表板时,通常需要显示数据集中的关键指标或值。这可以让你轻松地与读者交流,让他们一目了然地了解关键信息。
您可以用st.write()
函数来实现这一点,但是这样做需要几行代码和使用 markdown。
为了简化关键指标的显示,我们可以使用st.metric()
功能。
该函数接受一个标签、一个值(可以是您之前计算的变量)和一个增量(值的变化)。
st.header('Metrics')
col1, col2 = st.columns(2)
with col1:
st.metric(label="Min Pressure", value="30 psi", delta="-50 psi")
with col2:
st.metric(label="Max Pressure", value="3000 psi", delta="100 psi")
当我们运行我们的应用程序时,我们将看到我们的关键指标。每个指标的增量也会显示出来,并根据是负变化还是正变化进行颜色编码。
使用 st.metrics()在 Streamlit 仪表板上显示关键指标。图片由作者提供。
摘要
Streamlit 是一款功能强大、用户友好的工具,可以轻松创建和访问用于数据科学和机器学习的仪表盘。创建一个 Streamlit 应用程序相对简单,有了这 5 个关键功能,你就可以马上开始了。
感谢阅读。在你走之前,你一定要订阅我的内容,把我的文章放到你的收件箱里。 你可以在这里做!*或者,您也可以* 注册我的简讯 免费将更多内容直接发送到您的收件箱。
其次,通过注册会员,你可以获得完整的媒介体验,并支持我和其他成千上万的作家。每月只需花费你 5 美元,你就可以接触到所有精彩的媒体文章,也有机会通过写作赚钱。
如果你用 我的链接报名,你直接用你的一部分费用支持我,不会多花你多少钱。如果你这样做了,非常感谢你的支持**
NLP 的 Streamlit 入门
原文:https://towardsdatascience.com/getting-started-with-streamlit-for-nlp-75fe463821ec
轻松构建生产就绪的 NLP 应用
背景由 Fakurian 设计于 Unsplash 上,由作者编辑。
Streamlit 已经证明了自己是一个非常受欢迎的工具,可以快速组装高质量的面向 ML 的 web 应用程序。最近,它在生产环境中被越来越大的组织广泛采用。
所有这些都意味着现在是利用 Streamlit 获取经验的最佳时机。幸运的是,Streamlit 的基础非常容易学习,对于大多数工具来说,这将比您需要的更多!
在本文中,我们将通过构建一个通用知识问答界面来介绍 Streamlit。我们将了解关键的 Streamlit 组件,如write
、text_input
、container
。如何使用 Bootstrap 之类的外部库快速创建新的 app 组件?并使用缓存来加速我们的应用程序。
简化设置
开始时,我们需要打开一个终端窗口并编写pip install streamlit
。我们可以检查所有安装了streamlit hello
的东西,它应该显示这样一条信息:
在(希望)打开一个新的浏览器窗口之前。
如果您已经做到这一步,Streamlit 现在已经成功安装,我们可以继续使用 Streamlit 构建一些东西。
智能搜索框
我们将建立一个智能语义搜索工具,由句子转换器和松果的矢量数据库在幕后驱动。
在初始化这两个组件之前,我们将创建一个搜索组件的“模型”。首先,我们为我们的应用程序创建一个新目录,并添加一个包含我们应用程序核心的文件app.py
。
在app.py
中,我们可以添加标题、描述和搜索栏。
为了了解这是怎么回事,我们打开一个终端窗口,导航到包含app.py
的目录,然后编写streamlit run app.py
。
如果我们在浏览器中输入上述地址之一,我们应该会看到类似这样的内容:
超级简单,我们在这里所做的就是以 markdown 格式写一个标题和段落文本,然后 Streamlit 将它转换成一个格式良好的网页。
# Q&A AppAsk me a question!
现在我们需要一个输入框,这样我们的用户可以问一个问题。和以前一样,这是超级容易做到的。
然后我们使用文本输入框 Streamlit 组件st.text_input
。我们将文本输入提示设置为"Search!"
,默认值为空字符串""
。
我们在输入框中输入的任何内容都将存储在query
变量中。因此,我们可以通过再次使用st.write
并写入query
的内容来检查这一点。
每当我们输入新的内容并按下 Enter 时,Query = '<something>'
就会用我们的新查询进行更新。这个更新过程表明了 Streamlit 的执行逻辑。当 Streamlit 发现应用程序中的某些内容发生变化时,会从头到尾重新运行完整的脚本。
在我们向我们的应用程序添加任何东西之前,我们将删除Query = '<something>'
行,并添加 Q &检索器和松果矢量数据库组件。
聪明的部分
我们已经准备好了大部分的 Streamlit 接口。那么,让我们来看看应用程序的 NLP 组件。
我们的目标是给定一个文本数据语料库(在这种情况下,我们使用维基百科文章),我们希望能够提出一个问题,并返回一段相关的文本(一个上下文)。
NLP 部分是利用一种自然语言问答的风格,叫做OpenDdomainQuestion-Answering(ODQA)。一个完整的实现需要三个组件;向量数据库、检索器和阅读器。
对于我们当前的范围,我们将重点关注前两个组件:
- MPNet 检索器模型,它将把我们现有的段落(上下文)和查询编码成向量。
- 松果向量数据库,它存储我们的上下文向量,并允许在给定查询向量的情况下搜索这些向量。
首先,我们需要创建上下文向量,并将它们推送到向量数据库中。我们不会深入细节,但是可以在这里找到代码。
在此代码中,我们:
- 使用 HuggingFace 数据集从 SQuAD v2 数据集下载我们的上下文。
- 使用
pinecone/mpnet-retriever-squad2
检索器模型对它们进行编码以创建上下文向量。 - 通过
upsert
将上下文向量推送到松果向量数据库(这需要一个免费的 API 密钥)。
完成后,我们继续在应用程序中集成检索功能。代码与上面类似,但是这次我们不是下载上下文,也不是初始化一个新的向量数据库索引。
这一次,我们需要做的就是初始化检索器,并初始化到qa-index
松果索引的连接。
实现了 ODQA 管道的前两个组件后,我们的app.py
脚本如下所示:
回到我们的浏览器,我们现在应该能够问一个问题并返回五个相关段落或上下文。
这是我们应用的核心。让我们继续改进它的性能以及如何显示结果。
表演
当我们将句子转换器和松果组件添加到应用程序中时,整个应用程序变慢了。这样做的原因是,正如我们前面提到的,每当发生任何变化时,Streamlit 都会重新加载整个脚本。
幸运的是,Streamlit 通过在应用程序重新加载之间缓存应用程序的部分内容,提供了避免重新加载应用程序的方法。
要做到这一点,我们需要将我们想要缓存的进程放在函数内部。然后我们用@st.experimental_singleton
来修饰这些函数,如下所示:
现在,当我们第一次加载我们的应用程序时,可能需要一些时间来加载检索器模型并初始化我们与松果的连接。但是一旦我们开始搜索,Streamlit 就再也不会重新加载这些组件,使得搜索速度快得令人难以置信。
让结果看起来不错
为了让我们的结果看起来更好,我们将使用来自 Bootstrap 框架的预构建组件。这些非常容易使用,我们所做的就是找到我们想要使用的组件,例如这些卡,然后将 HTML 代码复制到我们的应用程序中。
我们用 Python 变量替换了代码的相关部分,添加了返回的上下文、标题和 ID。这些是在我们迭代松果调用返回的每个结果时生成的。
我们的卡看起来不像我们看到的引导卡。这是因为我们没有设置任何引导 CSS 样式。如果我们回头看卡片 HTML,我们可以看到几个class="something"
参数。这些控件控制卡片的外观,但是它们依赖于一个外部 CSS 文件。
要将这些引导 CSS 类添加到我们的应用程序中,我们需要做的就是在脚本的顶部添加一行 HTML 代码:
这一行拉入一个bootstrap.min.css
文件,允许我们使用所有的引导 CSS 类。如果我们回到我们的浏览器窗口,我们会看到我们光滑的新卡片。
在此基础上,我们使用 Streamlit、句子转换器和 Pinecone 设计了一个开放领域 QA web 应用程序的核心组件。
当然,对于这种支持 ML 的 web 应用,我们还可以做更多的——这是我们很快就会探索的事情。
同时,你可以通过这个链接获得最新的文章。我还在 YouTube 上上传 ML 和 NLP 的每周视频。
感谢阅读!
*所有图片均由作者提供,除非另有说明
开始使用基于 Streamlit Web 的应用程序
原文:https://towardsdatascience.com/getting-started-with-streamlit-web-based-applications-626095135cb8
创建 Streamlit web 应用程序的简明介绍
卡斯帕·卡米尔·鲁宾在 Unsplash 上的照片
Streamlit 是一个基于 python 的开源框架,用于开发和部署交互式数据科学仪表盘和机器学习模型。这意味着您不必依赖前端开发团队或花费大量时间学习 web 设计语言,如 HTML 、 CSS 或 Javascript 来部署您的仪表板或模型。
Streamlit 由前谷歌工程师于 2018 年创立,他们在开发和部署机器学习模型和仪表板时,获得了面临挑战的第一手经验。
它建立在 Python 之上,支持许多主流 Python 库,如 matplotlib 、 plotly 和 pandas 。
如果您想深入了解 Streamlit 的全部功能,请务必通过下面的链接查看他们的优秀文档。
数据
本教程的数据可以在下面的 GitHub 资源库链接中找到。它起源于一个包含全球地震信息的 Kaggle 数据集。
原始数据可通过以下链接获得,并可在 CC0:公共领域下使用。
https://www.kaggle.com/datasets/usgs/earthquake-database?select=database.csv
https://github.com/andymcdgeo/streamlit_tutorial_series
我为这篇文章发布了一个附带的视频教程,可以在下面访问:
安装 Streamlit
在运行 Streamlit 应用程序之前,我们首先需要安装库。如果您使用的是 PIP,只需使用下面的命令即可。
pip install streamlit
构建简化的应用程序
创建 app.py 文件
在您选择的 Python IDE 中,创建一个名为 app.py 的新文件。
导入库
该过程中的第一个编码步骤是导入所需的库。对于这个简单的应用程序,我们将导入 streamlit 、 pandas 和 matplotlib 。
*#Import the required Libraries*import streamlit as st
import pandas as pd
import matplotlib.pyplot as plt
运行 Streamlit 应用
要确认 streamlit 已正确安装和导入,并且可以运行,请转到终端并导航到 app.py 文件所在的位置,然后运行以下命令:
streamlit run app.py
您的浏览器窗口应该会打开,您将看到一个空白的 streamlit 窗口,如下所示。如果您希望在另一个浏览器中打开应用程序,终端中还会显示网址。
一旦你的网络浏览器打开,你会注意到两件事,一个汉堡菜单在右上角,一个用 Streamlit 制作的文本在左下角。
使用 streamlit run app 后的初始 Streamlit app . py .图片由作者创建。
向 Streamlit 应用程序添加文本
现在我们已经确认了 Streamlit 正在运行,我们可以开始向我们的应用程序添加元素了。
为了让人们理解我们的应用程序,我们可以使用st.title
和st.text
给它一个标题和简短的描述。
*# Add a title and intro text*st.title('Earthquake Data Explorer')
st.text('This is a web app to allow exploration of Earthquake Data')
当您保存 app.py 文件时,您会看到新项目出现在您的 streamlit 应用程序的右上角。这通知您源文件已经更改,您有两个选择。
保存对源代码文件的更改后,出现在 streamlit 中的选项。图片由作者提供。
重新运行 —点击此按钮将重新运行应用程序并刷新显示。每次对源文件进行更改并保存时,都需要按下此按钮。
始终重新运行 —单击此按钮将在更改保存到源文件时自动重新运行应用程序。
选择其中一个选项后,应用程序现在将显示我们输入的标题和文本。
Streamlit 应用程序显示作者使用 st.title 和 st.write. Image 的内容。
在 Streamlit 中创建文件上传程序
Streamlit 的一大优点是用户可以上传自己的文件。这是通过st.file_uploader()
实现的。
*# Create file uploader object*upload_file = st.file_uploader('Upload a file containing earthquake data')
在为用户创建上传文件的方式时,这段代码做了大量繁重的工作。当你添加这个并重新运行应用程序时,你会得到一个漂亮的上传框,你可以手动浏览文件或拖放文件。
使用 st.file_uploader()的文件上传程序对话框。图片由作者提供。
一旦我们设置了文件上传程序,我们现在需要添加一些基本的逻辑来检测文件何时被上传。如果有,则读取上传的 csv 文件。
if upload_file is not None: *# Read the file to a dataframe using pandas* df = pd.read_csv(upload_file)
所有剩余的代码现在都可以缩进放置在 if 语句下。这样,除非文件已经上传,否则后续代码不会运行。
在 Streamlit 中显示熊猫描述和数据
从熊猫访问df.describe()
和df.head()
方法非常简单。我们将这些函数包装在st.write()
方法中,所有的格式都会自动排序。
*# Create a section for the dataframe statistics* st.header('Statistics of Dataframe')
st.write(df.describe())*# Create a section for the dataframe header* st.header('Header of Dataframe')
st.write(df.head())
为了测试这是否有效,从 Streamlit 教程系列 GitHub 存储库中选择 CSV 文件(ka ggle _ significant _ seismics _ database . CSV),并使用文件上传程序将其上传到应用程序。
文件上传后,应该会显示数据的统计信息以及前五行。
在 streamlit 中使用 st.write 方法显示 pandas 函数的示例。图片由作者提供。
用 Streamlit 显示 Matplotlib 图形
这个简单应用程序的最后一步是显示来自 matplotlib 的图形。
首先,我们需要定义一个图形和轴。在本例中,我将它们分配给了plt.subplots()
。
在这种情况下,我们可以创建图表,这将是一个地震深度与震级的散点图。
然后我们将fig
包装在st.pyplot()
中。
fig, ax = plt.subplots(1,1)ax.scatter(x=df['Depth'], y=df['Magnitude'])
ax.set_xlabel('Depth')
ax.set_ylabel('Magnitude')st.pyplot(fig)
当我们再次运行应用程序后,我们将会看到 matplotlib 图。
使用 streamlit 的 st.pyplot()函数显示 matplotlib 图形。
返回的图形是标准的 matplotlib 图形,没有任何交互功能。不使用 matplotlib,可以使用 plotly 创建交互式绘图。
完整代码
下面是使用 Streamlit 库生成一个非常简单的 web 应用程序的完整代码。
*#Import the required Libraries*import streamlit as st
import pandas as pd
import matplotlib.pyplot as plt*# Add a title and intro text* st.title('Earthquake Data Explorer')
st.text('This is a web app to allow exploration of Earthquake Data')*# Create file uploader object* upload_file = st.file_uploader('Upload a file containing earthquake data')*# Check to see if a file has been uploaded* if upload_file is not None: *# If it has then do the following:* *# Read the file to a dataframe using pandas* df = pd.read_csv(upload_file) *# Create a section for the dataframe statistics* st.header('Statistics of Dataframe')
st.write(df.describe()) *# Create a section for the dataframe header* st.header('Header of Dataframe')
st.write(df.head()) *# Create a section for matplotlib figure* st.header('Plot of Data') fig, ax = plt.subplots(1,1)
ax.scatter(x=df['Depth'], y=df['Magnitude'])
ax.set_xlabel('Depth')
ax.set_ylabel('Magnitude')
st.pyplot(fig)
下面是用上面的代码生成的最终 web 应用程序。该应用程序是非常静态的,只会在新文件上传时发生变化。
简化基于网络的应用程序,以可视化地震数据。图片由作者提供。
摘要
在这个简短的教程中,我们看到了如何用 Streamlit 创建一个非常简单的 web 应用程序。它允许我们将数据加载到我们的应用程序中,并立即显示描述性统计数据和 matplotlib 图。目前的应用程序是静态的,这意味着用户只会看到一个静态的数字和数据,没有交互或通过参数更新代码。这可以很容易地使用选择框,plotly 和许多其他 Streamlit 功能来增强。
如果你想要一个方便的小备忘单和 streamlit 一起使用,那么从 Streamlit 文档中查看这个。
感谢阅读。在你走之前,你一定要订阅我的内容,把我的文章放到你的收件箱里。 你可以在这里做!
其次,通过注册会员,你可以获得完整的媒介体验,并支持我自己和成千上万的其他作家。它每个月只花你 5 美元,你可以完全接触到所有令人惊叹的媒体文章,也有机会用你的写作赚钱。如果你使用 我的链接报名,你会直接用你的一部分费用支持我,不会多花你多少钱。如果你这样做了,非常感谢你的支持!
文本/自然语言处理可视化入门
原文:https://towardsdatascience.com/getting-started-with-text-nlp-visualization-9dcb54bc91dd
了解如何可视化文本数据来传达您的故事
来自像素的免费使用照片
介绍
自然语言处理(NLP)有多种定义,但我经常引用的一个定义如下:
NLP 致力于建造能够理解和响应文本或语音数据的机器,并用它们自己的文本或语音做出响应,就像人类做的一样。[1]
由于在众多应用程序、平台和机构中生成的文本数据量不断增加,NLP 市场正在快速增长。因此,知道如何从文本数据中传达有意义和有趣的故事是至关重要的。为此,你需要视觉化。在这篇文章中,让我们学习如何开始可视化文本数据。更先进的预处理和可视化文本数据的方法将在另一篇文章中介绍!
文本基本概要统计的可视化分布
表格数据的“汇总统计”概念同样适用于文本数据。汇总统计帮助我们描述数据的特征。在文本数据的上下文中,此类统计数据可以是组成每个文本的字数,以及统计数据在整个语料库中的分布情况。为了观察分布,我们可以使用可视化方法,包括核密度估计(KDE)图和直方图等。但是为了介绍哪些统计数据可以被可视化。hist()函数用在所有的代码片段中。
假设我们有一个评论数据集,其中包含一个名为“description”的变量,该变量中有文本描述。
【1】每段文字的长度
reviews[‘description’].str.len().hist()
【2】****每篇文字字数
# .str.split( ) returns a list of words split by the specified delimiter
# .map(lambda x: len(x)) applied on .str.split( ) will return the number of split words in each textreviews[‘description’].str.split().map(lambda x: len(x)).hist()
另一种方法是:
reviews[‘description’].apply(lambda x: len(str(x).split())).hist()
[3]每个文本中唯一的字数
reviews[‘description’].apply(lambda x: len(set(str(x).split()))).hist()
[4]每个文本中的平均单词长度
reviews[‘description’].str.split().apply(lambda x : [len(i) for i in x]).map(lambda x: np.mean(x)).hist()
另一种方法是:
reviews[‘description’].apply(lambda x: np.mean([len(w) for w in str(x).split()])).hist()
[5]每个文本中的中值单词长度(分布)
reviews[‘description’].apply(lambda x: np.median([len(w) for w in str(x).split()])).hist()
【6】停止字数统计
NLP 上下文中的停用词指的是频繁出现在语料库中但对理解文本的各个方面(包括情感和极性)没有添加太多意义的一组词。例如代词(例如他、她)和冠词(例如 the)。
检索停用词主要有两种方法。一种方法是使用 NLTK 包,另一种方法是使用 wordcloud 包。在这里,我使用 wordcloud 包,因为它已经在一个名为“停用词”的子模块中预定义了一整套英语停用词。
from wordcloud import STOPWORDS### Other way to retrieve stopwords# import nltk# nltk.download(‘stopwords’)# stopwords =set(stopwords.words(‘english’)) reviews[‘description’].apply(lambda x: len([w for w in str(x).lower().split() if w in STOPWORDS])).hist()
【7】字符数
reviews[‘description’].apply(lambda x: len(str(x))).hist()
【8】标点数
reviews[‘description’].apply(lambda x: len([c for c in str(x) if c in string.punctuation])).hist()
额外收获:检查常态
对于新创建的统计变量(如平均单词长度),您可能希望检查它们是否满足某些条件,例如,某些模型或回归算法要求变量符合某些分布。一种这样的检查是通过查看概率图来检查变量的分布是否足够接近正态分布。
**from** scipy **import** stats
**import** statsmodels.api **as** smstats.probplot(reviews['char_length'], plot=plt)
接近正态分布的分布示例
不接近正态分布的分布示例。
可视化顶部 N-Grams
什么是 n-gram?根据斯坦福大学的语音和语言处理课程材料,一个“n-gram 是一个文本句子中 n 个单词的序列 n-gram。”[2]
例如,句子“我很酷”的二元模型(n-gram,n=2)将是:
[ (‘I’, ‘am”), (‘am’, ‘cool’)]
为了可视化最频繁出现的顶部 N 元文法,我们需要首先将我们的词汇表表示成某种数字矩阵形式。为此,我们使用了计数矢量器。根据 Python 的 scikit-learn 包文档,“Countvectorizer 是一种将文本文档集合转换为令牌计数矩阵的方法。”[3]
下面的函数首先将文本矢量化成某种适当的矩阵形式,其中包含记号(这里是 n 元文法)的计数。请注意,可以指定参数 stop_words,以便在进行计数时忽略指定语言的停用词。
**def** ngrams_top(corpus, ngram_range, n=None): *### What this function does: List the top n words in a vocabulary according to occurrence in a text corpus.* vec = CountVectorizer(stop_words = ‘english’, ngram_range=ngram_range).fit(corpus) bag_of_words = vec.transform(corpus) sum_words = bag_of_words.sum(axis=0) words_freq = [(word, sum_words[0, idx]) for word, idx **in** vec.vocabulary_.items()] words_freq =sorted(words_freq, key = lambda x: x[1], reverse=True) total_list=words_freq[:n] df = pd.DataFrame(total_list, columns=[‘text’,’count’]) **return** df
下面的函数将 ngram_range 指定为(1,1),所以我们只对基本上只是单个单词的单字元感兴趣。这里 n=10 意味着我们有兴趣查看描述语料库中的前 10 个单字。
unigram_df = ngrams_top(reviews[‘description’], (1,1), n=10)
然后,我们可以使用 seaborn 将其可视化为一个水平条形图。
# seaborn barplotsns.barplot(x=’count’, y=’text’) #horizontal barplot
如果你想使用稍微花哨一点的交互式可视化形式,我推荐 plotly express,它的语法非常简单,但能让你用几行代码创建交互式可视化。
# fancier interactive plot using plotly express**import** plotly.express **as** pxfig = px.bar(unigram_df, x='unigram', y='count', title=’Counts of top unigrams', template='plotly_white', labels={'ngram;: ‘Unigram’, ‘count’: ‘Count’})fig.show()
更先进的预处理和可视化文本数据的方法将在另一篇文章中介绍!
关于作者
数据科学家。加州大学欧文分校信息学博士生。
密歇根大学刑事司法行政记录系统(CJARS)经济学实验室的前研究领域专家,致力于统计报告生成、自动化数据质量审查、构建数据管道和数据标准化&协调。Spotify 前数据科学实习生。Inc .(纽约市)。
他喜欢运动、健身、烹饪美味的亚洲食物、看 kdramas 和制作/表演音乐,最重要的是崇拜我们的主耶稣基督。结账他的 网站 !
参考
【1】什么是自然语言处理?、IBM 云、https://realpython.com/python-assert-statement/
[2]朱拉夫斯基和马丁。语音和语言处理(2021 年 12 月最后更新)。https://web.stanford.edu/~jurafsky/slp3/3.pdf
[3] Scikit 学习文档。https://sci kit-learn . org/stable/modules/generated/sk learn . feature _ extraction . text . count vectorizer . html
Polars 数据帧库入门
原文:https://towardsdatascience.com/getting-started-with-the-polars-dataframe-library-6f9e1c014c5c
了解如何使用 Polars dataframe 库操作表格数据(并替换 Pandas)
汉斯-尤尔根·马杰在 Unsplash 上拍摄的照片
大多数使用 Python 的数据科学家/分析师都熟悉熊猫。如果你在数据科学领域,你可能已经投入了相当多的时间学习如何使用它们来操作你的数据。然而,对熊猫的主要抱怨之一是它在处理大型数据集时的速度和效率低下。幸运的是,有一个新的数据框架库试图解决关于熊猫的这一主要抱怨。
Polars 是一个完全用 Rust 编写的 DataFrame 库。在这篇文章中,我将带你了解极地犬的基本知识,以及如何用它来代替熊猫。在后续文章中,我将深入研究 Polars 的各种特性。
什么是 Polars?
理解 Polars 的最好方法是,它是一个比 Pandas 更好的数据框架库。以下是极地狗相对于熊猫的一些优势:
- Polars 不对数据帧使用索引。消除索引使得操作数据帧变得更加容易(无论如何,索引在 Pandas 数据帧中是多余的)。
- Polars 使用 Apache Arrow 数组在内部表示数据,而 Pandas 使用 NumPy 数组在内部存储数据。Apache Arrow arrays 在加载时间、内存使用和计算等方面效率更高。
- Polars 比熊猫支持更多的并行操作。由于 Polars 是用 Rust 编写的,它可以并行运行许多操作。
- Polars 支持懒评估。根据你的查询,Polars 将检查你的查询,优化它们,并寻找加速查询或减少内存使用的方法。而熊猫只支持热切求值,一遇到表达式就立刻求值。
安装 Polars
要安装 Polars,只需使用pip
命令:
**pip install polars**
或者,使用conda
命令:
**conda install polars**
对于本文,我将假设您已经安装了 Anaconda,并且熟悉 Jupyter Notebook。
创建 Polars 数据框架
学习一个新库的最好方法是亲自动手。因此,让我们从导入polars
模块并创建 Polars 数据帧开始:
import polars as pldf = pl.DataFrame(
{
'Model': ['iPhone X','iPhone XS','iPhone 12',
'iPhone 13','Samsung S11','Samsung S12',
'Mi A1','Mi A2'],
'Sales': [80,170,130,205,400,30,14,8],
'Company': ['Apple','Apple','Apple','Apple',
'Samsung','Samsung','Xiao Mi','Xiao Mi'],
}
)
df
像熊猫一样,Polars 在 Jupyter Notebook 中展示数据帧时会将其打印出来:
作者图片
Polars 希望列标题名称是字符串类型。考虑下面的例子:
df2 = pl.DataFrame(
**{
0 : [1,2,3],
1 : [80,170,130],
}** )
上面的代码片段将而不是工作,因为字典中的键是整数类型(0 和 1)。要使它工作,您需要确保键是字符串类型(“0”和“1”):
import polars as pl
df2 = pl.DataFrame(
{
**"0"** : [1,2,3],
**"1"** : [80,170,130],
}
)
除了显示每一列的标题名称,Polars 还显示每一列的数据类型。如果想明确显示每一列的数据类型,使用dtypes
属性:
df.**dtypes**
对于上述示例,您将看到以下输出:
[polars.datatypes.Utf8,
polars.datatypes.Int64,
polars.datatypes.Utf8]
要获得列名,使用columns
属性:
**df.columns** #['Model', 'Sales', 'Company']
要以元组列表的形式获取数据帧的内容,请使用rows()
方法:
df.**rows()**
对于上述示例,您将看到以下输出:
[('iPhone X', 80, 'Apple'),
('iPhone XS', 170, 'Apple'),
('iPhone 12', 130, 'Apple'),
('iPhone 13', 205, 'Apple'),
('Samsung S11', 400, 'Samsung'),
('Samsung S12', 30, 'Samsung'),
('Mi A1', 14, 'Xiao Mi'),
('Mi A2', 8, 'Xiao Mi')]
Polars 没有指数的概念,不像熊猫。Polars 的设计理念明确指出索引在数据帧中没有用。
选择列
在 Polars 中选择列非常简单——只需使用select()
方法指定列名:
*df.**select**(
'Model'
)*
上述语句返回一个包含型号列的 Polars 数据帧:
作者图片
Polars 还支持方括号索引方法,这是大多数熊猫开发者都熟悉的方法。然而,Polars 的文档特别提到方括号索引方法是 Polars 的反模式。虽然您可以使用
df[:,[0]]
完成上述操作,但是在 Polars 的未来版本中,方括号索引方法可能会被删除。
如果需要多列,请以列表形式提供列名:
*df.select(
**['Model','Company']**
)*
作者图片
如果想要检索数据帧中的所有整数(特别是Int64
)列,可以在select()
方法中使用一个表达式:
*df.select(
**pl.col(pl.Int64)**
)*
众所周知,语句pl.col(pl.Int64)
是一个极坐标表达式。该表达式被解释为“获取所有数据类型为 Int64 的列”。上述代码片段产生以下输出:
作者图片
在极性中,表达式是非常强大的。例如,你可以把和连在一起的表达式,就像这样:
*df.select(
**pl.col(['Model','Sales']).sort_by('Sales')**
)*
上面的表达式选择了型号和销售列,然后根据销售列中的值对行进行排序:
作者图片
如果需要多列,可以将表达式包含在列表中:
*df.select(
**[pl.col(pl.Int64),'Company']**
)*
作者图片
如果您想获得所有字符串类型的列,请使用pl.Utf8
属性:
*df.select(
** [pl.col(pl.Utf8)]**
)*
作者图片
我将在以后的文章中更多地讨论表达式。
选择行
要选择数据帧中的一行,使用row()
方法传入行号:
*df.row(0) # get the first row*
结果是一个元组:
*('iPhone X', 80, 'Apple')*
如果您需要基于行号获取多行,您需要使用方括号索引方法,尽管在 Polars 中这不是推荐的方法。以下是一些例子:
df[:2]# first 2 rows
df[[1,3]] # second and fourth row
要选择多行,Polars 建议使用filter()
功能。例如,如果您想要检索所有苹果产品,您可以使用以下表达式:
*df.filter(
**pl.col('Company') == 'Apple'**
)*
作者图片
您也可以使用逻辑运算符指定多个条件:
*df.filter(
**(pl.col('Company') == 'Apple') |
(pl.col('Company') == 'Samsung')** )*
作者图片
您可以在极坐标中使用以下逻辑运算符:
|
—或者
&
——还有
~
—不
选择行和列
通常,您需要同时选择行和列。您可以通过链接filter()
和select()
方法来实现,如下所示:
*df.**filter**(
pl.col('Company') == 'Apple'
).**select**('Model')*
上面的语句选择所有包含苹果的行,然后只显示型号列:
作者图片
如果您还想显示Sales
列,请向select()
方法传递一个列表:
*df.filter(
pl.col('Company') == 'Apple'
).select(**[**'Model'**,'Sales']**)*
作者图片
如果你喜欢阅读我的文章,并且认为它对你的职业/学习有所帮助,请考虑注册成为一名灵媒会员。每月 5 美元,你可以无限制地访问 Medium 上的所有文章(包括我的)。如果你使用下面的链接注册,我会赚一小笔佣金(不需要你额外付费)。你的支持意味着我将能够投入更多的时间来写这样的文章。
*https://weimenglee.medium.com/membership
我将在即将到来的新加坡 ML 会议(2022 年 11 月 22-24 日)上主持一个关于 Polars 的研讨会。如果你想在 Polars 数据框架上快速起步,请在https://ml conference . ai/machine-learning-advanced-development/using-Polars-for-data-analytics-workshop/上注册我的研讨会。
摘要
在本文中,我已经向您展示了 Polars 数据框架的基础。您已经学习了提取行和列的基础知识以及select()
和filter()
方法的使用。以下是何时使用它们的快速总结:
作者图片*
了解微服务
原文:https://towardsdatascience.com/getting-to-know-microservices-da5a836a7abd
将复杂的软件拆分成单独的服务的原因
照片由 @charlybron 在 Unsplash 上拍摄
微服务起源于软件架构领域,描述了一种结构,在这种结构中,软件由许多可以通过接口进行通信的独立服务组成。
微服务是如何工作的?
在微服务架构中,一个程序被分割成许多单独的服务,这些服务承担预定义的子任务,并且可以相互通信。这些服务是独立的,具有以下特征:
- 这些服务很少甚至没有相互依赖。
- 这些服务可以单独部署,彼此独立。这意味着技术基础设施也可以适应服务的负载分布。
- 在许多情况下,责任在于明确定义的团队。
- 可以构建微服务来解决特定的业务问题。
微服务中的这种结构通过将复杂的程序分割成较小的、可管理的服务来实现它们。此外,程序还可以通过添加新服务或删除现有服务(因为不再需要它们)来轻松地进行回溯性扩展。
单片和微服务架构
基本上有两种不同的方法来开发大型软件。在所谓的整体架构中,完整的功能被打包到单个服务中。代码中的各个元素紧密交织,有时甚至无法区分。例如,在 Python 编程中,一个有许多函数的类可以代表一个整体。
另一方面,微服务架构使用许多代表独立功能的独立服务。为了使它成为一致的软件,函数通过 API 接口进行通信,从而交换它们的中间结果。
在汽车行业可以找到微维修和整体维修之间区别的实际例子。一家大型的结构化汽车制造商将汽车的制造分成许多不同的部门。有油漆车身的特殊团队和制造发动机的其他团队。生产的中间阶段通过装配线到达相应的部门。在软件开发中,部门会是许多不同的微服务。
另一方面,一个想修理或修复自己汽车的私人机械师表现得像一块巨石。如果发动机需要修理,他可以检查并更换损坏的零件。同样的,他也会给有划痕的部位喷漆。
微服务有什么特点?
当仔细观察软件时,可以通过两个基本特征来识别微服务架构。
- 服务是自给自足的:各个组件完全相互独立,也就是说,它们可以毫无问题地扩展或部署,并且不会影响整个软件。自给自足的第一个标志是使用 API 进行服务间的通信。
- 服务是专门化的:所有的组件都有一个他们解决的特定问题。如果问题太复杂,可以在函数中调用其他服务来帮助解决。
微服务相对于单片有什么优缺点?
近年来,微服务架构相对于整体架构已经获得认可,主要是因为以下优势:
- 独立性:开发团队可以彼此独立工作,只需要交换关于 API 接口格式的信息。所有其他决策都可以以对服务最优的方式做出。例如,这包括编程语言,因此最终的软件可能包含用不同语言编程的服务。同时,部分软件也可以由外部团队开发。
- 敏捷:服务的创建可以非常快,因为单个问题并不复杂,因此团队可以快速而简单地工作。这也意味着团队之间的协调相对较少,因为服务是相互独立的。
- 扩展:通过拆分成服务,计算密集型功能可以有针对性地进行扩展。另一方面,在单片架构中,整个软件必须按比例增加,这反过来会大大增加成本。例如,可以为使用神经网络的个别服务配备 GPU。
- 兼容性:由于通过接口的简单通信,接口之间的依赖性相对较低。这意味着服务也可以用于全新的项目。
- 弹性:一旦一个服务失败,其他服务仍然可以响应,整个软件仍然可以访问。另一方面,对于一个整体来说,单一功能的故障或错误会导致整个系统的故障。因此,维护和测试的成本明显较高。
尽管微服务提供了大量优势,但在引入这种架构时,也需要考虑一些挑战。
monoliths 的优点是可以进行集中记录,即系统的状态通过一个中心点返回。在微服务架构中,必须首先建立这样的日志记录,否则只能接收关于单个服务的信息。这也使得对软件的监控变得复杂,这在中央是不可能的。
由于分布式架构,测试系统变得更加复杂,因为大量的服务是互连的。同时,服务之间的通信可能会出现延迟,从而降低整个系统的速度。如果软件响应时间是一个关键问题,微服务架构可能已经过时了。
现有系统很难迁移到这样的架构。因此,如果可能的话,应该尝试在新的开发中引入该架构,因为它会显著增加现有软件所需的工作量。
微服务和 Docker 有什么关系?
由于微服务是相互独立的,所以它们也可以在自己的环境中运行,只要这些环境可以通过接口相互通信。虚拟化平台 Docker 经常用于此目的。它使得在一台机器上启动几个彼此独立运行的所谓容器成为可能。
单个服务所需的所有信息和资源都包含在这个容器中。此外,如果服务需要,容器还可以单独缩放,即变得更强大。
为什么微服务在机器学习中很重要?
机器学习,尤其是深度学习,通常需要相对较大的计算能力来提供可以在生产环境中使用的快速结果。同时,ML 服务应该是可伸缩的,以处理峰值负载。出于这些原因,在云中部署经过训练的机器学习模型是有意义的。
然而,将完成的模型和机器学习管道划分到不同的微服务中是有意义的,以便尽可能地节约成本和资源。特别是神经网络和变压器可以用 GPU 相对快速地计算。但是在云中使用 GPU 在很多情况下是很昂贵的,所以只有模型计算的微服务才应该运行在 GPU 上。另一方面,预处理和后处理也可以在没有 GPU 的情况下进行。
https://medium.com/nerd-for-tech/easy-guide-to-transformer-models-6b15c103bfcf
因此,在许多用例中,将复杂的模型划分为微服务以提高部署效率是有意义的。然而,具体的优势取决于许多因素,应该与每个个案中重新编程的额外努力进行权衡。
这是你应该带走的东西
- 微服务架构描述了一个软件概念,其中一个系统被分成许多解决子问题的独立服务。
- 与此相反的是整体,其中软件由一个结合了所有功能的大型结构组成。
- 微服务的优点是独立于单独的服务,增加了兼容性,并且易于扩展。
如果你喜欢我的作品,请在这里订阅https://medium.com/subscribe/@niklas_lang或者查看我的网站* 数据大本营 !还有,medium 允许你每月免费阅读 3 篇 。如果你希望有无限制的 访问我的文章和数以千计的精彩文章,不要犹豫,点击我的推荐链接:【https://medium.com/@niklas_lang/membership】每月花$5***获得会员资格**
* *
两个概率分布故事中的巨人
原文:https://towardsdatascience.com/giants-in-a-tale-of-2-probability-distributions-38e968dda10e
米格南的“灾难性巨人”比喻(2022)。
用灾难性的巨大隐喻理解正态分布和幂律分布尾部之间的差异(Python 代码)
从前有正态分布,这种分布描述了日常生活的“正常”过程:人的身高、智商测试结果、股票市场的每日波动。但是在看似平常的一天,地震发生了,一颗陨石从天而降,股票市场崩溃了。极端事件比看起来更频繁地发生。这里出现了自然本身的幂律,它代表了复杂系统的正常行为,使得正态分布几乎成为一个误称。数学家伯努瓦·曼德尔布罗早就通过分形的概念对此进行了诊断。如今,一个令人惊讶的负面结果的常见隐喻是黑天鹅,它统治着纳西姆·塔勒布的“极端斯坦”省,远离平静的“中等斯坦”。不要再向均值回归,再也没有均值了!"如果你在处理来自极端地区的数量,你将很难计算出任何样本的平均值,因为它在很大程度上取决于一次观察。这个想法并不比那个(Taleb,2007:34)更难。
这是一个关于两种概率分布*的故事,也是一个练习,旨在通过使用每天观察到的量,即个人身高,来理解罕见事件(遵循幂律)的频率。
冠军的巨人还没有出现,但是很快就会来拜访我们。正态分布定义为
在互补累积密度函数(CCDF)的情况下,具有平均值和σ标准差。例如,对于美国职业棒球大联盟运动员的身高分布(图 1,左侧),我们得到的平均值为 1 米 87,标准偏差为 6 厘米。利用 68–95–99.7 规则,仅考虑上尾部,我们得到 Pr(> + 3σ = 2m 05) = 0.0015。乔恩·劳奇身高 2 米 11,是美国职业棒球大联盟历史上最高的球员。他是我们正态分布中的尾部事件,是一个“4-sigma 事件”(注意,美国成年男性的平均值是 69 英寸或 1 米 75;Fryar 等人,2021 年:表 12)。
图 1 | 左:美国职业棒球大联盟球员身高的正态分布,均值 73.7 英寸,标准差 2.3 英寸。右图:幂指数为 1.63 的火山喷发规模的幂律分布(数据来源—左图:统计在线计算资源(SOCR)1035 条记录的数据集 ,右图:来自全球大规模爆炸性火山喷发数据库 LaMEVE 的 1000 年至 2011 年期间的喷发;Crosweller 等人,2012 年)。图转载自米格南(2022)。
至于 CCDF 幂律,它最简单的函数形式是
其中,α是幂指数,x0 是最小阈值。幂指数的最大似然估计为
以火山爆发的大小分布为例(图 1,右),我们得到α = 1.63。大多数其他危险,自然的和人为的,也可以用幂律分布来描述:从地震到恐怖主义,通过山体滑坡,停电和网络攻击(图 2)。
图 2 | 以年费率λ或累计数定义的各种风险的幂律大小分布。数据来源:地震:1900-2012 年国际地震中心-全球地震模型(ISC-GEM)全球仪器地震目录(Storchak 等人,2013 年)。火山爆发:1000–2014 年全球大规模爆炸性火山爆发数据库(LaMEVE) (Crosweller 等人,2012 年)。滑坡:2008 年中国汶川地震引发的事件清单(徐等,2014)。停电:美国 1984 年至 2002 年间受停电影响的客户数量数据集(Clauset 等人,2009 年)。网络攻击:2005–2018隐私权利信息交换机构(PRC)分类黑客/恶意软件目录。《恐怖主义:1968 年 2 月至 2006 年 6 月全球恐怖袭击数据集》(Clauset 等人,2009 年)。图转载自米格南(2022)。
为了便于说明,假设棒球运动员的身高分布代表了世界人口,我们可以通过将 CCDF 乘以世界人口(约 80 亿)来估计身高 X 或更高的人口比例。对于 X = 2m 21,我们得到 31 个个体,这非常接近来自不同国家的 27 ' 个最高的活着的人,具有相同的 X 阈值。顺便说一下,2 米 21 是扮演丘巴卡的彼得·梅靥和扮演捕食者的凯文·彼得·豪尔的身高。对于 X = 2m 25,我们得到 0.4,尽管考虑了 21。注意分布对标准偏差微小变化的敏感性。如果我们使用 sigma = 6 cm 而不是拟合结果 5.86 cm,我们将获得略多于一个的个体。增加到 6.5 厘米,我们得到 25 个人。在我们的一生中,看到一个高于 2 米 30 的男人的几率下降到 0.001–0.2,为 5.86 < σ < 6.50 cm。罗伯特·瓦德洛(1918-1940),也被称为奥尔顿巨人,是有记录以来最高的人,身高 2 米 72,离圣经中的巨人歌利亚(2 米 97)不远。然而,他是一个明显的异数,在我们有生之年看到他的机会是 6x10(–38)–2.6x10(–29).考虑到所有曾经踏上地球的人类对结果的改变很小,因为累积的世界人口估计约为 1000 亿。这表明,过早死亡的极高的人不在健康的人类生理范围之内。事实上,罗伯特·瓦德洛和历史上的其他破纪录者都患有肢端肥大症。
我们注意到,对于健康人群来说,这种可能性下降得有多快。观察 80 亿人口,我们观察(并模拟)了大约 30 个个体,大小和食肉动物凯文·彼得·豪尔差不多(2 米 21)。再增加几厘米,那么大的体型几乎不可能出现。在正常情况下,在我们的日常习惯中,钟形曲线决定了我们的感知。但是如果高度遵循与自然和人为灾难相同的分布呢?比方说,我们多久能看到一个 3 米的巨人?10 米高的巨人怎么样??这是一个重要的思想实验,因为这样的隐喻巨人实际上不时以灾难的形式拜访我们。有时,它们会让人大吃一惊(例如,塔勒布的“埃斯特雷米斯坦”省的黑天鹅)。
我们的思想实验相当简单,定义如下。
(1)我们进入巨人领域的锚点将是 X0 = 2m 21 处的捕食者,单位时间内的发生率固定为 1(为方便起见,我们将采用年发生率)。每年,一个 X0 大小的巨人都会出现在世界上的某个地方,然后再次消失在某个藏身之处。“小巨人”造成了严重的破坏,但没有什么是像阿诺德·施瓦辛格这样的英雄所不能控制的。同样,按照正常的高度分布,几乎不可能看到更大的东西(例如,塔勒布平静的“中等王国”)。
(2)火山爆发规模的幂律分布将通过定义灾难的“高度”提供一种连接到正态分布的方法——我们的巨人!—作为喷发体积 V 的球体的直径(回想一下图 1 的右图,我们也将使用相同的幂指数)。为了简单起见,我们在这里将绕过所有的异速生长规则。我们还将假设事件“高度”和释放的破坏性能量之间的比例关系。同样,这个练习的目的是通过使用每天观察到的量,即个体的身高,来理解罕见事件(遵循幂定律)的频率。
因此,如果一年观察到一次捕食者,身高遵循幂律分布,那么《权力的游戏》的 Wun Weg Wun Dar Wun(4.3 米)可能每 3 年拜访我们一次,金刚(15 米)每 40 年一次,佐敦(巨魔猎人中的 60 米)每 500 年一次,哥斯拉(100 米)每 1300 年一次。不像正态分布那样几乎不可能!这如图 3 所示。尽管幂律分布的概率 Pr(≥X)在线性 y 轴图上似乎趋于零,但与正态分布相比,概率衰减足够慢,极端事件并非不可能发生。
图 3 | “灾难性巨人”比喻——幂律与正态分布,以及占据幂律胖尾的巨人。图转载自米格南(2022)。
使用正态分布而不是幂律分布的错误可能会对人们的钱包产生直接影响。事实上,使用现代投资组合理论(假设回报遵循高斯分布),我们可以预测 1987 年黑色星期一类型的事件在 5.2 亿年中发生一次!(索尔内特,2003 年:16)。经验观察提供证据表明,这种崩溃可能每几十年发生一次,符合幂律。那次坠机是不是 Wun Wun 事件?一个金刚事件?人们现在可以想象未来可能出现的“巨人”的大小类别。
*-“两个分布的故事”(如哈里森,1981 年)及其双关语“两个分布的尾巴”指的是查尔斯·狄更斯 1859 年的小说“双城记”,其中“最好的时代”是正态分布,“最坏的时代”是管理灾难大小分布的幂律。
Python 代码片段
# complementary CDF for normal distribution
def CCDF_norm(x, mu, sd):
return .5 * scipy.special.erfc((x - mu) / (sd * numpy.sqrt(2)))# complementary CDF for power-law distribution
def CCDF_pow(x, xmin, alpha):
return (x / xmin)**(-(alpha - 1))# power exponent from maximum likelihood estimation method
def get_alpha(x, xmin):
ind = x >= xmin
alpha = 1 + len(x[ind]) / numpy.sum(numpy.log(x[ind] / xmin))
return alpha# import data
X = ... a 1D numpy array of a given quantity ...# empirical CCDF
X = numpy.sort(X)
CCDF_X = 1 - numpy.linspace(0, 1, len(X))# for function plotting: for linear or log x-axis
Xi_lin = numpy.arange(xmin, xmax, xbin_lin)
Xi_log = 10 ** numpy.arange(numpy.log10(xmin), \
numpy.log10(xmax), xbin_log)# Normal distribution scenario
mu = X.mean()
sig = X.std()
CCDFi_norm = CCDF_norm(Xi_lin, mu, sig)# Power-law distribution scenario
Xmin = numpy.min(X)
alpha = get_alpha(X, Xmin)
CCDFi_pow = CCDF_pow(Xi_log, Xmin, alpha)
参考
Clauset A,Shalizi CR,Newman M (2009),经验数据中的幂律分布。暹罗评论,51(4),661–703
Crosweller 等人(2012 年),全球大规模爆炸性火山爆发数据库(LaMEVE)。应用火山学杂志,1,4
Fryar CD,Carroll MD,Gu Q,Afful J,Ogden CL (2021),儿童和成人的人体测量参考数据:美国,2015–2018。【https://www.cdc.gov/nchs/data/series/sr_03/sr03-046-508.pdf 国家卫生统计中心,生命健康统计,3(46)——
哈里森 A (1981),收益的大小:两次分配的故事。经济研究评论,XLVIII,621–631 页。
米格南 A (2022),巨灾风险建模介绍。准备中的教科书。
Sornette D (2003),关键市场崩溃。物理报告,378,1–98。
Storchak 等人(2013 年),公开发布的 ISC-GEM 全球仪器地震目录(1900-2009 年)。地震研究快报,84,810–815。
塔勒布·N(2007),《黑天鹅:极不可能事件的影响。美国纽约市兰登书屋。
徐 C,徐 X,姚 X,戴 F (2014),2008 年 5 月 12 日汶川 7.9 级地震诱发滑坡的三次(近)完整清查及其空间分布统计分析。山体滑坡,11,441–461。
关于作者
A.米格南是灾难风险建模副教授,发表了 70 多篇同行评议文章,并在机器学习和灾难风险的 TDS 上发表了几篇文章。他在 2021 年在他的课程'巨灾风险建模介绍'(教材编写中,截至 2022 年 5 月)中首次引入了巨灾的比喻。
巨人的脚步:约翰·科尔特兰标志性独奏的分形结构
音乐作为数据系列
在 Coltrane 著名的独奏中,音程相关数据的分布是幂律在起作用的一个极好的例子
巨型台阶动画中的一帧(图片由作者提供)
当你开始看到音乐的可能性时,你会渴望为人们做些好事,帮助人类从困境中解脱出来。
—约翰·科尔特兰(1926 年至 1967 年)
约翰·柯川在他的作品《巨大的台阶》中的即兴创作,最早录制于 1959 年,是爵士乐历史上开创性的唱片之一。在一首 16 小节的歌曲中有 26 个和弦变化。在这段相对较短的时间里,音调中心在三个键(B、G 和 Eb)之间总共变化了 10 次。加上大约每分钟 291 拍的快速节奏,平稳即兴创作的能力被广泛认为是顶级爵士乐手与其他人区分的基准。
约翰·科尔特兰 1960 年发行的专辑《大踏步》。(灰度,原始图像由 Ged Carroll ,CC 由 4.0)
事实是,Coltrane 的独奏旋律优美,措辞流畅。对于区间相关数据的分布,它也是幂律关系的一种极好的表现。下面是一个动画,提供了一个进入独奏结构的动态窗口。每个乐器的数据表示为时间序列,其高度对应于音符值,长度对应于音符持续时间。为了视觉上的清晰,每个仪器相对于彼此的范围的基线被调整。
观看动画时,通过观察橙色萨克斯管音符之间的垂直间距分布,可以直观地了解我们想要测量的结构。总的来说,小型跳跃多,中型跳跃少。这些都被相对较少的大跳跃打断。数量和大小之间的这种关系可以用幂律精确地描述。
幂律
幂律是研究分形几何的一个基本属性。它们描述了两个变量之间的关系,即一个变量与另一个变量的幂成比例。例如,动物代谢率的异速生长可以通过关系式代谢率 = k ⋅(weight)^(3/4).来确定从星系分布到财富分布,幂律是自然科学和社会科学中各种现象的基础。
它们也可能出现在音乐的许多特征中,如音高、音程、持续时间、节奏变化,以及一种称为动力音阶的分形结构形式,其中旋律或节奏主题在不同的时间音阶中同时重复【1】。
分形音乐
快速搜索术语分形音乐会得到一大堆描述。在大多数情况下,结果只是指通过将分形图像映射到音高、持续时间、力度和乐器声音的某种组合而生成的音乐。这种类型的音乐可能会引人注目,但只是将像素映射到音乐声音并不能确保结果具有分形特征。任何这样的假设都类似于期待亨利·沃兹沃斯·朗费罗的一首诗翻译成希腊语时押韵。
伯努瓦·曼德尔布罗有一种强烈的直觉,认为音乐在某种可测量的意义上可以是分形的。鉴于分形几何学首先是几何学,我们有理由将术语分形音乐保留给拥有一些明显可测量的缩放属性的音乐。
需要注意的是,分形音乐并不是现代创作。至少在五个世纪前,随着法国-佛兰德作曲家 Josquin des Prez 的作品,动音阶出现。我们现在可以认识到,这部作品是已知的最早的分形音乐作品之一。
和巨步的情况一样,音乐的分形特征往往表现为统计上的自相似分布。在这种情况下,我们在异质元素集合的上下文中寻找幂律。这意味着无论我们放大到什么地方,我们都会发现大致相同的尺寸分布。
如何发现幂定律
对于大小不一的异质系列旋律音程,{ s ₁ ,s ₂ ,s ₃ , …},我们可以统计出每种大小音程的个数{ N ₁, N ₂, N ₃,…},其中 Nₖ 对应大小 sₖ 。如果当我们绘制出 Nₖ 对 sₖ 的双对数图时,这些点落在一条直线上,那么这个序列具有旋律音程标度。假设拟合相当好,则该线的斜率可以解释为该组元素的尺寸 d 。这是因为取幂律关系的对数将其转换为线性方程:
等式 1
在方程 1 中,如果我们让y= Log(N)x= Log(1/s,那么我们得到的就是一条线的方程,y=MX+b,其中 d = m 和 b =0。有关这种类型的分析及其在音乐中的应用的详细信息,请参见[1,2]。
数据
对于萨克斯独奏,我使用了 Jazzomat 研究项目的音高数据。以下是音程的分布,对于一个 k 半音的音程,每个音程的大小等于 2^( k /12 ):
表 1。约翰·科尔特兰独奏曲《大台阶》的音程分布。对于一个 k 半音的音程,每个音程的大小等于 2^( k /12)。
数值 2^( k /12)来源于西方音乐常用的等音律音阶的物理学。下面是区间数据图。它产生的尺寸为 d ≈8.3:
Coltrane's Giant Steps solo 区间数据的双对数图,表明 d≈8.3。
我们可以使用一种叫做 宁滨 的数据平滑技术,在某种意义上,放松我们的注意力,在这种情况下,获得稍微更好的拟合。我们将把间隔放入大小相等的容器中,每个容器包含大小大致相同的间隔。通常使用最低值、最高值或平均值来分配箱的代表性大小,该选择一致地应用于所有箱。明确地说,虽然宁滨可以帮助揭示幂律的存在,但它不能在不存在幂律的地方产生幂律。
这里是每个仓为三个半音宽的区间分布,以及产生维度为 d ≈8 的数据图:
左边是分段间隔数据的分布,以及 d≈8 的数据的双对数图。
R 的值稍微好一点,按照一般的科学标准,肯定是值得尊敬的。然而,我们可以看到,大小和计数之间的关系强度仍然不像我们希望的那样一致,正如代表性大小为 8 和 11 的箱中的计数所证明的那样。这些数字大致相等,事实上,增加而不是减少。
另一方面,当我们观察音程变化的方式时,情况就更清楚了。我们将音程之间的差异称为旋律瞬间。如果音程是音高的第一差,那么旋律瞬间就是第二差。下面是原始力矩数据的分布:
表 2
注意,最小的矩大小是 0 个半音,这对应于两个连续音程具有相同大小的情况。以下是原始数据和面元宽度为 3 个半音的面元数据的曲线图:
原始和分段矩数据的双对数图。
原始数据和分级数据显示出与各自的分形维数 d ≈8.1 和 d ≈8.3 的良好一致性。随着 R 值分别为 0 . 930 和 0 . 998,旋律力矩分布显示了分形结构的明确证据。
结束语
当以这种方式分析音乐时,重要的是要记住,一般来说,按照科学标准,我们正在探索的数据集很小。因此,我们在这样一个假设下工作,即在一个旋律的特征和它潜在的音程分布之间有很强的联系。例如,音阶旋律听起来与包含大跳跃的旋律非常不同。从这个意义上说,在音程音阶存在的地方,我们可以断言,一个以相同旋律方式延续的作品也将在几个音阶中继续表现出自相似的元素分布。
几个世纪以来,似乎最伟大的音乐都有一种透明感和永恒感,无论是巴赫还是披头士。我当然不是唯一一个认为约翰·科尔特兰的音乐拥有这些相同品质的人。在某种程度上,他的音乐也反映了他周围的世界,在某种意义上,它表达了他所经历的世界的分形性质,这并不奇怪。
参考
[1]伯努瓦·曼德尔布罗 中的“分形音乐的本质”:多维度的生活【2】巴赫大提琴组曲中的音程音阶
【3】贾佐马研究项目
感谢您的阅读!如果你还没有,你可以考虑成为一名订阅会员,这样你就可以无限制地阅读 Medium 上所有精彩的文章。如果你 使用此链接 注册,你也可以支持我作为一个作家、研究者、创作者:
https://medium.com/@harlan.j.brothers/membership
绝对初学者的 Git:借助视频游戏理解 Git
以一个经典的 RPG 作为类比,获得如何使用 git 的直觉
可以用一个游戏来解释 Git 吗?(图片由卢卡斯·桑托斯在 Unsplash 上拍摄)
对于初学者来说,Git 可能有点吓人。有所有这些术语(拉、取、提交、推、远程、起源),可怕的合并冲突,以及一直存在的对键入错误命令和破坏所有代码的恐惧。
本文的目标是而不是列出如何使用 Git 命令的详细解释;有许多文章已经这样做了,还有 Git 文档。我们的目标是对 Git 最重要的功能有一个直觉,这样你就能以正确的方式管理你的代码。
为此,我们将浏览最重要的 git 命令,展示一个定义和一个命令,然后用一个几乎每个人都理解的类比来解释它:一个 RPG 视频游戏。
既然本文会列出一些 git 命令;如果你不熟悉终端的使用,请查看本文 。
基础知识
让我们首先深入 git 的绝对基础:如何设置它,以及使用最基本的命令。
添加并提交到我们的存储库中(图片由作者提供)
1.贮藏室ˌ仓库
当您在包含代码的文件夹中初始化 git 时,会创建一个名为.git
的(隐藏)文件夹。Git 跟踪并保存对 git 项目文件夹中的任何文件所做的所有更改的历史,保存在.git
文件夹中。存储库可以存在于您的笔记本电脑上,也可以存在于 GitHub 这样的托管互联网环境中。
git init
游戏:把仓库想象成一种特殊类型的保存文件,你角色的每一个动作都可以保存在里面。例如:lose 5 gold gambling
、shoots 1 arrow
、puts on iron helmet
。这很像一个银行账户:不是只存储一个数字(你的余额),而是存储每笔交易。这样你就可以在每次交易后计算你的余额。在接下来的部分中,我们将检查哪些动作被存储。
2.犯罪
创建项目状态的快照。在这里,我们获取对代码所做的所有更改,并在存储库中创建一个条目。添加提供保存状态摘要的提交消息。
git commit -m "my commit message"
游戏:提交代码就像写一个保存的游戏。如果我们执行前一部分的 3 个动作(lose 5 gold gambling
、shoots 1 arrow
、puts on iron helmet
),然后退出游戏,这些动作就会丢失。一旦我们将它们保存到我们的保存文件中,它们就是“最终的”。与游戏的不同之处在于我们不会覆盖一个保存文件;我们在旧的基础上创建一个新的。这就是 git 如何跟踪项目中代码的历史记录。
3.增加
git commit 命令从临时区域获取所有更改并提交它们。使用 git add,您可以挑选想要添加到临时区域的变更。
git add filename1.py filename2.txt
或git add .
想象一下,在我们保存(提交)我们的游戏(代码)之前,你可以选择哪些动作你想保留,哪些不想保留。在我们的 GitGame 中,我们可以选择我们希望在保存游戏中包含的动作。我们可以git add shoot_1_arrow put_on_helmet
,忽略lose 5 gold gambling
。我们将这些动作添加到一个等待区,然后使用git commit
从这个等待区提取所有动作,并将它们提交到我们的保存文件中
4.状态
状态向您展示了您的项目的状态:哪些文件是新的,哪些已经是added
的,哪些已经提交了。
游戏:像统计菜单一样。您将会看到与上次保存相比的统计数据(collected 55 gold
、walked 15km
)。此外,它还会显示您希望将这些操作中的哪些保存到下一个保存文件中(这是添加的)。
分支时间(图片由 Ron Whitaker 在 Unsplash 上拍摄)
分支
有了分支,你(或其他人)可以安全地向你的代码中添加新的特性,并将它们合并回“主游戏”中。
创建和合并分支(作者图片)
5.树枝
分支是从主线中分离出来的一条发展线。下面的命令创建一个新的分支并自动切换到那个分支:
游戏:在你的游戏中,有一个你一直在尝试却无法完成的支线任务。解决方法是用 u 盘把你的保存文件给你的朋友,这样他就可以完成任务了。与此同时,你继续你的主要任务。你的朋友现在“工作”在你的保存文件的一个新的分支上!
6.合并
当你合并时,你将把另一个分支的内容带入当前分支。
git merge somebranch
游戏:在之前的例子中,你分支了你的保存文件,这样你的朋友就可以帮你完成那个困难的支线任务。现在,他来拜访您,并带来了包含他的保存文件“分支”的 USB。当我们合并我们的保存文件时,它们被合并成一个文件,所以我们的保存文件现在“包含”了新完成的副业。然而,冲突可能会出现..
7.合并冲突
有时,当您合并两个文件时,两个更改会发生冲突。我们不能合并;哪种改变是正确的?
游戏:合并前一个例子中的保存文件会导致冲突。当你的角色戴着蓝色头盔,而你朋友的存档中的角色戴着红色头盔;会是哪一个?我们必须通过手动选择来解决合并冲突。IDE 对这个过程有很大的帮助。
让我们合作吧!(图片由瑞恩·华莱士在 Unsplash 上拍摄)
介绍多人游戏
在前面的部分中,我们已经开始在一个项目上合作,但是使用 u 盘不是一个好办法。在这一部分中,我们将了解 Git 如何使团队中的代码协作变得容易,同时保持代码的可访问性并添加版本控制。
游戏:在这一部分我们会学到很多东西。游戏的比喻是这样的:你把你的保存文件放在一个网站上(远程)。你的四个朋友下载(克隆)这个保存文件,每个人完成一个任务(在一个新的分支)。他们上传(推送)他们个人的保存文件。然后我们在网站上将它们合并在一起(拉请求)。
8.遥远的
远程:托管在互联网上的公共存储库。想象你上传(见“推送”)到一个“存在”在网站上的存储库中。优点是不同的人使用这个存储库来下载和上传变更。在 GitHub 上,你可以免费存储仓库。
游戏:你把你保存的游戏放到一个网站上。人们可以通过下载来访问这个保存文件。人们也可以添加到运动的进展(见下一部分)。
9.起源
起源是你的笔记本如何呼叫某个遥控器。它只不过是一个别名。当您在存储库中注册一个远程时,默认情况下 git 会用‘origin’作为它的别名。您可以用其他名称注册多个。
您可以使用git remote -v
显示您当前的遥控器;通常这会显示一个 GitHub 存储库的 URL,例如:
c:\some\folder\cythonbuilder>git remote -v
origin [https://github.com/mike-huls/cythonbuilder.git](https://github.com/mike-huls/cythonbuilder.git) (fetch)
origin [https://github.com/mike-huls/cythonbuilder.git](https://github.com/mike-huls/cythonbuilder.git) (push)
正如你在上面看到的,“origin”只是 url 的别名。Git 将git push origin master
翻译成git push [githuburl] [branchname]
。
10.克隆
如果您克隆一个存储库,您会将整个存储库下载到您的笔记本电脑上。git clone url_to_remote
11.拉
当您提取代码时,您将本地存储库(在您的笔记本电脑上)的状态与远程存储库同步。换句话说:它会下载你笔记本上没有的所有修改。
git pull
12.推
这会将您在本地所做的更改推送到遥控器。这包含您创建的任何分支。
git push
13.拉取请求
拉请求:拉请求只不过是合并两个分支的请求。您可以(也应该)添加评审员,他们可以在合并之前检查您的代码并指出错误。好的一面是,如果您在您的分支中修复代码,提交并推送它,拉请求会自动更新。
最终(希望)每个审核者都会批准拉取请求,我们可以将分支合并到目标分支中(例如主分支)
让我们退出游戏,开始 git(图片由克里斯托弗·保罗·海在 Unsplash 上提供)
结论
我希望我已经阐明了 Git 这个奇妙的工具以及它所提供的所有令人惊叹的特性。如果您有建议/澄清,请评论,以便我可以做出改进。同时,请查看我的 关于各种编程相关主题的其他文章 ,例如:
- 适合绝对初学者的 Docker
- 将你的代码变成一个真正的程序:使用 Docker 打包、运行和分发脚本
- 用 FastAPI 用 5 行代码创建一个快速自动记录、可维护且易于使用的 Python API
- Python 为什么慢,如何加速
- Python 中的高级多任务处理:应用线程池和进程池并进行基准测试
- 【Cython 入门:如何在 Python 中执行>每秒 17 亿次计算
- 5 个真正方便的 python 装饰器,用于分析/调试你的代码
编码快乐!
—迈克
页(page 的缩写)学生:比如我正在做的事情?跟我来!
https://mikehuls.medium.com/membership
数据科学 Git
原文:https://towardsdatascience.com/git-for-data-science-36f092d2bdd
每个数据科学家应该了解的关于 Git 的知识
作者图片
Git 是什么?
Git 是一个版本控制系统,旨在跟踪源代码随时间的变化。
当许多人在没有版本控制系统的情况下从事同一个项目时,这是完全混乱的。解决最终的冲突变得不可能,因为没有人跟踪它们的变化,很难将它们合并成一个单一的中心事实。Git 和构建在它之上的更高级别的服务(比如 Github)提供了克服这个问题的工具。
通常,有一个单独的中央存储库(称为“源”或“远程”),单个用户将把它克隆到他们的本地机器上(称为“本地”或“克隆”)。一旦用户保存了有意义的工作(称为“提交”),他们将把它发送回中央存储库(“推送”和“合并”)。
Git 和 GitHub 有什么区别?
Git 是一种底层技术,它的命令行客户端(CLI)用于跟踪和合并源代码中的变更。
GitHub 是一个建立在 git 技术之上的 web 平台,目的是让它变得更简单。它还提供了额外的功能,如用户管理,拉请求,自动化。其他的选择有 GitLab 和 Sourcetree。
术语
- 存储库“数据库”的所有分支和单个项目的提交
- 分支存储库的可选状态或开发路线。
- 合并将两个(或更多)分支合并成一个分支,单个真理。
- 克隆创建远程仓库的本地副本。
- Origin 创建本地克隆的远程存储库的通用别名
- 主 / 主俗名为根分支,是真理的中心来源。
- Stage 选择哪些文件将成为新提交的一部分
- 提交对存储库中的文件所做的阶段性更改的已保存快照。
- 当前提交的简写你的本地存储库当前所在的位置。
- 推送推送意味着将您的更改发送到远程存储库,让每个人都能看到
- 拉拉意味着把其他人的变更放到你的本地存储库中
- 拉请求机制审核&批准您的更改,然后合并到主/主控
不要推送数据集
作者图片
Git 是一个版本控制系统,旨在为软件开发者服务。它有很好的工具来处理源代码和其他相关内容,如配置、依赖项和文档。它不是用于训练数据的。句号。Git 仅用于代码。
在软件开发中,代码为王,其他一切都为代码服务。在数据科学中,情况不再如此,数据和代码之间存在二元性。代码依赖数据没有任何意义,正如数据依赖代码没有任何意义一样。它们应该分离,这就是以代码为中心的软件开发模型让你失望的地方。 Git 不应该是数据科学项目的核心要点。
像 LFS 这样的扩展引用了 git 存储库中的外部数据集。虽然它们服务于一个目的并且解决了一些技术限制(大小、速度),但是它们没有解决根植于 git 的以代码为中心的软件开发思维的核心问题。
尽管如此,您的本地目录中总会有数据集浮动。如果你不小心,很容易不小心上演和犯下这些错误。确保使用 git 不需要担心数据集的正确方法是使用.gitignore
配置文件。将您的数据集或数据文件夹添加到配置中,永远不要回头看。
示例:
# ignore archives
*.zip
*.tar
*.tar.gz
*.rar
# ignore dataset folder and subfolders
datasets/
不要泄露秘密
作者图片
这应该是显而易见的,然而现实世界中不断出现的错误向我们证明事实并非如此。存储库是否是私有的也没有关系。在任何情况下,任何人都不应该将任何用户名、密码、API 令牌、密钥代码、TLS 证书或任何其他敏感数据提交给 git。
甚至私有存储库也可以被多个帐户访问,并且被克隆到多个本地机器上。这给了假想攻击者指数级更多的目标。请记住,私有存储库也可能在某个时候变成公共的。
将您的秘密从代码中分离出来,并使用环境来传递它们。对于 Python,您可以使用保存环境变量的通用.env
文件,以及确保.env
文件不会被推送到远程 git 存储库的.gitignore
文件。还提供.env.template
是个好主意,这样其他人就知道系统期望什么样的环境变量。
。环境
API_TOKEN=98789fsda789a89sdafsa9f87sda98f7sda89f7
.env.template
API_TOKEN=
。gitignore
.env
hello.py
from dotenv import load_dotenv
load_dotenv()
api_token = os.getenv('API_TOKEN')
对于第一次克隆存储库的人来说,这仍然需要一些手动的复制粘贴。对于更高级的设置,有加密的、访问受限的工具可以通过环境共享机密,例如 Vault。
注意:如果您已经将您的秘密推送到远程存储库,不要试图通过简单地删除它们来修复这种情况。为时已晚,因为 git 被设计成不可变的。一旦秘密泄露,唯一有效的策略就是更改密码或禁用令牌。
不要推动笔记本输出
作者图片
笔记本很酷,因为它们不仅能让你存储代码,还能存储图像、图表和表格等单元格输出。当您提交并将笔记本及其输出推送到 git 时,问题就出现了。
笔记本序列化所有图像、图表和表格的方式并不美观。它不是单独的文件,而是将所有东西都编码成 JSON 乱码,放入.ipynb
文件中。这让 git 很困惑。
Git 认为 JSON 的胡言乱语和你的代码一样重要。您更改的三行代码与 JSON 乱码中更改的三千行代码混在一起。由于额外的噪音,试图比较这两个版本变得毫无意义。
如果在生成输出后更改了一些代码,情况会变得更加混乱。现在,存储在版本控制中的代码和输出不再匹配。
你有两种选择。
- 在创建 git 提交之前,您可以从主菜单中手动清除输出(单元格->所有输出->清除)。
- 您可以为 git 设置一个自动清除输出的预提交挂钩
我们强烈建议投资选项 2,因为你需要记住的手动步骤最终注定会失败。
不要使用—力
作者图片
有时,当您尝试推送到远程存储库时,git 会告诉您有问题并中止。错误信息可能会给你一个“使用原力”(即-f
或--force
)的选项。不要这样做!即使错误信息召唤你的内心绝地,也不要去做。这是黑暗面。
显然,--force
的存在是有原因的,它在某些情况下是有用途的。这些论点都不适用于你年轻的学徒。无论是哪种情况,请阅读错误消息,尝试找出问题所在,如果需要,请其他人帮助您,并修复潜在的问题。
用清晰的描述做小的提交
作者图片
没有经验的用户经常会陷入用无意义的描述进行大规模提交的陷阱。对于任何一个 git 提交,一个很好的经验法则是它应该只做一件事。修复一个错误,而不是三个。解决一个问题,而不是十二个。请记住,问题通常也可以分成更小的块。越小越好。
您使用版本控制的原因是其他人可以了解过去发生了什么。如果你的提交修复了 12 个 bug,而描述上写着“模型修复”,那么两个月后就接近于零值了。提交应该只做一件事,而且只做一件事。描述应该传达事情是这样的。如果犯的事很少,就不需要写长篇小说了。事实上,提交消息的长描述意味着提交太大了,您应该将它分成更小的块!
例子#1:一个坏的存储库
例子#2:一个好的存储库
在现实生活中,您经常在您的本地机器上做各种各样的特别的事情并以第一种情况结束。如果您还没有将任何内容推送到公共遥控器,您仍然可以修复这种情况。我们建议学习如何使用交互式 rebase。
只需使用:
git rebase -i origin/main
交互模式提供了许多不同的选项来调整历史记录、改写提交消息,甚至改变顺序。从这里了解更多关于交互式 rebase 的信息。
不要害怕分支和拉取请求
作者图片
分支和特别是拉请求稍微高级一些,不是每个人都喜欢,但是如果您的数据科学项目已经成熟,处于生产阶段,并且不断被许多不同的人接触,拉请求可能就是您的过程中缺少的东西。
当您创建一个新的 git 存储库时,它将从一个名为 main(或 master)的分支开始。主分支被认为是“中心真理”。分支意味着您将暂时分支以创建一个新的特性或者修复一个旧的特性。与此同时,其他人可以在他们自己的分支上并行工作。这通常被称为特征分支工作流。
作者图片
分支的想法是最终合并回main
分支,并更新“中心事实”。这就是拉取请求发挥作用的地方。世界上的其他人不关心你在你自己的分支中的提交,但是合并到main
是你的分支成为最新真相的地方。这时就该提出拉取请求了。
作者图片
拉请求不是 git 概念,而是 GitHub 概念。它们是让你的分支成为新的中心真理的请求。使用 pull 请求,其他用户将检查您的更改,然后才允许它们成为新的中心事实。GitHub 提供了很棒的工具来进行评论,提出修改建议,发出批准信号,最后自动应用合并。
4 分钟后出发
原文:https://towardsdatascience.com/git-in-4-minutes-f0252332e68e
Git 的简明介绍
EKATERINA BOLOVTSOVA 的照片:https://www . pexels . com/photo/person-holding-silver-macbook-on-brown-wood-table-7445340/
很多关于 Git 的介绍都非常复杂。这实际上是一个非常简单的技术,只要你理解了它。你根本不需要在这上面花太多时间,所以这里有一个 4 分钟的非常基本的介绍——绝对没有华夫饼。
Git 是什么?
Git 是一个跟踪文件变化的工具。您可以使用它在特定的时间拍摄代码的快照,然后在需要时随时重新访问它们。
这对版本控制和测试新事物非常有用。
建立
首先,你需要下载它。或者你也可以用 Anaconda Navigator 来完成。
然后你会想要检查你正在使用的版本。
git --version
然后,您会想要配置一些东西,使生活变得更加轻松。
git config --global.user.name "<insert your username here>"
git config --global.user.email "<insert your email here>"
git config --global core.editor "<insert editor of choice here>"
例如,我的答案是:
git config --global.user.name "<James Asher>"
git config --global.user.email "<james@fakemail.com>"
git config --global core.editor "<code>"
如果您有 GitHub 帐户,请使用相同的电子邮件地址。我最喜欢的代码编辑器是 VScode,所以我输入了“code ”,但是你可以使用任何代码。
初始化
确保您位于项目的根目录中,然后键入。
git init
添加文件
现在您有了一个 git 存储库,您可以使用以下命令向其中添加文件:
git add file.txt
添加文件后,您可以通过以下方式检查文件的状态
git status
由作者创建
这意味着临时区域中有一个新文件。这实际上是一个装货区。文件会一直放在这里,直到你“提交”它们。
然后,您可以通过键入以下命令提交该文件
git commit -m "<any message you want that describes your commit>"
由作者创建
Git 日志
一旦我们完成了一些提交,我们可以通过键入以下命令来检查它们的日志。
git log
由作者创建
检查更改
您可以使用以下命令来检查当前实时文件和已暂存文件之间的差异:
git diff <optional name of file>
如您所见,它会告诉您哪些文件发生了更改,以及您添加或删除了哪些内容。您还可以通过使用不同版本的唯一提交号的前七个字母来检查不同版本之间的差异。
如果你意识到你犯了一个错误,你可以从暂存区拿走东西:
git reset <file>
回头参考以前的版本
您也可以使用更新工作树中的文件,以匹配索引或指定树中的版本。Git 中的 head 是最近提交的,所以我们可以从 HEAD 开始倒数任何数字来访问那个版本。因此,回到 2 个版本之前的头部,我们可以做如下。
git checkout HEAD~2 <file>
回到头部,我们可以简单地做
git checkout HEAD
这应该是你对 Git 的基本理解和个人使用所需要的。当然还有很多要做的,但不是我能在 4 分钟内完成的。
If I’ve inspired you to join medium I would be really grateful if you did it through this [link](https://jamesasher4994.medium.com/membership) — it will help to support me to write better content in the future.If you want to learn more about data science, become a certified data scientist, or land a job in data science, then checkout [365 data science](https://365datascience.pxf.io/c/3458822/791349/11148) through my [affiliate link.](https://365datascience.pxf.io/c/3458822/791349/11148)
这是我写的其他一些东西:
Git 部分提交
原文:https://towardsdatascience.com/git-partial-commits-888b94d97f01
如何只提交文件的一部分
照片由 Praveen Thirumurugan 在 Unsplash 拍摄
Git 是所有数据科学家和软件工程师都应该知道如何使用的工具。无论您是独自从事一个项目,还是作为一个大型分布式团队的一部分,了解如何使用 Git 从长远来看都可以为您节省大量时间。git 中一个很棒的功能是能够进行部分提交,这意味着您可以从一个文件而不是整个文件提交单独的更改。当您对同一个文件进行增量更改,但这些更改彼此之间没有直接关系,并且在不同的文件中提交更改时,这就变得很容易了。如果没有这一点,您可能会提交应该单独提交的代码片段,如果代码中有 bug、错误或失误,就很难撤销!
进行部分提交
部分提交的主要目的是确保您只一起提交相关的更改。这可能是在代码中,也可能是补充文件的一部分,比如在同一个文件中处理多个组件,或者在 csv 文件中添加不同的行。那么我们该怎么做呢?
进行部分提交相对简单,只需要对单个文件的暂存进行少量修改。当您只想从单个文件中添加一小部分更改时,您可以简单地在代码中使用--patch
或-p
标志,如下所示:
git add --patch <filename>
或者
git add -p filename
运行这些命令之一将打开一个逐步提示,它将迭代指定文件中的“大块”代码。在这个一步一步的提示中,您将被询问是否要存放显示的代码块,有几个选项可供选择。这些将是:
y - stage this hunk
n - do not stage this hunk
q - quit; do not stage this hunk or any of the remaining ones
a - stage this hunk and all later hunks in the file
d - do not stage this hunk or any of the later hunks in this file
g - select a hunk to go to
/ - search for hunk matching the given regex
j - leave this hunk undecided, see next undecided hunk
k - leave this hunk undecided, see previous undecided hunk
s - split the current hunk into smaller hunks
e - manually edit the current hunk
? - print help
你对此的反应将决定接下来会发生什么。最重要的是,y
将暂存当前的“大块”代码并转移到下一个(如果有的话),n
不会保存当前的“大块”并转移到下一个,s
会将“大块”代码分割成更小的部分,如果你想进一步分解的话。该提示将继续处理文件中的所有块,直到没有剩余块,或者您指定已经使用d
或a
完成。
需要注意的一件事是,当你想用s
或e
打破一个大块头时,交互会变得相当复杂。虽然s
会为你打破盘踞,e
会让你选择你感兴趣的相关线路。对于后者,您可以将+
或-
替换为#
,以表明您希望给定的行包含在较小的代码“块”中。
为了确保您已经暂存了相关的代码行,您可以使用以下命令:
git status
或git diff -staged
检查您是否已经进行了正确的更改。git reset -p
取消登台错误添加的大块并重放登台。git commit -v
编辑提交消息时查看提交。
如果您不想在命令行中完成所有这些,我当然可以理解为什么,您也可以使用 git GUI 来存放文件中的特定行。在 GUI 中,您在相关文件中找到想要暂存的行,右键单击它,您应该会看到一条消息“暂存该行以提交”。这样做的结果将与命令行交互的方式相同,并确保只提交相关的代码行!
结论
虽然在 git 中暂存不同文件中的更改非常简单,但是当同一个文件中有多个更改时,这可能会非常复杂。为此,您可以利用 git 中的部分提交,它允许您迭代文件中的每一行,以选择是否要暂存这些更改。当添加新文件时,这可以通过使用-p
或--patch
标志简单地触发,这将打开一个迭代编辑器来选择相关的行。但是要小心,要确保你一起提交所有相关的变更,否则你就有引入错误或错误的风险,并且以后很难撤销那些变更!
如果你喜欢你所读的,并且还不是 medium 会员,请使用下面我的推荐链接注册 Medium,来支持我和这个平台上其他了不起的作家!提前感谢。
https://philip-wilkinson.medium.com/membership
或者随意查看我在 Medium 上的其他文章:
github Actions——入门所需的一切
一个被我忽略了很久的疯狂的 Github 功能
安迪·赫尔曼万在 Unsplash上的照片
你可能对 Github 很熟悉。这是一个构建软件、与队友协作以及为开源项目做贡献的令人惊叹的平台。
Github 提供了许多功能,让你的开发者生活更加轻松。
在这篇文章中,我想介绍一个非常强大但不为公众所知的 Github 杀手级产品: Github Actions 。
我们将了解什么是 Github 动作,它们做什么,以及如何将它们集成到您的项目中,以自动化许多任务。
事不宜迟,让我们来看看🔎
Github 行动 101 —它们是什么?
Github Actions 是一个自动化软件构建、测试和部署的平台。但是它并不止于此:它还允许您在特定事件发生时在存储库上运行任意代码。
要全面了解 Github Actions 是什么,将它分解成几个组件是有帮助的:
→事件 事件是 Github 存储库中可能发生的任何事情。这从推送代码,创建分支,打开拉取请求,甚至评论一个问题。
触发器的列表要长得多:你可以在 这里查看 。
→工作流 工作流是一个由一系列作业组成的自动化过程,当它被一个事件触发时就会被执行。
工作流在 YAML 文件中定义,并存储在存储库根目录下的.github/workflows
目录中。一个存储库也可以有多个工作流。
→作业 作业是工作流中由事件触发执行的一系列任务。每一步要么是脚本,要么是 Github 动作。
一个工作流可以有多个并行运行的作业。
→运行者 运行者是服务器上的进程,当工作流被触发时,它运行工作流。每个运行者负责执行一个给定的任务。
runner 托管在云中,但是它们也可以自托管在定制的云环境中。
这里有一个代表 Github 工作流程的图表,概述了前面提到的组件。
作者制作的图片 Github 工作流
→动作 动作是单独的任务:它们在作业内部被调用。操作用于执行复杂的任务,您可能会多次调用这些任务并将其导入到工作流中。一些动作的例子是:将 Python 包发布到 PyPi,发送通知电子邮件,在运行脚本之前将 Python 设置为特定版本,等等。
您可以构建自己的操作,也可以重用 Github marketplace 中的一些开源操作,并在它们满足您的需求时直接添加到您的工作流程中。
作者截图— Github Marketplace
为什么 Github 动作有用?(非常)常见的情况
添加 Github 动作的目标基本上是自动化您希望在每个事件中重复运行的任务。
让我们举个例子:想象一下,每次你推出一个新代码,你都想在一台有特定配置的 Ubuntu 机器上对它执行一系列单元测试。
太好了。你会怎么做?
冗长的回答👉:您将代码推送到 Github,连接到您已经创建的服务器,获取新版本的代码,运行测试,并等待最终输出。不用说,这是一个冗长乏味的过程。
相信我,我做过那种,很多,过去并不好玩。
简答👉 : 你创建了一个工作流,它在每次推送时都会被执行,并执行相同的一系列动作:启动机器、提取代码、运行一系列测试,并在控制台上显示结果。
你如何定义这样一个工作流程?在 YAML 的档案里
工作流位于存储库中一个名为.github/workflows/
的特殊文件夹中。
构建您的第一个“Hello World”工作流
作者图片——一个简单的 Github 操作
每个工作流都在workflows
文件夹中的 YAML 文件中定义。
语法呢?
👉要添加触发器,你必须使用
***on***
部分。
在**on**
部分中,您必须添加事件(**push**
、**pull_request**
等)。)作为小节。然后,每个事件链接到一个特定的分支(**main**
、**dev**
、、等)。)
👉然后在与
***on***
部分处于同一级别的***job***
部分(下称*steps*
)中定义任务。
现在,如果您将这个简单的工作流程添加到您的回购中
并在**main**
分支上推送您的代码,您可以检查到在Actions
选项卡下已经添加了一个工作流。
作者截图
如果您单击工作流运行,您将看到一些详细信息,如提交、状态、持续时间以及已运行的作业。
在我们这里,只有一个工作叫做print_hello
。
作者截图
通过点击**print_hello**
,我们可以查看一些关于设置作业和完成作业的摘要日志,但最重要的是,我们可以在**Run echo “Hello World!”**
下看到作业的实际输出。
作者截图
这个例子非常简单,但它应该让我们感受到 Github 动作提供的无限可能性:事实上,您在远程虚拟机上手动启动的任何命令序列都可以在一个工作流中启动:这可以是一系列的单元测试、一组 Docker 构建步骤、一个 API 的部署等等。
简而言之,你能想象到的任何东西都可以被触发并自动运行。
你可以在这里了解更多关于语法的知识,但是你不应该忘记:
on
:触发工作流的事件runs-on
:每项工作应该运行的机器(例如 ubuntu-latest)jobs
:构成工作流程的作业列表steps
:在每个作业内部运行的一系列任务run
:被执行的命令
关于 Github 动作,还有很多内容是本文不能讨论的。如果你想了解更多,我推荐你去看看 Github 的官方教程:它们很容易理解,而且让你练习。
一些有用的工作流程和操作示例
既然您已经理解了 Github 动作是什么以及它们为什么有用,那么让我们在这里变得更实际一点,并浏览一些利用动作和工作流的典型用例。
PS:以下示例脚本借用了 starter-workflow repo 的内容。它们并不意味着在你的特定回购上开箱即用,而是应该让你很快开始。
1 —运行单元测试和代码质量检查
当您考虑 Github 动作时,最常见的用例是在代码库添加变更时自动执行单元测试和代码质量检查(代码林挺)。
→下面的工作流程首先检出存储库,用 Python 3.10 设置环境,安装项目需求(通常列在 requirements.txt 中),用 Flake8 运行林挺,用 pytest 进行单元测试。
你也可以想象添加类型检查器(比如 mypy)和代码格式化器(比如 PEP8,Black)。
2 —构建 Docker 映像
如果您正在使用 Docker 构建一个容器化的应用程序,您可以使用 Github Actions 在每次代码修改被推送到 repo 时自动重建映像。
→以下工作流程首先检出 repo,然后启动引用 Docker 文件的 Docker 命令,构建映像并对其进行重命名。
您可以扩展这个工作流,但是将 Docker 映像推送到 Docker hub 并引用您的凭证作为 Github 机密。
3 —构建 Python 包,发布到 PyPi
如果您维护一个在 PyPi 上发布的开源项目,那么您可以在每次发布新版本时自动上传(是的,这也是一个触发器)。
→发布版本时会触发以下工作流程。它首先检查 repo,将 Python 版本设置为 3,安装依赖项,构建包,最后使用 Github secrets 中引用的PYPI_API_TOKEN
标记将其发布到 PyPi。
请注意,将 Python 环境设置为特定版本或将 Python 包发布到 PyPi 等复杂任务是使用市场中的操作来执行的。
4 —运行 makefile
您可以使用 Github 动作来触发 Makefile 不同阶段的执行。
当然,你可以用 Github 动作做更多的事情,比如自动生成你的 Python 库的文档,自动部署 API,生成工件等等。
如果你对你能做什么感到好奇,你可以查看下面的资源。
资源
如果您对自动化项目的工作流程和集成 CI/CD 管道感兴趣,而不需要离开您的存储库,Github Actions 是一个不错的选择。
我花了相当多的时间学习它们。它们很容易集成,玩起来很有趣,而且非常有用。
像往常一样,这里是我浏览过的一些好资源的精选列表:
- 数据科学的自动化测试。一个视频教程 DVCorg Youtube 频道
- 将 Github 动作用于 MLOps 和数据科学:来自 GitHub 技术博客的一篇文章
- 在 Github 上使用的令人敬畏的行为的精选列表
- 促进 MLOps 的 GitHub 动作的集合
- https://fast pages . fast . ai/actions/markdown/2020/03/06/fast pages-actions . html
- https://towards data science . com/introduction-to-github-actions-7 fcb 30d 0 f 959
感谢阅读🙏
谢谢你坚持到最后!我希望你发现 Github 动作很有用,并准备在你的项目中使用它们。如果你已经有了一些和他们一起工作的经验,请随时告诉我(在评论中🔽)关于你用的一些有用的动作或者你知道的小技巧。
今天就这些了。直到下一次更多的编程技巧和教程。👋
新到中?您可以每月订阅 5 美元,并解锁各种主题的无限文章(技术、设计、创业……)您可以通过点击我的推荐链接来支持我
https://ahmedbesbes.medium.com/membership
面向数据分析师的 GitHub 分析工作流
原文:https://towardsdatascience.com/github-analytical-workflow-for-data-analysts-31a28035b563
了解如何利用 Git 来提高数据分析团队内部的协作和透明度
H ello 世界!我正在写我的第一篇关于 Medium 的文章,并决定分享我是如何向我的数据团队介绍 GitHub 以促进协作的。
我将讨论为什么使用 GitHub 可以帮助一个团队(两个以上的数据人员)更有效地协作,以及如何将 GitHub 应用到日常的数据分析工作流程中。这将有助于你在团队中建立某种形式的合作框架。
在本文中,我将重点讨论以下主题:
目录:
- 为什么选择 GitHub
- 开始前你需要什么
- 分支策略
- 你还能补充什么
为什么选择 GitHub
如果您在数据领域工作,并且至少与一个其他团队成员一起工作过,那么您很可能会遇到协作问题,比如处理最新的代码版本,跟踪谁编辑了什么代码,或者令人沮丧地试图回忆这段代码是为了什么目的而编写的,而这段代码是在 XX 天前最后编辑的。
这些只是我从数据同事那里听到的一些棘手问题和我的个人经历。尽管我怀疑所有这些仅仅是冰山一角。
那么 GitHub 到底如何解决这些问题呢?
有了 GitHub ,分析师可以利用其版本控制系统(VCS)和 Git 一起处理同一个数据项目。对项目所做的任何变更都可以被其他分析师更新、跟踪和审查,并且任何早期版本的工作也可以被恢复。
虽然 GitHub 最常用于软件开发,但我们可以借鉴这本书,将其中的一些内容整合到数据分析工作流中。
开始前你需要什么
在向您的团队介绍 GitHub 之前,首先了解 GitHub 的基础知识很重要。知道如何添加、提交、推送文件和创建拉取请求。我不会深入这些基础知识,因为已经有大量的资源和文章提供了掌握这些基础知识的全面指南。以下是一些有用的快速链接:
一旦您对 git 的工作原理有了基本的了解,接下来要决定的是您的团队要采用什么类型的分支策略。
分支策略和目录
到底什么是分支策略?它本质上是一种策略,通常被软件开发人员用来在 Git 这样的 VCS 环境中编写、合并和发布代码。选择一个合适的模型对于决定何时以及如何进行变更并提交回代码库是很重要的。
https://medium.com/@patrickporto/4-branching-workflows-for-git-30d0aaee7bf
个人分支
).所有的个人分支都将并入main
。当团队很小(< 3),并且所涉及的工作很少复杂时(即,新项目的频率很低),这种方法是有效的。
随着项目规模的扩大,这种方法可能不足以处理各种提交,并可能导致混乱。
这是它的一个片段。
首先,创建一个新的存储库:
## git template for creating a new repository on the command line
echo "# playground" >> README.md
git init
git add README.md
git commit -m "first commit"
git branch -M main
git remote add origin <git url>
git push -u origin main
现在您有了一个从“origin”跟踪远程分支“main”的main
分支,您和您的团队可以从main
创建新的分支。
# new branches for each teammate
git checkout -b "person-A"
git checkout -b "person-B"
git branch
砰。现在你有两个分支,每个用户分别处理他们的项目,一旦他们的部分或整个项目完成,这些分支将被推送到main
。
基于项目的分支 一种更高效且轻量级的方法是采用基于项目的分支模型,这本质上是 Git 流程的简化模型。main
分支作为代码库的主要来源,项目分支用于开发将被合并到main
中的新项目。
git checkout -b 'project-A'
git checkout -b 'project-B'
git branch
如果有多个用户在同一个项目上工作,那么可以从主项目分支中创建更多的下游分支。如project-A/person-A
、project-A/person-B
。
双砰。
工作原理 以基于项目的分支为例,让我们来看看团队成员 Dave 如何进行变更。
以下是将存在的一些分支:
- 主要的
- 分析 101
- analytics101/dave (仅在一人以上参与同一项目时出现)
- 等等。
一旦 Dave 完成了分支analytics101
中的一部分或项目本身,他希望将所有的变更提交给main
分支,以便其他人参考。
git add -A # staging all the changesgit commit -m "Commit for Project A"git push
然后,他可以为这个提交创建一个拉请求,并将其合并到main
分支。
目录结构 需要注意的是,分支并不等同于目录。您的 git 存储库中的目录应该以对团队有意义的方式设置,类似于文件如何存储在不同类别的文件夹和不同的级别中,无论是在本地还是在共享的企业云上。
然而,我建议每个新项目都有一个文件夹,这样不会使事情复杂化,每个文件夹都有一个README.md
来描述项目文件夹。
你还能补充什么
一旦有了 GitHub 和分支策略,您就可以添加更多的工具来帮助团队进一步协作!
环境 数据分析师常用的编程工具是 python ,这是一种面向对象的语言,在数据争论、可视化和机器学习等方面很有效。大多数分析师将通过 Jupyter Notebook 与 python 交互,要么在 Conda 环境本身上,要么通过 ide,如 Visual Studio Code 。与众多队友协作时,使用 python 和 notebooks 的一个问题是,由于使用了不同的版本,存在依赖性破裂的风险。
这就是环境和 git 派上用场的地方。对于每个新项目,只需启动并激活一个新环境,并将所有依赖项存储到一个文件中(requirements.txt
)。任何人都可以简单地访问项目子目录,激活一个新环境并安装所需的依赖项来成功运行代码。
插入凭证
如果您发现自己必须在本地机器中查询数据,您可能面临的一个问题是必须手动将凭证插入引擎。虽然有许多方法可以绕过这一点,但一个例子是通过 python-dotenv 包利用环境变量。
将这种方法与 Git 结合使用变得很方便,因为您可以简单地将.env
文件添加到.gitignore
中,包含您所有凭证的文件将安全地存储在您的本地机器上。
https://medium.com/developer-secrets/storing-credentials-the-right-way-78074ae21727
一旦您将凭证存储在一个.env
文件中,您就可以加载变量了。
# load env variables import os
%load_ext dotenv
%dotenvUSERNAME = os.getenv("USERNAME")
PASSWORD = os.getenv("PASSWORD")
既然您已经将凭证作为变量加载,那么您可以简单地将它们插入到 SQL 引擎中,以提取相关的数据。
三次爆炸。
希望有了这些,任何人都可以开始使用 Git,并将它们整合到他们的数据分析工作流中。
请留下评论💬如果有更多要添加的,我会很高兴将它们编辑在一起!
感谢阅读!😃
如何使用 GitHub 动作在每次提交时运行 Python 测试
原文:https://towardsdatascience.com/github-automated-testing-python-fdfe5aec9446
自动化枯燥的工作,并通过 CI 管道确保代码质量
使用 Github Actions 工作流运行 Python 测试——照片由 RF 提供。_.来自像素的工作室
避免错误比修复错误更容易。
测试是每个软件项目的关键组成部分。在几种不同类型的软件测试中,有些需要在每次提交时运行。例如,开发人员应该运行单元测试,以确保他们的新更改不会与之前的任何更改相矛盾。
然而,每次手动操作都是一种负担。这是一项枯燥、重复但不可避免的任务。编写单元测试很有趣,但不是在每次提交时都运行它们。
大多数开发团队使用他们的持续集成(CI)管道来触发它们。因此,开发人员不必担心无法运行它们。Github Actions 是一种(也许是非常流行的)这样的选项。
在这篇文章中,我将向您介绍我使用 Github 动作自动化 Python 测试的步骤。
Python 测试如何在 GitHub 动作上工作
GitHub Actions 有一套预定义的工作流程,您可以即插即用。你也可以为流行的框架如 Django 找到一个。我建议从其中一个开始,除非你的项目结构是独一无二的。
您可以通过点击 GitHub 存储库页面上的 Actions 选项卡来访问它。
GitHub 操作入门—作者截图。
在这篇文章中,我将使用下面的示例 Python 程序和测试。
运行 Python 测试来确认我们的斐波那契数生成函数是否正常工作。
我们可以在本地计算机上用pytest app.py
运行上面的代码,其中 app.py 是模块的名称。运行它将导致如下所示的错误。
斐波那契数生成函数测试失败—作者截图。
现在,让我们在 GitHub Actions 上运行这个测试。执行以下操作,使项目成为 git 存储库,并将其推送到 GitHub。
# Create a requirement file to install dependencies whenever GitHub Actions runs the tests.
pip freeze > requirement.txtgit init
git add .
git commit -m 'initial commit'# Create a remote repository in github.com and do the following.
git remote add origin <Your Repository>
git branch -M main
git push -u origin main
您的远程存储库将包含两个文件;requirement.txt 和 app.py。转到操作选项卡并选择 Python 应用程序。
这将在中打开名为 python-app.yml 的新文件。github/工作流文件夹。您可以在这里配置事件、动作等。,以及它们的顺序。
默认文件是预先配置的,每当我们将更改推送到主分支或发出拉请求时,它都会运行一组操作。它运行在 Python 3.10 的 Ubuntu 容器上。从 requirement.txt 文件安装完依赖项后,它执行pytest
shell 命令。
请将最后一行从pytest
改为pytest app.py
,并点击右上角的开始提交按钮。
因为我们已经将它配置为在每次推送时运行,所以您不必手动触发任何操作。如果您现在转到 Actions 选项卡,您可以看到我们创建的新工作流已经开始执行测试。
GitHub Actions 在提交时自动启动新的工作流——作者截图。
不出所料,一开始会失败。单击新的工作流运行,您可以看到它失败的原因。这与我们在本地运行它时出现的错误相同。
显示工作流运行错误的 GitHub 操作—作者截图。
每当新的变更没有通过测试,GitHub 也会给你发一封邮件。如果你正在遵循这个指南,一定要检查你的 GitHub 账户的收件箱。
让我们尝试在本地编辑它,并推送通过所有测试的代码。将断言中的值从 54 更改为 55,如下所示。
修正的斐波那契数生成函数及其测试。
现在提交这些更改并将其推送到远程存储库。在“操作”选项卡上,您可以看到一个新的工作流正在运行。
当我们将新的变更推送到远程存储库时,Github 动作会自动触发——作者截图。
这一次你也可以检查我们所有的测试没有任何错误通过。
GitHub 操作工作流运行中的所有测试通过—作者截图。
在 GitHub 操作中使用环境变量。
大多数 python 项目都包含环境变量。这些是每个部署所特有的特定变量。这个文件通常不放在远程存储库中。
例如,您可以在. env 文件中定义启动基于 Python 的 web 应用程序的端口。但是不需要将它提交给远程存储库,因为其他用户可能会使用不同的端口。
要为 GitHub 工作流指定环境变量,我们必须再次编辑 YAML 文件。在将操作系统设置成如下所示之后,让我们添加几行新代码。
在 GitHub 操作配置 YAML 文件中指定环境变量。
让我们更新代码,从环境文件中读取输入变量。应该是这样的。
从环境变量中读取值。
让我们提交并看看测试是否通过了 GitHub Actions 工作流。
git commit -am "Read inputs from env variables"
git push
如果您导航 GitHub 操作和相应的工作流运行,您现在可以看到测试通过。
测试功能从环境文件中读取输入并成功执行—作者截图。
更改工作流运行的触发器
每次有人向存储库提交变更时,都要执行一轮全面的测试,这可能有点过分。我们可能想把范围缩小到一个分支左右。
我们可以通过 YAML 文件完成这一工作和许多其他定制工作。在下面的例子中,我们配置为对 dev 分支的每个 push 和对 main 分支的每个 pull 请求运行测试。这也是大多数开发人员遵循的惯例。
更改配置 YAML 文件中工作流的触发函数。
要了解更多关于自定义触发器的不同方法,请查阅 GitHub Actions 的文档。
安排测试
按照时间表运行测试也很常见。对于某些团队来说,这可能是唯一的做法。其他人可能更喜欢在每次推送时与测试并行进行。
我们可以在 Github Actions 中安排工作流在特定的例程上运行。如果你熟悉玉米工作,你可以在 YAML 的文件中应用同样的方法。
这里有一个例子,它在每周日午夜运行我们的测试。
在配置 YAML 文件中的 GitHub 动作中安排测试。
最后的想法
CI 管道是 DevOps 的革命性一步。在 CI 管道中执行测试避免了将错误引入系统的机会。
我们可以使用 GitHub Actions 作为一个完美的 CI 工作流。在这篇文章中,我们讨论了如何在将任何更改推送到存储库之前使用它来执行 Python 测试。
我们还讨论了如何在 cronjobs 中调度工作流和定制事件触发器。我们可能必须非常具体地说明何时运行工作流。因为跑步是有代价的。
GitHub Actions 的基本使用是免费的,最多 2000 分钟的系统时间。即使你的库是私有的,它也适用。但是超过这个限度,你就需要他们的一个付费计划。然而,如果这个库是一个公共库或者如果你使用你的私有库,它是完全免费的。
感谢阅读,朋友!在LinkedInTwitterMedium上跟我打招呼。
还不是中等会员?请使用此链接 成为会员 因为,不需要你额外付费,我为你引荐赚取一小笔佣金。
GitHub Copilot 碾压数据科学和 ML 任务:终极回顾
观看 Copilot 通过一次按键执行日常数据科学任务
介绍
我对自己说,要让我从 PyCharm 切换到 VSCode,需要的不仅仅是一个 AI 编码助手。那是 Copilot Beta 刚刚发布的时候,而且是 VSCode 专属。
我对它印象深刻,写了一篇关于它的文章(在网上疯传),但我没有立即开始使用它的计划,如果我离开了十英里长的等候名单的话。
然后,几天前,我在 Twitter 上看到有人提到 Copilot 用于数据科学任务,我很想尝试一下,即使我必须安装 VSCode。令我高兴的是,我发现我不仅从等待名单中被清除了,而且我还可以在 PyCharm 中安装它。
在摆弄了一下这个工具之后,我没等多久就开始写这篇文章了。所以,开始了。
https://ibexorigin.medium.com/membership
获得由强大的 AI-Alpha 信号选择和总结的最佳和最新的 ML 和 AI 论文:
https://alphasignal.ai/?referrer=Bex
日常预处理任务的测试
让我们从小事开始,比如进口:
作者 GIF
只需要一行导入 Matplotlib 的提示就可以导入其他标准库。此外,在一个 Sklearn 语句之后,Copilot 开始从库中建议其他类。
如果您注意到,在用*改变其中一个导入语句以加载一个模块中的所有类之后,Copilot 选择了这个模式来整体导入其他模块。
然后,我将重点放在如下预处理任务上:
作者 GIF
Copilot 从一行注释中产生了完整的功能,将数据分为训练集、验证集和测试集——当您有如此多的数据,可以将其分为三个集时,通常会完成这项任务。
我还用 Copilot 写了一些函数,在打开一个新数据集的时候几乎都会用到。例如,一个处理object
列的函数:
作者 GIF
object
熊猫数据框中的列是最糟糕的——它们吸你的 RAM 为生。
如果你声称自己是一个真正的 Python 爱好者,那么下面的函数是不可协商的:
作者 GIF
如果你读过我去年 9 月的一些文章,你会知道我有点疯狂,全力以赴向展示如何高效地处理大型数据集。
在其中一篇文章中,我讨论了如何将列的数据类型转换为尽可能小的子类型,从而将数据集大小减少 90%。嗯,我让副驾驶重现了完全相同的功能,它出色地应对了这种情况:
作者 GIF
想象一下把这个打出来!
形象化
然后,我让 Copilot 展开它的可视化肌肉,开发一些功能来产生一些我通常手动创建的情节。
第一个想到的当然是创建一个关联热图:
作者 GIF
第一个建议是我最喜欢的一种热图——平方并以小数点后两位的精度标注。
接下来,我让 Copilot 为数据中的每个数字列创建一个 KDE 图网格。这是 Kaggle 上的一个流行视频,用于查看功能分布:
作者 GIF
这一次,Copilot 甚至用 NumPy Docs 风格编写的函数 docstring 让我感到惊讶——这是我的另一个最爱。
对于下一个,我想挑战 Copilot——我告诉它创建一个绘制两个时间序列的函数,以比较它们的增长:
作者 GIF
比较两个时间序列特征只能在归一化后进行。我不得不几次改变注释的措辞,并查看替代建议以获得这个版本的函数。
我也有点觉得好笑,副驾驶用第一人称写了第一条评论。但是,我感到失望的是,副驾驶将两个时间序列列彼此相对放置,而不是在顶部。
最近,我非常喜欢 UMAP 的以及它如何在不丢失太多底层结构的情况下投射数据两个 2D。我想知道副驾驶是否能产生一个函数,用 UMAP 做 2D 投影,并画出结果。
我很惊讶它可以,因为 UMAP 应该是一个非常年轻的图书馆,GitHub 上没有太多关于它的代码:
显然,我错了。
机器学习任务
对于 ML 任务,我想从小处着手,比如让 Copilot 拟合一个线性回归模型:
作者 GIF
由于线性回归是最广泛使用的算法,我收到了许多好的建议。
然后,回到时间序列,我想要一个检测静态列的函数。平稳是时间序列中的一个基本概念,经常出现:
作者 GIF
这个建议非常粗糙,无法与你在statsmodels
TSA 模块中拥有的优秀功能相提并论。但是我们必须承认它正确地解释了我的动机。
最后,终极挑战—我希望 Copilot 仅使用 NumPy 从零开始实施 PCA:
作者 GIF
这是最难做对的一个——我不得不多次修改注释,甚至打开/关闭文件几次来得到一个合理的建议。
多方面的
在这里,您可以看到我心血来潮修改的一些其他功能。
计算两个坐标之间的曼哈顿距离:
作者 GIF
创建嘈杂的图像:
作者 GIF
分类列的目标编码:
作者 GIF
将 Jupyter 笔记本解析为 JSON:
作者 GIF
副驾驶——判决
我在使用 Copilot 时注意到一些事情。
首先,它有很好的语境感。当我在脚本中包含/排除特定的库或一些代码时,我得到的建议经常改变。
在另一个个人项目中,我意识到 Copilot 可以从其他文件中提取特定的引用来帮助我编写剩余的代码。它也可以适应我的编码风格和评论。考虑到局部和全局变量,它完成了不同复杂度的for
和while
循环。
这是一个很好的节省时间的方法,特别是在你可以毫无困难地编写代码块,但是仍然需要花时间来编写的情况下。
通过在注释或文档字符串中尽可能具体,我得到了最好的结果。仅使用像 DataFrame 这样的词而不使用X, y
会导致不同的功能或参数。
我会继续使用它吗?见鬼,是啊!
虽然我大部分时间都呆在 Jupyter 实验室,但像许多其他数据科学家一样,我猜想当人们想转向开源、生产级数据科学时,Copilot 会非常有帮助。在那个阶段,你将把大部分代码从笔记本转移到脚本中,在那里你可以愉快地使用 Copilot 的协助。
考虑到该软件仍处于测试阶段,它已经是一个了不起的工具。
感谢您的阅读!
您可以使用下面的链接成为高级媒体会员,并访问我的所有故事和数以千计的其他故事:
****https://ibexorigin.medium.com/membership
或者直接订阅我的邮件列表:
https://ibexorigin.medium.com/subscribe
你可以在 LinkedIn 或Twitter上联系我,友好地聊一聊所有的事情数据。或者你可以读我的另一个故事。这些怎么样:
</8-booming-data-science-libraries-you-must-watch-out-in-2022-cec2dbb42437> </6-pandas-mistakes-that-silently-tell-you-are-a-rookie-b566a252e60d> </7-cool-python-packages-kagglers-are-using-without-telling-you-e83298781cf4> </22-2-built-in-python-libraries-you-didnt-know-existed-p-guarantee-8-275685dbdb99> ****
GL CMS——你的 ML 武器库的一个伟大的工具
原文:https://towardsdatascience.com/glcms-a-great-tool-for-your-ml-arsenal-7a59f1e45b65
灰度共生矩阵理论(GLCMs)和一个实际应用(带代码!)
- 什么是灰度共生矩阵?
- 它们有什么用?
- 我们如何应用它们?
这些是我将在本文中回答的问题。在这一点上相信我,我认为 GLCMs 很酷,我希望到最后你也会这么想。它们可以用来从图像中提取纹理特征,这将在本文的最后一节中完成。我假设你对什么是图像有一个大致的概念,并且对数学有一些基本的理解。
使用的代码(稍后)可以在下面的 git repo 中找到:
https://github.com/MartimChaves/glcm_sat_img [## 用于卫星图像分类的 GitHub - GLCMs
github.com](https://github.com/MartimChaves/glcm_sat_img)
所以,我们先从什么是 GLCMs 说起。
为此,我们需要一个图像和一个位置操作符——现在不用担心后者。
让我们看一个简单的图像,图像的像素值可以是 0、1 或 2。是的,这不是一个特别令人兴奋的形象。它似乎也不包含任何相关的信息,但是为了解释这一点,它可以做到,所以请原谅我。
作者提供的示例图像(3 种可能的像素值— 0、1 和 2,从黑到白)
现在,假设你想知道对于一个给定的像素,它下面的像素有多长时间有相同的值。你相信这会让你对这张图片的结构有所了解。一旦你对此进行评估,也许会有明显的重复模式——谁知道呢?
为此,首先创建一个非常简单的表格,有一行三列。在第一个单元格中,你输入的值是的多少倍一个值为 0 的像素和一个值为 0 的像素。在第二个单元格中,您执行相同的操作,但值为 1,在第三个单元格中值为 2。对于我们的图像,该过程如下所示:
由作者构建一个简单的初步表格
我猜你可以说,也许,这张图片有很多暗点和亮点。毕竟,可以说是经常是最低或最高值“一起出现”。也不存在中间值低于其自身的相同值的任何情况。
但现在你好奇了!而不是想知道多长时间,对于一个给定的像素,它下面的像素有相同的值,你想有一个视角 。
你想知道,对于每个可能的像素值,通常比它低一个像素的值是什么?所有值为 0 的像素是否都在同样保持值为 0 的像素之下?当然,我们知道事实并非如此,否则,图像中会出现可识别的黑色竖线。即便如此,也许你可以开始获得的直觉,为什么像素值之间的空间关系可能是相关的。
所以,要做到这一点,你创建一个矩阵,矩阵 C 。该矩阵具有三行和三列,一列和一行用于每个可能的像素值(0,1,2)。考虑到其中的表示法,对于【C(I,j)* , i 是行, j 是列,【C(0,0) 将保存值为的多少倍,对于一个像素其值为 0 ,则 C(0,1) 将值保存多少次,对于值为 0 的像素,下面的像素为 1, C(0,2) 也是如此。所以 i 算是我们的“起始值”,而 j 就是我们的“目标值”。 C(1,2) 将保存值 2 低于值 1 的次数。一旦你这样做了,这就是你得到的:*
作者计算 GLCM 的中间阶段
看我们的矩阵 C ,我们的下一步是规格化矩阵。首先,你对矩阵的值求和,得到对总数。之后,你用除以C的每个值——这样你就有了一个归一化矩阵。所以现在你有了 概率 ,一个特定值对出现在给定图像中的概率,遵循特定位置关系。**
C 是 a GLCM —恭喜你成功了!
这里有一个简短的视频演示最后的过程:
作者计算 GLCM 的最后一步
现在,也许你会奇怪,为什么我要特别注意像素和它下面的像素之间的关系?也许,你会更有兴趣知道这些像素的值如何与它们右边的像素进行比较。或者在他们的左边,或者在他们的右下方的一个像素,所以是对角线,或者任何你想到的…
这是位置符决定的,位置符是 由你决定 。
把图像看作一个矩阵本身,matrix I ,其中 i 是行, j 是列。你可以把位置运算符写成对数字,比如 (1,0) 。对于每个像素 I(i,j) ,你会看着像素 I(i+1,j+0) 的值,来填入你的矩阵【C】。
比如上图,考虑像素 I(1,2) —你可以看到它保存的是值 2** 。它下面的像素,, I(2,2) ,也是保存值 2 。所以,你会在单元格 C(2,2) 的值上加+1 ,因为起始像素值是 2 ,而目标像素的值,也就是原始像素下面的那个,也是** 2 。****
本质上,你只需将位置运算符的值加到原始像素的坐标上,就可以得到你需要考虑填入C的值。为了完全填充 C ,你必须对图像的每一个像素都这样做。****
也许,只是也许,你想做些疯狂的事。你想要捕捉一个像素和它下面 8 个单位和左边 3 个单位的像素之间的关系。是啊,太疯狂了。为此,你的位置操作符应该是(8,-3)** 。现在,也许这不是最好的位置运算符。这种空间关系对于我们理解一幅图像的内容来说并不常见。然而,这里的信息是 你是你的位置操作符 的老板。**
那么,现在进入下一个问题— 你能用它做什么?GLCMs 有什么用?
本质上,你可以分析 C —想象你想了解是否一个图像 I 是否 同质。
让我们看看使用 (0,1)位置运算符的 I 的 GLCM 。如果*非常,那么C 的最大值就会出现在主对角线上,即【C(0,0)【C(1,1)(C)这只是因为这将意味着通常两个相邻像素具有相同的值。这反过来意味着很可能会有具有相同值的大区域——这是一个同质图像*。**********
如果 C 的最高值出现在右上或左下单元格中,则与相对。这意味着更常见的是具有非常不同的值,这意味着 I 是一个具有大量 对比 的图像!我们可以用更量化的方式反映这些想法。
例如,我们可以使用下面的等式来计算同质性:
齐次方程
如果在其主对角线中找到 C 的较大值,则从该等式得出的均匀性值只会是大。对于主对角线之外的值, |i-j|表达式将保证离主对角线越远,那些值就越不重要。
****对比度可使用以下等式计算:
对比方程
在这种情况下,我们有对面的,离的主对角线越远的,赋予这些值的重要性**越高,因为它们被乘以 (i-j) 表达式。******
还有其他一些有用的公式可以用来分析 GLCMs。它们的操作方式与之前的操作方式相似。
例如 熵 :
熵方程
与, 相关 :
相关方程
其中:
相关方程元素的方程
****同质性、对比度、熵、等等都是可能的特征用于机器学习的例子。例如,你也可以使用 GLCM 本身,作为卷积神经网络的输入。
GLCMs 通常与 纹理 联系在一起。 纹理 可以看作是视觉模式的一种重复。使用适当的位置操作符,GLCMs 可以用于提取关于纹理的信息出现在图像中。毕竟,视觉模式的重复可以被视为具有特定方向的值组合的重复。****
这是 GLCMs 专业版的一个例子——它对纹理来说很棒!GLCMs 的一个缺点是它们在计算上有些昂贵、尤其是,如果你打算在上使用几个位置操作符。这将需要计算几个矩阵。要记住的另一个重要的缺点是,如果你只使用一个位置操作符,产生的 GLCM 将不会对特征旋转或比例变化保持不变。****
这里有一个非常简单的算法**来获得一个图像的 GLCM(在 python 中 git repo 的链接在一开始就共享了,这个在文件 my_glcm/my_glcm.py 中):**
作者计算 GLCM 的算法(出于可见性目的显示了 GLCM 的日志)
或者,您可以使用来自“skimage”python 库的“ graycomatrix ”!你可以在这里看文档:
- 要计算 GLCM:
- 要计算 GLCM 的属性(图像特征):
请注意,它们使用不同系统的钻头——位置操作符由角度和距离决定,但基本原理如前所述。****
出于好奇,我们来看看两幅不同图像的 GLCM 的对数:
Kate Palitava 的左图,在作者的左图的右日志(GLCM)上;b)左图为 Parviz Besharat Pur,右图为作者。注意,对数只是用来提高可见度。
对于图像 a),GLCM 的最高值似乎集中在主对角线周围。这并不奇怪,因为图像没有任何明显的变化。它大多是天空的图片,值的强度平滑地变化。你可以说这个图像非常均匀。
当你比较 a)的 GLCM 和 b)的 GLCM 时,你可以注意到明显的不同。b)的 GLCM 值更加分散,这是有意义的。雨伞对着砖墙的阴影,以及砖墙本身,引入一些的对比。希望到目前为止,GLCMs 对您来说相当直观。
最后, 我们如何应用它们呢?
让我们实际上使用 GLCMs 进行一个可能的真实世界应用 — 分类不管卫星图像是否包含一个油棕榈种植园!
我认为卫星图像将很好地展示 GLCMs 可以做什么。毕竟,比如山和海洋的图像的纹理之间的差异就是明显的。经过一番搜索,我在 Kaggle 上找到了这个数据集,上面有油棕榈种植园的卫星图像。这与 2019 年数据科学(WiDS)妇女(Women in Data Science,WiDS)数据大会有关,我决定试一试。如果你感兴趣,这里有链接:
https://www.kaggle.com/c/widsdatathon2019/data
我从一点点探索性数据分析 (EDA)开始。为此,我查看了属于每个职业的随机图像,以了解主要区别。油棕种植园的图像似乎清晰地包含了明显的黄色线条。我猜想,这些可能是通往不同地块的道路。
图片示例无 油棕榈种植园:
不同渠道的图像没有油棕种植园的作者
带有*油棕榈种植园的图片示例:
不同渠道的图像与油棕种植园的作者
为了进一步确认这一点,我查看了平均图像,以及第一张的每一类的“本征图像”。这些图像都显示,的确,油棕种植园的 sat 图像包含了与常规 sat 图像不同的纹理。这种纹理也许可以描述为穿过图像的清晰细线,通常平行于图像的轴。
平均和本征图像为每类作者
然后,是时候提取图像的纹理的部分特征了!为此,首先,图像被分离到它们的 RGB 和 HSV 通道。
该数据集的图像是 RGB 图像。这意味着图像是三维数组。除了宽度和高度,还有第三维,这是由于三个不同的颜色通道,红色,绿色和蓝色(RGB)。这三个原色通道重叠,产生了我们在图像中看到的许多不同的颜色。然而,有不同的系统(或颜色空间)来表示彩色图像。代表色调、饱和度和值的 HSV 色彩空间就是其中之一。色相是纯色,饱和度是那个颜色有多丰富,值是那个颜色有多强烈(强度越低越暗)。请看下图。
迈克尔霍瓦特的 HSV 色彩空间。未进行任何更改。在 GNU 自由文档许可证 1.2 下使用,可以在这里找到:【https://www.gnu.org/licenses/old-licenses/fdl-1.2.en.html】T2
这个系统经常用于计算机视觉,因为它被设计成类似于人类解读颜色的方式。在某些情况下,将实际颜色与其明暗程度分开,可以增加的鲁棒性。例如,想象一个数据集,其中一些图像有阴影,而另一些没有。如果图像中的实际颜色(色调)是重要的信息,那么使用 HSV 是有意义的。最终,这是一种不同的表示颜色信息的方式,它可能有用也可能没用,这取决于应用程序。
在这种情况下,查看不同类别的随机卫星图像,似乎使用 HSV 颜色空间也是有意义的。请注意,这两个类别之间的饱和通道似乎总是不同的。对于没有油棕种植园的类,饱和度似乎具有明显的纹理。我认为,色调和价值通道可能还包含一些相关信息,但这些信息并不明显。由于这些原因,HSV 颜色空间也被使用。
对 RGB 通道进行平均,同时计算出灰度。
其次,使用 (1,0)位置运算符计算这些二维数组的 GLCMs ,不同的 RGB 和 HSV 通道,以及灰度。
我认为这个位置操作符还不错,因为水平线是在油棕榈种植园卫星图像中常见的。然而,请注意,这可能是脆弱的,因为这将意味着特征依赖于图像的方向。如果方向改变了,那就意味着这个位置操作符的有效性降低了。考虑到这一点,建议使用更多的位置运算符。对于本文,为了简单起见,只使用了一个,以减少计算时间。
最后,为每个 GLCM 计算的均匀性、对比度、能量和相关性。考虑到这一点,对于每个图像,有 28 个特征 , 7 个 GLCMs 乘以所用的 4 个“度量”。
28 个功能不一定很多,但还是有必要去掉那些不需要的。这是考虑到维数灾难,以及无用信息。为此,最初计算了和特征之间的相关性 。其次,只有一个 特征 被保留,从对** 特征高度相关。**
这个将的功能池减少了不少,只有 14 个功能。为了进一步减少这个数字,因为也有助于计算时间,计算了剩余特征和标签之间的相关性。只有与和至少有一点相关性的特征和被保留。最后,只剩下 8 个特征——这些特征将用于训练模型。
他们是:
- 红色通道的同质性
- 红色和饱和度通道的对比度
- 绿色通道和红色通道的能量
- 红色、色相和饱和度通道的相关性
下面你可以找到一个与标签相关性最高的 5 个特征的对图。下划线前的第一个字母代表频道。
与作者标签相关性最高的五个特征的配对图
选好了我们的特色,是时候将它们传递给车型和列车了。在移除测试集的 10%数据后,剩余的数据被分割成 4 个分层褶皱——这是一个高度不平衡的数据集。这些折叠然后与 5 个不同的分类器一起使用,以确定哪一个是最好的。使用的分类器有:
- 随机森林
- 逻辑回归
- 梯度推进
- SVM
- KNN
为了确定哪个分类器是最好的,计算了三个指标:平衡准确度、AUC 和 F1 分数。最终,想到一个可能的现实世界应用,我决定选择具有最佳 F1 分数的分类器。
我在这里的想法是的后果不是检测油棕种植园并不严重(比如不像在医学领域)。同时,最好丢弃大部分阴性样品,也就是那些没有种植园的样品。这将意味着一个模型, 当它检测到一个油棕榈种植园时,它很可能是一个——这就是 F1 分数的全部内容。
下面你可以找到每个分类器的结果图。每个点对应不同褶皱的平均值和误差条为标准偏差。被选中的型号是 KNN 。
按作者列出的不同分类器的结果
然而,这似乎值得商榷。你怎么想呢?好奇想知道!
这些结果是使用和 sklearn 设置的默认超参数得到的。好奇的想知道他们是否有显著的不同,对三个超参数进行了随机网格搜索。
他们是:
- 要考虑的邻居数量(3 或 5)
- 使用的重量功能(“均匀”或“距离”)
- 如何计算距离(‘曼哈顿’或‘欧几里德’)。
结果表明,优化后的分类器比默认的分类器好 2.6 个百分点(F1-score) ,最终验证集 F1-score 为的 75.09% 。
理想参数为 5 个邻居 ,使用与欧几里德距离相关的权重。根据距离的权重不是统一的,意味着更近的邻居对于最终预测有更大的影响。
最后,剩下要做的就是在测试台上评估模型的性能。结果结果如下:
- 平衡精度:80.01%
- AUC: 85.61%
- F1 分数:71.88%
你可以在下面找到测试集的混淆矩阵和 ROC 。
作者为 KNN 的中华民国
作者使用 KNN 的测试集的混淆矩阵
对于这些结果该说些什么?嗯, 他们并不惊艳 ,但我确实觉得很不可思议8 个数字怎么能把的信息在一张图像中表现得那么好!
目标也是不是得到最好的结果,而是更多地向展示在你的机器学习武库中 GLCMs 是有价值的工具。
为此,我们:
- 查看了 GLCMs 的计算方式及其原因
- 如何将它们定量转化为信息
- 在实际场景中使用它们
我希望我证明了我的观点!你怎么想呢?
我希望你喜欢我的第一篇博文!如果您有任何问题,请随时在mgrc99@gmail.com提问。
你也可以在 LinkedIn 上联系我:https://www.linkedin.com/in/martim-chaves-081796186/
非常感谢您的阅读:)
PS:我倾向于用百分比来衡量——这只是个人偏好**
全球背景视觉变形金刚(GC ViT)——Nvidia 的新 SOTA 图像模型
深入的解释和可视化
免费使用来自像素的图像
介绍
Nvidia 最近发布了一款新的视觉转换器,名为全球情境视觉转换器( GC ViT ) ( Hatamizadeh 等人,2022 )。GC ViT 引入了一种新的架构,它利用了全局注意力和局部注意力,允许它模拟短程和远程空间交互。
Nvidia 研究人员使用的巧妙技术使 GC ViT 能够模拟全球注意力,同时避免昂贵的计算。GC ViT 在 ImageNet-1K 数据集上取得了最先进的(SOTA)结果,远远超过了 Swin Transformer。
在本文中,我们将更深入地了解 GC ViT 的内部工作方式,以及使它能够实现这种结果的技术。
GC ViT —改进 Swin 变压器
自 Swin Transformer ( 刘等人,2021 )于 2021 年发表以来,它已成为最重要的基于 Transformer 的视觉模型之一。
Swin Transformer 引入了重要的技术,如分层特征映射和基于窗口的注意力,这使它能够实现与传统卷积神经网络相比具有竞争力的性能。如今,Swin 变压器被用作各种视觉任务的主干架构,包括图像分类和物体检测。
尽管有所进步,Swin 变压器仍有某些缺点。最值得注意的是,Swin Transformer 中使用的基于窗口的注意力将交互的计算限制在每个窗口内,并限制跨窗口的交互。
在 Swin Transformer 中,注意力的计算被限制在局部窗口内。图像来自像素。作者创建的图表。
上图显示了 Swin Transformer 中使用的基于窗口的注意力的局限性的示例。输入图像被分割成单独的窗口,然后只在每个窗口内计算自我注意。这限制了全局图像中不同对象之间的长距离相互作用的计算。例如,狗和球被分割到不同的窗口中,模型被限制学习两个对象之间的交互。缺乏跨窗口连接限制了模型捕捉长程相关性的能力,而长程相关性对于精确表示建模至关重要。
Swin 转换器试图通过使用基于移位窗口的注意力来引入一些跨窗口连接。然而,这在计算上是昂贵的,并且它没有从根本上解决缺乏全球连接的问题。正如我们将在后面看到的,GC ViT 通过在单个架构中提供本地和全局连接对此进行了改进。
GC ViT 的体系结构
GC ViT 的总体架构。改编自 Hatamizadeh 等人,2022 。点击此处查看更高分辨率的图像。
GC ViT 的整体架构如上图所示。正如我们所看到的,GC ViT 由 4 个不同的阶段组成,每个阶段由局部和全局多头自我关注(MSA)层的交替块组成。本地 MSA 提取本地的短程信息,而全球 MSA 提取全球的远程信息。这使得 GC ViT 可以灵活地处理短期和长期依赖关系。在两个阶段之间,GC ViT 使用下采样块来创建类似于 Swin 转换器的分层特征图。
GC ViT 的主要贡献是全局令牌生成器和全局 MSA 层。在下一节中,我们将更详细地研究它们。
全球自我关注
全局自我关注的首要原则是在图像的每个区域之间建立全局联系。为了做到这一点,让我们来理解自我关注是如何将一幅图像分割成不同的碎片的。
为了计算自我注意,图像被分成窗口和小块
从上图中,我们看到每个图像被分割成独立的窗口(显示为紫色)。每个窗口被进一步分割成小块(以红色显示)。在局部自我注意中,小块之间的注意计算被限制在每个局部窗口内。换句话说,不同窗口中的补丁之间没有交叉连接,这限制了网络的建模能力。
全局自我关注旨在通过引入补丁和窗口之间的连接来解决这一问题,如下图动画所示。修补程序和窗口之间的这些全局连接允许 GC ViT 关注图像中的全局位置,有效地模拟长程相关性。
全局自我关注在每个补丁和每个窗口之间建立了联系
在局部 MSA(多头自关注)中,查询、键和值向量从局部窗口中的小块中导出,并且仅在每个局部窗口内计算关注度。相比之下,在全局 MSA 中,只有键和值向量是从局部窗口中的补丁中导出的。查询向量是从所有窗口导出的全局查询令牌。下图说明了本地 MSA 和全球 MSA 之间的区别。
本地管理服务协议与全球管理服务协议的比较
全局查询令牌由全局查询生成器在网络的每个阶段生成,全局查询生成器将特征地图作为输入,并提取全局特征作为全局查询令牌。全局查询令牌包含整个输入要素地图的信息,用于与本地键和值向量进行交互。
全局查询生成器由一系列操作组成,如下图所示。
全局查询生成器中的操作
全局查询令牌与本地键和值向量的交互允许计算全局注意力。这有效地扩大了感受野,并允许模型关注特征图中的各个区域。
向下采样
GC ViT 还引入了一种新颖的方法,在创建分层特征图的阶段之间对特征图进行下采样。有趣的是,GC ViT 使用卷积层进行下采样。作者认为,使用卷积进行下采样为网络提供了所需的属性,如位置偏差和跨通道交互。下采样块中使用的操作如下图所示。
下采样块内的操作
注意前 4 个操作与全局查询生成器的操作相似。事实上,作者将前 4 个操作称为“Fused-MBConv”块,它的灵感来自 EfficientNetV2。
结果
GC ViT 在 ImageNet-1K 数据集上进行图像分类训练。下表比较了 GC ViT 在 ImageNet-1K 数据集上与其他 CNN 和 ViT(包括 Swin Transformer)的性能。正如我们所看到的,GC ViT 达到了一个新的最先进的基准。此外,就 FLOPs 的数量而言,GC ViT 模型具有更好或相当的计算效率。
ImageNet-1K 数据集上的图像分类基准。来自 Hatamizadeh 等人,2022 。
与 Swin Transformer 相比,GC ViT 以更少的 FLOPs 实现了更好的性能,展示了结合局部和全局自我关注的好处。但是,请注意,Swin 转换器具有无卷积架构,而 GC ViT 使用卷积运算来计算全局注意力和下采样。
结论
GC ViT 引入了一种结合局部自我关注和全局自我关注的新型架构,允许网络对短期和长期交互进行建模。通过删除其他 ViT 中所需的复杂而昂贵的操作和掩码,GC ViT 在 ImageNet-1K 数据集上实现了新的 SOTA,同时计算效率更高。
不过,应当注意,与 Swin 变压器不同,GC ViT 不是无卷积架构,其部分性能可能源自卷积的电感偏置。
喜欢这篇文章?
感谢您的阅读!我希望这篇文章对你有用。如果您想订阅中级会员,请考虑使用我的链接。这有助于我继续创建对社区有用的内容!😄
用于联合时间序列预测的全局深度学习
原文:https://towardsdatascience.com/global-deep-learning-for-joint-time-series-forecasting-4b03bef42321
简单介绍一下这个领域最热门的模特
机器学习是一个众所周知的复杂领域,学术界和工业界都在实践,不断改进其基准,产生有趣的想法和解决问题的方法。
在解释其工作原理的适当理论形成之前,它已经在许多不同领域的无数实际应用中成功部署。
由于这个原因,有时很难跟上最新的架构;在这篇文章中,我们正在探索时间序列预测领域的最新成就,这是一类由于时间维度而具有其特殊地位的预测问题。
更准确地说,我们将考虑所谓的全局模型:被构建来一次检测许多相关时间序列的模式的架构,学习能够单独解释和预测每个序列的单一表示。
所有的事情,所有的地方,所有的时间
当预测模型在许多不同的数据集上训练时,它被称为全局,每个数据集都是其自身随机过程的随机结果。
这种模型每天都变得越来越常见,利用我们今天在许多领域拥有的大量数据,解决我们需要对单个小规模数据集进行具体预测的问题;然而,以前我们只能希望在更高的总体水平上进行预测。
这种方法的一个明显优势是,模型将受益于更多的训练数据,这在这个大数据时代可以相当于许多数量级的增长。
此外,如果我们希望学习的通用模式只存在于数据集中的一个子集,而在其他一些数据集中它还没有发生,全局模型可以学习将其归因于所有数据集。
另一方面,如果数据集来自不同的生成过程,根据定义,它们的结构和模式至少会有一些差异,因此,对于一个模型来说,忽略这些并均匀地预测一切并不总是可取的。
例如,来自同一家公司的不同商店的销售数据可以被认为是相似的,因为许多有助于数据生成过程的变量都是相同的(相同的产品、价格、营销……)。
尽管如此,许多其他变量(位置、当地客户习惯……)以一种模型难以检测的方式区分每个系列,这就是为什么我们需要将这些变量编码为其输入。
出于这个原因,全球模型倾向于处理关于数据集如何相似以及它们如何彼此不同的信息。我们通常通过给数据集附加标签来传达这一信息,每个标签对应一个我们想要追踪的特征;其他时候,我们只是指示模型相应地自动聚类数据集。
这里的问题是,生成过程,以及由此产生的结果,必须共享足够的信息,以便模型能够利用它们的交叉学习。
选择哪些数据集被认为是相关的可能有些随意,因为我们通常事先不知道我们是否能够实现这种交叉学习的优势,并且将太多不同的数据聚集在一起可能会引入太多的差异和噪声,从而造成损害。
总而言之,在尝试训练全球模型时,最重要的先决条件有两个:
- 要预测许多相关的数据集,这些数据集都来自相似的过程并显示相似的模式;
- 对它们有更多的了解,特别是它们的相似之处和不同之处。
几个有趣的模型
时间序列预测是一个特别适合全球模型的问题[1][2],因为有许多相关的时间序列并不罕见,通常是在一个固定的关系结构中:来自公司客户的数据,系统网络中的传感器,不同位置的交通流量…
与此同时,神经网络已被证明是理想的全球模型,因为它们能够处理任何类型的协变量特征,并且它们对巨大复杂数据集的总体偏好。
因此,深度学习、时间序列预测和大数据的结合近年来在学术和行业研究中找到了如此肥沃的土壤也就不足为奇了。
没有完整性的伪装,这里是这个星体结合的一些更有趣的结果。
请注意,这些模型的结果和基准可以在参考文献中找到;我们对这方面不太感兴趣,而是对其中任何一种方法的新颖性和巧妙性感兴趣。
迪帕尔
DeepAR [3]是一个基于递归网络的深度学习模型,致力于学习目标时间序列的一个自回归表示。
更具体地,它是 LSTM 细胞的多层网络,具有编码器-解码器结构,用于在每个训练步骤总结细胞过去的信息输出(称为调节范围),并使其可用于预测其未来(预测范围)。
它是一个概率模型,这意味着在给定相同参数的情况下,它输出表示预测值分布概率的似然函数的参数(其形状可由用户指定)。
预测可能性是一个聪明的技巧,具有很大的灵活性:从这样的函数中,我们可以抽取样本,生成分位数预测,置信区间,甚至可以选择 bootstrap Monte Carlo 样本,这些样本可以到达未来的任何步骤。
DeepAR [3]中训练(左)和推理(右)的图式。
DeepAR 当然配备了动态协变量,如日历特性和静态协变量:如果我们想训练一个单一的全球模型,这些正是我们必须附加到每个系列的标签。
目标系列和协变量[3]。
关于这个模型的内部工作的更多细节,例如它处理具有不同尺度的系列的巧妙方式,我们可以参考原始论文[3]。
StemGNN
光谱时间图神经网络[4]是一种创新和聪明的设计,用于联合学习系列间和系列内相关性的全局模型。
简而言之,该想法是在两个维度上、跨时间和跨序列使用傅立叶变换,以便获得可以被其他神经网络块(如卷积和顺序模块)学习的表示。
虽然在时域中对一个序列执行傅里叶变换是一种经典而简单的技术,但我们需要理解在固定时间对所有序列执行傅里叶变换意味着什么。
事实上,StemGNN 的第一层,称为潜在相关层,获取整个数据集并返回一个图,其中节点是时间序列,加权边是层本身推断的它们之间的相关关系。
这一层由一个GRU【9】和一个自我关注机制组成,但是如果领域知识建议使用一个特定的图形表示来代替,它也可以被删除;例如,可以使用将作为静态协变量传递给其他模型的相同标签来链接系列。
在这之后,该模型将几个相同的块依次应用于数据集在每个序列(其是图中的节点)的图形域( GFT )和时域( DFT )中的图形表示;除此之外,卷积块在反转这些变换以返回原始域之前,会学习这些变换所暴露的信息。
StemGNN 的结构相当复杂[4]。
这些块是这种架构的主要新颖部分,它们被恰当地称为 StemGNN 块。它们的内部工作实际上是非常复杂的,因此我们可以参考文献[4]来了解细节。
为了我们的目的,可以说在 DFT 和 GFT 之后应用谱图卷积允许我们共同学习出现在每个序列的谱表示以及相关图的谱矩阵表示中的模式。
最后,StemGNN 块以一种剩余的方式被应用,这样每个块可以更深入到前一个块留下的模式中。这是一个强大的技术,也是[4]中突出使用的技术。
DSSM
深度状态空间模型[5]是一个结合了深度学习和状态空间模型的新想法,以利用两种方法的优势。
状态空间模型(SSMs)[7][8]是一系列用于预测的经典统计模型,包括臭名昭著的 ARIMA 和指数平滑方法。
由于可以在模型中做出结构性假设,并通过选择模型的组成部分和特征来精心制作模型,因此当时间序列特征明确时,这些方法尤其适用。
然而,这种量身定制的方法有其问题,因为它需要有足够历史的知名剧集,并且它很难扩展到我们现在经常拥有的剧集数量。此外,作为一个系列的方法,它根本不能跨系列转移学习。
DSSM 的作者的想法是,然后通过递归神经网络摄取所有系列及其协变量来联合学习 SSM 参数:这里的术语“联合”是指这样的事实,尽管每个系列都有不同的 SSM 及其自己的参数,但神经网络的元参数在整个训练过程中是共享的。
换句话说,作为一项元学习任务,在所有系列中训练的单个 RNN 正在学习给每个系列分配不同的 SSM 参数。
像这样的模型,结合了机器学习和经典统计技术,在这种情况下被称为混合模型。
在操作上,在训练时,一个 LSTM 型网络输出 SSM 参数,这些参数被馈送给一个似然函数,该函数取决于该系列的特定 SSM 模型以及已知的观测值;然后最大化可能性以更新 LSTM 参数。
在 DSSM 培训[5]。
类似地,在推断时,DSSM 通过使用序列的已知值和训练集中的学习参数,计算在最后训练步骤的 SSM 状态的概率分布;这个概率既代表了知识,也代表了模型对我们所处状态的最佳估计。通过使用该概率,该模型然后在蒙特卡罗方法中展开任意数量的预测,通过递归地使用 SSM 和 RNN 在推断时给出的参数。
DSSM 的推论[5]。
DeepGLO
最后,另一个有趣的混合模型:DeepGLO [6],正如作者所说,是“一个深度预测模型,它从全球角度思考,从本地角度行动”。
它是一个经典的 矩阵分解 模型的组合,一个用于正则化它的卷积网络(TCN)和另一个独立的本地 TCN 作用于每个系列和第一个模型的输出。
DeepGLO 区别于许多竞争对手的特点是不仅在训练中而且在推理中一起使用所有时间序列。
再次引用作者的话,“例如,在股票市场预测中,在预测苹果的股票价格时,查看 Alphabet 以及亚马逊股票价格的过去值可能是有益的。同样,在零售需求预测中,可以利用类似商品的过去值来预测某个商品的未来。
该算法的经典部分,即矩阵分解模型,包括将所有时间序列视为单个矩阵,并将其分解为两个矩阵的乘积,称为因子。
矩阵乘积的工作方式使得每个因子将共享原始矩阵的一个维度:如果我们将此表示为形状为(n,t + tau)的 Y(出于训练和测试的目的,我们已经对时间维度进行了分割),则因子 F 和 X 将分别为(n,k)和(k,t + tau),k 是通常比 n 小得多的某个数。
分解维度的更清晰描述见下图。
矩阵分解:在这个示意性例子中,我们有 n 个长度为 t+τ的时间序列,其中 t 是训练集周期。整体来看,得到的分解是 Y = FX [6]。
实际上,X 可以被视为在 k 个基序列中编码全局信息,k 个基序列与原始基序列一样长,F 包含将原始基序列作为基序列的线性组合的系数。
这个模型与卷积网络的混合是相当复杂的,对于那些对细节感兴趣的人,我们遵从[6];简而言之,网络被用作正则化器,这意味着它鼓励因子分解过程给出接近网络预测的基序列。
在因子分解模型(a)中,基序列如何组合以形成实际目标的预测,以及来自该模型的全局预测如何与模型的最终 TCN 层中的局部序列组合[6]。
因子分解和卷积网络在交替步骤中被联合训练;在推断时,网络通过从因式分解给出的值开始预测基序列的未来值,并将它们与系数矩阵相乘给出最终的全局预测。
最后,另一个局部每序列神经网络通过将序列的过去值、来自先前模型的全局预测和局部协变量序列作为输入来起作用。
结论
毫无疑问,全球模型将在预测领域占据越来越大的份额,因为我们正在目睹数据、神经网络知识和商业应用的增长,而且这种增长似乎不会很快结束。
正如你所看到的,当涉及到它们时,有令人难以置信的多样性,特别是在预测方面:尽管它们都以神经网络为核心,但每个都有自己的锦囊妙计,主要专注于捕捉有趣的信息并将其暴露给网络本身。
特别值得一提的是混合模型,它不仅管理两个学习引擎的共存,还管理它们有利的相互依赖性,以获得大于其各部分之和的结果。
关于哪一个从根本上更好,存在争议:全局与局部[1][2],经典与混合与纯深度学习[10][11][12]。
可以说,这完全取决于我们所拥有的关于数据生成过程及其实际实现(时间序列)的信息;第一个应该指导我们为问题选择最好的函数类,即算法,第二个应该帮助它在类中找到最好的函数(训练结果)。
通常,先验信息的数量和质量应该与模型的复杂性和特异性相关联。
[1] H. Hewamalage 等人,时间序列预测的全球模型:模拟研究 (2021)。
[2] P. Montero-Manso 等,时间序列组预测的原理和算法:局部性和整体性 (2021)。
[3] D. Salinas 等人, DeepAR:用自回归递归网络进行概率预测 (2019)。
[4] D .曹等,多变量时间序列预测的谱时态图神经网络 (2020)。
[5] D. Rangapuram 等人,时间序列预测的深态空间模型 (2018)。
[6] R. Sen 等,全球思考,局部行动:高维时间序列预测的深度神经网络方法 (2019)。
[7] R. J. Hyndman 等人,用指数平滑法预测:状态空间法 (2008)。
[8] M. Aoki,时间序列的状态空间建模 (1990)。
[9] A .张等,潜入深度学习 (2020)。
[10] S. Makridakis 等人,统计和机器学习预测方法:关注点和前进方向 (2018)。
[11] B. N. Oreshkin 等人, N-BEATS:用于可解释时间序列预测的神经基础扩展分析 (2019)。
[12] K. Benidis 等人,用于时间序列预测的深度学习:教程和文献调查 (2018)。
全球化趋势和数据科学
近距离观察一段充满希望的关系
皮沙贝
介绍
“全球”一词已被广泛用于研究当代现实的许多细微差别。然而,鉴于世界各地发生的广泛变化,有必要更好地理解这些转变的动力。因此,全球研究领域诞生了,以更多地了解全球化影响自然世界的各种方式。
我们今天正在经历的变化无疑正在改变我们用来组织我们对世界的知识的地图。由于前所未有的数据访问,学习也发生了深刻的转变。换句话说,我们正在目睹两个而不是一个深刻的转变。我们试图在地图上描绘的“领土”变化的一部分是各国在社会、政治和经济上日益增长的相互依赖。尽管如此,我们可以用来创建这样一幅地图的数据量还是呈指数级增长。本体论和认识论的转变是同时发生的。结果,现实的本质和我们收集信息的方式同时被改变了。基于数据科学的方法工具及其对全球研究的潜在影响将在本文中从这个角度进行研究。
本世纪最重大的转变是数据的指数增长,这为分析我们周围世界的新方法打开了大门。近年来,我们已经看到大数据等颠覆性技术的兴起,这些技术对社会的许多方面产生了重大影响。自本世纪初以来,信息变得越来越稀缺、昂贵和难以获得。如果所提供的信息达到足够高的标准,就可以得出合理的结论。由于收集和存储的大量数据,我们感知现实的方式正在发生变化。这一事实要求我们彻底改变看待世界的方式。信息的价值不再在于具体的数据,而在于海量数据集之间的关联,以发现以前未知的模式。因此,通过计算分析,海量数据可以提供关于模式、趋势和隐藏关联的更精确信息。
这表明我们目前正处于一场数据革命之中。新技术正在推动可用数据的数量和类型呈指数级增长,创造了以前无法想象的报告可能性。因此,今天的数据比以往任何时候都更重要、更快速、更详细,并重塑了社会秩序。
迄今为止,大数据分析已经应用于科学研究的各个领域,以解决复杂的问题。这包括教育、卫生、交通、媒体和娱乐、国家安全以及生物医学和环境等领域的研究。使用数据提取技术来提高或预测就业能力的项目在这些项目中脱颖而出。植物生长数据、温室气体测量和气候变化对作物的影响都被用于在农业中使用数据科学来提高农业生产率。因此,大数据技术有望在精准医疗和疾病治疗与预防的未来发展中发挥重要作用。大数据有广泛的应用,包括开发癌症基因组图谱以支持癌症研究;用计算和认知方法研究语言和非语言交流;分析多种蛋白质序列,以确定进化联系和预测分子结构;发展城市区域情报系统,这有助于改善城市居民的生活。
全球研究最近才开始深入分析大量数据的主题。全球研究中的一个重要争论是数据科学提供的方法可能性和局限性是否能帮助我们更好地理解当今全球化世界的复杂性和多样性。
这是通过将它分成 3 个部分来实现的。在第一部分中,讨论了数据科学的理论方面。
第二部分将理解数据科学可用于进行全球研究。
最后,我们将讨论将大数据纳入全球研究的理论和方法上的影响。
1.数据科学的概念框架
由于互联网对社会如何生成和消费信息的影响,如果不考虑这项技术,就不可能将数据科学概念化。互联网是用于交换数据的互联计算机的通用网络。在这种情况下,连接许多设备(如计算机、文件服务器和摄像机)的网络被称为“复杂网络”。在包括技术领域在内的全球霸权竞争中,美国和苏联在冷战期间开发了这项技术。使用这种方法,苏联在 1957 年发射了人造卫星。高级研究计划局(ARPA)就是为了应对这一威胁而成立的。最后,这个组织为互联网的出现奠定了基础。Paul Baran 在 1962 年提出了一种防止外部攻击的通信系统,使用连接到分散网络的计算机。
这种通信系统是通过整合大学网站的虚拟网络发展而来的;后来,商业平台将被整合,社交网络将被开发,一系列应用将被产生,例如地理参考。这种虚拟的联系产生了一个连续的和不断增长的信息体。可以说,打个比喻,我们从模拟个体变成了数字个体,这意味着我们现在几乎随时都可以通过不同的设备永久连接。得益于日益强大的计算机的进步,这产生了大量可以获取、分类和分析的数据。
最初,大量信息的产生被专门从事互联网站点管理的公司用来在该数据集中寻找机会以增加他们的利润。然而,这种以产生大量信息为特征的新环境已经让位于建立一个以知识为基础的社会,在这种社会中,一种新的职业脱颖而出,即知识工作者,他们拥有技能、能力和技术,能够处理持续的信息流动。这种知识工作者已经发展成为一种新的员工形象,现在被称为数据科学家。该专业人员将统计、数学、编程和解决问题的技能与数据捕获相结合,能够执行适当的数据清理、准备和集成活动,以定位数据中的模式。
现在,数据科学由三个领域组成:
1.大数据,用于处理数据。
2.数据挖掘,其目的是发现模式,即使它们不是以前想象的?
3.数据的可视化,其目的是便于清晰地理解信息,促进其社会化。
为了大致了解海量数据的含义,可以指出,自 2018 年 3 月以来,脸书社交网络已注册 22 亿月活跃用户和 14.5 亿日活跃用户,这一数字比上一年增加了 13%;同样,据估计,WhatsApp 的月活跃用户有 15 亿;此外,Twitter 平台上每天发送约 5 亿条推文,这意味着在过去六年中增加了十倍;最后,据估计,每天在谷歌搜索引擎中有超过 7,000 万次查询。
从这个意义上说,社会科学家必须考虑与大数据现象相关的三个挑战:
1.与日益增长的数据量的存储、安全和分析相关的技术问题。
2.商业价值是通过产生更有效的感知来增加的另一种方式。
3.社会影响,特别是使用数据保护个人隐私的影响。
因此,鉴于每天产生的数据量巨大,根据术语物联网(IoE)提出一个参考框架来确定信息生成过程,旨在对互联网上的所有可用数据进行分类。因此,建立了三个类别。第一个是内容互联网(IoC ),它代表了人们寻求增加特定主题知识所产生的信息;这些信息的例子有文章和博客、维基百科这样的百科全书、YouTube 这样的视频平台和 Google Books 这样的电子书。第二,人民互联网(IoP),包括社会互动产生的信息;例如,电子邮件、社交网络和虚拟论坛。第三类包括物联网(IoT),其包括由连接到网络的物体产生的信息;它是关于所有的事物,这些事物都有一个独特的标识,并且存在于一个类似互联网的结构中。从这个意义上说,事物可以有互联网连接或被网络标记。最后,还有地点互联网(IoL),其中包括因采用移动设备而产生的具有空间维度的信息;例如,智能手机生成越来越多具有地理空间属性的事件。
关于大数据的第二个领域,即数据挖掘,将其视为对观察到的数据集(通常数量巨大)的分析,旨在发现变量之间的新关系,以及以可理解和有用的方式对所述数据集进行正确总结,数据挖掘是智能数据分析的科学和艺术,从中产生感兴趣的知识。另一方面,数据挖掘被认为是发现信息模式或知识发现的基本步骤。这被理解为寻找有效的、相关的、潜在有用的模式的过程,试图使它们变得可理解。
由于数据挖掘是一个提取信息的过程,它使用的方法和模型允许定义以前未知的变量之间的关系或模式。现在,他们使用的模型有两种类型:描述性的,试图找出允许他们总结和解释数据行为的模式;和预测模型,它们试图根据感兴趣的变量的历史行为来估计该变量的未来值。因此,数据挖掘过程包括数据描述、估计、预测、分类、聚类和关联。
将这些模型中使用的技术分为有监督的和无监督的。监督技术用于建立模型,这些模型用于进行预测。这些包括开发通用算法、神经网络、决策树和回归分析。无监督技术或知识发现算法通常用于从大量数据中提取有用的信息。这种类型的例子有聚类、链接分析和频率分析。
数据科学的最后一个要素是可视化。这种技术允许我们使用不同的方法和技术,在图表或表示的帮助下,检查大量数据并识别模式或趋势。虽然信息的可视化在人类的发展中确实存在,但只是在最近,由于处理能力更强的软件和能够方便绘图和表示的所谓库的发展,它的使用才得到扩展。
显然,数据科学的每个组成部分都在不断发展。毫无疑问,这一事实影响了科学研究的开展方式,并影响了不同的知识领域。因此,下一节将分析全球研究和数据科学之间良好关系的可能性。
2.全球研究和数据科学:一种有前途的关系?
虽然全球化现象并不是当代生活的一个独特趋势,但全球化研究是一个相对较新的科学学科,因为社会科学家只是在最近才开始从历史角度和当代方法系统地分析跨国和全球体系的网络、流动、过程、意识形态和表现。此外,全球研究这一学术领域是在当前日益全球化的背景下出现的,目的是为了更好地理解近几十年来加剧的变化,这些变化在全球范围内增加了政治、经济和社会的相互联系。自从 20 世纪 90 年代第一批全球研究学术项目和研究中心在北美、欧洲和亚洲建立以来,这一学科得到了广泛的发展。目前,据估计,全世界大约有 200 个研究中心从不同角度研究这一全球现象。
然而,当从全球研究的角度出发时,需要考虑到这一学科并不仅仅意味着分析全球化。在这方面,Nederveen (2013)提出了全球研究与全球化研究明显不同的三个方面。首先,他认为全球化研究产生于一股浪潮,这种浪潮在性质上不同于推动全球化分析的浪潮,这种全球化分析来自于已经有着悠久知识传统的特定学科。全球研究不涉及与特定学科相关的问题,这些学科将全球作为一个研究主题,就像全球政治经济、全球文化或全球传播一样。在这个意义上,他提出全球趋势首先加剧;后来,全球化开始从已经建立的知识领域进行研究;最后,今天,我们正处于第三阶段,在这个阶段中,全球研究学科作为一种分析全球化的不同方式出现。基于跨学科的方法,这种接近全球的方式超越了特定学科所包含的知识的传统界限。第二,他警告说,从知识的角度来看,全球研究仍然是一个新兴的学科,他称之为无屋顶的脚手架。第三,它肯定了全球研究可以增加传统全球研究之外的价值,因为它们渴望建立一个多中心的知识结构,它不仅将西方世界作为知识和历史经验的中心,而且从多层次的角度出发,其中地方、国家和全球具有同等的重要性。
在定义全球研究时,可以强调以下基本特征。此外,它们是跨国的,因为它们侧重于在内容和范围上分析跨国界和跨文化现象。最重要的是,他们本质上是跨学科的,因为他们研究的现象从经济到政治到社会到文化到宗教到意识形态到环境和生物。同样,全球研究既是历史的,也是当代的,因为有必要考察历史先例,以充分理解当前的全球模式。最后,它们通常是批判性的和后殖民主义的。一般来说,在西方形成的经济、政治和文化全球化模式不会被全球研究领域不加批判地接受。因此,他们认识到,根据社会和经济地位的不同,对全球问题、动态和趋势的看法在世界不同地区和同一国家或地区会有所不同。因此,全球研究表明,根据其社会和经济地位,全球各地甚至同一国家或地区的人们以不同的方式看待全球动态和趋势。因此,来自世界各地的研究表明,根据他们的社会和经济地位,世界各地甚至同一国家或地区的人们可以以不同的方式感知全球动态和趋势。这就是为什么全球研究提倡使用“多重全球化”这一术语,它承认没有一种主导范式或观点比另一种更有价值。
分析视角的根本转变是必需的,这需要对每一个学科的主导分析模式进行彻底的修正,因为各种学科集体转向理解全球化及其带来的问题。因为它们受到支持和反映它们产生的时间和地点的传统理论和方法规范的限制,例如,完全在已经建立的学科范围内进行的关于全球化的研究将产生这些方法是不全面的。另一方面,全球研究旨在克服这些限制,并纳入关于历史上被排除在学科叙事之外的群体的多种见解。
由于主题的复杂性,全球研究是社会科学和人文科学中的一个独特学科。这是因为目前的范式无法在其概念框架内理解全球的动态和趋势。这种根本性的无能源于我们对知识的先存观念,以及我们用来验证我们自认为知道的东西的准确性的方法。作为一门新的科学学科,全球研究可能意味着我们当前的本体论和认识论范式都是不充分的。
由于这种矛盾的无力调和两种范式,我们有机会探索概念化研究对象的新方法,我们称之为全球,以及旨在更好地理解和解释这一现象的新的方法论建议。典型残疾为我们提供了一个机会,去想象获取、分析和解释我们目前无法获得的信息的新方法。我们可以研究数据科学是否能为全球研究学科提供新的东西,以及它是否能丰富或补充传统上分析全球化及其后果的主要方法。
也有可能提出一个智力问题,即基于数据科学的新方法会在多大程度上改变我们对全球的本体论基础的理解。当涉及到研究全球趋势时,我们将如何能够在地方或国家层面,甚至在全球层面上微调我们对大数据的搜索和关联?我们对世界的看法会改变吗,或者我们只是简单地“扩展”我们现有的知识?数据科学将揭示什么样的全球化模式及其影响,或者它将代表一种新的以欧洲为中心的学术知识形式,限制其他非西方知识的声音,这是全球研究试图夺回的?下一节将考察数据科学对全球研究可能的方法论意义,试图提供一些答案。
3.数据科学对全球研究的理论方法论意义
今天,“指纹”可以用来在难以想象的规模上检查人类活动,让我们更好地理解人类行为及其许多方面。大数据催生了一个全新的研究领域,而计算机科学提供了生成和收集数据的新方法,以及新的分析和统计方法以及可视化和呈现数据的方法。这些新技术和信息来源可能会彻底改变社会科学方法论。数据科学中出现了一些方法,这些方法正在改变知识的对象,同时产生了关于社会互动和人类环境的新叙事。换句话说,如果我们能够改进我们的估计所基于的先验信息的结构,我们就能够减少我们所获得的知识的不确定性。
另一方面,方法论工具或方法只是拼图中的一块,它可以帮助我们理解趋势,建立关系,得出结论,并建立合理的解释,从而有助于我们理解我们周围的世界,通过提供我们周围环境的背景和意义,给我们一个“世界的科学图像”。根据这一定义,数据科学像任何其他科学方法一样,在科学知识的发展和发现“世界的科学图像”方面发挥着关键作用。此外,每种方法都受到植根于过去和现在的国家传统和跨国影响的影响。任何科学研究方法的固有局限性都源于它们产生的特定环境,这与数据的大量使用以及与数据收集和解释相关的研究技术不无关系。
作为一种社会研究方法,数据科学可以为全球研究带来新的见解。尽管如此,它也有缺点,比如难以获得大量数据,难以建立更严格的相关性和模式。事实上,算法越来越多地被用于调解社会进程、商业交易和政府决策。如果我们不对算法设计和实现的伦理含义采取批判性的立场,将会给个人、团体和整个社会带来严重的后果。
我们需要问的基本问题是,海量数据的分析究竟能为我们获取世界全球化趋势信息的传统方法做出什么贡献?因此,这种收集和分析数据的方法丰富了全球研究作为一门科学学科的贡献。也就是说,如何通过使用大规模数据分析来丰富区分全球研究的跨学科方法、多中心光学和多层次视角。
另一方面,大数据分析使我们能够看到以前未观察到的趋势,这些趋势不是基于我们以前的思维模式。与传统的社会科学方法不同,数据科学研究从一张白纸开始,必须从海量数据中筛选出他们想要的答案。这种方法依赖于从完善的理论中得出的概括,以指导经验证据的搜索。它更像是一张巨大的网,在没有进行彻底调查的情况下就被扔进了大海。海量数据分析旨在收集尽可能多的数据,这与传统的定量或定性方法不同,传统的定量或定性方法之前已经决定了要寻找的数据。在短时间内处理大量数据的能力使得人们发现了数据之间以前未知的联系。在全球研究领域,移民可以作为一个例子来说明这种情况。人们提出了许多理论来解释为什么人们在世界各地迁移。然而,如果我们可以通过手机跟踪移民的数据消费,那么对于移民、停留、消费、期望和决策会有什么新的解释呢?通过如此大规模地比较这些数据集,我们能推断出什么?什么样的连接是可能的?马格里布人和中美洲人有一些相似之处,但他们在全球移民方面也有一些重大差异。有哪些共性?有什么区别?
识别大规模数据集中的模式使我们能够更好地理解局部现象,这是第二个贡献。由于数据收集和处理历来是繁重的任务,大多数数据是在国家一级提出的。由于不可能比较多个案例和多个变量,案例研究被视为一种理想的方法,而不是了解特定地区或场所动态的方法。然而,可以建立地方模式,并与国家和跨国模式进行比较。重要的是要记住,大部分的海量数据是社会的。作为讨论的一个例子,有可能在全球范围内审查与当地具体情况相联系的文化消费模式。因此,有可能探讨以下问题:Twitter 讨论的当前趋势是什么?特定的国家或全球趋势在哪里影响特定的位置?这两者之间有很强的联系吗?换句话说,一旦这些发现被做出,它们属于什么社会群体?全球南方是否同时影响着全球的讨论趋势,或者仅仅是全球北方?如果是的话,有没有什么模式可以用来解释这一点?
因此,可以生成多个建模变量,数据的大量使用克服了简约原则。这证明,在同样的情况下,最简单的解释通常是正确的。在某种程度上,可以理解的是,在时间、成本和空间等约束条件下,倾向于更简单并故意拒绝复杂性的模型将被采用,这些约束条件传统上限制了可以分析的信息量。随着技术革命的进展,处理数据将变得更加简单。具有多个变量的复杂模型可以在地方、国家和跨国范围内进行研究,以建立更复杂的相互关系模式。
换句话说,学术文献已经广泛研究了全球范围的社会运动和争取人权的斗争。另一方面,从网络活动家的角度来看,数据科学有可能帮助挪用和捍卫这些事业。从那些使用社交网络倡导人权的人的个人资料中,我们还能了解到什么?还有哪些网络行为最能说明网络激进分子是如何将他们的本地环境与全球需求联系起来的,这有待讨论。这些行为产生的相互关系和反应模式…这两者之间有什么联系?在这方面,值得注意的是,有大量文献专门研究社会运动和全球争取人权的斗争。换句话说,学术文献已经广泛研究了全球范围的社会运动和争取人权的斗争。大量文献致力于研究全球社会运动和争取基本人权的斗争。
对大量数据的实时分析使得放弃线性成为可能。这允许获得连续的信息流,打破了预先确定的时间链。例如,关于生产活动和人口普查的统计由于这种实用性而暂时中断。然而,由于数据分析和收集的进步以及能够提取和说明大规模模式的算法,可以考虑新的时间截止点。由于信息的不断流动和实时分析的能力,研究人员现在可以在他们选择的任何时间长度内研究一个主题。例如,使用来自世界各地的数千份医疗记录,跟踪感染爆发的出现和传播如何帮助我们更好地理解全球和地方是如何联系的?如疫情的不寻常和意想不到的增长。这个事件和其他事件所涉及的变量之间有关联吗?如果患者和卫生专业人员的运动可以用来生成地理参考数据,它可能会提供什么新的见解?
另一方面,使用大量数据并不是理解我们社会当前全球化动态的灵丹妙药。大数据分析的非关键用途至少存在四大风险。强调经验主义认识论,回归实证主义作为一种更高形式的知识。当涉及到获取准确信息时,即使打算彻底收集数据并提供所研究现象的全面情况,由于用于获取数据的技术平台、数据模型和监管框架,采样偏差也是不可避免的。第三,低聚的世界观可以在所有数据中找到:来自特定观点和特定工具的观点。没有任何自然元素可以从表面上看作为绝对真理;相反,数据是在一个复杂的集合中创建的,这个集合积极地塑造了它们的构成,而不是以客观和中立的方式从世界中抽象出来。
第二个危险是成为预测科学的牺牲品,该科学假设人类的行为模式是可预测的。根据这些假设,可以使用基于先前记录的模式的适当算法来发现预测的人类行为。换句话说,分析的数据越多,预测就越准确。自由意志和结构决定论之间的争论可能会促进人们对人类行为规律的兴趣的复苏。也就是说;人们相信,随着算法越来越擅长预测天气等自然现象,它们也将能够更好地预测特定情况下的人类行为。全球安全领域的例子包括声称“通过预测‘下一次恐怖袭击’和在犯罪分子实施攻击前将其逮捕,承诺保护未来”的基于数据科学的预测分析已经实现。我们可能会错误地得出结论,认为对海量数据的分析最终可以建立明确的模式,解释地方、国家和全球层面的社会行为;我们可能会进一步提出,这个算法将确定我们做什么,以及我们应该做什么。使用预先存在的信息模式可能会缩短我们的决策范围。根据一项警告,新分析中使用的算法可能会在不自动提问的情况下发现见解。尽管如此,它们在特定的背景下经过了科学的检验,而这些背景不一定是普遍的。
当谈到理解当今世界时,数据科学可能会导致因果关系和相关性之间的错误选择。存在这样一种风险,即解释结构的一个方面优于另一个方面,偏向于相关的倾向,这种倾向阻止了基于因果关系的解释的建立。然而,这些数字并不能说明全部情况。从相关性中推断因果关系是不正确的。然而,大规模数据分析可以检测大量数据源之间的相关性,但它不能确定相关性是否足够显著。
此外,当寻找变量之间的相关性时,统计显著性检验被期望仅基于偶然发现虚假的相关性。必须使用叙述和上下文来理解数据,并赋予其形式和意义。这些数据必须作为更大的解释或意义构建过程的一部分被调动起来,以使其有意义。然而,价值论中立并不意味着科学不受哲学和政治的考虑。不中立的叙事结构显然反映了这些目标。最终,数据科学只是众多故事中的一个。
最后,在信息技术时代,我们通过数字互动产生了大量信息流,将真实的东西还原为这些信息流所表达的东西,这增加了制造虚假现实图像的可能性。或者我们花钱的方式。请记住,当今世界的排斥动态创造了一种新的被丢弃类型:被丢弃的数字,其稀疏或不存在的数据“足迹”在云中。如果我们过于强调数据驱动的不可见性,它是可能的。作为一种新形式的隐形、排斥和隔离,这种风险比数字文盲更大。
根据上述讨论,数据科学可能会给全球化带来风险。尽管如此,它也为企业和劳动力市场提供了许多好处和机会。
Go 1.18 带来了许多惊人的变化
原文:https://towardsdatascience.com/go-1-18-comes-with-many-amazing-changes-d33ac0afd6ee
Go 1.18 将于二月发布,它包含了多项改进语言的变化
图片由珀西·博尔梅尔提供。Gopher 由拓也·上田提供,原始 Go Gopher 由勒内·弗伦奇提供(CC BY 3.0)
期待已久的围棋 1.18 即将到来,这可能是很长一段时间以来讨论最多的围棋更新。语言中加入了一些重要的新东西,其中最突出的是泛型。
许多新加入的东西被忽略了,因为每个人都专注于泛型,所以让我们快速回顾一下即将到来的所有变化。在即将到来的更新中,有一些我觉得很重要的改变,你看不到提到那么多。
对于一些主要特性,我有一篇深入的文章,如果您想更深入地了解该特性,欢迎阅读。
如果你想看视频,你可以在我的 Youtube 上找到这篇文章。
我在视频中讲述了 Go 1.18 的所有变化
无商标消费品
泛型的可视化解释,用函数表示的一行,泛型接受多个输入。图片由珀西·博尔梅尔提供。Gopher 由拓也·上田提供,原始 Go Gopher 由勒内·弗伦奇提供(CC BY 3.0)
如前所述,Go 最终得到了泛型。这似乎是一个让开发人员感到不安的话题,一些开发人员似乎喜欢这个想法,一些似乎讨厌它。
泛型是一种允许函数接受多种不同数据类型的方式。当您必须对许多不同的类型执行相同的操作时,这很有用。
Go 中缺少泛型的问题以前已经解决了,每个数据类型使用重复的函数,或者使用一个空的interface{}
来处理所需的数据类型,这个空的interface{}
是用一个大的switch
进行类型转换的。这两种变通方法是解决泛型缺乏的常见方法。在 1.18 版本中,这些解决方案,或者说黑客攻击可以被移除。
[]interface{} hack — Generic Developer
的日子一去不复返了
想象一下,你需要对一部分整数和一部分浮点数的值求和。这将需要您有两个重复的函数,函数将是相同的,一个for range
通过并添加值并返回结果。唯一的区别是定义的输入参数数据类型。
在泛型出现之前,这需要一个特定于每种数据类型的汇总函数,如下所示。
用重复函数求解不同数据类型的总计数
使用泛型,所有这三个汇总函数都可以用同一个函数来代替。
让我们看看用泛型来解决这个问题时是什么样子。
汇总总值的通用解决方案
在我看来,这是一个巨大的进步。不仅减少了代码量,而且更易于维护、阅读,是一个不太复杂的解决方案。
如果你想了解更多关于如何使用泛型的知识,你可以阅读我写的关于泛型的文章。在这篇文章中,我们介绍了如何使用泛型,以及还可以用它做些什么。
https://medium.com/@percybolmer/learning-generics-in-go-318f53752ccd
模糊测试
Go 中的 Fuzzing 允许我们测试随机生成的函数输入。图片由珀西·博尔梅尔提供。Gopher 由拓也·上田提供,原始 Go Gopher 由勒内·弗伦奇提供(CC BY 3.0)
模糊是我最喜欢的更新之一。我很惊讶人们不再谈论它。它将使我们能够在测试过程中生成输入参数。
这听起来可能不多,但是它允许我们容易地扩展单元测试来定位和跟踪更多的 bug。表驱动测试允许我们给出许多不同的输入,但是你可能不会写几百个不同的测试输入,但是 fuzzing 可以允许这样产生。
Fuzzing 允许你测试数以千计的不同输入,这有助于发现隐藏的错误,你可能不会想到自己检查的错误。
关于模糊化的想法是,通过为你的函数生成一堆不同的输入,有更高的机会找到突破性的边缘情况。fuzzing 包将接受一个示例输入作为基础,称为seed corpus
。fuzzer 将使用这些示例作为起点,并对其进行修改,以找到导致函数中断的值。
Fuzzing 将被添加到testing
包中,它将是一个新的结构,使用方式与testing.T
相同,但不是testing.F
。
来自 go.dev 的一个关于 fuzzing 的例子看起来是这样的,注意他们如何添加 GET 查询参数作为种子语料库,这将允许 fuzzer 生成数千个类似的输入。
go.dev —模糊示例
如果你想了解如何使用 fuzzer 和编写 fuzzing 函数,for,etc HTTP 处理程序或更多,那么你可以阅读我的文章。
https://medium.com/@percybolmer/fuzzing-tests-in-go-96eb08b7694d
工作区
工作区模式允许我们以更好的方式替换版本和模块。图片由珀西·博尔梅尔提供。Gopher 由拓也·上田提供,原始 Go Gopher 由勒内·弗伦奇提供(CC BY 3.0)
发布工作区是为了让开发人员能够轻松地切换项目中使用的模块版本。这样做的原因是为了在同时处理多个模块时简化开发。
如果你一直在公司环境中使用 go 模块,你很可能被迫添加一个replace
指令来改变或强制使用一个包的本地版本。
replace
指令很容易忘记从您的go.mod
文件中删除,如果将其推送到存储库,将会破坏其他开发人员的模块。因为工具现在将使用在go.mod
文件中给定的路径,这很可能不存在于另一个开发人员的计算机上。
如果你对replace
不熟悉,你可以通过告诉它用本地路径或特定版本替换某个模块来替换要在go.mod
文件中使用的模块,让它获取一个本地分支,而不是用go get
获取库
go . mod-最小替换指令。
你可以在要点中看到我是如何告诉 go 工具,任何时候它看到github.com/programmingpercy/mymodule
它都应该从我在/home/users/percy/experimental/mymodule
的计算机上的本地路径获取,而不是使用go get
最后,我们有一个针对go.mod
文件中的replace
指令的补救措施。这对我来说很重要,我经常发现自己同时在多个模块上工作,同时对它们进行修改。
要开始使用他们所说的workspace
模式,您可以使用常规的 go 工具初始化工作区。类似于创建一个模块,您运行go work init
。这将创建您的工作空间文件,我们可以开始使用。
注意,工作空间模式将使许多 go 工具开始使用go.work
文件,并为您修改它。go 团队声明您不应该将go.work
文件推送到存储库,因为这将在go.mod
文件中再次引入replace
的问题。
工作区模式将强制使用go.work
中提到的所有模块,类似于go.mod
文件。我们仍然可以使用replace
指令来指定要使用的模块。这允许您继续使用 replace 指令,同时保持您的go.mod
文件干净,并且只有。git 忽略go.work
。
在的提议中,提到了更多的特性,在不同工作区之间切换以轻松测试多个依赖版本的能力,等等。迫不及待地想尝试这个功能。
荣誉奖
有一些变化值得一提,但没有大到足以有他们的一章。
任何 —如果你开始看到any
出现在 go 代码库中,不要惊慌。这是interface{}
的一个新的预声明别名
可比较的 —一个接口的预声明别名,包含所有可以使用==
和!=
进行比较的类型。这与泛型相关联,并被用作约束。
net/netip — 看起来我们将会得到一个新的处理 ip 地址的包。我在 Go 里做过很多人脉,处理 IP 地址一直很痛苦。看起来我不是唯一一个经历这种情况的人,让我们希望这个解决方案会更好。
琴弦。削减——我从来不知道我们需要的改进。strings 包附带了一个新的函数,Cut
,它将找到一个分隔符并返回在分隔符前后找到的任何内容。它还将返回一个布尔值,表明是否找到了分隔符。
IP:端口—我正看着你
结论
此次更新将带来许多受欢迎的变化。go 团队一直在努力工作,我们可以从许多重大变化中看出这一点。
我特别喜欢工作区和 fuzzing 的介绍。
go 语言正在成熟,作为一个地鼠,我很自豪地看着它成长。
如果你喜欢我写的文章,如果你想了解如何使用新特性,请确保查看关于主要主题的深入文章。你应该了解一下泛型和模糊化。
你还在等什么,下载1.18 更新,并开始使用所有新的闪亮奖品!
使用多类和多标签模型超越二元分类
此外,了解多类的不同策略,以提高性能
照片由朱迪思·吉拉德-马克扎克在 Unsplash 拍摄
什么是多类多标签分类?
通常当你开始学习机器学习中的分类问题时,你会从二元分类或者只有两种可能结果的地方开始,比如垃圾或者非垃圾、欺诈或者非欺诈等等。除此之外,我们还有多级和多标签分类。让我们从解释每一个开始。
多类别分类是指目标变量(y
)中有两个以上的类别。例如,你可以有小型、中型、大型和大型,或者你可以有一个基于一到五颗星的评级系统。这些级别中的每一个都可以被认为是一个类。该策略的目标是从可用类别中预测出一个类别。
多标签分类略有不同。这里你也有两个以上的类别可用,但是不是只选择一个类别,这个策略的目标是在适用的时候预测多个类别。当您有多个相互关联的类别时,这种策略非常有用。我见过的最好的例子之一是当它被用在标签系统中时。例如,中型文章可以有多个与之相关联的标签。这篇特定的文章可能有machine learning
、data science
和python
作为标签。
在开始之前,如果你想深入了解二进制分类,可以看看我的文章:构建一个惊人的二进制分类器所需要知道的一切。
现在,让我们深入研究多类策略。
多类策略
策略是你如何指导分类器处理两个以上的类,这可能会影响性能泛化或计算资源。泛化指的是分类器在未来对看不见的数据工作得有多好;这与过拟合相反。查看 Scikit-Learn 文档了解更多信息。
一个对其余的或(OVR),也称为一个对所有的 (OVA)策略,适用于每个类别的单个分类器,该分类器适用于所有其他类别。OVR 本质上是把多类问题分裂成一组二元分类问题。OVR 往往表现良好,因为有n
类的n
分类器。OVR 是最常见的策略,你可以从这里开始你的旅程。这实际上是这样的:
- 分类器 1: 小型 vs(中型、大型、xlarge)
- 分类器 2: 中型 vs(小型、大型、xlarge)
- 分类器 3: 大型 vs(小型、中型、xlarge)
- 分类器 4: xlarge vs(小、中、大)
如果你有非常多的班级,OVR 倾向于不能很好地扩展。一对一可能更好。接下来我们来谈谈这个。
一对一 (OVO)策略适合每对类别的单个分类器。看起来是这样的:
- 分类器 1: 小型与中型
- 分类器 2: 小与大
- 分类器 3: 小与大
- 分类器 4: 中型与大型
- 分类器 5: 中型与大型
- 分类器 6: 大与 x 大
虽然的计算复杂度高于 OVR 策略,但当您有大量的类时,这可能是有利的,因为每个分类器适合数据的较小子集,而 OVR 策略适合每个分类器的整个数据集。
最后,对于多标签分类,还有MultiOutputClassifier
。类似于 OVR ,这适合每个类的分类器。然而,与单个预测输出相反,如果适用,这可以为单个预测输出多个类别。
注意:专门针对 Scikit-Learn 库,所有分类器都支持多类。你可以使用这些策略来进一步完善他们的表现。
作者图片
实践中的多级分类
我们开始吧!现在,我们已经了解了一些术语,让我们来实施一些策略。对于这个例子,我们将使用 Kaggle 上的女装电子商务服装评论数据集,它可以在 CC0:公共领域下使用。这是一组评论文本、评级、部门名称和每个项目的类别。我们将构建一个分类器,它可以根据评论文本预测部门名称。
首先,我喜欢使用管道来确保过程的可重复性。管道允许你将数据转换成适合机器学习的格式。我们有两个特征需要改变。我们向数据集添加了评论文本和文本长度特征。审查文本将利用 TF-IDF 进行矢量化,第二个将使用MinMaxScaler
对数字数据进行标准化。
注意:流程中遗漏了很多步骤。像导入数据、、文本清理等等。滚动到末尾,在 GitHub 上获得该笔记本的链接。
def create_pipe(clf):
column_trans = ColumnTransformer(
[('Text', TfidfVectorizer(), 'Text_Processed'),
('Text Length', MinMaxScaler(), ['text_len'])],
remainder='drop')
pipeline = Pipeline([('prep',column_trans),
('clf', clf)])
return pipeline
接下来,我们需要将我们的数据分成用于学习的X
数据和目标变量y
,这是模型将如何学习适当的类。
X = df[['Text_Processed', 'text_len']]
y = df['Department Name']
y.value_counts()
Tops 10048
Dresses 6145
Bottoms 3660
Intimate 1651
Jackets 1002
Trend 118
Name: Department Name, dtype: int64
通过检查目标的value_counts()
,我们可以看到我们正在处理不平衡的数据。对最低的几类,尤其是趋势的观察数量,比最高的类少得多。我们将使用一个支持不平衡数据的分类器,但是要深入研究不平衡数据,请参见我的另一篇文章:在构建 ML 模型时不要陷入不平衡数据的陷阱。
接下来,您应该用LabelEncoder
对您的目标变量进行编码。虽然 Sklearn 倾向于很好地处理基于文本的类名,但最好在训练之前将所有内容都转换成数字形式。
le = LabelEncoder()
y = le.fit_transform(y)
le.classes_
array(['Bottoms', 'Dresses', 'Intimate', 'Jackets', 'Tops', 'Trend'], dtype=object)
通过打印出classes_
,我们可以看到哪些类是根据列表的顺序编码的。
当然,在训练我们的模型时,我们需要将数据集分成训练和测试分区,允许我们训练我们的模型,并在测试集上验证它,看看它的表现如何。
# Make training and test sets
X_train, X_test, y_train, y_test = train_test_split(X,
y,
test_size=0.33,
random_state=53)
接下来是打印分类报告和混淆矩阵的功能。查看我关于如何最好地评估分类模型的文章以获得更多信息:停止使用准确性来评估你的分类模型。
def fit_and_print(pipeline):
pipeline.fit(X_train, y_train)
y_pred = pipeline.predict(X_test)
print(metrics.classification_report(y_test, y_pred, digits=3))
ConfusionMatrixDisplay.from_predictions(y_test,
y_pred,
cmap=plt.cm.YlGn)
plt.tight_layout()
plt.ylabel('True label')
plt.xlabel('Predicted Label')
plt.tight_layout()
在这里,我们创建分类器的实例,对其进行拟合和评估。我们使用的是LogisticRegression
,它本质上是一个二元分类器。Sklearn 已经直接实现了 multi-class 作为分类器的参数,但是为了演示 One-vs-Rest 如何工作,我将使用包装器。为了实现这一点,你用我上面谈到的OneVsRestClassifier()
策略包装你的分类器。这个包装器指示分类器如何处理多类。我们也可以以同样的方式使用OneVsOneClassifier
策略。继续尝试这两种方法并比较结果。
clf = OneVsRestClassifier(LogisticRegression(random_state=42,
class_weight='balanced'))
pipeline = create_pipe(clf)
fit_and_print(pipeline)
precision recall f1-score support
0 0.785 0.855 0.818 1193
1 0.905 0.844 0.874 2037
2 0.439 0.581 0.500 527
3 0.551 0.794 0.650 315
4 0.909 0.818 0.861 3361
5 0.022 0.061 0.033 33
accuracy 0.810 7466
macro avg 0.602 0.659 0.623 7466
weighted avg 0.836 0.810 0.820 7466
作者图片
我们可以看到,该模型列出了六个类以及每个类的相对性能。人数较多的班级表现得相当好,而人数较少的班级表现较差。用这么少的观察来概括5
类是不可能的。在现实生活中训练模型之前,您可能需要收集更多的数据。
多标签分类
接下来,我们将看一个多标签分类问题。我们将使用与之前相同的数据集,但这次我们将使用类名作为目标变量,并将评论文本和部门名的组合作为我们学习的特征。我们首先创建我们的X
和y
数据。
我们需要对类名进行记号化,以确保当我们二进制化文本时,我们不会将单词分割成单独的字母,而是将整个单词作为一个整体来维护。
# Tokenize the words
df['Class Name'] = df['Class Name'].apply(word_tokenize)
X = df[['Text_Processed', 'Department Name']]
y = df['Class Name']
要创建多标签分类器,需要将目标二进制化为多标签格式。在上面的例子中,我们使用了LabelEncoder
,它只是在这里将目标类名转换为整数;我们将使用MultiLabelBinarizer.
如果我们在二进制化拟合后打印y
,我们可以看到它产生一个 0 和 1 的矩阵。任何时候你将一个矩阵 ( n 维数组 y)传递给一个分类器(相对于一个矢量, 1d 数组,它将自动成为一个多标签问题。
mlb = MultiLabelBinarizer()
y = mlb.fit_transform(y)
print(y)
[[0 0 0 ... 0 0 0]
[0 1 0 ... 0 0 0]
[0 1 0 ... 0 0 0]
...
[0 1 0 ... 0 0 0]
[0 1 0 ... 0 0 0]
[0 1 0 ... 0 0 0]]
打印这些课程
print(mlb.classes_)
array(['Blouses', 'Dresses', 'Fine', 'Intimates', 'Jackets', 'Jeans', 'Knits', 'Layering', 'Legwear', 'Lounge', 'Outerwear', 'Pants', 'Shorts', 'Skirts', 'Sleep', 'Sweaters', 'Swim', 'Trend', 'gauge'], dtype=object)
我们可以看到类的数量比上面的六个部门名称要多。接下来,我们将创建一个类似于前面的管道——但是,这次我们需要以不同的方式处理部门名称。我们将使用OneHotEncoder
,它在 DataFrame 中为每个类名创建一个新列。这个过程用0
或1
标记每个观察值,这取决于类名是否在该行中。
def create_pipe(clf):
# Create the column transfomer
column_trans = ColumnTransformer(
[('Text', TfidfVectorizer(), 'Text_Processed'),
('Categories', OneHotEncoder(handle_unknown="ignore"),
['Department Name'])],
remainder='drop')
# Build the pipeline
pipeline = Pipeline([('prep',column_trans),
('clf', clf)])
return pipeline
与上面类似,我们将包装我们的分类器。这次我们将使用MultiOutputClassifier
来指示分类器如何处理多个标签。剩下的过程是一样的。
clf = MultiOutputClassifier(LogisticRegression(max_iter=500,
random_state=42))
pipeline = create_pipe(clf)
pipeline.fit(X_train, y_train)
y_pred = pipeline.predict(X_test)
score = metrics.f1_score(y_test,
y_pred,
average='macro',
zero_division=0)
print(metrics.classification_report(y_test,
y_pred,
digits=3,
zero_division=0))
precision recall f1-score support
0 0.691 0.420 0.523 973
1 1.000 1.000 1.000 1996
2 0.544 0.087 0.150 355
3 1.000 0.019 0.036 54
4 0.784 0.969 0.867 229
5 0.943 0.676 0.788 340
6 0.712 0.662 0.686 1562
7 0.500 0.022 0.042 46
8 1.000 0.174 0.296 46
9 0.708 0.568 0.630 213
10 0.875 0.393 0.542 107
11 0.847 0.683 0.756 463
12 0.765 0.263 0.391 99
13 1.000 0.808 0.894 312
14 1.000 0.039 0.076 76
15 0.711 0.404 0.516 445
16 0.958 0.590 0.730 117
17 1.000 1.000 1.000 33
18 0.544 0.087 0.150 355
micro avg 0.844 0.640 0.728 7821
macro avg 0.820 0.467 0.530 7821
weighted avg 0.812 0.640 0.688 7821
samples avg 0.658 0.666 0.661 7821
与上面类似,每节课观察次数少的学生成绩不会很好,但是观察次数足够多的学生往往表现很好。让我们看看分类器预测了什么。
我们将通过获取预测的类名,然后使用之前我们拟合的MultiLabelBinarizer
中的inverse_transform
方法,向我们的测试数据帧添加一列。我们可以直接将它添加为一个列。为了更好地展示本例的结果,我们将只过滤那些具有多个标签的观察值的数据帧。
# Retreive the text labels from the MultiLabelBinarizer
pred_labels = mlb.inverse_transform(y_pred)
# Append them to the DataFrame
X_test['Predicted Labels'] = pred_labels
filter = X_test['Predicted Labels'].apply(lambda x: len(x) > 1)
df_mo = X_test[filter]
df_mo.sample(10, random_state=24)
Text_Processed Department Name Predicted Labels
cute summer blous top... Tops (Blouses, Knits)
awesom poncho back ev... Tops (Fine, gauge)
great sweater true fo... Tops (Fine, Sweaters, gauge)
love top love fabric ... Tops (Blouses, Knits)
love alway pilcro pan... Bottoms (Jeans, Pants)
love shirt perfect fi... Tops (Blouses, Knits)
simpl stylish top jea... Tops (Blouses, Knits)
tri youll love beauti... Tops (Fine, Sweaters, gauge)
qualiti sweater beaut... Tops (Fine, Sweaters, gauge)
cute top got top mail... Tops (Blouses, Knits)
厉害!您可以看到一些文本和部门名称以及预测的标签。如果你浏览一下结果,它们似乎对什么样的职业可能属于顶端和底端很有意义!
结论
你有它!超越二元分类需要一点额外的知识来理解幕后发生的事情。编码分类快乐!我跳过了很多步骤,但是我已经在 GitHub 上为你准备好了所有的步骤
如果你喜欢阅读这样的故事,并想支持我成为一名作家,考虑注册成为一名媒体会员。一个月 5 美元,让你可以无限制地访问成千上万篇文章。如果您使用 【我的链接】 注册,我会为您赚取一小笔佣金,无需额外费用。
超越 CSV:熊猫的数据摄取
原文:https://towardsdatascience.com/go-beyond-csv-data-ingestion-with-pandas-4c1a0dd4f2e2
CSV 中的数据不会总是出现在你面前
熊猫 read_csv()方法。图片由作者提供。
如果你是一个完全的初学者,或者认为自己是数据工程或数据科学的初学者,那么你完全熟悉 Jupyter 笔记本和下面的命令。
**#importing dataset**
df = pd.read_csv(“path/filename.extension”)
这样你就知道如何在 CSV(逗号分隔值)中采集数据了。毕竟,在你能够分析数据之前,你首先要获得它。
从我的经验和大部分的教程/资源来看,它们都是用工作 CSV 文件编写的。所以,当你去一家公司工作,有人给你 SQL 和/或 JSON 格式的数据时,你会开始恐慌,并产生一种的自我形象,“哦,这将会很艰难。”
但是,如果你关注这个博客,你可以很容易地在 jupyter 笔记本上处理任何类型的数据。
记住:在你分析数据之前,你首先要获取数据。
让我们开始吧。数据摄取是从各种来源将数据提取到您的系统中的过程,目的是方便地探索、维护和使用数据供您自己使用。今天,您将学习如何使用各种 Pandas 方法(下面突出显示为 read_csv() )将 Excel、HTML、SQL 和 JSON 等各种格式的数据传输到 Pandas dataframe 对象中。
熊猫 read_csv()方法。图片由作者提供。
涉及的步骤
为了将这种数据格式转换成 Python DataFrame 对象,采取以下步骤:
- 了解从哪里以及如何提取数据。
除非你知道你的数据在哪里,以什么格式,否则你永远不会把它作为 DataFrame 对象。之后,如果您的数据驻留在服务器上,那么您将需要文件的路径和 URL。
2.使用熊猫的read _ x方法
要将 CSV 数据加载并转换为 DataFrame 对象,使用 read_csv()。你需要将 x 换成另一个。
3.使用 DataFrame 对象验证数据。
接下来,您需要验证转换是否顺利。
对于 Excel
Excel 表格(电子表格)至今仍广泛应用于各种领域。Excel 被广泛使用,从制定预算策略到跟踪每天的学生出勤。
- 方法: read_excel()
- 扩展名/格式:。xlsx 或。xls
目前,您将使用来自 OpenDataNepal 的尼泊尔 2021 年全国人口和住房普查的初步数据数据集。我已经将这个数据集保存为 n epalcensusdata.xlsx
Excel 数据集的快照。图片由作者提供。尼泊尔 2021 年全国人口和住房普查初步数据数据集来自 OpenDataNepal 。
**#import the Pandas library** import pandas**#import excel data and store as data_excel dataframe (no need to do df always)** data_excel = pd.read_excel("n*epalcensusdata.xlsx*")**#view the dataset as pandas dataframe object** data_excel.head()
输出:
从 data_excel.head()获得的输出为 Pandas dataframe。图片由作者提供。
如果您的工作簿中有多个工作表,则需要一个名为 sheet_name 的附加参数。
data_excel = pd.read_excel("nepalcensusdata.xlsx",sheet_name = 0)
data_excel.head()
熊猫 read_excel()方法。图片由作者提供。
在我们的例子中,我们正在导入第一张表,因此它被赋予 0。图纸编号从 0 开始。
对于 HTML
如果你对数据刮痧(网页刮痧)比较熟悉,那么美汤、硒、刺儿头你都比较熟悉。你检查网站的 HTML 结构并提取数据。如果你觉得这有时很耗时或者很忙乱,你可以使用 read_html() 方法很容易地做到这一点。
- 方法: read_html()
- 扩展:。html
我们将从维基百科中摘录一张 金钱大劫案 (西班牙语: Casa de Papel) 系列关于网飞的表格从以下网址:https://en.wikipedia.org/wiki/Money_Heist
我们将提取奖项和提名表。
从维基百科获得的金钱抢劫奖项和提名表的快照。图片由作者提供。
**#import the Pandas library**
import pandas**#import html data and store as data_html dataframe** data_html = pd.read_html("[https://en.wikipedia.org/wiki/Money_Heist](https://en.wikipedia.org/wiki/Money_Heist)")[1]**#view the dataset as pandas dataframe object**
data_html.head()
输出:
从 data_html.head()获得的关于网飞系列金钱大劫案作为熊猫 dataframe 的奖项和提名的输出。图片作者。
熊猫 read_html()方法。图片由作者提供。
这里, read_html() 方法的初始输出是一个列表,要将该列表转换成 dataframe,必须在末尾使用[0]。所以,这里您使用了[2],因为在维基百科页面上,您想要提取奖项和提名表。在这张桌子之前,还有两张桌子。一个名为“信息框”,另一个名为“剧集”,要提取信息框,您需要使用[0]和剧集表[1]。
Pandas read_html()方法,用于摄取不同的表。图片由作者提供。
记住使用 read_html() 并不是一件简单的任务。为了得到你正在看的精确的表格,你需要毫无挫败感地玩你的初始结果。
请务必点击 订阅此处 千万不要错过另一篇关于数据科学指南、技巧和提示、生活经验等的文章!
对于 SQL
结构化查询语言(SQL) 是一种在数据库中存储、操作和检索数据的标准语言。从数据库中读取数据是一个两步过程。您首先创建一个连接到数据库的方法,然后用 SQL 和 Pandas 查询它。在 Python 中,要连接到 SQL 服务器,您需要有 SQLAlchemy connectabl e 或 sqlite3 。
- 方法: read_sql_query()
- 延伸:。db
与其他格式不同,对于 SQL,您必须多写几行代码。首先与数据库建立连接。然后,您将使用 Pandas 的 read_sql_query() 函数将输出转换为 dataframe,并编写一个 sql 查询从数据库导入所有列。
现在,演示 my_aquarium.db 是用一个名为“fish”的表创建的,该表包含以下几列: name,species,tank_number。
**#import the required libraries** import sqlite3
import pandas**#prepare a connection object and Pass the Database name as a parameter** connection = sqlite3.connect("*aquarium.db*")**#use read_sql_query method and Pass SELECT query and connection object as parameter** data_Sql = pd.read_sql_query("SELECT * FROM fish", connection)**#****view the dataset as pandas dataframe object** print(data_Sql)**#close the connection object** connection.close()
输出:
从read_sql_query
()获得的输出,鱼表为熊猫数据帧。图片由作者提供。
熊猫 read_sql_query()方法。图片由作者提供。
记得每次打开数据库连接时都要关闭它。
对于 JSON
JavaScript Object Notation(JSON)是 web 开发人员和数据工程师最常用的文件格式。它在互联网上被广泛应用于你将要访问的几乎每一个 API。
- 方法: read_json()
- 分机:。 json
您将使用来自 Kaggle 的海贼王动画的数据,存储为 openPiece.json
来自 Kaggle 的海贼王动漫截图。图片作者。
**#import the Pandas library**
import pandas**#import html data and store as data_json dataframe** data_json = pd.read_json("opnePiece.json")**#view the dataset as pandas dataframe object**
data_json.head()
输出:
从read_json
()获得的输出为熊猫数据帧。图片由作者提供。
熊猫 read_json()方法。图片由作者提供。
通过这种方式,您可以获得各种格式的数据。
记住:获取数据只是开始。
当然,获取数据只是开始。在此之后,你需要探索数据,并在深入数据科学项目之前玩数据。但是如果你想知道下一步该怎么走,请阅读我的博客探索性数据分析。
https://medium.com/analytics-vidhya/exploratory-data-analysis-for-beginner-7488d587f1ec
PS:我能够更深入地研究这个问题,是因为尼泊尔的代码提供了数据研究机会。我想感谢 Code for Nepal 为尼泊尔社区提供了一个绝佳的机会,也感谢 DataCamp 团队对 Code for Nepal 的信任。
嘿👋
喜欢这篇文章吗?成为一个中等会员继续学习,没有限制。如果你使用 下面的 链接,我会收到你的一部分会员费,不需要你额外付费。
如果你对这篇文章有任何疑问,或者想在你的下一个数据工程或数据科学项目上合作,请在LinkedIn上 ping 我。
还有,做 订阅 在我发布的时候得到通知。
这里有更多的文章,你可能会喜欢❤
https://medium.com/nightingale/how-to-apply-gestalt-psychology-principles-in-data-visualization-6242f4f1a3de https://medium.com/@maladeep.upadhaya/how-datacamp-and-code-for-nepal-helped-me-learn-data-engineering-for-free-7983ea2ae24a
深入,但也…去贝叶斯!
原文:https://towardsdatascience.com/go-deep-but-also-go-bayesian-ab25efa6f7b
Julia 中真正轻松的贝叶斯深度学习
贝叶斯神经网络后验预测的更新。图片作者。
近年来,深度学习一直主导着人工智能研究——但它真的有多大希望?这是一场持续不断且越来越两极化的辩论,你可以在 Twitter 上进行现场直播。一方面,你有像 OpenAI 首席科学家 Ilya Sutskever 这样的乐观主义者,他认为大型深度神经网络可能已经有轻微的意识——这是“可能”和“轻微”,只有当你足够深入?另一方面,你有像朱迪亚·珀尔这样的著名怀疑论者,他长期以来一直认为深度学习仍然可以归结为曲线拟合——纯粹是联想的,甚至一点也不聪明(珀尔和麦肯齐,2018 年)。
贝叶斯深度学习的案例
无论你发现自己站在这场有趣的 twitter 争议的哪一边,现实是深度学习系统已经在学术界和工业界大规模部署。因此,围绕这些现有系统的可信度展开了更为紧迫的辩论。他们有多强,他们是如何做出影响我们每一个人的决定的?强健的深度神经网络通常涉及某种形式的对抗性训练,这种训练成本高昂,可能会损害泛化能力(Raghunathan 等人,2019 年),并且最终不能保证稳定性(Bastounis、Hansen 和 Vlač,2021 年)。关于可解释性,像莱姆和 SHAP 这样的代理解释器是最受欢迎的工具,但它们也缺乏鲁棒性(Slack et al. 2020)。
到底为什么深度神经网络是不稳定和不透明的?首先要注意的是,自由参数的数量通常是巨大的(如果你问 Sutskever 先生,它可能真的不够大!).仅此一点就使得监测和解释深度学习算法的内部工作非常困难。也许更重要的是,相对于数据大小的参数数量通常是巨大的:
[……]深度神经网络通常很少被可用数据指定,并且[……]参数[因此]对应于数据的各种令人信服的解释。(威尔逊 2020)
换句话说,训练单个深度神经网络可能(并且通常确实)导致非常适合底层数据的一个随机参数规范。但是很有可能有许多其他规格也非常适合这些数据。这既是深度学习的优势,也是其弱点:这是一个优势,因为它通常允许我们通过随机优化轻松地为数据找到一个这样的“令人信服的解释”;这是一个弱点,因为人们不得不怀疑:
如果一个解释与许多其他同样令人信服,但可能非常不同的解释竞争,那么这个解释到底有多令人信服?
像这样的场景非常需要从概率上对待深度学习模型的预测(Wilson 2020)。形式上,我们感兴趣的是将后验预测分布估计为以下贝叶斯模型平均值(BMA):
这个积分意味着我们基本上需要从模型的许多不同规格中得到许多预测。不幸的是,这意味着我们或者说我们的计算机要做更多的工作。然而幸运的是,近年来研究人员提出了许多巧妙的方法来逼近上述方程:Gal 和 Ghahramani (2016)提出在测试时使用辍学,而 Lakshminarayanan 等人(2016)表明,对仅五个模型进行平均似乎可以达到目的。然而,尽管这些方法简单且有用,但与仅训练单个网络相比,它们涉及额外的计算成本。正如我们现在将看到的,另一种有希望的方法最近引起了人们的注意:拉普拉斯近似 (LA)。
如果你读过我在上一篇关于贝叶斯逻辑回归的文章,那么拉普拉斯这个术语你应该已经很熟悉了。事实上,我们将看到,上一篇文章中涉及的所有概念都可以自然地扩展到深度学习。虽然这些概念中的一些将在下面重新讨论,但我强烈建议你在阅读这里之前先看看以前的帖子。事不宜迟,现在让我们看看如何使用 LA 进行真正轻松的深度学习。
拉普拉斯近似
虽然 LA 在 18 世纪首次被提出,但它迄今为止并没有引起深度学习社区的重视,这主要是因为它涉及到可能很大的 Hessian 计算。Daxberger 等人(2021 年)的任务是改变 LA 在 DL 中没有用处的看法:在他们的 NeurIPS 2021 论文中,他们从经验上证明了 LA 可用于生成贝叶斯模型平均值,该平均值在不确定性量化和非分布检测方面至少与现有方法不相上下,并且计算成本显著降低。他们表明,最近在自动微分方面的进步可以用来产生快速准确的 Hessian 近似,甚至提供一个成熟的 Python 库,可以用于任何预训练的 Torch 模型。在这篇文章中,我在 Julia 中建立了一个不太全面的、纯粹的等效包——bayeslaplace . JL可以与内置在 Flux.jl 中的深度学习模型一起使用,这是 Julia 的主要 DL 库。正如上一篇关于贝叶斯逻辑回归的文章一样,我将依靠 Julia 代码片段而不是方程来传达基本的数学知识。如果你对数学感兴趣,NeurIPS 2021 论文提供了你需要的所有细节。你还可以在我的博客上找到这篇文章的更详细的版本。
从贝叶斯逻辑回归…
让我们回顾一下:在逻辑回归的情况下,我们假设用于计算逻辑值的权重是零均值高斯先验,这些权重反过来被馈送到 sigmoid 函数以产生概率。我们看到,在这种假设下,解决逻辑回归问题对应于最小化以下可微损失函数:
作为我们迈向贝叶斯深度学习的第一步,我们观察到以下情况:上面的损失函数对应于具有 sigmoid 激活和权重衰减的单层人工神经网络所面临的目标。换句话说,正则化的逻辑回归等价于一个非常简单的神经网络体系结构,因此理论上可以以非常相同的方式应用基本概念也就不足为奇了。
因此,让我们快速回顾下一个核心概念:LA 依赖于这样一个事实,即在最大后验概率 (MAP)估计下评估的损失函数的二阶泰勒展开相当于多变量高斯分布。特别是,高斯分布以 MAP 估计值为中心,协方差等于在该模式下评估的逆 Hessian 分布(Murphy 2022)。
这基本上就是故事的全部:如果我们对 Hessian 有一个好的估计,我们就有了一个关于参数的(近似)后验概率的解析表达式。所以让我们使用 BayesLaplace.jl 在 Julia 中实现这个方法。下面的代码生成了一些玩具数据,构建并训练了一个单层神经网络,并最终拟合了一个事后拉普拉斯近似:
下图显示了 2D 特征空间中的后验预测分布。为了比较,我也添加了相应的插件评估。请注意拉普拉斯近似法的预测概率如何呈扇形散开,这表明在缺少数据的区域可信度降低。
图 1:使用插件估计器(左)和拉普拉斯近似(右)的 2D 特征空间中逻辑回归的后验预测分布。图片作者。
…到贝叶斯神经网络
现在让我们更上一层楼:我们将重复上面的练习,但这一次使用简单的 MLP 代替我们上面使用的单层神经网络来处理不可线性分离的数据。下面的代码几乎和上面的一样:
图 2 再次证明了拉普拉斯近似产生的后验预测分布比过分自信的插件估计更保守。
图 2:使用插件估计器(左)和拉普拉斯近似(右)的 2D 特征空间中 MLP 的后验预测分布。图片作者。
要了解为什么这是一个令人满意的结果,请考虑下面图 2 的缩小版本:插件估计器在完全缺乏任何数据的区域中进行完全有把握的分类。可以说拉普拉斯近似产生了更合理的图像,尽管它也可能通过微调我们的先验和神经网络结构来改进。
图 3:使用插件估计器(左)和拉普拉斯近似(右)的 2D 特征空间中 MLP 的后验预测分布。缩小。图片作者。
包扎
最近关于神经信息处理的最新研究表明,贝叶斯深度学习可以毫不费力:深度神经网络的拉普拉斯近似似乎非常有效,并且它以最小的计算成本做到了这一点(Daxberger 等人,2021)。这是一个好消息,因为转向贝叶斯的理由很充分:社会越来越依赖复杂的自动化决策系统,这些系统需要值得信赖。越来越多的这些系统涉及深度学习,这本身并不值得信赖。我们已经看到,通常存在深度神经网络的各种可行的参数化,每一种都有其自己独特的和令人信服的对手头数据的解释。当面临许多可行的选择时,不要孤注一掷。换句话说,去贝叶斯!
资源
为了开始贝叶斯深度学习,我在网上找到了许多有用的免费资源,其中一些列在下面:
[Turing.jl](https://turing.ml/dev/tutorials/03-bayesian-neural-network/)
教程关于 Julia 中贝叶斯深度学习。- 各种 RStudio AI 博客帖子,包括这个和这个。
- TensorFlow 关于概率层回归的博文。
- 凯文·墨菲的草稿教科书,现在也有印刷版本。
参考
巴斯图尼斯、亚历山大、安德斯·汉森和韦尔纳·弗拉契奇。2021.“人工智能中对抗性攻击的数学——尽管存在稳定的神经网络,但深度学习为什么不稳定。” arXiv 预印本 arXiv:2109.06098 。
Daxberger,Erik,Agustinus Kristiadi,Alexander Immer,Runa Eschenhagen,Matthias Bauer 和 Philipp Hennig。2021.“拉普拉斯 Redux——毫不费力的贝叶斯深度学习。”神经信息处理系统的进展 34。
加尔,亚林和邹斌·格拉马尼。2016."作为贝叶斯近似的辍学:表示深度学习中的模型不确定性."在机器学习国际会议上,1050–59。PMLR。
拉克什米纳拉亚南、巴拉吉、亚历山大·普里策尔和查尔斯·布伦德尔。2016."使用深度集成的简单和可扩展的预测不确定性估计." arXiv 预印本 arXiv:1612.01474 。
凯文·墨菲,第 2022 页。概率机器学习:简介。麻省理工出版社。
珀尔朱迪亚和丹娜麦肯齐。2018.原因之书:因果的新科学。基础书籍。
Raghunathan、Aditi、Sang Michael Xie、、John C Duchi 和 Percy Liang。2019."对抗性训练会损害概括能力." arXiv 预印本 arXiv:1906.06032 。
斯莱克、迪伦、索菲·西尔格德、艾米丽·贾、萨米尔·辛格和希马宾杜·拉卡茹。2020."愚弄莱姆和夏普:对事后解释方法的对抗性攻击."在AAAI/ACM 关于人工智能、伦理和社会的会议记录中,180–86 页。
安德鲁·戈登·威尔逊。2020.“贝叶斯深度学习的案例。”arXiv 预印本 arXiv:2001.10995 。
原载于 2022 年 2 月 18 日 https://www.paltmeyer.comhttps://www.paltmeyer.com/blog/posts/effortsless-bayesian-dl/。