TowardsDataScience-博客中文翻译-2021-八-
TowardsDataScience 博客中文翻译 2021(八)
Spring 注释与 Kotlin DSL 的对比基准
为什么您应该考虑迁移到 Springboot 的新 Kotlin DSL

Spring 的 5.0 版本引入了 Kotlin DSL(领域特定语言)来定义路由和bean。
使用 DSL,你不需要支付不确定性来知道你是否正确地连接了东西。
您是否曾经花费数小时进行调试,只是为了发现您将 bean 放在了一个没有被扫描的包中?或者您没有通过某些注释属性?或者你的 pom 中缺少了一些spring-boot-starter-*something*?
别提了。如果 DSL 可以编译,那么可以肯定 spring 正在解析您的 beans!
使用 DSL 还有第二大优势。来自 spring 文档
他的机制非常有效,因为它不需要任何反射或 CGLIB 代理
太棒了。但是有多高效?我做了一些基准测试,我将展示结果。
评估方法
基准的源代码在这里是。
我创建了两个 maven 模块进行测试:
- nodsl :带有经典注释的 spring 应用程序。
- dsl1 :带 dsl 初始化的 spring 应用。
注释模块具有以下类层次结构:
Controller
|__ Service
|__ Repository (using JPA)
DSL 模块代之以另一个层次结构:
Router
|__ Handler
|__ Repository (without JPA)
我用 Kapt 创建了一个注释处理器来复制这些结构。我用每种结构的 1000 个复制品做了测试。每个副本都有自己的包。

自动生成的包装结构
控制器的层次结构在 Spring 上下文中注册,带有典型的注释@RestController, @Service, @Repository, @Entity, @Component。路由器的层次结构注册如下:
@HowManyRoutes(1000)
@SpringBootApplication(scanBasePackages = [])
class DslApplication
fun main(args: Array<String>) {
*runApplication*<DslApplication>(*args)**{
addInitializers(*allBeans*())**
**}** }fun allBeans(): BeanDefinitionDsl {
return beans {
beans1(this)
beans2(this)
...
beans1000(this)
}
}public fun beans1(dsl: BeanDefinitionDsl): Unit {
dsl.bean<HelloHandler1>()
dsl.bean<InMemoryCustomerRepository1>()
dsl.bean(::helloRouter1)
}public fun helloRouter1(handler: HelloHandler1) = *router* **{** "hello1".*nest* **{** GET("", handler::hello)
**}
}**
结果
您可以在存储库的根目录中找到应用程序启动的日志:
这是第一次运行的结果:

第一轮测试的结果
乍一看,它很有希望。这种差异在上下文的初始化和总的开始时间方面都很显著。
我必须指出,对于 DSL,我没有使用 JPA。在“无 DSL”的情况下,存储库扫描需要 22.185 秒。在启动(spring.jpa.hibernate.ddl-auto=none)期间,我没有在数据库中创建表
如果我删除 JPA 同时保留依赖结构,会发生什么?我用普通的@Component替换了@Repository,去掉了spring-boot-starter-data-jpa。你可以在 Github 上的资源库的分支 no-jpa中找到代码。

这些结果让我大吃一惊。如果 JPA 没有到位,使用 DSL 与否几乎没有区别。初始化上下文所需的时间略有不同,但总的启动时间几乎相同。
讨论

我通过询问 DSL 会在多大程度上改善 Springboot 应用程序的启动时间来开始这个基准测试。基准测试之后,我可以说 DSL 本身没有直接的好处。
JPA 使用 CGLIB 代码生成,这个基准测试证明它对引导性能有巨大的影响。另外,使用@Transanctional注释会导致将 bean 包装在一个 CGLIB 代理中。
如果你想使用 Springboot 并保持可接受的引导时间,那么你应该避免 Spring 用 CGLIB 代理你的 beans。这意味着避免 JPA、@Transactional注释,以及在 [@Configuration](https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#beans-factorybeans-annotations)中声明的[@Bean](https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#beans-factorybeans-annotations) 工厂方法。
即使在这个基准测试之前,我也不喜欢 JPA。现在我又多了一个避免它的理由。
我已经在我的个人积压给一个尝试科特林暴露。这是替代 JPA 和 Hibernate 的一个很好的选择。暴露,与避免“约定优于配置”的 Spring DSL 方向相同。
结论
在扫描和注入 beans 的时间方面,DSL 并没有真正的好处。无论如何,前提是它提高了性能,因为它不使用 CGLIB 和反射。
有了这个基准,我证明了 CGLIB 的影响有多大。然后,使用一种强制您避免它的编码风格,具有改善引导启动时间的间接好处。
“约定胜于配置”第一次尝试的时候,听起来很迷人,很神奇。不幸的是,当你不得不调试复杂的情况时,它会变得像魔咒一样快。
采用声明式方法可以节省你调试的时间。有编译器作为编码伴侣,让你对你写的东西更有信心。这也是一个优势,因为以错误的方式使用工具的机会要少得多。
我很确定在我的下一个项目中,我会尝试 DSL,并且避免 JPA 和 Hibernate。
让我知道你对放弃 JPA、Hibernate 和“约定胜于配置”的感觉。它让你害怕,还是让你兴奋?
感谢您的阅读。
MOOCs 的一个好处你可能没有考虑到
MOOCs 如何提升你的搜索引擎优化
我一直听说 MOOCs 不会让你成为数据科学家,但它们实际上可以帮助你获得面试机会,因为它们提高了你 LinkedIn 个人资料的 SEO(搜索引擎优化)。当招聘人员寻找候选人时,他们经常利用 LinkedIn 搜索栏搜索职位,过滤到人,可能还会根据地点进一步过滤。
当搜索一个数据科学家时,这在 LinkedIn 上返回大约 100 万个结果。即使你已经是一名数据科学家,如果你希望通过这种方法被发现,你的机会相当渺茫。然而,如果招聘人员正在寻找具有特定技能的候选人,结果会更少,你也更容易在返回的用户中提升排名。例如,当我在“德克萨斯州奥斯汀”(我的家)中搜索“数据科学家”时,我看到 5300 个结果,但当我在“德克萨斯州奥斯汀”中搜索“TensorFlow”时,我看到 1200 个结果。如果你在 TensorFlow 的个人资料中添加了多个已完成的 MOOCs,你会在搜索栏中得到提升。我建议尝试一段时间,看看在你所在的地理区域,你的某项技能会在哪里出现。

在 Unsplash 上由 Austin Distel 拍摄的照片
我强烈推荐使用 LinkedIn Learning(不仅仅是因为课程质量),还因为 LinkedIn 有动力推荐使用过他们教育平台的候选人。将 LinkedIn 学习合法化作为获取技能的方法对他们来说将是一笔巨大的财富,就像他们希望将 LinkedIn 评估合法化一样。
我已经将我的想法应用到我自己的个人资料中,并且已经完成了大约 20 门关于移动应用程序开发的课程。由于没有正式经验,我的个人资料中也没有其他关于移动应用开发的帖子,我现在每 2-3 个月就会收到一次作为移动应用开发人员的面试请求。我不能说我会通过面试(因为我对成为移动应用程序开发人员没有太多兴趣),但如果我想进入那个领域,我会从比我开始数据科学时更大的潜在工作漏斗开始。当我在大学四年级时,我完成了两次数据科学实习,并赢得了一次博士生数据科学竞赛,我从未得到过招聘人员的关注,我现在得到了 DevOps,移动应用程序开发和 Unity 游戏开发(这些都是我参加过几门课程的事情)招聘人员的关注。
当然,这不是一个实验,所以我不能把因果关系归因于我所做的 mooc,但我会建议做更多的 mooc,作为提高你在 LinkedIn 上的 SEO 的起点。在更广的程度上,做你做的所有工作,无论是你职业生涯中的正式经历,个人项目,黑客马拉松,教程等等。当招聘人员寻找候选人时,在 LinkedIn 上尽可能公开将是你的一笔资产。
所有这些都把我从 MOOCs 中获得的核心利益放在一边,我学到的新技能比我投入课程的时间节省了更多的时间!
Python 中的 defaultdict 对象
了解 Python 字典对象的更好替代方案

戴维·舒尔茨在 Unsplash 上的照片
每个 Python 开发人员都知道 Python 中的字典对象。然而,还有另一个类似字典的对象,称为 defaultdict 对象,与常规字典对象相比,它提供了额外的功能。
下面我们通过一个例子来介绍一下这个 defaultdict 对象。
计数示例
假设我们想统计一个单词中每个字母出现的次数。我们将创建一个字典,用键作为字母,用它们的值作为出现的次数。
我们将使用大多数英语词典中最长的单词(根据谷歌):
word = 'pneumonoultramicroscopicsilicovolcanoconiosis'
我们可以使用我们都熟悉的 dictionary 对象来实现这一点,如下所示:
letter_count = dict()for letter in word:
if letter not in letter_count:
letter_count[letter] = 1
else:
letter_count[letter] += 1print(letter_count)**Output:**{'p': 2,
'n': 4,
'e': 1,
'u': 2,
'm': 2,
'o': 9,
'l': 3,
't': 1,
'r': 2,
'a': 2,
'i': 6,
'c': 6,
's': 4,
'v': 1}
我们首先使用 dict()构造函数创建一个空字典对象。然后我们使用 for 循环遍历单词中的每个字母。当我们循环单词中的每个字母时,我们检查该字母是否已经是字典中的一个关键字。如果没有,我们将键设置为字母,并将其值设置为 1(因为这是该字母在我们的循环中第一次出现)。如果这个字母已经作为一个键存在,我们就在它的值上加 1。
尽管一个常规的 dictionary 对象可以工作,但是这里有两个问题。
首先,注意我们首先必须检查字典中是否已经存在这个键。否则,我们就会有一个 KeyError 。
for letter in word:
letter_count[letter] += 1# KeyError
其次,如果我们试图检查字典中不存在的字母数,我们也会得到一个 KeyError 。
letter_count['p']
# 2letter_count['z']
# KeyError
当然,我们可以用字典 get() 的方法来解决第一个问题,然而第二个问题仍然存在:
for letter in word:
letter_count_2[letter] = letter_count_2.get(letter, 0) + 1letter_count_2['p']
# 2letter_count_2['z']
# KeyError
get 方法接受两个参数:我们想要检索的键的值,以及如果键不存在,我们想要分配给该键的值。
defaultdict 对象
这是一个很好的例子,当使用一个default dict对象会很有帮助。 defaultdict 类是内置 dict 类的子类,也就是说它继承了它。因此,它具有与 dict 类相同的功能,但是,它接受一个额外的参数(作为第一个参数)。这个附加参数default _ factory将被调用来为一个尚不存在的键提供一个值。
类
collections.**defaultdict**(default _ factory = None, / ,... ])
我们来看看如何使用 defaultdict 来完成上述任务。
我们首先必须从集合模块导入 默认字典 :
*from collections import defaultdict*
然后我们使用default dict()构造函数创建一个 defaultdict 对象:
*letter_count_3 = defaultdict(int)*
注意我们是如何将 int 函数作为default _ factory参数的参数传入的。如果某个键不存在,这个 int 函数将被不带任何参数地调用,以提供该键的默认值。
*for letter in word:
letter_count_3[letter] += 1*
因此,当我们在单词上循环时,如果那个键不存在(意味着我们第一次遇到一个字母),则调用 int 函数,而不向它传递任何参数,它返回的将是那个键的值。在不传递任何参数的情况下调用 int 函数会返回整数 0。然后,我们在返回的 0 上加 1。
这解决了我们上面遇到的两个问题,因为检索任何不存在的键的值都将返回零:
*letter_count_3['p']
# 2letter_count_3['z']
# 0*
我们还可以传入一个 lambda 函数作为default _ factory参数,该参数不接受任何参数并返回值 0,如下所示:
*letter_count_3 = defaultdict(lambda: 0)*
如果你喜欢阅读这样的故事,并想支持我成为一名作家,考虑注册成为一名媒体会员。每月 5 美元,你可以无限制地阅读媒体上的故事。如果你用我的 链接 注册,我会赚一小笔佣金。
*https://lmatalka90.medium.com/membership *
我希望你喜欢这篇关于 Python 中default dict对象的文章。感谢您的阅读!
一种更好的文本摘要方法
用 Python 完整实现的分步指南

图片由 Dianne Hope 发自 Pixabay
有几种方法来执行自动文本摘要。它可以通过监督或无监督学习来完成,通过深度或只有机器学习。在这些类别中,有各种各样的方法。
就摘要的类型而言,有两种——提取的和抽象的。这里介绍的程序使用无监督学习,并生成一个提取摘要。
摘要是指摘要是原始文本的子集,因为摘要中的所有单词都包含在原始文本中。对于自动文本摘要领域的概述,我推荐这个调查。
不同的词,但是相同的意思
一般来说,在简单的解释中,摘要者的目标是找到最相关的单词,然后选择包含这些单词的句子作为摘要的一部分。
不同但意思相同的单词怎么办?
举个例子,
- 哈里搬到了美国。
- 王子移居北美。
他们没有共同语言,但这两个句子有很强的关联性。“该”和“到”不算。这些单词在预处理步骤中已经被删除了。下面将详细介绍。
问题
在一种简单的方法中,“Harry”或“US”可以被选择为相关的单词,但是句子“The prince relocated to North America”将没有机会成为摘要的一部分。
这篇研究论文中提出的技术解决了这个问题。
本帖显示的代码只是稍加修改的版本(结构、方法名等。)的出处 可用上Github。
算法步骤
我在本教程中使用的文本与由 Praveen Dubey 撰写的这篇优秀的文章中使用的文本相同。确保也阅读它(为了证明概念,我改变了其中一个句子)。
1.生成嵌入模型
摘要器依赖于单词嵌入,因此它还选择包含与最相关单词(质心)具有相同含义的单词的句子,即使这些单词是不同的。
因此,在预处理文本之前,程序必须定义嵌入模型。我使用被总结的文本作为数据来获得嵌入模型。
但它可能是维基百科的转储或其他文本。也已经有可用的模型可以使用。为了获得单词嵌入,我将文本分割成单词,并将其传递给 Word2vec。
for sent in clean_sentences:
words.append(nlkt_word_tokenize(sent))
**model =** Word2Vec(words, min_count=1, sg = 1)
2.预处理
您必须对文本进行预处理。它包括将文本拆分成句子,降低所有单词的大小写,删除停用词(是,安,等。)和标点符号等任务。
我在另一篇文章中单独介绍了这一步。目标是不要浪费资源(计算能力、时间)来处理那些对提取语义和理解文本没有太大价值的事情。
特别是识别和分割文本成句子,这是至关重要的,因为稍后算法将评分并选择它们作为摘要的一部分。
**def sent_tokenize(text):**
sents = nlkt_sent_tokenize(text)
sents_filtered = []
for s in sents:
sents_filtered.append(s)
return sents_filtered**def cleanup_sentences(text):**
stop_words = set(stopwords.words('english'))
sentences = sent_tokenize(text)
sentences_cleaned = []
for sent in sentences:
words = nlkt_word_tokenize(sent)
words = [w for w in words if w not in string.punctuation]
words = [w for w in words if not w.lower() in stop_words]
words = [w.lower() for w in words]
sentences_cleaned.append(" ".join(words))
return sentences_cleaned
3.查找最相关的单词嵌入表示
该算法使用 TF-IDF 来查找文本中最相关的单词。这些词是文章的重心。
找到单词质心后,程序对作为质心一部分的单词的向量求和,这个和就是质心的嵌入表示。
假设最相关的词是:微软,程序,AI
质心的嵌入表示(最相关项)=微软向量+程序向量+ AI 向量。
提醒一下,嵌入模型将每个单词表示为一个向量。这就是为什么每个单词都有一个向量。
**def build_embedding_representation(words, word_vectors, embedding_model):**
embedding_representation = np.zeros(embedding_model.vector_size, dtype="float32")
word_vectors_keys = set(word_vectors.keys())
count = 0
for w in words:
if w in word_vectors_keys:
embedding_representation = embedding_representation + word_vectors[w]
count += 1
if count != 0:
embedding_representation = np.divide(embedding_representation, count)
return embedding_representation**def get_tf_idf(sentences):**
vectorizer = CountVectorizer()
sent_word_matrix = vectorizer.fit_transform(sentences)transformer = TfidfTransformer(norm=None, sublinear_tf=False, smooth_idf=False)
tfidf = transformer.fit_transform(sent_word_matrix)
tfidf = tfidf.toarray()centroid_vector = tfidf.sum(0)
centroid_vector = np.divide(centroid_vector, centroid_vector.max())feature_names = vectorizer.get_feature_names()relevant_vector_indices = np.where(centroid_vector > 0.3)[0]word_list = list(np.array(feature_names)[relevant_vector_indices])
return word_list**centroid_words** = get_tf_idf(clean_sentences)**centroid_vector** = build_embedding_representation(centroid_words, word_vectors, emdedding_model)

最相关的单词(质心)。图片作者。
4.给句子打分
基于句子与质心嵌入的相似程度对句子进行评分。
为了与质心嵌入进行比较,该算法计算每个句子的嵌入表示。
句子嵌入=作为句子一部分的单词向量的总和。
举个例子,
**Sentence1** = word1, word2, word3
**Sentence1 embedding** = word1 vector + word2 vector + word3 vector
最后,在定义了句子嵌入之后,该算法使用 余弦相似度 来计算质心和句子嵌入之间的相似度。
每个句子根据它们与质心的相似程度得到一个分数。
**def build_embedding_representation(words, word_vectors, embedding_model):**
embedding_representation = np.zeros(embedding_model.vector_size, dtype="float32")
word_vectors_keys = set(word_vectors.keys())
count = 0
for w in words:
if w in word_vectors_keys:
embedding_representation = embedding_representation + word_vectors[w]
count += 1
if count != 0:
embedding_representation = np.divide(embedding_representation, count)
**return embedding_representation** sentences_scores = []
for i in range(len(clean_sentences)):
scores = []
words = clean_sentences[i].split() #Sentence embedding representation
sentence_vector = build_embedding_representation(words, word_vectors, emdedding_model) #Cosine similarity between sentence embedding and centroid embedding
score = similarity(sentence_vector, centroid_vector)
sentences_scores.append((i, raw_sentences[i], score, sentence_vector))sentence_scores_sort = sorted(sentences_scores, key=lambda el: el[2], reverse=True)sentence_scores_sort = sorted(sentences_scores, key=lambda el: el[2], reverse=True)
句子得分
红色句子的形心部分没有单词。尽管如此,它还是取得了不错的成绩。高于另一个包含质心词的词。

按分数排序的句子。图片作者。
5.选择句子并解决冗余
这些句子是根据它们的分数选择的。选择的句子数量受到摘要应包含多少单词的限制(50 个单词,100 个单词,还是?).
处理自动摘要的一个常见问题是处理冗余——摘要中包含太相似的句子。
为了克服这一点,在选择句子时,你要将它们与已经在摘要中的句子进行比较。如果选择的句子与摘要中的句子太相似,你就不会把它添加到最终文本中。
该算法在进行比较时使用余弦相似度和一些预定义的阈值。
count = 0
sentences_summary = []#Handle redundancy
for s in sentence_scores_sort:
if count > 100:
break
include_flag = True
for ps in sentences_summary:
sim = similarity(s[3], ps[3])
if sim > 0.95:
include_flag = False
if include_flag:
sentences_summary.append(s)
count += len(s[1].split())sentences_summary = sorted(sentences_summary, key=lambda el: el[0], reverse=False)**summary =** "\n".join([s[1] for s in sentences_summary])
原始文本和生成的摘要
原文

原文。图片作者。
生成汇总

生成的摘要。图片作者。
Python 中的完整代码
为了让我更好地理解并在本文中解释,代码被稍微修改/简化了。但是您可以查看研究论文作者的 Github 以获得完整和更准确的实现。
原文在这里:https://github.com/gaetangate/text-summarizer。我玩它只是为了更好地理解和稍微改变一下。
最后的想法
我发现这个项目真的很有趣。它比其他只依赖单词包(BOW)或 TF-IDF 的算法更有效。我用作主要参考的研究论文要详细得多。它针对其他算法评估生成的摘要,并涵盖多语言文档摘要。
接下来是评估。我计划再写一篇关于 ROUGE 的文章,ROUGE 是一个常用于评估摘要好坏的工具。我还将深入研究基于监督机器学习的摘要器。以及深度学习。
感谢阅读。
参考
[1] 文本摘要技术:简要综述。作者:Mehdi Allahyari,Seyedamin Pouriyeh,Mehdi Assefi,Saeid Safaei,Elizabeth D. Trippe,Juan B. Gutierrez,Krys Kochut
【2】通过单词嵌入的复合性进行基于质心的文本摘要。作者:盖塔诺·罗西耶洛,皮耶保罗·巴西勒和乔瓦尼·塞梅罗|Github page:https://github.com/gaetangate/text-summarizer
张量板实验的更好比较
TensorBoard 在对实验进行排名时只考虑最后一个历元。以下是如何更好地评估您的指标。

图片来自 Pixabay
如果你使用 TensorFlow 进行机器学习算法的开发和实验,你很可能会遇到 TensorBoard 并使用它来记录每次运行的结果,并有更好的方式来直观地比较它们。您甚至可以通过记录各种指标来比较您的结果,从而进行一些超参数调整。
然而,在我写这篇文章的那天, TensorBoard 只能根据上一个时期获得的指标来比较结果。尽管每次运行都有所有历史值,但当按特定指标对结果进行排序时,它将只考虑最后一个时期进行比较。根据不同的用例,这可能导致在不同的超参数上运行的模型的不正确的排序。
本文的目的是介绍一种比较 Tensorboard 中记录的结果的更好方法,不仅包括超参数调整,还包括正常标量运行的比较。具体来说,我们将通过 Python TensorBoard 库 访问这些指标,将它们转换成 Pandas DataFrame,并从那里根据指标的子选择对运行进行分组和比较。
上传实验到 TensorBoard。偏差
首先,我们将把我们的实验目录上传到 TensorBoard.dev 。对于那些不熟悉这个工具的人来说,它允许我们上传并与任何人分享我们的 ML 实验,最棒的是:零设置。
如果您尚未安装,可以通过以下方式安装:
pip 安装-U 型张量板
然后,您需要运行下面的命令,指定您的 logdir,并可选地指定您想要上传的实验的名称:
tensorboard dev 上传—logdir REPLACE _ LOG _ DIR
—name“(可选)我的最新实验”
该命令第一次执行将提示一些身份验证阶段。完成后,你将能够有一个看起来像"https://tensorboard.dev/experiment/EXPERIMENT_ID"的链接。
将实验加载到熊猫数据框架中
下一步是将上传的实验加载到本地 Pandas DataFrame 中,这可以通过将实验 id 传递给 TensorBoard API 调用tensor board . data . experimental . experimental from dev()来完成,然后获取实验标量。

装载冲浪板。开发一个熊猫数据框架的实验。图片作者。
为了将数据帧转换成更有用的格式(宽格式),我们可以用get _ scalar 调用 get _ scalar(pivot = True)。但是,如果我们没有对所有实验使用相同的指标,并且我们想比较它们,即使有些实验没有可用的指标,这也是行不通的。为此,我们必须通过运行以下命令来手动旋转表格:

将数据帧转换为宽格式格式。图片作者。
排名结果
一旦我们准备好数据帧并正确格式化,剩下唯一要做的事情就是对结果进行排序,不仅要考虑最后一次,还要考虑每次运行的所有时期。此外,如果使用多个指标来比较运行,并希望找到与这些指标相结合的最佳模型,我们将使用调和平均值,因为它比简单平均值更适合比率。
为此,我在下面创建了一个要点,所有这些功能都包含在一个方法中。要调用它,只需要两个参数,我们上传到 TensorBoard 的实验的 id。Dev 和用于比较结果的指标。还有一些可选参数,用于仅考虑验证运行,根据整体性能对值进行排序,或者格式化返回数据帧的指标百分比。
作为一个例子,下面是我在 DeepFashion 数据集上进行的一些实验。

深度时尚实验示例。图片作者。

执行先前代码的输出。图片作者。
最后的话
在本文中,我向您介绍了一种更准确的方法来评估您的 TensorFlow 实验,即使在查看多个指标时,也可以通过手动访问 TensorBoard 数据来实现。我希望你喜欢这篇文章,它对你有帮助!😄你也可以在以下网址查看我的最新作品:
更好的辍学生!在 PyTorch 中实现 DropBlock

作者图片
这篇文章的互动版本可以在这里找到
我的计算机视觉库中的眼镜上有 DropBlock!
介绍
今天我们要在 PyTorch 中实现 DropBlock !Ghiasi 等人介绍的 DropBlock 是一种正则化技术,专门用于图像,从经验上看比 drop 更有效。为什么辍学是不够的?
图像丢失的问题
Dropout 是一种正则化技术,在将输入传递到下一层之前,随机丢弃(设置为零)部分输入。如果你不熟悉,我推荐斯坦福的这些讲义(跳转到退学部分)。如果要在 PyTorch 中使用,可以直接从库中导入。我们来看一个例子!

作者图片
如您所见,输入的随机像素被丢弃了!
这种技术在 1D 数据上工作得很好,但是在 2D 数据上,我们可以做得更好。
主要问题是我们正在丢弃独立的像素,并且这个在移除语义信息方面是无效的,因为附近的激活包含密切相关的信息。我认为这是相当直观的,即使我们去掉一个元素,邻居仍然可以携带重要信息。
让我们探索一下特征地图会发生什么。在下面的代码中,我们首先获得一个婴儿尤达图像,然后我们使用眼镜创建一个预训练结果。然后我们馈入图像,从第二层得到特征图。最后,我们展示了有和没有Dropout的第一个通道的激活

作者图片
在左边,我们有特征映射的激活,在右边是同一特征映射在退出后的激活。它们看起来非常相似,注意在每个区域,即使一些单位为零,邻居的激活仍然在进行。这意味着,信息将传播到下一层,这是不理想的。
下落滑车
DropBlock 通过从特征地图中删除连续区域来解决这个问题,下图显示了主要思想。

图片由 Ghiasi 等人提供。
Dropblock 的工作方式如下

图片由 Ghiasi 等人提供。
履行
我们可以从用正确的参数定义一个DropBlock层开始
block_size是我们要从输入中删除的每个区域的大小,p是keep_prob,就像Dropout中的那样。
到目前为止一切顺利。现在棘手的部分,我们需要计算伽玛来控制特征的下降。如果我们想用p prob 保持每次激活,我们可以从一个具有均值1 - p的伯努利分布中采样,就像在 Dropout 中一样。问题是我们将block_size ** 2单位设置为零。
Gamma 的计算公式如下

Ghiasi 等人的图片(论文中的公式 1)
乘法运算的左侧是将被设置为零的单位数。而右侧是有效区域,即 dropblock 未触及的像素数
# Output
0.14222222222222222
下一步是对掩码\(M\)进行采样,其大小与中心为γ的伯努利分布的输入大小相同,在 PyTorch 中很简单
接下来,我们需要清空大小为block_size的区域。我们可以使用最大池,其中kernel_size等于block_size并且一个像素步距来创建。请记住,掩码是一个二进制掩码(只有 0 和 1),因此当 maxpool 在其 kernel_size 半径中看到 1 时,它将输出 1,通过使用 1 步长,我们确保如果输入中至少有一个单元设置为 1,则在输出中创建大小为block_size x block_size的区域。因为我们想把它们归零,我们需要把它反过来。在 PyTorch
饭桶
然后我们将它标准化
x = mask_block * x * (mask_block.numel() / mask_block.sum())
让我们用宝贝尤达来测试一下,为了简单起见,我们将在第一个通道中显示被丢弃的单元

作者图片
看起来不错,让我们看看一个预训练模型的特征图(像以前一样)

作者图片
我们成功地将连续区域清零,而不仅仅是单个单元。
顺便说一句,DropBlock在block_size = 1时等于Dropout,在block_size为全特征图时等于Dropout2d(又名空间差)。
结论
现在我们知道如何在 PyTorch 中实现 DropBlock,这是一种很酷的正则化技术。本文给出了不同的实证结果。他们使用一个普通的 resnet50 并迭代地添加不同的正则化,如下表所示

如你所见,与 SpatialDropout 相比, ResNet-50 + DropBlock '存档+1%(py torch 中的经典 Dropout2d ')。
在论文)中有更多关于不同 DropBlock 超参数的研究,如果你有兴趣可以看看:)
感谢您的阅读!
弗朗西斯科
用于对象检测的更好的地图
ML 提示和技巧/潮流
为什么你的物体检测模型很烂?让我们改进它!
TL;DR:你的对象检测模型很糟糕,你想通过利用更好的评估指标来改进它……跳过介绍,找出方法或者直接去 Google Colab 笔记本看代码或者这里看 ClearML 实验日志!

误报和漏报是物体检测中的主要问题(来源:图片 6 喷气飞机向云行进,作者是赛义德·阿里和像素
介绍
O 物体检测容易。你需要做的只是获得一个训练数据集,从开源库中下载一个预训练的模型,如 Tensorflow 对象检测 API 、 Detectron2 和 mmdetection ,并(重新)训练它。
实际上,在训练你的模型几个小时后,你评估它,检查度量标准…你最终对性能完全失望,因为它与你的预期相差甚远!
现在你有麻烦了…关于如何调试模型的性能,以及如何优化模型以获得更好的性能,没有好的教程、菜谱或 StackOverflow 主题。在 Forsight ,我们使用潮汐来实现精确的物体检测。这个博客是关于分享小技巧的,这些小技巧对于提高你的物体探测模型的准确性是非常宝贵的。
模型性能评估—基础
构成物体检测模型性能的关键有两个方面:速度和精度。从下表中可以看出,这两者之间总是有一个权衡。在这篇博客中,我们将专注于分析模型的准确性。

EfficientDet 模型中速度与准确性的权衡(来源:图片由作者提供)
简而言之,物体检测器以一定的置信度得分预测给定类别的物体在图像中的位置。通过在对象周围放置边界框来识别它们的位置,来定义对象的位置。
因此,检测由一组三个属性表示:
- 对象类(即人)
- 对应的包围盒(即【63,52,150,50】)
- 置信度得分(即 0.583 或 58.3%)
类似地,对象检测模型的性能评估是基于:
- 一组基本事实边界框,表示包含待检测类别的对象的图像的矩形区域,
- 由模型预测的一组检测,每个检测由一个边界框、一个类别和一个置信度值组成。
借据和信心得分
考虑下图中由真实边界框(绿色)表示的感兴趣对象(人)和由预测边界框(红色)表示的检测区域。在不考虑置信度得分的情况下,当预测框和真实框的面积和位置相同时,即完美地包围了人,则出现完美匹配。

预测与现实(来源:照片中的男子站在灰色金属设备附近,作者是 Pexels 的 Kateryna Babaieva )
这两个条件通过交集除以并集(IoU) 进行评估,这是一种基于 Jaccard 指数的测量,Jaccard 指数是两组数据的相似系数。在对象检测范围中,IoU 等于预测边界框(红色)和地面真实边界框(绿色)之间的重叠(相交)面积除以它们的并集面积。

IoU 等于重叠面积除以边界框的并集面积。(来源:图片由作者提供)
下图中可以看到一些不同 IoU 分数的例子。通过设置 IoU 阈值,一个指标可以或多或少地限制检测是正确还是不正确。更接近 1 的 IoU 阈值更具限制性,因为它需要几乎完美的检测,而更接近但不同于 0 的 IoU 阈值更灵活,即使很小的重叠也被视为有效检测。

边框的不同借据(来源:作者图片)
置信度得分反映了盒子包含感兴趣对象的可能性以及分类器对它的置信度。如果该框中不存在任何对象,则置信度得分理想情况下应该为零。一般来说,越紧密的边界框(严格的 IoU)的置信度得分往往越高。
精确度和召回率
精度(Pr) 是模型仅识别相关对象的能力,计算为正确正面预测的百分比。
Recall (Rc) 是一个模型找到所有相关案例(所有真实边界框)的能力。它是所有已知事实中正确的正面预测的百分比。

精确与召回(来源:https://en.wikipedia.org/wiki/File:Precisionrecall.svg有 CC 许可)
为了计算精度和召回值,每个检测到的边界框必须首先被分类为:
- 真阳性(TP) —地面真实边界框的正确检测;
- 假阳性(FP) —不存在物体的错误检测或存在物体的误检测;
- 假阴性(FN) —未被检测到的真实边界框;
- 真否定(TN) — 不适用于对象检测,因为有无限多的实例不应被检测为对象。
概括地说,对象检测器的输出由一个边界框、一个类和一个置信度得分来表征。通过仅将置信度大于置信度阈值τ的那些检测视为阳性检测,可以在精确度和召回率计算中考虑置信度得分。置信水平小于τ的检测被认为是否定的。
TP(τ)和 FP(τ)都是τ的减函数,因为较大的τ会减少阳性检测的数量。相反,FN(τ)是τ的增函数,因为较少的阳性检测意味着较多的阴性检测。


平均精度
精确度-召回率(PR)曲线是精确度作为召回率的函数的图。它显示了模型检测的不同置信度值的两个度量之间的权衡。 AP@α是精确召回曲线下的面积(AUC-PR)。数学上,AP 定义为:

符号: AP@α表示 IoU 阈值为α时的平均精度(AP)。因此,AP@0.50 和 AP@0.75 分别表示 IoU 阈值为 50%和 75%时的 AP。
高 AUC-PR 意味着高精度和高召回率。自然地,PR 曲线具有之字形行为(不是单调递减的)。在计算 AP 之前,我们使用以下插值公式使 PR 曲线单调递减:

在我们为每个单独的类计算 AP 之后,我们计算平均 AP 作为所有类的所有 AP 的平均值:

关于精确召回曲线计算的例子,请查看 Harshit Kumar 的博客文章。
在下图中,我们可以看到三条不同的 PR 曲线,它们的不同之处在于精度/召回计算中使用的 IoU 阈值。由于 AP 对应于曲线下的面积,显然宽松的 IoU 阈值会导致比严格的 IoU 阈值更高的 AP 分数。

(图片来源:Harshit Kumarhttps://kharshit . github . io/blog/2019/09/20/evaluation-metrics-for-object-detection-and-segmentation)
更多关于可可地图的具体细节,请访问:https://cocodataset.org/#detection-eval!
潮汐——更好的地图
如前几节所示,对象检测和实例分割任务通常只使用一个指标来衡量模型性能:平均精度(mAP)。虽然 mAP 用一个数字简洁地总结了模型的性能,但是从 mAP 中理清对象检测和实例分割中的错误是困难的:假阳性可能是重复检测、错误分类、错误定位、与背景混淆,或者甚至是错误分类和错误定位。
TIDE(用于识别检测和分割错误的工具包)被引入来解决上述挑战。它将目标检测误差分为六种类型,并引入一种技术来测量每种误差的贡献,以隔离其对整体性能的影响。潮基本上是田蜜地图上的类固醇!
如果您的对象检测模型在自定义数据集上训练后有一个低 mAP,您肯定会想知道我们可以在哪里进一步改进它。这是潮汐闪耀光芒的地方。为了让你的模型运行得更好,TIDE 将帮助你精确定位你应该重点改进的错误类型。

(作者提供 gif 图片——丹尼尔·博尔亚等人提供图片来源——https://www.youtube.com/watch?v=McYFYU3PXcU)
潮汐误差类型
在本文中,有对错误类型以及如何定义它们的详细解释。
为了创建一个有意义的误差分布来捕捉 mAP 的组成部分,我们将模型中的所有假阳性和假阴性分为 6 种类型,如下图所示。注意,对于某些错误类型(分类和定位),假阳性可能与假阴性成对出现。我们将使用 IoUᵐᵃˣ来表示假阳性的最大 IoU 重叠与给定类别的真实情况。

(来源:图片来自[1])
前台 IoU 阈值表示为 tᶠ,后台阈值表示为 tᵇ,除非另有说明,否则均设为 0.5 和 0.1。
1。分类错误 : IoUᵐᵃˣ ≥ tᶠ的燃气轮机分类不正确(即正确定位但分类不正确)。
2。本地化错误 : tᵇ ≤ IoUᵐᵃˣ ≤ tᶠ为正确级别的 GT(即分类正确但本地化不正确)。
3。Cls 和 Loc 错误 : tᵇ ≤ IoUᵐᵃˣ ≤ tᶠ的 GT 分类不正确(即分类不正确和本地化不正确)。
4。重复检测错误:正确类别的燃气轮机的 IoUᵐᵃˣ ≥ tᶠ,但另一个更高得分的检测已经匹配该燃气轮机(即,如果不是更高得分的检测,将是正确的)。
5。背景错误:所有 GT 的 IoUᵐᵃˣ ≤ tᵇ(即检测到的背景为前景)。
6。遗漏的 GT 错误:所有未检测到的地面真相(假阴性)尚未被分类或定位错误覆盖。
TIDE 旨在替代 COCO 评估工具包,入门非常简单:
from tidecv import TIDE, datasets
tide = TIDE()
tide.evaluate(datasets.COCO(), datasets.COCOResult('path/to/your/results/file'), mode=TIDE.BOX)
tide.summarize() # Summarize the results as tables in the console
tide.plot() # Show a summary figure
潮汐实例
我们已经准备了一个 Google Colab 笔记本,里面有一个代码样本,展示了如何使用带有 Tensorflow 对象检测 API 的 TIDE。
如果您不想运行笔记本并等待评估完成,您可以在我们用于实验管理的 Allegro ClearML 仪表盘 上查看结果。通过使用 ClearML ,我们可以以有组织的方式持久存储所有实验数据,这使我们能够轻松地执行比较并上传自定义工件,如 COCO 格式的地面实况和检测元数据,以及自定义图到实验。

潮汐评估— ClearML 实验日志(来源:作者提供的 gif)


EfficientDet-D0 与 EfficientDet-D7 的 TIDE 和 COCO 评估结果(来源:作者提供的图片)
通过比较 EfficientDet-D0 和 EfficientDet-D7 型号的 COCO 评估结果,我们看到 EfficientDet-D7 的 mAP 比 T22 高出约 15%。这是因为该型号的容量更大——与 EfficentDet-D0 相比,efficent det-D7 中的参数多了到 13 倍。
注意:TIDE 和 pycocotools 中的地图计算有一些已知的差异。这就是可可地图和潮汐地图在某些情况下不相等的原因。
当比较 TIDE 评估结果时,我们可以看到两个模型的分类和重复检测误差类似于,而 D7 模型的定位误差低 2%。
与 D0 相比,D7 车型检测背景作为前景的误差高出 2%,而 D7 车型遗漏的 GT 误差则小 3.5%。这可以通过以下事实来解释,即 D7 模型具有较高的召回率,这降低了遗漏的 GT 误差,但同时增加了背景分类误差。
比较的有趣部分是假阳性和假阴性的映射。我们可以看到两个模型的误报率非常相似。这可能是数据集中有一些未标记数据的标志,并且模型正在以高置信度检测它。另一方面,D7 型号的假阴性率明显较低。很明显,较大的模型可以检测到较小容量模型难以处理的对象实例。
在这次比较中,我们比较了相同的模型架构(EfficientDet)和不同数量的参数(D0 和 D7)。当我们想要比较不同的模型架构的时候,TIDE 模型评估就更加有用了,大家可以随意自己尝试一下。TIDE 论文[1]包含对不同对象检测和分割模型的附加分析。
前面分析的潮汐图如下所示。


EfficientDet-D0 与 EfficientDet-D7 的潮汐图(来源:作者提供的图片)
结论
最近,人工智能先驱吴恩达为以数据为中心的人工智能发起了一场活动,他的主要目标是将人工智能从业者的关注点从模型/算法开发转移到他们用来训练模型的数据质量上。虽然高质量的数据确实是实现模型高性能的关键,但是模型也起着关键作用:模型和数据不能完全解耦。在开发过程中,根据深入分析确定的瓶颈,只能将重点从一个切换到另一个。
在 Forsight 上,我们发现理解物体检测模型的性能必须超越普通的每秒帧数(FPS)和基于地图的精度指标。非常重要的是,不仅要天真地对模型进行基准测试,而且要明白你应该做些什么来进一步提高它们的性能。
使用像 TIDE 提供的更有洞察力的度量标准可以更容易地识别数据集中的具体问题,如未标记的图像、松散的边界框等。它还可以帮助您识别模型容量对于手头的特定任务来说不够大的情况。逐一解决这些问题最终会让您获得更好的机器算法,并帮助您为现实世界的应用程序创建生产就绪的模型!
我们希望这篇博文对你有用,请看看我们团队在 Forsight 上写的其他一些博客,如果你有任何问题,请随时通过 info@forsight.ai 联系我们!
https://medium.com/swlh/construction-feat-tf2-object-detection-api-4465a3937c87
参考
- 丹尼尔·博尔亚、西恩·佛利、詹姆斯·海斯和朱迪·霍夫曼; TIDE:识别物体检测错误的通用工具箱, ECCV 2020。
- 帕迪拉、拉斐尔、帕索斯、韦斯利、迪亚斯、塔迪乌、内托、塞尔吉奥和达席尔瓦、爱德华多。与配套开源工具包、电子 2021 的对象检测指标对比分析。,https://doi.org/10.3390/electronics10030279
- 图沙尔·科尔赫;如何通过理解数据提升物体检测精度,https://blog . gofynd . com/boost-object-detection-model-accuracy-552586 d698 c
- 亚当·凯利;从头开始创建 COCO 注解,https://www . immersivelmit . com/tutorials/Create-COCO-Annotations-从头开始
- 基普洛诺·伊利亚·克希;对象检测度量与工作示例https://towards data science . com/on-Object-Detection-Metrics-With-Worked-Example-216 f 173 ed 31 e
- 哈什特·库马尔;目标检测和分割的评估度量-图,https://kharshit . github . io/blog/2019/09/20/Evaluation-metrics-for-object-detection-and-segmentation
一种更好的数据预处理方式:熊猫管道
高效、有条理、优雅。

现实生活中的数据通常是混乱的。它需要大量的预处理才能使用。Pandas 是使用最广泛的数据分析和操作库之一,它提供了几个函数来预处理原始数据。
在本文中,我们将关注一个特殊的函数,它将多个预处理操作组织成一个单独的操作:管道函数。
当谈到软件工具和软件包时,我通过例子学习得最好。我在创作内容时会牢记这一点。在这篇文章中,我也将这样做。
让我们从用模拟数据创建一个数据框架开始。
import numpy as np
import pandas as pddf = pd.DataFrame({
"id": [100, 100, 101, 102, 103, 104, 105, 106],
"A": [1, 2, 3, 4, 5, 2, np.nan, 5],
"B": [45, 56, 48, 47, 62, 112, 54, 49],
"C": [1.2, 1.4, 1.1, 1.8, np.nan, 1.4, 1.6, 1.5]
})df

(图片由作者提供)
我们的数据框包含一些由标准缺失值表示法(即 NaN)表示的缺失值。id 列包含重复值。最后但同样重要的是,B 列中的 112 似乎是一个异常值。
这些是现实生活数据中的一些典型问题。我们将创建一个管道来处理我们刚刚描述的问题。
对于每个任务,我们都需要一个函数。因此,第一步是创建将放置在管道中的函数。
需要注意的是,管道中使用的函数需要将数据帧作为参数并返回数据帧。
第一个函数处理丢失的值。
def fill_missing_values(df):
for col in df.select_dtypes(include= ["int","float"]).columns:
val = df[col].mean()
df[col].fillna(val, inplace=True)
return df
我更喜欢用列的平均值替换数字列中缺失的值。可以随意定制这个功能。只要它以一个数据帧作为参数并返回一个数据帧,它就会在管道中工作。
第二个函数将帮助我们删除重复的值。
def drop_duplicates(df, column_name):
df = df.drop_duplicates(subset=column_name)
return df
我从熊猫内置的 drop duplicates 功能中得到了一些帮助。它消除给定的一列或多列中的重复值。除了数据框之外,该函数还将列名作为参数。我们也可以将额外的参数传递给管道。
管道中的最后一个函数将用于消除异常值。
def remove_outliers(df, column_list):
for col in column_list:
avg = df[col].mean()
std = df[col].std()
low = avg - 2 * std
high = avg + 2 * std
df = df[df[col].between(low, high, inclusive=True)]
return df
该函数的作用如下:
- 它需要一个数据框和一列列表
- 对于列表中的每一列,它计算平均值和标准偏差
- 它使用平均值和标准偏差计算下限和上限
- 它删除由下限和上限定义的范围之外的值
就像前面的函数一样,您可以选择自己的方式来检测异常值。
我们现在有 3 个函数来处理数据预处理任务。下一步是用这些函数创建一个管道。
df_processed = (df.
pipe(fill_missing_values).
pipe(drop_duplicates, "id").
pipe(remove_outliers, ["A","B"]))
这个管道按照给定的顺序执行函数。我们可以将参数和函数名一起传递给管道。
这里要提到一点,管道中的一些函数修改了原始数据帧。因此,如上所述使用管道也将更新 df。
克服这个问题的一个选择是在管道中使用原始数据帧的副本。如果您不关心保持原始数据框不变,您可以在管道中使用它。
我将更新管道如下:
my_df = df.copy()df_processed = (my_df.
pipe(fill_missing_values).
pipe(drop_duplicates, "id").
pipe(remove_outliers, ["A","B"]))
让我们来看看原始数据帧和经过处理的数据帧:

df(作者图片)

df_processed(图片由作者提供)
结论
当然,您可以通过分别应用这些函数来完成相同的任务。但是,管道功能提供了一种结构化和有组织的方式,可以将多个功能合并到一个操作中。
根据原始数据和任务,预处理可能包括更多步骤。您可以根据需要在管道函数中添加任意数量的步骤。随着步骤数量的增加,与单独执行函数相比,管道函数的语法变得更加清晰。
感谢您的阅读。如果您有任何反馈,请告诉我。
创建 TfLite 模型的更好方法?
使用 TensorFlow 的模型优化工具包的量化感知训练来创建四倍小的模型,而不会影响结果。

久尼尔·费雷拉在 Unsplash 拍摄的照片
我最近有一篇关于不同 TensorFlow 库的文章,其中一个是 TensorFlow 的模型优化工具包。
模型优化工具包提供了修剪、量化和权重聚类技术,以减少模型的大小和延迟。量化可以在训练期间和之后执行,并将模型转换为使用 8 位整数而不是 32 位浮点整数。然而,量化是一个有损耗的过程。TfLite 模型也是量化的,因此它们不如原始模型精确。为了解决这个问题,可以使用量化感知训练。它在训练时将权重转换为 int-8,然后再转换回 32 位浮点,因此它就像模型的噪声一样,迫使它们相应地学习。
所以在本文的其余部分,这就是我们将要做的。我们将在量化和不量化后生成 TfLite 模型,然后根据它们的大小和精度进行比较。
目录
- 要求
- 创建量化感知模型
- 将它们转换成 TfLite
- 结果
要求
TensorFlow 模型优化工具包需要与正常的 TensorFlow 分布一起安装。可以使用以下语句进行 pip 安装:
pip install tensorflow
pip install -q tensorflow-model-optimization
创建量化感知模型
为了使用量化感知训练,模型需要被包装在tfmot.quantization类中。可以包裹整个模型,也可以根据需要包裹某些层。建议首先训练模型,然后使用包装模型进行微调;否则,模型的性能不会很好。我将在本文中讨论必要的最低要求部分,但是这篇文章可以作为详细通读的参考。
使用 Keras TensorFlow 和任何顺序或模型方法创建简单模型。下面,我给出了一个使用模型方法为 MNIST 数据集创建的简单模型的示例,并对其进行了 20 个时期的训练。
inp = tf.keras.layers.Input(shape=(28, 28, 1))
x = tf.keras.layers.Conv2D(64, kernel_size = (3, 3), padding = 'same', activation='relu')(inp)
x = tf.keras.layers.Conv2D(32, kernel_size = (3, 3), padding = 'same', activation='relu')(x)
x = tf.keras.layers.Dropout(0.5)(x)
x = tf.keras.layers.Conv2D(16, kernel_size = (3, 3), padding = 'same', activation='relu')(x)
x = tf.keras.layers.Dropout(0.25)(x)
x = tf.keras.layers.Flatten()(x)
x = tf.keras.layers.Dense(10)(x)model = tf.keras.models.Model(inputs=inp, outputs=x)model.compile(optimizer='adam', loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True, metrics=['accuracy'])model.fit(train_images, train_labels, epochs=20, validation_split=0.1, batch_size=500)
要将此模型转换为使用量化感知训练:
import tensorflow_model_optimization as tfmot
quantize_model = tfmot.quantization.keras.quantize_model
q_aware_model = quantize_model(model)q_aware_model.compile(optimizer='adam', loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True, metrics=['accuracy'])q_aware_model.fit(train_images_subset, train_labels_subset, batch_size=500, epochs=20, validation_split=0.1)
以下是他们的历史:

此外,在测试集上比较它们的准确性,这是结果看起来像什么。
Without Quantization: 98.930%
With Quantization: 99.000%
量化后我们得到了性能的提高!尽管情况并不总是如此,但需要注意的重要一点是,我们并没有受到性能下降的影响。然而,这种技术有一个问题。许多层不被支持,包括一些甚至一些基本的,如批量规范化,添加,连接,全局平均池等。不是转换整个模型,只有一些层可以量化,这也意味着您可以跳过量化那些降低精度最多的层。
在 TensorFlow 的文章中还建议,最好尝试量化后面的层而不是第一层,并避免量化像注意力机制这样的关键层。让我们看看,如果我们只想做密集层,我们会如何处理代码。(下面的代码部分直接从这篇 TensorFlow 文章中复制而来)
import tensorflow_model_optimization as tfmot
quantize_annotate_layer = tfmot.quantization.keras.quantize_annotate_layer
model = tf.keras.Sequential([
...
# Only annotated layers will be quantized.
quantize_annotate_layer(Conv2D()),
quantize_annotate_layer(ReLU()),
Dense(),
...
])
# Quantize the model.
quantized_model = tfmot.quantization.keras.quantize_apply(model)
对于本文,将使用完全量化的模型。
转换为 TFLite
要将模型转换为 TFLite,需要创建一个 TFLite 转换器来传递模型。需要为量化模型设置优化,以告知 TFLite 转换器使用 int8 而不是浮点值。
converter = tf.lite.TFLiteConverter.from_keras_model(q_aware_model)
converter.optimizations = [tf.lite.Optimize.DEFAULT]
quantized_tflite_model = converter.convert()converter = tf.lite.TFLiteConverter.from_keras_model(model)
baseline_tflite_model = converter.convert()
通过保存这些模型,比较它们的大小,量化的模型比它的浮动对应物小四倍。如果优化也应用于浮点模型,其大小与量化感知训练模型相当。尽管如此,它还是会在浮存模型的结果上遭受进一步的损失。
Quant TFLite test_accuracy: 0.9901
Baseline TFLite test_accuracy: 0.9886
Quant TF test accuracy: 0.9900000095367432
结论
TfLite 模型对于边缘应用非常有用,如果您打算从头开始训练一个模型,并希望将其转换为 TfLite 模型,那么量化感知训练是一个不错的选择。它不仅解决了精度比基本模型低得多的问题,而且模型也更小。
作为一名数据科学家,更好的编码方式
即使你只是在绘制一个图表,干净的代码总是受欢迎的,所以在编码时尝试这些实践

我现在是一名数据科学家,但我最初是一名软件开发人员,所以在我心里,我仍然是那个害怕糟糕的缩进和 x,y,z,a,b,c 变量名的人。因此,让我来分享一些技巧,让你写一个更干净的代码,并使用工具为你清理。
编写可重用的代码
可重用性是穿着闪亮盔甲的骑士。有几种方法可以提高代码的可重用性,从而使所有人的生活更加轻松。
在我看来,对我们这些数据科学家来说,最好的方法是模块化我们的代码,也就是说,将代码分解成最小的逻辑部分,并对它们进行松散耦合,也就是说,使它们相互独立。一种方法是使用函数,在某些语言中是使用类。模块化您的代码也增加了它的可伸缩性,因为您可以在不干扰基础设施的情况下向现有的代码添加更多的功能。所以让这些模块相互独立是一个很好的做法。
例如,假设在您的代码中您需要多次寻找不同数字的阶乘,直观的做法是定义一个计算阶乘的函数,而不是多次复制相同的代码。所以更好的选择是在函数中定义递归逻辑,这样函数(模块)就可以多次使用。
让代码易于理解。
这里的第一步是编写信息丰富的变量名。我不希望你用变量名写故事,但是如果一个变量包含了纪元号,那就不要命名为 x 了,用 epoch_number 或者类似的吧。
这里的第二部分是在你的代码中写简短而甜蜜的注释。同样,不期望你长篇大论,但是让我们在所有函数和类之前写一个简短的描述。如果出现混淆,您可以使用以下格式:
def func(number):
"""
func finds factorial of number
param number: integer whose factorial will be returned
"""
除了函数之外,还有一些地方需要强制注释,例如,当您在代码中使用幻数时。您需要指定该特定数字代表什么。此外,在实现一些复杂的逻辑时,我让您来判断是否要写注释。让我们为其他开发人员提供更方便的服务。
谷歌风格指南
嗯,我是否支持时尚指南还没有定论,但在我下定决心之前,我会推广它。谷歌风格指南是开发者在编码时应该遵循的一套规则。这些规则主要与代码样式、格式和林挺相关。几乎所有的语言都有一套规则。让我们看看 Python 风格指南和一些帮助我们自动化的工具。Python 风格指南分为两部分;第一个包括林挺规则,第二个包括样式规则。
下面是一些让您了解风格指南的规则:
- 只对包和模块使用
import语句,不要对单独的类或函数使用。 - 使用模块的完整路径名位置导入每个模块。
- 避免全局变量。
- 不要用分号结束行,也不要用分号将两个语句放在同一行。
不要把它看作是一本规则手册,你应该把它看作是帮助我们所有人坚持一种代码风格的惯例。我知道对于大型代码库来说,这些会变得令人不知所措,所以这里有一些工具会有所帮助:
- pylint:这是一个在 Python 源代码中查找 bug 和风格问题的工具。它发现了编译器在 C 和 C++等动态性较低的语言中通常会发现的问题。它可能会显示一些误报。
- flake8 :类似于 pylint,它会对代码中的违规行为发出警告。它速度快,假阳性率低。您可以使用这些工具给出的建议来修改您的代码。
- autopep8:如果你像我一样,直接想要一个林挺工具来修复你的代码,而不仅仅是抛出警告,那么使用 autopep8。使用此处给出的文件进行 autopep8。
使用智能编辑器
我知道上面的部分在所有需要做的方面是压倒性的,但是不要担心。我们开发者懒惰是既定的事实,我们花了大量的时间构建工具来安慰我们的懒惰。所以我们来谈谈这样的编辑器,它们为我们做所有的工作。
我在工作笔记本上使用 IntelliJ PyCharm for python 脚本。否则,我使用 sublime 和大量开源的 sublime 插件。您可以直接将 autopep8 之类的工具与 PyCharm 集成,自动完成代码机制也非常方便。同时,sublime 有自己的 linter,你可以试试。你可以在那里选择底层的 linter,所以对于 python,我使用 flake8。
这些编辑器还允许您直接链接到 git repo,因此这是一个额外的好处,在解决合并冲突或重定基础时更容易使用。
另一个有用的新开发是 Github CoPilot。如果你还没有尝试过,那么我恳求你这样做。它严重地改变了我的生活。你需要知道的关于副驾驶的一切都在这里。
结论
我希望你喜欢这些建议,并尝试在未来的代码中使用它们。让我知道你对写出更好的代码有什么建议。
在媒体上关注我们,了解更多此类内容。
成为 媒介会员 解锁阅读媒介上的许多其他故事。
处理数据集中缺失值的更好方法:使用迭代估算器(第一部分)
动手教程 / 数据清理
如何使用多元估算器处理缺失数据

照片由 R.D. Smith 在 Unsplash 上拍摄
目录
介绍
在制定有利可图的商业决策、解决健康问题、更好地了解人群等方面,缺失数据是一个主要问题。丢失数据不仅对人类来说是个问题,对我们接收数据集的机器来说也是个大问题。
这里有一个假设的情况:想象你的老板递给你一本书,每隔几页就少了几页。现在想象一下,你必须阅读这本书,对每一章进行分析,并对整本书进行最终分析,否则你会被解雇。现在,这可能对你们中的一些人来说并不奇怪,因为你们中的一些人可能有老板实际上让你做类似的事情,但是你会如何处理这种情况呢?没有问任何问题,你接受了任务并继续。我知道,现在你在想,“我会在网上搜索这本书,”你做到了。你到处寻找,却发现这本书除了在你身旁的书桌上,其他地方都不存在。
你只剩下一个选择,所以,你开始阅读。当你在书的中间看到一页破损的时候,你看到了第一页丢失的内容。这本书从第 14 页跳到第 17 页。你耸耸肩,继续看这本书,同时对第 15-16 页可能发生的事情做出假设。你继续读这本书,你会遇到更多的缺页,除了现在,这本书从 38 页跳到 45 页。当你开始阅读第 45 页时,你简直不敢相信自己的眼睛。这个故事不再有意义了…
你最终到达了这本书的结尾,你发现结尾并不是实际上是结尾。没错,最后还少了几页。“读完”这本书后,你回头看看你对每一章所做的分析,试图为这本书做出你自己的结论。
你的数据集就像你给你的机器学习模型读的一个故事,每一行就像一个章节。如果数据集有缺失值,您的模型将无法完全理解正在发生的情况,并可能做出错误的预测。
有遗漏的推理
有几种处理缺失数据的方法,包括但不限于:忽略缺失数据,根据行/列中缺失的质量移除行/列,用单个数字(0、1、任何其他数字、平均值、中值)替换,分组替换等。
更好地理解数据缺失的原因有助于您确定可以使用哪种插补方法。一般来说,有三种类型的缺失数据 <#a775> :
- 完全随机缺失(MCAR): 数据集中的值完全随机缺失。这是因为没有明确的理由来解释为什么数据集中的某个值会丢失。例如,如果被调查者忘记回答自愿调查中的问题。处理这种情况相对简单,我们可以用均值/中值替换来替换丢失的数据——丢失的数据是可恢复的。
- 随机缺失(MAR): 数据集中的值随机缺失。此时,我们可以确定数据值丢失的原因。这种情况的一个例子是,对于相同性别的多个被调查者,调查中的某个问题是空白的。我们可以处理这种情况的一种方法是通过使用其他特征进行分组均值/中值替换的——丢失的数据仍然是可恢复的。
- 非随机缺失(MNAR): 数据集中的值不是随机缺失的。这时我们可以清楚地看到缺失值的模式。一个例子是,如果调查中的某个问题类别由于问题本身而被调查者留空,因为它可能是被调查者的敏感问题(缺失取决于缺失的数据)——缺失的数据将很难恢复,除非进行进一步的研究。与其他两类缺失数据不同,MNAR 是 不可忽略的 。
它是如何工作的?
在我开始有趣的事情之前,我必须做更多的解释,所以喝杯咖啡或做一勺运动前饮料(或两杯😏)或者任何你感兴趣的事情,因为是时候集中注意力了。
从Giphy.com获得的史蒂芬·科拜尔晚间秀的 GIF,由 DJ 哈立德强有力地说服你保持专注。
让我们从基础开始。
缺失的数据会导致研究或实验出现偏差,会使数据分析更加可怕,会降低假设检验和结果的质量。做更多的研究来减少你的数据偏差总比只是说“随便”然后继续你的分析要好。在我看来,这违背了你最初开始实验的目的。最终,不仅你的结果质量不好,你甚至可能会误导他人。这就是为什么我们今天看到很多“神话”的原因。创建一个“点击诱饵”标题是一回事,但创建一个点击诱饵标题和散布神话是完全不同的水平。
现在让我们说你不能再做任何研究了,因为这超出了你的能力范围。对于数据集中丢失的数据,您能做些什么?您可以删除一些可能导致偏见的数据,但是如果您没有太多的数据可以处理,因此您没有能力删除数据,该怎么办呢?这时,您可以找到其他方法并研究输入值来替换丢失的数据。
什么是插补?根据维基百科的定义,
在统计学中,插补是用替代值替换缺失数据的过程。
如前所述,我们可以使用几种不同的方法替换(估算)缺失值。这些方法归结为两种不同的插补算法:单变量插补和多变量插补 <#a775> 。
单变量插补
单一插补方法被称为单变量插补。如 scikit-learn 的文档所述:
一种类型的插补算法是单变量算法,其仅使用第 I 个特征维度中的非缺失值来插补该特征维度中的值。
这意味着单变量插补方法仅使用所选特征(由您选择)中的非缺失值,并使用您提供的常数或所选特征的统计测量值(平均值/中值/众数)来插补同一特征中的缺失值。
例如,在 Python 中,一个简单的一行程序就能解决问题:
>>> data['chosenFeature'].fillna(data['chosenFeature'].mean(), inplace = True)"""
Where `data` is your dataset and `chosenFeature` is the column that you choose to replace the missing NaN values for by using the current mean of the non-missing values in the column.
"""
还可以使用 scikit-learn 的 SimpleImputer 类来估算缺失值。要了解有关该课程的更多信息,请点击此处。
多元插补
在数据为 MCAR、马尔和的情况下,即使数据为 MNAR ,也可以使用多重插补。多重插补方法被称为多元插补。
多元插补算法使用整套可用的特征维度来估计缺失值。
基本上,多变量插补的目的是使用数据集中的其他特征(列)来预测当前特征中缺失的值。
您可能会想“Gifari,您告诉我多重插补算法使用其他列,但是如果我们有多个缺失值的特征(列),算法如何知道首先选择哪个特征来插补值?”别急,比利(无意冒犯所有在场的比利),这是一个非常好的问题,我会在下一章讨论这个问题,我会谈到你如何成为被选中的那个人。
正如你可能已经猜到的,单一插补方法没有考虑不确定性。单一的插补方法不关心“如果怎样”;它只有一个目标:消除缺失的价值观。当我们使用多重插补方法时,我们不仅要剔除缺失值,还要考虑“如果”。
虽然多重插补的方法听起来很难实现,但这是 21ˢᵗ世纪,我们的生活是有史以来技术最先进的时代。如果你是从 22ⁿᵈ读到这篇文章的,希望 Medium 那时已经开始支持字母上标和下标,感谢你花时间阅读这篇文章,但是我不认为你应该在这里。我很确定在你那个世纪有更先进的估算方法。
有几个统计软件包可以让你轻松地进行多重插补。例如,如果你用 R 语言编程,那么你有鼠标(链式方程多元插补)或 missForest 软件包可供你使用。如果你用 Python 编程,你有 scikit-learn 的iterative imputr包,它最初是受 r 中的 MICE 包的启发。
迭代估算器
迭代插补是一种多变量插补策略,它以循环方式将缺失值(目标变量)的列作为其他特征(预测变量)的函数进行建模,并使用该估计值进行插补。源代码可以通过点击这里在 GitHub 上找到。
注意 : 这个估计器现在仍然是实验性的:预测和 API 可能会在没有任何折旧周期的情况下改变。要使用它,您需要显式导入
enable_iterative_imputer:
**>>>** *# explicitly require this experimental feature*
**>>> from** **sklearn.experimental** **import** enable_iterative_imputer *# noqa*
**>>>** *# now you can import normally from sklearn.impute*
**>>> from** **sklearn.impute** **import** IterativeImputer
你可以在迭代输入器的文档中看到许多参数。它不需要参数输入,因为默认参数已经为您设置好了,这意味着,在您完成导入必要的包之后,您可以通过运行以下命令简单地引入一个迭代估算器:
imp = IterativeImputer()
如何成为“天选之人”
“当前特征”是根据给定的imputation_order选择的,默认顺序是“升序”这意味着首先选择的特征将是包含最少缺失值的特征。在输入所选特征中的所有缺失值后,该特征不再包含缺失值,因此它不再是具有最少缺失值的特征,因此估算器将移动到下一个具有最少缺失值的特征。**
其他订单包括:
- “降序”:缺失值最多到最少的特征
- “罗马”:左特征到右特征
- “阿拉伯语”:右特征到左特征
- “随机”:每轮随机下单
迭代背后的过程
既然你知道了如何成为“被选中的人”,那么让我们来理解迭代背后的过程。
迭代估算器最初用传递给initial_strategy的值初始化缺失值,其中初始策略是每个特征的“平均值”。然后,估算者在循环插补的每一步使用一个估算器(其中使用的默认估算器是贝叶斯岭)。在每一步,选择一个特征列作为目标变量y,其他特征列作为预测变量X。然后为每个要素预测缺失值。这个过程最多重复“max_iter次,默认为 10 次(轮)。我说“最多”而不是“正好”的原因是因为sample_posterior=False的默认参数化启用了提前停止。
停止准则满足一次
max(abs(X_t-X_{t-1}))/max(abs(X[known_vals])) < tol,其中X_t为迭代t时的X。
这实质上意味着对于单个缺失值,最多有max_iter个预测,并且一旦某个值的前一次迭代预测和当前迭代预测之间的差值小于给定的tol值(默认值= 1e-3),迭代就会停止。
您可以使用其他几个参数,所以一定要在您自己的数据集上尝试它们!现在这些无聊的东西已经够多了,让我们来看看一些行动。
入门指南
现在您对 IterativeImputer 有了一些了解,让我向您介绍两个不同的数据集,我将用它们作为例子:
- 皮马印第安人糖尿病数据库:第一个例子,我将使用糖尿病数据集,点击此处可在 Kaggle 上找到该数据集。你也可以在 Kaggle 上查看我的笔记本,它可以和这个例子一起使用。您可以通过点击此处查看/编辑笔记本。
笔记本更关注不同的估算方法,以及我所说的估算缺失值的“混合方法”。最后,比较了不同插补方法得到的不同数据集上多模型训练的准确率。这是一个冗长的笔记本,但我对这个项目非常感兴趣。它是作为机器学习和迭代输入教程而创建的。
- 股票市场数据:对于第二个例子,我将对股票市场数据使用迭代估算。不幸的是,由于信息量太大,这个例子必须有自己的文章。我还不想破坏这个,因为我发现这个非常有趣。写完这篇文章后,我还在 Kaggle 上为这个例子做了一个笔记本,它使用函数来简化这个过程,我将在第二篇文章中介绍这个过程。你可以通过点击这里来玩笔记本。
点击这里阅读第二篇文章。我真的很希望你看完这篇文章后能看看!
单一插补方法的问题
当我说我不再做那些无聊的事情时,我撒谎了。我有时候控制不住。在开始我的第一个例子之前,先玩一会儿怎么样?
当您将单个数字归入某个要素中的缺失值时,根据您的数据在特定要素中缺失的数量,您会增加导致您的数据具有更多峰度的可能性。峰度是对分布“尾部”的度量。像偏度一样,峰度是另一种用于描述数据分布形状的度量。为了简单起见,当一个人想到分布有多“对称”时,他们通常会想到数据的偏斜度。当人们想到分布有多“弯曲”或“尖锐”时,他们通常会想到数据的峰度。这是一个我编码的分布的偏斜度/峰度差异的可视化例子(奇怪的伸缩,但还可以):


(左)“偏度例子”(图片作者)| (右)“峰度例子”(图片作者)
任何一元正态分布的峰度都是三(不使用费雪定义) <#a775> 时)。
要了解更多关于偏度和峰度的知识,点击这里。
现在你已经准备好去参加派对了。
单一插补与迭代插补如何影响数据集的分布
对于我的第一个例子,我将使用皮马印第安人糖尿病数据库,它可以在 Kaggle 上免费找到。该数据集在整个数据集中包含几个缺失值(被零屏蔽)。我更新的数据集用 nan 替换了零。特别是,我将只处理最缺少值的两个特征:“皮肤厚度”和“胰岛素”。更具体地说,在该数据集中的 768 行中,“皮肤厚度”有 227 个缺失值,“胰岛素”有 374 个缺失值。是不是很疯狂?
注意 :
对于这个例子,你不需要知道这两个特性的重要性。我在 Kaggle 上创建了一个笔记本,可以和这篇文章一起使用。这本笔记本深入探讨了不同的估算方法,包括我喜欢称之为“混合方法”的方法。笔记本是作为机器学习和迭代输入教程创建的,您可以通过点击此处查看/编辑笔记本。
现在让我们来看看这两个特性的分布情况:

图片由作者提供|该分布图使用了 541 个“皮肤厚度”值和 394 个“胰岛素”值。这个图是通过使用 Seaborn 的 distplot()函数获得的。
这两种分布的偏斜度和峰度如下:
- “皮肤厚度”的偏斜度:0.690619
- “胰岛素”的偏斜度:2.166464
- “皮肤厚度”的峰度:2.935491
- “胰岛素”的峰度:6.370522
现在让我们看看在对缺失值进行均值插补后,我们的分布图、偏度和峰度是如何变化的。甚至不用思考,我们就可以想象我们的两个特征的分布图有更多的峰度,因为我们为“皮肤厚度”输入了一个单一的值 227 次,为“胰岛素”输入了一个不同的值 374 次(当我们只有 768 行要处理时)。
注:“皮肤厚度”的平均值为 29.153420,“胰岛素”的平均值为 155.548223。
这是我们用 NaNs 做的数据集。

图片由作者提供|这是均值插补后的分布图,对“皮肤厚度”和“胰岛素”使用了 768 个值。
这两种分布的偏斜度和峰度如下:
- “皮肤厚度”的偏斜度:0.837608
- “胰岛素”的偏斜度:3.380019
- “皮肤厚度”的峰度:5.430987
- “胰岛素”的峰度:16.232455
毫不奇怪,我们看到 S̶O̶M̶E̶的峰度急剧上升,尤其是对“胰岛素”而言。
现在让我们使用 IterativeImputer 来估算包含 nan 的数据集中这两个要素的缺失值。我将使用RandomForestRegressor()作为迭代输入的估算器。
# Introduce IterativeImputer with an estimator
>>> imp = IterativeImputer(estimator=RandomForestRegressor())# Fit to the dataset containing missing values
>>> imp.fit(ndf)# Transform the dataset containing missing values
>>> df = pd.DataFrame(imp.transform(ndf), columns = ndf.columns)"""
Where `ndf` is the dataset containing missing values
"""
现在让我们看看插补后两个特征的分布。

图片由作者提供|这是使用 RandomForestRegressor()作为估计量迭代输入缺失值后两个特征的 768 个值的分布图。
这两种分布的偏斜度和峰度如下:
- “皮肤厚度”的偏斜度:0.679155
- “胰岛素”的偏斜度:2.052863
- “皮肤厚度”的峰度:3.039844
- “胰岛素”的峰度:6.778861
我们可以看到,这两个特征的峰度并不像我们将这两个特征的平均值估算到它们相应的缺失值时那样疯狂。不仅如此,我们可以看到两个特征的偏度和峰度水平与包含 nan 的数据集的水平相对相似。
结论
一般来说,多变量方法比单变量方法更受欢迎,但我个人认为这取决于您自己的需求和您项目的目标。在这篇文章中,我们使用 PIMA 印第安人糖尿病数据库看到了均值插补和迭代插补之间的差异。在第二篇文章中,您将能够看到如何通过使用股票市场数据来使用 IterativeImputer 的另一种方法。
我真的希望阅读这篇文章是值得的。这是我在 Medium 上写的第一篇文章,但就我看来,这不会是我的最后一篇。我高度鼓励你点击下面的链接来看看这篇文章的第二部分。我希望你能使用 IterativeImputer,并且我希望你能发现我的两个例子中至少有一个是有用的。
请随意查看我写在 Kaggle 上的笔记本,在那里我可以更深入地了解 PIMA Indians 糖尿病数据库上的 IterativeImputer。
https://www.kaggle.com/gifarihoque/pidd-missing-data-ml-iterimputer-tut-86/comments
参考
[2]: Scikit-learn。缺失值插补 ( 点击返回)
最后的话
如果你注意到我的文章中有任何错误,请留下评论并联系我,这样我可以尽快修复它!我不想误导任何人,也不想教任何人错误的东西。对我来说,任何批评都是积极的批评,这是我学习的最好方法之一。我也总是乐于接受提示和建议。
如果你喜欢阅读这篇文章,请关注我的 中型 和Kaggle我会不时在这里发布更多内容。还有,随时和我联系LinkedIn。如果你认为我的工作应该得到一杯咖啡,请给我买一杯 !任何支持都帮助我坚持下去。
一如既往,再次感谢你的来访。
用 dtreeviz 库可视化决策树的更好方法
实践教程
一个用于决策树可视化和模型解释的开源包

作者图片
一幅画胜过千言万语,这是正确的说法。这个公理同样适用于机器学习模型。如果一个人能够可视化和解释结果,它会给模型的预测注入更多的信心。可视化机器学习模型如何工作也使得向具有较少或没有机器学习技能的人解释结果成为可能。Scikit-learn 库天生具有绘制决策树的能力。但是,与默认选项有一些不一致。本文将着眼于一种叫做 dtreeviz 的替代方案,它可以呈现更好看、更直观的可视化效果,同时提供更好的可解释性选项。
用于可视化基于树的模型的 dtreeviz 库
dtreeviz 是一个用于决策树可视化和模型解释的 python 库。根据其 Github repo 上的可用信息,该库目前支持 scikit-learn 、 XGBoost 、 Spark MLlib 和 LightGBM 树。
下面是在著名的葡萄酒质量数据集上,从默认 scikit-learn 和从 dtreeviz 生成的可视化的视觉比较。该数据集包括 178 个实例和 13 个数字预测属性。每个数据点可以属于名为 class_0、class_1 和 class_2 的三个类之一。


在著名的葡萄酒质量数据集 |作者提供的图片上,对默认 scikit-learn(左)和 dtreeviz(右)生成的可视化效果进行视觉比较
从上面的图片可以明显看出,右边的图比左边的图提供了更多的信息。默认的 scikit 学习可视化存在一些明显的问题,例如:
- 目前还不清楚不同的颜色代表什么。
- 目标类没有图例。
- 可视化返回样本的计数,并且不容易可视化分布。
- 无论样本数量多少,每个决策节点的大小都是相同的。
dtreeviz 库填补了这些漏洞,提供了一个更清晰、更容易理解的画面。以下是作者要说的话:
可视化效果的灵感来自 R2D3 的教育动画;机器学习的可视化介绍。使用
dtreeviz,您可以可视化特征空间如何在决策节点上分割,训练样本如何在叶节点上分布,树如何对特定观察进行预测等等。这些操作对于理解分类或回归决策树的工作方式至关重要。
在接下来的几节中,我们将通过一些常见的例子来看看 dtreeviz 如何优于其他可视化库。关于安装说明,请参考官方 Github 页面。可以安装pip install dtreeviz but需要预装graphviz。
dtreeviz 的卓越可视化
在可视化决策树之前,理解它是如何工作的也是很重要的。决策树是一种监督学习预测模型,它使用一组二元规则来计算目标值。它可用于回归和分类任务。决策树有三个主要部分:
- 根节点:执行第一次拆分的节点。
- 终端节点/叶节点:预测结果的节点。
- 分支:箭头连接节点,显示从问题到答案的流程。
决策树模型的算法通过重复地将数据划分到多个子空间中来工作,使得每个最终子空间中的结果尽可能地相似。这种方法在技术上被称为递归分区。该算法试图将数据分成子集,以使每个子组尽可能纯净或同质。
以上摘录摘自我写的一篇关于理解决策树的文章。本文深入解释了算法通常如何做出决策。
我在“分析优势”课程中关于决策树的笔记](https://medium.com/analytics-vidhya/a-guide-to-machine-learning-in-r-for-beginners-decision-trees-c24dfd490abb)
现在让我们回到 dtreeviz 库,使用上面提到的葡萄酒数据绘制其中的几个。
资料组
我们将使用来自 葡萄酒质量数据集 的著名红酒数据集。数据集由与葡萄牙“Vinho Verde”葡萄酒红色变种相关的几项理化测试组成。目标是在这些测试的基础上建立葡萄酒质量模型。由于该数据集可以被视为分类和回归任务,因此它适合我们的用例。我们将不必使用单独的数据集来演示分类和回归示例。
这里是 nbviewer 链接 到笔记本 incase 你想跟着走。
让我们看看数据集的前几行:

数据集一瞥|作者图片
quality参数是指葡萄酒的质量,是一个介于 0 和 10 之间的分数

形象化
轻松创建特性和目标变量。
features = wine.drop('quality',axis=1)
target = wine['quality']
回归决策树
对于回归示例,我们将预测葡萄酒的quality。
# Regression tree on Wine datafig = plt.figure(figsize=(25,20))
regr= tree.DecisionTreeRegressor(max_depth=3) regr.fit(features, target)viz = dtreeviz(regr,
features,
target,
target_name='wine quality',
feature_names=features.columns,
title="Wine data set regression",
fontname="Arial",
colors = {"title":"purple"},
scale=1.5)
viz

回归决策树|作者图片
- 水平虚线表示决策节点中左右桶的目标平均值;
- 垂直虚线表示特征空间中的分割点。
- 黑色楔形突出显示分割点,并确定精确的分割值。
- 叶节点用虚线表示目标预测值(平均值)。
分类决策树
对于分类示例,我们将从给定的六个类别中预测葡萄酒的class。这里的目标也是质量变量。
# Classification tree on Wine datafig = plt.figure(figsize=(25,20))
clf = tree.DecisionTreeClassifier(max_depth=3)clf.fit(features, target)# pick random X observation for demo
#X = wine.data[np.random.randint(0, len(wine.data)),:]viz = dtreeviz(clf,
features,
target,
target_name='wine quality',
feature_names=features.columns,
title="Wine data set classification",
class_names=['5', '6', '7', '4', '8', '3'],
histtype='barstacked', # default
scale=1.2)
viz

葡萄酒数据分类树|按作者分类的图片
与回归变量不同,目标是分类器的类别。因此,直方图用于说明特征-目标空间。当类别数量增加时,堆叠直方图可能难以读取。在这种情况下,histogram type参数可以从默认的barstacked,更改为bar。
自定义
dtreeviz 库还提供了一系列定制。我将在这里展示其中的一些:
缩放图像
缩放参数可用于缩放整个图像。
从左到右对齐的树
可以将orientation参数设置为LR来从左到右显示树,而不是从上到下
fig = plt.figure(figsize=(25,20))
clf = tree.DecisionTreeClassifier(max_depth=2)clf.fit(features, target)# pick random X observation for demoviz = dtreeviz(clf,
features,
target,
target_name='wine quality',
feature_names=features.columns,
title="Wine data set classification",
class_names=['5', '6', '7', '4', '8', '3'],
**orientation='LR',**
scale=1.2)
viz

从左到右对齐的树|作者图片
单次观测的预测路径
该库还有助于隔离和理解特定测试观察遵循的决策路径。这对于向他人解释预测或结果非常有用。例如,让我们从数据集中挑选一个随机样本,并遍历它的决策路径。
fig = plt.figure(figsize=(25,20))
clf = tree.DecisionTreeClassifier(max_depth=3)clf.fit(features, target)**# pick random X observation for demo
X = features.iloc[np.random.randint(0, len(features)),:].values**viz = dtreeviz(clf,
features,
target,
target_name='wine quality',
feature_names=features.columns,
title="Wine data set classification",
class_names=['5', '6', '7', '4', '8', '3'],
scale=1.3,
X=X)
viz

单次观察的预测路径|作者提供的图像
保存图像
输出图形可以保存为 SVG 格式,如下所示:
viz.save_svg()
结论
在绘制决策树时,dtreeviz 库比其他库得分更高。使结果可解释的额外能力是一个极好的附加功能;您可以隔离单个数据点,并在微观层面上理解预测。这有助于更好地理解模型的预测,也便于将发现传达给其他人。我在这里所触及的只是冰山一角。Github 库和作者的附带文章更详细,我强烈推荐浏览它们。链接在下面的参考资料部分。
参考资料和进一步阅读:
- dtre eviz 的官方 Github 库。
- 如何可视化决策树——dtre eviz 的创建者们对决策树可视化的一个很好的阅读。
- 了解决策树
👉有兴趣看我写的其他文章。这个 回购 包含了我分类写的所有文章。
更好的投票方式
实践教程
从个体排序选择量化集体偏好的马尔可夫方法

作者图片
晚餐吃什么?
前几天,我们一家四口不得不做出一个关键的决定:晚餐吃什么。桌上的选择有寿司、炸鸡、印度菜或比萨饼。我们本可以像以前一样:每个人都为他们的首选投票,得票最高的选项获胜。但是,我一直在思考如何更准确地模拟集体偏好,我们决定尝试一下。所以我们每个人从最喜欢到最不喜欢排列 4 个选项。以下是我们的投票方式(保持匿名以保护无辜):
Vote 1: sushi, fried chicken, pizza, Indian
Vote 2: sushi, fried chicken, pizza, Indian
Vote 3: friedchicken, sushi, pizza, Indian
Vote 4: pizza, fried chicken, Indian, sushi
典型的首选投票法会有一个明确的赢家:寿司。但是 4 号选民对寿司的反感难道不应该算点什么吗?我通过一个模型分析了投票结果,这个模型考虑了投票中表达的所有偏好,而不仅仅是首选。这个模式产生了一个不同的赢家:炸鸡。这很有意义。如果每个人的前两个选择都有所收获(就像炸鸡一样),而不是四分之一的人不得不忍受最后一个选择(寿司)的痛苦,那么这个群体整体上不是更快乐吗?
这个模型不仅仅是选出一个赢家:它还对选项进行了排序。结果是,我的家人(当时)更喜欢炸鸡,然后是寿司,接着是披萨,最后是印度菜。
该模型不仅对选项进行了排序,而且还揭示了选项之间的相对偏好比率:
fried-chicken:sushi:pizza:Indian as 44:37:16:3
这是炸鸡和寿司之间的一场势均力敌的比赛,前者击败了后者。披萨远远排在第三位。那天晚上,这家人没有心情吃印度菜。
我们是如何得到这些结果的?继续读下去,我会详细解释。但在此之前,先澄清一下。我希望晚餐吃什么这个不严肃的问题没有让你误以为这只是一种娱乐和游戏。事实上,这是非常严肃的事情。各级集体(家庭、公司、社区、国家)都重视成员的意见(或者至少应该重视)。通常,群体决策归结为如何在一组相互竞争的优先级之间分配有限的资源(例如权力或金钱)。这就是这个方法的闪光点。它定量地确定了一系列选择中的相对偏好,然后可以用来按照人民的意愿分配预算。
该方法
我将使用上面的晚餐是什么的例子来说明从个人的排序选择中确定一组选项的集体相对偏好的方法。将一个人在两种美食之间的排序选择表示为有向图https://en.wikipedia.org/wiki/Graph_(discrete_mathematics)**,其中每种美食都是一个节点(顶点),一条边(链接/箭头)从不太喜欢的美食指向更喜欢的美食。

代表一个人对寿司比对披萨的偏好的图表(图片由作者提供)
对于个人来说,一种方便、简洁且一致的表达他们对不同菜系的相对偏好的方式是将这些菜系从最喜欢的到最不喜欢的排列出来。例如,投票 2(寿司、炸鸡、比萨饼、印度菜)表明寿司是那个人的首选,然后是炸鸡,接下来是比萨饼,最后是印度菜。这概括了 6 个成对比较:
- 寿司比炸鸡更受欢迎
- 寿司比披萨更受欢迎
- 寿司比印度寿司更受欢迎
- 炸鸡比比萨饼更受欢迎
- 炸鸡比印度炸鸡更受欢迎
- 披萨比印度披萨更受欢迎
用一个有 6 条有向边的图来表示投票,每条边代表一个比较。

一张图表显示了排名递减的投票“寿司、炸鸡、比萨饼、印度菜”(图片由作者提供)
通过将投票图表相加来合计投票数。把图形加在一起是什么意思?这意味着求和图中的每条边都被分配了一个权重,该权重是被求和的图中有多少条边的计数。以下是示例中 4 张投票的 4 个图表的总和:

4 张投票汇总图(图片由作者提供)
在聚合投票图中,权重为 3 的从披萨到寿司的边表示在 3 次投票中寿司比披萨更受欢迎。在每次投票中,一种特定的菜肴会与其他菜肴进行比较。因此,在一次投票中,针对一种美食的成对比较的数量比美食的数量少 1,或者在本例中为 4 种美食的 3。对于总共 4 次投票,对一种菜肴进行成对比较的次数是 4 x 3 或 12。给定针对每种美食表达的成对偏好的总数,通过向每个节点添加自边来转换聚合图,该自边的权重等于该美食比其他美食更受欢迎的偏好的数量。该权重是该节点的传入边的权重之和。或者,等价地,它是节点的偏好总数(本例中为 12)与该节点的输出边的权重之和之间的差。这是带有自边的转换聚合图:

带有自边缘的聚合投票(图片由作者提供)
最后,将图转换为归一化所有权重,使得来自一个节点的所有外出边的权重之和为 1* 。然后,从比萨饼到寿司的边的归一化权重表示相对于比萨饼更喜欢寿司的成对偏好的分数(以投票表示)。权重现在可以被认为是在不同的集体偏好的美食之间转换的经验概率。下面是该示例的标准化图表,通过将每个权重除以 12(这是在 4 次投票中一种菜肴与其他菜肴的比较总数)获得:*

具有自边缘和归一化权重的聚合投票(图片由作者提供)
现在你已经准备好决定投票结果了。从一个特定的初始假设开始,假设这个群体对这些菜系的相对偏好。例如,所有菜系都同样受欢迎:
*fried-chicken:sushi:pizza:Indian as 1:1:1:1*
在一次迭代后,将概率转移(编码在转换的聚合投票图中)应用于初始偏好,以获得修正的集体偏好。怎么会?从图中,你知道印度菜有 8%的机会保持首选,33%的机会输给披萨,另外 33%的机会输给炸鸡,25%的机会输给寿司。因此,最初偏好中的印度部分转变为 8%的印度菜、33%的比萨饼、33%的炸鸡和 25%的寿司。将同样的过程应用到其他菜系,你会得到新的比率:
*fried-chicken:sushi:pizza:Indian as 38:33:25:4*
如果应用转换看起来很乏味,不用担心,线性代数会有所帮助。构建一个 4x 4 矩阵,其中第一列包含炸鸡转变为炸鸡、寿司、披萨和印度菜的概率,第二列包含寿司以外的转变概率,依此类推。

转移概率矩阵 T(图片由作者提供)
然后,将相对偏好表示为相对概率的向量。因此,最初的无偏偏好,v₀,对于 4 种美食之间相等的相对概率,具有分量 0.25,025,0.25,0.25。将偏好向量乘以转移矩阵,以在一次迭代后获得修正的偏好向量。不断迭代,直到偏好稳定到投票者的实际偏好。**

相对偏好转变直至稳定(图片由作者提供)
在这个例子中,稳定的相对偏好结果是:
*fried-chicken:sushi:pizza:Indian as 44:37:16:3*
正如你在本文开头看到的,炸鸡是赢家,以微弱优势击败了寿司。
基本理论
如果这种从个人的排序选择中计算相对加权偏好的方法看起来有点像从帽子里变出一只兔子,请放心,它有坚实的数学基础。如果你喜欢数学,我会在这里简述这个理论。否则,请随意跳过这一部分。
该问题映射到一个 马尔可夫链 ,使其完整的数学机器可用。马尔可夫链是一个事件序列,其中每个事件的概率只取决于当前状态(不考虑任何过去的状态或历史)。个体排序选择的标准化聚集图描述了一个 4 态马尔可夫链。一个州是图中的一个节点(或者节点的混合),在我们的例子中,它代表了投票者对美食的偏好权重。一个事件是状态的变化,即不同菜系之间相对权重的变化。从投票中确定的经验转移概率定义了一个 随机矩阵 (或者更准确地说,一个每列总和为 1 的左随机矩阵)。这样的矩阵保证有一个特征值为 1 的特征向量。该特征向量是马尔可夫链的稳态,因为它在概率转移下保持稳定。并且,如果矩阵是正的(即,没有条目是 0),那么任何初始状态都保证收敛到稳态。事实证明,我们的偏好转移矩阵确实是正的。这就是为什么你可以从任何初始相对偏好出发,达到稳定的集体偏好。
有了这个理论基础,让我们把这个方法命名为马尔可夫方法。
怎么能尝试呢?
在我家,我们每个人对每件事都有强烈的看法,从我们晚餐应该吃什么到我们应该做什么活动来充分利用西雅图的阳光假期。手动遍历马尔可夫方法的所有步骤是非常繁琐和容易出错的。但是让计算机为我们做这件事是一件简单的事情。我在https://vishesh-khemani.github.io/collective-preference有一个原型网络实现,你可以玩玩。只需输入选项和对这些选项的投票,它就会给出投票者偏好的相对权重,以及转移概率图。这里可以看到代码:https://github . com/vis hesh-khe mani/vis hesh-khe mani . github . io/tree/main/collective-preference。
以下是晚餐吃什么示例的运行截图:

https://vishesh-khemani.github.io/collective-preference/(图片作者)
怎么有用?
优先选择投票
在美国政治选举中,第三方候选人经常扮演搅局者。例如,在 2000 年的总统选举中,阿尔·戈尔在佛罗里达州以大约 500 票之差输给了乔治·w·布什,如果拉尔夫·纳德没有获得大约 97,000 票的话,这个赤字本可以轻松克服。这导致选民进退两难:是投票给他们最喜欢的候选人,还是他们更喜欢的更有胜算的候选人。如果选民可以排列他们的选择,而不是只投票给一个候选人,会怎么样?澳大利亚等国家和美国缅因州等州已经这样做了。然而,他们的计票方法是有缺陷的(因为它只考虑那些较高排名的选择在前几轮计票中被淘汰的较低排名的选择)。
考虑一个有三个候选人的选举:X,MX 和 m。X 是一个极端的候选人,一个要么被喜爱要么被厌恶的两极分化的人物。m X 是 X 的温和版本,M 是与 X 和 MX 站在政治光谱另一边的温和候选人。X 的支持者可能会把 M 排在最后。MX 或 M 的支持者很可能会把 X 排在最后(因为 X 要么受人喜爱,要么令人厌恶)。考虑以下投票:
*Vote 1: X, MX, M
Vote 2: X, MX, M
Vote 3: MX, M, X
Vote 4: M, MX, X
Vote 5: M, MX, X*
在传统的多数选举中,结果将是 M 和 X 打成平手。x 会破坏 MX 的机会。
在传统的排名选择选举中(正如缅因州实施的那样),MX 将在第一轮计票中被淘汰。然后,vote 3 将为其第二选择 M 贡献一票,推动 M 战胜 X。X 将再次成为 MX 的搅局者。
在马尔可夫方法中会发生什么?下面是截图:

作者图片
完全不同的结果: MX 赢了!X 的支持者仍然能够把 MX 作为他们的第二选择。x 不是剧透。这难道不是最公平的结果吗?MX 是所有投票者的第一或第二选择。大部分投票者最不喜欢 X 和 M。选民可以根据自己的真实喜好投票,而不用担心会从更受欢迎的候选人那里抢走选票。此外,候选人受到激励减少两极分化,以免对手支持者的反感破坏他们的机会。
锦标赛排名和预测
这里有一个场景,不涉及投票,而是预测。为什么我要说预测?成对偏好的建模同样适用于一组实体之间的任何成对比较。例如,在锦标赛中,我们可以将过去比赛的结果输入到模型中,它将预测每个竞争者获胜的相对可能性。
我在最近完成的一场锦标赛中尝试了这种方法:一场有 8 支队伍参加的板球联赛,每支队伍与其他队伍比赛两次。56 场比赛下来,前 4 名球队晋级季后赛。我输入了前 28 场比赛的数据(联赛阶段进行到一半时,在每支球队与其他球队比赛一次之后),看看这个模型会预测接下来的 28 场比赛。结果如下:

作者图片

作者图片
模型的预测有多好?
- 该模型完全准确地预测了前 4 名球队(有资格进入季后赛的球队)。
- 该模型的排名需要 5 次成对交换(在上图中用红线和绿线之间的交叉线表示)才能与实际排名完全匹配。如果该模型通过预测与实际排名相反的排名尽可能地错误,它将需要 28 次成对交换。因此,这里有一个预测排名准确程度的衡量标准:(1-5/28)或 0.82 (其中 1 表示完全准确,0 表示最不准确)。这是一个相当高的准确度分数。
- 也许你认为这太夸张了,你可以简单地跟踪 28 场比赛后每支球队的输赢记录,然后得出一个类似的准确预测。但事实并非如此。28 场比赛后的输赢记录将充斥着不准确和其他问题。例如,班加罗尔、孟买和德里的战绩都是 5:2,班加罗尔击败孟买,孟买击败德里,德里击败班加罗尔。所以还不清楚这三支队伍之间的相对排名。加尔各答赢的比赛比海得拉巴多,也赢了海得拉巴,所以你会认为它的排名应该比海得拉巴高。然而,马尔科夫模型将海德拉巴排名靠前,主要是因为他们击败了其他一些顶级球队,这一预测在现实中得到证实。
摘要
- 如果一组实体(例如,选举中的候选人或锦标赛中的团队)被成对比较(例如,投票人更喜欢候选人 A 而不是候选人 B,或者在最近的比赛中 A 队击败了 B 队),并且每个成对比较发生任意次(例如,两个候选人之间不同的投票人偏好,或者两个团队之间不同的比赛结果),则您可以确定完全概括成对比较的实体之间的相对权重(例如,投票人更喜欢候选人 A 而不是候选人 B,比例为 3:2,或者 A 队的赔率为 5:3)
- 计算方法基于将实体之间的成对比较映射到实体之间的经验转移概率。怎么会?将成对比较表示为一个图,其中每个被比较的实体是一个节点,该节点具有从“输家”到“赢家”实体的有向边。将所有比较图组合成聚合图,其中每条边的权重等于该边在成对比较中的出现次数。对权重进行归一化,使得从实体 A 到实体 B 的边的权重是 A 和 B 之间以 B 为获胜者的比较的分数。这个分数是 B 对 a 获胜的经验转移概率。现在,该图表示用于在比较的实体之间获胜转移的马尔可夫链。从实体之间的任何初始胜率开始,并重复应用转移概率,马尔可夫链稳定到胜率的稳态。
- 该方法可广泛应用,从决定哪种烹饪最能满足朋友/家人的不同口味,到比赛中竞争者的相对获胜几率,到哪个候选人是投票者真正最喜欢的选择,到如何在竞争项目中分配固定预算,等等。
- 您可以在 https://vishesh-khemani.github.io/collective-preference 使用原型 web 实现,用这种马尔可夫方法来处理您自己的一组实体和比较。如果你想出了一个有趣的结果,我希望你能留下评论。
时间序列预测的蓝图
指数平滑和 ARIMA 模型的探索

正如我在之前的故事中提到的,我讨论了如何“展示”你的预测,让我们开始时间序列预测吧。
让我们从基础开始。什么是时间序列?
时间序列表示的是一段时间内有序的数据,也就是说,我们在 等时间间隔 观察到的数据。为什么 顺序 很重要?这是因为我们假设今天发生的事情取决于昨天发生的事情等等。因为时序在这里很重要,所以把一个时间序列事件看作一个随机过程是不正确的。这意味着我们不能从时间序列数据中随机抽样分布。
为什么要使用时间序列而不是回归技术之类的预测模型呢?嗯。
有时,我们可能没有足够的信息(预测因子)来预测未来(目标)。此外,我们可能无法在可用的预测因子和我们的目标变量之间建立显著的相关性。
因此,与预测建模不同,在预测建模中,您有预测器来解释您的目标变量,时间序列预测依赖于来自以前时间段的目标变量本身(也称为滞后)来进行预测。
插图:
*预测建模:收入= 1.78 利润+ 2.5 ( 这是一个简单的线性回归方程,根据利润提供的信息预测收入)。假设我们没有利润信息。我们转向时间序列。
时间序列模型:收入(t+1)= a 收入(t)+a(a-1)收入(t-1) +..(在此模型中,收入取决于之前时间段的收入,当我们通过使用平滑参数‘a’*回到过去时,收入的权重逐渐衰减。)*

让我们开始查看我们的数据。我们将点燃我们的 Jupyter 笔记本。

我们超市数据集的前 5 条记录
让我们快速浏览一下我们的元数据。
*superstore.info()*

我们将处理时间序列的销售和订单日期
我们可以安全地使用订单日期和销售额,而不必进行任何缺失值处理。让我们建立我们的时间序列。

我们正在合计每个月的销售额
想象的时间到了。
python 中有许多可视化数据的库,最广泛使用的是 matplotlib 和 seaborn。在这里,我使用了 plotly 来呈现一个交互式的视觉效果。如果你有兴趣了解更多关于 plotly 的信息,请点击这里。您可以使用下面的代码:
好吧。让我们更深入地研究时间序列的组成部分。我们将探索样本地块以更好地理解概念。
一个时间序列的主要成分有:季节性,周期性 和 白噪声(误差) 成分。趋势、季节性和周期性是可以建模的系统成分,误差是随机成分。**

有明显的上升趋势
趋势 量化序列的整体向上或向下移动。
季节性 量化每年重复的数据中的 年内 波动。例如,雨伞的销量在一年中的每个雨季都会上升,冰淇淋的销量在夏季会上升,配送公司在一年中的每个季度都会开出更多的账单;这些很可能每年都会发生。 问题 :每年的时间序列会有季节性吗?号码

香槟销售的季节性
当数据显示上升和下降不是固定周期*时,存在循环模式。因此,它不同于季节性,我们在后者中看到一个固定的模式。此外,一个周期的平均长度比季节长得多,通常跨越数年。*

大多表现出多年来没有固定模式的周期性行为
最后, 白噪声 是随机误差分量,假设其具有均值 = 0 和常数方差 𝛔的正态分布。
我们将关注趋势、季节性和白噪声。循环组件建模很复杂(至少就我目前所知),我们将尝试使用其他三个组件来解释该模型。很多时候,周期成分是用趋势本身来解释的。
我们可以通过两种方式组合这些组件:
加法模型 : Y(t) =趋势(t) +季节性(t) +误差(t)
**乘法模型 : Y(t) =趋势(t) 季节性(t) 误差(t)
加性模型将假设随时间推移或多或少的恒定季节性。另一方面,随着时间的推移,乘法模型将会看到季节性以及趋势的显著变化。乘法模型更真实。然而,为了使用乘法模型,我们将对数据进行对数转换,使其成为加法模型,然后我们可以轻松提取趋势和季节性成分。如果你熟悉对数,你应该能理解下面的等式:
**log( Y(t) ) = log( 趋势(t) 季节性(t) 误差(t))
log( Y(t) ) = log( 趋势(t)) + log(季节性(t)) + log(误差(t))
可视化是理解我们时间序列本质的良好开端。

这看起来像一个乘法模型

附加模型的插图(你对循环模式有什么看法?是还是不是?)
可视化有其自身的局限性。我们需要一种方法来量化和确认我们的发现——输入分解。
分解有助于我们提取和量化时间序列的组成部分。这也有助于我们理解在解释时间序列时什么更重要。
让我们回到我们的超市。看完系列,我就先来个加法模型。让我们来分解我们的系列:我已经说明了所使用的各种方法中的两种。
经典分解

分解我们的时间序列
仔细观察趋势和季节性成分。随着时间的推移,我们可以看到一个上升的趋势和或多或少不变的季节性。同样残差以零点为中心。这证实了我们对加法模型的假设。**
STL —稳健方法

STL 只适用于附加模型;如果我们有乘法模型,我们使用对数变换。我们不会在这个故事中探讨这个问题。
我想特别强调另一种识别季节性的方法。我们可以利用 month_plot 来查看这些年中是否有任何月份具有显著的重复模式。
使用 month_plot 识别季节性

在销售额最低的(1 月、2 月)和销售额最高的(9 月、11 月、12 月)观察到的季节性行为
让我们探索一下可以用于预测的各种模型。我们将通过 指数平滑 方法和 ARIMA 方法来预测我们的时间序列。
指数平滑模型
在指数平滑法中,我们根据过去观察值的加权平均值来预测值。这些重量随着我们回到过去而衰减。这意味着我们更加重视最近的观察。参数用于计算权重,其取值介于 0 和 1 之间。
简单指数平滑
当数据中既没有明显的趋势也没有季节性时,使用这种方法。我们只是试图通过使用参数【⍺来平滑级数的电平。水平无非是局部的意思。数学上我们可以这样表示:
y(t+1)= ⍺y(t)+⍺(⍺-1)y(t-1)+⍺(⍺-1) y(t-2)+……….. 权重随着时间的推移而衰减,⍺是所使用的平滑参数,可以在 0 和 1 之间调整。*
让我们编码这个。

SES 模型
**#Let's check accuracy score by using RMSE
Root mean squared error = 36203.014759074365**
你可以看到天气预报并不乐观。你可以试着调整⍺的看看你是否能得到更好的预测。简单的指数平滑总是给出一个平坦的预测,因此应该只用于预测下一个数据点。
霍尔特模型或双指数平滑
这里我们考虑两个属性:水平和趋势——这意味着我们将有两个平滑参数,分别是和 β 。当有趋势但没有季节性时,我们应用这种类型的模型。**
让我们直接进入代码。

霍尔特线性趋势模型
**#Let's check accuracy score by using RMSE
Root mean squared error = 19269.25908614305**
我们可以看到,RMSE 越低,预测越好。还有一个没有任何季节性的线性趋势成分。
除了不能捕捉季节性之外,这种方法也不能稳定或者“抑制”趋势。在未来的日子里,它只会越来越高。这是不可取的,尤其是当我们进行长期预测时。对此的一个解决方法是添加阻尼参数。这是霍尔特线性趋势模型的扩展。

带阻尼的霍尔特线性趋势
**#Let's check accuracy score by using RMSE
Root mean squared error = 19646.815465033545#You can see our RMSE has suffered to some extent due to damping. #This is a trade off that we need to consider while selecting the #model.**
霍尔特-温特模型或三重指数平滑
让我们找回我们的季节性。该模型考虑了序列中的水平、趋势和季节性。由于趋势和季节性的存在,霍尔特-温特的模型可以是加法或乘法。平滑参数有三个:⍺β和 𝜸 分别用于水平、趋势和季节性。如果您还记得,我们的超市数据在分解图中同时包含趋势和季节成分,那么我们可以期待这个模型能给我们带来更好的准确性。我们还假设了一个加法模型。

霍尔特·温特模型
**#Let's check accuracy score by using RMSE
Root mean squared error = 17660.235617816732#Note that the model automatically applies smoothing parameters; you #can manually tune them by specifying smoothing_level, #smoothing_slope and smoothing_seasonal values.**
您可以观察到,该模型捕捉到了图中的趋势和季节性。我们的 RMSE 分数也提高了。
简而言之,我们已经看到我们的模型既有趋势部分又有季节部分,它们是以一种“相加”的方式结合在一起的。因此,霍尔特-温特的模型给了我们最好的结果。接下来我们将看看更复杂的自回归模型。
ARIMA 模式
ARIMA 代表自回归综合移动平均线。我们来分解一下。
一个自回归模型试图识别与其构成 AR(p) 分量的滞后(以前的时间段)的显著相关性。例如, AR(1) 过程将意味着时间 t 的值是时间 T1 的值的线性函数(换句话说,滞后 1)。从数学上来说,AR(1)模型看起来像是:
*****y(t)=𝛿+ɸy(t-1)+w(t),其中 w(t)为时刻(t)的白噪声,正态分布为 N(0, 𝛔 )
但是什么是 相关性 又是如何计算出来的呢?让我们来看看公式。考虑两个变量 x 和 y,相关性由下式给出:
关联= Cov(x,y)/(𝛔x * 𝛔y)
分子是 x 和 y 的 协方差 ,分母是 x 和 y 的标准偏差的乘积。相关系数是此公式的结果,它给出了取值在-1 和 1 之间的 x 和 y 的关联程度的估计。如果你熟悉皮尔逊的 r,你会发现这里的相似之处。对于 AR(p)过程,x 和 Y 无非是 Y(t)和 Y(t-p)。
时间序列模型中的移动平均项 MA(q) 是过去误差(乘以一个系数)。假设相应滞后的每个误差项具有方差恒定且均值为零的正态分布。从数学上讲,一个 MA(1) 过程将看起来像:
***【y(t)=+w(t)+𝛉w(t-1),其中 w(t)为时刻(t)的白噪声,正态分布为 N(0,【𝛔】, 为数列的均值
**积分**代表数列求差的次数。差异是用前一个时间段减去当前时间段的值的过程。这将产生一阶的差异。我们可以做 d 次,这样我们的系列就是 d 阶差分系列。但是我们为什么要区分我们的系列呢— 引入平稳性的概念,这是自回归模型的强制性标准。
那么我们来理解一下平稳性。
一个数列是平稳的当且仅当:
a) 时间序列的均值是一个常数 —这意味着具有趋势性或季节性的时间序列是非平稳的。趋势或季节性成分会影响时间序列在不同时期的值。
b) 方差/协方差随时间恒定
c) 时间段(滞后)t 与 t + mth 之间的相关性始终是一个常数 —这暗示着一月和十一月将具有与十二月和十月相同的相关性。

总的来说,平稳序列是统计特性不随时间变化的序列。
有几种方法可以使非平稳序列平稳。这些是对数转换(用于平滑方差),消除分解后的趋势和季节性,计算移动平均数和差分序列。在上述方法中,差分广泛用于使我们已经描述过的数列平稳。
让我们测试数据的平稳性。我们将使用扩展的 Dickey-Fuller 检验来检验我们的零假设,即我们的序列不是平稳的。如果我们得到一个显著的 p 值,我们将拒绝我们的零假设,并建立我们的序列是平稳的。相反,如果我们不能拒绝我们的零假设,我们将差分序列,并再次运行测试。

以 99%的置信度测试统计值大大超过临界值
查看结果后,我们的零假设可以被拒绝。我们可以有 99%的把握说我们的数据是稳定的。
如果我们用分解代替差分,我们可以用同样的方法测试残差的平稳性
我们继续吧。下一步是探索 ACF(自相关函数)和 PACF(部分自相关函数)。还记得我们讨论过 AR(p)和 MA(q)过程吗?ACF 和 PACF 图有助于我们识别订单 p 和 q
ACF 寻找与序列滞后的相关性。滞后为 1 的序列是偏移了一个时间周期的序列。下表显示了 1、2、3 等阶的滞后序列。

滞后的例证
ACF 绘制原始序列与滞后 1、滞后 2 等之间的相关性。我们来看看用 python 怎么做。

我们可以看到滞后(1)是显著的
任何高于蓝色条纹的尖峰都是有意义的。我们可以看到滞后(1)和滞后(12)是显著的。此外,我们可以看到,随着相关性的逐渐衰减(每隔 12 个滞后观察一次),滞后之间有升有降。比方说,滞后(1)和滞后(4)之间的相关性很可能受到中间滞后(滞后(2)和滞后(3)的影响。因此,重要的是消除这些并获得真实的画面— PACF 图用于相同的目的。

我们可以看到滞后(1)在其他冲击中是显著的
上面的 PACF 图显示滞后(1)在其他信号或冲击中是显著的。滞后(12)在这里也很重要。
我们如何解读 ACF 和 PACF 的阴谋?
下面的矩阵应该有助于我们选择最佳订单:

这里的“m”是季节周期
根据我们的图,看起来有季节性和非季节性的成分。每个第 12 个滞后峰值表示季节性,我们可能需要区分它。我们可以迭代不同的组合,并检查 AIC(赤池信息标准)和 BIC(贝叶斯信息标准)的分数。我们的目标是最小化这些分数以获得更好的模型性能。
构建 ARMA、汇总统计数据和准确度分数
**#Call the function and fine tune parameters
# d=0 as we did not difference the series
#Try with p=1, q=1 based on our ACF and PACF plots
#Alternatively, start with (1,0,0) AR model and (0,0,1) MA modelarima_model(1,0,1) #ARMA model**
****
预测 6 个月的 ARMA 模型
**#MAPE
0.6571285433084803#RMSE
23037.553921953575**
精确度不是很高。我们可以尝试不同的组合,看看什么给我们最好的准确度分数。此外,MA(1)系数在统计上并不显著。我们可以放下它,再看看我们的结果。这个迭代可以继续下去,直到我们找到一个最佳的精度。请注意,我们还没有包括季节性。这为季节性 ARIMA 铺平了道路。
让我们来看看季节性的 ARIMA。我们将使用非季节性 arima (p,D,q) 的参数,并添加相应的季节性订单 (P,D,Q)m ,其中 m 是季节性周期;因为我们的数据是每月一次,并且模式每年重复,所以 m=12。
**#Since we saw significant spikes at 12th lag in our ACF and PACF #plots, we can try the followingsarimamodel(0,1,0,12)
sarimamodel(1,0,0,12)
sarimamodel(0,0,1,12)#We need to minimize our AIC and BIC values**
****
我们可以看到 MAPE 的显著改善

分析残差以查看它们是否正态分布
我们可以看到,在模型中引入季节性因素后,AIC、BIC 和 MAPE 的得分有了显著提高。模型残差也几乎遵循正态分布,如下所示:
这是两组分位数的散点图。如果两组分位数来自相同的分布,我们应该看到这些点形成一条大致笔直的线。
****标准化残差-这表明我们的残差经过标准化后,以零为中心。
直方图和 KDE 绘制标准化残差的分布,并将其与正态分布进行比较。
****相关图用于查看残差之间是否有显著的自相关;我们没有。这意味着残差是稳定的,没有任何模式。
残差的平稳性是最终确定模型的必要条件
然而,如果你观察结果,我们仍然没有重要的系数,我们应该迭代其他组合。
幸运的是,python 提供了一个内置函数 auto _ arima(pmdarima 包的一部分),它节省了我们确定 ARIMA 最优顺序的时间。下面是一幅插图:

自动搜索最小化 AIC 的最佳模型

残差正态性
我们可以看到 auto_arima 选择了 ARIMA(1,0,1)(1,0,0)[12]作为最佳模型。残差看起来也是稳定的。如果你没有得到有统计学意义的参数,我们可以区分系列,并按照相同的步骤再次运行 ARIMA 模型。在快速运行 auto_arima 时,它给出了 ARIMA(2,1,0)(1,0,0)[12]。我将把它留在那里,并鼓励你尝试差异和建模。
**#Differencing
data_diff = data-data.shift()
plt.plot(data_diff)**

差分序列
注意,auto_arima 在捕捉季节性方面做得不太好。因此,简单地依赖 auto_arima 不会产生最佳模型。我们必须仔细检查 ACF 和 PACF 图,了解季节效应(如果有的话),绘制 auto_arima 以供进一步实验,并应用业务领域知识来最终确定模型参数。
我希望这为开始实现时间序列预测提供了一个思路。我想强调的是,我们没有在 ARIMA 模型中将数据分为训练和测试。这是因为我们没有太多的数据来建模。这是您根据可以处理的数据量做出的决定。
一个盆景和一个椭圆
拟合图像中的椭圆形状

我最近不得不深入识别图像中的椭圆的细节。我必须承认,我天真地认为这很简单。在我参与的各种计算机视觉项目中,我必须在图像中找到圆和圆盘。椭圆是一个压扁的圆,对吗?从一个圆到一个被压扁的圆能有多难?原来圆锥曲线(椭圆的一个例子)有一些错综复杂的东西,在退化的圆的情况下我们可以忽略。
我邀请你跟随我在图像中拟合椭圆形状的过程。您可以在这个库中找到代码和输入图像。
我们要完成的任务是在这张图片中找到花盆边缘的椭圆形状:

花盆里的盆景。图片由作者提供。作者妻子精心照料的盆景。
创建遮罩
每当我们想要在图像中定位一个形状时,无论它是直线、多项式、圆还是椭圆,我们首先需要创建作为目标形状一部分的候选像素的遮罩(即二进制图像)。
在我们的例子中,与背景相比,锅边看起来是一个明亮的边缘。在这种情况下,我们的首选工具是 Canny 边缘检测器,之前进行了一些模糊处理,以忽略我们在苔藓表面和背景中的草地上看到的高频纹理。
这个操作的结果是这个掩码:

精明的面具。图片由作者提供。
除了来自树叶的大量不相关的像素之外,我们有覆盖大约 75%的花盆边缘的候选点。通过观察树叶是绿色的,而我们感兴趣的点是白色的,我们可以过滤掉大部分的异常值。我们将创建一个非绿色像素的蒙版,只保留属于这个蒙版的候选点。
我们获得以下掩码:

去掉以绿色为主的像素后的 Canny mask。图片由作者提供。
该操作剥夺了边缘上大约 25%的候选点,但去掉了大约 90%的离群点。那是一笔好交易。我们的目标是增加掩膜中感兴趣点的比例,但我们不必消除所有异常值。我们下一步的任务是将内联者和外联者分开。
用 RANSAC 建模椭圆
椭圆是称为 圆锥曲线 的一般对象类的实例。圆锥曲线(之所以这样叫是因为它们是一个圆锥和一个平面的交点)是以下两个变量的二次方程的解:

…它具有等效的矩阵形式:

参数 A、B 和 C 的相对值将决定圆锥截面的类型。具体来说,如果判别式 B–4AC 为负,圆锥曲线将是一个椭圆。否则,圆锥曲线可能是抛物线(零判别式)或双曲线(正判别式)。在接下来的讨论中,我们将假设等式(1)代表一个椭圆。
以 Mz=0 的形式重写等式(1 ):

方程(3)是 6 个变量的齐次线性方程,参数为 A、B、… F。我们将需要五个这样的方程来求解系统,直到达到一个比例因子。
我们有六个参数需要求解。为什么不是六个线性方程?
因为它是一个 齐次线性方程组,即每个方程的右边都是 0。如果我们在这个系统中加入第六个线性独立的方程,我们就会陷入平凡解 A=B=C=D=E=F=0 的困境。这看起来不像一个有趣的椭圆。
我们将求解一个由五个线性独立方程组成的系统,因此我们的解将有一个自由度。五个点(x,y)将允许我们用 numpy.linalg.eig() 创建这个由五个线性齐次方程组成的系统,我们可以通过特征值分解来求解这个系统。
解齐次线性方程组(3)。改编自关于 StackOverflow 的讨论。
让我们假设我们有一个属于椭圆的五个 2D 点的点云。我们现在知道如何通过在五行中堆叠方程(3)来计算其二次方程的参数。不幸的是,我们有成千上万个点的点云,其中一些属于椭圆,一些不属于椭圆。这就是 RANSAC 算法发挥作用的地方。通过随机采样 2D 点的五元组并求解相应的候选椭圆,我们可以找到满足云中最高点数的候选点。
在下图中,找到的椭圆用蓝色突出显示,内点用绿色标记,外点用红色标记[1]:

RANSAC 算法找到的椭圆、内点和外点。图片由作者提供。
我们找到了椭圆的二次方程的隐含参数。这很好,但是对我们真正重要的参数呢?椭圆的中心,半轴的长度,倾斜角?
我不确定我们会走这条路,但是既然你问了,我们就开始吧!
椭圆参数的计算
最重要的是:中心。
让我们定义函数 z(x,y):

由于 A 和 C 具有相同的符号(记住:在椭圆的情况下,B–4AC 是负的),z(x,y)可以被视为具有扁平的一杯葡萄酒的形状。
椭圆是这杯扁平的葡萄酒与平面 z=0 的交点。z(x,y)围绕其中心的径向切片是抛物线(方程(4)中的项最多是二次的),因此我们围绕极值具有径向对称性,因为抛物线围绕它们的极值是对称的。z(x,y)的极值就是我们要找的中心。
z(x,y)的中心,以及对称的椭圆的中心,可以通过求解线性方程组来找到:

求解(xc,yc):

半轴和倾斜角
知道了椭圆的中心,我们可以定义另一个椭圆,平移使其中心为原点:

将(4)插入(8)并求解居中椭圆的参数:

这里需要注意的重要一点是,Dc 和 Ec 项为零。因此,居中椭圆的二次方程可以写成:

…或者,以矩阵形式:

这让人想起以原点为中心的椭圆的二次方程,其轴恰好与 x 轴和 y 轴重合:

等式(17)比等式(19)更复杂,因为一般的椭圆具有相对于 x 轴的倾斜角,这导致 Bc 项非零。等式(19)只是 x 轴和 y 轴各自半轴的平方变形。
我们观察到,等式(17)中的中心 2x2 矩阵是对称的,因此可以正交对角化[2]。这意味着它的奇异值分解 A=UDVᵀ可以采取 A=UDUᵀ=RᵀDR 的形式,其中 r 是一个正交的 2x2 矩阵。我选择称它为“R ”,因为一个正交的 2x2 矩阵围绕原点旋转。
用中心矩阵的奇异值分解重写(17 ):

我们可以用下面的方式解释等式(21)的左边,从右向左读:
- 从属于倾斜椭圆的原始向量(x0,y0)开始,我们将它围绕原点旋转角度θ,以使椭圆的主轴与 x 和 y 轴对齐;

- 既然旋转的向量属于其主轴与 x 和 y 轴对齐的椭圆,我们用对角矩阵挤压轴,就像等式(19)一样;

- 我们去旋转被挤压的向量,使得被挤压的椭圆的主轴与原始椭圆的主轴对齐;

- 我们计算原始向量和去旋转向量之间的点积。
这种解释允许我们从等式(21)的条目中读取我们正在寻找的最后参数,倾斜角θ和半轴 a 和 b:

回到我们的 bonsai 图像,计算出的显式参数是非常好的估计,因为我们可以通过手动测量来确认。

结论
我们经历了在图像中拟合椭圆形状的过程。我们从检测边缘开始,然后应用 RANSAC 算法来调整具有一致点的椭圆的参数。我们使用找到的二次方程的隐式参数来提取椭圆的显式参数。
如果你有一个在图像中拟合椭圆形状的应用,请告诉我。我很高兴听到这件事,并尽我所能帮助你。
[1]点云被随机子采样到 400 个点,以限制计算时间。
[2]https://math . emory . edu/~ lchen 41/teaching/2020 _ Fall/Section _ 8-2 . pdf
数据科学有效学习完全指南
基础和详细的指南,让你在数据科学(或任何学科)上突飞猛进

由 Unsplash 上的 Element5 数码拍摄
“人工智能是新的电力”——吴恩达
我们正处于一场新革命的高潮——数据科学和人工智能——变革着经济的各个领域。因此,很自然地,许多人渴望加入这一潮流,或者在这一领域推动自己。因为这个领域发展如此之快,不断学习新的工具和研究对于跟上新技术的发展至关重要。许多这样的努力自然是以自学的形式出现的。
自学可以采取参加 MOOCs、学习课本、在线文章或研究论文的形式。就我个人而言,我每月都参加深度学习和软件工程等领域的 MOOCs,如 Coursera 和 Udemy。你可能会觉得奇怪,我在自我发展上下了很大功夫,因此我在“不断学习”。对吗?
不完全是。努力是一回事,但真正有效地学习是另一回事。更糟糕的是,你可能会自欺欺人地认为自己有能力。参加 Coursera MOOC— 学习如何学习:强大的智力工具,帮助你掌握困难的科目。
https://www.coursera.org/learn/learning-how-to-learn
作为一名实践数据科学家,在学习数据科学上的时间投资是宝贵的,但我参加这门课程绝对是战略性的,因为我相信它可以促进我未来的学习努力。为了获得更详细的报道,我会推荐任何数据科学家、软件工程师,或者实际上,任何其他专业领域的人参加这个 MOOC。除此之外,这篇文章是我从 MOOC 中获得的重要要点的高潮和精华,并经过了进一步的微调和彻底的研究——从在线/研究文章到书籍,如:
- 原子习惯:微小的变化,显著的结果
- 查尔斯·杜希格的《习惯的力量》
- 凯莉·麦克戈尼格尔的《意志力的本能》
花些时间品味和浏览这篇文章 —其中有我亲自实践过的重要技巧,我相信你会发现这些课程很有启发性,非常有用,无论你将应用于学习数据科学还是其他技术工作领域。
不再拖延,让我们开始吧!
关键词: 记忆力、注意力、情绪/动机、意志力
1.学习导论——大脑和记忆
只有当我们的大脑健康并准备好掌握和保留信息时,我们才能有效地学习。因此,任何关于学习的研究都必须涉及它与记忆和大脑的关系。此外,了解大脑有助于你更好地理解和记住有效学习是如何发生的。你明白了。
学习特有三种记忆——工作记忆,短时记忆,长时记忆。一个成功的学习过程经历了从工作记忆到短时记忆再到长时记忆的过程。这需要时间,我们只能说我们成功地吸收或掌握了长期记忆中的知识。
让我们首先开始谈论工作记忆——当我们在学习过程中进入专注或警觉模式时,工作记忆变得活跃。一般来说,大脑的前额皮质控制工作记忆,在我们处理信息的时候会亮起。你可以将工作记忆视为类似于计算机的临时内存或很快就会自行擦除的劣质黑板,因为它的存储容量很小,在没有任何强化的情况下只能持续 15 秒左右。由于这种脆弱性及其作为学习初始窗口的重要性,我们将在后面讨论优化工作记忆的方法。
有了专注和理解,信息就会以知识的形式转移到短期记忆中——由大脑的海马体控制。这通常是由于前额叶皮层(工作记忆)和海马体之间的相互作用而发生的,就好像前额叶皮层的“注意力章鱼”伸出触角,在海马体中形成并创建临时的神经连接模式。
正如我们将要讨论的,随着不断的强化和持久的练习,暂时储存在海马体中的记忆和知识会逐渐转移到新大脑皮层作为长期记忆。
此外,研究人员发现这种向长期记忆的转移也发生在我们睡觉的时候,如下图所示:
https://qbi.uq.edu.au/brain-basics/memory/where-are-memories-stored
2.为大脑学习做准备——睡眠、锻炼、环境、饮食
在我们逐步完成从工作记忆到长期记忆的知识转移过程之前,让我们首先了解如何培养大脑最佳学习的合适条件。一般来说,大脑适应学习有四个方面——睡眠、锻炼、环境和饮食。
睡觉和午睡可能看起来几乎是无用和懒惰的活动,但实际上它们对学习至关重要。除了将记忆转移到新皮层,睡眠还会清除大脑中的毒素,使信息畅通无阻,并允许神经元之间形成新的突触。这巩固了我们的记忆和学习,我们已经建立了之前的睡眠。此外,休息、睡眠和午睡也可以为我们在清醒时耗尽的意志力充电——意志力是学习的关键精神肌肉,稍后会解释。
专家们通常建议我们每天睡 7-9 个小时,但是因为我个人觉得睡眠对自己非常有益,我会向我的读者建议8-9 个小时的睡眠和小睡。
当我们沉浸在新奇、刺激和支持的物理和社会环境中时,学习也会变得异常活跃。先说社会环境。当大脑经历恐惧、愤怒或压力时,学习就会受到损害(工作记忆表现不佳,意志力耗尽)。因此,培养友好、激励而非有害和破坏性的关系至关重要。在工作、社交圈和家庭中,你可能会遇到嫉妒或愤怒的人,他们出于恶意或仅仅是误解而试图削弱你。最好说出来解决任何潜在的问题,否则形成一种冷漠的形式,不要让负面情绪潜入你的意识。相反,与家人、朋友、配偶和同事的支持和温暖的关系可以大大提高我们的学习潜力。
接下来,一个新奇的物理环境是增强记忆力和改善我们情绪的一大好处。研究表明,我们大脑中的神经元和树突在对积极刺激的反应中存活并生长,这大大有助于记忆的保持。因此,偶尔去各地旅游,甚至去海外旅游,是个不错的主意。这也让我想到,我们有时可以调整学习环境,或者偶尔在咖啡馆学习/工作,制造一种新鲜感。
附加提示 :偶尔整理和装饰一下我们的工作环境也能保持新鲜感,提高我们的心情和动力。
另外,我们需要锻炼。锻炼不仅对身体有益,而且对记忆保持和增强意志力也很有帮助。研究表明,即使没有刺激的环境,锻炼对促进我们大脑中神经元的生长也同样有效。我们通常尝试的典型练习有:
- 慢跑
- 游泳
- 自行车运动
- 力量训练(无氧运动)
然而,我想提出一项被低估了,但对我自己大有裨益的运动——长距离步行。让我解释一下。长距离散步(30 分钟或更长)不仅被普遍认为是一种健康的运动,它还对心理健康带来巨大的好处,如缓解情绪和增强动力。在大自然附近散步甚至更好——一举两得——因为你可以享受享受一个新的物理环境的额外好处。此外,当美妙的创意突然出现在脑海中时,散步会进入分散的思维模式(很快就会解释)。我不记得有多少次在我的徒步旅行中,我在工作中的机器学习问题的解决方案已经被孵化出来。我一点也不惊讶,研究发现散步是创造力的助推器:
https://news.stanford.edu/2014/04/24/walking-vs-sitting-042414/ https://medium.com/personal-growth/why-intelligent-minds-take-their-brains-for-long-walks-19beaab05c18
另一种锻炼形式是通过电子游戏进行精神锻炼。与直觉相反,虽然许多人认为视频游戏会上瘾,可能会阻碍学习,但研究表明,定期但有纪律的游戏可以扩展大脑的认知能力和记忆。特别要叫出一款著名的类似象棋的即时战略(RTS)游戏——星际争霸 2。这款游戏对认知能力的要求很高,每分钟执行的动作也很高,这使得它非常适合进行一系列脑力锻炼,研究表明,这种锻炼可以提高认知敏捷度和流畅智力。
https://www.medicalnewstoday.com/articles/319614#Weather-prediction-task https://www.wired.co.uk/article/starcraft-cognitive-abilities
最后,通过保持大脑处于最佳工作状态,培养合理的饮食对滋养大脑有效学习也大有帮助。相比之下,经常吃油腻和垃圾食品会阻碍大脑的表现,尤其是当它含有高糖分时。研究表明,过量摄入糖分会导致记忆力受损。此外,最近的研究还发现肠道和大脑通过数百万条神经相连,这表明消化系统的健康与大脑的健康密切相关。因此,随着时间的推移,油腻和致癌的饮食也可能降低大脑健康。因此,健脑饮食的关键是避免垃圾食品,多吃一些增强记忆和提高注意力的超级食物,这些食物含有各种有益的营养成分,见以下链接:
额外提示 :吃清淡的食物(减少脂肪和糖分)和间隔摄入热量,比如吃健康的零食(我会推荐超级食物,比如黑巧克力和坚果),可以维持我们一整天的情绪和注意力水平。
https://www.healthline.com/nutrition/11-brain-foods https://www.inc.com/drew-hendricks/10-superfoods-that-increase-productivity.html
3.思维模式——分散和集中
在我们知道如何为有效学习准备大脑之后,现在让我们讨论一下大脑是如何运作的,以便更有效地使用它来学习。总之,大脑以两种模式运作——分散思维和集中思维。
当我们的大脑在思考时,大脑中的神经连接会变亮。在扩散的思维模式中,神经连接照亮了大脑中稀疏的区域,访问我们记忆的不同区域。扩散模式通常与放松活动中的思想自由漫游有关,如散步或闲聊。它可以帮助产生创造力,帮助解决问题和回忆想法。通常,当大脑被一个特定的问题困住时,散步或休息大脑实际上有助于通过分散思维产生新的想法。
(与此相反的观点是,大脑陷入了一种特定的思维模式,无法找到解决问题的创造性方法,这种观点被称为 Einstellung ,此时神经连接更加集中,而不是稀疏。这让我想起了集中思考模式——这还有另一个重要的好处。)
在集中思维模式中,神经连接在大脑的集中区域变亮,通常与对一系列熟悉问题的逐步处理的高度集中相关,例如通过测试。集中思维模式在学习中也特别重要,因为来自前额皮质(工作记忆)的神经连接与海马相连,产生新的线路或知识块。
你可能会认为,通过始终保持我们的大脑处于专注模式,可以最大限度地提高学习和生产力。然而,事实证明,持续的填鸭式学习和大脑工作会对记忆保持产生反作用,同时也会产生压力。更糟糕的是,它可能让意志力肌肉(位于前额皮质)超负荷,引发轻微的倦怠。
相反,研究表明,轻松而深入的学习包括交替的高度集中****(组块——专注模式)和放松(锻炼、休息、小睡、音乐——分散模式)期,以保持积极的情绪,帮助大脑发展新的神经连接,并形成对所学内容的长期记忆。
既然我们已经理解了分散模式的重要性——作为努力工作或产生创造性想法的平衡——现在让我们把注意力转向在集中模式下形成知识块。
4.形成心理块——学习的过程
组块是学习新概念时神经连接模式的形成——使用聚焦模式。通常,组块包括将独立的神经连接拼凑在一起(回想一下“注意力章鱼”),并将它们组合成一个紧凑的整体,以便有效地检索。形成知识块有三个重要步骤:
4.1 集中注意力——学习新材料(工作记忆)
这是你的“注意力章鱼”,它的触角从前额叶皮层(工作记忆)伸向海马体(短期记忆),处于集中思维模式。因为你的前额叶皮层中的工作记忆是如此珍贵,就像一块很快就会自行擦除的劣质黑板,你希望减少让你无法专注的分心和专注(一般来说,我会建议你在学习时避免听音乐,因为它会占用你的工作记忆,而在做重复工作时它可能会提振情绪)。
4.2 理解(短期记忆)
这是“注意力章鱼”的触角,将大脑(海马体)的不同区域串连起来,形成新的神经连接。强大的理解可以通过强大的视觉化和类比、或链接与我们已知事物相关的增量知识来实现。其他的理解技巧包括手写笔记,记录我们正在学习的内容,以利用我们的触觉记忆。在这一点上,一个微弱的或暂时的组块图像形成了,并保存在我们的短期记忆中。
4.3 练习和回忆(长期记忆)
为了将知识从短期记忆转移到长期记忆,最重要的是通过回忆或测试进行练习,理想情况下每隔几天进行间隔重复(回忆集中和分散思维的交替时期)。值得注意的是,有时我们通过方便的练习来欺骗自己,让自己产生能力的错觉。很多这样的便捷做法包括画概念图,重读材料,过度高光。值得注意的是,真正的学习应该是稍微不舒服的,这就是为什么一些有意识的和积极的回忆是绝对必要的。
为了进一步强化组块,还有另一个叫做刻意练习的过程,即专注于一系列让你不舒服、更难的相关问题。对于数据科学家来说,这可能意味着进行一场 Kaggle 竞赛,而不是盲目地通过 MOOCs。通过这些练习,知识块的临时图像被嵌入大脑的更深处,形成持久的记忆。
5.克服拖延症
虽然太多的集中思维模式会带来压力,产生反效果,导致精疲力竭,但另一方面,太多的分散思维模式会导致拖延。
拖延和学习是交织在一起的。拖延的习惯可以通过延迟学习时间表/过程来阻止坚实的知识/理解块形成长期记忆。就其本身而言,拖延似乎是无害的,但它的影响会累积起来,最终让我们成绩不佳,成就感降低。
如果我们知道它不好,为什么我们喜欢拖延?我们拖延是因为当面对困难或无聊的任务时,我们大脑中的疼痛中心会点亮,我们会将注意力转移到更令人愉快的事情上,比如玩手机或发呆。这样理解拖延,我建议 6 种打破习惯的方法(最后一点最重要):
5.1 番茄工作法
第一个建议——一个著名的技巧通常建议在开始一项活动时保持 25 分钟的计时器,然后休息 10 分钟左右,再开始另一项番茄工作法。你明白了。但我个人觉得这太过团队化,转而将其解释为设定短暂的突发中断焦点(没有电话,没有音乐,没有朋友)当然要服从灵活性。如果你觉得开始做番茄工作法太痛苦,只要屏住呼吸,开始手头的工作,哪怕是不到 2 分钟也会让你顺利进入工作状态进入状态。这就是我所说的两分钟规则。
5.2 理解和改变习惯
对于长期拖延者来说,有时拖延已经成为太多根深蒂固的习惯,因此第二个建议是关于重新养成习惯。为了理解如何成功地改变这个习惯,让我们先来看看这个习惯的循环:
5.2.1 球杆
让我们开始一项活动触发器。拖延触发器可能是大脑中的疼痛中心,当我们想要开始一项学习活动时,同时我们看到 YouTube 上推荐的视频时,它就会亮起。
5.2.2 例行程序
这就是习惯性的‘僵尸模式’,这是我们所熟悉的,表演自如的。例如,在我们开始观看 YouTube 推荐的视频后,我们盲目地前进,在没有意识到的情况下一个接一个地消费视频。
5.2.3 奖励
通常一个习惯会重复,因为它感觉很棒——这就是回报。抛开完成任务的痛苦,参与愉快的活动会点亮大脑的快乐中心,尽管只是暂时的,促使我们重新寻找习惯。
为了打破拖延习惯的恶性循环,我们首先必须注意什么是我们的拖延线索并主动将自己从包含这种分心和线索的环境中移除。但是如果我们做不到呢?
尽管意志力很昂贵(我们应该节约),但有时我们可以运用一点意志力来转移我们对拖延暗示的注意力,而不是让自己进入两分钟规则。从线索开始,我们切断了恶性循环,并开始了一个生产力习惯循环,其中,在学习例程之后,我们可以通过自我庆祝、为我们的工作/成就感到自豪或允许自己短暂放松来奖励自己,例如阅读一篇喜欢的网络文章或吃零食。
5.3 让学习变得清晰易懂
第三个建议是关于调节环境——通常,我们被周围的环境塑造。研究表明,在容易开始(容易接近)和多种暗示(明显)的环境中,我们会被吸引去从事活动。如果我们沐浴在拖延暗示和容易分心的环境中,重新养成拖延的习惯可能会很困难。尽管如此,我们可以反击:
对抗容易分心的事物——如果我们沉迷于电脑游戏,一个解决办法是彻底删除游戏。当我们再次沉迷于电脑游戏时,重新安装游戏的麻烦和时间可能会阻止我们拖延。
****抵制拖延线索——将手机放在身边会是一个巨大的分散注意力的来源,尤其是收到新信息和轻松访问社交媒体的时候。在自习室做学习的时候,把我们的手机放到另一个房间暂时消除提示也是有帮助的。
另一方面,如果学习和其他生产习惯是显而易见的,那么它们会变得容易。例如:
无障碍学习 —如果我们正在积极地学习 MOOCs 或其他在线学习材料,那么将我们的互联网浏览器设置为当我们重启会话时总是立即重新打开这些学习标签,可以大大有助于我们将注意力转移到在线学习上。
显而易见的有效线索 —如果我们设定了每天阅读一本特定的教科书的目标,那么不断地将它放在房子里一个容易拿到且显眼的角落(而不是藏在书架上)会经常提醒我们一整天都在学习。
5.4 关注过程,而不是产品
第四个建议是关于心态 —在学习课程开始时有负面情绪(恐惧/厌倦/痛苦)是正常的,即使我们喜欢这个主题或对工作充满热情,也许是因为这个主题或工作很难。然而,这只是取决于我们如何处理感情。一个好方法是专注于过程——咬紧牙关,开始行动,我们会很容易适应工作流程。相比之下,我们不想把注意力放在产品** — 上,而是专注于完成任务,判断自己是否以及何时完成任务。以这种方式,开始并继续我们的任务/学习会不那么痛苦。**
5.5 知道下一个目标(习惯堆积)
第五点是关于目标设定的。尽管我们尽了最大努力,拖延的暗示可能潜伏在我们一天中的每个角落。通常在我们完成手头的任务后,我们会愉快地休息一下。这没什么,但有时我们最终会有一个长的休息。这是因为我们没有一个明确的目标,知道下一步该做什么是有成效的,当我们最终制定了一个计划时,从放松的快乐中抽离出来会变得很痛苦。结果——拖延症又开始了。因此,有一个的心理结构和活动时间表(包括休息时间)来分配你一天的时间对保持高效非常重要。事实上,一旦我们养成了有意识地(精神上和积极地)将几项生产活动(与休息时间交替)堆叠在一起的**习惯,这就变成了学习和生产效率的良性循环。****
5.6 拥抱不适,让意志力成为一种习惯
(这是 最重要的 提示!最后我不得不承认:如果你正在读这篇文章,很可能你想成为更好的自己,并有效地学习。然而,有时拖延的根源可能源于我们对生活的态度,也就是说,我们在生活中寻求太多的安慰。在这里,我们必须认识到,刻意的学习是成长的一部分,这本身可能会令人不舒服。
我们中的一些人在学习中寻求新奇感,这是真的——学习应该是有趣的,有动力的。然而,整个过程并不总是阳光明媚。当学习的动力不强时(也许因为主题是枯燥的),接受某种形式的不适(尤其是在开始阶段)是绝对必要的,可能会让我们在学习的旅途中走得更远。为此,我们需要训练大脑,让不适(运用意志力)成为一种习惯。
最近的研究表明,大脑是可塑的,像力量训练一样,意志力(由前额叶皮层控制)就像肌肉一样可以随着时间的推移而发展。难怪一些研究发现,像慢跑这样的定期锻炼是一个重要的习惯——一个同步提高生活中其他方面自制力的良好习惯。因此,我们可以通过一次专注于一个领域来培养意志力——开始养成一贯的习惯,如健康饮食或定期锻炼。慢慢地,但肯定地,这些小胜利会像滚雪球一样越滚越大,我们可以更容易地鼓起意志力开始富有成效的学习课程。
附加提示 :养成每天早上整理床铺的习惯,尤其是在我们刚醒来的时候,可以成为一个很重要的习惯,研究表明,这可以提高意志力和一整天的工作效率。
最后,也是最重要的一点,个人经验告诉我,除了养成基本习惯,还有 3 个重要因素可以让意志力成为一种习惯:
- 日常活动的自我意识——有时我们会分心,全神贯注于突然需要我们注意的即兴活动。因此,我们失去了平衡,失去了动力。在我们每天的循环中练习对活动和干扰的正念可以防止我们迷失目标。
- 明确的每日和短期目标— 对我们的目标只有模糊的想法是不够的。练习花一些时间思考它们,并找出一个清晰的原因,为采取行动提供强大的动力。
- 早上培养积极进取的心态——当我们以正确的方式开始新的一天时,成功了一半。除了有规律的晨间活动(整理床铺等),通过在早上想象进展和练习专注于我们的目标来为这一天做宣传可以给我们的意志力一个显著的提升。
附加提示 :每天进行 5 分钟的呼吸冥想练习,对自我认知和增强意志力有很大帮助。冥想时,头脑会感到惊奇,这是完全正常的;当你意识到这一点时,试着把注意力带回呼吸。 除了 之外,我们也可以在冥想时练习放慢呼吸,因为研究表明,降低我们的呼吸频率对应着更好的自我控制。
“冥想不是要摆脱你所有的想法;而是学会不要太沉迷于其中,以至于忘记自己的目标是什么。”
-凯利·麦克戈尼格尔,意志力本能
P.S. 冥想会不舒服,有一些练习者在练习后出现负面精神状态的报道。因此,我一般只推荐短时间的冥想练习(严格来说是 5-10 分钟),从个人经验来看,这是非常有益且无害的。
**https://influencedigest.com/productivity/how-to-make-willpower-a-habit/
6.超级充电学习——教授你所知道的
在我们理解了所有这些深入学习和克服拖延症的技巧之后,还有最后一点可以为我们的学习充电——教!
许多人认为学习是一个内部的、孤立的过程,在这个过程中,你会退回到自己安静的、私人的空间去培养知识。很多时候,这是真的,因为你需要一个尽可能少分心的隐蔽环境来吸收信息。尽管如此,学习还有另一个互动的维度——那就是边学边教!除了在学习中进行回忆,教学的想法使你集中注意力,并迫使你尽可能系统地解释概念,从而加强我们对、的清晰理解,这是另一种形式的有意练习。支持把教学作为一种学习形式的一个著名范例叫做费曼技巧:
https://blog.doist.com/feynman-technique/
其中,掌握一门学科有 4 个步骤,核心是把这门学科教给别人:
- 选择一个概念学习
- 教给自己或者别人。
- 如果遇到问题,请返回源材料
- 简化你的解释,创造类比。
然而,教别人的好方法是什么,尤其是当你没有听众的时候?很简单,最简单的方法之一就是写一篇文章,在媒体上!随着你写作技巧和清晰解释概念的能力的提高,你开始获得追随者,并从 Medium 获得月费,这给了你进一步的激励(对习惯的奖励)来与世界分享你的知识。这种强大的良性循环可以大大增强你的学习能力。
7.数据科学中的学习管道
虽然这篇文章通常对不同学科的所有学习者都有好处,但它是为有抱负和正在工作的数据科学从业者而写的。将这些见解有效地应用到学习中,人们可以对数据科学中成功的持续学习充满信心。在这里,让我以我对初露头角的数据科学家和机器学习工程师的 MOOC 学习管道的几点见解来结束我的发言。
7.1 基本工具——Python 和 SQL
6.1.1 Python
- 2021 年完成 Python Bootcamp 从零到 Python 中的英雄 (Udemy)
- 现代 Python 3 训练营
- Python 3 编程专门化 (Coursera)
- 数据结构与算法— Python (Udemy)
6.1.2 结构化查询语言(SQL)
- 数据科学大师 SQL(Udemy)
- 学习数据科学专业的 SQL 基础知识 (Coursera)
7.2 机器学习
- 2021 用于机器学习和数据科学的 Python 大师班 (Udemy)
- 机器学习 (Coursera)
- 机器学习专业化 (Coursera)
7.3 深度学习
- 深度学习专业化 (Coursera)
- 深度学习。AI Tensorflow 开发者 (Coursera)
- Tensorflow:高级技术专精(Coursera)
- Tensorflow:数据和部署专业化(Coursera)
7.4 附加主题
- AI for every one(Coursera)
- Git 和 Github Bootcamp (Udemy)
- Linux 精通:掌握 Linux 命令行 (Udemy)
- Docker 和 Kubernetes:完全指南
- 亚马逊 AWS 上面向初学者的云计算介绍 (Udemy)
- 终极 AWS 认证云从业者(Udemy)
8.最后的想法
唷,祝贺你来到这里!在这本全面的指南中,你获得了关于有效学习的宝贵而详尽的见解。你在以下方面学到了很多:
- 学习是如何发生的(记忆、形成组块和主动回忆)
- 大脑如何运作(分散和集中模式)以及如何利用它们进行最佳学习
- 启动大脑学习——睡眠、锻炼、饮食和环境
- 克服拖延持续学习(详细指南)
- 作为一种学习形式的教学
为了充分利用这些知识,你应该经常重温本指南,并实践有效学习的习惯。将来,如果你学到了新的东西,我会鼓励你通过在像 Medium 这样的渠道上写一篇博客来转发它。注意,通过写这篇文章,几乎完全用我自己的话,我练习了费曼技巧,这让我学得更深。
最后,感谢学会如何学习!请在评论区留下你的想法,并与任何希望更有效学习的人分享这些知识!我期待您的参与,请在 LinkedIn 关注我。
干杯!_/_
感谢阅读!如果您喜欢这些内容,请在 中的 上阅读我的其他文章,并在LinkedIn上关注我。
支持我! —如果你没有订阅 Medium,并且喜欢我的内容,请考虑通过我的推荐链接加入 Medium 来支持我。
https://tanpengshi.medium.com/membership **
度量存储的简要历史
一个内部数据产品如何成为下一个热门数据工具
术语“度量存储”在数据社区引起了一些轰动。“时髦”的东西通常是新的,对吗?实际上,与商业智能(BI)非常相似,度量存储的概念比您想象的要早。
像 Airbnb 、优步和 LinkedIn 这样的大型科技公司是一些在指标层变得酷之前就看到其价值的公司的例子。我甚至在一篇过去的媒体文章中谈到了我在 Airbnb 的经历。这些公司发现,为了了解他们的业务、进行实验和分享见解,他们需要一个集中的位置来存放指标定义、治理和上下文。数据和分析项目只有在人们信任数据,并且在整个公司范围内,从小团队到最高管理层,每个人都同意如何报告数据的情况下才能发挥作用。
在本文中,我们将讨论指标存储概念的起源,公司过去是如何处理这项技术的,以及指标存储的未来。现在,让我们踏上一次通往过去和未来的旅程。

有人看见布朗医生了吗?我们要一直追溯到 2000 年。 Photo 由乔尔·穆尼斯在 Unsplash
度量存储的起源:事件的时间表
度量存储的想法来自一个常见的问题,即杂乱的数据。你已经听过一遍又一遍了,但是随着对更多数据需求的增长,复杂性也在增加。
用不了多久,您的度量逻辑就会散落一地——这是数据分析师最糟糕的噩梦。

此图显示了指标逻辑如何轻松地分散在数据分析工具中。作者照片。
那么我们是怎么到这里的呢?

导致指标存储创建的高级事件时间表。作者照片。
数据建模的早期(1996 - 2010 年)
在自助式 BI 出现之前,公司严重依赖人员和流程来管理消费数据集。大多数 BI 工程师可用的工具都是围绕 cron 作业和 SQL 语句构建的,这使得编排数据管道变得非常困难。存储大量数据的成本也很高,因此人们必须小心提取的内容,小心转换,然后加载小数据集以供使用。这需要非常小心地正确管理,以便数据立方体可以用于推动业务决策。为手动数据输入编排一系列手动 Excel 表格编辑是早期 BI 从业者面临的另一个挑战,IT 团队被召集来帮助构建技术步骤和流程以保持同步。

在自助式商业智能出现之前,存储大量数据的成本很高,因此团队必须小心提取的数据,进行仔细的转换,然后加载小数据集以供使用。
自助式商务智能工具出现(2010 年初)
2010 年后,交互式仪表盘席卷了整个行业,为数据团队和业务团队之间的更多协作铺平了道路。随着交互式报告成为数据分析师团队的主要产出,以帮助业务利益相关者消费他们自己的数据,出现了“仪表板即服务”的想法。但这也导致了复杂性,因为分析师的时间有限,无法以新请求到来的速度完成数据面包线。更糟糕的是,大多数公司有不止一个 BI 工具,因为最终用户更喜欢需要重复逻辑的不同界面。

2010 年后,交互式仪表盘席卷了整个行业,为数据团队和业务团队之间的更多协作铺平了道路。
这是像 Airbnb 这样的科技公司开始构思一个指标库的想法,用于报告和准备指标进行分析。
数据平台的进步(2015 - 2020 年)
大约在 2015 年,OLAP 立方体的概念过时了,因为人们努力为他们的 BI 工具维护这些漂亮干净的切片和骰子界面。相反,数据平台旨在接收、处理、分析和呈现不同的数据,将不同的数据集中到一个地方,以便更容易直接从数据仓库进行管理。

大约在 2015 年,OLAP 立方体的概念过时了,因为人们努力为他们的 BI 工具维护这些漂亮干净的切片和骰子界面。
但是,随着越来越多的公司投资于数据,这些工具变得更加先进,在某些情况下更加专业化。不是所有的都可以存在于单一的数据平台中。数据管道的每一步都变得更加精简和专业化,工具变得更加分散。这使得数据治理变得复杂而难以控制。
专业化催生新的数据应用(2017 年至今)
最近几年,这种专门化走得更远,产生了支持新应用的新颖工具。这导致了对治理的需求,以及围绕可见性、沿袭和业务运营健康的新挑战。不一致的数据集让分析师费力地通过数百行 SQL 语句来确保他们提供的答案是准确的。
如今,团队从他们的数据仓库或数据湖中提取数据,并将这些数据带入各种工具,包括多种商业智能和实验平台。

近年来,我们简化了流程的每一步,因此,我们现在拥有的工具比以往任何时候都多。这导致了两大挑战:不同的数据建模实践和治理挑战。
指标商店(2020 -未来)

现在,度量存储已经成为他们自己的 SaaS 产品,最终恢复了 OLAP 立方体的治理,同时减少了数据重复和逻辑重复,并支持新的数据应用程序。

度量存储(或度量层)位于组织的数据仓库和其他下游工具之间。
这意味着,所有组织现在都可以使用曾经为大型科技公司保留的复杂技术,这些公司可以集中资源,利用内部工具实现这些新的数据应用程序。让我们来谈谈那些成功创建了内部度量工具的科技公司,以及他们学到了什么。
获得牵引力:科技公司如何建立内部指标商店
由于像 Transform 这样的度量存储直到最近才开始商业化,组织用内部工具解决了不同度量定义的问题。这是一个需要解决的复杂问题,许多试图建立指标平台的公司都失败了。不幸的是,关于这些经历的博客文章并不多,但是感兴趣的人可以从这些经历中学到一些东西。
成功构建这些工具的组织通常是拥有大型创新内部数据团队的大型科技公司。他们比更广泛的行业更早地经历了治理和不断增长的数据多样性和消费点的挑战,并且拥有寻求内部工具来解决这些问题的资源。
Airbnb: Minerva 公制平台
Airbnb 的 metric 平台 Minerva 是内部 metric 商店的一个流行例子。Airbnb 的度量工具可以追溯到 2014 年,当时该公司开始扩大 A/B 测试的规模。该公司在称为“core_data”的最关键数据表的高质量数据建模方面进行了大量投资,但分析师仍然花费太多时间来收集数据集进行分析,并且经常难以报告相同的数字。为了解决这个问题,他们创建了 Minerva,它“将事实和维度表作为输入,执行数据反规范化,并将聚合的数据提供给下游应用程序。”
优步:uMetric 解决公制差异
优步明白指标在他们的决策中起着至关重要的作用。优步的一个众所周知的指标是司机接受率,他们将其表示为“司机接受的请求总报价”,这是他们客户和司机体验的关键。但是确定这些类型的度量标准只是难题的一部分。由于数据民主化程度的提高,优步的数据团队也看到了一个共同的挑战——团队都有自己的数据管道和消费工具,导致完全不同的指标逻辑和价值。优步创建了 uMetric ,目标是“构建工程解决方案来解决业务关键指标的差异。”
LinkedIn:单一真实来源的统一度量平台
LinkedIn 在他们的工程博客上写了一篇关于他们的统一指标平台的文章,该平台“通过提供集中的指标处理管道(即服务)、指标计算模板、一套工具和流程,以一致、可信和简化的方式促进指标生命周期,成为 LinkedIn 所有业务指标的唯一真实来源。”
Spotify:扩展实验和分析
这些公司并不孤单。Spotify 还宣布了他们的新实验平台,其中包括一个指标目录,可以运行“SQL 管道将指标导入数据仓库,从那里可以将数据以亚秒级延迟提供给用户界面和笔记本电脑。”⁴
这些组织正在努力平衡更广泛的数据访问和跨所有工具的标准化度量逻辑,从他们那里可以学到很多东西。
未来:新技术正在铺平道路
直到最近,如果组织想要一个集中的度量位置,他们必须自己构建。这需要大量的基础设施投资,有时需要代表工程团队工作数年。
现在,度量存储作为现代数据堆栈中的一个独立类别,正在获得越来越多的关注。这项技术提供了一些关键优势:
- 指标成为数据的语言:您可以在一个地方构建指标逻辑并支持各种数据模型。度量标准已经是业务的语言,那么为什么不使用它作为您如何与洞察互动的模型呢?
- 消除事实的第二来源:整合你所有的度量标准,这样你所有的度量标准在所有的上游和下游工具中都是一致的。
- 围绕指标建立一个知识中心:为您的指标添加上下文,这样数据团队就不会一遍又一遍地回答相同的问题。所有的问题和上下文都已经准备好,可供数据团队和业务用户访问。
我认为未来每个人都可以使用 metrics stores,无论您的组织规模或行业如何。
[1]: Amit Pahwa,Cristian Figueroa,Zhang,Haim Grosman,John Bodley,Jonathan Parks,Maggie Zhu,,Robert Chang,Shao Xie,Sylvia Tomiyama,。(2021 年 6 月 1 日)。Airbnb 如何规模化标准化公制计算 https://medium . com/Airbnb-engineering/Airbnb-Metric-computing-with-Minerva-part-2-9 AFE 6695 b486
[2]:王晓东,,孟,俞,。(2021 年 1 月 12 日)。迈向公制标准化的旅程 https://eng.uber.com/umetric/
[3]:领英工程。统一指标平台(UMP)。 https://engineering.linkedin.com/unified-metrics-platform
[4]:约翰·里德堡。(2020 年 10 月 29 日)。 Spotify 的新实验平台(第一部分) https://engineering . atsspotify . com/2020/10/29/spotifys-New-Experimentation-Platform-Part-1/
Azure 机器学习工作室简介
Azure ML Studio 的主要概念概述:在不到 10 分钟的时间内理解基础知识

作者图片
Azure 机器学习工作室是微软在 Azure 云中机器学习计算的中心联系点。它是微软机器学习工作室 Classic 的继任者,将于 2024 年退役。多年来,微软在其 Azure ML Studio 中扩展了功能和可能性的数量,并且进展仍在继续。微软试图使算法和实验的创造和工作尽可能简单。因此,与硬编码算法相比,Azure ML 的自动化和点击界面功能会越来越多。但是现在,让我们看看工作室的主要组成部分:
Azure ML Studio 菜单分为三个主要部分:、作者、资产和管理。部分作者处理代码的创建和机器学习过程的设置。资产是在作者部分创建和存储的资源,比如在设计器中创建的管道。它控制资源的整个工作流程,从通过管道输入数据集到输出的端点(例如,通过 REST API 连接到真实系统)。管理部分用于系统的后台。它包括计算集群和实例、存储数据集的数据存储以及与其他系统的集成。不知何故,这是看不见的一层。
作者
- 笔记本:这个部分包含了笔记本的创建、编辑和运行的所有内容。
- 自动化的 ML: 这个部分是为了简单的自动化和不同机器学习技术的比较。您可以很容易地输入一个数据集,并在不同的性能指标下查看各种 ml 技术的性能。
- 设计器:设计器是一个拖放工具,用于创建从数据集输入到评估的 ML 工作流。它很容易使用,有许多功能,可以通过文本搜索或点击左边的箭头和标题找到。

作者图片
资产
- 数据集:顾名思义,这个部分用于注册和管理你的数据集。
- 实验:实验提供了使用不同设置(如超参数)测量运行和控制指标的可能性。
- 管道:通过设计器,可以创建管道并在管道段内注册,以控制运行次数、运行时间、名称等。
- 型号:用于注册和管理您的型号。
- 端点:在这里,您可以控制和查看所有端点,通过这些端点,您可以让其他系统和应用程序使用您的模型/算法。
https://medium.com/@hucker.marius/membership
经营
- Compute: 这个部分允许您设置、启动和停止计算集群和实例,以及运行您的模型的推理集群
- 数据存储:包含数据集、模型、笔记本以及所有文件和数据的存储。
- 数据标注:老实说,我不明白为什么这个部分在管理下,而不在作者下。我认为数据标签更适合第一部分,因为它与数据处理有关,但也许它指的是“管理您的数据”,这就是为什么它在“管理”之下。在该部分,您可以获得很大的可能性和支持来标记您的所有数据示例。
- 关联服务:用于其他服务和系统的集成。
在对每个菜单点和基本类别的第一次简要解释之后,我现在将进一步看看在每个类别中你能实现什么。让我们从笔记本开始:
笔记本电脑

作者图片
在“笔记本”部分,您可以选择您在“计算机”下创建的计算机来运行您的笔记本。如果还没有可用的,您需要创建一个计算资源。在左手边你可以找到文件夹结构,上面你可以看到四个蓝色的小图标。左边的是打开一个终端,通过控制台管理你的系统。控制台图标旁边的加号是创建新文件夹或文件所必需的。剩下的就很简单了。双击打开您的笔记本,键入代码和文本,并通过按 enter(和 shift)打开您的单元格或笔记本。
自动化 ML

作者图片
AutoML 非常适合不使用代码创建模型。这是一个无代码的用户界面,你可以简单地点击通过一切。当然,你不能构建最复杂的模型,但是对于标准模型,获得第一次评估或者比较不同的模型是非常有用的。正如您在下图中看到的,您可以选择主要指标、运行模型的迭代次数、阈值、交叉验证的数量、应该阻止(因此不使用)的算法,以及其他一些设置。

作者图片
一如既往,您可以使用的算法取决于任务类型,因此您可以轻松地在回归、分类和时间序列之间进行选择。对于一些深度学习,可以勾选深度学习复选框。此外,可以自动选择特征或定制特征。要了解更多信息,这里有一个完美的指南。

部署模型后,您可以看到模型的详细信息、解释和指标(如下图所示)。

作者图片
设计师&设计师模块

作者图片
设计器是用来设置机器学习管道的拖放工具。对于设计者来说,目前大致有 60 多个模块,可以分为数据转换、数据输入和输出、特征选择、统计函数、回归、聚类、分类、模型训练和模型评估/评分。如你所见,这些设计模块涵盖了整个 ML 周期。所以你可以不用编码就能建立模型。您只需选择模块并连接它们,您的管道就可以进行测试和干预了。了解更多关于模块的信息。

作者图片

作者图片

作者图片

作者图片

作者图片

作者图片

作者图片

作者图片

作者图片
Azure ML 中的资产部分
数据集
顾名思义,这个部分是设置数据集的部分。重要的是要知道,您的数据不是传输的,而是从现有存储中引用的。因此它不会导致额外的存储空间成本。有两种类型的 Azure 数据集:文件数据集和表格数据集。文件数据集是指存储中的文件,并基于这些文件(如音频文件或图像)创建数据集。使用文件数据集时,必须已经为模型准备好数据。
表格数据集更加结构化,当然还有 excel 和 CSV 文件中的表格。这种类型的数据非常适合基于文本的数据。
实验
这是您可以开始 ML 模型运行的部分。实验提供了使用不同设置(如超参数)测量运行和控制指标的可能性。在实验部分,您可以跟踪指标,测试不同的超参数,并运行一些测试来检查您的模型性能是否有所提高。

管道
您通过设计器构建的管道可以在“管道”选项卡中进行编排。您可以在那里看到您的管道运行,管道创建的时间,管道运行所需的时间,它们的状态,以及它属于哪个实验。管线运行旁边有“管线草稿”选项卡,用于查看您只计划了哪些管线而没有运行。求更多
模型
在模型部分,您可以编排和管理您的所有模型。对于每个模型,您可以看到细节,例如参数、工件、端点以及模型所使用或连接到的数据集。在工件部分,您可以看到模型的文件,比如 config 和 score.py。
端点
端点部分实际上是提供 web 服务和 API,以便其他应用程序、公司和网站可以使用您培训、构建和优化的模型和服务。在这里,您可以管理端点、密钥和与它们连接的 URL,当然,这是您可以允许干扰的地方

管理 Azure ML 的部分
计算
计算部分为您提供了选择模型或服务所需的计算资源的可能性。微软提供各种计算资源,从只有几 GB RAM 的小型计算机到能够管理几乎所有型号的大型计算机。当然,价格也各不相同。也可以在 CPU 和 GPU 之间选择。“计算”部分中的选项包括计算实例、训练群集、推理群集和附加计算。

来源:https://azure . github . io/azure ml-cheat sheets/docs/cheat sheets/python/v1/compute-targets/
数据存储
包括存储数据集、模型、笔记本以及所有文件和数据的存储器。您可以在此部分设置和管理存储。有不同种类的数据存储。在微软网站的下图中,你可以看到所有这些。例如,您可以在您的 Azure 门户中设置类型 Azure Blob 存储,并将该存储连接到它。

来源:https://docs . Microsoft . com/en-us/azure/machine-learning/how-to-access-data
结论
Azure ML Studio 是一个真正强大的平台,正在被永久开发以进一步改进和促进数据科学家和数据工程师的工作。我相信在未来他们会进一步自动化流程,包括越来越多不同的和更复杂的算法。如果你还没有尝试过 Azure,有一个 30 天的试用阶段来测试资源和玩一点点。我可以推荐你深入 Azure 并了解一点它——它是许多基于机器学习的系统和应用的未来。
https://medium.com/@hucker.marius/membership
[阅读更多像这样的](http://Read more like this)
上下文解释网络简介
CEN 学会了同时预测和解释

随着人工智能和机器学习算法在现实世界中的稳步采用(麦肯锡,2020 年),需要更适当的方法来解释或解释算法的预测。这些算法影响着金融、医疗和刑事司法领域的关键决策。信誉度、医疗诊断或司法判决可以在很大程度上由机器来确定(Brotcke,2020;Rudin,2019)。在许多情况下,人类无法完全解释或说明一个关键决策是如何做出的。
为了解决这种类型的算法不透明性,来自卡内基梅隆大学和谷歌的一小组研究人员提出了上下文解释网络(CENs)。与以前的解释方法相反,CEN 学会了同时预测和解释,提供了定性的见解,并经常改善原始预测的性能,而不会产生额外的计算开销(Al-Shedivat 等人,2020)。
压榨石灰
解释模型的概念并不新鲜。2016 年,来自华盛顿大学的研究人员引入了本地可解释的模型不可知解释,俗称 LIME (Ribeiro et al .,2016)。LIME 建议用文本或视觉人工制品来补充算法,这可能会提供对其行为的定性理解。更广泛地说,目标是在数据通过算法时提供人类可理解的数据表示。例如,用于理解语言的算法(例如,Google Translate)将单词表示为高维向量空间中的点(Bengio 等人,2006)。一种更易于人类理解的表示是一个简单的二进制向量,表示一个单词的存在或不存在(Ribeiro 等人,2016)。
虽然 LIME 和其他不可知论者的解释方法可能会提供一些透明度,但解释是独立学习的,并不能保证是潜在预测的基础。这些缺点会导致对原始算法方法的错误解释或曲解(Al-Shedivat 等人,2020)。类似地,流行的不可知论方法容易被仅仅通过引入噪声或随机化数据来利用(Al-Shedivat 等人,2020;金等,2017)。相比之下,语境解释网络没有继承前人的任何固有缺陷。
将复杂性转移给编码器

CEN 建筑的例子(Al-Shedivat 等人,2020 年)
一般来说,CEN 的工作分两步走。首先,原始算法的输入子集生成由领域专家定义的简单概率模型(例如,稀疏线性模型)(Al-Shedivat 等人,2020)。然后,将生成的模型应用于另一个输入子集,并生成预测。简而言之,CENs 可以使用强大的编码器来表示复杂的模型类。通过将复杂性卸载到编码过程,在变量和预测之间得出简单的结论(即线性模型)(Al-Shedivat 等人,2020)。换句话说,所有的复杂性都浓缩到适合问题背景的高级概念中。
“通过使用强大的编码器,CENs 可以表示复杂的模型类。与此同时,通过将复杂性纳入编码过程,我们实现了解释的简单性,并可以根据感兴趣的变量来解释预测。”
-谢迪瓦等人,2020 年
衡量成功
该团队主要着手完成两个目标。定义高性能、内在可解释的深度学习架构,并证明不可知论者的解释如何不足。当应用于常见用例(例如,计算机视觉、情感分析)时,通过 CEN 与替代方案的实证分析收集证据。
该团队使用两个指标来衡量他们的成功——相对于基线的预测性能和定性见解。在一个例子中,使用高度复杂的 VGG-F 嵌入对分类器(使用卫星图像作为输入)进行预训练。选择逻辑回归和多层感知器(MLP)作为基线。CEN 模型的表现优于简单的逻辑回归和 MLP,同时还能清晰地识别出一个有背景的、有意义的解释(Al-Shedivat 等人,2020)。
最终,实验证明了从复杂的低级输入到领域专家预先定义的高级有意义变量(例如,分类特征)的映射是可能的。该算法的预测与人类可解释的映射相结合,有效地使其变得透明。该团队设法在不产生额外开销的情况下实现了这一点,并且在某些情况下提高了性能。
CENs 与“黑箱”问题
用 CEN 代替像莱姆这样的不可知论解释的好处是显而易见的。额外的性能、零开销和线性可解释性使 CENs 成为一种实用的替代方案。但是,CENs 可能只能部分解决“黑箱”问题,而事后解释通常是用来解决这个问题的。
在两种不同的情况下,算法被称为“黑盒”。第一种是当其基础数学计算超出人类理解时(Rudin,2019)。第二种情况是算法是专有的,方法是秘密的。
前者由 CENs 优雅地提出,至少和它的前身 LIME 一样具有可解释性。这项研究没有直接涉及后者。CEN 架构输出一个完整的解决方案,不会用于事后解释专有解决方案。
使用专有解决方案的理由是充分的。原生机器学习应用需要多学科能力、基础设施以及持续的监控和维护。依靠第三方往往不那么令人望而却步。明显的(但并非无关紧要的权衡)是不透明。很难确保它们的可靠性、稳健性和不存在不良偏差(Al-Shedivat 等人,2020 年)。像许多其他新兴的可解释性方法一样,CENs 打算用透明性来取代不透明的专有解决方案。
尽管这项研究非常清楚地证明了它的价值。没有直接对照其他事后模型不可知的方法对 CEN 进行评估。从业者可能会接受 LIME 通常代表许多其他广泛使用的解释方法,但并不是所有的解释模型都适合所有类型的模型(Hall 等人,2021)。尽管如此,这项研究并没有消除它和类似方法之间的歧义(Guidotti 等人,2019 年)。
其他当代方法呈现出许多与 CEN 相同的好处。例如,杜克大学的一个研究团队提议在算法的现有训练过程中添加一个特殊的原型层。最终的预测是与人类可解释的原型相似性的加权和(Chen et al .,2019)。2017 年,谷歌引入了概念激活向量(TCAV)测试,该测试分享了将复杂的低级功能映射到有意义的高级概念的想法(Kim et al .,2017)。这两个例子都可以作为有用的基准或 CEN 的适当替代。
适应性强且高效
在我看来,语境解释网络架构是对可解释性研究的一个有意义的贡献。这种方法(以及其他类似的方法)使得透明度和准确性之间的权衡变得不那么重要。CEN 对现有算法的适应性和它的计算效率使它成为重视算法透明性的从业者的一个可访问的选项。
参考
谢迪瓦特 M,杜比 A,兴 EP。2017.上下文解释网络。arXiv [csLG]。[访问日期:2021 年 1 月 28 日]。https://www.jmlr.org/papers/volume21/18-856/18-856.pdf.
Bengio Y,Schwenk H,Senécal J-S,Morin F,Gauvain J-L. 2006 年。神经概率语言模型。《机器学习的创新》。柏林/海德堡:施普林格出版社。第 137-186 页。
布罗特克湖 2020。在 AI/ML 时代修改模型风险管理实践。金融机构风险管理杂志。[访问日期:2021 年 2 月 6 日]。https://hs talks . com/article/5661/modificing-model-risk-management-practice-in-the-er/。
Goodfellow I,库维尔本吉奥 Y,2016 年深度学习。英国伦敦:麻省理工学院出版社。
Guidotti R,Monreale A,Ruggieri S,Turini F,Giannotti F,Pedreschi D. 2019。解释黑盒模型的方法综述。计算机监测。51(5):1–42.
H2O 无人驾驶人工智能的机器学习可解释性。H2O . ai[2021 年 2 月 8 日访问]。http://docs . H2O . ai/driverless-ai/latest-stable/docs/booklets/mli booklet . pdf
Kim B,Wattenberg M,Gilmer J,Cai C,Wexler J,Viegas F,Sayres R. 2017。特征归因之外的可解释性:概念激活向量的定量测试(TCAV)。arXiv [statML]。http://arxiv.org/abs/1711.11279.
Lundberg S,Lee S-I. 2017。解释模型预测的统一方法。arXiv [csAI]。http://arxiv.org/abs/1705.07874.
Rudin C. 2019。停止解释高风险决策的黑盒机器学习模型,而是使用可解释的模型。纳特马赫智能。1(5):206–215.
Simonyan K,Zisserman A. 2014 年。用于大规模图像识别的非常深的卷积网络。arXiv [csCV]。http://arxiv.org/abs/1409.1556。
2020 年的 AI 状态。Mckinsey.com。[访问日期:2021 年 2 月 6 日]。https://www . McKinsey . com/business-functions/McKinsey-analytics/our-insights/global-survey-the-state of-ai-in-2020。
聊天机器人一瞥
自然语言处理笔记
探索聊天机器人分类

照片由 Jeffery Ho 在 Unsplash 上拍摄
什么是聊天机器人?
我不会定义聊天机器人是什么,我会把它留给一些可靠的来源…
“聊天机器人是一种软件应用程序,用于通过文本或文本到语音进行在线聊天,而不是提供与真人代理的直接联系”来源 : 维基百科。
“聊天机器人是一种互动系统,允许用户用自然语言进行互动。他们通常通过文本进行互动,但也可以使用语音界面。
“在最基本的层面上,聊天机器人是一种模拟和处理人类对话(无论是书面还是口头)的计算机程序,允许人类与数字设备进行交互,就像他们与真人进行交流一样”来源 : 甲骨文。
目标导向&闲聊
聊天机器人可以分为两大类:面向目标的对话或闲聊。
面向目标的对话框
作为一种习惯,人类倾向于参与谈话以达到某种目的。为了在对话结束时达到既定目标,一方会从另一方寻求相关信息,因此,寻求信息的一方需要确切知道他们在对话结束时想要达到的目标。当订购比萨饼或预订火车票时,可以找到这些类型的对话代理的例子。
由于一个关键方面,可推广性和可伸缩性受到显著阻碍。他们的定义。基于它们的定义,面向目标的对话系统通常是特定领域的,这意味着它们需要特定领域的知识。
闲聊
如果我们所有的对话都像面向目标的对话所描绘的那样是事务性的,那就奇怪了;人类也有缺乏结构和方向的一般性对话。这些互动形式自由,观点鲜明,涵盖了一系列不同的主题。
正如你可能已经猜到的那样,目标的缺失使得聊天对话代理成为一个非常难以解决的任务,因此它被认为是一个展现出巨大希望的未来。例如,想象一下创造一个聊天机器人的想法,它可以与孤独和抑郁的人进行积极的对话——它甚至可以提供护理,但我超越了自己。
进一步分解它…
基于流量的机器人
基于流的对话代理遵循预定义的对话流;对话以类似流程图的方式映射,因此每次查询方向系统发送请求时,机器人都会理解并记录整个对话过程中的信息,这将允许它始终提供适当的响应。
比方说,我们用基于流的对话代理订购比萨饼。用户会逐渐建立他们喜欢的比萨饼来表达他们的偏好,因此,要求机器人能够理解并记住用户过去说过的所有事情,并询问相关问题来帮助用户达到订购比萨饼的目标。
开放式机器人
开放式对话代理主要用于娱乐目的,这意味着这些类型的机器人的一个重要方面是,它们能够与用户就一系列主题进行对话,而不必保持特定的方向或流程。这些类型的代理在不利用预先存在的模板或固定问答对的情况下进行对话,并且非常擅长从一个主题转移到下一个主题。
例如,你可以问机器人它在做什么,它会回答并问你同样的问题。当你回复它的回复时,它会要求你根据你的回复提供更多的信息,等等。
常见问题机器人
FAQ 对话代理具有一组固定的响应,它们可以在确定用户发送的查询的含义时使用这些响应。在这些代理中,来自用户的响应不依赖于先前的响应。
在我题为 的文章中,我用深度学习 用 Python 构建了一个非常简单的聊天机器人。请务必查看它,以了解关于这种类型的对话代理的更多信息。
包裹
聊天机器人开启了一个新的科技时代;这些对话界面使企业能够简化人与他们必须提供的产品或服务之间的交互,从而显著改善客户体验——这对企业来说也很好,因为企业仍然可以以客户为中心,但价格更低。
感谢您的阅读!
如果你喜欢这篇文章,请通过订阅我的每周简讯与我联系。不要错过我写的关于人工智能、数据科学和自由职业的帖子。
相关文章
**</4-data-related-books-ill-be-reading-in-april-efd06b367e35> **
解释人工智能的方法概述(XAI)
模型可解释性
如何设计一个可解释的机器学习过程

我知道这个话题已经讨论过很多次了。但是我最近做了一些关于可解释性的演讲(针对 SCAI 和法兰西创新),并且认为在这篇文章中包含我的一些工作会很好。可解释性对于机器学习中决策过程的重要性不再需要证明。用户要求更多的解释,尽管对可解释性和可解释性没有统一和严格的定义,但解释人工智能(或 XAI)的科学论文数量正在呈指数级增长。
正如你可能知道的,有两种方法来设计一个可解释的机器学习过程。要么你设计一个本质上可解释的预测模型,例如使用基于规则的算法,要么你使用一个黑盒模型并添加一个代理模型来解释它。第二种方法叫做事后可解释性。有两种类型的事后可解释模型:描述黑箱模型平均行为的全局模型,或解释个体预测的局部模型。如今,有几种工具可以用来创建事后可解释模型,其中大多数是模型不可知的,也就是说,它们可以独立于所使用的算法来使用。
我将介绍其中最常见的。本文基于 Christoph Molnar 的参考书:可解释机器学习。为了说明这些方法,我使用了常见的波士顿住房数据集。目标是以 1000 美元为单位的自有住房的中值价格。
部分相关图。
PDP 是一种全局的、与模型无关的解释方法。该方法显示了单个特征对黑盒模型预测值的贡献。它可以应用于数字和分类变量。首先,我们选择一个特征及其网格值(所选特征的范围)。然后,该特征的值被网格值代替,并且预测被平均。对于网格的每个值,都有一个对应于预测平均值的点。最后画出曲线。主要的限制是人类不能理解超过三维的图形。因此,我们不能在一个部分依赖图中分析两个以上的特性。

PDP 应用于随机森林的示例,该随机森林针对
要素 RM 和 LSTAT 的波士顿数据进行了训练。图片来自作者。
在这些图表中,我们可以看到每个住宅的平均房间数(RM)和人口中较低阶层的百分比(LSTAT)对中间价格的平均影响。比如,我们可以推导出房间数越少,价格中位数越低(这似乎是连贯的)。
函数plot _ partial _ dependency已经在软件包 scikit-learn 的检查模块中实现。
累积局部效应(ALE)。
ALE 也是一种全局的、与模型无关的解释方法。它是 PDP 的替代方法,当变量高度相关时,PDP 会出现偏差。例如,表示房间数量的变量 RM 与房子的面积高度相关。所以 RM=7.5 对于一个非常小的区域来说是不现实的。ALE 背后的思想是考虑与所选变量具有相似值的实例,而不是替换所有实例的值。当你平均预测时,你得到一个 M 图。不幸的是,M 图代表了所有相关特征的综合效应。为了更好地理解,我引用了来自可解释机器学习的例子:
“假设居住面积对房子的预测价值没有影响,只有房间数有影响。M 图仍然会显示居住面积的大小会增加预测值,因为房间数量会随着居住面积的增加而增加。”
ALE 计算预测的差异,而不是小窗口的平均值(例如,使用经验分位数)。在下面的例子中,我用 10 个窗口绘制了特征 RM 和 LSTAT 的 ALE。

ALE 应用于随机森林的示例,该随机森林针对
特征 RM 和 LSTAT 的波士顿数据进行了训练。图片来自作者。
垂直线代表考虑中的窗口。经验分位数被设计成使得 10 %的个体位于每个窗口中。不幸的是,没有设置仓数量的解决方案,这会严重影响解释。
为了便于说明,我使用了 Github 上的开源包 ALEPython 。
个体条件期望。
ICE 是一种局部的、与模型无关的解释方法。这个想法和 PDP 一样,但是我们没有画出平均贡献,而是画出每个人的贡献。当然主要限制和 PDP 一样。此外,如果你有太多的个人,情节可能会变得无法解释。

ICE 应用于随机森林的示例,该随机森林针对
要素 RM 和 LSTAT 的 Housing Boston 数据进行了训练。图片来自作者。
在这里,我们看到了每个住宅的平均房间数(RM)和人口中较低阶层的百分比(LSTAT)对 506 个观察值中的每一个的中值价格的影响。再次,我们可以看到房间数量越少,价格中位数越低。然而,RM 对 5 个人表现出相反的行为。应该仔细检查这 5 个人,因为他们可能表明数据库中有错误。
函数plot _ partial _ dependency已经在软件包 scikit-learn 的检查模块中实现。
局部可解释的模型不可知解释(LIME)。
LIME,顾名思义,是一种局部模型不可知的解释方法。这个想法很简单,从一个新的观察时间生成一个新的数据集,该数据集由扰动样本和底层模型的相应预测组成。然后,在这个新的数据集上拟合一个可解释的模型,该数据集通过采样的观察值与感兴趣的观察值的接近度来加权。

石灰应用于根据波士顿数据训练的随机森林的示例。选择的观察值是第 42 个,替代局部模型是岭回归。图片来自作者。
在这个石灰的视觉输出中,解释了对第 42 次观察的预测。在创建的数据集中,预测值的范围从 8.72 到 47.73。interset 观察的预测值为 24.99。我们可以看到,LSTAT 的值对预测有积极的影响,这证实了以前从 PDP 和 ICE 得出的结论。
沙普利加法解释(SHAP)。
SHAP 是一种基于 Shapley 值的局部模型不可知解释方法。Shapley 值来自博弈论,有几篇关于数据科学的文章和其他文章讨论了这个问题。在这里我只想提醒大家,在这个背景下,游戏是协作的,任务是预测,增益是预测和一个基线预测(通常是观测值的平均值)之间的距离,玩家是特征。然后,Shapley 值被用于在特征中从基线预测中分离预测偏移。因此,每个特征的实现都意味着预测的变化,积极的或消极的。SHAP 背后的想法是将沙普利值解释表示为一种附加特征归因方法。因此,它成为一个线性模型,其中截距是基线预测。下面应用于 XGBoost 的 SHAP 的图形表示说明了这些解释。

SHAP 应用于根据 Housing Boston 数据训练的 XGBoost 回归器的示例。选择的观测值是第 42 个。图片来自作者。
在这个图中,我们看到了每个变量对第 42 次预测的影响。这里,基线预测是 22.533。然后变量 INDUS=6.91 将预测移动-0.1,变量 B=383.37 将预测移动+0.19,以此类推。我们看到最大的变化来自变量 LSTAT 和 RM,它们是该数据集最重要的特征。
结论
局部模型比全局模型更常用。如果您想要模型的全局描述,最好使用本身可解释的预测算法。这些不是最精确的算法,但是它们允许对生成的模型进行整体描述。如果您想要一个非常准确的预测模型,通常您希望能够单独解释每个预测,而不仅仅是整个模型。例如,自动驾驶汽车的算法应该能够在发生事故的情况下解释它的每一个预测。
为了简洁起见,我省略了介绍特征重要性、特征相互作用、二阶 ALE、KernelSHAP 和其他方法。我只是做了一个简要的概述,向您展示了今天可以用来解释您的黑盒模型的内容。如果你想进一步了解这个话题,我推荐 Christoph Molnar 的书:可解释机器学习。
关于我们
Advestis 是一家欧洲合同研究组织(CRO ),对统计学和可解释的机器学习技术有着深刻的理解和实践。Advestis 的专长包括复杂系统的建模和时间现象的预测分析。
领英:【https://www.linkedin.com/company/advestis/】T4
对在线学习技术的广泛而实用的阐述
思想和理论
在线学习技术的概述,重点是那些对从业者最有效的技术。

在这篇博文中,我将深入探讨一下在线学习这个话题——这是深度学习社区中一个非常受欢迎的研究领域。像深度学习中的许多研究课题一样,在线学习在工业环境中有着广泛的应用。也就是说,数据对学习者顺序可用的情况非常普遍;请参阅动态电子商务推荐、设备上的学习场景甚至联合学习等示例,这些示例中可能不会同时提供完整的数据集。我的目标是从从业者的角度解决在线学习的话题,回答如下问题:
- 在线学习模式面临哪些问题?
- 在线培训模型的最佳解决方案是什么?
- 我应该期望一个在线训练的模型有什么样的表现?
这篇文章的目标是让从业者意识到在线学习空间中存在的选择,为模型必须从不断可用的新数据中学习的场景提供一个可行的解决方案。这种训练设置不仅消除了充满延迟的离线再训练过程(即,模型实时更新),而且更接近地反映了智能系统如何在我们周围的世界中学习——当人类学习一项技能时,他们不需要几个小时的 GPU 训练来利用他们的新知识!
该员额的结构如下。我将首先介绍在线学习的主题,并从一个广泛的角度,概述这个领域中已经确定和提出的问题和解决方案。尽管存在大量关于在线学习主题的出版物,但其中只有一些出版物提供了现代大规模架构和数据集的切实好处,而这些正是当今深度学习实践者最感兴趣的。在这篇文章中,我将概述大多数现有的在线学习方法,但我将特别注意那些能给实践者带来最佳“性价比”的方法,从而提供关于最有用的方法的必要背景。
什么是在线学习?
在这篇文章中,我将在线学习定义为一种训练场景,在这种场景中,模型永远不会同时获得完整的数据集。相反,该模型被顺序地暴露给数据集的部分,并被期望通过这样的部分暴露来学习完整的训练任务。通常,在暴露给数据集的某个部分之后,不允许模型稍后重新访问该数据。否则,模型可以简单地在数据集上循环,并执行正常的训练过程。
任何读过研究在线学习的论文的人可能都不知道这种培训应该叫什么,因为在多年的研究中已经给它取了很多名字。例如,终身学习[1,2],持续学习[3,4],增量学习[5,6]和流学习[7,8]——除此之外还有更多!这些名称中有许多是指非常相似的场景,只是在实验设置上略有不同。例如,终身学习通常是指按顺序学习多项任务,而增量学习则倾向于按顺序学习批量数据。有些不同的是,流学习只需通过数据集一次,永远不允许模型一次查看多个数据示例。尽管如此,我还是将“在线学习”作为一个通用术语,指的是所有这类共享部分、顺序接触数据这一共同属性的实验设置。
设置很重要吗?
鉴于在线学习技术的研究存在如此多不同的实验场景,人们可能很容易开始怀疑研究在线学习总体上是否有用。换句话说,我们能不能只研究在线学习,或者实验设置的选择对某些培训方法的有效性有重大影响?在终身学习中行之有效的方法是否也适用于增量学习?总之,实验装置的选择很重要。例如,以前的工作表明,为增量学习场景训练的模型很少在流设置中表现良好[7]。出于这个原因,我将试着具体说明正在讨论的确切的学习场景,尤其是在研究一篇特定的论文时。然而,幸运的是,用于所有类型的在线学习的许多培训技术都非常相似[9,10]——它们可能只需要稍微修改,以使它们在给定的环境中更有影响力。
为什么在线学习很难?
在深入当前的在线学习方法之前,人们可能会想为什么需要任何专门的培训方法。难道我们不能在数据可用时正常训练模型吗?这个问题的简短回答是否定的,但是理解为什么需要一些背景知识。
天真的方法…
在线学习最简单的方法之一是维护一个单一的模型,在新数据到来时对其进行微调。在这种情况下,数据对模型变得连续可用,并且不能重新访问以前的数据。因此,随着新数据的到来,模型会实时更新/微调,并随着时间的推移慢慢学习感兴趣的任务。如果传入的数据流是 i.i.d. ,这种微调方法会工作得很好!即,将从训练数据的分布中均匀地采样数据流(例如,在分类任务中所有不同类别的均匀采样),并且模型将以平衡的方式暴露给所有数据。随着时间的推移,如果数据继续变得可用,模型将开始表现得相当好。
那么,有什么问题呢?在许多实际应用中,传入的数据流是非独立同分布的,这意味着它不会以无偏的方式从底层数据集进行采样。例如,暴露给模型的所有数据示例可能来自分类任务中的单个类。更实际的是,考虑一个正在基于时尚的电子商务网站上使用的深度学习模型。在平常的一天,模型从网站上的客户活动中学习,从同一组产品中取样。然而,有一天,一个新的产品线可能会被添加到电子商务网站上,围绕一个产品组催化大量的客户活动。在这种情况下,模型暴露于与单个主题/产品相关的大量数据,导致网站上新的和现有的产品之间的不平衡暴露。很明显,这将使在线学习过程变得复杂,但是怎么做呢?
灾难性遗忘
在在线学习社区中,通过非独立身份数据流以在线方式训练模型的主要问题是灾难性遗忘[11,12]。灾难性遗忘是在线学习模型的一个属性,其中模型在暴露于新数据时会忘记如何对以前的数据进行分类。例如,考虑具有 10 个类的数据集(例如,CIFAR10),并且假设在线学习模型已经在类 1 和类 2 上被训练。然后,假设模型接收的新数据只是从第三和第四类中抽取的。如果在不访问任何先前学习的数据的情况下对该新数据进行微调,则该模型将开始在类 3 和类 4 上表现良好,但很可能在类 1 和类 2 上表现恶化。换句话说,它将遭受灾难性的遗忘!
在线学习的目标是找出如何消除灾难性遗忘。因此,这一领域的几乎所有研究(1)都假设输入数据是非独立同分布的(即,独立同分布数据流可以通过简单的微调轻松处理),以及(2)提出了一些防止灾难性遗忘的方法。一般来说,生成非独立数据挖掘数据流(用于基于分类的学习问题)的最常见方法是将所有可能的输出类分成多个不相交的组,并将这些类组按顺序展示给在线学习者[5,12]。在这种情况下,在线学习模型必须学会如何处理新的类,同时保持普遍遇到的类的知识。对于除分类之外的任务(例如,对象检测或强化学习),许多其他训练设置已被导出用于生成非独立同分布数据流,这些数据流会诱发灾难性遗忘[8,13]。
存在哪些方法?
已经提出了许多方法来减少在线学习领域中的灾难性遗忘。出于本文的目的,我将这些方法大致分为以下几类:架构修改、正则化、提炼、重放、再平衡和其他。对于其中的每一个类别,我都将提供该方法的简要概述、相关论文的摘要以及实际有效性的讨论。在这些描述中,我通常会考虑增量学习(即任务/课程增量学习)和流设置,因为它们是文献中最常见的。
建筑改造
概述。 架构修改背后的想法很简单:当您收到新数据时,向您的模型添加更多参数以增加其容量。这些参数可以以结构化(例如,向架构添加全新的神经元或过滤器)或非结构化(例如,在现有神经元之间添加新的连接)的方式添加。此外,在接收到新数据之后更新扩充的模型(即,这里我使用“扩充的”来指代具有添加的参数的模型)可以以两种方式来完成:(1)简单地更新模型而没有限制,或者(2)使用掩蔽/选择性可塑性策略来确保仅更新不重要的神经元(即,那些不影响先前数据的性能的神经元)。在这两种情况下,这种方法的目标是通过确保模型永远不会因能力有限而无法扩展其对潜在学习问题的知识,从而允许模型在新旧数据上都表现良好。通过总是添加更多的参数,我们确保模型可以继续从新数据中学习。
方法。 已经为架构修改提出了许多不同的方法,列举了上面列出的所有可能的选项。[13]研究了任务增量设置,并为每个任务实例化了一个全新的神经网络(即,每个任务都有自己的连接到输入的相同大小的隐藏/输出层)。然后,为了确保先前的知识被保持和利用,横向连接被形成到先前任务的隐藏层,允许新任务利用先前学习的特征表示来增强学习过程。类似地,[14]研究了任务增量设置,并建议每次引入新任务时将额外的神经元添加到网络中,而[15]研究了类似的(但结构化的)方法,即每次在任务增量设置中遇到新数据时将整个神经元“块”添加到网络中。最后,[16]在在线学习过程中向网络中添加新的参数,但探索各种掩蔽和可塑性策略来控制模型的参数更新,以避免之前任务对知识的破坏。
讨论。虽然架构修改技术已经在小规模在线学习问题中取得了成功,但是它们有两个主要的属性限制了它们的潜力。首先,因为架构在不断地扩展或扩大,这些技术的内存需求通常很大/没有限制,这在大规模模型或数据集的情况下可能变得难以处理。理想情况下,如果在线学习算法的内存使用不依赖于接收的数据量,效果会更好。此外,大多数这样的方法依赖于“任务边界”的存在(即,输入数据流中的预定断点,例如增量学习中存在的批次)。这种任务边界在在线训练过程中提供了明显的点,在该过程中可以将参数/模块添加到网络中。一些架构修改方法完全依赖于这种任务边界的存在[13,14,15],但是这些边界并不总是存在(例如,在流学习期间)。因此,对任务边界的依赖限制了这些技术在某些场景中的应用。
正规化
概述。 用于在线学习的正则化技术通常试图(I)识别“重要”的参数,以及(ii)在训练期间引入正则化项,以防止这些参数被改变太多。典型地,重要的参数被定义为当被更新/扰乱时使网络性能恶化的那些参数。已经提出了许多不同的重要性试探法,但是它们都有一个共同的目标,即表征修改该参数是否会损害网络对于不再存在的旧数据的性能。通过确保重要参数在在线训练期间不被修改,网络对旧数据的性能得以保持,因为新数据的参数更新被合并到与网络行为不相关的区域。
战法。 在【17】中,参数重要性被定义为参数对先前任务的网络准确度的贡献,其使用参数的近似后验概率(使用 Fisher 信息矩阵计算)来估计。然后,当网络在新数据上被训练时,施加动态正则化,使得如果具有高重要性的参数从它们的原始值被更新,则它们招致大的惩罚,从而鼓励训练仅更新对于维持先前数据的性能不重要的参数。[18,19]遵循几乎相同的方法,但对参数重要性使用不同的试探法。也就是说,[18]使用整个训练中每个参数的梯度幅度来定义参数重要性,而[19]将与每个参数相关的损失变化视为学习新任务时参数重要性的代理。虽然不是精确的正则化方法,[20,21]提出了在线学习的约束优化公式,确保在线训练期间的参数更新不会损害先前任务的性能。
讨论。 类似于架构修改方法,基于正则化的在线学习方法在较小的规模上显示出前景。然而,当用于大规模实验时,这种方法往往不是超级有效的,并且对于大型深度学习模型来说,计算参数重要性变得极其昂贵。因此,基于正则化的在线学习方法通常不被认为对大规模在线学习应用有用[9,10]。
蒸馏
概述。 在线学习的提炼方法受到深度学习中知识提炼概念的启发[22]。最初,提出了知识提取,通过训练学生在数据集上匹配教师的输出,将大型“教师”网络的知识“提取”到较小“学生”网络的参数中。稍微不同的是,在线学习方法采用提取,使得先前模型的知识(即,那些在旧数据上训练的)可以被提取到正在学习的当前网络中,以确保历史知识不会丢失。这种方法与普通的知识提炼非常相似。主要区别在于教师和学生网络通常具有相同的规模/架构,但来自在线培训阶段的不同点。这一过程类似于自蒸馏技术[39,40]。
战法。在线学习空间中已经提出了许多蒸馏方法。事实上,因为它倾向于不干扰其他方法(即,它只是对损失函数的修改!),蒸馏通常与其他方法相结合,以增强在线学习过程[5,24]。[23]是最早提出在批量增量设置中使用蒸馏进行在线学习的著作之一。这项工作表明,如果相对于先前网络设置的蒸馏损失被添加到根据新数据计算的损失中,则可以更好地保持网络在先前任务中的性能。这种方法旨在取代以前的直接方法,以避免批量增量学习中的灾难性遗忘——增加一个微调阶段,以平衡的比例包括新旧数据(即平衡微调)[23,25]。随后的工作发现,如果新数据的分布与旧数据显著不同,用新数据执行蒸馏可能会导致性能下降。然后,通过直接缓存用于计算蒸馏损失的旧数据示例,解决了这一问题[2,5,24,26]。
讨论。 蒸馏是在线学习社区中一种常用的方法,即使在大规模学习中也很有效。然而,当考虑将蒸馏用于在线学习时,后续工作表明,当之前的数据被缓存用于微调时,蒸馏的效率较低[27]。事实上,一些工作甚至认为,当维护以前数据的明确记忆以用于在线更新时,增加损失是不必要的,甚至可能是有害的[9,10,28]。因此,尽管提炼方法仍然流行,但是当允许记忆以前的数据示例时,它们的有效性是值得怀疑的。因此,存储以前的数据示例以供在线更新时使用的方法(统称为重放(或预演)技术)已经成为一种常用方法。
重播
概述。 术语“重放”泛指存储来自数据集先前部分的样本的在线学习方法。然后,当新数据到达时,这些存储的样本可以被合并到在线学习过程中,以防止灾难性的遗忘。例如,可以将这些先前的数据示例添加到蒸馏损失中,以避免网络输出偏离先前的设置太多。更常见的是,在在线学习过程中,可以简单地对先前的数据样本进行采样(即,在小批量中)以便与新数据相结合。在批量增量设置中,先前的示例将在微调期间与批量新数据混合,以确保旧知识不会丢失。类似地,流方法会将以前课程中随机抽样的样本合并到在线更新中,从而确保知识得到维护。
方法。 基于重放的在线学习已经提出了很多方法。首先,许多提取方法存储以前的数据示例以供提取期间使用,这大致属于重放的范畴[2,5,6,24]。然而,纯重放通常直接用旧数据执行模型更新。[3]发现,即使保留非常少量的先前数据示例(即,每节课几个示例)直接用于在线培训,也会大大降低灾难性遗忘的影响。类似地,[29]证明了完全重放(即在缓冲区中保存所有以前的数据)完全消除了灾难性遗忘,而部分重放(即使示例数量显著减少)为在线学习提供了显著的好处——这一结果类似于[3]的发现。最近,[7]广泛探索了流设置中的重放技术,允许将压缩的要素制图表达存储在重放缓冲区中,而不是数据本身。这种方法在[8]中针对对象检测进行了修改,在大规模图像分类任务的流学习中取得了很好的结果。
讨论。由于这些方法在各种应用中的规模不可知的成功,重放机制现在是大多数在线学习方法的核心组件。尽管存储以前的数据示例可能会占用大量内存,但执行重放可以极大地提高在线学习的性能。事实上,如果在缓冲器中保持足够的数据样本,重放已经被证明可以完全消除灾难性遗忘[29]。由于它的简单性和实用性,replay 在在线学习社区中变得非常流行。
调整资金组合
概述。 最近在批量增量学习方面的几项工作已经注意到,以在线方式学习的模型往往偏向于最近观察到的数据(即,最近一批中的数据)。因此,提出了几种技术来消除这种不平衡,我称之为再平衡技术。这种方法背后的核心思想是确保预测不会偏向较新的数据(例如,在分类设置中,有偏向的模型会将几乎所有数据预测为最近观察到的一批训练数据中的一个类别)。相反,预测的大小应该在所有类别或类型的数据之间保持平衡,不知道在训练过程中何时遇到这样的数据。
方法。 最初在【6】和【37】中探索了用于消除批量增量设置的网络分类层中的偏差的技术。在[6]中,在模型的 softmax 层上采用余弦归一化,从而确保每个类别的输出向量具有相等的幅度。[37]提出了一种类似的、可学习的方法,该方法利用小型验证数据集来训练线性“校正”模块,以消除分类层中的偏差。除了这些最初的工作之外,还提出了其他几种方法来固定增量学习的分类层中的偏差——通常是受这样一种想法的启发,即为一个类设定与该类第一次学习时相似数量级的分类权重。[38]当在线学习期间第一次遇到一个类时,将分类统计数据存储在一个小的存储缓冲区中,然后使用这些统计数据在以后的训练中使类分数更具有可比性。[27]遵循类似的方法,但是直接重用在线训练期间第一次学习一个类时的分类权重。
讨论。 预测偏差是增量学习(即任务和批次/类增量)中一个已知的、可测量的问题。此外,添加再平衡被证明可以显著提高增量学习性能,即使是在大规模数据集上,如 ImageNet [6,37]。因此,再平衡方法值得在这个领域使用。一般来说,最好是利用不需要任何验证集的方法来执行重新平衡(也就是说,这只是避免了创建验证集的麻烦)。例如,[6]不需要任何验证集,而[37]需要验证集。在增量学习设置之外(例如,在流学习中),不清楚分类偏差是否遵循与增量学习中相同的模式。然而,增加再平衡不太可能损害在线学习模型的性能。
其他技术
有些类似于 replay,一些作品提出使用生成模型来“幻觉”以前课程中的例子。在[16]中,生成式对抗网络(GAN) [30]用于批量增量在线学习,其中生成器创建用于重放的数据示例,鉴别器既鉴别又解决潜在的学习问题。[31]采用自动编码器的类似方法,但在损失函数中加入了额外的蒸馏项。[32]在任务增量设置中利用 GANs,其中每个任务训练鉴别器和生成器。然后,可以将生成器传送到下一个任务,以便可以通过生成器构建旧的数据表示,用于在学习新任务期间重放。[33]采用了类似的方法来解决领域扩展问题(即只有两个任务的任务增量学习)。
另一个比较受欢迎的研究领域是在线学习的双重记忆技术。这种方法受到大脑的启发,试图模仿记忆巩固的生物过程。在高层次上,双记忆方法——通常与 replay [12]相结合——将单独的模型组件用于新形成的和长期的知识。例如,可以维护两个独立的网络,其中一个网络专门用于学习新数据,另一个网络试图解决整个学习问题(即,旧数据和新数据)[12]。[35]提出了一个类似的方法,保持两个独立的模型:短期记忆的概率模型和长期记忆的自动编码器。稍有不同的是,高度不确定的例子可以存储在一个单独的存储缓冲区中,该缓冲区随后被整合到网络中[34]。
其他一些较少研究(但仍然值得注意)的在线学习方法包括稀疏编码方法[12],基于集成的方法[36],以及修改神经网络内激活函数以避免灾难性遗忘的方法[26]。虽然这些方法不太受欢迎,并且与常见的方法(如 replay 和 extrification)相比已经不再受欢迎,但记住这些技术仍然是有用的,可以获得对该领域的整体理解,并(希望)为未来的创新提供思路。
那么…我该用什么呢?
鉴于这篇文章中对在线学习方法的广泛讨论,提供一个现有方法的总结很重要,它强调了大规模深度学习应用中最有用的方法。现有方法的效用可以简单地总结如下:
- 架构修改和正则化很少使用,因为它们有某些缺点(如讨论中所提到的),并且往往在大规模时表现不佳。
- 蒸馏非常受欢迎,但是当允许重放时,它的有效性是值得怀疑的。
- 重放被广泛认为是减轻灾难性遗忘的最佳方法,并且在大规模在线学习实验中表现得非常好。
- 再平衡在增量学习环境中很重要,因为它消除了对最近观察到的数据形成的偏见。
因此,在线学习领域最好的“物有所值”的方法是使用基于回放的在线学习方法。例如,[7,8]提出了一种用于大规模深度学习场景的基于重放的方法,该方法表现得令人惊讶地好并且是内存高效的。类似地,像[3,29]这样的方法表明,简单地维护一个以前数据的缓冲区以供在线更新时使用是一个非常强大的工具。考虑到这一点,在几乎所有的在线学习场景中,执行重播似乎都是一个不错的选择。
除了重放之外,使用蒸馏在某些情况下可以提高性能,尽管一些工作认为蒸馏在与重放结合使用时没有用。例如,[5]将提取和重放结合起来,即使在大规模的情况下也表现得非常好,从而表明提取在某些情况下可以积极地影响在线学习的性能。此外,如果使用增量学习来训练模型,利用再平衡是很重要的,因为分类层内的偏差会显著降低性能。
非常感谢你的阅读,我希望你喜欢这篇文章。欢迎访问我的研究页面,或者联系我,询问你对这篇文章的任何问题/评论。如果你想了解我最新的出版物/博客文章,你也可以在 twitter 上关注我。如果你对这个话题感兴趣,我鼓励你访问我在莱斯大学的实验室的网页,我是一名博士生,专注于深度学习的经验和理论基础。
参考书目 【1】https://arxiv.org/abs/1704.01920
[2]http://home . ustc . edu . cn/~ sai hui/papers/eccv 2018 _ lifetime . pdf
[3]https://arxiv.org/abs/1902.10486
https://arxiv.org/abs/1908.04742
[5]https://arxiv.org/abs/1807.09536
[7]https://arxiv.org/abs/1910.02509
https://arxiv.org/abs/2008.06439
https://arxiv.org/abs/2011.01844
https://arxiv.org/abs/1909.0838
[11]https://www . science direct . com/science/article/ABS/pii/s 0079742108605368
https://arxiv.org/abs/1708.02072
https://arxiv.org/abs/1606.04671
https://arxiv.org/abs/1612.03770
https://arxiv.org/abs/1908.08017
https://arxiv.org/abs/1904.03137
https://arxiv.org/abs/1612.00796
https://arxiv.org/abs/1711.09601
https://arxiv.org/abs/1703.04200
【https://arxiv.org/abs/1706.08840?source=post_page 20】-
https://arxiv.org/abs/1812.00420
https://arxiv.org/abs/1503.02531
[23]https://arxiv.org/abs/1606.09282
【https://arxiv.org/abs/1606.02355 24】
【https://arxiv.org/abs/1903.12648 25】
https://arxiv.org/abs/1607.00122
https://arxiv.org/abs/2001.05755
[29]https://arxiv.org/abs/1809.05922
https://arxiv.org/abs/1406.2661
[31]https://arxiv.org/abs/1704.01920
[32]https://arxiv.org/abs/1705.08690
[33]https://arxiv.org/abs/1705.00744
[34]https://hal.archives-ouvertes.fr/hal-01418123/document
[35]https://arxiv.org/abs/1711.10563
[36]https://arxiv.org/abs/1611.06194
[37]https://arxiv.org/abs/1905.13260
[40]https://arxiv.org/abs/1805.04770
时间序列分析中反对主成分分析的一个案例
思想和理论
如何对时间序列使用线性降维?
我们都用 PCA 进行线性降维。从图像处理到非结构化数据,随时随地为您服务。我们甚至用它来进行时间序列分析,尽管有更好的技术存在。在这篇文章中,我将向您介绍动态模式分解 (DMD),这是一种针对高维时间序列的线性降维技术,源于我的研究领域:流体动力学。DMD 结合了两个世界的精华:PCA 和傅立叶变换。在数学上,它与动力系统理论中的一个基本算子有关,这个算子被称为库普曼算子。但是在深入 DMD 的数学之前,让我们用一个相当简单的例子来说明为什么 PCA 不是高维时间序列分析的最佳选择。
激励人心的例子

作者的动画,灵感来自史蒂夫(@eigensteve)和宾·布伦顿。
想想这部电影。它由 1024 帧 128×128 像素的图像组成。从概念上讲,这是一个高维的时间序列。尽管有 16 384 个自由度,但很明显存在底层低秩结构。毕竟,电影只是由一个正方形和一个以两种不同频率振荡的圆形加上一些随机噪声组成。通过将每一帧重铸为 16 384 维向量,我们可以构建数据矩阵 X ,其中每一列都是不同的帧。因此,它是一个 16×384×1024 的矩阵。现在让我们使用主成分分析来解开这个低秩结构。PCA 依赖于 X 的奇异值分解,即

在 U 包含 PCA 模式的情况下,σ的对角线条目描述了这些模式的重要性,而 V 的列描述了它们的时间演变。此外uᵀu=I,即 PCA 模式形成正交基。类似地,vᵀv=I意味着它们的时间演化是线性不相关的。下图描述了奇异值分布以及两种主要的 PCA 模式。

PCA 分析的结果。左:奇异值分布。中间和右边:两种主要 PCA 模式的空间支持。图片由作者提供。
正如预期的那样,我们数据集中的大部分差异被前两种模式捕获。然而,它们对应于正方形和圆形的混合。当看下面的时间演变时,这种无法将两者分开的现象尤其明显。

两种主要主成分分析模式的时间演变。图片由作者提供。
尽管 PCA 识别了一个好的低维子空间,但是相关联的坐标系没有提供对正在进行的简单动力学的清楚理解。那么我们能做得比这更好吗?我们能同时找到一个好的低维嵌入和坐标系统来描述这些动态吗?
介绍动态模式分解
数学上,PCA 主要旨在表征我们数据集的二阶统计量,而不是动力学。DMD 来了。假设我们的数据是由一个未知的动态过程产生的

我们的目标是找到一个函数h(x):ℝⁿ ↦ ℝⁿ逼近 f ( x ):最小二乘意义下的ℝⁿ↦ℝⁿ。关于h(x)的不同假设导致不同的模型。在 DMD 框架中,(x)被假设为线性映射

其中 A 是一个 n × n 矩阵。现在的问题是如何识别这个矩阵一个?
数学细节
在没有额外信息的情况下,我们可以得到的最佳解决方案是通过解决优化问题

介绍矩阵 X 和 Y ,定义如下

这个优化问题可以被改写为

它的解由下式给出

其中 X 表示 X 的 Moore-Penrose 伪逆。尽管易于计算,但该解决方案受到两个限制,这两个限制都与 A 是 n × n 矩阵的事实有关。因为在高维设置中,n 通常可以是几百万,所以显式地构造这个矩阵可能是不可能的。这也意味着我们有 n 个参数,远远超过我们有限的数据集允许我们合理估计的数量。因此,即使我们可以构造出和,这个模型也很难推广。
为了克服这些限制,可以假设 A 是低等级的。如果是这样,它可以分解为

其中 P 和 Q 为 n × r 矩阵。在不失一般性的情况下,我们还将 r × r 单位矩阵强加给pᵀp=I和 I 。将这种因式分解引入到我们的优化问题中会产生

这是非凸问题。然而,使用简单的代数运算, P 和 Q 可以作为的 r 个主要特征向量获得

到目前为止,我们还没有指定线性模型的秩。然而,这是一个广义的厄米本征问题。所有的特征值都是非负的。因此,可以使用与 PCA 相同的试探法来确定我们模型的最佳等级。PCA 实际上是这个更普遍问题的一个特例。的确,假设 X = Y 和 P = Q ,就归结为 PCA 本征问题。
与 PCA 相比,DMD 的优势在于它不仅提供了低维嵌入,而且还适合我们未知动态过程的线性模型

在计算了低秩因子分解 A = PQ ᵀ之后,它可以很容易地转换成它的本征分解

其中ψ和 ϕ 分别是 A 的左右特征向量。它们也被称为 DMD 模式。 D 是由 A 的特征值组成的对角矩阵。我们的线性模型现在可以改写为

ψ和 ϕ 因此是投射进和投射出低维空间的矩阵。此外,在这个潜在的空间,动力学是近似的一个简单的对角矩阵。正如我们将看到的,这使得我们的嵌入比 PCA 更容易理解。
我们激励性例子的说明
让我们回到激励性的例子,用 DMD 处理数据。下图描述了广义厄米本征问题的本征值和 DMD 模式的空间支持。

DMD 分析的结果。左:广义厄米特征问题的特征值。中间和右侧:两个主要 DMD 模式的空间支持(ψ矩阵的第一列和第二列)。图片由作者提供。
至于 PCA,本征值分布表明秩-2 模型,而 DMD 模式现在清楚地分开两种动力学。当描绘每个潜在变量的时间演变时,这也是可见的。

两种主要 DMD 模式的时间演化。图片由作者提供。
正如所料,DMD 恢复了两个纯音振荡。最近的研究表明,DMD 表现为源分离算法(例如 ICA),尽管该框架可以更加灵活。对于类似的计算成本,它还提供了一个比 PCA 更容易解释的模型!
流体动力学中热对流的应用
在结束之前,让我展示一个实际的例子,这个例子是由我自己的研究启发而来的。左边显示的配置是一个热虹吸管。这是一个闭环系统,流体从下面加热,从上面冷却。随着上下壁温差的增加,热流体开始上升,冷流体开始下降。这个运动产生了一个对流单体。超过临界温差,动力学变得混乱。对流单元经历随机的反转,导致它以顺时针或逆时针方向来回振荡。某种程度上,这是某些地球物理流的简化版。
我研究的一个关键目标是确定这种流动的低阶模型,我们可以用它来进行快速预测或反馈控制。然而,先决条件是具有良好的数据低维嵌入。这是 DMD 出现的地方。在收集了相当多的温度场和速度场的快照后,进行了 DMD 分析。结果如下所示。

混沌热虹吸管的 DMD 分析。等级 1 模型捕获速度场中的大部分动态,而等级 2 模型需要用于温度。图片由作者提供。
尽管问题中有大量的自由度,但是动力学的固有维度是 3。一个是速度,两个是温度。DMD 模式强调速度场中的主导模式在方位角方向上基本不变。对于温度,它表明最重要的模式是左右和上下温差。这几乎是你建模动态所需要的全部(但那是另一个故事了)。将数据投影到这些 DMD 模式的跨度上会产生以下低维嵌入。

从 DMD 分析中获得的低维嵌入。 x 变量表示与速度场相关的模式的振幅,而 y 和 z 是左右和上下反对称温度分布的振幅。图片由作者提供。
对于任何稍微熟悉混沌理论的人来说,这种低维潜在空间动力学看起来就像洛伦兹系统!这确实是因为我没有时间在这篇文章中解释的原因。你应该记住的基本上是,即使对于这种强非线性、高维、混沌的动力系统,DMD 也提供了对所述系统的物理性质的许多见解。
结论
PCA 因其简单而在数据科学中无处不在。由于这种简单性,它也经常在不应该使用它的情况下使用,或者在存在同样简单但更好的方法的情况下使用。高维时间序列分析就是这样一个例子。我希望您现在确信,在这种情况下,动态模式分解更好。它不仅被设计用来处理顺序数据,而且它还提供了一个比 PCA 更容易解释的模型。
自从 10 年前引入流体动力学以来,DMD 已经被证明是一个非常通用和健壮的框架来分析由高维动态过程产生的数据。它现在被常规应用于其他领域,如视频处理或神经科学。还提出了许多扩展。一些包括用于控制目的的输入和输出(见 DMDc )。其他人将 DMD 与来自压缩感知的思想相结合,以进一步减少计算成本和数据存储,或者来自小波,用于多分辨率分析。可能性是无限的。如果你想了解更多,我强烈推荐 Kutz 和同事的《动态模式分解:复杂系统的数据驱动建模 》这本书。也可以看看帖子最后的参考文献。如果你觉得 DMD 实际上是你所缺少的工具,请告诉我!
想看更多这方面的内容?查看我关于低秩结构和数据驱动建模的其他文章,或者只是我的机器学习基础知识!
* *
数据科学中直觉的一个例子
直觉如何帮助我们成为更好的数据科学家

作者的作品,来自 Pixabay 的 Gordon Johnson 的图片
直觉和科学并不总是和谐相处。事实上,任何用想象或“感觉”来简化科学过程或成果的东西,在历史上都会遭到强烈的反对。正如利里在讨论感觉和想象在科学中的应用时所说:
“科学试图通过规定可接受的情感形式和限制想象力的自由发挥来使这种矛盾心理合法化。”
数据科学也不例外。事实上,其他人也有类似的观点,数据科学可能不是凭直觉做出决策的好职业。这个论点是以最崇高的目的提出的。直觉、常识和勇气在过去让我们失望了,因为它们不仅被经验所渗透,也因此与正常、自然的人类偏见交织在一起。

所以,我们来做个区分。依靠直觉和仅仅依靠直觉与允许一个人的决策被直觉告知是非常不同的。也就是说,直觉是一种信息,尽管有偏见,但仍因我们独特且通常难以描述的经历而丰富,因此在我们的决策过程中具有价值。在科学、数据或其他方面,直觉仍然是有价值的,但不应该作为任何最终决定的唯一来源。
为了说明这一点,对直觉的研究表明,在高度复杂的行业和/或任务中,专家的直觉可以带来更快更准确的决策。
数据科学是一个复杂的行业,其特征也在于许多复杂任务的组合。此外,由于大多数数据科学家受雇于企业而非研究机构,因此快速做出决策是构建成功解决方案的必要条件。因此,我认为发展专家直觉并学会如何利用它来为决策提供信息可以帮助数据科学家成为更好的数据科学家。
那么我们如何发展专家直觉呢?
专家的直觉被定义为…
“……在具有天赋的个人获得大量学习经验并意识到每次表演的质量后,在某一领域发展起来的技能。”
发展这样的技能需要经验,但不是任何经验。这需要我所说的“修补”经验。以统计建模为例。理解统计如何工作是所有数据科学家的核心要求,但是当我们查看数据科学工具包中可用的所有模型以及数学在这些模型中的各种应用时,期望任何一个数据科学家知道它们所有的数学推导是徒劳的。
我们可以期待的是,数据科学家对这些模型如何工作有一种直觉,因为他们有修补输入和超参数并观察它们产生不同输出的经验。正是这种类型的修补产生了在模型选择和开发中快速决策所需的专家直觉,使数据科学家能够从一个不可能的可能性世界进入一组有限的“最有可能”输入和超参数值。
在这些情况下,专家的直觉也可以帮助我们在开发工作中寻找更有创造性的组合和机会。例如,对无监督算法的行为有一个总体的直觉,可能有助于为建模问题提供更具创造性的方法,因为开发人员知道如何在不丢失太多特征集信息的情况下减少数据的维度。

图片来自 Pixabay
为了在数据科学中发展直觉,我们必须确保我们可以修补,并且有效地修补我们需要的东西…
模仿
不要害怕模仿。所有的学习都是从模仿开始的,所以为了成为优秀的直觉数据科学家,我们需要学会成为优秀的模仿者。但并不是任何形式的模仿就足够了。我们需要模仿最佳的、最佳的解决方案、最佳的代码、针对不同情况的最佳部署。所以,当你在模仿的时候(例如,跟随教程,浏览博客文章,浏览书籍或课程),不要让自己陷入困境。
社区
找到一个让你感到安全的社区,同时尝试你不断扩展的技能。确保它是一个你可以表达你的直觉而不会感到被评判的地方。
灵感
记住,学习不是灌满一桶水,而是“点燃一把火”——叶芝。在谈论教育时,叶芝是否说过这些话没有这个观点提供的价值重要。学习是鼓舞人心的,因此,我们必须在追求专家直觉的过程中寻找灵感。这是一个重要的心态,因为水桶是固定的,但火只受到燃料的限制。学习也不是固定的,所以我们的学习能力更像一把火,而不是一桶我们可以装满的知识。
创造力
随着我们经验的积累,我们的创新能力也在增强。在你决定学习数据科学之前,不要忘记你的经历,因为这些经历可能会成为你今天所做事情的新颖和创造性应用的基础。
报酬
在数据科学中,在我们的代码中不出错是值得的,同样值得的是看到我们的模型准确地识别、分类或预测我们想要输出的信息。花点时间从这些胜利中获得回报,因为当你在未来寻求新的解决方案时,给自己这些时间有助于调整和训练你的专业直觉。
玩
最后,我们需要给自己留出玩耍的时间。玩转数据、玩转代码、玩转模型、玩转技术……不胜枚举。什么是玩耍,它和学习有什么不同?玩耍是我们发展的空间,在这里没有期望和期限。我们用自己建立的直觉进行实验。我们允许自己尝试一些事情,即使我们不知道它们是否有明确的意义。我们允许自己犯错,并把这些错误看作是我们专业直觉的进一步补充。
感谢阅读!
比如参与学习更多关于数据科学的知识?加入我。
SQL 连接中的 Case 语句
俏皮的把戏

照片由伦纳德·冯·比布拉通过 Unsplash
当连接两个或更多的表时,有时会出现连接的值不完全匹配的情况。有很多方法可以解决这个问题:在要连接的表的 join 语句中使用 CASE 语句的子查询,在 temp 表中使用 CASE 语句,其中所有值都被更改为匹配,或者在 JOIN 的 ON 子句中使用 CASE 语句这种方便的小技巧。
让我们快速地看一下使用一个没有调整的常规连接(显然不是下面几个卷轴的超级酷的聚会技巧):

作者照片
在上图中,我们可以看到,当我们试图将“cust_table”左连接到超级漂亮的 capes“orders”表时,当连接到“cust_num”时,我们最终得到了一些空值。在“orders”表中,我们发现 Count von Count 和 Papa Drac 使用了我们的 E-Comm 订购系统,从而将一个漂亮的小“-EC”连接到他们的“cust_num”的末尾。他们位于' cust_table '中的客户数据没有我们喜欢的新的'-EC '字符串。
世纪派对把戏!
我猜这不是常识,主要是因为我不记得曾经听说过,但是 CASE 语句可以用在 JOIN 语句的 on 部分,如下所示:
现在让我们来看看结果:

作者照片
哔哔哔。成功了!
这个什么时候能派上用场?
根据我的经验,在更新现有查询时,这可能会节省大量时间。你可能有其他的连接或者其他你不想乱来的东西。你真正希望的是正确地加入表格。这有助于你到达那里!
最后的想法
SQL 是一个强大而有用的工具。肯定有不止一种方法可以敲碎鸡蛋/编写代码来做你需要它做的事情。这只是你可能想要添加到你的工具箱中的东西。下次见,继续学习!
警示故事:人工智能伦理中的多方利益相关者反馈
人工智能校准和安全
多利益相关者反馈有其固有的缺陷,因此,最好将它们视为建立和维护人工智能道德的“手段之一”,而不是“目的”

来源:Freepik |高观多样木质人物包容理念
设定背景
道德是一个公认的信念体系,它指导或控制个人或团体的行为。信念在某些部分代表理性的、真实的、事实的观点,在某些部分代表与当前时间相关的相反观点。公认的信仰可能与从历史中学到的社会哲学、群体认可的道德、社区界定的原则以及有时由负责治理的人制定的规则有关。公认信念的共同主题是支持和赋予人们力量。几个世纪以来,社区、团体、组织和治理结构总是通过多方利益相关者协商和收集不同意见来建立道德规范。全球各地的社会和文化都将道德作为一种生活方式,远远超出了定义“什么是道德的”(建立一个公认的信仰体系)的监管范围。因此,道德(不受任何地域限制)应大于相关监管要求(固有地受地理区域限制)。
对多利益主体反馈的需求
人工智能是一种变革的代理,它赋予人们权力并优化手中资源的效用。任何对人们产生重大影响的变革因素都会带来变革带来的某些副作用。对于人工智能来说,这些副作用包括伤害和歧视等。鉴于这些副作用对人的潜在影响的重要性,有必要建立道德规范。关于谁、如何以及何时应该被授权建立伦理(关于人工智能伦理)的争论正在进行。为了将道德规范扩展到更广的范围,除了法规之外,还需要社会、社区和地区的不同派别的人提供更广泛的投入和包容性参与。因此,多利益相关方的参与、社会对话和多样化的投入被认为是建立“人工智能伦理”的关键因素。
信任:这一机制的核心
多利益主体和多样化投入(在线和离线)的潜在目的是确保在构建公认的信仰体系时考虑包容性因素。多利益相关方反馈机制旨在固有地信任利益相关方的(1)意图和(2)诚信。这是多利益主体反馈机制最重要的优势和局限性之一。最近的历史表明,由多利益攸关方反馈驱动的社交媒体趋势操纵了政治体系和宗教和谐。
在确定反馈的利益相关者时,无意的采样偏差可能会产生更多的分裂性反馈,从而放大现有的社会结构。这是因为在现有的社会结构中存在意图冲突(接受的信念的差异)和不均衡的基线(代表性不足或缺乏接触)。社会系统通过提供领土独立(不一定总是地理领土)来处理这些冲突。例如种族)或者为不同的组提供最小或最大的机会限制(例如配额系统)以确保它们的共存,这在人工智能的世界中实际上是困难的。例如,在为 AI 应用中的选定组建立配额系统时存在实际困难和困境,其中 AI 应用当前不具有这样的组的标识符。这是一个人工智能应用是否应该在现有的社会歧视基础上扩展的两难问题。
对诚信的信任很难验证。社会系统中的经济机会和不稳定的领土独立有时会诱使利益相关者或利益相关者群体利用对诚信的信任,淡化/转移对公认信仰(伦理)系统的印象。虽然这些剥削迟早会从宿舍里浮出水面,但到那时它已经造成了损害。
为什么多利益主体的意见可能有缺陷?
从商业组织构建人工智能系统的角度来看“道德规范中的多利益相关方投入”将有助于揭示多利益相关方反馈机制中的挑战以及使用该机制时应注意的事项。
1。研究:商业组织的人工智能研究对于构建更好的解决方案,服务于现有和未来的客户群非常重要。这些都是通过与教育机构的研究合作、研究金/实习以及为从事本组织感兴趣的特定主题的社会机构和智囊团提供资金来实现的。这项研究涉及收集多利益相关方的反馈,这些反馈在最近受到质疑,如(1)沉默的反馈——交易忠诚度、激励或更高目的的意识形态,( 2)没有探究研究缺点的反馈——要么是出于选择,要么是由于数据不足或时间不够。
2。自动反馈:采用自我学习或人类训练学习或两者结合的人工智能应用程序容易出现(1)错误反馈——例如机器人驱动的趋势新闻(2)受影响的反馈——例如分享和评论假新闻(3)非理性反馈——例如关于一个国家应该如何应对恐怖分子的无保留意见或判断或建议。在许多情况下,很难区分授权和/或分裂人们的反馈。这些都是通过各种敌对攻击完成的,包括数据中毒和通过雇佣对手进行的反馈渠道。
3.决策和相关行动:人工智能应用程序可能会根据多方利益相关者的反馈(在线和离线)做出选择或做出权衡。这些选择/权衡可能与对基本事实的理解/印象及其与组织的经济或商业原则的冲突有关。这些反馈可能会受到以下问题的困扰:(1)代表性不独立的反馈——利益冲突;( 2)似乎是一个或多个群体对其他群体的问题的选择性表述——例如,在某些问题上比其他群体更快地寻求反馈;( 3)具有保护商业组织利益的固有观念的反馈;( 4)来自权力和能力不平衡的利益相关方群体的反馈;( 5)当潜在环境发生变化时未达成共识的反馈。
重要的是要理解,并非所有这些都受到构建人工智能系统的商业组织本身的影响,有时它是由(a)利益相关者的经济和社会需求以及(b)谁在迎合这些需求(公司、其竞争对手——如间谍攻击、活动家和/或政治代表等)驱动的。这决定了他们是表达(赞同、反对或虚假)还是保持沉默。
结论
这并不是说多利益相关方反馈机制无效。这是恢复人类对人工智能系统信心的最具包容性的方法之一,然而,重要的是要认识到它们是有缺陷的。揭露这些缺陷的原因是在使用它们时要足够谨慎,并将它们视为建立和维护人工智能道德的“手段之一”,而不是“目的”。
与 Grab 的高级数据分析师 Cliff Chew 的对话
从经济学家转型为数据分析师的教训
我最近偶然发现了符号连接,这是一个由经验丰富的数据从业者 Thu Ya Kyaw 和 Koo Ping Shung 运营的数据播客。这个播客引起了我的注意,因为嘉宾主要来自新加坡,东南亚新兴的科技中心之一(也是我的家乡。)
作为一家科技公司的数据分析师,我总是对其他科技公司分析师的工作感到好奇。因此,我听了 Grab 高级数据分析师 Cliff Chew 的播客。

克里夫是 Grab 的高级数据分析师,Grab 是一家总部位于新加坡的打车科技独角兽公司,业务遍及东南亚。
在这个播客中,Cliff 分享了他在 Grab 中作为数据分析师的角色,他作为分析师的经验以及他对有抱负的数据从业者的建议。
我发现这个播客很有启发性——并在这篇博文中进行了总结,与广大数据社区分享。
关于克里夫·周
Cliff 是新加坡国立大学的经济学学位持有人,曾在几家科技公司担任数据分析师几年。他当过公务员和研究员,与经济学家、社会学家、地理学家、统计学家、建筑师、数字营销人员、设计师、欺诈分析师、数据工程师、数据科学家和数据分析师同事共事过。
最近,他在 Carousell 担任数字营销分析师,并于 2019 年进入 Grab,担任信任、身份和安全团队的高级数据分析师,负责检测欺诈。
说的够多了。我们去面试吧!
你在工作中使用的工具是什么?

Katie Rodriguez 在 Unsplash 上的照片
他作为分析师使用的一些工具是
- 结构化查询语言
- Google Sheets,用于与利益相关方合作
- 计算机编程语言
- 稀有
- 数据可视化表格
他还强调了 SQL 作为大规模数据提取工具的重要性。其实在他看来,学 SQL 比学 python 更重要。
他还讨论了 R 和 Python 的比较。虽然 R 比 Python 对非技术人员更友好,但它比 Python 更不容易生产和与现有系统集成。
总的来说,他还明确指出,具体使用哪种工具并不重要,他评论说
“我不会说你必须学习 python 或 R,因为工具总是在变化。但是,在当今的数据经济时代,能够以可扩展的方式执行数据处理过程是必要的。”
在你的职业生涯中,你学到的最重要的东西是什么?
用概率思考问题。
当 Cliff 希望从经济学进入数据科学领域时,他通过观察市场趋势和他现有的技能组合,使用概率来评估他的机会。
很明显,要对 data analyst 感兴趣,你很可能需要知道如何编码。所以他选择了编程。

从机会的角度来考虑……马库斯·斯皮斯克在 Unsplash 上的照片
你是怎么学会编程的?
他首先用 python 构建了自己的基本编程语言,从数字和字符串操作等基本概念开始。
这些积木帮助他建造项目来巩固他的学习。结合他对篮球的兴趣和他的知识网络搜集,他从 NBA 官方网站上清理了数据,并通过逻辑回归,SVM 和随机森林等模型运行。最终,他能够建立一个管道来收集数据并进行日常预测。端到端流程耗时 3 年。
谈到他从这个项目中学到的一个教训,他哀叹来自官方来源(NBA)的数据的肮脏,并提到—
不要相信你的数据源,要经常检查你的数据。
作为一名分析师,你目前的工作有哪些性感和不性感的地方?
克里夫诙谐地说,一个角色是否性感取决于一个人的个性。不管怎样,他分享了一些他的角色的亮点。
与利益相关者互动
作为 Carousell 数字营销团队的一员,他与多个利益相关者互动——无论是技术方面还是其他方面。作为一名分析师,他是技术人员和非技术人员之间的连接器,创建从工程师到最终利益相关者的管道。
他还花时间教育非技术利益相关者关于“实时”或“AB 测试”等技术术语的定义,并传达了现有数据的局限性。这种交流的关键还在于以非技术的方式传递技术信息,或者如克里夫所说,
“你必须以一种让他们理解的方式向他们解释,而不是简单地向他们抛出专业术语,”
处理不确定性
克里夫角色的不确定性有不同的表现形式。一种形式是统计测试的不确定性。
“有人问我:为什么你所有的 AB 测试都失败了?【答案是】因为它们是测试;我们提出的不是事实,而仅仅是假设。”
另一种形式是数据清洁度的不确定性。使用多个数据源,比如脸书、谷歌和内部数据,会导致数据差异。因此,知道如何向利益相关者解释在出现这种差异时数据的局限性是很重要的。
做文档
在这个公司希望快速行动和打破常规的时代,文档被藏起来是可以理解的。然而,文档之所以重要,至少有两个原因。
首先,文件可以提醒你自己曾经做过的事情。 Cliff 将他的工作整理到文件夹中,当他需要提醒时,就会把它们拉出来。
其次,它允许其他人在你的工作基础上快速构建。
如果你要为你的职位雇人,你会寻找什么样的品质?

理想的候选人应该是
- 谦虚和可训练的,因为有很多要学习和遗忘。
- 对理解统计和编程概念的求知欲。
- 善于与人相处,无论是技术方面还是其他方面。
- 能够处理不确定性和不完整的信息
你对那些想转行做数据的人有什么建议吗?
学习 SQL(结构化查询语言)
Cliff 提到,SQL 应该是人们应该学习的最重要的东西,因为它允许您以优化的方式进行数据提取。
学习 Python 和/或 R
在决定是学习 python 还是 R 时,Cliff 鼓励听众研究他们各自行业中偏好的工具。
例如,金融行业可能要求分析师精通 VBA,而其他一些行业可能喜欢懂 SPSS 的分析师。
了解你的比较优势
重要的是要知道你相对于其他候选人的优势是什么,并知道如何给自己定位。例如,由于你之前的知识,金融领域的人比其他人更有可能侵入金融科技领域的数据。
当 Cliff 考虑一个角色时,他将自己定位为具有领域和技术知识的混合体
"我比一个计算机科学家知道更多的领域知识,比一个商人知道更多的统计学."
看重机会胜过大牌
找工作时,你应该仔细阅读职位描述,寻找成长机会,而不是追逐名人。对于应届毕业生来说更是如此。
结论
我从播客中最大的收获之一是有效沟通作为数据分析师技能的重要性。数据分析师是技术和非技术利益相关者之间的中间人,对于防止沟通中断至关重要!
如果你想和克里夫保持联系,请随时通过他的 LinkedIn 联系他。
想听听我刚刚总结的播客吗?听听这里。

如果你喜欢这篇文章,请在 LinkedIn 上联系我!我撰写关于学习数据科学、数据科学技巧和诀窍以及一般技术的内容。
https://www.linkedin.com/in/travistang/ [## Travis Tang -数据分析师- Gojek
www.linkedin.com](https://www.linkedin.com/in/travistang/)
确认
特别感谢 Cliff Chew、Koo Ping Shung 和 Thu Ya Kyaw 允许发表本文。
构建机器学习模型之前的数据预处理清单
创建机器学习数据集的数据预处理要做的事情列表(以及一些方便的技巧)

图片提供:unsplash.com
在这篇文章中
在本文中,我们将看到预处理中涉及的数据处理步骤是什么,以及 python 中执行这些操作的一些相关代码。
我们还将看到需要建立一个详尽的预处理步骤清单,您可以在您的数据集上应用。本文提供了一份入门清单。它可以作为你正在处理的特定项目的基础。
背景
大多数机器学习算法需要干净的数据集作为算法的输入。
这些是您的train 和test 数据集。这些数据集进一步分为通常所说的x-train, y-train和x-test, y-test数据集。x-train 和 x-test 被称为特征向量(因为它们包含代表x-variables或features的列)。y-train和y-test代表目标变量的数据集。
但是在得到干净的数据集之前,我们需要对原始输入数据集执行一些广泛的操作,以最终得到可用的数据集。这里有一些清单和问题要问(作为一个数据工程师/分析师),以达到你的机器学习算法的最终干净输入。
命名
在本文中,我们将使用以下名称来指代变量—
df —数据帧的变量名
X_train —训练数据集(x 变量)
y_train —训练数据集(y 变量/目标变量)
X_test —测试数据集(x 变量)
y_test —测试数据集(y 变量/目标变量)
摘要
这些是我们将在本文中详细讨论的检查—
**1.Check for Data Types
2.Check Columns Names
3.Check for Missing Values
4.Check for Bad Data
5.Imputation of Null values
6.Check for distribution type
7.Scaling the data
8.Checks for outliers
9.Check for data Imbalance
10.Perform necessary transformations
11.Perform feature Engineering
12.Binning Continuous data
13.Feature selection**
数据集
我们将使用"波士顿房价数据"(你可以从这里得到这个:https://www . cs . Toronto . edu/~ delve/data/Boston/Boston detail . html或这里http://lib.stat.cmu.edu/datasets/boston)
为了方便用例,我们将对原始数据集进行一些更改(例如:重命名一些列,引入一些空值等,以演示代码)
数据包含以下各列—
>> df.dtypesCRIM float64
ZN float64
INDUS float64
CHAS int64
NOX float64
RM float64
AGE float64
DIS float64
RAD int64
TAX object
PTRATIO float64
B float64
LSTAT float64
Price float64
dtype: object
数据预处理的详细清单

图片提供:unsplash.com
1.检查数据类型
- 每列中代表哪种数据?
df.dtypes # Displays all the data types of the columns in df
- 所有的列都被正确分类为正确的数据类型了吗?如果不是,请更改数据类型。
#change the datatype to floatdf['col'] = df['col'].astype('float')
- 有应该是数字的分类列吗(反之亦然)?
邮政编码通常被归类为数字,但是它们确实是分类的。熊猫会将它们解析为正确的数据类型吗?
例如:日期类型分类正确吗——还是用' *object* ' 表示

来源:https://numpy.org/doc/stable/reference/arrays.scalars.html
提示 1: 要查看数据的描述,(不仅仅是连续变量,使用include = 'all’参数。这包括你的分类变量的其他函数,如unique、top和freq。
提示#2 :如果你有很多列,在数据框.T 上使用转置功能将有助于你在一个页面中查看它(一个在另一个下面,而不是水平方向,这可能会截断显示的列数,基于你的 jupyter 笔记本设置)
data.describe(include='all').T
提示#3: 如果您只想选择分类列,请使用select_dtypes函数
cat_cols = data.select_dtypes(include=[“object”, “category”]).columns.tolist()
提示#4 :要仅选择数字列,
num_cols = data.select_dtypes(include=[“number”]).columns.tolist()
2.准备数据集—检查列名
- 列名是否有冗长的名称、特殊字符、空格等?如果是,是否必须更改列名?
提示:如果删除列名中的空格,对列的操作会更容易。
data.columns =[col.replace(' ','_') for col in data.columns]#replacing spaces in column names with '_'
3.缺失值-删除还是估算?
- 您的任何要素是否有缺失值?
df.info() # shows non-null values and dtypes in a single command#ordf.isnull().sum()# or to display the sum of ALL nulls in the dataframedf.isnull().sum().sum()

df.info()

- 你应该对自己缺失的价值观做什么处理?
- 缺失值有什么模式吗?
例如:记录可能在第 1 列、第 2 列、第 3 列有一致的值
在这个节骨眼上,有必要调查一下您丢失的数据中是否存在某种模式(并思考最初发生这种情况的可能原因)。检查他们是否—
随机失踪(MAR)
·完全随机失踪(MCAR)
·非随机失踪(MNAR)
如果一列中有许多缺失值(例如:约 60%),最好删除这些列(而不是试图用某个值来估算)
要查找数据帧中每列缺失数据的百分比
df_isna().sum()*100/len(df)#or df.isnull().sum()*100 / len(df)
选择要删除的列,然后删除它们
# dropping columns 'RAD' and 'RM' ( if there are lots of missing values )
df.drop(columns=["RAD", "RM"], inplace=True)
如果您只想删除某一特定列的空值记录——
# Keeping only the records where the 'Price' column is not null.
df = df[df["Price"].notna()]
4.错误数据
- 你的数据集中有坏数据吗?(比如'?'还是'-'代表空值?).如果你替换它们,处理这些数据可能会容易些
import numpy as np
df.INDUS= df.INDUS.apply(lambda x: np.nan if x == ‘?’ else x)# here we are replacing '?' with NaN
- 检查数据中的零。
- 注意:数据集中的零是坏数据还是数据的一部分?(在您的业务环境中,坏数据的定义是什么)
df[df == 0].count() # Check for zeros in all columns

检查您的列中是否有零
- 期望自然数的特性中有-ve 值吗?
例如:-2 在子女数量/工作年限等方面
- 用你的领域知识过滤掉异常的号码
例:年龄值高(年龄= 221)。这可能是数据输入/数据捕获错误
- 是否有混合了文本和数值的列需要清理?(如果列混合了数据类型(例如:数字和文本),则列的
dtype显示为object。但是,这并不意味着如果 dtype 是 object,它就一定会有混合的数据类型) - 你对不良数据的处理方式是什么?您是删除记录还是用其他东西替换坏数据?
# Remove the rows where ZN column has '0' as value
df = df[df.ZN != 0])
- 是否有重复的记录?真的有重复的吗?还是你必须删除它们?
df[df.duplicated() ] # Shows records which are duplicatedlen(df[df.duplicated() ]) # count of records that are duplicated
5.数据处理——插补
- 为什么会出现价值缺失?(查看数据,您能否发现缺失值是否有规律可循?)
- 你对缺失值处理(插补)的策略是什么——替换这些缺失值的正确方法是什么?(均值插补、中值、随机还是只是一个固定数?)
df.RM = df.RM.fillna(df.RM.mean()) # mean imputation of RM columndf.RM = df.RM.fillna(df.RM.median()) # median imputation
- 是否要基于其他列的值进行估算?(例如:使用一些类似
**KNNImputer**的包)(https://scikit-learn . org/stable/modules/generated/sk learn . impute . knnimputer . html)
6.数据分布
- 数据是否偏向一边?你更愿意使用什么方法来检测偏斜度(统计/视觉——通过分布图?)
找到skew 和kurtosis 将是确定任何列中的数据是否需要任何处理的良好开端。
df.CRIM.skew() # Find the skew value of the data in column 'CRIM'
df.CRIM.kurtosis() # Kurtosis of the data in column 'CRIM'
- 数据中可接受的“偏斜度”是多少?

查找列的偏斜度和峰度,以确定它是中度偏斜还是重度偏斜
df.skew() # Or get the skew values of all the columns

你可以

卷曲柱的分布图将显示它是右偏的。
7.缩放比例
- 您的数据是否需要跨要素列表进行缩放/标准化?
- 你会用什么样的缩放方法?(标准缩放/最小-最大缩放?)
#Using sklear's StandardScaler package from sklearn.preprocessing import StandardScaler
X_scaled = StandardScaler().fit_transform(X)
8.极端值
- 有没有异常值?(使用方框图快速可视化您的异常值)
- 这些是真正的离群值吗?(基于您对该领域的了解)
- 如果是,这些离群值需要处理吗?
- 你会做什么样的异常处理?你会限制离群值,将它们从数据集中移除,还是让它保持原样?
import seaborn as sns
df.RM = df.RM.fillna(df.RM.mean())

RM 列的箱线图显示有一些异常值
9.数据不平衡
- 如果您正在构建一个分类模型,并且您的数据中的所有类至少是几乎均匀分布的吗?(换句话说,你的数据平衡吗?)
10.转换
- 基于数据的偏斜度,数据需要任何转换吗?
- 要应用的相关转换是什么?(对数变换/平方根/立方/逆变换)
在任何转变之前
import seaborn as sns
sns.displot(df.Price); # plot without transformation
对数变换后
sns.displot(np.log(df.Price));

- 如果是文本数据呢?(您想标记文本数据吗,例如:使用
nltk的word_tokenize或sentence_tokenize) - 将数据离散化(是否有可以分类的连续值?)
11.特征工程
- 可以从现有数据中创建哪些附加要素?
例如:对于一个日期字段,你可以得到一周中的某一天,某一个月中的某一天,并且在外部数据集的一点帮助下,得到额外的信息,例如假期列表
12.扔掉
- 对于连续变量,你想选择什么样的区间?
- 这些间隔是恒定的还是基于某些领域知识的,您会创建自定义的间隔条柱吗?
- 如果它们是分类值,您会使用什么业务逻辑来分类和绑定它们?
# Sample code demonstrating the creation of a new variable - Income_category and bin them based on the value in the 'Income' fielddf1["Income_category"] = pd.cut(
x=df["Income"],
bins=[0, 15, 30, 50, 100,500,1000,10000],
labels=["Low_income", "Lower_Middle", "Middle", "Upper_Middle","High_Income","Rich","Super_Rich"]
)# You can see the distribution of the numbers in the new variable
df1["Income_category"].value_counts()
13.分类编码
- 有分类的数据类型吗?
- 它们是有序的吗?你会做什么编码?(分类/标签编码)
14.特征选择/缩减
- 有可以删除的功能吗?最后,您将在您的模型中选择哪些特性,以及基于什么?
- 您是否检查过多重共线性(对于不喜欢密切相关的 x 变量的算法)
结束语
这是一个初学者指南,可以帮助你考虑为数据预处理步骤建立清单。你还可以在清单中添加许多其他步骤。
每个清单都有一个代码片段也是一个好主意,这样你就可以定制代码,大大减少实验时间。
线性回归的清单

照片由 Unsplash 上的Hatice yardm拍摄
用于实现回归算法的待办事项列表
关于线性回归的在线资源有两种。第一种描绘了一幅简单的图画,好像只用几行代码就可以实现回归并做出很好的预测。
另一方面,回归是作为一种复杂的算法引入的,具有令人生畏的概念,如使用脊/套索/弹性网格的正则化、超参数调整、交叉验证等。
我不打算去任何一个方向,我更愿意把重点放在你在实现回归时可能会遇到的一系列问题以及解决它们的方法上。我称之为“回归清单”。
请注意,下面提供的清单不是一个线性的工作流程,它应该被视为一个迭代过程,直到您对您的模型满意为止。
什么是线性回归?
线性回归是一种统计程序,用于找出两个或多个变量之间的关系,并根据该关系进行预测。例如,我们知道年龄和身高是两个相互关联的变量,也就是说,随着年龄的增长,孩子会越长越高。机器学习利用这个统计概念来进行预测。这意味着,如果我们知道孩子的年龄,我们可以合理地预测他们的身高。
如果只有一个变量被用作预测因子(例如,年龄作为身高的预测因子),那么它就是简单线性回归。有时会使用一个以上的预测因子——它们被称为多元回归。多元回归的一个例子是根据树的年龄和高度预测树的体积。
下面是一个典型的机器学习工作流,其中列出了数据科学家在回归项目中遇到的问题。
1.数据争论
收集数据的人和开发模型的人往往是不同的人。因此,当数据集被移交给建模人员时,它会以各种形式出现,这需要相当多的工作——首先要理解数据,然后将其转换为可用于建模的格式。
因此数据争论简单来说就是清理数据,理解每个特征代表什么,并将数据转化为更好的形式。您可以采取的一些初始步骤包括:
- 检查特征名称和测量单位。
- 检查数据集的大小和形状(行&列)。
- 检查特征的数据类型是否需要类型转换(例如,将字符串转换为数字数据类型)。
- 运行描述性统计来查看数字数据的集中趋势和分散情况,并观察任何不寻常的情况。确保为分类变量单独运行描述性统计。
- 检查的分类特征是否存储为“对象”,如果是,可能需要将其转换为“类别”类型。
2.特征选择
预测值和因变量之间的线性关系是回归的先决条件。如果一个特征没有显示出与因变量的任何关系,它就不会给模型增加任何价值。你应该消除它们。在这个过程的最后,找到一组有很强解释力的预测值。
如何找到最佳的特征集?这里有一些工具:
- 散点图和结对图
- 关联和热图
- 主成分分析( PCA )
- 线性关系的统计检验: t 检验,ANOVA
- 使用方差膨胀因子测试多重共线性(VIF)
3.特征工程
特征工程意味着完全改造一个或多个现有特征。这意味着您会生成一个在某些方面与原始要素不同的新要素。
以下是特征工程的清单:
a)通过分解现有特征来创建新特征:例如,通过拆分时间戳(例如 06/30/2021)列,可以获得新列:年、月、工作日、假假日等。
b)创建计算列:例如,从两个现有的列——住房数据集中的价格和建筑面积——创建一个新的计算列(" $/sqft ")。
c)特征转换
- 特征编码:标签编码器、一键编码器/虚拟变量
- 重新缩放:规范化、标准化
- 对数变换
- 数据类型转换
d)异常值处理/风向分析
- 统计技术:箱线图、分布图、散点图、四分位数间距(IQR)
- 时间序列:移动平均线
- 机器学习技术:局部异常因素(LOF)
e)缺失值处理
- 删除行
- 移除列(如果单个特征中缺少太多列)
- 用适当的值进行插补(如平均值、中间值)
- 插入文字
f)正规化
- 套索回归(L1 正则化)
- 岭回归(L2 正则化)
- 弹性网(L1 和 L2 正则化的结合)
4.创建基准模型
在数据清理、特征选择和特征工程之后,您就可以构建您的第一个模型了。这不是最终模型,而是一个“基准模型”用于以后对其进行改进。构建基线模型的关键步骤包括:
I)分别存储因变量(y)和预测变量(X)
ii) 将数据分割成训练和测试集
iii) 导入和实例化模型(带默认参数)
iv) 用模型拟合训练数据
v) 根据测试数据预测
5.性能赋值
一旦您构建了基准模型,接下来就要评估它是如何处理测试数据的。有许多评估指标可供选择:
- 误差指标:梅伊/姆塞/RMSE/MAPE
- 模型对比指标: AIC , BIC , R^2
- 剩余剧情
- Q-Q 剧情
- 常态检查的误差直方图
- 将误差(如 RMSE)与 y 值的平均值进行比较(检查误差与 y 值的平均值相差多远)
- K 倍交叉验证
6.模型调整
根据评估指标,您已经知道您的模型有多差(是的,您的基准模型应该是差的)。现在,您可以继续下去,看看可以调整哪些旋钮来使它变得更好。你迭代地做它,可能会以模型和数据的许多不同版本结束(所以确保你使用一个版本控制系统,例如 DVC 来跟踪变化)。以下是模型调整的一些选项:
- 正规化
- 拟合多项式
- 向前选择功能
- 反向消除
- 检查特征系数(剔除对模型贡献很小或没有贡献的特征)
- 交互效度分析
- 梯度下降
- 应用另一种算法(例如随机森林回归、支持向量回归)
7.最终模型
一旦您构建了满意的“正确”模型,就该将它部署到生产中了。生产可以简单到创建一个桌面应用程序,也可以简单到在云上进行商业部署。无论如何,清单中的最后几项包括:
- 完整数据集上的再训练模型
- 通过领域专家进行健全性检查
- 保存模型(例如在 joblib 中)
- 现实世界中的口译模式
- 定期检查模型漂移
一锤定音
本文的目的是介绍在分析管道中实现线性回归的“清单”——从数据清理到模型调整和部署。重申一下,建模是一个迭代过程,涉及到在过程中的每一步做出决策,因此,有一个清单将有望使数据科学家更容易地完成这个过程。下面是一些相关的文章,如果你想了解的话:
如果你有意见,请随意写在下面,或者通过媒体、推特或 LinkedIn 与我联系。
向 arXiv 提交研究的清单
向流行的开放存取学术文章档案馆提交 PDF 的 21 个问题

arXiv 是一个受欢迎的开放存取学术文章档案库,已经运行了 30 年,包括物理、数学、计算机科学、定量生物学、定量金融学、统计学、电气工程和系统科学以及经济学等领域的研究。arXiv 通过预印本帮助开放科学,并通过允许作者在科学过程中更早地分享知识并获得对他们的研究和想法的反馈来改善全球合作。今天,arXiv 仍然是最大的和指数增长的预印本服务之一。
向 arXiv 的提交过程是免费的,不包括同行评审。根据 arXiv 投稿指南中的“遵循学术交流公认标准的主题性和可评价的科学投稿”所有文章投稿都要经过精心准备。此外,还有一个由志愿主题专家进行的审核过程,以确保提交的内容遵循格式指南并具有学术价值。提交后,作者必须向arXiv.org提供永久许可以分发文章,并同意提交条款和协议以及提交说明。
无论你打算在哪里发表你的研究,文章提交过程,甚至在同行评议之前,都是令人困惑和耗时的!
为了使 arXiv 的提交过程更容易,我们将 30 多页的链接和提交信息汇编成一份 21 个问题的清单,其中包含向 arXiv 提交 PDF 的必要组件。我们不能保证您的提交将被接受,但希望这有助于你前进的道路!我们将这些放在一起,同时努力改善与 Curvenote 的科学交流,这是一个科学写作平台,包括一个专用的导出功能,集成了必要的格式要求,并为 350 多家期刊生成提交就绪文件。
以下清单包括一些问题,当回答“是”时,您可以继续下一个问题。如果您回答“否”,我们会提供更多信息和链接。我们建议你在写作过程中仔细阅读这份清单,以避免不必要的延误或额外的工作!祝你提交和研究好运!
必需的 arXiv 提交清单
1。)你是 arXiv 注册作者吗?
- 在这里注册。
- 必需的注册信息:
▹电子邮件地址—应该是最新的;强烈推荐学术或研究机构的电子邮件地址
▹用户名——应该是唯一的;不可更改!
▹密码—必须是 8 个字符,1 个非字母 - 个人信息
▹姓名
▹隶属关系
▹档案馆最有可能提交给 - 所有成员都必须遵守 arXiv 的行为准则
2。)您的电子邮件地址和联系方式是最新的吗?
- 在此更新您的用户信息。
3。)你是文章的原作者吗?
- 您是 arXiv 预先批准的代理人吗?
- 第三方或代理提交文章必须获得特别许可。点击了解更多信息并请求许可。
4。)你是文章的唯一作者吗?
- 所有合著者都同意向 arXiv 投稿了吗?
- 所有合著者必须同意向 arXiv 提交文章。点击了解更多。
5。)你的文章展示了“学术兴趣、原创性、新颖性和/或重要性”吗?
- 不包括实质性研究的文章,或者与最近提交的其他文章过于相似的文章可能会被拒绝。点击阅读更多关于 arXiv 审核指南的内容。
6。)您是否已经或计划向禁止在 arXiv 上发布您的文章的期刊或其他渠道提交您的文章?
7。)你为你的文章选择许可证了吗?
- arXiv 需要作者不可撤销的许可才能分享你的文章。这种许可是通过他们的可用许可证之一授予的。
8。)你知道你打算提交的档案和学科类吗?
- 查看 arXiv 上每个主要档案中的类别,选择最适合您文章的类别。
9。)您是否已被认可提交到您选择的主题类别?
- 你以前提交过那个档案和主题类吗?
- 第一次提交某个类别时需要得到认可,以确保 arXiv 贡献者是科学界的成员。背书不是同行评审的一种形式!
- 一些 arXiv 作者通过他们的学术或研究机构自动获得认可。
- 如果您不确定您是否被认可,您可以在此处开始提交流程。填写第一页上的信息,然后点击
Continue。如果您需要背书,请求背书信息将出现在页面顶部。在您获得认可之前,您将无法继续提交流程。 - 要获得签注,请遵循此处的指示。
10。)您的整篇文章(文本、图表、表格等)是否都包含在一个 PDF 文件中?
- 当你从 Curvenote 将你的文章导出为 PDF 格式时,所有内容都包含在一个文件中,我们已经满足了 arXiv 的 PDF 格式要求。
- 向 arXiv 提交的 PDF 文件目前不支持辅助文件。
- 如果你用多种语言提交文章的多个版本,两个版本应该合并成一个 PDF 文件。
11。)如果你的 PDF 中有版权声明,是否符合 arXiv 的 再分发许可 ?
- 版权声明只有在不违背提交和接受您的文章所必需的再分发许可协议的情况下才被允许。
12。)你的 PDF 文件名是否符合 格式要求 ?
- 仅包含这些字符:
a-z A-Z 0–9 _ + — . , = - arXiv 上的文件名区分大小写。任何提交的文件名称包含上述字符以外的其他字符将被拒绝。
13。)您的标题是否符合 web 提交表单字段的 格式要求 ?
- 不是所有的大写字母
- 没有 unicode 字符
- 只有一些 MathJax
- TeX 宏已扩展
- 拼写正确
- 正确使用 TeX 重音命令
- 对 arXiv 上其他文章的引用格式应为
arXiv:arch-ive/YYMMNNN或arXiv:YYMM.NNNN(N)
14。)您的作者列表是否满足 web 提交表单字段的 格式要求 ?
- 所有作者姓名都包括在列表中(编号等
- 作者姓名或首字母采用以下格式之一—姓氏是您的姓氏
First Last或First Middle Last或F. Last或F. M. Last - 按顺序输入了多个姓氏
First Last Last - 正确使用 TeX 重音命令
- 没有带“,”或“和”的名称
- 没有敬语(博士、教授)
- 没有学位后缀(医学博士、哲学博士)
- 输入为
First Last IV或First Last Jr的其他后缀 - 作者关系以下列格式之一放在作者姓名后面的括号中:
Author One (Affiliation1), Author Two (Affiliation1), Author Three (Affiliation2) Author One (1), Author Two (1 and 2), Author Three (2)
((1) Affiliation1, (2) Affiliation2)
- 作者姓名用逗号或“和”分隔:
First Last, First Last, First Last
First Last, First Last and First Last - 如果您以命名协作的形式提交,应使用以下格式之一。
- 由以下作者合作撰写的文章:
Collaboration: Author One, Author Two, Author Three - 文章由以下作者代写协作:
Author One, Author Two, Author Three for the Collaboration - 由以下合作成员撰写的文章:
Author One, Author Two, Author Three (the Collaboration)
15。)您的摘要是否满足 web 提交表单字段的 格式要求 ?
- 少于 1,920 个字符—当您尝试从 Curvenote 导出 PDF 时,如果您的摘要过长,您将会收到通知。
- 不包括“摘要”
- 只有一些带有 MathJax 的 TeX 命令
- TeX 宏已扩展
- 无纹理格式命令(
\it\,) - 正确使用 TeX 重音命令
- 对于新段落,所有回车后面必须跟有空格(缩进)。其他回车将被删除。
- 参考 arXiv 上的其他文章应使用以下格式
arXiv:arch-ive/YYMMNNN或arXiv:YYMM.NNNN(N) - 提交非英语或多种语言的文章时,需要提交英文版的摘要,并应按照以下格式提交:
English abstract
(intentional blank line)
-----
(intentional blank line)
Other language abstract
16。)如果您的文章有您所在机构的报告编号,您是否已将其包含在 web 提交表单字段中?
- 该字段中不应包含任何其他内容。
17。)如果您的文章已经出现在期刊或会议录上,完整的参考文献是否满足网络提交表单字段的 格式要求 ?
- 参考文献应包括卷号、年份和页码/范围。
- 多个引用应该用分号和空格隔开
- 没有 URL
- 只有以前出现在期刊或会议录上的论文才可以包含在这个字段中。你可以在你的 arXiv 文章提交后添加一个期刊参考,一旦它出现在其他地方。
18。)如果你的文章已经出现在期刊或论文集上,那么 DOI 是否满足 web 提交表单字段的 格式要求 ?
- 只有 DOI 应包含在此字段中。示例:
10.1016/j.cageo.2015.09.015 - 多个 doi 应该用空格隔开
- 只有以前出现在期刊或会议录上的论文才可以包含在这个字段中。你可以在你的 arXiv 文章提交后添加一个 DOI ,一旦它出现在其他地方。
19。)如果是提交到数学存档,是否有自己的数学分类代码,是否符合 web 提交表单字段的 格式要求 ?
- 根据数学科目分类在这里找到你的分类代码。
- 格式化您的分类代码:
▹“主要”和“次要”在括号中
▹多个分类关键字用逗号分隔
▹如果只有一个主要分类,“主要”关键字是可选的
20。)如果您要提交到计算机科学档案库,您是否有自己的分类代码,以及它是否满足 web 提交表单字段的以下 格式要求 ?
- 根据 ACM 计算分类系统在这里找到你的分类代码。
- 格式化您的分类代码,以便:
- 多个分类由分号和空格分隔
21。)有没有仔细检查标题、摘要、处理过的文件有没有错误?
- 如果您在提交后和公开发布前发现错误,您可以在您的用户页面的“操作”栏中点击“取消提交”。然后,您可以进行更改并重新提交您的文章。
- 如果你在你的文章公开发表后发现错误,不要修改后重新提交。应按照此处的说明替换原物品。
可选的 arXiv 提交建议
除了必要的组件之外,还有一些推荐的 arXiv 文章提交组件。我们在下面列出了这些组件的建议和常见使用案例。
评论
- 输入到 web 提交表单的注释字段中。
- 如果不为你的文章创建一个新版本,就无法编辑。
- 此处不应包含许可证信息的版权声明。
- 可选但推荐的注释包括以下内容:
- 页数
- 数字数量
- “提交”或“接受”期刊和/或会议记录信息
- 相关文件或信息的 URL—我们建议包含您的 Curvenote 原创文章的链接。
- 检查 URL 后面的任何句点或文本是否与 URL 分开,以便正确解释。
- 在存档中,URL 将显示为带有文本“this http URL”的链接,因此周围的文本应该相应地书写。
- 如果不是英语,请注明主要文本的语言或不同语言的多个版本。
- 参考 arXiv 上的其他文章应使用以下格式
arXiv:arch-ive/YYMMNNN或arXiv:YYMM.NNNN(N) - 作者角色—例如“附录作者”或“编辑者”
- 如果这是一个替换版本—对更改的程度和替换的原因进行注释。
- 正确使用 TeX 重音命令
大图像文件尺寸
- 在提交的 PDF 文件中包含大量数字会降低某些读者对您的文章的阅读速度。arXiv 建议使用有效的数字来降低文档大小。
随着 arXiv 继续支持和推进开放存取出版物,我们在 Curvenote 的使命是帮助科学家、研究人员和工程师通过交流加速科学发现。作为一个专注于协作的科学写作工具,我们明白只有当我们分享我们的想法并一起工作时,科学才能向前发展。
Curvenote 努力在作者研究工作流程的每一步为他们提供支持,从笔记、起草、协作、再现和出版。在我们对不列颠哥伦比亚大学助理教授 Lindsey Heagy 博士的采访中,你可以了解更多关于 Curvenote 如何帮助我们的信息。要了解更多关于 Curvenote 的专用导出功能和 arXiv 格式模板的集成,请查看 Steve Purves 的博客 如何使用 Curvenote 导出到 PDF & LaTeX。
我们希望这份 arXiv 提交清单有助于加快您的进程,并尽早分享您的研究成果!
基本统计一览表
统计推断,实验设计,显著性检验,回归。
我最近在 Coursera 上参加了斯坦福大学提供的免费统计学入门课程。有了物理科学的背景,我的知识严重偏向于像玻尔兹曼分布和玻色-爱因斯坦统计这样的东西。因此,我想用一些更传统和相关的东西来补充我的知识库。
总的来说,我很喜欢这门课。我认为这很好,即使你已经知道一些材料,因为我对他们用来强调统计学中某些概念的重要性的例子印象特别深刻。下面,我想总结一下课程中涉及的几个话题。

照片由 Unsplash 上的 Edge2Edge Media 拍摄
统计推断
统计推断试图从人口样本中获得有关人口的信息。当我们感兴趣的人群如此之大,以至于我们没有资源从其中详尽地收集数据时,它通常变得相关。例如,假设我们想知道有百分之多少的选民赞成美国总统处理工作的方式。那可是大约 2.5 亿人口啊!获取这些信息的一个更容易管理的方法是使用统计学从几千个投票者的随机样本中进行估计。我们的估计可以表示为
估计值=参数+偏差+机会误差
这里,参数是我们感兴趣的人口数量(在我们的例子中,是所有美国选民中的支持率%)。因为我们是随机抽取,所以会出现随机误差。幸运的是,虽然它可以粗略计算,并可通过增加我们的样本量来减少。
另一方面,偏倚随着样本量的增加而增加,并且难以识别/量化。本课程讨论了两种类型的偏见:选择和参与。在这个例子中,当我们没有人口的代表性样本时,选择偏差就发生了。如果你走上芝加哥街头,询问选民对总统的看法,你就排除了其他城市的意见,引入了选择偏见。参与偏差有两个子类别:自愿和无响应偏差。假设你发布了一份在线调查。当有强烈意见的选民更有可能回应时,就会出现自愿偏见,因此你会得到极端的结果。因为这是一项在线调查,你可能会排除那些倾向于回避技术的老年人或那些来自低收入家庭、无法接触技术的人;因此,无反应偏差。
随机对照实验与观察性研究
观察性研究测量感兴趣的结果,并可用于建立关联。但是要确定因果关系,你需要随机对照实验。
考虑这个问题:“吃有机食品会让你更健康吗?”(对任何人来说,这都是一个奇怪的争论话题,还是只有我这样认为?你可以去全食超市测量顾客的体重和其他健康指标。然后你去麦当劳,因为缺少一个不那么政治化的例子,进行同样的测量。然后你用计算器算一算,发现在全食超市购物/吃饭的人(假设来说)比经常去麦当劳的人更健康。
但是这并不意味着是食物导致了这些数字。这可能只是意味着那些在全食超市购物的人真的很注意他们的健康,因此锻炼得更多,并真的试图保持身材(假设来说)。因此,要真正尝试建立因果关系,你可以做一个随机对照实验,其中有两组人,称为“对照组”和“治疗组”。不过,我很确定我听过“治疗”的其他叫法。随机性来自于将人们随机分组。然后,你给治疗组只吃有机食物,给对照组只吃非有机食物(你问什么是非有机食物?无可奉告)。经过一段适当的时间后,你进行这些测量以得出结论。
在这里,可能很难引入安慰剂,它类似于治疗,但却是中性的。安慰剂在流行文化中出现得如此频繁,所以我认为不需要更多的解释。此外,实验应该是双盲的,进行评估的人不知道哪个组是哪个组,以防止他们的结论受到影响。
正态和二项式分布
可以用正态分布近似二项分布!事实上,随着实验次数的增加,相应的直方图看起来越来越像正态分布。我们可以通过以下方式标准化二项式概率

其中 x 是成功的次数,μ是总体均值,σ是总体标准差,n 是我们进行实验/抛硬币的次数,p 是成功的概率。当通过二项式分布计算概率很繁琐时,这个公式很方便。举个例子,如果我们掷一枚硬币 100 次,问最多得到 45 个头的概率是多少,我们要计算得到 1 个头,2 个头,… 45 个头的概率,然后把它们加起来。通过用正态分布近似二项分布,我们可以得到 45 的标准化值,如下所示:

然后,我们可以使用一些软件来计算-1 左侧的正态曲线下的面积,以获得最多 45 个头的概率。
统计的期望值、标准误差、置信区间和抽样分布
假设我们知道美国男性人口的平均体重(μ)和标准差(σ)。然后,我们随机测量 100 名男性,发现平均值与总体平均值不同。我们再做一次,找到另一个平均值。如果我们一直这样做很多次,中心极限定理告诉我们,当我们绘制计算平均值的直方图时,它将看起来像一个具有均值μ和分布的钟形曲线

其中 x-bar 是大小为 n 的样本的平均值(本例中为 100),se 代表标准误差。这个公式告诉我们,随着样本量的增加,计算平均值的误差会减小。此外,一个相当反直觉的观点是,误差不取决于人口规模!本课程还将介绍和与百分比的标准误差,并且中心极限定理也适用于这些量。
然而,要应用中心极限定理,有几个注意事项。首先,我们需要替换抽样,这样每次抽取都是相互独立的。第二,兴趣的统计必须是总和(这里,平均值和百分比是伪装的总和)。最后,样本量需要足够大。人口直方图越倾斜,你需要的就越多。如果没有很强的偏态,大约 15 的样本量就足够了。
通常,估计值(或统计值)以如下置信区间报告:

其中 z 是对应于所需置信区间的 z 得分。例如,95%的置信区间对应于 z=1.96。在我们的例子中,估计值是一个美国成年男性的平均体重。我们首先随机测量 100 名男性,然后计算相应的平均值和标准差。然后我们代入 z=1.96,说‘我们有 95%的把握,男性的平均体重在平均+zSE 和平均-zSE 之间。但这意味着什么呢?如果我们再随机抽取 100 个样本,我们会得到一个稍微不同的置信区间。如果我们继续这样做,我们可以确定在 95%的情况下,总体平均值实际上落在我们的区间内。
如果我们不知道σ会怎么样?根据 bootstrap 原则,我们可以用样本的计算标准偏差来估计σ!
假设检验
为了测试我们的假设是否为真,我们需要将其与不为真的场景(也称为零假设)进行比较。假设你买了一枚硬币,但随后怀疑它不公平。在这种情况下,零假设是硬币是公平的。如果公平,那么我们期望得到正面或反面的概率是 0.5。但是然后你抛几次硬币,观察得到人头的概率是 0.7!这足够要求你退钱了吗?
为了回答这个问题,我们首先设置了一个测试统计量,来衡量如果零假设为真,数据与我们应该得到的数据有多远。一个常见的测试是 z 统计:

这里,期望值和 SE 值是在假设零假设为真的情况下计算的。|z|越大,我们假设成立的证据就越强。然而,如果我们再掷硬币 10 次,我们可能会观察到不同的统计数据。结果是,如果零假设为真,那么 z 遵循标准正态曲线。然后,如果 z 为负,你可以计算曲线下 z 左侧的面积,如果 z 为正,你可以计算曲线下 z 右侧的面积,从而得到 p 值。标准是,如果 p 值小于 5%,可以拒绝零假设。
当样本量很小时(小于约 20),你应该使用学生的 n-1 自由度的 t 分布,这样我们就可以用

这里,不同的 n 将给出稍微不同的 t 分布。因此,当我们报告平均值时,我们将通常的置信区间表达式替换为

测试导致错误决策的方式有两种。当我们拒绝一个被证明是正确的零假设时,第一类错误就发生了。当我们不能拒绝一个错误的零假设时,就会出现第二类错误。
分类数据的χ检验
在 3 种情况下,χ检验统计优于其他方法。
拟合优度检验用于确定一组分类数据是否来自声称的分布。例如,你有没有玩过《卡坦的殖民者》,并且怀疑死亡是不公平的?我的意思是,如果 2 是如此不可能,为什么它一直出现?!你可以用χ检验来解决这个问题。假设你掷骰子 60 次。零假设是“骰子是公平的”,这意味着我们应该期望看到从 1 到 6 的每个数字出现 10。然后,我们像这样合计我们观察到的计数:
使用χ检验,我们得到:

然后,我们选择对应于我们的自由度(类别数-1)的χ分布,在这种情况下是 5,并查找χ值(碰巧也是 5)。p 值是χ值右侧曲线下的面积。如果它低于 5%,我们可以拒绝零假设,并得到新的模具。

当我们试图确定两个或更多的子群体是否共享同一个分类变量的相同分布时,同质性检验是有用的。例如,我们可以查看泰坦尼克号上的乘客数据(可在 Kaggle 上获得),并询问幸存者与非幸存者的比例是否取决于阶级。我们首先从零假设开始,即所有人群的生存概率是相同的,并通过汇集我们的数据来计算每一类中的预期幸存者人数。假设有 2229 名乘客,只有 713 人幸存。这意味着机上所有人的生还概率为 32%,如果头等舱有 325 名乘客,我们预计会有 104 名幸存者。然后,我们统计每个班级的实际幸存者人数,得出下表:
其中括号中的数字是预期的计数。然后,使用上面相同的公式,我们计算χ。这种情况下的自由度是
(列数— 1) x(行数— 1) = 3
独立性检验用于确定给定人群中的两个分类变量是否相互关联。假设我们要查看的两个类别是性别和吸烟。然后我们从 500 人中随机抽取样本
其中括号中的数字是零假设为真时的预期计数。如果零假设是正确的,那么我们有

然后,我们可以将这个数字乘以总数,得到男性吸烟者的预期数 150。使用上面相同的公式,我们可以获得χ,并按照相同的程序计算 p 值。
独立性检验和同质性检验之间可能有些混淆。区别在于我们如何设计实验。在独立性测试中,从总体中随机收集单位,每个单位记录 2 个类别。在同质性检验中,从每个子群体中抽取随机样本。
线性回归
在线性回归中,相关系数 r 从-1 到 1 不等。符号告诉我们相关线的斜率是向上(+)还是向下(-)。幅度告诉我们 x 和 y 之间的线性关系的强度。如果幅度接近于零,当我们绘制 x 与 y 的关系时,我们不应该期望看到任何类似的直线。但是,r = 0 并不意味着 x 和 y 之间没有相关性。它只是意味着 x 和 y 之间的关系不是线性的。
我们线上的每一点都由

最小二乘法背后的思想是,最好的线是最小化的线

线性回归并不总是合适的回归方法。检验是否如此的一个好方法是将数据的散点图可视化,并检查它是否至少看起来像足球。另一个好的(补充的)方法是绘制残差图(预测值和真实值之间的差异),看看是否有任何可检测的模式。只有当残差的散点图无模式时,线性回归才是合适的。有时,我们可以转换数据,使结果图具有正确的形状(平方根和对数是流行的)。
总结
我会推荐这门课吗?如果你是新手或者想复习,我强烈推荐这门课。讲师不会浪费时间,他会用非常有启发性的例子非常透彻地解释这些概念。几乎每个视频的中间和/或结尾都有小测验。还有非同小可的周末测验,所以你有足够的机会来测试和巩固你的理解。此外,请记住,这门课涵盖了比我在这里谈到的更多的主题。
我的更多文章
*</6-common-metrics-for-your-next-regression-project-4667cbc534a7> https://medium.com/@h-vo/greece-used-reinforcement-learning-to-curb-influx-of-covid-19-a7d62ccfe508 *
跟踪数据科学进展的清单
入门
使用每周一天的原则来逐步勾选它
学数据科学有很多牛逼的课程。还有很多证明你成功的证书。
但是,你如何跟踪你的进展呢?你怎么知道你已经取得了什么,还有什么需要你去做?
下面的清单可以帮助你了解你的进展。它的目的是为用户寻找一个大致的轮廓,他们在他们的旅程。它没有强调具体的指南、课程和软件包,而是侧重于一般概念:

图片由作者提供。在这里的概念上可用,在这里的 PDF 上可用,在这里的 GitHub 上可用。
让我们详细介绍各个级别,从入门级开始,继续到中级,最后到高级的广泛领域。
入学水平
这是开始的地方。它涵盖了您未来旅程的基本要素。
数据处理
这一类别的目的是为了能够处理最常见的数据类型:
- 形象
- 文本
- 音频/时间序列
一般来说,这个阶段的数据集非常小,处理大于内存的数据集没有(精神上的)开销。典型的例子有经典的 MNIST 图像数据集( PyTorch 、 TensorFlow )、IMDB 评论文本数据集( PyTorch 、 TensorFlow ),以及小型音频或时间序列数据集。它们最多只有几百兆字节,可以轻松放入内存。
其中一些数据集需要最少的预处理(缩放图像、缩短句子),这通常不超过几行代码。由于示例的数量很少,或者它们很小,所以您可以在运行时轻松地进行处理(而不是预先运行单独的复杂脚本)。
总之,这一类别强调处理小型音频/时间序列、图像和文本数据集,并应用简单的操作来预处理数据。
网络
这一类别背后的目的是从经典的机器学习过渡到神经网络,并了解常见的构建模块:
经典的机器学习技术包括支持向量机、线性回归和聚类算法。尽管更复杂的亲戚,神经网络,似乎主导了最近的研究,但它们对小问题很方便——或者作为基线。在分析数据时,知道如何使用它们也很方便。
当向深度学习进展时,密集层是一个好的开始;我猜它们几乎被用在每一个(分类)模型中,至少用来构建输出层。
第二种常用的网络类型是卷积神经网络,其核心使用卷积运算。很难想象有任何成功的研究没有使用并受益于这个简单的操作:在输入上滑动一个内核,然后计算内核与其覆盖的补丁之间的内积。卷积运算从输入数据的每个可能位置提取一小组特征。难怪他们在图像分类方面非常成功,重要的特征分散在四周。
前两种网络类型主要用于静态输入,我们事先知道数据的形状。当你有不同形状的数据时,以句子为例,你可能会寻找更灵活的方法。这就是递归神经网络的用武之地:它们可以长时间保留信息,这使得“我今天早上起床穿衣后,带着我的狗散步”和“带着我的狗散步”中的“我”能够连接起来,以回答“谁带着狗散步了?”。
总之,这一类别侧重于使用简单的密集、卷积和递归神经网络对图像、文本和时序数据进行分类。
一般
此类别的目的是学习数据科学相关任务的一般处理。
重要的一步是了解数据本身。这不仅仅限于图像数据或文本数据,还包括时间序列和音频数据,以及任何其他数据类型。术语探索性数据分析(感谢 Andryas Waurzenczak 指出了这一点)最好地描述了这一步:使用技术来发现模式、离群值(超出公共范围的数据点)、子结构、标签分布,以及可视化数据。这可能包括主成分分析或降维技术。此时,您所使用的数据集通常已经被详细研究过了(尝试搜索数据集名称以找到有趣的特征),但是一旦您开始学习自定义数据集,现在学习这些内容将会有所收获。
您还将学习如何加载和保存上面的那些模型,以便您可以在以后重用它们。同样常见的是将关于数据的数据,即元数据,存储在单独的文件中。以 CSV 文件为例:第一列存储数据示例的文件路径,第二列存储示例的类。能够解析这个是强制性的,但是感谢许多库,这是一个简单的任务。
当你开始在那些小数据集上训练你的基本网络时,使用上面的那些架构,你会逐渐发现回调的有用性。回调是在训练过程中执行的代码,它们实现了许多功能:定期保存您的模型以使其防故障、停止训练或更改参数。最常见的已经内置在大多数库中,只需一个简短的函数调用就可以使用它们!
总之,这个类别让你学会处理在小数据集上运行神经网络的任务。
中间能级
在我看来,这是伟大的乐趣开始的地方,你会比你想象的更快地到达它!我发现这种转变很自然地发生了:你寻找一种更有效的方法来处理数据——在你意识到这一点之前,你已经为一个大型数据集编写了一个定制管道。
数据处理
这一阶段的目的是能够处理更大或更复杂的数据集,这些数据集可能需要以增强和定制预处理管道的形式进行特殊处理。
在这个阶段,数据集往往会变得更大,可能不再适合您的内存。高效的预处理变得更加重要,因为你不会让你的硬件闲着。此外,当您处理不同形状的样本、从数据库中提取样本或进行自定义预处理时,您可能需要编写自己的生成器。
或者处理复杂且不平衡的数据集,其中大部分样本属于一个类,只有少数样本属于所需的类。您将学到一些有用的技术:扩充数据以生成更多的次要类样本,或者缩减主要类的样本。
对于这两种数据集类型,自定义管道变得更加重要。当您想要快速迭代设置时,例如将图像裁剪为 32x32 而不是 50x50,您很少想要启动长时间运行的脚本。将这种可能性融入到您的管道中可以实现快速试验。
总之,数据集往往变得更复杂(认为不平衡)和更大(认为高达 30,40 GBs ),需要更复杂的处理。
自定义项目
这是中级的心脏。这背后的意图是定制项目,从而学习和使用许多其他类别的项目。
第一个层次可能会引导您在 MNIST 数据集上训练一个网络;在这个层次上,您自然会更加关注您自己的数据以及如何解析它。我只列出了三个主要领域:音频、图像和文本,但还有很多。
通过参与自己的项目,你可以将之前学到的知识联系起来,解决你面临的挑战。
培养
这一类别背后的意图是学习更多关于训练神经网络的知识。这个类别是中级水平上最大的类别,这是由于关注更高级的主题。
定制培训的第一步是使用迁移学习和微调。以前您通常会使用标准方法来训练和测试您的模型,但是现在您可能需要更强大的工具。这就是简单地重用其他从业者训练的模型的方便之处。你加载他们的模型,并在你自己的数据集上仔细训练他们。
万一您错过了一些特性,这就是您开始编写定制训练循环和定制回调的地方。想每 20 批打印一些统计数据?编写自定义回调。想累计 20 批以上的渐变?编写一个自定义的训练循环。
更复杂的循环可能需要更多的资源:多 GPU 培训即将到来!然而,这不是简单地增加第二个、第三个甚至更多的资源;你必须足够快地获取数据。请记住,多设备训练增加了复杂性。PyTorch 和 TensorFlow 已经完成了大部分的后台工作,它们只需对代码做一些小的修改就可以处理这种情况,但是前端部分就留给您了。(但是不要担心,有很多指南可以帮助你完成这件事!).
如果你像我一样,无法使用多个 GPU,那么下一步可能是在云上进行培训。所有主要的云服务都为您提供所需的资源。起初,从本地设置过渡到云计算会有一些(精神上的)开销,经过一些尝试后,这变得更容易了。(当我开始在 Kubernetes 集群上运行脚本时,事情变得非常棘手:什么是 Dockerfile?如何运行 jobs?如何利用当地资源?数据如何进出?经过试验之后,我已经习惯了,现在只需几个简单的命令就可以让我的脚本运行了)。
当你已经在云端,为什么不试试 TPU 培训?TPU 是谷歌定制的芯片。他们速度很快:一年前,我和一个团队一起对一个大型文本语料库进行分类,大约有 20 000 个文档,每个文档大约有 10 页文本。我们只在 CPU 上运行了我们的第一个实验,一个时期花费了 8 个小时。下一步,我们改进了预处理、缓存,并使用了单个 GPU:时间下降到了 15 分钟,这是一个巨大的飞跃。在阅读了 TensorFlow 的文档并摆弄了我们的代码后,我设法让 TPU 训练成为可能,一个纪元下降到了 15 秒。因此,我鼓励您升级您的渠道,并在 TPU 上开展培训。
总之,这一类别侧重于扩展实际的训练部分,向更复杂的训练循环和定制回调发展。
一般
这一类别背后的意图是了解除了使用普通架构之外还有什么可能。
既然您正在处理自定义数据集,掌握问题思维就变得很重要。让我这样解释:假设你正在处理一个音频数据集。你已经做了你的初始数据分析,结果发现你的数据不仅不平衡,而且样本长度不一样。有些只有三秒钟,有些有 20 秒甚至更长。此外,您只想对音频片段进行分类,而不是按原样对整个剪辑进行分类。最后,这是一个行业项目,所以有限制。把所有这些结合在一起是你所需要的。
幸运的是,你并不孤单。数据分析已勾选,预处理自定义数据也已勾选。这里最受关注的是工业部分。检查其他人做了什么(并发表在 GitHub 上),询问你的人,并尝试不同的技术。
比方说,您想要一个遵循自定义方案对数据进行规范化的网络层。查看您的库文档,您不会发现任何类似的已经实现的东西。现在是时候自己实现这样的功能了。和以前一样, TensorFlow 和 PyTorch(这使得这个方法更加简单)首先提供了一些很好的资源。
不久前的一个项目,我想写一个自定义嵌入层。在发现没有实现之后,我决定通过编写一个自定义的来解决这个问题。现在,这是一个挑战!经过多次反复试验,我终于想出了一个工作 TensorFlow 层。(讽刺的是,最后并没有证明比现有的更好)。
深度学习的另一个大领域现在开放了:生成网络。之前,您主要是对现有数据进行分类,但是没有什么可以阻止您生成数据。这正是生成网络所做的,他们最近的成功很大一部分来自一篇论文:生成对抗网络。研究人员真的想出了一些聪明的东西:不是训练一个网络,而是训练两个网络,并且随着时间的推移,两个网络都变得更好。详细检查他们的工作超出了这里的范围,但约瑟夫和巴蒂斯特罗卡做了一个了不起的工作解释他们在这里。
除了使用更多种类的模型,你还需要跟踪你的实验。当您想要理解模型的度量如何受其参数影响时,这是很有帮助的。说到这里,您还可以开始参数搜索,目的是找到优化目标指标的最佳参数集。
作为最后一部分,你肯定会用更高级的型号。除了那些生成网络,还有庞大的语言模型。听说过变形金刚(不是电影)?我敢打赌你有,现在是时候把它们用在你自己的项目上了。或者尝试访问 GPT 3 号,并与它讨论人工智能。
总之,这一类别扩展到了正常模型之外,并进一步探索了相关技术。
优等
这是最后一个阶段,尽管与前一个阶段的界限确实很模糊。祝贺你来到这里!只有一个类别可供您探索,因为这些项目是基于您之前的经验:
一般
即使你已经走了这么远,仍然有更多的事情需要探索。
第一项是庞大的数据集。虽然开始训练过程是你现在的例行任务,但这里的重点是快速训练,尽管数据集很大。我指的是数百 GB 甚至更多的数据集。想想 ImageNet 规模或堆规模。解决这些问题需要考虑您的存储选项、数据获取和预处理管道。不仅大小增加了,而且复杂性也增加了:一个几百 GB 的多模型数据集,其中每幅图像都伴随着文本和听觉描述?这需要一些思考。
当处理如此巨大的数据集时,可能需要运行一个多工人培训设置。GPU 不再安装在一台机器上,而是分布在多台机器上,即工人。管理工作负载和数据的分布变得很有必要。谢天谢地,PyTorch 和 T2 tensor flow 在这方面也有一些资源。
在您花了几个小时建立模型之后,为什么不部署它们呢?使用一些可以让你轻松构建一个好的 GUI 的库(比如 streamlit.io ),并部署你的模型,这样其他从业者就可以实时看到你的工作。
当你也做到这一点,进入强化学习。将它放在高级部分可能不是 100%正确的(基础可以很早就掌握),但是它与你目前从事的领域有很大的不同。一如既往,有一些 资源 可用让你开始。
既然你已经获得了大量的经验和知识,是时候做研究了。你在旅途中有没有注意到任何可以改进的地方?这可能是第一件要做的事情。或者通过向他们的存储库提供代码来为其他研究项目做出贡献。与其他爱好者合作,不断学习。
这可能是这里留下的最后一样东西:保持最新。数据科学是一个快速发展的领域,每天都有许多令人兴奋的新事物出现。跟踪重要的事情很难,所以选择一些时事通讯(我读过吴恩达和 deeplearning.ai 的 The Batch )来获得浓缩信息。为了缩小范围,你还可以使用安德鲁·卡帕西的 Arxiv 理智保护工具,它可以帮助你过滤符合你兴趣的论文。
总之,这一类别侧重于将强化学习作为一个全新的领域进行探索,为研究做出贡献,并保持最新——最后两点从未真正被选中。
接下来去哪里?
你可以在这里找到概念清单,在这里找到 PDF 。做一个拷贝,自定义,然后逐步打勾。
请在 GitHub 上留下任何建议或评论。
如果您正在寻找特定的资源:
- deeplearning.ai 的 TensorFlow 开发者职业证书 (事后试试 TF 考试,看看自己已经学会了什么!)
- Berkley 的全栈深度学习课程涵盖了部分入门级、中级和高级水平
- DeepMind 的高级深度学习&强化学习讲座涵盖了中高级水平的部分内容
- deeplearning.ai 的张量流:数据和部署专精和张量流:高级技术专精涵盖了部分中级和高级水平
如果你在寻找更具体的清单,你可以点击这里查看丹尼尔·伯克的路线图。
Thanks to 林彥良 and Andryas Waurzenczak at this point for pointing out a bias towards ML and DL; and to Elliot Gunn for providing feedback on a raw first draft.
问题
…不见了。
留下评论让我知道。请记住,本清单旨在作为一个总体概述,因此不包含与 NumPy/pandas/TensorFlow/… 合作或类似的具体项目。
某些类别之间不是有重叠吗?
是的,肯定的。不同类别的一些项目之间没有明显的分界线。例如,当您处理图像时,您可能已经将它们存储在磁盘上,而 CSV 文件包含所有文件路径和标签。因此,您可以一次勾选两个项目。
同样,同一类别中的两个项目有时会有重叠。这种情况发生在它们大部分是齐头并进的,但仍然有些不同的时候。
有顺序吗?
我最初试图维持每个类别的顺序(例如,在训练中),最容易的任务在顶部,较难的任务在底部。但是两个项目之间并不总是有明确的难度排序。以转移学习和微调为例:两者紧密交织,没有明确的先后顺序。
TPUs 有什么特别的地方给他们单独的地方?
虽然去 Colab 选择 TPU 很容易,但快速准备好数据是至关重要的。我在这一点背后的意图不是简单地在 TPUs 的帮助下运行一个模型,而是使您的管道非常有效,以便数据按时到达 TPU。因此,重点更多的是高效的预处理,而不是加速计算。
不可否认,在 TPU 上运行您的代码非常酷。
名单很长。怎么才能完成?
两个简短的回答:
—你不必;我是远离这一点的我自己。把它作为你下一步努力的指南。
—您不必先完成一个级别或类别,然后再进入下一个级别或类别。
还有一个更长的答案:
每周花一天时间,排除所有其他事情。
在其他日子,你可以做你的研究。周三、周四和周五参加课程。周末学习是额外的收获。
然后,周一你重复过去几天所学的内容,利用周末巩固知识。
在你被封锁的星期二,你把你的新知识应用到你自己的项目中。
这些项目不必很大:
学到了一些关于高效数据处理的知识?为您自己的数据建立一个管道(并勾选自定义管道)。
学会了图像分类?给你房间里的东西拍些图像,并分类(并勾选自定义图像项目)。
了解了一个具体的网络架构?使用您选择的库并简单地重新实现它(并勾选高级架构或卷积神经网络或两者)。
使用这些简短的独立项目来接触广泛的主题。你做的小主题越多,你就越容易开始那些一开始看起来令人生畏的大项目——因为它们只是由你已经采取的许多小步骤组成的。
跟踪机器学习进度的清单
入门
可能对深度学习有最小的偏见
你有没有问过自己,在你的机器学习之旅中,你目前处于什么位置?你还能学到什么呢?
这个清单可以帮助你回答这些问题。它提供了这个领域的大纲,分为三个大的层次:入门层次(每个人都从这里开始),中级层次(你很快就能达到),高级层次(你要在这里呆很长时间)。它没有列出具体的课程或软件,而是侧重于一般概念:

清单概述,图片由作者提供。在这里的观念和这里的 GitHub 上可以找到。
让我们从入门级开始,更详细地介绍各个级别。
入学水平

入门级产品概述。作者摘录;完整的列表可以在这里找到。
入门级分为 5 类:
- 数据处理侧重于能够处理小型数据集
- 机器学习涵盖了经典机器学习的关键概念
- 网络局限于 dnn、CNN 和 rnn
- 理论意在帮助你理解机器学习背后的思想
- 概述列出了您将在此阶段处理的主要事项
数据处理
这个类别侧重于使您能够处理小型数据集。
最常见的数据类型有
- 形象
- 声音的
- 时间序列
- 文本
这一级别的数据集通常已经内置在 PyTorch 或 TensorFlow 中;不需要额外下载。它们通常在尺寸和复杂度上都很小,并且它们通常适合于存储器。一个很好的图像示例是经典的 MNIST 图像数据集( PyTorch 、 TensorFlow ),用于 IMDB 评论数据集( PyTorch 、 TensorFlow ),用于文本数据,以及小型音频或时间序列数据集。
一些初学者友好的数据集需要预处理——这通常只不过是调整图像大小或缩短句子。几行代码就可以了。由于示例的数量很少,或者它们很小,所以您可以在运行时进行(而不是预先运行单独的复杂脚本)。
由于您正在处理经过仔细检查的数据,因此您通常会在网上找到有用的指南、提示和工作解决方案。
总结一下这个类别:重点是处理小型(内置)数据集和应用简单的预处理。
机器学习
不可否认,与深度学习模型相比,经典方法看起来很乏味。但是请相信有一天你也会遇到他们。有几个概念很突出:
回归重在回归数据。通常给你的是数据点,你不得不推断出那些看不见的数据点。这些新点依赖于已知(可观察)点,而回归的目的是找到这种依赖性。
聚类包含许多算法,它们都做同样的事情:将相似的数据样本聚类在一起,并将不相似的样本分开。已经出现了许多不同的方法来解决这个任务,但是我建议首先检查 k-Means。
最后,我们有支持向量机,一种更复杂的算法。支持向量机试图构建一个超平面,以最合适的方式将类别相互分离。一旦你了解了前两种方法,请阅读并使用这种方法。
总结一下:虽然这份清单的重点是深度学习,而不是经典的机器学习,但学习久经考验的基础知识也是有用的。
网络
这一类别侧重于从以前的经典 ML 走向神经网络。
密集层和密集神经网络通常是一个很好的起点。我猜这些层在 90 %的分类模型中使用非常频繁。例如,在最基本的设置中,您将输入乘以一个权重,并添加一个偏差:w₁ × x₁ + b
如果你有两个输入,用各自的权重相乘,然后加上一个偏差:w₁ × x₁ + w₂ × x₂ + b
如果你有三个、四个、一百个输入,那么对于单个神经元来说,这个过程是一样的。如果你有两个接收神经元和一个输入,那么每个神经元就有一个权重和偏差:w₁₁和 w₂₁,b₁和 b₂.如果有 20 个输入,每个接收神经元有 20 个权重,每个神经元有一个偏差。如果这让你感到困惑,不要担心。你拿到这个后,它就不是问题了。
名单上的下一个网络是卷积神经网络,简称 CNN。CNN 使用卷积运算,其中一个核遍历一个矩阵,并计算其自身和所覆盖的补片之间的内积。通过这种方式,您可以从数据中提取少量要素,并帮助网络检测这些要素,而不管其实际位置如何。换句话说:虽然图像上的鞋子可能在底部,但当它在上半部分时,网络也能发现它。很难想象有哪个成功的研究没有使用并受益于这个简单的操作。
第三大类网络是递归神经网络。当你必须长时间保留信息时,这些网络是有用的。比如,考虑一下这句话:“狗叫,然后它跳”。要推断“他”是谁,必须记住句子的开头“狗”。rnn 和它们的衍生物帮助你建模这样的时间依赖性。
总结这一类:你从密集的神经网络开始,它们容易理解,应用广泛。然后,您使用 CNN 提取分散的特征,并在时间依赖性起作用时继续使用 RNNs。
理论
这一类侧重于理论概念。这不仅仅是数学,更是对一般的 ML 和具体的 DL 技术的一种理解。
要做到这一点,学习数学符号是一个很好的开始。以σ为例。一旦你使用它一段时间,你会开始接受它的简洁。例如,与其重复地写“我们计算所有单个元素的总和乘以概率”,不如写:

这种方式更短,随着时间的推移,也更精确。我们要乘以最终的总和吗?还是我们把每个元素相乘?使用数学符号,这是清楚的。
留在数学领域,要学的一件事就是矩阵运算。还记得我建议作为良好起点的密集层吗?它们可以写成矩阵运算;使用“偏差技巧”,您还可以整合偏差。
学习像回归和聚类这样的经典机器学习技术也包括学习它们的背景。这是齐头并进的:一旦你学习了一种新的算法,你同时也学习了它背后的理论。一次勾选多个项目很好!
一旦你从经典学习转向深度学习——也就是说,一旦你开始使用神经网络——你就会遇到卷积运算。在入门级别,这是我建议熟悉的唯一技术。
当你开始用这样的层来增强你的网络时,你当然想要测量效果。这就是度量标准的用武之地,目标函数量化了你的方法的成功。准确度、均方误差、召回率和精确度是标准的。由于数据集不太复杂,所以还不需要切换到高级指标;你可以留着以后用。
总结一下这个类别:你学习阅读数学符号,普通技术背后的(数学)思想,以及如何衡量你的神经网络的成功。
一般
这个类别包含您早期学习的技术和最佳实践。
要使用神经网络,您需要使用一些有用的工具。其中包括一个 IDE(帮助你写代码的计算机程序)和 GitHub(一个为你的代码版本化的服务)。
如果你不熟悉一门编程语言,我建议从 python 开始。这种特殊语言的资源数量是巨大的。这是一个临界点;作为初学者,您希望避免构建自定义操作,并查阅文档来利用内置特性。
一旦您分析数据集,这些提供的功能也会派上用场。比如,取值范围是多少?是否存在一些异常值?他们有多极端?是否存在不可用的数据样本?这些是你想回答的问题,通常发生在旅途中:
你写一小段代码——因为数据样本不同而中断——你更新代码——你对数据有了更多的了解。
通常,您处理的数据在这个阶段受到监督,这意味着它包含了数据 → 标签映射。对于一个图像,这可能是一只狗或一只猫;对于文本,它可能是正面的,也可能是负面的;对于音频,可能是音乐类型。
这些映射是元数据的一部分,元数据是关于实际数据的数据。这些额外的知识通常是通过 CSV 或 JSON 文件提供的。因此,能够解析它们是件好事。幸运的是,主要的编程语言都提供了实现这一点的代码;几行就行了。
成功解析元数据并准备好数据后,您还需要将训练好的模型保存到磁盘上。保存和加载模型变得很重要
- 当你与其他学习者分享你的作品时,
- 当你想验证你的结果时,
- 当您只想将它们保存到磁盘上并在以后运行推理时
如果你也这样做了,你应该看看回调,在训练中执行的代码。想要定期保存您的模型吗?想提前取消训练?这些和其他标准回调是大型深度学习框架附带的。你不必从头开始写。一、两行代码就足够了。把复杂的回调留到以后。
总的来说,这一类别关注的是您将在早期遇到的一般事情,比如解析元数据文件、使用 ide 和分析数据集。
中间能级

中级概述。作者摘录;完整的列表可以在这里找到。
中间级别是您将花费大量时间的地方,这里有很多东西可以探索:语言模型、大型数据集、定制训练循环、超参数搜索,等等。但是,从上一级到这一级的过渡非常顺利。我会说这需要三到四个月的时间,这取决于你的背景,但是:你会比你想象的更快到达那里!
你学到的东西分布在六大类中:
- 数据处理关注大型数据集和定制预处理
- 定制项目希望你获得这方面的经验
- 网络包含了这个层面的几个趋势
- 训练涵盖了前两点带来的很多内容
- 理论侧重于拓展你的背景知识
- General 列出了您在该级别使用的几个项目
数据处理
数据处理侧重于更大更复杂的数据集、广泛的定制预处理和高效的数据加载。
我观察到,这个级别的数据集往往会变得更大或更复杂。或者两者都有。以前您处理的是可以轻松放入 RAM 的小型数据集,现在您可以处理 30 到 100 GBs 的数据。然而,这不应该吓到你。一旦你查阅了 TensorFlow 或 PyTorch 的文档,你就会知道该怎么做了。
不平衡数据集也是如此,这是现阶段的第二个趋势。标签——如果这毕竟是一个分类问题的话——不再平均分布,而是集中在一个大类上。这与更复杂的数据集密切相关,在这种情况下,数据可能具有不规则的形状。
然后你探索的一种技术是增强,人工改变现有的数据以产生新的样本。您可以将此应用于只有少量样本的类。或者,您可以随机旋转图像,使您的网络更强大。
说到图像,通常还需要对数据进行规范化。例如,一些图像可能是 400 × 400,但大多数是 350 × 350。这是一个重新标度的例子,一种可能的技术。但是,它们并不局限于图像数据。对于音频、文本和时间序列数据也存在其他策略。
在你标准化了你的数据之后,你必须把它提供给网络。简单地将它加载到 RAM 中不再可能,但是编写一个定制的生成器是可能的。你不必在这里重新发明轮子;有很多资源可以帮助你获得动力。
这种生成器也可以是自定义管道的一部分:在输入数据之前,在生成器中对数据进行预处理;不需要名为预处理数据的单独文件夹。你的管道也可能帮助同学;为什么不分享呢?
总之,这一类别侧重于大型、复杂、不平衡的数据集,这带来了几个需要克服的挑战。
自定义项目
定制项目是中间级别的核心。从我自己的经验来看,我保证你从做这些事情中获得巨大的协同效应:
您可以从一个新的数据集开始,甚至从头开始创建一个数据集。要做到这一点,你必须编写一个自定义管道(tick),处理大型数据集(tick),并使用高级(tick)和自定义层(tick)。
但是,自定义数据集不是强制性的。相反,你可以使用一个现有的,并尝试新的算法。或者你可以参加一个(Kaggle)挑战,击败你的竞争对手。
你看,通过一些你自己的项目,你会学到大量的新东西。这就是我们所追求的。
总的来说,这个类别可能很短,但是从您的定制项目中得到的东西是很长的。
网络
在中间层次,所使用的网络变得更加先进:
第一个趋势是网络变得更大。前一阶段的模型通常只有几兆字节大小,几层就够了。现在这将变成数百兆字节,通常使用超过 100、200、500 层。
第二个趋势是标准层被诸如批处理规范化、池化和注意等操作所取代。这些是更复杂的,如果你缺少一个功能:现在是时候写一个自定义层。
说到更高级的特性,生成网络和语言模型现在进入了人们的视线。有了你的基础,你现在就可以玩它们了——如果你能在本地运行它们的话(看看你,变形金刚)。如果你需要更多的马力,尝试切换到 Colab 并保留一个 GPU 或甚至一个 TPU。没有必要仅仅为了玩玩就去租一个云计算实例。一旦你完成了你的模型并准备好训练一些纪元,就这样做。
与其他网络一样,代码通常是可用的。例如, Hugginface 在他们的变形金刚库方面做得非常出色,TensorFlow 也有一些关于生成网络的教程。
总之,使用更高级的网络和图层与定制项目是相互关联的。
培养
培训类别关注的是与培训模特相关的所有东西。
如果我们的资源有限,我们可能无法完全训练一个网络——但是,我们可以训练它的一部分。这种技巧是迁移学习的一部分:
我们只需加载一个在类似任务上预先训练好的模型,然后在我们自己的数据集上对其部分内容进行微调。当然,您也可以利用一个完全不同领域的模型,比如图像分类,来对声音或文本数据进行分类。因为经典的预训练模型是随主要的 DL 框架一起提供的,所以您可以在这样的网络之间快速切换。
下一点是定制嵌入。嵌入是一种计算和使用存储在我们数据中的信息的聪明方法。一个例子:
一张桌子和一把椅子连在一起。这反映在两个单词的嵌入中,这两个单词只是任意大小(通常为~300)的浮点向量。前面提到的单词的相似性被处理到它们的向量中。这个概念并不局限于文本数据——尽管在那个领域它是直观的——而是也可以扩展到音频和图像数据。
嵌入主要是针对单个单词计算的。因此,您可以下载最常见的插件,如“the”、“like”、“You”、“car”。不利的一面是,这些可能无法反映您的数据中的知识:他们预先训练的数据集可能是关于汽车比赛的,因此具有不同类型之间的广泛关系:卡车、豪华轿车、跑车。另一方面,你自己的数据并不关注汽车,所以不必要的信息太多,有用的信息太少。简而言之,预先训练的嵌入不能反映数据点之间的相似性。解决方案是训练你自己的嵌入。这其实是在神经网络的帮助下完成的!
一旦您训练了定制嵌入,您可能也会对定制回调感兴趣。循环学习率?有可能。提前终止一个模型?有可能。训练时提取中间层的输出?同样,这是可能的,但需要努力。当标准回调没有提供您需要的东西时,只需实现您自己的回调。
接下来的点,数据并行性和多 GPU 训练,是紧密相连的。一旦你幸运地使用了不止一个 GPU,或者一旦你的训练花费了很长时间,这些概念提供了一种补救方法。您可以利用多个 GPU,而不是使用单个 GPU 来处理数据。输入在您的设备之间共享,每个设备运行相同的模型,并且它们的权重保持同步。
用更高级的模型实现这样的东西通常需要您编写定制的循环。不利的一面是,您必须从头开始编写训练逻辑,但是作为回报,您会获得洞察力和很多自由。PyTorch 和 TensorFlow 都提供了这方面的指南。
最后两项再次交织在一起:TPU 仅在云中可用,并且都需要一些努力才能实现。但是它们很快:对于一年前的一个项目,我的团队最初在 CPU 上运行一个模型,每个时期运行八个小时。使用 GPU 将时间缩短到了 15 分钟。经过一番争论后,我设法在 TPUs 上运行我们的代码,我们获得了每个纪元 15 秒的。真快。
从转移学习到云 TPU 培训,这一类别侧重于培训高级模型时附带的技术,通常是为了减少培训时间或实现自定义功能。
理论
这个类别收集了这个层次的想法背后的概念。
反向传播是这一切的动力;一个聪明的技术来计算你的网络更新,梯度。简单来说就是链式法则的应用,连续多次。这是可能的,因为您的网络可以被视为一个简单功能的(复杂)链。这些简单的功能是你的网络的操作,而一个层的激活功能在这里起着至关重要的作用。
听说过 ReLU 吗?这就是这样一个激活函数——不使用任何函数(或者只使用线性函数,例如 y = x+2)会将你的网络简化为一个线性分类器。(有关这方面的更多信息,请参见注释)。全世界都在研究这个。以泄漏的 ReLU 或 LReLU 为例:
正常的 ReLU 是 y = max(x,0)。如果你的输入低于 0,这将设置导数为零。这意味着向后传递中后面的每个渐变(即前面的每个层)也是零。这就是 LReLUs 的原因:当 x < 0 时,y = αx,否则就是 y = x,这是解决上述问题的一个简单方法。
还有更多的功能可供选择:GeLU、swish、卢瑟等等。只要你能推导出来,就没问题。
如上所述,计算梯度需要导数。一旦你有了它们,优化器的任务就是应用更新。这就是学习率发挥作用的地方:
wᵘᵖᵈᵃᵗᵉᵈ=wᵒˡᵈ——学习率×梯度
这是普通梯度下降的更新规则,它与梯度相反。其他优化器有自己的更新步骤,看看 Adam 或 RMSProp 了解更多。
首先计算梯度需要一个损失函数。这个函数告诉你对于给定的输入,你的输出有多好。标准损失是交叉熵、均方误差等等,但是对于更高级的任务有更高级的损失。
当准确性本身不再起作用时,这通常与更高级的度量标准相结合。对于不平衡的数据集,精确度不是一个有意义的度量;在这种情况下,切换到宏 F1。提示:为了在以后计算任何丢失的度量,保存网络的预测和基本事实。
在这个层次上,掌握概率是必要的一步。我从自己的经历来说:还可以。从许多来源听到统计和随机数据后,情况会变得更清楚。在计算数据的平均值或正常数据范围时,您可能已经使用了一些方法。看看 Coursera 了解更多,或者浏览顶级大学的讲座,这些讲座经常免费上传。(能够免费获得高质量的资料真是一件幸事!).
因为我们已经在讨论高级主题,所以试着理解高级层的概念。退学是一个好的开始,BatchNorm 是下一个候选人。
这同样适用于学习正则化。一般来说,这些是在不影响测试结果的情况下减少训练错误的技术。换句话说:让你的网络在训练数据上做得好,而不是在单独的测试数据上做得差。
**理论范畴的最后一点是相当模糊的:了解常见问题。我打算让你获得与深度神经网络相关的广泛知识。这包括消失梯度、模式崩溃和过拟合等现象。你可能会在旅途中遇到他们中的一些,然后知道发生了什么是有利的。最终,这有助于你解决问题,并让你下次避免它们。
总之,这一类别涵盖了巩固你对机器学习的理解的相关主题。
一般
这一大类涵盖了您在这一级别会遇到的各种主题。
从有监督的数据——可以使用(人类)注释的数据——开始,您现在可以扩展到无监督的数据。使用生成技术时就是这种情况。你也可以结合两个世界的优点:训练一个自动编码器来生成真实的图像,并在另一个分类器网络中使用学习到的权重。这是某种(高级)迁移学习。
处理任何数据,无论是否受监督,通常都需要将数据分成一个训练集(也称为开发集)、一个验证集和一个测试集。将数据分成三部分很容易,但您通常会希望进行分层拆分,在所有拆分中,类百分比保持大致相似。例如,当您有 100 个数据点,A 类和 B 类有 50 个数据点时,您希望在所有三个拆分中都保留此比率。
为了以后追溯您所做的任何事情,不仅仅是在分割数据集时,对代码进行版本化是一个很好的做法。您不需要维护 100 个文件夹。使用对你有帮助的工具会更容易、更舒服:Git 是第一位的。
这与跟踪你的实验是一致的。跟踪可以包括存储模型,但基本上意味着保存任何指标和设置。对于我几个月前的一些跑步,我最近不得不检查平均跑步时间。如果我没有使用像 Weights & Biases 这样的工具来保存实验数据,我可能已经被束缚住了。有了它,获得所需信息就相当容易了。
这同样适用于模型配置:要复制运行或论文,您需要知道每层的过滤器数量、内核大小、退出概率等等。跟踪你的实验因此成为一件强制性的事情。它不仅客观地告诉我们哪些跑步做得最好,而且看到这些图表填充起来也很酷:

实验跟踪的样本可视化;作者写的。你能看出这里可能发生了什么样的问题吗?
进一步进行实验跟踪,您还可以进行超参数搜索。为了确定最佳值,您可以并行启动多次运行。每次运行使用一组不同的参数值,一个中央协调器决定哪一个导致更好的分数。这是一个时间和计算密集型过程,需要一些初始设置。但是一旦你完成了一次搜索,你就为下一次做好了准备。
已经花了很多时间来确定最佳度量、最佳参数、最佳优化器,您不希望让最终的模型变得尘土飞扬。相反,使用一些工具,如 streamlit.io 来快速构建应用程序原型,然后使用您的云技能来部署您的模型。最后,记录代码并在 GitHub 上发布您的发现。像我这样的人会为此感谢你的!
看了这么多,练了这么多模型,你肯定在问题思考方面有了一些经验。我不确定这是不是真的,但它只是意味着知道当 xyz 发生时该做什么。这可能会简化问题,在 StackOverflow 上提问,或者咨询其他人。这是我们学习过程中必不可少的一部分:即使两个问题可能完全不同,我们总会从之前的第三个问题中学到一些我们现在可以使用的东西。
这对于您的工作和对其他项目的贡献都是一项有用的技能:像 Hugginface 和 PyTorch 这样的大型软件包依赖于社区的反馈来发展。运用你所学到的知识进行合作。
最后,记住所有这些,要知道这是一个持续不断的过程。提出新发现并证伪旧发现是研究领域的一部分。阅读论文是了解这一进展的好方法,但它永远不会真正完成。
从抽象开始。这部分通常在实际论文之前提交,所以作者在这里花一些时间。它总结了一篇论文的故事。事后妄下结论。如果你认为这篇论文适合你,那么你可以完整地阅读它。对于那些得到大量媒体报道的报纸,聪明人可能已经写了一篇博文,用他们的方式解释了。检查一下也无妨。
总之,这一类别涵盖了关于机器学习进展的广泛主题:从创建数据分割到阅读论文,这里有很多内容。
优等

高级水平概述。作者摘录;完整的列表可以在这里找到。
相比中级,这个显得矮了点。那是个陷阱!这块地很大。到目前为止,你所学的一切都在这里发挥了作用。优化者?发明你自己的。自定义训练循环?现在创建一个全新的算法。参与项目?在报纸上发表你自己的文章。
以下所有类别都扩展了上一级别的概念:
- **数据处理现在专注于庞大的多模态数据集
- **定制项目现在具有生成技术
- **培训建立在之前的基础上,变得更加复杂
- 理论扩展到全新的领域
- **概括性延伸到更广泛的话题
数据处理
这一类别比中间级别的对应类别更进了一步,并转移到更大的数据集。
形容词巨大是对数据集大小相对模糊的描述。就 GB 而言,这超过了 100、200 GBs。就纪元时间而言,这是单程一个多小时。这取决于您可用的计算能力——您的多 GPU 或 TPU 培训知识会派上用场。你可以申请 TPU 研究云来加速你的训练。
加速也可以通过运行分布式管道来实现。对于数据集预处理,Apache Beam 就是这样一个框架;对于 TensorFlow,我知道你也可以分发数据预处理(不是训练而是实际的预处理)。您肯定希望在这里使用 Kubernetes 集群或类似的集群。大型云提供商(AWS、GCP、Azure)提供相关解决方案。
这些分布式管道在处理多模型数据集时非常有用。您使用的不再是单一的数据类型,而是多种数据类型的组合。一个简单的例子是文本和图像,其中文本描述了图像的内容。存储和解析这些数据可能会很麻烦;您可能想编写一个定制的生成器来使事情变得更简单。
总之,这一类别扩展到更大和更复杂的数据集。
自定义项目
此类别仅包含一个项目:
处理自定义创成式项目。GAN 的领域已经变得相当大:BGAN,GAN,GAN,CycleGAN,…选择一个并使用自定义数据集。上一层的很多东西都在这里汇集:正则化、高级损失、概率、自定义图层。因此,这是一个将你所学付诸实践的好机会。*
培养
**训练类别现在专注于加速训练。
随着数据集越来越大,训练时间也越来越长。第一种方法是使用混合精度训练。该模型尝试使用 16 位精度,而不是使用 32 位精度。确保数值稳定性(舍入误差等。),部分权重保持其原始类型,并在可能的情况下使用 16 位精度计算。最近的 GPU 有特殊的硬件来加速 float16 格式的计算;这种训练通常可以加快 2 到 3 倍。
减少培训时间的第二种方法是从多 GPU 扩展到多工人培训。这些设备不再安装在单个节点上,而是分布在多个节点上。如果你能在一个地方安装它们,那么就这样做,因为在运行多工人安装时会有通信开销。然而,这通常被限制为每个节点 8 或 16 个 GPU,如果这还不够,您仍然可以通过将培训负载分配给多个工作人员来扩展计算,每个工作人员有多个 GPU。
当然,如果你有多个 TPU,那么训练时间也会减少。这然后使用 TPU 吊舱;谷歌在这里有大量的文档。从 8 个 TPU 内核到 2048 年,有一种神奇的感觉。您肯定希望确保您的代码是高效的,因为计算时间就是金钱。
最后一步主要是为大于 RAM 的模型定制的:模型并行训练。在此设置中,模型不再在特定设备上复制,而是放置在所有设备上。因此,例如,一组层可能由 GPUs 0 到 4 处理,下一组由 GPU 4 到 8 处理,依此类推。这也是当 a 层激活大于可用 RAM 时诉诸的技术。
综上所述,训练重在速度。越快越好。
理论
理论部分介绍了机器学习广泛领域中的几个全新领域,从图形神经网络开始。
图可以模拟许多数据交互。想象一张地图,其中顶点是特别感兴趣的地方,边是它们之间的连接。这样的结构也存在于分子和社交网络中。图形神经网络使用 DL 中的技术来使用这种图形存储的数据。有一个大的正在进行的研究领域。
**开放式也是如此。这是整个列表中最有趣的一点。想想大自然,想想经常从你窗前飞过的鸟儿。他们并不总是那样看。他们也不会永远保持这种状态。进化在这里起了作用,尽管这个措辞太弱了。进化不是一个过程;是工作中的某件事发明了一切。你,我,电脑,鸟,鱼,树。而且绝对没有完成。因此,进化是一种永不终止的算法(T2 停顿问题,有人知道吗?).人工智能的一个希望是实现类似的东西,创造新的东西。就像我们人类一样。
这个方向的第一步是进化算法,它创造了一些东西。但只是少量。和自然/进化比起来,这根本不算什么。这同样适用于我们日常做的许多事情。一千个俯卧撑?当然,但是大自然创造了鸟、植物、狗、猫、树——试着与之匹配。倒立?好吧,但是大自然创造了鱼、牛、猪、花、细菌——与之匹配。我们已经取得了很多成就,但还有很多需要学习(和发明)。看看这篇文章,了解更多关于这个有趣的话题。
一个更容易进入的领域是强化学习。我已经把它放在高级水平,以免吓到初学者。如果我在早期看到了 RL 的所有概念(事实上,我看到了),我会怀疑我是否能掌握这一切。这些概念并不难,但是对于初学者到中级用户来说太难了。一旦你有了一个坚实的基础,你就可以开始学习 RL 了。
**理论范畴的最后一点是扩展你的知识,超越计算机科学。知道得多总是有益的,生物学、化学、心理学、物理学……都是你可以涉足的伟大领域。这并不是说你必须做一个完整的学习,而是这些领域的一两门课程已经足够了。
生物学是我们研究的一个很好的例子:拥有数十亿连接的 GPT x,图灵 NLG 和威震天 LM,以及拥有惊人的一万亿参数的开关变压器。他们只能做一两件事。而作为人类,我们无法插入额外的硬件或在几分钟内处理数 TB 的数据,因此我们可以做得更多。边走边解数学?明白了。游泳和重播面试?明白了。写论文吃零食?明白了。
睡觉、吃饭、走路、游泳、淋浴、哭、笑、跳、摔、潜、爬、开车、惊奇、学习、写作、作曲、创作、做梦、思考的时候,运行你的心?甚至不需要我们的注意。
与一个人或一群人所能做的相比,这些模型有所欠缺。这也是生物学、化学、物理学和其他领域可以帮助我们提高技术水平的地方。
总之,你可以从标准的 DL 任务扩展到那些重要的任务。
一般
这个类别列出了在高级阶段出现的一般模式。
高效的代码:显然,这在早期很重要,但是当你在 5 天内用 128 个 GPU 在 3 台服务器上训练你的 5 亿个参数模型时,你真的想要确保你的代码很快。这不再是一个人的项目,而是一个团队的努力。看一看的变形金刚论文告诉我们,它是许多人的共同努力才能让事情发生。
诚然,从高效代码到量子深度学习,这是一个相当大的挑战。但是在这个层次上,你已经学到了太多,以至于不能不看一看这个领域。它被列在一般而不是理论中,因为它是一个强烈分离的领域。生物学可以促进算法的发展,但量子物理学是一个完全独立的领域。还是那句话,主要动力是做事,要快。尝试一系列密码的组合是乏味的。同时尝试一千个很快。我们甚至不必看得太远;从候鸟身上,我们目前假设它们利用量子效应来感受地球磁场。大自然又一次参与其中。
如果你还没有完成,实现论文是一个很好的实践:你学习新的东西,巩固你的编码技能,甚至挣钱。
说到论文,现在是从阅读论文到理解(大部分)论文的时候了。你会很自然地专注于几个领域,并试图抓住那里的东西。这是跟上时代的一部分。人工智能研究领域发生了太多事情:
一年前,我和 ML 有一个关于 NLP 的讲座。我们有一些模型大小的图表,威震天 LM 被列为最大的模型之一。然后,演讲结束后不久,GPT-3 出现了,爆炸规模。这只是一个领域。还有 GANs,音频模型,图像分类器,还有很多很多。所以这归结为有一个总的概述,由时事通讯提供,如吴恩达的 The Batch ,然后专注于你自己的领域。
那就是研究了。和前面两点一样,这一点永远不会完全做到。
总之,您需要高效的代码,并希望保持最新。
接下来去哪里?
你可以在 GitHub 这里找到清单,在这里找到概念。复制定制,然后逐步打勾。
如果您正在寻找特定的资源:
- deeplearning.ai 的 TensorFlow 开发者专业证书涵盖了大部分入门级,可以作为 TF 考试的备考(此处涵盖)
- Berkley 的全栈深度学习课程涵盖了部分入门级、中级和高级水平
- DeepMind 的高级深度学习&强化学习讲座涵盖了中高级水平的部分内容
- deeplearning.ai 的张量流:数据和部署专精和张量流:高级技术专精涵盖了部分中级和高级水平
如果你正在寻找一个更具体的清单,你可以查看丹尼尔·伯克的路线图这里。
进一步说明
- 这个清单以我自己的经验来看是有偏差的。
- RNNs 可以用于任何类型的数据。然而,逐渐消失的坡度和长时间的训练使它们更难训练。
- PyTorch 和 TensorFlow 没有太大区别。作为初学者,没有压力去选择一个而不是另一个。然而,一旦您有了更多的进展,您可能会遇到一个问题,并注意到它在其他框架中可能更容易。那是相当普遍的,但是我这么说:
你遇到这样的问题,说明你在物质的深处;因此,转换框架将导致从头开始的巨大开销。因此,硬着头皮挺过去往往更好。(我在 TensorFlow 写自定义图层的时候也有过类似的经历。那是多么大的努力啊!). - 嵌入不限于单个数据点。例如,你可以计算完整句子的嵌入。此外,预制致密层也经常用作嵌入层。
- 嵌入只是浮点的数组:[0.1213,0.542,1.993,...],可以经常检查。
- 我注意到,不使用激活函数可以使您的网络可复制到单个线性分类器。这对于仅密集的神经网络是正确的。这样做的问题是,你只能学习线性依赖,这一般不是你想要的。
- 生成技术可以扩展到监督数据,例如,CycleGAN 就是这样做的:你在两个已知的域之间转换,因此需要每个域的标记数据。不过,最初的生成技术,即最初的 GAN、AEs 和 VAEs,并不依赖于这些标签。
- 我确实错过了一些事情。请留下评论,让我知道。
- 你不必去上大学课程来学习所有这些。互联网已经使知识的传递民主化,所以只要看看 YouTube、谷歌的博客、OpenAI 的博客、DeepMind 的博客等等。
绘图图表的简洁风格
开箱即用的默认图表看起来很棒,但我经常发现自己做了一些常见的更改来使它们“流行”。
直到最近,当我在构建一个报告管道时,我才发现了 plotly 的定制模板功能。在此之前,我制作图表,然后编辑它们的外观。使用 plotly 中的自定义模板功能,您可以定义在创建图表时应用的自定义样式。这个方便的功能降低了我的报告管道的复杂性,并且已经成为我在专业和家庭中使用 plotly 的核心部分。
为了了解这是如何工作的,让我们创建一个玩具数据集。请参见下面所需的导入和数据集的生成,以帮助说明如何使 plotly 图表看起来更专业。
出于本文的目的,我们将给出一个使用条形图的例子,因为它是对两个分类变量和一个数值进行编码的好选择。我们将沿 X 轴编码动物,沿 Y 轴编码高度,并用颜色编码腿的数量。

帕特里克·科菲的图片
虽然上面的图表本身没有任何问题,但它看起来有点无聊和普通。我觉得灰色的背景对我来说有点太暗了,标题和标签字体也有点太淡了。让我们定义一个自定义样式模板,并将其应用到我们的图表中,以解决其中的一些问题。
在上面的自定义样式模板中,我们将标题字体改为 Lato,因为这是一种很好的可读字体,就宽度而言也很紧凑。这使得标题可以更长,而不会脱离图表的边缘,这是一种更好地描述图表试图传达的故事的简单方法。
印刷术是一门我无法理解的艺术;我选择试错法!
Nunito 是我用这种方法找到的一种字体,我用它作为轴标签。对我来说,它看起来有点像漫画 Sans 的方形版本,讽刺的是在图表上看起来很专业。如果你的标签很长,那么试试 Lato 可能是个好主意,因为它是一种稍微精简的字体,在同样的宽度下可以容纳更多的字符。让我们看看当我们应用这个样式时,我们的图表是什么样子的!

帕特里克·科菲的图片
好了,现在我们开始有所进展了!除了字体之外,我们还做了一些其他的改变,让图表看起来更好。我们采用了不同的配色方案;我通常选择 plotly 内置的 G10 配色方案作为起点。它似乎被我为之制作图表并在许多不同美学领域工作的人们广泛接受。它可能有点亮,但我认为这让我们的编码数据从其他图表元素中脱颖而出。背景颜色已被切换为白色。
这允许来自编码数据的更大的负对比度,这再次帮助它“弹出”。
Y 轴网格线使用非常浅的灰色阴影,并且非常细;这使得他们仍然可以被看到,但不会在解读图表时给读者增加太多的认知负担。这些标签也受到了类似的待遇。它们被略微变灰,以避免分散读者对数据的注意力,当读者需要它们时,它们仍然在那里作为参考,但它们不会下意识地将用户的视线从数据上引开,直到它们准备好。
我使用的另一个常用技巧是为我的图表创建一个副标题。这使我能够在副标题中描述数据范围或收集方法等重要信息时有一个简洁明了的标题,这为图表添加了一些很好的上下文(有时甚至在需要数据集属性时是强制性的!).
在 plotly 中,我们可以通过在标题字符串中添加一些 HTML 格式来实现一个副标题。
我们将加粗标题,强调它,添加一个换行符,然后使用一个缩小字体大小的 span 元素来添加一个副标题。

帕特里克·科菲的图片
就是这样!现在,您可以使用我坚持的自定义样式作为起点,在 plotly 中制作专业外观的图表。祝好运和快乐的阴谋!
ggplot2 中的数据可视化集合

迈克尔·泽兹奇在 Unsplash 上的照片
入门
对初学者来说也是很好的学习材料
对于 R 用户来说,ggplot2 是最受欢迎的可视化库,拥有大量可用的图形。它使用简单,能够用简单的命令快速生成复杂的图形。对于 R 用户来说,没有理由不使用 ggplot2 进行数据可视化
正如我前面提到的,有很多选项和图形可用。没有人能记住所有这些。所以,手里有一份小抄或指南是很有帮助的。本文试图为从基础到高级的一些常见类型的情节制作一个很好的指南或备忘单。
为了做这里的所有练习,我使用了 RStudio。希望这对你也有好处。让我们从基础开始。
如果您还没有安装 ggplot2,这是安装它的命令:
install.packages("ggplot2")
安装完成后,需要像这样调用这个库:
**library**("ggplot2")
ggplot 可以使用了。是时候做一些很酷的策划了。
基本散点图
该平台内置了大量数据集用于练习。我将在本文的所有演示中使用其中的一些,从非常流行的 iris 数据集开始。
data(iris)
请使用 head()命令查看数据集的前 5 行,以检查数据集
head(iris)

数据集足够小,可以在这里显示一个截图。下面是如何在 ggplot 中绘制一个基本的散点图。萼片长度将在 x 轴上,花瓣长度将在 y 轴上。
ggplot(iris, aes(x=Sepal.Length, y=Petal.Length))+geom_point()

代码非常简单明了。第一个输入是数据集本身。然后在美学上,有‘x’和‘y’参数。最后是定义绘图类型的函数。在这种情况下,“geom_point()”。这个函数可以接受一些样式参数,这些参数将在本文后面讨论。
按颜色区分物种
数据集显示,有不同的物种。有趣的是,不同物种的萼片长度和花瓣长度各不相同。它只需要在美学上增加一个颜色参数。我们将颜色设置为物种。所以,不同的物种会有不同的颜色。
ggplot(iris, aes(x=Sepal.Length, y=Petal.Length, col=Species))+geom_point()

区别很明显。有些人喜欢用不同的形状来表示不同。下面是怎么做的。
ggplot(iris, aes(x=Sepal.Length, y=Petal.Length, col=Species, shape=Species))+geom_point()

可以使用透明度添加另一个变量。我将添加阿尔法参数,并将其设置为花瓣长度。所以花瓣长度越短,点就越透明。
ggplot(iris, aes(x=Sepal.Length, y=Petal.Length, col=Species, shape=Species, size=Sepal.Width, alpha=Petal.Width))+geom_point()

太多传说了!有时候少即是多。我们应该去掉一些传说。让我们摆脱阿尔法和大小传说。只有物种就够了。“指南”功能将有助于消除图例。
ggplot(iris, aes(x=Sepal.Length, y=Petal.Length, col=Species, shape=Species, size=Sepal.Width, alpha=Petal.Width))+geom_point() +guides(size=FALSE, alpha=FALSE)

不是每个参数都适用于每个图。
在这里,我将展示另一种方法,在同一个图中显示所有五个变量:
ggplot(iris) + geom_point(aes(x=Sepal.Length, y=Petal.Length, colour=Sepal.Width, size = Sepal.Width, shape=Species))+guides(colour='legend')

直方图
像往常一样,从最基本的直方图开始是个好主意。这是萼片长度的分布。
ggplot(iris, aes(x=Sepal.Length))+geom_histogram()

增加一些颜色和一个变量会更好看,同时也更有意义
ggplot(iris, aes(x=Sepal.Length, fill=Species))+geom_histogram()

三种颜色显示了三种不同物种的分布。
这是另一件很酷的事情。除了显示另一个变量,还可以使用一个条件作为颜色。如花瓣长度> 4。
ggplot(iris, aes(x=Sepal.Length, fill=Petal.Length > 4))+geom_histogram()

条件也可用于散点图。如果您还没有尝试过,请尝试一下。
这个情节有一个问题。两个图相互叠加。这可以通过增加透明度并将位置设置为“身份”来避免。
ggplot(iris, aes(x=Sepal.Length, fill=Petal.Length >4))+geom_histogram(position='identity', alpha=0.4 )

现在看到隐藏的部分了吗?或者在发行版中简单地使用侧边栏是另一种解决方案。因为该位置需要设置为“闪避”。
ggplot(iris, aes(x=Sepal.Length, fill=Petal.Length >4))+geom_histogram(position='dodge', alpha=0.7)

哪个是更好的选择?每个人都有自己的观点和选择。
柱状图
我将使用具有更多分类变量的不同数据集。另一个内置数据集名为“mpg”。
data(mpg)
这个数据集比上一个大。因此,这里不可能显示整个宽度的截图。请运行此代码查看前五行,亲自检查数据集:
head(mpg)
该数据集显示汽车的制造商、级别、公路里程、汽缸、年份、型号和一些其他变量。
这是一个基本的条形图,显示了数据集中每个制造商的汽车数量。这里还介绍了一个功能,即“主题”。这里我改变了文本在 x 轴上的角度。稍后我们会看到更多的“主题”功能的使用。
ggplot(mpg, aes(x=manufacturer))+geom_bar()+theme(axis.text.x =element_text(angle = 90))

制造商的名字不得不放在 45 度角,因为它们变得太杂乱了。在这个图中,只有 x 的值是足够的。因为 ggplot 可以计算幕后的计数。
如果您只需要百分比而不是计数怎么办
函数‘after _ stat’需要作为美学中的 y 参数使用。以下是方法:
ggplot(mpg, aes(x=manufacturer, y = after_stat(100*count / sum(count))))+geom_bar()+theme(axis.text.x =element_text(angle = 90))+ labs(y="Percentage of cars")

如果你注意到,我们在这里改变了 y 轴的标签,否则,它会把百分比公式作为 y 轴标签。
添加一些额外的样式和信息
为了这个演示,让我们制作一个新的情节。这次 x 轴将代表类别,y 轴上将有“hwy”。该图还将引入“stat”参数。在“geom_bar”函数中,我们将使用“stat”作为“summary ”,使用“fun”作为“mean ”,以条形图上的点的形式获得平均值。以下是完整的代码:
ggplot(mpg, aes(class, hwy))+geom_bar(stat='summary', fun="mean",
fill='steelblue')+geom_point()

这些点也显示了数据的差异。
有些人更喜欢抖动点而不是规则点。这里有一个例子。情节之后我会再解释一些:
ggplot(mpg, aes(class, hwy))+geom_bar(stat="summary", fill='steelblue', col='black')+
geom_point(position=position_jitter(0.2), size=2, shape=15)+
labs(
title="hwy for Each Class",
x = NULL,
y = NULL
)+
theme(panel.grid=element_blank(),
panel.background = element_rect(fill = 'White'),
panel.border=element_rect(colour = 'white', fill=NA, size=0.2))

我喜欢我的情节尽可能的简洁明了。这里,x 轴和 y 轴的标签被拿掉了。仅仅因为它感觉是多余的。标题上写着‘hwy’代表每一类”。x 轴上的名字清楚地表明它们是类。那么 y 轴上有什么呢?所以,已经很清楚了。在“labs”函数中,x 和 y 被设置为空,以避免 x 和 y 标签。
在上方的柱状图中增加一个变量
上面的情节只是简单的“阶级”对“公路”。在 x 轴上添加另一个分类变量将使它提供更多信息。我为第三个变量选择“cyl”。
ggplot(mpg, aes(class, hwy, fill=as.factor(cyl)))+
geom_bar(stat="summary", fun="median", col="black",
position="dodge")+
geom_point(position=position_dodge(0.9))

此处,x 轴显示了每个类别中不同“类别”和不同“气缸”类型的不同条形图。在“2seater”类中,该数据集中只有 8 种“cyl”类型可用。所以,那一个占据了整个宽度。同样,“超小型”类拥有所有四种“气缸”类型。它显示那里有四个小节。正如我们之前讨论过的,position = 'dodge '将条形并排放置,而不是堆叠在一起。
箱线图
另一个常用的可视化。它在一个盒子里提供了关于数据的伟大的基本统计。如果你对盒状图完全陌生,这里有一个教程解释了如何从盒状图中检索数据。虽然情节是用 Python 写的。但是箱线图的解释是简单明了的英语。这对任何人都适用。
这是一个基本的箱线图:
ggplot(mpg, aes(class, hwy))+
geom_boxplot(fill='steelblue', col="black")

代码很简单。它接受美学函数中的数据集、x 和 y 参数,我决定将颜色放在 geom_boxplot 函数中。
给它添加更多的风格:
ggplot(mpg, aes(class, hwy))+geom_point()+
geom_boxplot(fill='steelblue', col="black", , notch=TRUE)

增加了一些缺口,给人一点有趣的看法。
抖动图
抖动图实际上是散点图的修改版。它随机地将点展开一点点或展开指定的量,这样点就不会彼此重叠。
这里有一个例子。我将把 class 放在 x 轴上,把 hwy 放在 y 轴上。
ggplot(mpg, aes(x=class, y=hwy), )+geom_jitter(width=0.2)

如果你画一个散点图,那些点将会在一条垂直的直线上。因为这是一个抖动图,这些点有点分散。当宽度参数设定为 0.2 时,点分散在该范围内。请随意更改 geom_jitter()函数中“宽度”参数,以查看不同宽度下的图形变化。
添加更多信息到其中
添加一个红点,代表这些点的平均值。
ggplot(mpg, aes(x=class, y=hwy), )+geom_jitter(width=0.2)+
stat_summary(aes(x=class, y=hwy), fun=mean, geom='point', colour='red', size=3)

那些红点使情节变得更漂亮,也更有用。
小平面网格
非常有用。因为它提供了一个以更清晰的方式比较数据的机会。我们将在第一个情节之后解释更多。因为一张图胜过千言万语。
在这次演示中,我将使用不同的数据集。让我们在这里导入数据集:
gap = read.csv("[https://raw.githubusercontent.com/resbaz/r-novice-gapminder-files/master/data/gapminder-FiveYearData.csv](https://raw.githubusercontent.com/resbaz/r-novice-gapminder-files/master/data/gapminder-FiveYearData.csv)")
head(gap)

我将该数据集命名为“gap”。请看看上面数据集的前五行。这是不言自明的。
这是我们的第一个面网格图:
ggplot(gap, aes(x=lifeExp, fill=continent))+ geom_histogram(bins=20)+ facet_wrap(~continent)

这就是小平面网格。在这里,我们用一行代码在同一个图中制作了每个大陆的预期寿命直方图。您也可以使用上一节直方图中显示的技术将所有直方图放在一个图中。
但这是另一种方式。而且看起来也很干净清晰。我想删除图例,以节省一些空间。这样图片会有更多的空间。反正传说看起来没必要。
ggplot(gap, aes(x=lifeExp, fill=continent))+ geom_histogram(bins=20)+
facet_wrap(~continent)+
theme_minimal()+
theme(legend.position = 'None')+
xlab("Life Extectancy")+
ylab("count")

参数的图例。“位置”被设置为“无”以删除图例。
但也可以设置为“上”、“下”、“左”或“右”
这个情节中添加的另一个重要功能是“theme_minimal()”。这样,它在这里给出了一个有点不同的背景。
有这么多不同的主题可供选择。以下是其中的一些:
theme_bw(),
主题 _ 经典(),
theme_dark(),
theme_gray(),
主题 _ 灯光(),
theme_dark()
还有很多。
一个使用这些大陆数据的散点图,其中 x 轴为“gdpPercap ”, y 轴为“lifeExp”。
ggplot(gap)+
geom_point(aes(x=gdpPercap, y=lifeExp))+
facet_wrap(~continent)

虽然我认为在大多数情况下保持相同的规模会有所帮助。因为在尺度相同的情况下,很容易比较不同大洲的数据。但与此同时,当数据如此混乱时,从头到尾改变尺度可能看起来不错。
ggplot(gap)+
geom_point(aes(x=gdpPercap, y=lifeExp))+
facet_wrap(~continent, scale='free')

每个散点图都有自己的刻度,这意味着不同的 x 轴和 y 轴值。
也可以在刻面 _ 包裹功能中使用‘自由 _ x’或‘自由 _ y’作为刻度,只改变 x 轴或 y 轴的值
“facet_wrap”可以接受多个变量。回到我们的“mpg”数据集,让我们使用“year”和“drv”为 facet_wrap 绘制一个 facet_grid 图。
ggplot(mpg)+
geom_point(aes(x=displ, y=hwy))+
facet_wrap(~year+drv)

请随意多做一些实验。
最后一个使用 facet_wrap()的例子包含了很多风格的想法
该图将再次使用“mpg”数据集。这是一个柱状图,显示了各个年份每个制造商的汽车数量。
ggplot(mpg)+
geom_bar(aes(y=manufacturer))+
facet_wrap(~year)+
labs(title = "Number of cars per manufacturer",
x = NULL,
y=NULL)+
scale_x_continuous(expand = c(0, NA))+
theme_minimal()+
theme(
text = element_text("Tahoma"),
strip.text = element_text(face='bold',
hjust=0),
panel.grid.major = element_line('white', size=0.6),
panel.grid.minor = element_blank(),
panel.grid.major.y = element_blank(),
panel.ontop = TRUE
)

这里使用了 theme_minimal()。但是看,即使你使用内置的主题,你也可以加入你自己的风格。在这个图中,x 和 y 的标签被拿掉了,因为标题说明了一切。面板网格次要项留空,面板网格主要项设置为“白色”,大小为 0.8。那给情节增添了许多风格。添加'面板。“ontop = TRUE”将这些面板样式添加到绘图顶部。如果你删除这条线,那些细网格就不会显示出来。
包括一些统计参数
在本节中,我们将添加回归线和置信区间。在本演示中,来自“mpg”数据集的“displ”将放在 x 轴上,“hwy”将放在 y 轴上。颜色将被“cyl”改变。
ggplot(mpg, aes(x= displ,
y = hwy))+
geom_point(aes(colour = cyl))+
geom_smooth()+
labs(x = 'displ',
y = 'hwy',
title = "displ vs hwy")+
theme_minimal()

上面的 geom_smooth()'函数添加了这条回归线和显示每个点的置信区间的置信带。
在统计学的不同情况下,标度变换是一种非常常见的做法。我现在不打算深入细节,因为那不是本文的范围。但我将在 ggplot 中显示比例转换,而不是使用默认回归,而是在绘图中使用线性回归线。
ggplot(mpg, aes(x= displ,
y = hwy))+
geom_point(aes(colour = cyl))+
geom_smooth(method='lm')+
scale_x_log10()+
labs(x = 'displ',
y = 'hwy',
title = "displ vs hwy")+
theme_minimal()

在此图中,通过 x 轴使用 log10 变换,并通过在 geom_smooth()函数中设置“method='lm '引入线性回归线。请随意尝试 y 轴上的 log10 变换。
结论
ggplot 库有大量可用的图形。更多的几何图形,更多的风格。我可以在这篇文章中展示一些。如果你是一个初学者,并且完成了上面所有的练习,你已经走了很长的路。同时,还有很长的路要走。我以后会在 ggplot 上做更多的教程。我希望你能在你的工作或项目中运用这些想法。
如何不分析时间序列

艾萨克·史密斯在 Unsplash 上拍摄的照片
我看到初级数据科学家和面试候选人犯的最常见的时间序列数据错误之一是假设数据有规律的跳动,没有间隙。这是一个糟糕的假设。
以一次面试练习为例,我向候选人提供了一个数据集,其中包含数百个应用程序的下载和使用指标。我要求候选人从投资的角度来看,找出哪些应用是有趣的,并暗示增长可能是有趣的一个很好的指标。候选人通常会直接计算增长率,方法是将每个应用程序的最新分数与最新分数之前的分数进行比较,然后根据计算出的增长率对应用程序进行排名。这假设每个系列中的最新数据点代表相同的时间点,并且相应的先前点在 1 个时间单位之外。事实证明,我提供的数据集不是这样的,会导致不正确的结果。数据集中的许多时间序列都有缺失的数据点,并且刻度的间隔不均匀或不一致。正如您在下面的可视化示例中看到的,不考虑变化的分笔成交点大小和数据缺口的简单逐点分析就像将苹果与橙子进行比较。

两个不均匀勾选的时间序列-作者图片
大多数真实世界的数据集将遭受这种类型的问题,因为它们通常是从有缺陷的系统和过程中排出的,这些系统和过程在设计时没有考虑到分析用例。因此,在进行任何分析或建模之前,理解数据中的这些缺陷并正确地解释它是很重要的。否则,很可能导致垃圾进,垃圾出。
那么,对于时间序列数据,我们如何做到这一点呢?我通常遵循的步骤是
- 通过直观查看示例时间序列,了解时间序列的时间范围和刻度粒度
- 将每个时间序列中的实际刻度数与(最大值减去最小值时间戳)除以刻度间距得出的理论刻度数进行比较。该比率有时被称为填充比率-远小于 1 的值意味着缺少很多刻度。
- 筛选出填充率低的系列。我经常使用 40%作为信息内容不足的界限,但这可能会根据手头的具体任务而有所不同。
- 通过向上采样到更精细的分辨率,标准化跨时间序列的刻度间距。
- 为您的分析使用适当的插值方法填充上采样分笔成交点。例如,采用最后已知的值,或者线性/二次插值等
下面是我用 PySpark 实现这种时间序列标准化方法的一些示例代码:
值得注意的一点是——我在分组的 PySpark 数据帧上使用了applyInPandas方法进行插值。在底层,它使用了 pandasUDF,相对于 PySpark 中的普通 UDF,它提供了巨大的性能提升。通过 Apache Arrow 实现更高效的数据传输,通过 Pandas 矢量化实现更高效的计算,从而提高了性能。只要有可能,总是尽量使用 pandasUDF。参见 API 文档了解更多信息。
为了更直观地说明代码执行时底层时间序列发生了什么,下面是流程各个阶段的图表。希望这能让人们明白,不经过标准化,就不能简单地开始逐点计算增长指标。

开始,不均匀打勾时间序列-作者图片
以 1 分钟为间隔进行上采样,并使用最后一个已知值进行插值,得出以下时间序列

向上采样并用最后一个已知值填充-图片由作者提供
上采样和线性插值产生以下结果

向上采样和线性插值—图片由作者提供
只是为了好玩,五次多项式插值

希望这是一次有益的讨论,我已经帮助你避免将来犯这样的错误。如果您有任何意见或问题,请随时联系我们。Twitter|Linkedin|Medium
海本与牛郎星之比较
两个流行的 Python 数据可视化库

约尔根·哈兰在 Unsplash 上的照片
数据可视化是数据科学的重要组成部分。揭示变量之间的关系有助于更好地理解数据。还可以使用设计良好的数据可视化来探索数据集中的底层结构。
在本文中,我们将比较两个流行的 Python 数据可视化库:Seaborn 和 Altair。
比较将基于使用两个库创建 3 组可视化。比较的重点是语法、图形结构以及这些库如何处理变量之间的关系。
我们将使用营销数据集来创建可视化效果。让我们从导入依赖项并把数据集读入 Pandas 数据帧开始。
import numpy as np
import pandas as pdimport seaborn as sns
import altair as altdf_marketing = pd.read_csv("/content/DirectMarketing.csv")df_marketing.head()

(图片由作者提供)
柱状图
第一组可视化包括基本直方图。
在 Seaborn 中,我们传递数据帧的名称和要绘制的列的名称。高度和纵横比参数用于修改地块的大小。纵横比是宽度与高度的比率。
sns.displot(df_marketing, x='AmountSpent', aspect=1.5)

Seaborn 直方图(图片由作者提供)
在 Altair 中,我们从一个顶级图表对象开始,它将数据帧作为参数。然后我们指定绘图的类型。encode 函数获取要绘制的列、关系和转换。我们放入编码函数的任何内容都需要链接到传递给图表的数据帧。最后,属性函数调整绘图的大小。
alt.Chart(df_marketing).mark_bar().encode(
alt.X('AmountSpent:Q', bin=True), y='count()'
).properties(height=300, width=450)

牛郎星直方图(图片由作者提供)
Altair 在数据转换方面非常高效。让我们解释一下为创建上面的直方图所做的转换。
alt.X('AmountSpent:Q', bin=True), y='count()'
这一行的作用是将“花费的金额”列分成多个箱,并计算每个箱中数据点的数量。
基于分类变量的分离
Seaborn 和 Altair 都提供了简单的方法,根据分类变量中的不同组来区分数值变量的数据点。
在 Seaborn 中,这种分离可以通过色调、颜色或行参数来实现。下面的代码将返回一个散点图的金额和工资列和分离将根据性别进行。
sns.relplot(
data=df_marketing, x='AmountSpent', y='Salary',
kind='scatter', hue='Gender', aspect=1.5
)

Seaborn 散点图(图片由作者提供)
在 Altair 中,我们在编码函数中使用颜色参数。类似于 Seaborn 的色相参数。
alt.Chart(df_marketing).mark_circle().encode(
x='AmountSpent', y='Salary', color='Gender'
).properties(height=300, width=450)

牛郎星散点图(图片由作者提供)
创建地块网格
在某些情况下,在一个可视化中使用多个图会更有吸引力或提供更多信息。每个子情节都可以用来强调某个特定的特征或关系,因此整体视觉传达更多的信息。
有许多方法可以创建多图可视化。在 Seaborn 中,我们可以使用 Matplotlib 的 pyplot 接口,通过指定图形中轴对象的数量来手动创建网格。然后,我们可以为每个轴对象生成一个图。
另一种方法是使用 FacetGrid 或 PairGrid 来自动生成绘图网格。基于给定的变量(即特征),FacetGrid 创建一个子图网格,它允许将数据集的结构转移到可视化中。Row、col 和 hue 参数可以看作是 FacetGrid 对象的三个维度。
g = sns.FacetGrid(
data=df_marketing, col='Gender', row='Location', hue='OwnHome',
height=4, aspect=1.4
)g.map(sns.scatterplot, "Salary", "AmountSpent").add_legend()

Seaborn FacetGrid(图片由作者提供)
我们首先使用 hue、col 和 row 参数构建模式。色调参数使用不同的颜色进行分色,而 col 和 row 参数分别使用 x 轴和 y 轴。然后,映射函数用于指定绘图类型以及要绘制的变量(即数据帧中的列)。
在 Altair 中,创建这种网格的逻辑与 Seaborn 非常相似。
alt.Chart(df_marketing).mark_circle().encode(
x='AmountSpent', y='Salary', color='OwnHome',
column='Gender', row='Location'
).properties(height=200, width=300)

牵牛星网格图(图片由作者提供)
除了颜色参数之外,我们还使用 encode 函数中的 column 和 row 参数向返回的可视化添加维度。
结论
Seaborn 和 Altair 都是流行的 Python 数据可视化库。对于大多数数据可视化任务来说,这两种方法都足够了。在不同方面都有一个比另一个小的优势。
我认为 Seaborn 的语法比 Altair 的语法简单一点,更容易阅读。另一方面,Altair 在数据转换方面优于 Seaborn。牛郎星还提供了交互式可视化。
感谢您的阅读。如果您有任何反馈,请告诉我。
训练 UNet 分割模型的合成与人工标记数据集的比较
理解大数据
作者:伦索林格 & 威尔昆兹@WallShots.co

将合成训练数据与传统训练数据进行比较(图片由作者提供)
手动标记数据既昂贵又繁琐。一种紧急的方法是使用合成数据生成来大规模减少标记足够大的数据集以进行分割所必需的提升。在本文中,我们使用一个合成数据集和一个传统标记的小型数据集创建了一个基准模型。在我们的例子中,没有现成的训练数据集,所以我们必须创建它。
我们正在构建的模型的目标是识别图像中的任何图片框架。具体来说,我们希望识别包含图片的图片帧区域(不是填充或帧本身)。本文描述了我们的解决方案架构,以及我们如何使用它来比较用两种不同方法创建的基线模型,以形成训练数据集。首先,我们生成一个简单的合成数据集,它由彩色矩形组成。我们将其与另一种方法进行比较,即所谓的“传统方法”,在这种方法中,使用我们的标记工具(Label Studio)找到并标记“野生的”。
我们针对这个问题的栈是 Label Studio,用 Python Fast.ai 训练环境进行标注。在本文中,实验基于带有resnet34主干的 unet 架构。
最后,我们将展示使用不同方法训练的两个基线模型的比较,以开发训练数据集。我们希望这篇文章对那些实现细分模型的人来说是一个有趣的案例研究。
对于传统的标签任务,我们使用 Label Studio。我们贴标设置的详细描述可在此处找到:
数据集
合成数据集
处理 1:在具有以下特征的 2000 幅合成生成的图像上进行训练:
- 单一矩形,即放置在背景“场景”上的“框架”
- 在界限内随机调整大小
- 在界限内随机着色
- 随机放置在固定大小(224x224)的背景矩形边界内

可视化小批量合成数据示例(图片由作者提供)
## Primary synthetic data generator method, amount of desired data can be adjusted ##
def _random_select_valid_tl(scene:MaskedImg, frame_template:dict, mask:MaskedImg):
"""
TODO: support multiple frames, currently could have overlapping frames
"""
sx,sy,sz = scene.img.shape
vertices = np.array(frame_template['label_data'])
fwidth = abs(
max(vertices[:,0]) -
min(vertices[:,0])
)
flength = abs(
max(vertices[:,1]) -
min(vertices[:,1])
)
tlx,tly = (
np.random.randint(0, sx-fwidth-1),
np.random.randint(0, sy-flength-1)
)
return tlx,tlydef add_frame_to_scene(scene:MaskedImg, frame_template:dict, mask:MaskedImg, plot:bool):
"""
In:
scene: np.array, mutable, frame gets written on top
frame: np.array, list of vertices
"""
# adjust the frame coords
tlx,tly = _random_select_valid_tl(
scene=scene, frame_template=frame_template, mask=mask
)
frame = np.array(frame_template['label_data']).copy()
frame[:,0] = frame[:,0] + tlx
frame[:,1] = frame[:,1] + tly
# create the "filled scene"
vertices_to_region(
mask_i=scene.img, # gets muted
label_data=frame.tolist(),
label_num=frame_template['color'],
plot=plot
)
# update the mask to reflect the frame added to the scene
vertices_to_region(
mask_i=mask.img, # gets muted
label_data=frame.tolist(),
label_num=frame_template['label_num'],
plot=plot
)
# plt.imshow(mask.img)
# plt.show()def select_random_color(color_list:list):
sc = color_list[np.random.randint(0,len(color_list))]
return color_list[
np.random.randint(0,len([x for x in color_list if x != sc]))
]def generator(scene_shapes, frame_templates, color_list, masks_path, scenes_path, plot=False):
errors = 0
for i in range(len(scene_shapes)):
scene_shape = scene_shapes[i]
frame_template = frame_templates[i]
# instantiate the scene
scene = MaskedImg()
scene.load_from_parameters(
shape=scene_shape,
value=select_random_color(color_list),
value_space='rgb'
)# instantiate the mask
mask = MaskedImg()
mask.load_from_parameters(
shape=scene_shape[:2],
value=0,
value_space='int'
)
try:
add_frame_to_scene(
scene=scene,
frame_template=frame_template,
mask=mask,
plot=plot
)
except:
errors += 1
continue
# plt.imshow(scene.img)
# plt.show()
# plt.imshow(mask.img)
# plt.show()
maskfp = f'{str(masks_path)}/{i}.tif'
scenefp = f'{str(scenes_path)}/{i}.jpeg'
mask.save(fp=maskfp)
scene.save(fp=scenefp)
print('Finished with {} errors.'.format(errors))exp_id = 0
scenes_path = Path("/ws/data/wallshots-framefinder/{}/scenes".format(exp_id))
masks_path = Path("/ws/data/wallshots-framefinder/{}/masks".format(exp_id))
scenes_path.mkdir(exist_ok=True, parents=True)
masks_path.mkdir(exist_ok=True, parents=True) n = 2000
color_list = [
(0, 0, 255), (102, 255, 51), (204, 153, 0), (255, 51, 204),
(51, 102, 153), (255, 0, 0), (0, 255, 0), (255, 255, 0), (0, 255, 255),
(128, 0, 255), (204, 102, 0), (153, 0, 51), (255, 102, 153),
(102, 255, 153), (204, 255, 153), (255, 255, 204), (51, 51, 0),
(126, 153,64), (230, 30, 120), (50, 23, 200)]scene_shapes = [(224,224) for i in range(n)]
lws = [(np.random.randint(10,100),np.random.randint(10,100)) for i in range(n)]
frame_templates = [{
'label_data': np.array([
(0,0), (0,lw[1]), (lw[0],lw[1]), (lw[0],0)
]),
'color': select_random_color(color_list),
'label_num': 1
} for lw in lws]generator(
scene_shapes=scene_shapes,
frame_templates=frame_templates,
color_list=color_list,
scenes_path=scenes_path,
masks_path=masks_path,
plot=False
)
手动标记的数据集
从互联网上选择 10 张图片,并使用我们的标签测试平台进行标签。我们再次使用 Label Studio 完成传统的标记任务,建立了一个新的 Label Studio 项目,这样我们就不会混淆我们为基准目的标记的图像和我们为训练目的创建的标签。
- 10 张基础图片取自“野外”资源,如谷歌搜索。
- 在没有显著微调的情况下,在“第一遍”中手动标记。
- 图像包含真实世界或真实世界人造场景中的图片框架

Label Studio 设置的屏幕截图(图片由作者提供)

可视化小批量的传统标签示例(图片由作者提供)
基准数据集
我们通过仔细选择“来自野外”的示例图像并使用 label studio 分段功能对其进行标记,创建了一个基准数据集。参见我们之前的一篇文章,该文章对此进行了深入描述:https://towardsdatascience . com/development-of-a-benchmark-dataset-with-a-interface-to-the-fastai-data loader-using-label-studio-d3aa 3c 26661 f
- 8 基本图像取自“野外”来源,如谷歌搜索。
- 手动标记,并进行重大微调。
- 图像包含真实世界或真实世界人造场景中的图片框架。
- 图像选择绊倒模型。例如,条纹墙、障碍物、景深、图像噪声。

可视化基准数据集(作者提供的图片)
实验
测量结果
每个实验中创建的模型都根据独立的基准数据集进行评估,该数据集旨在准确地表示模型的真实环境。单独的基准数据集将是实验结果的唯一真实来源。我们之所以有一个单独的基准数据集和自己的数据管道,是因为基准数据集的目的需要明确,并反映在创建它的底层流程中。这里定义了一个基准数据集来注入导致模型失效的例子。与此相反,训练数据集旨在使模型尽可能好。我们认为这可能是一个重要的区别,它保证了单独维护基准数据集的额外开销。
模型
这里,我们比较两个不同实验的结果。这些模型将非常相似,但不同之处在于它们训练的训练数据集。在每种情况下,模型的实例化都是相似的,使用 fastai unet_learner需要一个分段数据加载器。我们能够对所有数据集使用相同的目录结构,这简化了数据加载器的创建,因为它们可以共享相同的代码。该结构如下所示:
benchmark/
├── masks
│ ├── 1.tif
│ ├── 2.tif
│ ├── 3.tif
│ ├── 4.tif
│ ├── 5.tif
│ ├── 6.tif
│ ├── 8.tif
│ └── 9.tif
└── scenes
├── 1.jpg
├── 2.jpg
├── 3.jpg
├── 4.jpg
├── 5.jpg
├── 6.jpg
├── 8.jpg
└── 9.jpg
数据加载器实例化如下,其中saveto是文件夹位置(例如在benchmark/之上):
size = 224
imgs_saveto = saveto/'scenes'
dls = SegmentationDataLoaders.from_label_func(
imgs_saveto,
bs=6,
fnames=[
name
for name in imgs_saveto.iterdir()
if not name.is_dir()
],
label_func=
lambda x:
str(x).replace('scenes', 'masks').replace('jpg', 'tif'),
item_tfms=[Resize((size,size))],
batch_tfms=[
Normalize.from_stats(*imagenet_stats)
],
valid_pct=0.00
)
在传统创建的训练示例的情况下,valid_pct参数为 0.0,因为我们想要利用所有 10 个图像,因为图像很少。每一个都是珍贵的…
培养
我们通过绘制覆盖在来自基准数据集的输入图像上的预测掩膜,对两个模型进行了并排比较。为此,我们对用合成数据训练的模型运行了以下训练程序:
learn = unet_learner(
dls,
resnet34,
cbs=WandbCallback(),
n_out=2,
path=Path('.')
)learn.fit_one_cycle(10, slice(1e-2, 1e-3))
learn.save('model1-a-0')
learn.fit_one_cycle(10, slice(1e-2, 1e-3))
learn.fit_one_cycle(10, slice(1e-5, 1e-7))
learn.fit_one_cycle(10, slice(1e-6, 1e-7))
learn.save('model1-a-1')
对于每个历元,合成数据集比传统标记的数据集花费更长的时间。我们使用的训练策略只是给定数据的一个合理的起点。对于传统标记的数据集,我们运行以下程序:
learn = unet_learner(
dls,
resnet34,
cbs=WandbCallback(),
n_out=2,
path=Path('.')
)learn.fit_one_cycle(20, slice(1e-2, 1e-3))
learn.fit_one_cycle(20, slice(1e-2, 1e-3))
learn.fit_one_cycle(20, slice(1e-2, 1e-3))
learn.fit_one_cycle(20, slice(1e-2, 1e-3))
learn.fit_one_cycle(20, slice(1e-2, 1e-3))
learn.fit_one_cycle(20, slice(1e-5, 1e-7))
learn.fit_one_cycle(20, slice(1e-6, 1e-7))
learn.save('model2')
定性评价
在下图中,我们将调整了大小的输入上的预测掩码叠加到使用上面粘贴的策略训练的每个模型上。在左侧,图像通过仅使用合成数据创建的模型运行,而右侧是来自我们手动标记的数据集的模型。

用 2000 个简单合成生成的图像(左)与 10 个手动标记的图像(右)训练的模型的比较(图像由作者提供)
结论
这个练习的目的是对下一次投资做出决定。我们应该花费更多的资源来标记数据,还是应该进一步开发代码以使合成数据集更加真实?我们对传统上标记为训练数据集的 10 图像的有效性印象最深。因此,前进的道路是添加数据扩充,以更好地最大化来自传统数据集的每个训练样本。我们直观地看到,虽然很有希望,但使合成标签代码更真实的前期投资是很高的。因此,我们决定以后再来。有价值的方法是传统和合成标记方法的结合,这将使我们能够生成大量有点真实的标记图像,而无需在标记或虚拟现实开发方面的巨大投资。
为期 15 周的完整课程,旨在掌握数据科学的 SQL
在 15 周内学习最重要的数据科学技能

由 fullvector 创建的技术向量—www.freepik.com
一定要 订阅 千万不要错过另一篇关于数据科学的指南、诀窍和技巧、生活经验等文章!
介绍
随着我作为一名数据科学家在企业界的工作越来越多,我越来越相信掌握 SQL 对于事业成功至关重要。这就是为什么,如果你一直在关注我的文章,我最近写了很多关于 SQL 的文章。
SQL 不是一项很难学习的技能(例如,从哪里选择),但它肯定是一项很难完善的技能。我发现掌握 SQL 如此困难的原因之一是,我必须自学所有的东西——我不知道我不知道的东西,所以在编写 SQL 代码时很难主动预防潜在的错误。
这就是为什么我想写这个课程。这门课程的目的不仅仅是涵盖基础知识,还包括其他不经常被提及的非常重要的东西。
完成本课程后,您将学到以下内容:
- 如何编写简单查询和高级查询
- 如何将您的 SQL 技能应用到实际业务案例研究中
- 编写专业 SQL 代码的最佳实践
- 帮助你准备面试的几个资源
该课程的总体结构如下:
- 基础 SQL(第 1–3 周)
- 中级和高级 SQL(第 4–9 周)
- 编写专业 SQL 代码(第 10 周)
- SQL 练习题(第 11 周)
- 商业案例研究(第 12–14 周)
- 面试准备(第 15 周)
如果您已经熟悉 SQL 的基础知识,可以跳过第 1–3 周,,但请确保从第 4 周开始:SQL 查询的执行顺序。
如果您喜欢这篇文章,请务必订阅 订阅 ,千万不要错过另一篇关于数据科学指南、技巧和提示、生活经验等的文章!
SQL 有那么重要吗?
是的。
可以说,SQL 是任何数据专业人员需要学习的最重要的技能,而不仅仅是数据科学家。
SQL 用于从数据库中提取特定的数据,这样您就可以分析数据、可视化数据、建模数据等。因此,开发强大的 SQL 技能将使您的分析、可视化和建模更上一层楼,因为您将能够以高级方式提取和操作数据。
为了在本课程中取得成功,您应该首先熟悉在模式下使用 SQL 的,然后您将能够浏览以下主题。
说了这么多,让我们开始吧!
第 1 周:基本 SQL
在第一周,您将学习查询的所有构造块,以便能够编写最基本的 SQL 查询。
第二周:逻辑和比较运算符
既然您已经学习了 SQL 的基础知识,我们将在接下来的几周里学习中级到高级的概念,这样您就可以增强您的查询能力。
本周,我们将讨论用于过滤数据的逻辑运算符和比较运算符:
一定要 订阅 千万不要错过另一篇关于数据科学的指南、诀窍和技巧、生活经验等文章!
第 3 周:总量
在第 3 周,您将学习聚合函数,这是在数据行之间执行的返回单个值的操作。
第 4 周:SQL 查询的执行顺序
这是一个非常重要的主题,大多数 SQL 指南和课程都忽略了这个主题。SQL 查询的执行顺序指的是执行查询子句的顺序。通过理解这一点,您将能够调试更多的问题并编写更高效的查询。
要了解 SQL 的执行顺序, 查看 SQLBolt 的这个页面。
第五周:条件表达式
类似于 Python 或 JavaScript 中的 IF/THEN/ELSE 语句,SQL 有自己的条件表达式版本,我们将在第 5 周深入探讨:
第 6 周:连接和联合
现在,您已经学习了编写基本查询、筛选数据、聚合数据和编写条件表达式的所有构件,您将学习如何将不同的表相互组合:
第 7 周:子查询和公共表表达式
在我看来,这是最重要的一周。这两个概念允许您编写复杂的查询并以深刻的方式操作数据。在继续之前,请确保您对这些主题有深刻的理解!
第 8 周:字符串操作
一个好的 SQL 编码者应该能够以他/她喜欢的方式操作数据。为了做到这一点,你必须对字符串函数有很深的理解,这一点我们将在本周讨论:
第 9 周:日期时间操作
与前一周类似,您必须学会如何操作日期时间数据,这一点我们将在本周讨论:
- 摘录
- 日期 _ 添加()
- DATE_SUB()
- DATE_DIFF()
- 更多功能见 此处 (网页左侧)
第十周:窗函数
最后,您将学习一个更高级的主题,叫做窗口函数(或分析函数)。窗口函数类似于聚合函数,只是它们返回的行数与输入的行数相同。
查看以下链接,了解更多信息:
第 11 周:编写专业的 SQL 代码
写 SQL 代码是一回事,写好的 SQL 代码是另一回事。编写干净专业的 SQL 代码是高级数据分析师和初级数据分析师最大的区别之一。因此,学习一些基本规则来编写好的代码是很重要的。查看下面的文章:
第 12 周:SQL 练习题
现在是测试您的 SQL 知识的时候了。
Leetcode 和 Hackerrank 是很棒的资源,它们包含了各种编程语言的一系列练习题,包括 SQL!这些将是你练习目前所学知识的绝佳资源。
以下是一些你可以尝试的好问题:
第 13–15 周:案例研究
作为数据科学家,案例研究是模拟现实生活问题的最佳方式。下面是三个 SQL 案例研究,它们代表了您在企业环境中必须解决的问题:
要打开模式的 SQL 编辑器,请转到 此链接 并点击显示“打开另一个模式窗口”的超链接。
第 11 周:案例研究 1——调查用户参与度的下降
链接到案例 。
对于这个案例研究,您的目标是找到 Yammer 项目用户参与度下降的原因。首先,您应该在这里阅读 Yammer 做什么的概述。
如果你需要指导,请点击查看我是如何完成这个案例研究的。
第 12 周:案例研究 2 —了解搜索功能
链接到案例 。
这个案例更侧重于产品分析。你的目标是确定用户体验的好坏。这个案例的有趣之处在于,由你来决定“好”和“坏”是什么意思,以及如何评估用户体验。
第 13 周:案例研究 3 —验证 A/B 测试结果
链接到案例 。
商业世界中应用最广泛的数据科学应用之一是执行 A/B 测试。在本案例研究中,您的目标是验证或否定 A/B 测试的结果,其中对照组和治疗组之间存在 50%的差异。
下一步是什么?
如果你坚持到了最后,恭喜你!承诺在 15 周内学习一项新技能绝非易事。不要贬低自己的成就,你应该为自己感到骄傲,对自己的 SQL 能力充满信心!
接下来,看看下面的两篇文章,确保你对这些概念有很深的理解:
如果你还想要更多学习 SQL 的资源,我已经整合了 6 个学习高级 SQL 的绝佳资源:
</6-incredible-resources-to-learn-advanced-sql-364fe0889dcd>
感谢阅读!
一定要 订阅 千万不要错过另一篇关于数据科学的文章,包括指南、诀窍和技巧、生活经验等!
如果你喜欢这个,我会非常感激,如果你给它一个关注!一如既往,我祝你学习一切顺利:)
不确定接下来要读什么?我为你挑选了另一篇文章:
[## 六分钟内解释所有概率分布
towardsdatascience.com](/all-probability-distributions-explained-in-six-minutes-fe57b1d49600)
又一个!
特伦斯·申
2022 年,学习数据科学 Python 的完整的 26 周课程
在 26 周内学习数据科学所需的大部分 Python 知识

由 Pakata Goh 在 Unsplash 上拍摄的照片
Python 是一种编程语言,许多数据科学家使用它来清理数据、进行可视化和构建模型。学习用于数据科学的 Python 从未如此简单——有大量免费指南和教程可供您利用。
也就是说,Python 有超出数据科学的应用,所以如果没有适当的指导,你可能最终会学到数据科学中很少用到的东西。这就是为什么我建立了一个 26 周的课程,其中包含免费资源,你可以在 2022 年学习数据科学的 Python。
我把这个课程分成 4 个主要部分。我相信最好的学习方法是实践,所以除了留下视频教程和指南的链接,我在每个部分都包括了练习和项目,以帮助您掌握数据科学的 Python。
数据科学所需的所有 Python 材料的概述也可以在下面的视频中找到。
请务必点击 订阅此处 获取我在所有教程中使用的 Python for Data Science 备忘单(免费 PDF)
数据科学的 Python 核心概念
掌握数据科学的 Python 首先要做的是理解核心概念。这意味着学习最常见的数据类型,如何使用变量以及如何正确使用列表和字典。
其他需要学习的重要内容是条件语句(if/else 语句)和循环(for、while 等)。它们是用 Python 做更高级的事情的关键。
请记住,作为一名有抱负的数据科学家,您应该使用的文本编辑器是 Jupyter Notebook。这个编辑器不仅允许我们编写代码,还允许我们编写方程、绘制可视化图形、添加文本,以及更多让我们的 Python 脚本看起来像笔记本的事情。
第一周:Jupyter 笔记本介绍&数据类型(整数、浮点、布尔、字符串等)
第二周:变量、列表、元组和字典
第三周:IF 语句和 FOR 循环
第 4 周:功能和模块
第 5 周:Python 初学者项目
下面的初学者项目帮助我们将前几周学到的所有东西付诸实践,所以考虑至少采用其中的一个。它们是按难度排序的,所以第一个项目最简单,最后一个项目最难。
- 问答游戏:在这个项目中,我们将使用 if/else 语句,并使用 input 函数用 Python 提问和回答一些随机问题。
- 石头、布、剪刀:这是一个流行的游戏,你可以很容易地用 Python 实现。我们将练习 if/else 语句,如何处理变量、列表和循环。
- 文件重命名工具:该项目包括用 Python 做 7 个常见的文件系统操作。这个项目是所有关于操作系统模块。这是 Python 自带的一个简单而强大的模块。
用于数据分析的 Python
Pandas 和 NumPy 等数据分析库是解决数据清理和探索性数据分析(EDA)等常见数据科学任务的基础。
接下来的几周将重点学习如何创建和操作数据帧。除此之外,我将向您展示一些包含有用练习的资源,这些练习将有助于实践所有这些内容。
第 6 周:Pandas & Numpy 简介:如何创建一个数据框架,选择和添加列,操作,和常用方法。
第 7 周和第 8 周:过滤和提取数据
第 9 周:数据透视表
第 10 周:分组依据和聚合函数
第 11 周:合并数据框架
第 12 周:数据分析项目
下面的项目将让你练习到目前为止你所学的大多数熊猫方法。第一个项目是收集数据,第二个项目是清理数据(这是数据科学中最耗时的任务之一),在第三个项目中,你必须只用熊猫进行数据可视化。
- 数据收集:在这个项目中,你必须使用熊猫和硒来收集数据。Selenium 是一个 web 自动化工具,它允许我们提取数据。并建立我们自己的数据集。
- 用熊猫清理数据:清理数据是每一个数据科学家都要处理的常见任务。在这个项目中,我们将应用不同的技术来清理数据集,以确保数据的高质量。
- 只有熊猫的数据可视化:熊猫允许我们进行可视化,比如饼状图、条形图、线图等等。在这个项目中,我们将制作一个数据透视表,然后创建图表来帮助我们进行更好的分析。
用于数据可视化的 Python
虽然你可以用 Pandas 创建数据可视化,但是没有足够的选项来定制我们的绘图。这时,Matplotlib 和 Seaborn 等更完整的库就派上了用场。
这两个图书馆让我们超越了熊猫提供的基本情节。也许你可以只用熊猫来制作可视化效果,但是如果你像我一样喜欢制作华丽的可视化效果,可以考虑学习 Matplotlib 和/或 Seaborn。
此外,在这一节中,我们将学习如何制作一个 wordcloud 并解决一些很酷的项目,在享受乐趣的同时练习数据可视化技术。
第 13 周:用 Matplotlib 实现数据可视化,用 Stylecloud 实现 Wordclouds
第 14 周:使用 Seaborn 实现数据可视化
第 15 周:项目
数据可视化应该很有趣,所以对于这个项目,你必须选择任何你喜欢的电影/电视节目,然后去 Kaggle 寻找包含这部电影/节目脚本的数据集。一旦你有了数据集,和熊猫一起阅读它,并制作所有必要的情节,以了解更多关于你喜欢的电影/节目的信息。
如果你需要一些灵感,看看下面的文章。
用于机器学习的 Python
每个数据科学家都应该能够建立机器学习模型。这也是我们需要学习 sklearn、Keras、Tensorflow 等库的原因。他们将帮助我们为我们的项目构建、评估和选择最佳模型。
最终,您会意识到用 Python 实现这些模型并不难。对于大多数人来说,最难的部分是理解每个算法背后的核心概念,并在项目中正确应用它们。
这就是为什么在接下来的几周里,我们将重点学习最常见的机器学习算法是如何工作的,只有这样,我们才会看到如何用 Python 实现它们(大多数实现都是用 sklearn 实现的,在 26 周后,我鼓励你学习更高级的库,如 TensorFlow 和 Keras)
第 16 周:机器学习介绍
第 17 周:线性回归
第 18 周:逻辑回归
第 19 周:决策树
第 20 周:朴素贝叶斯
第 21 周:支持向量机
第 22 周:KNN
第 23 周:主成分分析
第 24 周:随机森林
第 25 周:模型度量
第 26 周:项目
- 银行客户流失预测:在这个项目中,你要对一个客户是否会流失进行分类。您将使用包含银行客户财务数据(信用评分、任期、产品数量、估计工资等)的数据集来构建预测模型。
注意:在这 26 周后,我鼓励你学习更多的机器学习算法。
与 3k 以上的人一起加入我的电子邮件列表,获取我在所有教程中使用的 Python for Data Science 备忘单(免费 PDF)
如果你喜欢阅读这样的故事,并想支持我成为一名作家,可以考虑报名成为一名媒体成员。每月 5 美元,让您可以无限制地访问数以千计的 Python 指南和数据科学文章。如果你使用我的链接注册,我会赚一小笔佣金,不需要你额外付费。
https://frank-andrade.medium.com/membership
Python 中从头开始的完整异常检测算法:分步指南

由 Unsplash 上的 Edge2Edge 媒体拍摄
基于概率的异常检测算法
异常检测可以被视为异常值分析的统计任务。但如果我们开发一个机器学习模型,它可以自动化,像往常一样,可以节省很多时间。异常检测有很多使用案例。信用卡欺诈检测、故障机器检测或基于异常特征的硬件系统检测、基于医疗记录的疾病检测都是很好的例子。还有更多的用例。异常检测的使用只会越来越多。
在本文中,我将解释用 Python 从头开始开发异常检测算法的过程。
配方和流程
这将比我之前解释的其他机器学习算法简单得多。该算法将使用平均值和方差来计算每个训练数据的概率。
如果一个训练样本的概率很高,这是正常的。如果某个训练示例的概率很低,则它是一个异常示例。对于不同的训练集,高概率和低概率的定义是不同的。我们稍后将讨论如何确定这一点。
如果非要我解释异常检测的工作流程,那就很简单了。
- 使用以下公式计算平均值:

这里 m 是数据集的长度或训练数据的数量,xi 是单个训练示例。如果你有几个训练特征,大多数情况下,需要计算每个特征的平均值。
2.使用以下公式计算方差:

这里,mu 是上一步计算的平均值。
3.现在,用这个概率公式计算每个训练示例的概率。

不要被这个公式中的求和符号迷惑了!这实际上是对角线形状的方差。
稍后当我们实现算法时,你会看到它的样子。
4.我们现在需要找到概率的阈值。正如我之前提到的,如果一个训练样本的概率很低,这就是一个异常的例子。
多大概率才算低概率?
对此没有普遍的限制。我们需要为我们的训练数据集找出答案。
我们从第 3 步得到的输出中取一系列概率值。对于每种概率,找出数据是异常还是正常的标签。
然后计算一系列概率的精确度、召回率和 f1 分数。
精度可以使用下面的公式计算

召回率可以通过以下公式计算:

这里,真阳性是算法将一个例子检测为异常而实际上它是异常的情况的数量。
假阳性发生在当算法检测到一个例子是异常的时候,但事实上它不是。
假阴性表示该算法检测到一个示例不异常,但实际上,它是一个异常示例。
从上面的公式中,你可以看到更高的精确度和更高的召回率总是好的,因为这意味着我们有更多的真阳性。但与此同时,假阳性和假阴性也起着至关重要的作用,正如你在公式中看到的那样。这里需要一个平衡。根据你所在的行业,你需要决定哪一种是你可以忍受的。
好的方法是取一个平均值。取平均值有一个独特的公式。这就是 f1 的分数。f1 分数的公式为:

这里 P 和 R 分别是精度和召回率。
我不会详细解释为什么这个配方如此独特。因为这篇文章是关于异常检测的。如果您有兴趣了解更多关于精确度、召回率和 f1 分数的信息,我这里有一篇关于该主题的详细文章:
基于 f1 的分数,你需要选择你的阈值概率。
1 是完美的 f 值,0 是最差的概率值
异常检测算法
我将使用来自吴恩达的机器学习课程的数据集,该课程有两个训练特征。我在本文中没有使用真实世界的数据集,因为这个数据集非常适合学习。它只有两个特点。在任何真实世界的数据集中,都不太可能只有两个要素。
拥有两个特性的好处是你可以将数据可视化,这对学习者来说很好。请随意从该链接下载数据集,并遵循:
https://github.com/rashida048/Machine-Learning-With-Python/blob/master/ex8data1.xlsx
我们开始任务吧!
首先,导入必要的包
import pandas as pd
import numpy as np
导入数据集。这是一个 excel 数据集。在这里,训练数据和交叉验证数据存储在单独的表中。所以,我们把训练数据拿来。
df = pd.read_excel('ex8data1.xlsx', sheet_name='X', header=None)
df.head()

让我们对照第 1 列绘制第 0 列。
plt.figure()
plt.scatter(df[0], df[1])
plt.show()

通过看这个图表,你可能知道哪些数据是异常的。
检查此数据集中有多少训练示例:
m = len(df)
计算每个特征的平均值。这里我们只有两个特性:0 和 1。
s = np.sum(df, axis=0)
mu = s/m
mu
输出:
0 14.112226
1 14.997711
dtype: float64
根据上面“公式和过程”一节中描述的公式,我们来计算方差:
vr = np.sum((df - mu)**2, axis=0)
variance = vr/m
variance
输出:
0 1.832631
1 1.709745
dtype: float64
现在把它做成对角线形状。正如我在概率公式之后的“公式和过程”一节中解释的,求和符号实际上是方差的对角线。
var_dia = np.diag(variance)
var_dia
输出:
array([[1.83263141, 0\. ],
[0\. , 1.70974533]])
计算概率:
k = len(mu)
X = df - mu
p = 1/((2*np.pi)**(k/2)*(np.linalg.det(var_dia)**0.5))* np.exp(-0.5* np.sum(X @ np.linalg.pinv(var_dia) * X,axis=1))
p

训练部分完成了。
让我们把所有这些概率计算放入一个函数中,以备将来使用。
def probability(df):
s = np.sum(df, axis=0)
m = len(df)
mu = s/m
vr = np.sum((df - mu)**2, axis=0)
variance = vr/m
var_dia = np.diag(variance)
k = len(mu)
X = df - mu
p = 1/((2*np.pi)**(k/2)*(np.linalg.det(var_dia)**0.5))* np.exp(-0.5* np.sum(X @ np.linalg.pinv(var_dia) * X,axis=1))
return p
下一步是找出阈值概率。如果概率低于阈值概率,则示例数据是异常数据。但是我们需要找出我们特殊情况下的阈值。
对于这一步,我们使用交叉验证数据和标签。在这个数据集中,我们有交叉验证数据,也有单独工作表中的标签。
对于您的情况,您可以简单地保留一部分原始数据用于交叉验证。
现在导入交叉验证数据和标签:
cvx = pd.read_excel('ex8data1.xlsx', sheet_name='Xval', header=None)
cvx.head()

以下是标签:
cvy = pd.read_excel('ex8data1.xlsx', sheet_name='y', header=None)
cvy.head()


交叉验证数据的目的是计算阈值概率。我们将使用阈值概率来发现测向异常数据。
现在调用我们之前定义的概率函数,找出交叉验证数据“cvx”的概率:
p1 = probability(cvx)
我将把 cvy 转换成 NumPy 数组,因为我喜欢使用数组。数据帧也很好。
y = np.array(cvy)
输出:
#Part of the array
array([[0],
[0],
[0],
[0],
[0],
[0],
[0],
[0],
[0],
这里,“y”值 0 表示这是一个正常的例子,而“y”值 1 表示这是一个异常的例子。
现在,如何选择阈值?
我不想只检查概率列表中的所有概率。这可能是不必要的。让我们进一步检查概率值。
p.describe()
输出:
count 3.070000e+02
mean 5.378568e-02
std 1.928081e-02
min 1.800521e-30
25% 4.212979e-02
50% 5.935014e-02
75% 6.924909e-02
max 7.864731e-02
dtype: float64
如图所示,我们没有太多异常数据。所以,如果我们只是从 75%的值开始,那应该是好的。但是为了安全起见,我将从平均值开始这个范围。
所以,我们将从平均值和更低取一个概率范围。我们将检查该范围内每个概率的 f1 分数。
首先,定义一个函数来计算真阳性、假阳性和假阴性:
def tpfpfn(ep, p):
tp, fp, fn = 0, 0, 0
for i in range(len(y)):
if p[i] <= ep and y[i][0] == 1:
tp += 1
elif p[i] <= ep and y[i][0] == 0:
fp += 1
elif p[i] > ep and y[i][0] == 1:
fn += 1
return tp, fp, fn
列出低于或等于平均概率的概率。
eps = [i for i in p1 if i <= p1.mean()]
检查,列表的长度、
len(eps)
输出:
128
根据我们之前讨论的公式定义一个函数来计算“f1”分数:
def f1(ep, p):
tp, fp, fn = tpfpfn(ep)
prec = tp/(tp + fp)
rec = tp/(tp + fn)
f1 = 2*prec*rec/(prec + rec)
return f1
所有功能都准备好了!
现在计算我们之前选择的所有ε或概率值范围的 f1 分数。
f = []
for i in eps:
f.append(f1(i, p1))
f
输出:
[0.16470588235294117,
0.208955223880597,
0.15384615384615385,
0.3181818181818182,
0.15555555555555556,
0.125,
0.56,
0.13333333333333333,
0.16867469879518074,
0.12612612612612614,
0.14583333333333331,
0.22950819672131148,
0.15053763440860213,
0.16666666666666666,
0.3888888888888889,
0.12389380530973451,
这是 f 评分表的一部分。长度应该是 128。f 分数通常在 0 到 1 之间,其中 1 是完美的 f 分数。f1 分数越高越好。所以,我们需要从我们刚刚计算的 f 分数列表中取最高的 f 分数。
现在,使用“argmax”函数来确定最大 f 分数值的索引。
np.array(f).argmax()
输出:
127
现在用这个指数得到阈值概率。
e = eps[127]
e
输出:
0.00014529639061630078
找出反常的例子
我们有了阈值概率。我们可以从中找出我们训练数据的标签。
如果概率值低于或等于该阈值,则数据是异常的,否则是正常的。我们将正常和异常数据分别表示为 0 和 1,
label = []
for i in range(len(df)):
if p[i] <= e:
label.append(1)
else:
label.append(0)
label
输出:
[0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
这是标签列表的一部分。
我将在上面的训练数据集中添加这些计算出的标签:
df['label'] = np.array(label)
df.head()

我绘制了数据,其中红色标签为 1,黑色标签为 0。剧情是这样的。

有意义吗?
是的,对吗?红色的数据显然是异常的。
结论
我试图一步一步地解释开发异常检测算法的过程。我没有在这里隐藏任何步骤。希望可以理解。如果你仅仅通过阅读理解有困难,我建议你自己在笔记本上运行每一段代码。这将使事情变得非常清楚。
如果你正在用这个算法做一些很酷的项目,请不要犹豫分享。
欢迎在推特上关注我,喜欢我的 T2 脸书页面。
Python 和 scikit 中的完整数据分析工作流程-学习
数据分析
一个现成的代码,包括预处理,参数调整和模型运行和评估。

在这个简短的教程中,我展示了一个利用scikit-learn Python 库的完整数据分析过程。该过程包括
- 预处理,包括特征选择、归一化和平衡化
- 带参数调整的模型选择
- 模型评估
本教程的代码可以从我的 Github 库下载。
加载数据集
首先,我通过 Python pandas库加载数据集。我利用了由 Kaggle 库提供的heart.csv数据集。
import pandas as pddf = pd.read_csv('source/heart.csv')
df.head()

作者图片
我计算数据集中的记录数和列数:
df.shape
它给出了以下输出:
(303, 14)
功能选择
现在,我将数据集的列分为输入(X)和输出(Y)。我使用除了output之外的所有列作为输入特征。
features = []
for column in df.columns:
if column != 'output':
features.append(column)
X = df[features]
Y = df['output']
为了选择输入特征的最小集合,我通过由pandas dataframe提供的corr()函数计算特征之间的皮尔逊相关系数。

作者图片
我注意到所有的特征都具有低相关性,因此我可以将它们都作为输入特征。
数据标准化
数据归一化会在相同的时间间隔内缩放所有要素。我利用了由scikit-learn库提供的MinMaxScaler()。在我的上一篇文章中,我在scikit-learn中讨论了数据规范化,而在我的这篇文章中,我描述了没有scikit-learn的数据规范化的一般过程。
X.describe()

作者图片
查看每个特性的最小值和最大值,我注意到有许多特性超出了范围[0,1],因此我需要对它们进行缩放。
对于每个输入特征,我计算MinMaxScaler()并将结果存储在同一个X列中。MinMaxScaler()必须先通过fit()功能进行拟合,然后才能通过transform()功能申请转换。注意,我必须对格式(-1,1)中的每个特征进行整形,以便作为缩放器的输入参数传递。例如,Reshape(-1,1)将数组[0,1,2,3,5]转换为[[0],[1],[2],[3],[5]]。
from sklearn.preprocessing import MinMaxScalerfor column in X.columns:
feature = np.array(X[column]).reshape(-1,1)
scaler = MinMaxScaler()
scaler.fit(feature)
feature_scaled = scaler.transform(feature)
X[column] = feature_scaled.reshape(1,-1)[0]
在训练和测试中拆分数据集
现在我把数据集分成两部分:训练集和测试集。测试集的大小是整个数据集的 20%。我利用了scikit-learn函数train_test_split()。我将使用训练集来训练模型,使用测试集来测试模型的性能。
import numpy as np
from sklearn.model_selection import train_test_splitX_train, X_test, y_train, y_test = train_test_split( X, Y, test_size=0.20, random_state=42)
平衡
我检查数据集是否平衡,即训练集中的输出类是否被同等地表示。我可以使用value_counts()函数来计算每个输出类中的记录数。
y_train.value_counts()
它给出了以下输出:
1 133
0 109
输出类不平衡,因此我可以平衡它。我可以利用imblearn库来执行平衡。我尝试过采样少数类和欠采样多数类。关于不平衡学习库的更多细节可以在这里找到。首先,我通过RandomOverSampler()执行过采样。我创建模型,然后我适应训练集。fit_resample()函数返回平衡的训练集。
from imblearn.over_sampling import RandomOverSampler
over_sampler = RandomOverSampler(random_state=42)
X_bal_over, y_bal_over = over_sampler.fit_resample(X_train, y_train)
我通过value_counts()函数计算每个类中的记录数,我注意到现在数据集是平衡的。
y_bal_over.value_counts()
它给出了以下输出:
1 133
0 133
其次,我通过RandomUnderSampler()模型执行欠采样。
from imblearn.under_sampling import RandomUnderSamplerunder_sampler = RandomUnderSampler(random_state=42)
X_bal_under, y_bal_under = under_sampler.fit_resample(X_train, y_train)
模型选择和培训
现在,我准备好训练模型了。我选择一个KNeighborsClassifier,首先用不平衡数据训练它。我利用fit()函数来训练模型,然后利用predict_proba()函数来预测测试集的值。
from sklearn.neighbors import KNeighborsClassifiermodel = KNeighborsClassifier(n_neighbors=3)
model.fit(X_train, y_train)
y_score = model.predict_proba(X_test)
我计算模型的性能。特别是,我计算了roc_curve()和precision_recall(),然后绘制它们。我利用scikitplot库来绘制曲线。
从图中我注意到每个类别都有一条 roc 曲线。关于精确召回曲线,类别 1 比类别 0 工作得更好,可能是因为它由更大数量的样本表示。
import matplotlib.pyplot as plt
from sklearn.metrics import roc_curve
from scikitplot.metrics import plot_roc,auc
from scikitplot.metrics import plot_precision_recallfpr0, tpr0, thresholds = roc_curve(y_test, y_score[:, 1])# Plot metrics
plot_roc(y_test, y_score)
plt.show()
plot_precision_recall(y_test, y_score)
plt.show()

作者图片

作者图片
现在,我用过采样平衡重新计算同样的事情。我注意到类 0 的精确召回曲线增加,而类 1 的精确召回曲线减少。
model = KNeighborsClassifier(n_neighbors=3)
model.fit(X_bal_over, y_bal_over)
y_score = model.predict_proba(X_test)
fpr0, tpr0, thresholds = roc_curve(y_test, y_score[:, 1])# Plot metrics
plot_roc(y_test, y_score)
plt.show()
plot_precision_recall(y_test, y_score)
plt.show()

作者图片

作者图片
最后,我通过欠采样数据来训练模型,我注意到性能普遍下降。
model = KNeighborsClassifier(n_neighbors=3)
model.fit(X_bal_under, y_bal_under)
y_score = model.predict_proba(X_test)
fpr0, tpr0, thresholds = roc_curve(y_test, y_score[:, 1])# Plot metrics
plot_roc(y_test, y_score)
plt.show()
plot_precision_recall(y_test, y_score)
plt.show()

作者图片

作者图片
参数调整
在本教程的最后一部分,我试图通过为我的模型搜索最佳参数来提高模型的性能。我利用了由scikit-learn库提供的GridSearchCV机制。我为要测试的每个参数选择一系列值,并将它们放入param_grid变量中。我创建了一个GridSearchCV()对象,与训练集相适应,然后检索包含在best_estimator_变量中的最佳估计值。
from sklearn.model_selection import GridSearchCVmodel = KNeighborsClassifier()param_grid = {
'n_neighbors': np.arange(2,8),
'algorithm' : ['auto', 'ball_tree', 'kd_tree', 'brute'],
'metric' : ['euclidean','manhattan','chebyshev','minkowski']
}grid = GridSearchCV(model, param_grid = param_grid)
grid.fit(X_train, y_train)best_estimator = grid.best_estimator_
我利用最佳估计量作为我的预测模型,并计算算法的性能。
best_estimator.fit(X_train, y_train)
y_score = best_estimator.predict_proba(X_test)
fpr0, tpr0, thresholds = roc_curve(y_test, y_score[:, 1])# Plot metrics
plot_roc(y_test, y_score)
plt.show()
plot_precision_recall(y_test, y_score)
plt.show()

作者图片

作者图片
我注意到 roc 曲线有所改善。我现在尝试使用过采样的训练集。我省略了代码,因为它与前面的代码相同。在这种情况下,我获得了最佳性能。

作者图片

作者图片
摘要
在本教程中,我展示了为数据分析构建良好模型的完整工作流程。工作流程包括:
- 数据预处理,包括特征选择和平衡
- 利用交叉验证的网格搜索进行模型选择和参数调整
- 模型评估,通过 ROC 曲线和精确召回曲线。
在本教程中,我没有处理异常值检测。如果你想了解这方面的东西,可以看一下我之前的文章。
如果你想了解我的研究和其他活动的最新情况,你可以在 Twitter 、 Youtube 和 Github 上关注我。
相关文章
新到中?您可以每月订阅几美元,并解锁无限的文章— 点击此处。
Python PyCaret 中的完整数据分析工作流
数据分析
这是一个现成的教程,利用了我用过的最好的机器学习库。

在这个简短的教程中,我展示了一个利用pycaret Python 库的完整数据分析过程。这个库的文档可以在这个链接找到。该过程包括:
- 预处理,包括标准化和平衡化
- 带参数调整的模型选择
- 模型评估
- 在看不见的数据上部署。
本教程的完整代码可以从我的 Github 库下载。
加载数据集
首先,我通过 Python pandas库加载数据集。我利用了由 Kaggle 库提供的heart.csv数据集。该数据集可用于执行二元分类。数据集包含 303 条记录和 13 个输入要素。
import pandas as pddf = pd.read_csv('source/heart.csv')
df.head()

作者图片
分割数据
现在,我可以将数据分为用于建模的数据和用于预测的不可见数据。用于建模的数据将被分割成训练集和测试集,但是我将在本教程的后面执行这个操作。
通过dataframe函数sample()使用建模/不可见分割,该函数返回随机项目的一部分,并接收要返回的项目的一部分作为输入(frac)。在我的例子中,我保留 95%的数据用于建模,5%用于隐藏。
data = df.sample(frac=0.95, random_state=42)
当先前提取的数据被丢弃时,我将data_unseen计算为剩余的数据帧。
data_unseen = df.drop(data.index)
我重置了data和data_unseen的索引。
data.reset_index(inplace=True, drop=True)
data_unseen.reset_index(inplace=True, drop=True)
最后,我打印两个数据集的大小。
print('Data for Modeling: ' + str(data.shape))
print('Unseen Data For Predictions: ' + str(data_unseen.shape))
它给出了以下输出:
Data for Modeling: (288, 14)
Unseen Data For Predictions: (15, 14)
数据预处理
数据预处理通过setup()函数在pycaret中完成,该函数可以接收数量惊人的参数作为输入。在本教程中,我利用了三种预处理技术:
- 标准化,在同一区间内缩放所有特征。我利用了
minmax函数。我在我的上一篇文章中讨论了数据标准化。 - 训练测试分割,将数据集分割成两部分:训练和测试集。训练集大小是整个数据集的 80%。
- 平衡,试图平衡阶级。我试图对少数民族阶层进行过度采样。
pycaret库利用imblearn库来执行平衡。关于不平衡学习库的更多细节可以在这里找到。
在执行平衡之前,我会检查数据集是否平衡,即训练集中的输出类是否被均等地表示。我可以使用value_counts()函数来计算每个输出类中的记录数。输出类是不平衡的,因此我可以平衡它们。在本教程中,我通过RandomOverSampler()执行过采样。
data['output'].value_counts()
它给出了以下输出:
1 155
0 133
setup()功能接收以下输入:
normalize = True和normalize_method='minmax',指定如何执行标准化train_size = 0.8执行训练/测试分割fix_imbalance = True和fix_imbalance_method=RandomOverSampler()指定如何执行平衡
其他参数包括:
session_id=123,用于实验的再现性。值123可以是随机数data = data,指定型号数据target = 'output',指定目标类。
在运行设置之前,该功能要求确认参数的自动识别。
from pycaret.classification import *
from imblearn.over_sampling import RandomOverSamplermodel = setup(data = data, target = 'output', normalize = True, normalize_method='minmax', train_size = 0.8,fix_imbalance = True, fix_imbalance_method=RandomOverSampler(), session_id=123)
模型选择和调整
型号选择有两种方式:
- 选择一个特定的模型,然后调整它
- AutoML:让库选择最好的模型,然后调整它。
选择具体型号
在这种情况下,我选择 K-Neighbors 分类器。首先,我通过create_model()函数创建模型,然后通过tune_model()函数调整它。有趣的是,该模型不将数据作为输入,因为它们已经通过setup()函数包含在pycaret中。作为默认选项,tune_model()执行 10 重验证。
作为输出,tune_model()函数打印每个折叠的以下指标,以及平均值和标准偏差:准确性、AUC、召回率、精确度、F1、Kappa、MCC。
knn = create_model('knn')
tuned_knn = tune_model(knn)

作者图片
pycaret库还提供了plot_model()函数,允许绘制一些度量标准,如auc (ROC 曲线)和pr(精确召回曲线)。
plot_model(tuned_knn, plot = 'auc')

作者图片
plot_model(tuned_knn, plot = 'pr')

作者图片
现在,我可以利用调整后的模型对用于建模的所有数据集中的项目进行分类(包括训练集和测试集)。这可以通过predict_model()函数来完成,该函数返回计算出的指标。此外,对于数据集中的每一项,它返回原始输入、输出、预测标签(列Label)和分数(列Score)。
predict_model(tuned_knn)

作者图片
AutoML
AutoML 可通过compare_models()功能完成,该功能分析所有可用模型,返回最佳模型,并打印每个测试模型的测量指标。
best = compare_models()

作者图片
最佳模式是:
LinearDiscriminantAnalysis(n_components=None, priors=None, shrinkage=None, solver='svd', store_covariance=False, tol=0.0001)
现在,我可以通过tune_model()功能调整最佳型号。该过程类似于单个模型所遵循的过程。因此,在模型调优之后,我可以绘制一些度量标准,比如精度-召回曲线。
tuned_best = tune_model(best)

作者图片
plot_model(tuned, plot = 'pr')

作者图片
最终确定模型
一旦模型被调优,它就可以被最终确定,即通过包含测试集来重新调优。
final_best = finalize_model(tuned_best)
首先,通过再次调用predict_model()功能,可以检查最终模型的性能。
predict_model(final_best)

作者图片
最后,可以在看不见的数据上测试模型的性能。
predict_model(final_best, data = data_unseen)

作者图片
摘要
在本教程中,我展示了如何利用 Python pycaret库执行完整的数据分析工作流。该工作流程包括以下步骤:
- 数据预处理
- 模型选择和调整
- 模型最终确定。
如果你想了解我的研究和其他活动的最新情况,你可以在 Twitter 、 Youtube 和 Github 上关注我。
相关文章
[## Python 和 scikit 中的完整数据分析工作流程-学习
towardsdatascience.com](/a-complete-data-analysis-workflow-in-python-and-scikit-learn-9a77f7c283d3)
新到中?您可以每月订阅几美元,并解锁无限的文章— 单击此处。
2021 年完整的数据科学路线图
利用在线资源构建您自己的数据科学课程

安妮·斯普拉特在 Unsplash 上的照片
大约三年前,我攻读了计算机科学的本科学位。我选择了数据科学专业,因为当时它被大肆宣传。
一年前,我意识到我的学位并没有让我具备成为数据科学家所必需的技能。
它花了我父母大约 25000 美元。
这是在我知道像 edX 和 Coursera 这样的在线学习平台之前。
我自学了成为数据科学家所需的所有技能。我是在我的学位之外学到的——我是在网上学到的。
现在,我是一家数据和人工智能公司的数据科学家。
在我去年写的一篇文章中,我提供了一份你可以进入数据科学行业的课程清单。
我将在这里更新该列表,并为您提供更多学习资源,帮助您在 2021 年进入数据科学领域。
这些课程教给你的要比我整个学位课程教给我的多得多,而且只需要很少一部分费用。
第一步:学习 Python
如果你想从零开始学习数据科学,你需要做的第一件事就是学习如何编码。
选择一种编程语言(Python 或 R ),然后开始学习。
我建议从 Python 开始,因为它比 r 应用更广泛。它也更通用和高度灵活,如果你有 Python 知识,你将能够过渡到不同的领域(数据分析,web 开发)。
要学习 Python,请参加以下任何一门课程:
data camp:Python 简介
这个 DataCamp 课程将带你通过练习,教你如何用 Python 编码。
你将从这门课程中学到什么?
- 你将学习 Python 的基础知识:变量,数据类型,函数,方法,列表,以及数组。作为一名数据科学家,知道如何操作数组是一项非常重要的技能,这就是为什么他们有一个专门的模块。
- 在你有了坚实的 Python 基础之后,这个课程将教你使用一个叫做 Numpy 的库。Numpy 是一个流行的 Python 包,数据科学家用它来操作数组。
本课程将为你提供 Python 编程的基本理解。
有几个话题是本课程没有涉及的,比如条件语句和循环。
这些是非常重要的概念,你不应该跳过学习它。我建议使用外部资源,如 FreeCodeCamp 和 YouTube 视频,来了解这些概念。
Udemy: 2021 完整 Python Bootcamp 从零到 Python 中的英雄
这是 DataCamp 课程的替代课程。它是由何塞·波尔蒂利亚教授的,他是(在我看来)当今最好的老师。
我没有上过这门课,因为在进入数据科学领域之前,我有一些基本的编程知识。
不过,我上过他的数据科学和机器学习课程。这是我上的第一门数据科学在线课程,我立刻就爱上了这门学科。
何塞的教学风格令人难以置信。他的编程练习难度适中,会促使你思考并提出解决方案。
如果你是一个完全没有任何编程经验的初学者,我会 100%推荐你参加这个课程。
与 DataCamp 课程相比,唯一的缺点是没有内置的代码编辑器。你需要建立你自己的编程环境(但是 Jose 会指导你,这一点也不难)。
学习完这些课程中的任何一门后,您应该对编程基础有了很好的掌握。
然而,您学习如何编码的旅程并没有就此结束。
你需要学习如何用你学过的新语法解决问题。
我曾经问过一位数据科学家他是如何学习编码的,他推荐了一个名为 HackerRank 的网站。
他告诉我,每次他想学一门新语言,他都会在网站上尽可能多地解决问题。他建议每天做 10 道左右的题。
对于刚刚起步的你来说,这可能有点过了。
当我刚刚开始学习编程的时候,我记得我花了一整天的时间来解决网站上的一个编码挑战。
然而,随着我的 Python 和解决问题的技能的不断进步,我开始变得越来越擅长。
每天花大约 4-5 个小时解决 HackerRank 问题,你的 Python 编程技能会很快提高。
第二步:学习数据科学
在你对 Python 编程和问题解决有了更好的掌握之后,你就可以开始学习数据科学和机器学习的基础知识了。
为此,您可以参加以下一门(或两门)课程:
Udemy:用于数据科学和机器学习的 Python 训练营
这是我上的第一堂数据科学课。我每天花大约 5 个小时上这门课,并在一个月内完成。
这是何塞·波尔蒂利亚教授的数据科学入门课程。它将教你如何使用像 Numpy 和 Pandas 这样的库进行数据分析,以及像 Matplotlib 和 Seaborn 这样的可视化库。
Jose 还向您介绍了机器学习的基础知识。他解释了不同的机器学习模型是如何工作的,然后向您展示了这些模型在 Python 中的实现。
我在一个月内从这门课程中学到的东西比我整个数据科学学位学到的还多。
请记住,在学习这门课程之前,您需要有一些编程经验,所以在学习这门课程之前,请务必先学习 Python 课程。
data camp:Python 的机器学习基础
本课程将教你使用 Python 进行机器学习的基础知识。
在本课程中,您将学习监督和非监督机器学习算法背后的理论。
您还将学习这些模型在 Python 中的实际实现。
我以前没有参加过 Datacamp 课程。不过课程内容似乎比我上的 Udemy 课程更详细更全面。
何塞·波尔蒂利亚教授的 Udemy 课程中没有涉及很多主题(比如模型正则化和度量)。
我建议先上 Jose 在 Udemy 上的课程,学习基础知识,了解如何用 Python 构建和训练模型。
然后,你可以参加 Datacamp 课程来填补你学习中的空白。
学完这两门课程后,你将对机器学习算法的工作原理及其在 Python 中的实现有完整的理解。
现在,您已经准备好开始构建自己的机器学习项目了。
对机器学习有理论上的理解不足以打入这个行业。
事实上,我得到了我的第一份数据科学工作,因为我建立了数据科学项目,并展示在我的投资组合中。一旦你在这些课程的帮助下完成了数据科学的学习,请阅读这篇关于你应该在作品集上展示的数据科学项目类型的文章。
第三步:学习统计学
大多数人建议在投身机器学习和数据科学之前先学习统计学。
我建议相反。
我建议先学习 Python,建立机器学习模型。
一旦您对这些模型有了较高层次的理解,并且知道如何用 Python 实现它们,您就可以了解它们是如何工作的。
你可以进去学习这些模型背后的理论和数学。
这被称为自上而下的学习方法,这也是我自学数据科学的方式。
为了更好地理解数据科学和机器学习,您应该参加以下一些统计学课程:
概率与统计:要 p 还是不要 p?
如果你以前没有统计学知识,这门课程是为你准备的。这是我上过的最好的统计学入门课程之一。
它将带你了解统计学中一些最重要的概念,如不同的概率分布、标准化、描述性统计、随机抽样、假设检验和中心极限定理。
这门课程最大的好处是:它是为那些没有统计学背景的学生量身定做的。
本课程的讲师詹姆斯·阿卜迪用有趣的例子和案例研究来解释这些材料。
他用简单的英语解释所有的概念,不使用任何复杂的数学符号。
一旦你完成了这门课程,你将对概率和统计有一个基本的了解,以及在不确定的情况下做决策的方法。
edX:统计学习
本课程将为您提供对机器学习算法的深入理解。
它是这个列表中唯一用 R 语言讲授的资源。在学习这门课程之前,您不需要了解 R 编程。在带你进行实际的实现之前,教师会教你如何用 R 编写代码。
本课程涵盖监督学习技术,如线性回归、逻辑回归、支持向量机和决策树。它还涵盖了无监督学习算法,如 K 均值聚类和主成分分析。
与上面列出的所有资源不同,本课程假设学生有微积分和线性代数背景。要学习这门课程,你必须熟悉求和符号和矩阵运算。
我之所以建议上这门课,是因为我发现它深入到了机器学习模型背后的直觉。
它会教你根据变量的分布来挑选最佳的机器学习算法。
当您没有足够的可用数据时,您将学习可用于训练模型的不同采样技术。
你也会得到类似“为什么线性回归不能用于分类问题”这样的问题的答案
如果你想对机器学习算法及其工作原理有一个全面的了解,我也建议你看一本叫做统计学习入门的书,这个课程就是以它为基础的。
我按照上面的步骤自学了数据科学。
这个路线图帮助我进入了数据行业,并获得了一份数据科学家的工作。
当然,您的数据科学学习之旅不会就此结束。
机器学习和数据科学还有更多的东西,而这些主题仅仅触及了所有要学习的东西的表面。
注:
本文包含附属链接。这意味着,如果你点击它,并选择购买我上面链接的课程,你的订阅费的一小部分将归我所有。
作为一名创作者,这有助于我成长并继续创作这样的内容。
不过,我只推荐我觉得好的课程。我几乎参加了上面提到的所有课程,它们对我向数据科学的过渡至关重要。
感谢您的支持!
本文到此为止,感谢阅读!
一个完整的深度学习组合项目
用 Python 创建和部署面部识别应用程序

作者图片
在本文中,我将涉及以下主题:
- 为什么在你的投资组合中展示深度学习项目?
- 投资组合项目的重要性
- 我面部识别项目的预演
- 学习模型部署的资源
- 学习深度学习的资源
如果您愿意,可以随意跳到这些部分。你可以在这里找到我的 web 应用程序的代码。
为什么在你的投资组合中展示深度学习项目?

深度学习是最受追捧的机器学习技术之一。
当业外人士想到机器学习时,他们通常会想到自动驾驶汽车或面部识别系统——所有这些都属于深度学习。
自动编码器、物体检测和面部识别系统等深度学习应用可能会在你的投资组合中留下非常深刻的印象。
老实说,创建深度学习模型并不是一项非常困难的任务。研究人员已经花了大量的时间将深度学习大众化。
像 Keras 和 FastAI 这样的高级库让人们建立和训练深度学习模型变得越来越容易。
像Deep Learning . ai specialization这样的在线课程,结合像面向程序员的实用深度学习这样的材料,可以帮助人们相当快速地进入深度学习领域。
投资组合项目的重要性

如果你之前没有机器学习工作经验或正规教育,你可以做以下两件事之一来展示你的技能:
- 参加在线课程并获得高分认证
- 创建个人项目以展示在您的作品集上
我总是选择后者。
如果你想学习一个新的话题,在线课程是一个很好的开始方式。他们给你指出正确的资源,并给你一个很好的主题基础。
然而,强化这些概念并深入主题的唯一方法是实践。
当你用学到的材料解决现实世界的问题时,你将能够在理论和实践之间架起一座桥梁。你也将开始理解机器学习中的不同用例,以及应用哪种算法来解决不同的问题。
除了是很好的学习经历之外,个人项目在你的简历上看起来也很不错。
事实上,展示我的个人项目帮助我获得了第一份数据科学实习。你可以在这里读到我的经历。
不要只是告诉,展示

几个月前,我创建了一个简单的“猫和狗”图像分类模型。该模型可以区分给定图像是猫还是狗。
我告诉我的朋友我正在做的模型。她来自非技术背景,问我她是否能看到它是如何工作的。
我让她给我发一张猫的图片,存到我的笔记本里,打开我的 Jupyter 笔记本里的图片路径。然后,我运行了单元,并自豪地向她展示了结果。模特预测她的猫是只猫!
她似乎对此不太兴奋。
她解释说:“如果我能上传图片,看看它是如何为自己工作的,那就更好了。”
她来自非技术背景,所以可以理解她不理解任何代码,只是想看看它是如何工作的。
但问题是。大多数招聘人员和猎头都是非技术背景。他们每天浏览数百份 LinkedIn 个人资料和简历,几乎所有这些都链接到 GitHub 知识库。
如果你的简历和他们那天看过的几百份简历差不多,他们凭什么挑你?是什么让你与众不同?
创造一些人们可以使用的东西。这会让你脱颖而出。
即使您创建了像猫狗分类模型这样简单的东西,也要部署它并共享链接。
如果招聘人员偶然发现你的个人资料并点击链接,他们会发现一些他们可以利用的东西。他们甚至会上传自己狗狗的照片,来试用这个模型。
不管它是不是一个好的模型,它都会激发人们的兴趣。
足够的兴趣让他们好奇它是如何工作的,并实际尝试一下。
这将使他们记住你,并将你与其他潜在候选人区分开来。
一个巨大的好处是它很有趣。
模型部署很有趣,因为您可以创建一些其他人可以与之交互的东西。你在为你的机器学习模型建立一个可用的界面,其他人都可以使用。
虽然这可能比实际构建模型花费更多的时间,但这很有趣。一旦你完成了,你会为创造一个有用的产品而感到非常自豪。
您可能已经熟悉使用 Python 进行机器学习,所以模型部署应该不会太难。
在这个项目中,我创建了一个面部识别模型。我用名人的图片训练模型,然后部署它。
现在,我将向您介绍我是如何创建这个项目的。我将向您展示它是如何工作的,并向您介绍我使用的工具和编程语言。
项目演练
我的应用程序的工作方式非常简单。你只需要上传一张你自己的照片,然后点击“预测”按钮。
它会给你一个你最喜欢的名人的名字。
你也可以和真正的名人一起试用这个应用程序,这就是我将在这里演示的:

作者图片
够简单!
创建 web 应用程序
(注意:如果你不想要所有的技术细节,可以随意跳到下一节标题为 的一些教程,你可能会发现有用的”
我研究了不同的方法来部署我的面部识别模型。
最初,我打算创建一个没有 web 服务的基于浏览器的模型,用 HTML、CSS 和 Javascript 做所有的事情。
这意味着我不需要后端应用程序,可以在前端做任何我需要做的事情。
然而,我很快意识到这是不可能的。
用户上传的图像需要用 Keras 中的模块和 Javascript 中没有的其他几个库进行预处理。
无论我在前端做什么,我都需要一个后端 Python 应用程序来做预处理、做预测和解码输出。
因此,我选择使用 Flask 来创建 web 应用程序。在前端,我决定使用 HTML 、 CSS 和 Javascript 。
- Flask : Flask 是一个 web 框架,是一个用来用 Python 开发 web 应用的库。我为这个项目选择 Flask 的原因是因为它的设置相对简单。如果您已经了解 Python,那么为您的模型运行 Flask web 服务器应该很容易。
- HTML、CSS 和 Javascript :我用这些语言搭建了 app 的前端界面,像“上传图片”和预测按钮。
您可能会发现一些有用的模型部署教程

Rami Al-zayat 在 Unsplash 上拍摄的照片
如果你不熟悉 web 开发,而且我上面说的对你来说听起来像希腊语,不要烦恼!
你不需要对 web 开发有广泛的理解就可以在线部署你的深度学习模型。
我建议从一些关于 web 如何工作的教程开始,然后继续学习一些 Flask 模型部署教程。
以下是一些帮助我开始的方法:
- 客户端机器学习模型:如果您希望纯粹在前端部署机器学习模型,您应该遵循本系列的所有教程。丹是一个不可思议的教练,他的热情会让你想继续看下去。请记住,本系列不涉及任何服务器端编程。所有的模型都将使用纯 Javascript 进行部署。
- Flask 教程:如果你想用 Python 后端应用程序创建你的模型,你需要学习 Flask。
- 用 Flask 部署 Keras 模型:这是一个很棒的教程系列,教你如何用 Flask web 服务部署 Keras 模型。前端使用 HTML 和 Jquery。如果你只有时间跟随其中一个教程,我推荐这个。它教你如何设置 Flask,并且假设你没有 web 开发方面的知识。
如果你想要一些不涉及太多开发的东西,或者不想使用 Flask 和 Javascript,可以尝试创建一个更轻量级的应用程序。
您可以通过 Jupyter 笔记本直接部署您的模型,其中一些包括瞧和 Jupyter-Dash 。
这是我不久前用 Jupyter-Dash 创建的一个机器学习应用程序:

作者图片
如果你觉得在 Jupyter 笔记本上工作更舒服,用 Voila 或 Jupyter-Dash 部署应用程序会更容易。但是,请记住,这些应用程序更加简单,您无法设计它们的样式或使网页具有交互性。
你可以看看这个 GitHub 库来开始学习。
深度学习资源

汤姆·赫曼斯在 Unsplash 上的照片
通过使用预先训练的模型,即使没有正确理解深度学习,也可以创建深度学习 web 应用程序。
我上面链接的所有教程都使用预先训练好的模型来帮助你创建一个机器学习 web 应用程序。
然而,不可能在每种情况下都使用预先训练好的模型。如果你想创建自己的深度学习模型来解决特定问题,有不同的课程/教程可供选择。
如果你想学习深度学习,我认为这里有一些课程和书籍会对你有用:
- 深度学习专精 —这是吴恩达在 Coursera 上做的深度学习专精,用 Tensorflow 授课。他用一种容易理解的方式分解数学概念。他不仅教授实践,还确保你对模型如何工作有很好的理论理解。
- 面向程序员的深度学习 —如果你喜欢用自上而下的方法学习(先实践实现,后理论),那么我推荐 Fast.ai 的这门课程。这门课程完全免费,你将使用 FastAI 库完成所有的实现。
- 用 Python 进行深度学习 —这是一本由 Francois Chollet 写的书,他是 Keras 图书馆的作者。他在理论和实际实施之间提供了极好的平衡。你将带着对深度学习的巨大直觉,对要使用的超参数调整技术的理解,以及适用于不同用例的模型来完成这本书。所有实际实施都将在 Keras 进行。
所有这些都是学习深度学习的惊人资源。没有必要做所有的事情,只需选择上面的一个。
以上资源都可以免费访问。用 Python 书的深度学习在网上是免费的,不需要证书可以选择审核 Coursera 专业化。
结论
如果你过去参加过深度学习或机器学习课程,巩固你的理解的最好方法是将你所学的应用到现实世界中。
通过创建项目展示你所学到的东西。不要在 GitHub 存储库中创建项目,而是尝试部署您的模型并创建一个功能性的 web 应用程序。
这样,你会激发人们的兴趣。通过以这种方式展示你的技能,你也会在求职时从其他求职者中脱颖而出。
一个完全免费的 R 语言数据科学家推断统计学课程

通过例子和项目学习
学好统计学对于数据科学家来说非常重要。学习可视化工具和数据操作工具都很棒!但是如果没有统计学的知识,就无法从数据中推断出一些真实的信息。
我写了几篇关于不同推断统计学主题的教程。现在我意识到,如果我把它们结合在一起,它将成为学习者的一门好课程。此外,除了第一篇文章之外,每一篇文章都处理一个带有数据集的项目。所以,通过做项目来学习是一种很好的学习方式。
同时,你会找到一些坚实的例子来制作自己的作品集。
在这里,我将从基础开始,转向一个更复杂的话题。
我建议,请按照顺序来。后面的文章假设您了解基础知识。
描述统计学
描述性统计包括基本概念,如平均值、中位数、标准差、最小值、最大值、四分位数范围等。下面的文章详细解释了所有这些概念。我建议在深入研究之前浏览一下这篇文章:
概率分布
概率分布是统计测试方法、一些预测模型和机器学习算法的基础之一。有这么多不同类型的概率分布可用。不同的概率分布在不同的条件下起作用。以下文章解释了一些常用的离散概率分布,如均匀分布、二项式分布、超几何分布、几何分布、负二项式分布、泊松分布,以及一些连续概率分布,如均匀分布、正态分布、指数分布,并附有公式、R 函数和用例示例:
置信区间和假设检验
为了对一个群体进行调查或研究,我们承担不起研究整个群体的费用。大多数情况下,我们会抽取一个样本,然后从样本中推断出总体均值或总体比例等参数。为此,置信区间和假设检验程序已经被使用了很长时间。使用 r 来执行这些测试要容易得多。本文用大量的例子解释了执行一些假设测试和构造置信区间的过程。
预测模型
预测模型是统计学中的高级功能。许多其他机器学习模型都是建立在一些简单的统计模型上的。最基本的预测模型是线性回归模型。接下来的两篇文章结合一些实际项目详细解释了简单和多元线性回归模型的理论、公式、R 函数和推断过程:
逻辑回归是稍微高级一点的预测模型。它基于简单的直线公式,如线性回归。但是有一点修改。它用于预测分类变量。尽管今天开发了许多更先进的预测模型,逻辑回归仍然是使用最广泛和最有效的模型之一。以下链接通过一个项目详细解释了逻辑回归:
方差分析和协方差分析
这是一个确定均值差异的过程。如果你不熟悉推断统计的概念,你可能会认为我们可以简单地取不同样本的平均值,看看平均值的差异。但是,当我们无法接触到整个人口,而且我们只能用一个样本进行工作时,仅仅取个体平均值的差异是不够的。ANOVA(方差分析)和 ANCOVA(协方差分析)是方差在找出差异中起重要作用的过程。下面是用一个项目解释这些概念的详细教程:
https://pub.towardsai.net/dissecting-1-way-anova-and-ancova-with-examples-in-r-a3a7da83d742
结论
如果您来这里是为了学习这篇文章,并试图使用这些文章来学习,那么您也应该使用自己的数据和实践。这是唯一的学习方法。理解这些想法可能需要一些时间。但是,如果你是一个有抱负的数据科学家,你就无法避免推理统计学。
更多阅读
如何在 Python 中选择和应用正确的统计测试的完整指南
基本了解何时应该应用 Z 检验、T 检验、卡方检验、方差分析和相关性检验

作者图片
我们经常低估统计数据及其重要性,根据我自己的经验,我可以肯定地说,这是数据科学中最重要的部分。数据科学中统计学最有用的部分之一是统计测试和假设检验。我们需要知道什么时候使用统计检验,这样我们才能对一个假设得出正确的结论。
假设检验有 3 个主要步骤:
- 陈述你的无效(Ho)和替代(H1)假设。
- 执行适当的统计测试。
- 决定是否拒绝零假设。
为了决定是否拒绝零假设,我们需要为 p 值设置一个阈值,称为 alpha 值。如果我们将 alpha 值设置为 0.05,这意味着我们对结果有 95%的把握。对于下面的统计测试,alpha 被设置为 0.05 。
数据集中主要有两种类型的变量。像性别这样的分类变量和像体重和身高这样的数字变量。
让我们创建一个包含两个分类变量和两个数值变量的数据集。
import pandas as pd import numpy as np
import random df=pd.DataFrame({'Gender':random.choices(["M",'F'],weights=(0.4,0.6),k=1000), 'Age_Group':random.choices(["18-35",'35-45','45-80'],weights=(0.2,0.5,0.3),k=1000)})df['Weight']=np.where(df['Gender']=="F",np.random.normal(loc=55,scale=5,size=1000),np.random.normal(loc=70,scale=5,size=1000))
df['Height']=np.where(df['Gender']=="F",np.random.normal(loc=160,scale=5,size=1000),np.random.normal(loc=172,scale=5,size=1000)) df['Weight']=df['Weight'].astype(int) df['Height']=df['Height'].astype(int) df.head()

关于一个分类变量的检验
样题:人口中男女数量有区别吗?
对于我们想要检查其值的数量之间是否存在差异的单个分类变量,我们将使用 one proportion Z 检验。我们来陈述一下假设:
- 胡先生:男女人数没有区别
- H1:男人和女人的数量是不同的
我们需要澄清这是一个双边测试,因为我们正在检查男性 Pm和女性 Pm 的比例是否不同。如果我们想检查 Pm > Pw 或 Pm < Pw ,那么我们将进行单尾测试。
from statsmodels.stats.proportion import proportions_ztestcount = 592 #number of females
nobs = 1000 #number of rows | or trials
value = 0.5 #This is the value of the null hypothesis.
#That means porpotion of men = porpotion of women = 0.5#we are using alternative='two-sided' because we are chcking Pm≠Pw. #for Pw>Pm we have to set it to "larger" and for Pw<Pm to "smaller" stat, pval = proportions_ztest(count, nobs, value, alternative='two-sided')
print("p_value: ",round(pval,3))
p 值小于 0.05。因此,我们在 95%的置信度下拒绝零假设。这意味着人口中男性和女性的数量存在差异。
关于两个分类变量的检验
例题:不同年龄段的男女比例是否不同?
如果我们想要检查两个分类值的独立性,我们将使用卡方检验。
让我们陈述假设:
- 胡先生:性别和年龄组是独立的
- H1:性别和年龄组有依赖性
from scipy.stats import chi2_contingency
#The easiest way to apply a chi-squared test is to compute the #contigency table.contigency= pd.crosstab(df['Gender'], df['Age_Group'])
contigency

#Chi-square test of independence.
c, p, dof, expected = chi2_contingency(contigency)print("p_value: ",round(p,3))p_value: 0.579
p 值不小于 0.05。因此,我们无法在 95%的置信水平下拒绝零假设。这意味着性别和年龄组是独立的。
测试一个分类变量和一个数字变量
样题:男女身高有差异吗?
在这种情况下,我们将使用 T 检验(学生 T 检验)。
from scipy.stats import ttest_ind
#this is a two-sided test #you can divide the two-sided p-value by #two, and this will give you the one-sided one.t_stat, p = ttest_ind(df.query('Gender=="M"')['Height'], df.query('Gender=="F"')['Height'])print("p_value: ",round(p,3))
p 值小于 0.05。因此,我们在 95%的置信度下拒绝零假设。这意味着男女身高有差异。
测试一个具有两个以上唯一值和一个数值变量的分类。
样题:各年龄段之间身高有差异吗?
现在,我们将使用 ANOVA(方差分析)测试。
- 胡先生:各组的平均身高是相等的
- H1:至少,一个群体的平均身高不同于其他群体
import scipy.stats as stats
#stats f_oneway functions takes the groups as input and returns #ANOVA F and p value
fvalue, pvalue = stats.f_oneway(df.query('Age_Group=="18-35"')['Height'], df.query('Age_Group=="35-45"')['Height'], df.query('Age_Group=="45-80"')['Height']) print("p_value: ",round(pvalue,3))p_value: 0.141
p 值不小于 0.05。因此,我们无法在 95%的置信水平下拒绝零假设。
测试两个数值变量
样题:身高和体重有关系吗?
- 胡先生:身高和体重没有关系
- H1:身高和体重是有关系的
我们将使用相关性测试。相关性测试会给我们两个结果,一个相关系数和一个 p 值。正如你可能已经知道的,相关系数是显示两个变量相关程度的数字。对于其 p 值,我们应用与之前相同的原则,如果 p 值小于 0.05,我们拒绝零假设。
import scipy.stats as stats
#for this example we will use the Pearson Correlation.
pearson_coef, p_value = stats.pearsonr(df["Weight"], df["Height"]) print("Pearson Correlation Coefficient: ", pearson_coef, "and a P-value of:", round(p_value,3) )Pearson Correlation Coefficient: 0.6213650837211053 and a P-value of: 0.0
正如我们所见,p 值小于 0.05,因此,我们在 95%的置信水平上拒绝了零假设。这意味着身高和体重之间有关系。
总结一下
这是对统计测试和假设检验的介绍。根据变量类型和一些常见问题,我们对何时应该应用 Z 检验、T 检验、卡方检验、方差分析和相关性检验有了基本的了解。你可以把这篇文章作为统计测试的备忘单,但是我鼓励你阅读更多关于它们的内容,因为正如我之前所说的,统计学是数据科学中最重要的部分。
【https://predictivehacks.com】最初发表于https://predictivehacks.com/how-to-choose-and-apply-the-right-statistical-test-in-python/。
为生产中的机器学习系统构建 Docker 映像的完整指南
一个完整的分步指南,用于构建 Docker 映像(GPU 或 CPU ),并解释所有应遵循的最佳实践,这些实践将用于任何基于机器学习的软件

作者图片
与开发 ML 系统的其他组件(如数据管道、模型训练、服务基础设施等)相比,构建 Docker 映像通常被认为是微不足道的。但是,低效、庞大的 docker 映像会大大降低性能,甚至会导致服务中断。
0.免责声明:
这个博客旨在建立一个理想的码头工人形象,而不是它的概念或好处。我假设你对写给 Docker 的几个主题有基本的了解:
- 码头工人的一般工作
- Docker 构建、运行的基础知识
- Dockerfile 文件的编写和语法
1.通用码头构建最佳实践
有很多关于一般最佳实践的非常好的资源,比如官方 docker 指南,但是我希望这篇文章简短并且与基于 ML 系统的项目相关
- Requirements.txt 必须始终有一个 python 包版本。永远不要只写包名,因为它将总是安装最新的包,在这个过程中完全违背了使用 docker 的目的。

来源:阿布舍克·塔库尔(推特)
- 始终将类似的运行命令组合在一起,这将产生一个停靠层。(我将避免解释这一点,因为这有点超出范围)
例如:
RUN apt update && \
apt install --no-install-recommends -y build-essential gcc curl ca-certificates python3 && \
apt clean && rm -rf /var/lib/apt/lists/*
- 使用 pip 的“- -no-cache-dir”标志,因为目标环境是生产环境
RUN pip install --no-cache-dir --user -r /req.txt
- 使用。避免不必要的构建上下文。这就像。gitignore
- 尽可能使用基本映像的精简版本,如 python:buster-slim、debian:buster-slim 等。
- 避免使用基于 Alpine 的基本 Docker 图像。这可能有一点争议,但相信我,它们与 Python 配合得不好。请参考伊塔马尔·特纳-图雷的这篇精彩的博客。
2.为任何 Python 项目构建 Docker 映像(CPU):
大多数情况下,ML 系统都是基于 Python 的,因此高效地构建任何基于 Python 的 Docker 映像都是至关重要的。让我们一一道来。
2.1 单级
- 单级将在同一个/单个 docker 构建时间内执行所有任务。
- 流程是,选择基本映像、安装操作系统包、复制源代码、安装包、设置入口点(如果需要)或其他命令。
单级 Docker 构建示例
出于演示目的,我使用以下软件包:

作者图片
运行 docker build 命令后,docker 映像的大小为 1.64 gb。

单级 docker 构建的大小(图片由作者提供)
- 单级非常简单,可以在许多用例中工作。这是一个不错的实践,但是有一些基本的缺点,特别是对于基于 python 的项目。
- 这里,在 apt 中使用'--no-install-recommendes '和在 pip 中使用'- -no-cache-dir '是关键,正如我前面说过的,我们不希望存储缓存,因为它不是为开发环境设计的,而是为生产环境设计的。事实上,如果您使用的是任何存储空间有限的 CI/CD 平台(如 Github action ),则只能使用这种方法。
- Python 库不是开箱即用的,必须先用 c 编译,我们只需要任何库的编译部分,而不是其他所有的剩余部分。正如你在上面的单级 eg 中看到的;当执行“pip 安装”时,所有的库首先被下载,然后被编译。
- 我们应该移除(我们可以使用 bash 命令)安装库时创建的所有中间和剩余组件。这将充满了麻烦,甚至可以打破一个图书馆,如果做得不正确。这是一个真正的交易破坏者,所以我们中的许多人会避免它&将笨重的形象带入生产。但是 Docker Multi-stage 拯救了我们。
2.2 多级
- 多阶段 Docker 构建是迄今为止最有效的优化技术之一,同时保持它们易于阅读和维护。要编写一个真正高效的 Dockerfile,您通常需要使用 shell 技巧和其他逻辑来保持层尽可能小,并确保每一层都有它需要的来自前一层的工件,而没有其他东西。
- 对于多阶段构建,您可以在 docker 文件中使用多个
FROM语句。每条FROM指令可以使用不同的基础,并且它们中的每一条都开始了构建的新阶段。您可以有选择地将工件从一个阶段复制到另一个阶段,在最终图像中留下所有不想要的东西。为了说明这是如何工作的,请看例子。
多阶段 Docker 构建的示例

作者图片
对比一下,多级 docker 镜像大小为 1.61 gb,单级为 1.64 gb。这是一个进步(尽管看起来很小),这里有很多东西,让我们试着用简单的话来理解。
- 第 1–5 行是第一阶段或编译器阶段,我们在这里安装 python 库(首先下载,然后用 C 编译,这就是我们安装 gcc 的原因)。然后,我们只是使用语法将编译后的库从阶段 1 复制到阶段 2 或运行时阶段
COPY --from=<stage 1> stage1/src stage2/destination
- 但是从截图中我们可以看到,我们并没有看到太大的改善。我们肯定会在其他语言中看到巨大的进步,但是 python 也有一些窍门,
→现在许多库都是预编译的。whl 是 PyPi 的车轮格式,不需要任何编译。
→那么这是否意味着,Python 项目没有多阶段构建的范围? 绝对是的!!! 不是每一个来自 PyPi 的包都是预编译的。whl 格式,许多都是传统的 tar.gz(压缩的 tarballs),需要首先编译&在这里,多阶段构建将发挥其魅力。
→此外,如果您正在从源代码构建 python 包或使用 setup.py 使用本地包,多阶段也是适用的,同样,它们需要首先进行编译。
→我强烈建议你阅读这篇来自真实 Python 的文章,解释什么是 Python 中的轮子。

作者图片
→从我在演示中使用的 req.txt 来看,只有上面的包不是轮式格式&而且它们的尺寸已经很小了。但是如果一些软件包不是预先编译好的,并且大小很大,最终会浪费很多磁盘空间
3.为任何 Python 项目(GPU)构建 Docker 映像:
构建基于 CPU 的 docker 映像并不复杂,但构建基于 GPU 的 Docker 则不同。如果建造不当,它最终会变得巨大无比。我将把重点放在实践和实现部分,而不是理论部分(因为我认为这超出了本文的范围)。
3.1 了解先决条件
- Tensorflow 和 Pytorch 都使用 Nvidia CUDA gpu 驱动。因此,最新的 Nvidia 驱动程序、CUDA 驱动程序及其各自的 cuDNN 必须首先安装在主机上(我不能在这里包括它的过程,因为它超出了范围,可能是其他一些博客)。
- 在准备好主机设备后,必须安装 nvidia-docker2 ,这使得 docker 引擎能够访问底层 Nvidia gpu 驱动程序。
- 最关键的部分是选择正确的 CUDA 版本/标签,cuDNN 为 nvidia docker 镜像,tensorflow/pytorch wrt 为其。因此 ML 系统可以利用底层 gpu 硬件。相信我,这确实是一项令人沮丧的任务,所以我有一些经验法则:
→在 Docker 映像中始终使用与底层主机中相同的 CUDA 和 cuDNN 版本。
→不要盲目安装 PyPi 最新的 tensorflow/pytorch 库。这是绝对不正确的,任何版本的这两个软件包将与任何版本的 CUDA,cuDNN。其实两者最新版本的组合,tensorflow/pytorch 和 CUDA/cuDNN 可能不兼容。总是首先在开发环境中测试组合。
Nvidia 的 Docker hub 有很多图片,所以了解它们的标签并选择正确的图片是最重要的组成部分。来自 Nvidia docker hub 官方的描述是,

我们只对 base、runtime 感兴趣,对 devel 不感兴趣(因为我们的目标是 prod 环境)。如何选择一个确切的特定标签?我将在下面的部分回答这个问题。
3.2 单级
- 选择标签:我遵循的经验法则是:
I .步骤 1:检查底层主机的 CUDA 和 cuDNN 版本

我有 CUDA=10.1,cuDNN=7.5(图片由作者提供)
二。步骤 2:根据步骤 1 选择 Docker 图像。所以就我而言,我选择了‘NVIDIA/cuda:10.1-cud nn 7-runtime’。为什么是运行时?因为这是一个既包含 CUDA 又包含 cuDNN 的。
三。第三步:选择与此版本 CUDA 和 cuDNN 兼容的正确版本 tensorflow/pytorch。在我的例子中,它是张量流=2.20。
四。注意:Nvidia 的 docker 镜像可能是旧的 Ubuntu (18.04 甚至 16.04),它将安装 python 3.6。所以这里必须注意检查你的项目以及外部包与 python 版本的兼容性。不管怎样,特定的版本可以从源代码安装。
单级 docker 构建(GPU)示例
注意:正如你所看到的,nvidia 的 Docker 镜像是基于 ubuntu 18.04 的,我必须做一点额外的调整来安装 tensorflow=2.2.0。

tensorflow 可以使用 gpu 的验证(图片由作者提供)

使用单阶段构建的 Docker 图像的大小(图像由作者提供)
3.3 多级
- 我们可以使用我在 2.2 中展示的相同机制。
- 第一阶段将用于下载和编译 python 包,然后它们将被复制到第二阶段或运行时阶段
- 这里还必须使用 3.2 中的所有经验法则
多阶段 docker 构建(GPU)示例
注意:为了使 python 3.8 成为默认版本,我添加了一些额外的代码,如果你不是这样,那么你可以避免这个麻烦。

tensorflow 可以使用 gpu 的验证(图片由作者提供)

使用多阶段构建的 Docker 图像的大小(由作者创建的图像)
- 同样,这不是一个显著的改进,但即使在这里,逻辑/解释也适用,如情况 2.2
- 我强烈推荐在任何用例中总是使用多阶段构建,因为它也提高了可读性。
4.使用 Dive 检查 Docker 图像
- 即使通过遵循所有可能的最佳实践建立了 docker 形象,我们仍然应该调查任何改进。
- Dive 是一个优秀的命令行工具,用于探索 docker 图像、图层内容,并发现缩小 Docker/OCI 图像大小的方法。它有 24k+ GitHub 星。此外,它非常容易使用和导航。
- 它有两个非常有用的矩阵:
一、潜在浪费空间
二。图像效率得分
- 但是它最好的特性是与任何 CI 工具的集成。我们可以为这两个指标中的任何一个或两个设置条件,如果条件失败,CI 作业也将失败。通过这种方式,我们可以始终对每项 CI 工作创建的 Docker 形象建立信心。
结论:首要目标始终必须是最小的 docker 映像大小,因为为 ML 系统构建的任何 docker 映像总是很大。我们应该始终遵循所有的最佳实践,尤其是多阶段构建和软件包的版本控制。对于基于 gpu 的映像,最后也是最重要的是在开发环境中测试配置。
如果需要帮助,可以通过LinkedIn联系我。
SQL 数据库规范化完全指南
使用 PostgreSQL 正面处理混乱的表格。

萨姆·穆卡达姆在 Unsplash 上拍摄的照片
假设您刚刚被要求管理公司的关系数据库系统。为了给人留下深刻印象,您很快运行了几个初始查询来熟悉数据…却发现表格杂乱无章。
你冻僵了。您担心不一致的依赖关系可能会对未来的数据操作查询和长期分析产生负面影响。但是您也不确定采取什么步骤来正确地重新设计表格。突然,一种不受欢迎的冲动开始困扰你,那就是翻看你很久以前上过的数据库管理课程的笔记。
听起来熟悉吗?
不要惊慌。你是否继承了一个杂乱的数据库,无意间合成了一个完整性很差的(呜呜!😬),或者想完全避免上述情况,数据库规范化是您的解决方案。
什么是数据库规范化?
根据维基百科上的数据库规范化页面:
“规范化需要组织数据库的列(属性)和表(关系),以确保它们的依赖关系由数据库完整性约束来正确实施。”
呀。
不要让这些类型的定义吓跑你。简单地说,这意味着规范化是创建最高效的关系数据库的过程。本质上,应该组织数据库以减少冗余并避免依赖性异常。
更简单地说,你的数据库结构应该有直观的意义。如果你的同事害怕在使用你创建的数据库时犯致命错误,即使你已经向他们解释了,你的数据库可能还没有规范化。
这些规范化理念可以应用于数据库合成(从头开始创建数据库)或分解(改进现有设计)。
什么是范式?
Giphy 的 Gif
“规范化”是一个宽泛的概念,当您迷失在无数杂乱的表格中时,它没有太多的实际用途。为了给这个过程增加具体的步骤,埃德加·f·科德制定了详细的正式规则。
Codd 的规范化指南有五种正式的范式,但是(谢天谢地)前三种通常和你需要的一样深入。让我们在这里简单回顾一下:
第一范式(1NF)
这一组初始规则为保持数据库的合理组织设置了基本准则。
- 删除任何重复的数据组(即注意同一表格中重复的列或行)
- 为每组相关数据创建单独的表
- 每个表应该有一个主键(,即一个用非空的唯一值标识每一行的字段)
第二范式(2NF)
下一组规则建立在 1NF 中概述的规则之上。
- 满足 1NF 的所有规则
- 删除不依赖于表主键的数据(将数据移动到适当的表或创建新的表和主键)
- 外键用于标识表关系
第三范式(3NF)
这组规则将 1NF 和 2NF 中概述的规则向前推进了一步。
- 满足 1NF 和 2NF 的所有规则
- 删除依赖于其他非键属性的属性(即删除依赖于非外键或主键的列的列)
你为什么要在乎?
Giphy 的 Gif
是的,高端数据库规范化通常被认为是一种奢侈,而不是绝对的需求。但是,即使是朝着正确方向迈出的一小步,也有助于避免数据完整性随着时间的推移而慢慢退化。
确保您的数据库依赖关系有逻辑意义并且冗余被最小化,同样也确保了最大程度的有洞察力的查询和分析。
规范化还可以防止数据操作(比如删除、插入和更新)异常。如果依赖关系没有被规范化,您将冒允许部分更新(因此,部分不正确)数据的风险。部分不正确的数据=部分不正确的查询结果。
数据库规范化示例
为了充分理解这些抽象的定义,让我们用一个具体的例子来回顾一下每个范式。在整个例子中,我们将关注于分解,但是这些概念仍然适用于基于合成的项目。
对于这些例子,我个人使用了 PostgreSQL (一个流行的开源关系数据库)和它的开发平台对应物 pgAdmin 。但是,同样,如果您喜欢替代的 SQL 服务器,如 MySQL 或 Oracle ,您可以将这里讨论的技术移植到您选择的平台上。
如果您对这些工具完全不熟悉,请参考提供的网站的概述和指导信息,或者 PostgreSQL/pgAdmin 上的数据科学文章,如本 one 或 one 。
数据
让我们假设你被一家公司雇佣,这家公司有一个数据库,里面有位于加利福尼亚的治疗师的信息。出于本教程的目的,我创建了一个模拟的 PostgreSQL 数据库,通过 pgAdmin 托管,其中的数据试图模拟一个类似的真实数据库可能包含的一小部分内容。该数据库包含以下表格:

原始数据库模式
从表中,我们可以看到我们有一系列与治疗师有关的变量,他们在哪里工作,他们擅长什么,以及我们如何联系他们。
在我们开始之前,先浏览一下可用数据本身。分解之前一定要仔细检查你的值- 不要假设你只通过列名就知道表中有什么。


我们将在整个教程中继续使用这些合成表。如果您想查看用于创建和填充这些表的原始 SQL 语法,请访问我的 GitHub 项目报告这里。还请记住此处使用的所有合成数据仅用于演示目的,并不能准确代表加州医院、治疗师人口统计数据或典型的数据集规模(您可能已经知道,SQL 通常用于大数据项目,而不是总共有 20 行的表格)。
最初的询问
使用上面显示的数据库,您的公司希望您运行一个查询:
确定北加州专门治疗情绪障碍的治疗师的数量,以及这些治疗师中,有多少目前正在接受新病人。
然而,在查看了这些表之后,您会发现尝试准确的查询可能是一个挑战。这两个表之间的关系尚未建立,似乎有冗余信息。在运行请求的查询之前,您必须进行一些分解。
1NF
回想一下,规范化的第一步(1NF)涉及正确的行标识和正确的数据分组。
我们先来补救一下两个表的关系:治疗师 _ 目录(父表)和医院(子表)。治疗师 _ 目录已经有一个主键(“治疗师 _id”),但是医院缺少主键和外键。我们可以用序列和主键规范为医院添加一个递增的主键:
ALTER TABLE hospitals ADD COLUMN hospital_id SERIAL PRIMARY KEY;
添加一个外键将需要从治疗师目录表中提取信息,以将其与医院准确链接。尽管医院表中的“治疗师”列有每个治疗师的姓名,但这并不是理想的外键,因为正如您将在接下来的步骤中看到的,我们将更新治疗师 _ 目录中相应的“姓名”列。相反,为了一致起见,让我们将治疗师的 id 添加到医院中。
UPDATE hospital h
SET therapist_id = td.therapist_id
FROM therapist_directory td
WHERE td.name = h.therapist;
现在,我们可以更新医院中包含的“therapist_id”列,以表明它是一个外键,从而准确地链接数据库中的表。因此,从医院中删除“治疗师”一栏也将确保表格中的数据与治疗师的工作地点特别相关。
ALTER TABLE hospitals
ADD CONSTRAINT fk_therapist_directory
FOREIGN KEY (therapist_id)
REFERENCES therapist_directory(therapist_id);
现在,让我们解决表中出现的冗余信息。
再次引用数据后,我们看到,为了使数据保持最简化的形式,我们应该使用 SUBSTRING()将治疗师 _ 目录的“姓名”列拆分为名字和姓氏列。
/* make new last_name column */
ALTER TABLE therapist_directory ADD COLUMN last_name VARCHAR(30); /* add last name values to last_name */
UPDATE therapist_directory
SET last_name =
SUBSTRING(name, POSITION(' ' IN name)+1, LENGTH(name)); /* update name column to first_name */
ALTER TABLE therapist_directory
RENAME COLUMN name TO first_name; /* remove last name substring from first_name */
UPDATE therapist_directory
SET first_name =
SUBSTRING(first_name, 1, POSITION(' ' IN first_name)-1);
治疗师 _ 目录表中也有重复的数据组(“专业 _ 一”、“专业 _ 二”、“专业 _ 三”)。我们将把这些变量移到它们自己的表中。不要忘记给新表添加一个主键和外键!(参见 GitHub 此处了解使用的全部 SQL)。
CREATE TABLE IF NOT EXISTS specialties(
specialties_key serial PRIMARY KEY,
speciality_one VARCHAR(100),
speciality_two VARCHAR(100),
speciality_three VARCHAR(100),
therapist_id INTEGER,
CONSTRAINT fk_therapist
FOREIGN KEY(therapist_id)
REFERENCES therapist_directory(therapist_id));
现在让我们看一下数据库模式。

到目前为止一切看起来都很好。是时候去 2NF 了。
2NF
回想一下,第二种范式涉及删除与主键无关的数据,并建立外键来巩固表关系。
因为我们所有的表目前都有外键(万岁!),我们可以重点识别不相关的数据。
在治疗师目录中,我们有三列与“治疗师 id”主键不匹配:保险、新患者和电话。“保险”和“新病人”并没有指明每个治疗师是谁。相反,每个人都指出了选择治疗师时要考虑的访问规范。
考虑到这一点,让我们将这两个变量移到它们自己的名为 visit_specifications 的表中。我们可以通过再次使用 CREATE TABLE 语法来做到这一点。并且,请记住,只要表是以一对一的关系连接的,那么如果一个列同时作为主键和外键也是可以的。
CREATE TABLE IF NOT EXISTS visit_specifications(
therapist_id INTEGER PRIMARY KEY,
insurance VARCHAR(3) CHECK(insurance IN ('Yes', 'No')),
new_patients VARCHAR(3) CHECK(insurance IN ('Yes', 'No')),
CONSTRAINT fk_visits
FOREIGN KEY(therapist_id)
REFERENCES therapist_directory(therapist_id));
太好了,现在我们在治疗师 _ 目录里只剩下一个不相关的栏目:电话。仔细看,我们可以看到列出的号码实际上是医院的电话号码,而不是每个治疗师的个人联系信息。

我们可以继续将电话号码添加到更合适的医院表中,但是这种修改会违反 3NF!让我们转到第三范式来详细解释为什么会这样。
3NF
回想一下,第三种范式涉及删除依赖于列的。
…这实际上意味着什么?🤨
让我们一步一步地看“phone”列的例子来理解这个需求。
如上所述,我们可以将“phone”列添加到医院表中,以更好地匹配“hospital_id”主键。然而,电话号码值将取决于“hospital _ id”和医院所在的城市。让我们再来看一下上一个查询返回的数据:

我们可以清楚地看到,在同一城市的同一家医院工作的治疗师都有相同的电话号码(例如,有 2 名治疗师在旧金山的蓝十字诊所工作,但在不同地点的同一家医院工作的治疗师有不同的电话号码(还有 2 名治疗师也在蓝十字诊所工作,但因为他们位于洛杉矶,所以他们的电话号码与旧金山的两名蓝十字诊所治疗师的电话号码不同)。
这意味着电话号码取决于医院(主键)和医院所在的城市。将“电话”值放入他们自己的表中,而不是医院的表中,可以防止在不改变电话号码的情况下意外改变治疗师的城市。
这是传递依赖的基础:列值根据主键和表中的其他列而变化。
为了避免破坏数据完整性,我们可以创建三个表:一个为每个特定位置的医院建立一个键,一个根据医院 id 建立电话号码,一个为每个治疗师对应的医院 id。最终的数据库模式如下所示(再次使用,查看提供的 GitHub 页面 ,这里的 用于完整的 SQL 查询):

太棒了,现在我们的列只依赖于它们各自的主键!🥳,我们的正常化步骤到此结束
重新访问最初的查询
既然数据库符合 1NF、2NF 和 3NF 标准,您可以重新访问您的公司所请求的查询。以防万一,这里有一个复习:
确定北加州专门治疗情绪障碍的治疗师的数量,以及这些治疗师中,有多少目前正在接受新病人。
假设情绪障碍包括“焦虑”、“抑郁”和“躁郁症”,您可以使用以下查询,而不会对数据完整性产生怀疑:
SELECT
sub.new_patients,
COUNT(therapist_id) AS norcal_therapists
FROM
(SELECT s.therapist_id, s.speciality_one, s.speciality_two, s.speciality_three, td.new_patients
FROM specialties s
JOIN therapist_directory td ON s.therapist_id = td.therapist_id
JOIN therapist_location tl ON td.therapist_id = tl.therapist_id
JOIN locations l ON tl.hospital_id = l.hospital_id
WHERE l.city ~ '(San Francisco|Oakland|San Jose|Sacramento|Auburn)') sub
WHERE
speciality_one ~ '(Anxiety|Depression|Bipolar)'
OR speciality_two ~ '(Anxiety|Depression|Bipolar)'
OR speciality_three ~ '(Anxiety|Depression|Bipolar)'
GROUP BY sub.new_patients;

根据调查结果,北加州有 11 名治疗师专门治疗情绪障碍,而这 11 人中,有 9 人目前正在接受新病人!
除了像这样的一般分析之外,具有非传递依赖列的规范化数据库同样允许我们满怀信心地完成数据操作查询。如果你的公司要求你改变一个治疗师工作的城市(而不是医院),你可以很容易地使用一个更新语句来完成,而不用担心意外地没有同时更新电话号码。
UPDATE
therapist_location
SET
hospital_id = (SELECT hospital_id
FROM locations
WHERE hospital_name = 'Open Clinic'
AND city = 'San Jose')
WHERE
therapist_id = 15;
上述查询将 Sid Michael(therapist _ id = 15)的医院位置从奥克兰更改为圣何塞。因为她的医院 id 现在在治疗师 _ 位置表中是最新的,所以我们不需要通过另一个更新查询来手动更改 Sid 的联系信息。相反,Sid 的联系信息会根据她的新医院 id 自动更新。我们可以用一个 JOIN 语句对此进行双重检查。
SELECT
td.first_name,
td.last_name,
l.hospital_name,
l.city pn.phone_number
FROM
therapist_directory td
JOIN
therapist_location tl
ON td.therapist_id = tl.therapist_id
JOIN
locations l
ON l.hospital_id = tl.hospital_id
JOIN
phone_numbers pn
ON tl.hospital_id = pn.hospital_id
WHERE
td.therapist_id = 15;

简短的闭幕词
分解一个数据库可以简单到 1(NF),2(NF),3(NF)!要更深入地了解模拟的“Northern_California_Therapists”数据库和为本文合成的单个 SQL 查询,请查看我的相关 GitHub 知识库这里。
正常化快乐!🤖
自然语言处理完全指南(第三部分)
机器如何识别人类语言并据此行动

“语言是文化的路线图。它告诉你它的人民来自哪里,他们将去哪里”——丽塔·梅·布朗
动机
我想分享一下我的真实经历。早在 2016 年,我被孟加拉国一所著名的工程大学录取,目标是成为计算机科学毕业生。在我第四学期开始的时候,我开始知道机器学习这个时髦词。我立即投入到机器学习中,并渴望了解相关技术。我开始从最基础的 ML 算法开始学习。在熟悉了 ML 算法之后,我做了一些项目来了解如何实现 ML 算法来解决现实生活中的问题。我惊讶于我的 ML 模型的令人满意的结果。在一个无聊的晚上,我是员工,坐在宿舍的阳台上,探索我的智能手机的功能。发现语音助手选项这么准,纳闷。我的心灵被某种感觉所触发,我应该知道这项技术的机制。随即,我冲到房间,拿着笔记本电脑坐了回去,用谷歌搜索了一下,发现一个名为自然语言处理(NLP) 的新术语是语音助手的关键。我开始深入了解 NLP。乍一看,我认为这很难,但是经过几天的学习,我清楚地知道 NLP 只不过是一些与 ML 模型集成的技术。有趣的是,这些技巧与语法规则非常兼容。
然而,在这篇文章中,我将讨论我所学到的 NLP 所必需的所有基础知识。阅读这篇文章不需要任何先验知识。
基本概述
语言作为人与人之间交流的媒介起着非常重要的作用。它将人类与其他生物区分开来。一般来说,语言(书面或口头)承载着巨大的信息。当我们说话或写东西时,它表示包含一些单词、语法规则、音调信号等的主题。语言的每一部分都提供了一些信息。如果有人试图分析这些信息,他可能会得到综合信息,表明一些行动或类似的事情。
当我们致力于为机器开发一个自动系统时,我们必须从这个角度考虑问题。考虑一个真实的场景,一个人可以生成成千上万的单词和句子。另外,不同的人的句子结构和风格是不同的。你能找到表示数据的通用方法吗?你不能。另一个例子是推特、脸书等社交媒体的帖子。人们的用词、造句、表达方式等。可能因个人和地理区域而异。所有这些都是非结构化数据的例子。这些很容易被人脑识别和处理。但是如果是机器呢!?一些现代技术,如机器学习、数据科学,使之成为可能。
现在,我们可以很容易地获取句子的意思,识别演讲,从一堆书面文件中推断信息,分析情感,将句子从一种语言翻译成另一种语言等等。
试着正式一点……
自然语言是指人类用来交流和自然进化的语言。埃拉·库马尔在他的书“自然语言处理”中对自然语言处理的定义如下——
自然语言处理是计算机科学和语言学的一个领域,涉及计算机和人类(自然)语言之间的交互。自然语言生成系统将计算机数据库中的信息转换成可读的人类语言[1]。
如今大多数现代工业都在使用自然语言处理。这是一个蓬勃发展的部门。不断增长的计算能力、对数据的大量访问以及使用数据科学的现代技术成为可能。
我们在哪里使用 NLP
简单地说,NLP 用于需要通过自然语言与智能系统进行人类交互的地方。自然语言处理的应用越来越广泛。在各行各业,我们都在下意识地使用 NLP。
为了深入了解自然语言处理在我们日常生活中的作用,下面给出了一些例子
- 在农业领域,NLP 可以通过提供基于问题分析的智能解决方案来支持新手农民。NLP 还用于使用实时农民数据分析作物疾病的趋势。它还可以通过语音助手帮助发展中国家的文盲农民。
- NLP 广泛应用于医疗保健行业。这种疾病是通过分析病人的病历和语音检测出来的。基于疾病和症状的药物建议是 NLP 的另一个重要应用。它还有助于支持临床决策。
- 在需要大量技术支持的大型行业中,聊天机器人在自动回复和智能服务客户方面发挥着关键作用。BM Watson 是一款有趣的基于自然语言处理的行业级聊天机器人支持工具。
- 电子商务的推荐系统是 NLP 的另一个应用。电子商务在现代工业中占有很大份额。从这个角度来看,基于自然语言处理的推荐系统对商业有很大的影响。
- NLP 的另一个有前途的用例是网络安全。谷歌、雅虎等。邮件服务使用 NLP 来检测垃圾邮件。钓鱼和不安全的网站也可以用 NLP 检测出来。点击这里,您可能还会了解到 NLP 在网络安全表单中的更多应用。
- Google 也使用 NLP 将一种语言翻译成另一种语言。 斯坦福自然语言处理小组 在机器翻译领域也做得非常出色。
- 对于一个智能系统来说,根据自然语言命令行动不是一件容易的事情。谷歌的语音助手,苹果的 Siri,亚马逊的 Alexa 都在使用 NLP 作为语音助手。
- 情感分析也是 NLP 的一个重要且广泛使用的用例。通过分析社交媒体帖子和其他文章的情绪,我们可以获得有意义的信息。
NLP 的应用不仅限于上述用例。关于 算法等级 的文章解释了下一篇文章中的一些其他用例—
面向绝对初学者的 NLP 基础
语言,作为社会群体的成员和文化的参与者,人类用来表达自己的一种传统的口语、手势语(手语)或书面符号系统[2]。
当我们相互交流时,人脑对语言的编译和识别并不困难。在大多数情况下,人们不必在意语法规则,这似乎是人脑的一个自动过程。但是对于一台机器来说,事情并不那么容易。这是一项复杂的任务,需要严格遵守规则。因此,在自然语言处理中应该有效地使用几种技术。这些技术很有趣,python、R 和其他编程语言库被用来完成这些任务(代码将在接下来的文章中讨论)。NLP 有两个组成部分。分别是 i)自然语言生成 ii)自然语言理解。
NLP 的组件

作者图片
- 自然语言生成是指使用 NLP 技术生成有意义的语言。
- 自然语言理解表示对特定语言含义的提取。它比自然语言生成更复杂。尤其是对机器而言。这需要一些技巧和一套规则。
是时候了解这些技术了—
标记化
标记化是自然语言处理的第一步。想法很简单,把句子分成独特的单元。先说个例子。
孟加拉的自然美景深深吸引了我。
如果我们把这个句子符号化,它将如下。

作者图片
好像挺简单的吧!只是通过跟随空格来分隔句子。在标记化中,标点符号会被自动删除。但是对于所有的语言来说,情况并不总是一样的。还有一些其他的场景。假设在一个句子中,有一个名字,迈赫迪博士,它应该是一个记号,因为它代表一个名字。博士和迈赫迪的分离没有意义。另一个例外可能是硅谷这样的地方的名字。如果我们考虑在一个空格后划分记号的简单过程,硅和谷将是两个不同的记号。但它代表一个地点的名称。在使用学名的情况下,我们会经常遇到这些问题。所以,我们应该非常小心。
词干
词干提取是为了找到词根而删减单词的过程。主要是通过去掉一个单词的后缀和前缀来完成的。让我们对后缀和前缀有一些了解。
加在单词开头部分的词缀,称为前缀(如不高兴。“un”加在 happy 这个词的开头。所以,‘un’是前缀)。另一方面,后缀是指加在单词最后的额外部分(例如,小心。ful 加在 care 这个词的最后。所以,‘ful’是后缀)。
举个例子,
Doing → Do,Helping → Help但并不总是正确的。这个过程有很多限制。如果我们考虑新闻和应用词干,它将是news → new。但它已经完全改变了意义。
虽然词干法有局限性,但它被用来提高速度和降低计算成本。
词汇化
词汇化与词干化是兼容的。词汇化的主要思想是从单词的不同形式中找出词根,并统一同义词。例如,‘affected’是‘affect’的过去式。在这里,“情感”是词根。考虑到形容词的比较级和最高级形式的转换,bad 是 bad 和 worst。在这里,词根是“坏”。
词汇化是一个语言学术语,指的是将具有相同词根或词汇,但具有不同词形变化或意义导数的单词组合在一起,以便它们可以作为一个项目进行分析[2]。
这里,“lemma”是词根的意思。从上面的例子来看,单词“坏”和“影响”是引理。
“词干化”和“词汇化”的过程有些不同。词汇化过程总是找出实际的词根。但是词干提取过程只是把单词砍掉,去掉后缀和前缀,并不总能找到词根。让我们举个例子——

作者图片
从上面的例子中,我们可以看到,在单词“believes”的词源中,只有后缀“es”被删除,但在词汇化的情况下,我们发现了一个有意义的单词。
词汇化更准确,但它需要复杂的语法知识和其他规则。它也需要强大的计算能力
停止字
停用词是指在 NLP 情况下没有价值或价值极小的词。“一个”、“一个”、“这个”等词。有助于造句。但是如果我们想推断出什么信息,我们可能会发现这些词并不重要。停用词不是固定的。
在编码中,有一些 python、R 和其他编程语言的库可以去掉停用词。我们可以根据需要添加或删除默认单词。去除停用词有助于我们提高效率。它减少了数据库的大小和计算能力。
位置标记
词性标注。词性标注是对特定句子的单词的词性进行标注的过程。例如,句子“猫杀死了老鼠”的词性标注如下

作者图片
这里,**the → determinant, cat → noun, killed → verb, rat → noun**。有时,当同一个词用在不同的词类时,就变得很困难。一般来说,“谷歌”是一个名词,但如果你看看这个句子“我已经谷歌过这个话题。”在这里,“谷歌”被用作动词。
命名实体识别
它将句子中的名词分为不同的类别,如人、组织、地点、时间表达、数量、货币价值、百分比等[3]。让我们举个例子——
穆罕默德·哈姆扎加入谷歌时是纽约分公司的软件工程师。
这里,**Mohammed Hamza → person, google → organization, New York → location**。
还有一个基础话题叫做话袋。这可能会在另一篇文章中讨论。现在,你可以从这里读出这袋单词。

毫无疑问,自然语言处理使我们的生活变得更容易,减少了我们的工作量,提高了效率。最后一点,NLP 不仅仅适用于英语,而是适用于所有语言。
你也可以读出以下有趣的文章作为 NLP 的基础
参考
[1].K. Ela,自然语言处理(2011),IK 国际私人有限公司
[3].https://www.techslang.com/definition/what-is-lemmatization/
[4].基于 NLTK 和 SpaCy 的命名实体识别| Susan Li |走向数据科学
用 Seaborn 绘制分类变量的完全指南
了解 Seaborn 如何让您的绘图看起来更好,传达更多信息,并且只需要几行代码

目录
数据
分类分布图
∘ 箱型图
∘ 小提琴图
∘ 箱型图
分类估计图
∘ 柱状图
点状图
∘ 计数图
分类散点图
数据
在本帖中,我们将使用 Seaborn 的一个关于泰坦尼克号的方便可用的数据集,我相信许多读者已经看过了。Seaborn 有相当多的数据集可以加载到 Python 中进行实践;它们非常适合练习数据处理、探索和基本的机器学习技术。
titanic = sns.load_dataset('titanic')titanic.head()

泰坦尼克号. head()
titanic.info()titanic['species'].unique()

泰坦尼克号信息()
这个数据集很棒,因为它有相当数量的条目——近 900 个——同时还有一个有趣的故事可以挖掘。有很多问题要问,变量之间的关系要探索,使它成为一个很好的示例数据集。对于本文来说,最重要的是,还有一个很好的数字和分类变量的混合。
分类分布图
我们有两种不同的分类分布图,箱形图和小提琴图。这些类型的图允许我们选择一个数字变量,如年龄,并在选择的分类变量中绘制每个类别的年龄分布。
箱线图
这些年来,我们中的许多人可能已经绘制了相当多的方框图。它们是通过四分位数可视化数字数据组的简单而有效的方法。Seaborn 使创建有吸引力的盒状图变得简单,并允许我们轻松地将一个额外的维度与许多 Seaborn 函数中出现的hue参数进行比较。
基本方框图
让我们来看看乘客等级的年龄分布。
plt.figure(figsize=(8,5))sns.boxplot(x='class',y='age',data=titanic, palette='rainbow')plt.title("Age by Passenger Class, Titanic")

基本 Seaborn 箱线图
我们可以看到,随着乘客级别的降低,年龄有下降的趋势。这是有道理的,年轻人倾向于在预算内旅行。注意创建一个非常美观的情节需要多少代码?Seaborn 的基本剧情都很打磨。
还要注意我们如何将 Matplotlib 格式语法包装在 Seaborn 图中。这只在我们使用轴级函数时有效,你可以在我的另一篇关于 Seaborn 中图形级和轴级函数的文章中读到。
添加色调
像 Seaborn 中许多其他可用的图一样,箱线图可以采用一个添加的hue参数来添加另一个变量进行比较。

带色调的箱线图
添加色调向我们表明,不管阶级如何,幸存乘客的年龄一般都低于那些去世的乘客。
使用hue进行额外的比较,可以使这个盒状图的信息非常密集。情节越复杂,观众理解它就需要越长的时间,但当有趣的见解更容易以额外的维度显示时,有这个选项是很好的。
小提琴情节
小提琴情节并不经常使用,但我发现它们有时很有用,它们是对更受欢迎的选项的有趣改变。他们为每个类别绘制了一个垂直的核密度图,并绘制了一个小方框图来总结重要的统计数据。
plt.figure(figsize=(10,6))sns.violinplot(x='class',y="age",data=titanic, hue='sex', palette='rainbow')plt.title("Violin Plot of Age by Class, Separated by Sex")

小提琴情节
虽然我喜欢这个图,但我认为用稍微不同的格式来比较性别更容易:
plt.figure(figsize=(10,6))sns.violinplot(x='class',y="age",data=titanic, hue='sex', **split='True'**, palette='rainbow')plt.title("Violin Plot of Age by Class, Separated by Sex")

小提琴情节在色调上分裂
当我们把小提琴按色调分开时,就很容易看出每个 KDE 的不同。然而,IQR 的统计数据不再按性别划分了;相反,它们适用于整个班级。因此,以特定方式设计您的地块需要权衡利弊。
博兴图
boxen 图,也称为字母值图,是一种适用于大型数据集(n > 10,000)的箱形图。它类似于传统的箱线图,但本质上只是绘制了更多的分位数。有了更多的分位数,我们可以看到超过中心 50%数据的分布形状的更多信息;这种额外的细节尤其出现在尾部,这里的盒状图提供的信息有限。
plt.figure(figsize=(8,5))sns.boxenplot(x='class', y='age', data=titanic, palette='rainbow')plt.title("Distribution of Age by Passenger Class")

简单博森图
万一这里还没有发生足够的事情,我们还可以在一个 boxen 情节中添加一个hue!
plt.figure(figsize=(8,5))sns.boxenplot(x='class', y='age', data=titanic, palette='rainbow', hue='survived')plt.title("Distribution of Age by Passenger Class, Separated by Survival")

带色调的 Boxen 图
我们可以看到,除了中间的 50%的数据,boxen 图给了我们更多的信息。但是,请记住,boxen 图适用于条目在 10,000 到 100,000 之间的较大数据集。这个不到 1000 个条目的数据集肯定不理想。这里有一篇论文的链接,这篇论文很好地解释了博兴地块的产生。
分类估计图
条形图
条形图很经典。你得到了 x 轴上每一类数值变量的集中趋势的估计值。假设我们想知道来自不同城镇的乘客的平均票价:
plt.figure(figsize=(8,5))sns.barplot(x='embark_town',y='fare',data=titanic, palette='rainbow')plt.title("Fare of Passenger by Embarked Town")

基本条形图
Seaborn 将平均值作为默认值,但是您也可以使用其他集中趋势的度量。瑟堡和其他两个城镇有明显的不同,让我们把酒吧按等级分开,看看每个城镇都有谁在寄宿。
plt.figure(figsize=(8,5))sns.barplot(x='embark_town',y='fare',data=titanic, palette='rainbow', hue='class')plt.title("Fare of Passenger by Embarked Town, Divided by Class")

带色调的条形图
现在我们可以看到,由于一些非常昂贵的头等舱机票,瑟堡的平均票价如此之高。从瑟堡出发的头等舱票价上的大误差栏也很有趣;这可能意味着一些价格非常高的异常门票和其他门票之间存在很大的差异。我们将在下面的组合图部分进一步探讨这个问题!
点图
点图传达的信息与样式不同的条形图相同。它们可以很好地覆盖不同的地块,因为它们占用的空间较小。
plt.figure(figsize=(8,5))sns.pointplot(x='embark_town',y='fare',data=titanic)plt.title("Average Fare Price by Embarked Town")

基点图
plt.figure(figsize=(8,5))sns.pointplot(x='embark_town',y='fare',data=titanic, hue='class')plt.title("Average Fare Price by Embarked Town, Separated by Sex")

带色调的点图
计数图
计数图本质上是分类变量的直方图。他们采用与 Seaborn 中条形图相同的参数,这有助于保持事情的简单性。
plt.figure(figsize=(8,5))sns.countplot(x='embark_town',data=titanic, palette='rainbow')plt.title("Count of Passengers that Embarked in Each City")

基本计数图
plt.figure(figsize=(8,5))sns.countplot(x='embark_town',data=titanic, palette='rainbow',hue='sex')plt.title("Count of Passengers that Embarked in Each City, Separated by Sex")

用色调计数绘图
分类散点图
带状图和群集图本质上都是散点图,其中一个变量是分类的。我喜欢使用它们作为其他类型的图的补充,我们将在下面讨论,因为它们对于快速可视化一个组中的数据点的数量很有用。
带状图
plt.figure(figsize=(12,8))sns.stripplot(x='class', y='age', data=titanic, jitter=True, hue='alive', dodge=True, palette='viridis')

当你有很多数据点时,我不喜欢带状图的样子。但是群体图可能会让这个更有用一点。带状图可以用更少的数据点看起来很棒,并且它们可以传达数据的真正有趣的属性,因为它们没有隐藏聚合背后的细节。
群体图
群体图非常奇妙,因为它们提供了一种简单的方法来显示分布中的单个数据点。与带状图中的大斑点不同,群图只是沿 x 轴调整点。尽管它们也不能很好地适应大量的价值,但它们提供了更有组织的洞察力。
plt.figure(figsize=(10,7))sns.swarmplot(x='class', y='age', data=titanic, hue='alive', dodge=True, palette='viridis')plt.title("Age by Passenger Class, Separated by Survival")

群体图
在这里,我们可以更容易地看到密集的年龄组在哪里,而不是上面难以解释的带状图。
组合图
我最喜欢用群体图来增强另一种图,因为它们很好地传达了相对量。正如我们将在下面的 violin 图中看到的,尽管在某一点上 KDE 值可能看起来类似“大”,但每个类中的数据点的数量可能有很大不同。我们可以在 violin 图的基础上添加一个蜂群图来显示各个数据点,这有助于给我们一个更完整的画面。
plt.figure(figsize=(12,8))sns.violinplot(x='class',y="age", data=titanic, hue='survived', split='True', palette='rainbow')sns.swarmplot(x='class',y="age", data=titanic, hue='survived', dodge='True', color='grey', alpha=.8, s=4)plt.title("Age by Passenger Class, Separated by Survival")

小提琴图和蜂群图
通过添加虫群图,我们可以看到实际大多数数据点包含在哪里。我曾多次看到小提琴图被曲解,观众可能会假设大约相同数量的 25 岁三等舱乘客在三等舱生存,而蜂群图很好地澄清了这一点。
plt.figure(figsize=(12,8))sns.boxplot(x='class',y='age',hue='survived',data=titanic, palette='rainbow')sns.swarmplot(x='class',y='age',hue='survived', dodge=True,data=titanic, alpha=.8,color='grey',s=4)plt.title("Age by Passenger Class, Separated by Survival")

箱形图和群集图
这个故事与盒子情节和小提琴情节非常相似。每组的汇总统计非常有用,但是添加群体图有助于展示一个更完整的故事。
还记得我们之前看的按乘客等级划分的城镇平均票价吗?

带色调的条形图
我们看到瑟堡机票的价格很高,这是因为瑟堡头等舱机票的平均价格很高。瑟堡头等舱机票的平均价格也有很大的误差。使用带状图,我们可以尝试更好地了解那里正在发生的事情。
plt.figure(figsize=(12,7))sns.barplot(x='embark_town',y='fare',data=titanic, palette='rainbow', hue='class')sns.stripplot(x='embark_town',y="fare",data=titanic, hue='class', dodge='True', color='grey', alpha=.8, s=2)plt.title("Fare of Passenger by Embarked Town, Divided by Class")

条形图和条形图
现在我们可以看到在瑟堡卖出了两张非常昂贵的票,这扭曲了平均值,这就是为什么我们的头等舱柱状图有一个很大的误差线。虽然有两个人支付了接近第二贵的头等舱机票的两倍,但也有头等舱的人支付了比二等舱乘客更低的票价!当我们组合情节时,我们会获得各种新的见解。
使用 Catplot 对数据进行分面
Catplot()是图形级函数,可以创建我们讨论过的所有上述图。图形级函数绘制 Seaborn 对象并与 Matplotlib API 接口,而不是像 Seaborn 的轴级函数那样创建 Matplotlib 对象。
虽然使用图形级函数通常更复杂,文档也不太清晰,但在某些情况下,它们有一些优点值得使用。正如我们在下面看到的,他们特别擅长将数据分成支线剧情。
g = sns.catplot(x='class',y='survived', col = 'who', data=titanic,
kind='bar', aspect=.6, palette='Set2')(g.set_axis_labels("Class", "Survival Rate")
.set_titles("{col_name}")
.set(ylim=(0,1)))plt.tight_layout()
plt.savefig('seaborn_catplot.png', dpi=1000)

Catplot 数据分面
分面数据允许我们以不同的粒度查看数据。分面实际上是一个时髦的词,用于将数据按照特定的维度分成不同的类。因此,这里我们将数据与“谁”变量分开,这使我们可以分别绘制每种类型的人。
能够说col='<column_name>'来自动分面是大多数图形级函数都可以使用的强大选项。在 Matplotlib 中完成同样的事情需要更多的时间来设置数据子集和手动创建多个子图。我会在这篇文章的中讨论图形级绘图的威力。
不要忘记,我们仍然可以添加一个hue参数来为这个图添加更多的信息!使用 Seaborn 的图形级功能对数据进行分面处理是制作更复杂绘图的一种很好的方式。
你会注意到 Seaborn 图形需要不同的函数来格式化,然而保存绘图仍然可以通过plt.savefig()来完成,因为最终的 Seaborn 图形与 Matplotlib API 接口。我不会详细讨论图形级别的绘制,因为有很多内容需要讨论,但是如果你感兴趣的话,请阅读我的另一篇文章。
包扎
在这篇文章中,我们经历了许多不同的情节。我希望你已经看到了 Seaborn 可以多么容易地制作出一个美学上令人愉悦的情节,向观众传达许多有用的信息。一旦我习惯了使用它,Seaborn 为我节省了大量的时间,编写了更少的代码来产生令人愉快的可视化效果。
如果你喜欢在 Medium 上阅读我和其他人的内容,考虑使用下面的链接订阅,以支持像这样的内容创建并解锁无限的故事!
https://medium.com/@willmnorris/membership
文档和链接
海生的
推荐系统完全指南——包含 Sklearn、Surprise、Keras 和推荐器的教程
推荐系统实施的简要介绍和实践指南

哈基姆·拉赫曼在 Unsplash 上的照片
介绍
我们都知道,推荐系统在从零售、电子商务、娱乐到食品配送等许多行业中起着至关重要的作用。它极大地提升了任何平台上的用户体验。想象一下,你反复滚动 marketplace feed,你对手中所有推荐的东西都非常满意,尽管你可能不想要。这个组件是任何业务的事实上的标准。
对于我自己来说,我也是在 Spotify 的推荐系统中留下印象进入数据科学领域的。我很惊讶 Spotify 的推荐有多精准。我探索我从未听过的音乐新趋势和流派,发掘我喜欢的歌曲类型。这个灵感驱使我写这篇文章。
目录
您在本文中可以期待的是:
- 概述—介绍推荐系统的前景
- 实现——说明如何实现各种推荐系统。
提供了以下实现:
- 基于受欢迎度,IMDB 加权评级
- 基于内容的风格特征
- 协同过滤——基于记忆——亲和矩阵
- 协同过滤—基于内存—截断的 VD (Sklearn)
- 协同过滤—基于模型— Funk MF(惊喜)
- 协同过滤——基于模型——GMF(Keras)
- 协同过滤——基于模型——NCF(推荐器)
- 其他先进的算法
此外,在文章的结尾还提供了动手教程笔记本。
概述
今天,我们将深入探讨各种推荐系统,我们将为您提供每个部分的实践教程代码和解释。我们几乎没有找到完整的描述和实践指南。因此,本文将这两个方面整合在一起,为您提供启动推荐系统实现的一站式服务。
概观

推荐系统概述,图片由作者提供
上图显示了推荐系统的高级概览。看起来它没有太多种类的推荐引擎。然而,在每个基于的建议中有许多变化。我们将向您介绍一些算法,并为您提供进一步的资源供您探索。如今,这一研究领域仍在迅速发展。因此,要在这篇文章中囊括所有最新的技术并不容易。
现在,让我给你解释一下上面每个建筑盒子的基本描述。
- 流行型 —这是基线表现,也是我们随处可见的最直观的推荐。例子有 IMDB 收视率最高的电影,今日贵国前十名网飞等。当你是一名新加入者,并且提供商没有足够的关于你的信息时,可以找到这些建议。所以向你推荐别人喜欢的东西是一个稳妥的选择。
❗限制:所有用户得到相同的推荐集。不是个性化的。

国家排名前 10 的系列,图片由作者提供
2.基于内容— 如果您想改进基于流行度的方法,我们可以结合每个项目的细节,推荐更符合用户需求的内容。例如,您可能会在网飞上看到下图。它为你提供了一个项目列表,因为你喜欢某部特定的电影。该电影在诸如流派、子流派、频道、国家等方面与用户喜欢的内容相关或相似。推荐的质量取决于您的特性集有多丰富。
❗限制:推荐将限于用户以前喜欢、观看和互动的内容。它没有给用户一个探索他们从未去过的新领域的机会。此外,所有喜欢项目 X 的用户都将收到相同的推荐集。

基于内容的推荐,按作者排序的图片
3.协同过滤 —现在,如果你有关于用户和用户之前交互的项目的先验信息,会怎么样?你如何能想出一个更复杂的推荐引擎?这就是协同过滤发挥作用的地方。协同过滤基于所谓的用户-项目交互矩阵推荐项目集。这是用户-项目交互矩阵的样子。

用户-项目交互矩阵,按作者排序的图像
每个单元格中的数字代表用户给予特定项目的显性或隐性评分。
详细说明显性和隐性评级。
- 显式评分是我们直接要求用户对特定项目的满意度评分。例如,如果我们假设数字是用户对特定项目的满意度,范围从 1 到 5。这意味着用户 1 喜欢满意度得分为 3 的项目#1。
- 另一方面,隐性评分是我们从用户行为中间接得出的评分。例如,他们的用户观看电影的分钟数或用户浏览产品页面的点击次数。
如何构建用户-项目交互矩阵的选择取决于你的业务目标。鉴于上述矩阵,现在我们想为每个用户创建一个个性化的建议。
在协同过滤方法中有两种主要的方式来实现它。
3.1。基于用户的协同过滤 —这种技术将基于我们从上述用户-项目交互矩阵中得出的相似用户组来个性化我们的推荐。下图向您展示了我们是如何为用户#1 提出建议的。您可以看到,对于每个用户,推荐集将根据相似用户组而变化,并且相似用户组将根据用户#1 与每个项目的交互方式而变化。

基于用户的协同过滤,按作者分类的图像
⭐️通知: 这是为每个用户个性化的推荐集。 每个用户都将根据他们过去互动过的项目获得他们的推荐,这将导致每个用户的不同推荐集,除非他们喜欢所有相同的产品集。
❗限制:在我们能够导出用户#1 和其他人的相似性之前,我们需要关于用户的先验信息。如果你没有关于用户#1 的任何信息,你就不能得出预测,因为你不能计算用户之间的相似性。这个问题被称为冷启动问题。我们可以通过首先向新用户提供基于流行的推荐列表来解决这个问题,让他们先给他们喜欢每部著名电影的程度打分,直到我们有足够的信息。

基于个性化的推荐,按作者分类的图片
3.2。基于项目的协同过滤 —我们将找到与用户#1 刚刚喜欢的项目相似的项目组,并将其推荐给用户#1,而不是找到一组相似的用户。这种方法将类似于基于流行或基于内容的推荐。但是,区别在于如何想出一套类似的物品。这里我们使用用户-项目交互矩阵,而不是像上面提到的方法那样的评级或流派。
❗限制:你需要关于新项目和其他项目的先验信息,以及基于用户的协同过滤。否则,您无法衍生出相似的项目组来推荐。我们可以通过首先为新项目提供基于内容的推荐来解决这个问题,直到他们有足够的信息。

基于内容的推荐,按作者排序的图片
4.混合——我们看到每种方法都有它的长处。如果我们能够结合所有这些优势,提供更好的推荐,那将是最好的。这个想法引导我们对推荐的另一个改进,那就是混合方法。例如,我们可以将基于内容和基于项目的协同过滤推荐结合在一起,以利用这两种领域特征(流派和用户-项目交互)。
履行

莱尼·屈尼在 Unsplash 上的照片
现在,我们已经完成了对推荐系统的简单介绍。我们将讨论的下一个主题是上述所有方法的变体。
- 怎么才能把那些高层次的概念映射到实际的算法上呢?
- 我们可以使用多少种推荐算法来导出最佳推荐集?
- 我们如何评估每种方法的性能?我们如何在相同的基础上进行比较?
我们开始吧。
基于大众的

基于流行的实现可以很快完成。您可以从所有用户中平均评分最高的项目、投票数最高的项目或被观看的成员数开始。
为了使它更复杂,我们向您介绍基于 IMDB 加权评级的流行方法。
关于 IMDB 系统,有一种叫做加权评分系统的指标用于对每部电影的评分。
下面是加权评分的公式。
WR = (v ÷ (v+m)) × R + (m ÷ (v+m)) × C
在哪里
R是该项目的平均评分。v是该项目的投票数。m是列入热门项目所需的最低票数(由>总票数的百分之 80 定义)C是整个数据集的平均评分。
对于每个项目的平均评分,我们会根据收到的投票数调整分数。我们还根据投票数筛选项目组,以确保在合适的候选人中调整分数。
基于流行的推荐,按作者编码
预言;预测;预告
使用提供的代码片段,我们生成了流行商品的列表。让我们看看基于 IMDB 加权评分的 10 大热门商品。这里,我们使用合成数据作为我们计算的例子。

按加权评分排名的 10 大热门项目,按作者排序的图片
现在,我们可以把上面图表中的名字带到我们应用程序的 UI 中,如下图所示。

国家排名前 10 的系列,图片由作者提供
我们可以添加预定义的功能,如时间和位置,使其更符合用户的偏好。如上图所示,网飞还添加了时间(今天)和地点(你的国家)来区分前 10 个基于人口的推荐。
基于内容

对于基于内容的,我们将使用每个项目的流派功能向用户推荐相关或相似的项目。
基于我们的数据集,我们有动作、喜剧、戏剧等类型特征。我们将对流派列进行一次性编码,并找出每个条目之间的相似性。最后一步是从最高到最低排列相似性得分,并根据我们想要提供的推荐数量选择项目集。
下面是提供基于内容的推荐的代码片段。
相关项目数据框如下所示。

分级项目数据框,按作者分类的图像
基于内容的推荐,按作者编码
预言;预测;预告
让我们查一下编号为 99 的物品。你可以看到,最受推荐的剧集是爱情片和动作片,这与基于内容的推荐理念相一致。

为了将您链接回基于内容的推荐界面,我们可以用我们的推荐列表填充下面的 UI 模板。

基于内容的项目推荐,按作者排序的图片
协同过滤
协作过滤有两种主要方法。
- 基于记忆的
- 基于模型的
似乎我们没有太多的方法可以建立模型。然而,每种方法内部的变化是无限的。在阅读本文的过程中,您会看到更多的细节。开始吧!
基于记忆的方法

照片由 Soragrit Wongsa 在 Unsplash 上拍摄
基于记忆的方法非常简单,它直接从用户-项目矩阵计算相似性矩阵。
让我们看看用户-项目交互矩阵。对于协同过滤,我们需要做的第一件事是找到用户或项目之间的相似性。

用户-项目交互矩阵,按作者排序的图像
相似性度量
- 皮尔逊相关性 —线性关系最广为人知的相似性度量是人物相关性。它根据值的变化方向来衡量两个样本的相似程度。
- 余弦相似度——顾名思义,它度量的是多维空间中两个向量的余弦角度。两件事可以在方向上相似,而不是在大小上相似。
- 其他相似性得分——我们还可以得出许多其他相似性得分。这里有一个例子,一个混合的基于用户的协同过滤算法,主题模型【1】。它们提供了一种利用时间衰减的潜在狄利克雷分配来计算相似性度量的方法,这是一个很好的例子,说明了我们如何利用推荐系统的这一部分。
您可以选择您喜欢的任何相似性度量。使用您选择的相似性度量,我们可以计算用户或项目之间的相似性,然后根据以下步骤进行推荐。
基于用户的实现
- 我们根据您的相似性度量选择找到相似用户的组(组大小是任意的)。
- 我们根据相似用户群对每个项目的评分进行平均
- 根据平均评分降序排列项目,并向目标用户推荐他们以前从未互动过的项目。
基于项目的实施
- 我们根据您选择的相似性度量标准找到相似项目组。
- 选择最多前 k 个最相似的项目进行推荐。
⭐️注意:你可以看到,我们可以在没有学习参数的情况下导出推荐集,就像我们在其他机器学习模型中所做的那样。这就是基于内存的名字的由来。我们创建的引擎会记住用户喜欢什么和不喜欢什么,然后我们根据这些交互的相似性检索结果——不需要任何推理。
❗限制:因为该方法的想法是记住用户和项目之间的每一次交互,这里将发生的问题是引擎的可伸缩性。在现实中,用户数量和项目数量的不平衡使得用户-项目矩阵非常稀疏,导致预测结果的泛化能力很差。
还有一点要考虑,假设你有了一个新的用户和物品,你需要为那些新用户做一个新的推荐。您必须通过添加这些新用户并计算每对用户之间的相似性来再次拟合模型。此外,真实应用程序中的用户和项目规模非常庞大,导致用户-项目交互度量更加稀疏。这个执行计划会导致短时间内无限的资源消耗。
更好的调整
现在,代替使用用户-项目交互矩阵的直接计算。我们将把用户-物品交互矩阵分解成代表更有用的低维空间的潜在因素矩阵。
让我简单地向你介绍一下这个想法背后的数学概念。分解的思想是我们相信观察到的用户-项目评级矩阵是从潜在用户和项目潜在因素矩阵构建的。
假设我们可以提取出使重构矩阵和原始矩阵之间的损失最小的最佳潜在因子矩阵。然后我们可以使用用户和项目潜在因素矩阵的内积来推断未观察到的评级。
我们把这个概念和方法叫做 矩阵分解 。有几种矩阵分解技术,每一种都提供一组不同的结果,从而产生不同的建议。
这是经典方法如奇异值分解、主成分分析或先进前沿技术如深度学习出现的地方。这一领域的研究仍在迅速发展。
基于模型的方法

维多利亚诺·伊斯基耶多在 Unsplash 上拍摄的照片
这是你可以有很大发挥空间的地方。顾名思义,我们将结合一些种类的模型作为预测的推荐引擎。
我们从基于记忆的方法中得知,直接计算用户和项目的相似性在可扩展性方面是不实际的。
⭐️注意:基于模型的方法和基于记忆的方法之间的关键区别在于基于模型的方法包括基于评级数据集构建模型。更准确地说,我们从用户-项目交互矩阵中提取数据,并将其用作提出建议的模型。这解决了基于内存的方法的可伸缩性问题,从而使现实世界的实现更容易。
技法
这里我们将解释我们在实践指南笔记本中实现的三种矩阵分解。我们可以使用一个基本的技术来创建一个简单的推荐引擎。
然而,如果你想看看最先进的技术,我也会附上我感兴趣的资源供你进一步探索。
截尾 VD (Sklearn)
这里我们将带您了解的第一种方法是使用truncated VD和 sklearn 库进行矩阵分解。
TruncatedSVD 是奇异值分解的变体,它只计算 K 个最大奇异值(n_components)。此外,它还应用了 线性降维 ,并能很好地处理稀疏矩阵,如用户项目矩阵。

用户-项目交互矩阵,按作者排序的图像
我们的目标是将用户项目矩阵分解成这些潜在因素。每个单元的值将是满足优化约束(SVD 假设)的估计值。另一个矩阵分解的例子是非负矩阵分解 (NMF)。

用户潜在因素矩阵,作者图片

项目潜在因素矩阵,图片由作者提供
下面是如何在 python 中将 truncatedSVD 应用于用户项矩阵的示例代码片段
截断的 VD 实现
这似乎是这种方法的一个类似版本,但是我们考虑到了分解步骤。这将使推荐更加健壮,并减少大尺寸用户-项目交互矩阵的存储器消耗。然而,当我们有一个新的用户或项目时,我们仍然需要在进行预测之前重新调整用户-项目交互矩阵。
之后,我们可以像基于记忆的方法一样实现该步骤,以创建基于用户或基于项目的推荐。
⭐️注意:现在,推荐将基于一些潜在的因素,与基于记忆的方法的显式或隐式评级相比,我们不能直接解释这些因素。
❗限制:即使我们用分解方法减小矩阵的大小,可扩展性仍然是这个算法的一个问题。此外,可解释性也是一个问题。我们不知道如何用人类的解释来描述每个潜在的因素。
Funk 矩阵分解 ( 惊喜)
该算法由 Funk,Simon [ 2 ]发起。它持有矩阵分解的概念,将用户-项目交互降低到更低维的空间潜在矩阵中。然而,没有像奇异值分解或主成分分析那样的降维技术被应用。
⭐️注意:即使惊喜包中的方法名是 SVD,它也是一种不同的方法,与奇异值分解方法无关。Funk MF 只是一种类似 SVD 的算法。
FunkFM 的目标是估计潜在因子矩阵和偏差,偏差被称为最小化原始显式评级和重建预测评级之间的损失。因此,这就是为什么我们称这种方法为基于模型的。
预言;预测;预告
要进行预测评级,我们可以乘以估计的潜在因素矩阵,并添加用户或项目的偏差项,如下图所示。

Funk MF 预测公式
在哪里
u表示用户索引,i表示物品索引。r(hat)ui是用户u对商品i的预测评分mu是所有评分的平均值b是用户或项目的偏差术语(基线)qi是项目潜在因素矩阵pu是用户潜在因素矩阵
如果我们不知道关于用户的信息,那么项bu和pu将被假定为零。因此,新用户的预测评级将是所有评级的平均值加上bi项,这意味着如果我们不知道用户,我们将向他们推荐我们从数据中了解到的具有高基线项的产品。
这同样适用于项目术语bi和qi。如果是新用户和新项目,我们的预测评分将等于所有评分的平均值。
学问
为了让你更好的理解优化什么,这里是在惊喜库中实现的损失函数。

Funk MF 最小化的目标函数
在哪里
lambda是我们可以调整的正则化超参数。
我们还可以在估计的潜在因子和偏差项上添加正则化,并调整模型中潜在因子的数量。
使用随机梯度下降(SGD)方法,所有学习参数将被随机初始化和更新。拟合的模型将用于预测特定用户对特定项目的评级。
❗限制:在评分预测中你可以看到,这个模型只考虑了显性评分(用户给物品的一个真实评分),并不关心隐性评分(点击次数、花费在物品上的时间等)。).这个限制也有所改善。例如,您可以查看一下 SVD++ 算法。
下面是如何在 python 中将 funk MF 应用于用户项矩阵的示例代码片段
Funk MF(类奇异值分解算法)实现
广义矩阵分解(GMF)
⭐️注意:这种方法的名称并不通用。它只在神经协同过滤【3】的上下文中有效。
对于这个实现,当我开始学习深度学习如何与推荐系统一起工作时,我在 this Keras 示例上找到了这个教程。通过一个简短而精确的代码片段,它对我理解如何构建推荐引擎的神经网络架构有很大帮助。
我还发现实现是基于神经协同过滤【3】的部分后续研究。在第 3.2 节中,论文展示了如何将矩阵分解作为神经协同过滤(NCF)框架的特殊情况。
他们的出发点是,密集稀疏输入用户和项目向量(用户-项目交互矩阵)的嵌入层可以被视为正常矩阵分解过程中的潜在因子矩阵。
预言;预测;预告
记住上面提到的想法,这就是他们提供预测的方式。

GMF 预测公式
在哪里
y(hat)ui是用户u对物品i的预测a(out)是神经网络最后一层的激活函数h^T是输出图层的边权重pu和qi在这里可以看作是嵌入层产生的潜在向量。circle(.)是逐元素的乘积运算
根据如何导出预测,如果我们使用相同函数作为激活函数a(out)并且使用一的均匀向量作为输出层的边缘权重h^T。我们可以证明矩阵分解是 NCF 框架的特例,它是由潜在因子矩阵的内积得到的预测。
然而,对于这里提到的 GMF,他们用 sigmoid 函数替换了a(out),并从输入数据中学习h^T,以最小化对数损失。
总而言之,GMF 模型的架构是下图中的部分红线。这只是整个 NCF 模型的一部分,我们将在下一个主题中进一步讨论。

提出 NCF 架构的图,参考图 3 来自神经协同过滤【3】
也就是说,让我们看看如何在 Keras 中实现这个模型。这里,我们还增加了神经网络结构的变化,以预测评级,而不是参考文件中提出的 0 到 1 之间的值。
GMF 实现,作者代码
现在我们来看看如何用 Keras 库集成和构建推荐系统。这开启了利用深度学习架构为推荐引擎构建架构的可能性。这是另一种推荐引擎,相比于像 Surprise 这样的软件包,我们可以调整和使用它。
神经协同过滤(NCF)(推荐器)

该图提出了 NCF 架构,参考图 3 来自神经协同过滤【3】
最后但同样重要的是,让我们看看我们前面提到的 NCF 算法的完整架构和强大功能。
你可以看到整个 NCF 建筑有一个多层感知(MLP)部分。这一提议的想法结合并激活了模型如何利用非线性函数来估计潜在因素矩阵。其思想是,由于用户-项目交互矩阵的复杂性,只有前面矩阵分解技术的线性乘积不足以检索有用的信息。因此,提出了添加 MLP 部分以帮助捕捉数据中的模式的想法。
基于推荐库中 movielens@100k 数据集的初步比较,NCF 算法优于传统的 Funk MF(Surprise package 中的类 SVD 算法)。这个证据表明了深度学习方法在推荐系统中的重要性。
让我们看看推荐库实践教程的最后一段代码。
NCF 实施
其他尖端技术
当我学习和研究推荐系统时,它把我带到了微软提供的推荐器库。这个库将我对推荐系统的观点和理解提升到了一个新的层次。
在推荐库的第一眼,我被许多看不见的方法淹没了。一些方法似乎是当今推荐引擎的最新技术。但是在我花了一些时间使用 Github、文档和提供的例子之后。我意识到所有的内容都非常丰富和有用。
如果您阅读了本文的这一部分,您将对推荐系统有足够的了解。然而,天空是无限的。你可以用推荐者库中的东西在这条路上走得更远。深入研究各种详细的算法需要一些时间,但是我们认为如果你需要在推荐系统领域出类拔萃的话,这是非常值得的。
在接下来的部分,我会为你提供一些有趣的阅读资源。
这里有一些有趣的资源,我想你应该看看
- 双边变分自动编码器(BiVAE)
- 贝叶斯个性化排名
- 光图卷积网络
- Nvidia-Merlin 提供的 Transformaer4Rec】
- [推荐系统列表](http://List of Recommender Systems)
最后的想法
这里我们来结束这篇文章。我希望所有的材料和解释对于开始你在推荐系统领域的旅程足够有用。
没有正确的方向,我开始很难在互联网上收集信息。因此,我相信这个指南将帮助你从推荐系统的基础开始,并为你提供进一步的阅读和探索未来的努力。
这个领域正在发生很多事情。因此,您必须不断地跟上技术的步伐,以利用您的所有数据,并向您的所有用户提供最满意的建议集!
实践教程
您可以在这个 Colab 笔记本中找到以下所有代码。笔记本还在开发中,我会随着对推荐系统的了解不断更新。
https://colab.research.google.com/drive/1w92d6_BjrF5nVLoFhD1Oa6dV3DgKra8G?usp=sharing
参考
[1] Na,l,Ming-xia,l .,Hai-yang,Q. 等 一种基于主题模型的混合用户协同过滤算法。应用智能 51,7946–7959(2021)。
[2]放克,西蒙。《网飞更新:在家里试试这个】 (2006)
[3]何湘南,廖,汉王张,,聂,,蔡达生.神经协同过滤 (2017),第 26 届万维网国际会议论文集(WWW '17)。
SQL 和 Python 收入群组分析完全指南
了解客户行为的重要数据分析工具
介绍
理解你的客户和他们的行为是任何成功创业的顶峰,这正是群组分析的目的。群组分析是一个非常有用的工具,可以让你收集关于客户流失、终身价值、产品参与度、粘性等方面的见解。
群组分析对于改进用户加入、产品开发和营销策略特别有用。群组分析之所以如此强大,是因为它们本质上是一种三维可视化,您可以随时比较不同细分市场的价值/指标。
在本文结束时,您将学习如何创建这样的东西:

作者创建的图像
如果你不确定你在看什么,或者为什么这会有用,请继续关注并阅读。
什么是队列分析?
为了理解什么是队列分析,你首先需要理解什么是队列。一个群体仅仅是一群有共同之处的人。因此,群组分析只是对几个不同群组(即客户群)的分析,以更好地了解行为、模式和趋势。如果这还没有完全理解,继续读下去,我向你保证会的!
群组类型

基于时间的群组
最常见的群组分析类型之一是查看基于时间的群组,该群组通过特定的时间框架对用户/客户进行分组。例如,一家公司可能希望了解一月份开始使用该产品或开始付款的客户与二月份的客户相比情况如何。
基于细分市场的群体代表使用或购买特定产品或服务的客户群体。例如,你可以根据用户每周登录你的应用的时间来划分用户。
另一种类型的群组是基于规模的群组,即根据客户的货币价值对其进行细分。这是游戏行业(免费用户对鲸鱼)或 SaaS 世界的常见做法,通过客户的 LTV 或计划对客户进行细分。
在本文的剩余部分,我们将只关注实施基于时间的收入群组分析。
队列分析所需的数据
在开始队列分析之前,需要以下数据:
- 收入数据链接到采购数据
- 用户的唯一标识符,如客户 ID 或账户 ID
- 一个初始开始日期为每个用户,无论是注册日期或首次付款。
例如,您可能有一个如下所示的表:

SQL 中的群组分析
在 SQL 中进行群组分析时,目标通常是以特定格式处理数据,然后将结果导入 BI 工具,如 Sisense、Mode、Looker 等。
假设我们正在处理上表,我们可以使用 SQL 以下列方式操作数据:
首先,我们希望将用户划分到不同的群组中——在本例中,我们希望根据他们的Order Date来划分。
with user_cohorts as (
SELECT customerId
, MIN(DATE_TRUNC('MONTH', orderDate)) as cohortMonth
FROM orders
GROUP BY 1
)
接下来我们想要创建一个order_month变量。例如,客户在初次付款后一个月进行的付款的order_month为 2。
with order_month as (
SELECT customerId
, DATE_DIFF(
MONTH,
cohortMonth,
DATE_TRUNC('MONTH', orderDate)
) as month_number
, SUM(revenue) as revenue
FROM orders
LEFT JOIN user_cohorts USING(customerId)
GROUP BY 1, 2
)
后退一步,我们现在可以通过第一步中创建的cohortMonth来合计收入。这将允许我们创建我们的rentention_table。
with cohort_size as (
SELECT sum(revenue) as revenue
, cohortMonth
FROM orders
LEFT JOIN user_cohorts USING (customer_id)
GROUP BY 1
ORDER BY 1
)with retention_table as (
SELECT c.cohortMonth
, o.month_number
, sum(revenue) as revenue
FROM order_month o
LEFT JOIN user_cohorts c USING (customerId)
)SELECT r.cohortMonth
, s.revenue as totalRevenue
, r.month_number
, r.revenue / s.revenue as percentage
FROM retention_table r
LEFT JOIN cohort_size s USING (cohortMonth)
WHERE r.cohortMonth IS NOT NULL
ORDER BY 1, 3
现在,您应该有一种支持群组分析的商业智能工具能够消化的数据格式(或者总是有 Excel🤣)

以下是一些帮助你实现这种可视化的技巧:
- 每行代表一个组群。在这种情况下,一个群组代表一个给定的月-年。
- 群组规模(左边的条形)显示了每个群组的基线收入,也就是从第 0 个月开始的总收入。
- 百分比是相对于每个群组在第 0 个月的收入而言的。
Python 中的群组分析
Python 中的数据操作过程非常相似,唯一的区别是您可以使用 Seaborn 等工具直接可视化群组分析。
如果你想看群组分析的例子,请查看我的 GitHub。这仅仅是艾里克·莱温森 的延伸,所以所有的荣誉都归于他!
进行队列分析的价值
“一幅画等于千言万语”
群组分析不仅有助于衡量和评估与收入相关的趋势,如净 MRR 留存、客户流失、终身收入等,还可以帮助实现以下目标:
- 用户行为:群组分析可以让你了解群组的生命周期,因此,它可以让你更好地了解客户在他们的生命周期中是如何参与业务的。在上面的例子中,我们可以立即注意到销售是重复销售通常在圣诞节前后更高。
- 比较群组:无论你是按时间段、产品还是其他方式划分群组,群组分析都能让你比较不同的群组。请注意,在上面的例子中,第一组的重复销售比例比其他组高得多。通过识别这一点,你就可以研究第一批人购买更多的可能原因。
- 客户流失&终身价值:最后,群组分析可以让你计算有用的指标,比如流失和终身价值。通过了解不同群组之间的流失率和终身价值的差异,您可以从流失率低的群组和终身价值高的群组中学习。
感谢阅读!
如果你读到这篇文章的结尾,你应该知道什么是队列分析,为什么它如此有用,以及如何进行队列分析!我保证,拥有这个工具在你的职业生涯中会派上用场,因为每个公司都可以从中受益。
一如既往,我祝你学习一切顺利——下次再见!
这篇博客最初发布在 Census 的博客上,你可以在这里查看https://blog.getcensus.com/a-complete-guide-to-revenue-cohort-analysis/。人口普查局和 TDS 允许我转贴这篇博客。
不确定接下来要读什么?我为你选了另一篇文章:
*
又一个!
特伦斯·申
Python 中用于图像分类的完全逻辑回归算法

带有项目的逻辑回归算法的详细布局
逻辑回归在机器学习和统计学中非常流行。它可以很好地处理二元和多元分类。我以前写过用逻辑回归进行二元和多类分类的教程。本文将集中讨论用逻辑回归进行图像分类。
如果你对逻辑回归完全陌生,请先阅读这篇文章。本文详细解释了简单的逻辑回归算法是如何工作的。
如果你已经熟悉了逻辑回归,这将会很有帮助。如果没有,我希望你仍然理解这里的概念。我试着解释清楚。
如果你读这个是为了学习,唯一的办法就是自己运行所有的代码。
问题陈述
这个项目的想法是开发和训练一个模型,能够采取一个数字的像素值,并确定它是否是一个数字 1 的图像。
将在本教程中使用的数据集在机器学习教程中非常常用。著名的数字数据集。数据集的每一行表示一个数字的展平像素值。后面我会详细给你看。
数据准备
该数据集包含从 0 到 9 的数字的像素值。但是因为本教程是关于二进制分类的,所以这个模型的目标是如果数字是 1 就返回 1,否则返回 0。请随时从下面的链接下载数据集,以便跟进:
https://github.com/rashida048/Machine-Learning-With-Python/blob/master/ex3d1.xlsx
我在这里导入数据集:
import pandas as pd
import numpy as npdf= pd.read_excel('ex3d1.xlsx', 'X', header=None)
df.head()

您可以看到数据集有 400 列。这意味着每行有 400 个像素值,每行代表一个数字。让我们使用 matplotlib 库的“imshow”函数来检查一些数字。请注意,图像的像素值最初不是一维的。这就是为什么它在传递给“imshow”函数之前被整形为一个 20 x 20 的二维数组。
import matplotlib.pyplot as plt
plt.imshow(np.array(df.iloc[500, :]).reshape(20,20))

是一个!这里我使用了数据集的第 500 行。
下面是另一个使用数据集第 1750 行的例子:
plt.imshow(np.array(X.iloc[1750, :]).reshape(20,20))

现在是三点。
让我们检查一下这个数据集中有多少行:
len(df)
输出:
5000
标签存储在此 excel 文件的不同工作表中。以下是标签:
df_y= pd.read_excel('ex3d1.xlsx', 'y', header=None)
df_y.head()

我只显示了产生前 5 行的数据集的头部。因为这个模型将只识别数字 1,如果数字是 1,它将返回 1,否则返回 0。所以,在标签中,我将只保留 1,其余的数字将变成 0。让我们将其余的数字转换为零。
为此
y = df_y[0]
for i in range(len(y)):
if y[i] != 1:
y[i] = 0y = pd.DataFrame(y)
y

在这 5000 行数据中,4000 行将用于训练模型,其余 1000 行将用于测试模型。对于任何机器学习或深度学习模型来说,通过模型看不见的数据进行测试是很重要的。
x_train = X.iloc[0:4000].T
y_train = y.iloc[0:4000].Tx_test = X.iloc[4000:].T
y_test = y.iloc[4000:].T
使用。t,我们取每个数据集的转置。这些训练和测试数据集是数据帧形式的。为了便于计算,它们需要是数组格式。
x_train = np.array(x_train)
y_train = np.array(y_train)
x_test = np.array(x_test)
y_test = np.array(y_test)
训练和测试数据集已准备好用于模型中。这是开发模型的时候了。
第一步:
逻辑回归使用我们在高中都学过的基本线性回归公式:
Y = AX + B
其中 Y 是输出,X 是输入或独立变量,A 是斜率,B 是截距。
在逻辑回归中,变量是这样表示的:
公式 1

这里 z 是输出变量,x 是输入变量。w 和 b 将首先被初始化为零,并且它们将在训练模型时通过迭代次数来修改。
这个输出 z 通过一个非线性函数。常用的非线性函数是 sigmoid 函数,其返回值介于 0 和 1 之间。
公式二

提醒一下,sigmoid 函数的公式是:
公式 3

这个“a”将是最终输出,即“y_train”或“y_test”中的值。
下面是定义 sigmoid 函数的函数,供以后使用:
def sigmoid(z):
s = 1/(1 + np.exp(-z))
return s
正如我们之前提到的,w 和 b 将被初始化为零。将为每个像素值初始化一个 w 值。让我们定义一个函数来初始化 w 和 b 的零值:
def initialize_with_zeros(dim):
w = np.zeros(shape=(dim, 1))
b = 0
return w, b
成本函数
成本函数是对模型的一种度量,它反映了预测输出与原始输出的差异程度。下面是一个训练示例或一行数据的成本函数的公式:
公式 4

所有行的平均成本函数为:

该模型的目的是降低成本函数值。
梯度下降
我们需要更新公式 1 中的变量 w 和 b。它将被初始化为零,但是它们需要在以后用更合适的值来更新。梯度下降将有助于这一点。让我们看看怎么做。
在公式 4 中,我们将成本函数表示为“a”和“y”的函数。但是它也可以表示为“w”和“b”的函数。因为‘a’是用‘w’和‘b’派生出来的。
“w”和“b”的微分公式将通过对“w”和“b”的成本函数进行偏导数来推导。
公式 5 和公式 6

现在我们有了所有的公式,让我们把它们放在一个名为“propagate”的函数中:
def propagate(w, b, X, Y):
#Find the number of training data
m = X.shape[1]
#Calculate the predicted output
A = sigmoid(np.dot(w.T, X) + b)
#Calculate the cost function
cost = -1/m * np.sum(Y*np.log(A) + (1-Y) * np.log(1-A))
#Calculate the gradients
dw = 1/m * np.dot(X, (A-Y).T)
db = 1/m * np.sum(A-Y)
grads = {"dw": dw,
"db": db}
return grads, cost
传播函数计算预测输出“A”、成本函数“cost”以及梯度“dw”和“db”。使用该函数,我们现在可以更新公式 1 中的“w”和“b”。这是下一步。
优化参数以最佳拟合训练数据
在这一步中,我们将更新这个模型的核心参数。“传播”功能将通过多次迭代运行。在每次迭代中,“w”和“b”将被更新。下面是一个完整的“优化”功能。我解释了代码片段中的每一步。请仔细阅读。
在这个函数中引入了一个新的术语学习率。那不是计算出来的值。对于不同的机器学习算法是不同的。尝试几种不同的学习速度,看看哪种效果最好。
def optimize(w, b, X, Y, num_iterations, learning_rate, print_cost = False):
costs = []
#propagate function will run for a number of iterations
for i in range(num_iterations):
grads, cost = propagate(w, b, X, Y)
dw = grads["dw"]
db = grads["db"]
#Updating w and b by deducting the dw
#and db times learning rate from the previous
#w and b
w = w - learning_rate * dw
b = b - learning_rate * db
#Record the cost function value for each 100 iterations
if i % 100 == 0:
costs.append(cost)
#The final updated parameters
params = {"w": w,
"b": b}
#The final updated gradients
grads = {"dw": dw,
"db": db}
return params, grads, costs
我们有优化参数的功能。这是预测输出的时间:
每行代码的解释都嵌入在代码片段之间。请仔细阅读,以便更好地理解它。
def predict(w, b, X):
m = X.shape[1]
#Initializing an aray of zeros which has a size of the input
#These zeros will be replaced by the predicted output
Y_prediction = np.zeros((1, m))
w = w.reshape(X.shape[0], 1)
#Calculating the predicted output using the Formula 1
#This will return the values from 0 to 1
A = sigmoid(np.dot(w.T, X) + b)
#Iterating through A and predict an 1 if the value of A
#is greater than 0.5 and zero otherwise
for i in range(A.shape[1]):
Y_prediction[:, i] = (A[:, i] > 0.5) * 1
return Y_prediction
最终型号
将所有功能放在一起,最终的模型将如下所示:
def model(X_train, Y_train, X_test, Y_test, num_iterations = 2000, learning_rate = 0.5):
#Initializing the w and b as zeros
w, b = initialize_with_zeros(X_train.shape[0]) parameters, grads, costs = optimize(w, b, X_train, Y_train, num_iterations, learning_rate, print_cost)
w = parameters["w"]
b = parameters["b"]
# Predicting the output for both test and training set
Y_prediction_test = predict(w, b, X_test)
Y_prediction_train = predict(w, b, X_train)
#Calculating the training and test set accuracy by comparing
#the predicted output and the original output
print("train accuracy: {} %".format(100 - np.mean(np.abs(Y_prediction_train - Y_train)) * 100))
print("test accuracy: {} %".format(100 - np.mean(np.abs(Y_prediction_test - Y_test)) * 100))
d = {"costs": costs,
"Y_prediction_test": Y_prediction_test,
"Y_prediction_train" : Y_prediction_train,
"w" : w,
"b" : b,
"learning_rate" : learning_rate,
"num_iterations": num_iterations}
return d
完整的逻辑回归模型准备好了!
使用模型
这是使用模型来看看它的效果如何的时候了。让我们将开始时准备的数据传递给模型:
d = model(train_x, train_y, test_x, test_y, num_iterations = 2000, learning_rate = 0.005)
输出:
train accuracy: 99.75 %
test accuracy: 99.5 %
精度不就是优秀吗!
从“model”函数中可以看出,我们的最终模型返回一个字典,其中包含成本、最终参数、预测输出、学习率和使用的迭代次数。让我们看看成本函数如何随着每个更新的“w”和“b”而变化:
plt.figure(figsize=(7,5))
plt.scatter(x = range(len(d['costs'])), y = d['costs'], color='black')
plt.title('Scatter Plot of Cost Functions', fontsize=18)
plt.ylabel('Costs', fontsize=12)
plt.show()

看,随着每一次迭代,成本函数都会下降。这意味着在每次迭代中,参数‘w’和‘b’都在不断完善。
结论
如果你能运行所有的代码并能理解其中的大部分,你就学会了逻辑回归是如何工作的!恭喜你!如果你还不能完全理解这个模型,我建议分解函数,单独运行每一行代码。那会让你有更好的想法。如果你有任何问题,欢迎在评论区提问。
欢迎在推特上关注我,喜欢我的脸书页面。
更多阅读
使用 Python 的 Scikit-Learn 的完整情感分析项目

比较 Python 中情感分析项目的两种不同矢量器和三种机器学习模型
情感分析是自然语言处理的重要组成部分之一。它不同于使用数字数据的机器学习,因为算法不能直接处理文本数据。它需要被转换成数字形式。因此,文本数据在输入机器学习模型之前会进行矢量化处理。矢量化有不同的方法。本文将使用两种类型的矢量器和三种机器学习模型来演示情感分析。
数据预处理
我正在为这个项目使用来自 Kaggle 的亚马逊婴儿产品数据集。如果你想继续,请随意从这个链接下载数据集。
原始数据集有三个特征:名称(产品名称)、评论(客户对产品的评论)和评级(客户对产品的评级,范围从 1 到 5)。评论栏将是输入栏,评级栏将用于了解评论的观点。以下是一些重要的数据预处理步骤:
- 该数据集大约有 183,500 行数据。有 1147 个空值。我只需要去掉那些空值。
- 由于数据集相当大,运行一些机器学习算法需要花费大量时间。因此,我在这个项目中使用了 30%的数据,仍然是 54,000 个数据。样本具有代表性。
- 如果评分为 1 和 2,将被视为差评或负面评价。而如果评论是 3、4、5,那么该评论将被认为是好的评论或积极的评论。因此,我在数据集中添加了一个名为“情感”的新列,用 1 表示正面评价,用 0 表示负面评价。
也许我在一个块中放了很多代码。如果数量很多,请将其分解成小块,以便更好地理解。
下面是代码块,它导入数据集,抽取 30%的代表性样本,并添加新列“情感”:
import pandas as pd
df = pd.read_csv('amazon_baby.csv')#getting rid of null values
df = df.dropna()#Taking a 30% representative sample
import numpy as np
np.random.seed(34)
df1 = df.sample(frac = 0.3)#Adding the sentiments column
df1['sentiments'] = df1.rating.apply(lambda x: 0 if x in [1, 2] else 1)
这是数据集现在的样子。这是前 5 行数据:

情感分析
在开始情感分析之前,有必要定义输入特征和标签。这里只有一个特征,那就是‘回顾’。标签将是“情绪”。这个项目的目标是训练一个模型,如果一个评论是积极的或消极的,可以输出。
X = df1['review']
y = df1['sentiments']
首先,我将使用计数矢量化工具作为矢量化方法。本文将关注如何应用矢量器。所以,我不想谈细节。但是,如果您对矢量器完全陌生,请随意查看本文以了解更多关于计数矢量器的信息。
计数矢量器
我将使用一个计数矢量化工具对 review 列中的文本数据进行矢量化处理(本项目的训练特性),然后使用 scikit-learn 模型中的三个不同的分类模型。之后,评估这个数据集上的模型,找出准确性、混淆矩阵、真阳性率和真阴性率。以下是步骤。
- 第一步是将数据集分成训练集和测试集。
- 向量化检查列之外的输入要素(训练和测试数据)
- 从 scikit 学习库中导入模型。
- 找到准确度分数
- 找出真正的正利率和负利率。
现在,我将对三个不同的分类器重复相同的过程。这里使用的分类器是逻辑回归、支持向量机和 K 最近邻分类器。我将在本文结尾总结结果。
逻辑回归
这是逻辑回归的代码块。我在代码之间使用了注释。
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y,
test_size = 0.5, random_state=24)from sklearn.feature_extraction.text import CountVectorizer
cv = CountVectorizer()#Vectorizing the text data
ctmTr = cv.fit_transform(X_train)
X_test_dtm = cv.transform(X_test)from sklearn.linear_model import LogisticRegression#Training the model
lr = LogisticRegression()
lr.fit(ctmTr, y_train)#Accuracy score
lr_score = lr.score(X_test_dtm, y_test)
print("Results for Logistic Regression with CountVectorizer")
print(lr_score)#Predicting the labels for test data
y_pred_lr = lr.predict(X_test_dtm)
from sklearn.metrics import confusion_matrix#Confusion matrix
cm_lr = confusion_matrix(y_test, y_pred_lr)tn, fp, fn, tp = confusion_matrix(y_test, y_pred_lr).ravel()
print(tn, fp, fn, tp)#True positive and true negative rates
tpr_lr = round(tp/(tp + fn), 4)
tnr_lr = round(tn/(tn+fp), 4)print(tpr_lr, tnr_lr)
如您所见,我有打印准确性、真阳性、假阳性、真阴性、假阴性、真阴性率和假阴性率的打印语句。
支持向量机
我将重复与之前完全相同的过程来使用支持向量机。
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y,
test_size = 0.5, random_state=123)#Vectorizing the text data
cv = CountVectorizer()
ctmTr = cv.fit_transform(X_train)
X_test_dtm = cv.transform(X_test)from sklearn import svm#Training the model
svcl = svm.SVC()
svcl.fit(ctmTr, y_train)svcl_score = svcl.score(X_test_dtm, y_test)
print("Results for Support Vector Machine with CountVectorizer")
print(svcl_score)y_pred_sv = svcl.predict(X_test_dtm)#Confusion matrix
cm_sv = confusion_matrix(y_test, y_pred_sv)tn, fp, fn, tp = confusion_matrix(y_test, y_pred_sv).ravel()
print(tn, fp, fn, tp)tpr_sv = round(tp/(tp + fn), 4)
tnr_sv = round(tn/(tn+fp), 4)print(tpr_sv, tnr_sv)
我应该提醒你,支持向量机比逻辑回归需要更多的时间。
K 最近邻
我将运行一个 KNN 分类器,得到和以前一样的评估矩阵。代码几乎是一样的。
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y,
test_size = 0.5, random_state=143)from sklearn.feature_extraction.text import CountVectorizer
cv = CountVectorizer()
ctmTr = cv.fit_transform(X_train)
X_test_dtm = cv.transform(X_test)from sklearn.neighbors import KNeighborsClassifier
knn = KNeighborsClassifier(n_neighbors=5)
knn.fit(ctmTr, y_train)knn_score = knn.score(X_test_dtm, y_test)
print("Results for KNN Classifier with CountVectorizer")
print(knn_score)y_pred_knn = knn.predict(X_test_dtm)#Confusion matrix
cm_knn = confusion_matrix(y_test, y_pred_knn)tn, fp, fn, tp = confusion_matrix(y_test, y_pred_knn).ravel()
print(tn, fp, fn, tp)tpr_knn = round(tp/(tp + fn), 4)
tnr_knn = round(tn/(tn+fp), 4)print(tpr_knn, tnr_knn)
KNN 分类器比支持向量机分类器耗时少。
至此,计数向量机方法的三个分类器就完成了。
TFIDF 矢量器
接下来,我将使用 TF-IDF 矢量器。众所周知,这个矢量器是一个更受欢迎的矢量器,因为它使用了词的频率这一术语。请随意查看本文以了解关于 TF-IDF 矢量器的详细信息。
我将遵循与计数矢量器完全相同的过程。只有矢量器是不同的。但这不是问题。超级酷的 sklearn 库会照常打理计算部分。
逻辑回归
使用 TF-IDF 矢量器进行逻辑回归的完整代码块:
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y,
test_size = 0.5, random_state=45)from sklearn.feature_extraction.text import TfidfVectorizer#tfidf vectorizervectorizer = TfidfVectorizer()X_train_vec = vectorizer.fit_transform(X_train)X_test_vec = vectorizer.transform(X_test)from sklearn.linear_model import LogisticRegressionlr = LogisticRegression()lr.fit(X_train_vec, y_train)lr_score = lr.score(X_test_vec, y_test)
print("Results for Logistic Regression with tfidf")
print(lr_score)y_pred_lr = lr.predict(X_test_vec)#Confusion matrix
from sklearn.metrics import confusion_matrix
cm_knn = confusion_matrix(y_test, y_pred_lr)tn, fp, fn, tp = confusion_matrix(y_test, y_pred_lr).ravel()
print(tn, fp, fn, tp)tpr_knn = round(tp/(tp + fn), 4)
tnr_knn = round(tn/(tn+fp), 4)print(tpr_knn, tnr_knn)
如你所见,除了矢量器部分,你可以重用之前的代码。
支持向量机
除了矢量器之外,这也将是与前面的支持向量机相同的过程。
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y,
test_size = 0.5, random_state=55)vectorizer = TfidfVectorizer()
X_train_vec = vectorizer.fit_transform(X_train)
X_test_vec = vectorizer.transform(X_test)from sklearn import svm
#params = {'kernel':('linear', 'rbf'), 'C':[1, 10, 100]}
svcl = svm.SVC(kernel = 'rbf')
#clf_sv = GridSearchCV(svcl, params)
svcl.fit(X_train_vec, y_train)
svcl_score = svcl.score(X_test_vec, y_test)
print("Results for Support Vector Machine with tfidf")
print(svcl_score)y_pred_sv = svcl.predict(X_test_vec)#Confusion matrix
from sklearn.metrics import confusion_matrix
cm_sv = confusion_matrix(y_test, y_pred_sv)tn, fp, fn, tp = confusion_matrix(y_test, y_pred_sv).ravel()
print(tn, fp, fn, tp)tpr_sv = round(tp/(tp + fn), 4)
tnr_sv = round(tn/(tn+fp), 4)print(tpr_sv, tnr_sv)
和以前一样,这将比逻辑回归花费更多的时间。所以,这可能需要一些耐心。
K 个最近邻居
这是最后一个。
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y,
test_size = 0.5, random_state=65)vectorizer = TfidfVectorizer()
X_train_vec = vectorizer.fit_transform(X_train)
X_test_vec = vectorizer.transform(X_test)from sklearn.neighbors import KNeighborsClassifier
knn = KNeighborsClassifier(n_neighbors=5)
knn.fit(X_train_vec, y_train)knn_score = knn.score(X_test_vec, y_test)
print("Results for KNN Classifier with tfidf")
print(knn_score)y_pred_knn = knn.predict(X_test_vec)#Confusion matrix
cm_knn = confusion_matrix(y_test, y_pred_knn)tn, fp, fn, tp = confusion_matrix(y_test, y_pred_knn).ravel()
print(tn, fp, fn, tp)tpr_knn = round(tp/(tp + fn), 4)
tnr_knn = round(tn/(tn+fp), 4)print(tpr_knn, tnr_knn)
结果
这里我总结了上面所有六个代码块的结果。

以下是一些重要的发现:
- 总的来说,TF-IDF 矢量器给出的结果比 count 矢量器部分稍好。对于矢量器。
- 考虑到整体准确性、真阳性率和真阴性率,逻辑回归是本项目使用的所有三个分类器中最好的。
- KNN 分类器似乎不适合这个项目。尽管真正的正利率看起来非常好,但真正的负利率看起来非常差。
结论
我已经做了这个项目作为我的课程的一部分,并决定分享它。您可能会奇怪,为什么每次我训练一个模型时,我都要将数据集分成训练和测试两部分。这是这门课的要求。这个想法是某个训练测试分割可能偏向某个分类器。因此,我必须在每个模型之前分割数据集。
更多阅读
关于 Keras 和 Tensorflow 中情感分析的一步一步的完整教程

马克·戴恩斯在 Unsplash 上的照片
用于数据准备、深度学习模型开发和训练网络的完整工作代码
情感分析是非常常见的自然语言处理任务之一。企业使用情感分析来有效地理解社交媒体评论、产品评论和其他文本数据。Tensorflow 和 Keras 是解决这个问题的绝佳工具。
Tensorflow 可以说是最受欢迎的深度学习库。它在幕后使用一个神经网络。它如此受欢迎的原因是,它真的很容易使用和工作相当快。甚至不知道神经网络如何工作,你就可以运行神经网络。尽管这很有帮助,如果你知道一些关于神经网络的基础知识。
Tensorflow 也有非常好的文档。所以,也很好学。如今,我们中的许多人都希望从事机器学习和深度学习,但害怕这可能需要非常高水平的编程解决问题技能。
是的,肯定需要有编程知识。但是因为有了 Tensorflow 这种很酷的库,如果你有中级的编程技能,就有可能致力于机器学习和深度学习问题。大多数专业数据科学家不会从头开始开发他们的算法。在行业层面上,使用这些库非常普遍。
本文结合一个项目重点介绍 TensorFlow 中一个简单的情感分析项目。请随意从以下链接下载数据集:
https://www.kaggle.com/sameersmahajan/reviews-of-amazon-baby-products
这个数据集有三列。产品名称、评论和评级。review 列是包含客户评论的文本列,rating 列具有从 1 到 5 的数字评级。1 表示最差,5 表示最好。
从这篇文章中可以期待什么?
本文将提供:
- 如何使用 TensorFlow 和 Keras 进行情感分析的分步说明
- 完整的工作代码将在一些现实生活中的项目工作。
- 参数说明
- 一些资源来更好地理解参数
数据准备
在自然语言处理项目中,数据预处理是工作的一半。因为算法不理解文本。所以,我们需要将文本转换成算法可以理解的数字。
在处理文本之前,定义积极情绪和消极情绪也很重要。在这个数据集中,我们可以使用评级列来了解情绪。我们有从 1 到 5 的等级。因此,当评级为 1 或 2 时,该评论可被视为负面评论,而当评级为 3、4 或 5 时,则该评论可被视为正面评论。我们可以为负面情绪设置 0,为正面情绪设置 1。
df['sentiments'] = df.rating.apply(lambda x: 0 if x in [1, 2] else 1)
使用数据集上方的代码行添加“情绪”列后,如下所示:

下一步是像我们之前提到的那样对文本进行标记。为此将使用记号赋予器函数。默认情况下,它会删除所有标点符号,并将文本设置为以空格分隔的有序形式。每个单词通过 tokenizer 函数变成一个整数。让我们设置记号赋予器函数:
from tensorflow.keras.preprocessing.text import Tokenizerfrom tensorflow.keras.preprocessing.sequence import pad_sequencestokenizer = Tokenizer(oov_token="<OOV>")
这里,oov_token 的值被设置为“oov”。这意味着任何未知单词都将被 oov_token 替换。这是一个更好的选择,而不是抛出未知的单词。我们稍后将讨论“填充序列”。
分割数据集
我们将保留 80%的数据用于培训,20%用于测试目的。
split = round(len(df)*0.8)train_reviews = df['review'][:split]train_label = df['sentiments'][:split]test_reviews = df['review'][split:]test_label = df['sentiments'][split:]
我会格外小心地做一步。也就是把每个评论转换成一个字符串。因为万一有不是字符串格式的数据,我们稍后会得到一个错误。所以,我想多走一步。
import numpy as nptraining_sentences = []training_labels = []testing_sentences = []testing_labels = []for row in train_reviews:
training_sentences.append(str(row))for row in train_label:
training_labels.append(row)for row in test_reviews:
testing_sentences.append(str(row))for row in test_label:
testing_labels.append(row)
训练和测试集已经准备好了。一些重要的术语需要修正。我将在这段代码之后解释它们:
vocab_size = 40000embedding_dim = 16max_length = 120trunc_type = 'post'oov_tok = '<OOV>'padding_type = 'post'
在这里,vocab_size 40,000 意味着我们将采用 40,000 个唯一的单词来训练网络。嵌入维数 16 意味着每个单词将由 16 维向量表示。Max_length 120 表示每个评论的长度。我们将保留每个评论的 120 个单词。如果最初的评论超过 120 个单词,它将被截断。术语 trunc_type 设置为‘post’。因此,当评论超过 120 个单词时,评论将在最后被截断。另一方面,如果评论少于 120 个单词,它将被填充为 120 个单词。最后,padding_type 'post '意味着填充将应用于结尾,而不是开头。
现在,让我们从词汇化开始:
tokenizer = Tokenizer(num_words=vocab_size, oov_token=oov_tok)tokenizer.fit_on_texts(training_sentences)word_index = tokenizer.word_index
以下是 word_index 值的一部分:
{'<OOV>': 1,
'the': 2,
'it': 3,
'i': 4,
'and': 5,
'to': 6,
'a': 7,
'is': 8,
'this': 9,
'for': 10,
看,每个单词都有一个整数值。现在,复习句子可以表示为一系列单词。下一个代码块将句子转换成单词序列,然后根据需要进行填充:
sequences = tokenizer.texts_to_sequences(training_sentences)padded = pad_sequences(sequences, maxlen=max_length, truncating=trunc_type)testing_sentences = tokenizer.texts_to_sequences(testing_sentences)testing_padded = pad_sequences(testing_sentences, maxlen=max_length)
数据处理在这里完成。现在,模型开发可以非常容易地完成。
模型开发
对于这个项目,' keras。将使用顺序模式。请查看附加链接,了解有关“keras”的详细信息。连续的。我将解释如何在这类项目中使用它。模型如下:
model = tf.keras.Sequential([
tf.keras.layers.Embedding(vocab_size, embedding_dim, input_length=max_length), tf.keras.layers.GlobalAveragePooling1D(), tf.keras.layers.Dense(6, activation='relu'), tf.keras.layers.Dense(1, activation='sigmoid')])
第一层是嵌入层,其中所有的参数都已经在前面定义和解释过了。第二层是' GlobalAveragePooling1D()'展平矢量。最初,数据是三维的(批量大小 x 步骤 x 特征)。GlobalAveragePooling1D 制作(batch_size x features)。
第三层是密集层,其中使用了“relu”激活功能。您可以尝试“tanh”或您选择的任何其他激活功能。这一层称为隐藏层。我只用了一个隐藏层。随意尝试多个隐藏层。更复杂的问题可能需要更多的隐藏层。此外,我在隐藏层中使用了 6 个神经元。你可能想知道如何选择神经元的数量。
有很多关于这方面的文章。这里有一篇简短的文章,简要地提供了一些见解。
最后一层使用 sigmoid 激活函数或逻辑函数。
在隐藏层中,您可以使用“relu”或“tanh”激活函数,但分类问题中的最后一层总是 sigmoid 或 softmax 激活函数。
现在,编译模型:
model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy'])
我选择二元交叉熵作为损失函数,因为这是一个概率损失。这里还描述了其他的损失函数。我用优化器作为“亚当”。还有其他几个优化器函数,如 RMSProp、adadelta、adagrad、adamax 等等。以下是模型总结:
model.summary()
输出:
Model: "sequential"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
embedding (Embedding) (None, 120, 16) 640000
_________________________________________________________________
global_average_pooling1d (Gl (None, 16) 0
_________________________________________________________________
dense (Dense) (None, 6) 102
_________________________________________________________________
dense_1 (Dense) (None, 1) 7
=================================================================
Total params: 640,109
Trainable params: 640,109
Non-trainable params: 0
_________________________________________________________________
如果您查看模型摘要,我们之前讨论的许多术语现在都有意义了。
让我解释一下。嵌入层显示三维。我们没有提到任何批量大小。这里,我们使用每个时期的整个数据集进行训练。每个评论中的 120 个单词,每个单词都表示为一个 16(我们之前选择的是这个 16)的元素向量。这意味着我们有 16 个特征。在全局平均池化之后,它变平了,我们只有批量大小(没有)和特性的数量。
训练模型
在训练模型之前,我们只需要将标签转换为数组。如果你注意到,它们是列表形式的:
training_labels_final = np.array(training_labels)testing_labels_final = np.array(testing_labels)
让我们开始训练“模型”。我将训练 20 个纪元的模型。
num_epochs = 20history = model.fit(padded, training_labels_final, epochs=num_epochs, validation_data=(testing_padded, testing_labels_final))
输出:
Epoch 1/20
4589/4589 [==============================] - 40s 8ms/step - loss: 0.2849 - accuracy: 0.8831 - val_loss: 0.2209 - val_accuracy: 0.9094 Epoch 2/20
4589/4589 [==============================] - 35s 8ms/step - loss: 0.2098 - accuracy: 0.9127 - val_loss: 0.1990 - val_accuracy: 0.9186 Epoch 3/20
4589/4589 [==============================] - 36s 8ms/step - loss: 0.1931 - accuracy: 0.9195 - val_loss: 0.2000 - val_accuracy: 0.9177 Epoch 4/20
4589/4589 [==============================] - 35s 8ms/step - loss: 0.1837 - accuracy: 0.9234 - val_loss: 0.1993 - val_accuracy: 0.9168 Epoch 5/20
4589/4589 [==============================] - 35s 8ms/step - loss: 0.1766 - accuracy: 0.9264 - val_loss: 0.2013 - val_accuracy: 0.9163 Epoch 6/20
4589/4589 [==============================] - 35s 8ms/step - loss: 0.1708 - accuracy: 0.9287 - val_loss: 0.2044 - val_accuracy: 0.9174 Epoch 7/20
4589/4589 [==============================] - 36s 8ms/step - loss: 0.1656 - accuracy: 0.9309 - val_loss: 0.2164 - val_accuracy: 0.9166 Epoch 8/20
4589/4589 [==============================] - 35s 8ms/step - loss: 0.1606 - accuracy: 0.9332 - val_loss: 0.2122 - val_accuracy: 0.9155 Epoch 9/20 4589/4589 [==============================] - 35s 8ms/step - loss: 0.1560 - accuracy: 0.9354 - val_loss: 0.2203 - val_accuracy: 0.9170
Epoch 10/20
4589/4589 [==============================] - 36s 8ms/step - loss: 0.1515 - accuracy: 0.9373 - val_loss: 0.2222 - val_accuracy: 0.9161 Epoch 11/20
4589/4589 [==============================] - 35s 8ms/step - loss: 0.1468 - accuracy: 0.9396 - val_loss: 0.2225 - val_accuracy: 0.9143 Epoch 12/20
4589/4589 [==============================] - 37s 8ms/step - loss: 0.1427 - accuracy: 0.9413 - val_loss: 0.2330 - val_accuracy: 0.9120 Epoch 13/20
4589/4589 [==============================] - 36s 8ms/step - loss: 0.1386 - accuracy: 0.9432 - val_loss: 0.2369 - val_accuracy: 0.9131 Epoch 14/20
4589/4589 [==============================] - 34s 7ms/step - loss: 0.1344 - accuracy: 0.9455 - val_loss: 0.2418 - val_accuracy: 0.9102 Epoch 15/20
4589/4589 [==============================] - 36s 8ms/step - loss: 0.1307 - accuracy: 0.9470 - val_loss: 0.2487 - val_accuracy: 0.9073 Epoch 16/20
4589/4589 [==============================] - 37s 8ms/step - loss: 0.1272 - accuracy: 0.9490 - val_loss: 0.2574 - val_accuracy: 0.9058 Epoch 17/20
4589/4589 [==============================] - 36s 8ms/step - loss: 0.1237 - accuracy: 0.9502 - val_loss: 0.2663 - val_accuracy: 0.9009 Epoch 18/20
4589/4589 [==============================] - 36s 8ms/step - loss: 0.1202 - accuracy: 0.9519 - val_loss: 0.2734 - val_accuracy: 0.9028 Epoch 19/20
4589/4589 [==============================] - 36s 8ms/step - loss: 0.1173 - accuracy: 0.9536 - val_loss: 0.2810 - val_accuracy: 0.8978 Epoch 20/20
4589/4589 [==============================] - 36s 8ms/step - loss: 0.1144 - accuracy: 0.9550 - val_loss: 0.2959 - val_accuracy: 0.9058
从上一个历元的结果可以看出,训练准确率为 95.5%,验证准确率为 90.58%。看起来有点太合身了。您可能想尝试更多的历元,看看精度是否会进一步提高。
我们可以绘制训练和验证的精确度,以及训练和验证的损失:
%matplotlib inlineimport matplotlib.pyplot as pltimport matplotlib.image as mpimgacc = history.history['accuracy']val_acc = history.history['val_accuracy']loss = history.history['loss']val_loss = history.history['val_loss']epochs=range(len(acc))plt.plot(epochs, acc, 'r', 'Training Accuracy')plt.plot(epochs, val_acc, 'b', 'Validation Accuracy')plt.title('Training and validation accuracy')plt.figure()plt.plot(epochs, loss, 'r', 'Training Loss')plt.plot(epochs, val_loss, 'b', 'Validation Loss')plt.title('Training and validation loss')plt.figure()

模型做好了。主要部分完成了。现在,如果您愿意,可以使用不同的其他绩效评估指标。
结论
这几乎是一个基本但有用的模型。你可以使用这个具有不同隐藏层和神经元的相同模型来解决自然语言处理中的相当多的问题。我还在文章中提供了更多的资源,可以用来改进或更改模型,并尝试不同的模型。随意用不同数量的隐藏层、神经元、激活函数、度量或优化器来改变模型,并进行尝试。那会给你很多学习经验。
请随时在 Twitter 上关注我。
更多阅读
[## 文本数据的探索性数据分析,包括可视化和情感分析
towardsdatascience.com](/exploratory-data-analysis-of-text-data-including-visualization-and-sentiment-analysis-e46dda3dd260)
从 Excel 迁移到 Python 的完整而简单的指南
用熊猫和 Numpy 代替 Excel。

作者图片(Canva 上制作)
作为一个使用 Excel 多年的人,我知道在像 Python 这样的全新环境中学习 Excel/VBA 中已经可以做的事情听起来并不令人兴奋。然而,Python 环境提供的所有好处都使它物有所值。出于这个原因,我想出了一个有用而简单的指南,我希望当我从 Excel 转换到 Python 时就有了。
在本文中,我们将使用 Python 的 Pandas 和 Numpy 库来替换您过去可能使用的许多 Excel 函数。
**Table of Contents** 1\. [The Dataset](#4dc0)
2\. [Sum, Average, Max, Min, Count](#00e3)
- [Columns (e.g. sum a column)](#0d49)
- [Rows (e.g. sum a row)](#c754)
3\. [IF](#312b)
- [Replace IF with np.where](#75c6)
- [Replace nested IF with np.select](#f528)
4\. [SumIf, CountIf, AverageIf](#0a91)
- [One condition (select a column with square brackets [ ] )](#7c86)
- [Two or more conditions (select columns and use & or |)](#69f4)
5\. [Basic Data Cleaning](#0cbd)
- [Change the case of text with .str.lower, .str.upper or .str.title](#b662)
- [Extract text in a column with .str.extract](#64d6)
- [Identify whether a cell is empty with the .isnull method](#e0a3)
6\. [Vlookup](#b306)
- [Find an element with .loc[ ]](#071c)
- [Merge two tables with pd.merge or pd.concat](#e97b)
7\. [Pivot Table](#09cd)
- [Use the .pivot_table method](#ac65)
8\. [Replace Excel Graphs with Python’s Matplotlib or Seaborn](#d450)
数据集
在本指南中,我们将使用. csv 格式的 Excel 文件,这是处理数据集时通常使用的格式。该数据集由学生在各种科目中获得的分数组成,将帮助我们轻松地从 Excel 电子表格切换到 Python 的数据框架。你可以在 Kaggle 或者 my Github 上下载这些数据(查看数据集文件夹)。
从本指南开始,让我们导入 Pandas 和 Numpy 库。
import pandas as pd
import numpy as np
注意:如果你没有在 Python 中安装那些库,你可以通过在你的终端或者命令提示符上写 pip install pandas和pip install numpy 来轻松安装。
有了这个,我们就可以先看看数据了。为此,我们使用pd.read_csv()。确保 CSV 和 Python 脚本位于相同的位置(相同的路径)。
df_excel = pd.read_csv('StudentsPerformance.csv')
df_excel
一旦我们读取这个 CSV 文件,我们给它一个名字。在这种情况下,我将其命名为df_excel。df 代表 dataframe,它是读取 CSV 文件后给出的典型名称。运行下面的代码后,我们获得了下面的输出。

作者图片
这看起来类似于 Excel 电子表格,但在这种格式下,更容易争论数据。现在我将向你展示如何用 Python 来做一些你可能在 Excel 中用过的常用函数。
总和、平均值、最大值、最小值、计数
Excel 的常用函数可以很容易地替换为 Pandas 方法。让我们来看看。
列(例如,对一列求和)
如果我们想得到上面列出的大多数函数,我们使用.describe()方法。
df_excel.describe()

作者图片
如您所见,这样我们就可以得到所有数字列的计数、平均值、最大值和最小值。
但是,如果我们想要选择一个特定的行,我们首先用方括号[ ] 选择,然后使用我们需要的方法(.sum()、.mean()等)。).比如,我们来计算数学成绩的平均值。
In [1]: df_excel['math score'].mean()
Out [1]: 66.089
这些是你可以使用的一些其他方法。
df_excel['math score'].mean()
df_excel['math score'].max()
df_excel['math score'].min()
df_excel['math score'].count()
行(例如,对一行求和)
现在,假设我们要计算 3 个分数(数学、阅读和写作)的平均值。这些值在不同的列中,所以我们有两个选项。我们可以对每列求和
df_excel['average'] = (df_excel['math score'] + df_excel['reading score'] + df_excel['writing score'])/3
或者使用我们之前使用的方法对一列中的值求和,但是在这种情况下,我们添加了axis=1
df_excel['average'] = df_excel.mean(axis=1)
在计算平均值时,我们还添加了一个新列[‘average’]。我们将在下一节使用这个新专栏(IF)。
请记住,在这个示例中,我们可以使用第二个选项,因为只有数字值在所需的列中(数学、阅读和写作分数)。如果有一个额外的列我们不想在总和中考虑,我们应该只使用第一个选项。
如果我们想计算一列中特定数据的数量,我们可以使用.value_counts()方法
In [2]: df_excel['gender'].value_counts()
Out [2]:
female 518
male 482
如果
我们可以通过使用 Numpy 轻松替换 Excel 的 IF 函数。
将 IF 替换为 np.where
假设我们想知道一个学生是否通过了考试,并用这些信息创建一个新的列。我们可以通过下面的代码轻松做到这一点。
df_excel['pass/fail'] = np.where(df_excel['average'] > 70, 'Pass', 'Fail')
如你所见,np.where()需要 3 个参数——条件,条件为真时的值,条件为假时的值。
用 np.select 替换嵌套 IF
假设我们希望根据获得的分数给出从 A 到 F 的分数。在这种情况下,我们有 2 个以上的值,所以我们使用np.select()
np.select() needs to arguments——一个条件列表和一个值列表。Python 中的列表由方括号[ ]表示
conditions = [
(df_excel['average']>=90),
(df_excel['average']>=80) & (df_excel['average']<90),
(df_excel['average']>=70) & (df_excel['average']<80),
(df_excel['average']>=60) & (df_excel['average']<70),
(df_excel['average']>=50) & (df_excel['average']<60),
(df_excel['average']<50),
]values = ['A', 'B', 'C', 'D', 'E', 'F']
请记住,每个条件都应该在括号内。现在我们使用.select()方法,并将其分配给一个新的[‘grades’]列。
df_excel['grades'] = np.select(conditions, values)
现在输出的前 5 行应该是这样的。

作者图片
我们挑选了这 3 列,用双方括号df_excel[[‘平均’,‘通过/失败’,‘等级’]] ,前 5 行可以用.head() 方法显示。
SumIf,CountIf,AverageIf
在 Python 中,为了基于条件求和、计数或计算平均值,我们首先过滤掉值,然后进行计算。
一个条件(用方括号[ ]选择一列)
假设我们只想对女性的分数求和。为此,首先,我们编写条件df_excel[‘gender’] == ‘female’ ,然后。我们使用方括号[ ]来选择df_excel框架中的条件
df_female = df_excel[df_excel['gender'] == 'female']
我们只选择了女性,并将其放入一个名为df_female的数据框中。现在,我们可以执行我们在“总和、平均值、最大值、最小值、计数”一节中看到的任何计算。
两个或更多条件(选择列并使用&或|)
如果我们有两个或更多的条件,代码看起来将与上面的类似,但有一些变化。假设我们想要计算 B 组(‘race/ethnicity’)中女性的分数
df_sumifs = df_excel[(df_excel['gender'] == 'female') & (df_excel['race/ethnicity'] == 'group B')]
因为我们可以使用两个条件&分别代表和/或。请记住,每个条件都应该在括号内。
现在让我们把分数加起来。
df_sumifs = df_sumifs.assign(sumifs = df_sumifs['math score'] + df_sumifs['reading score'] + df_sumifs['writing score'])
在这种情况下,我使用了.assign() 方法向您展示了在进行计算时创建新列的另一种方法。
基本数据清理
我们将检查一些用于数据清理的方法。如果您想了解所有用于清理数据的方法,请查看我写的关于如何在 Python 中清理和准备数据的完整指南。
我们将继续使用我们之前定义的df_excel框架。
更改带有. str.lower、. str.upper 或. str.title 的文本的大小写
要访问包含在列中的字符串,我们使用.str,然后我们可以用下面的代码改变文本的大小写
df_excel['gender'].str.title()
df_excel['gender'].str.upper()
df_excel['gender'].str.title()
为了保存这些值,我们可以覆盖一个列,如下面的代码所示。
df_excel['gender'] = df_excel['gender'].str.title()
但是在这种情况下,我们将保持值不变。
用. str.extract 提取列中的文本
我们可以很容易地用.str.extract从一个列中提取文本。除此之外,如果我们想提取文本的特定模式,我们可以使用正则表达式。
假设我们只想提取列‘race/ethnicity’中的大写单词(例如,“B 组”中的“B”)。为此,我们编写以下代码。
df_excel['race/ethnicity'].str.extract(r'([A-Z])')
在这种情况下,我们使用正则表达式r'([A-Z])’ ,其中[A-Z]表示大写的单词,而括号() 是选择所需模式所必需的。正则表达式可能看起来吓人,但它们比你想象的要简单。在下面的链接中,你会发现我为轻松学习正则表达式而制作的简单指南。
属性标识单元格是否为空。isnull 方法
为了替换 Excel 的 COUNTA,在 Python 中我们可以使用.isnull()来查找空值。
df_excel[df_excel['gender'].isnull()]
在这种情况下,没有空值,因此结果将是一个空数据帧。
纵向查找函数
为了像 Vlookup 在 Excel 中那样查找元素,我们将根据我们想要解决的问题使用.loc[]、.merge()或.concat()。
仅对于这些示例,我们将使用 2 个 Excel 电子表格。第一个是我们到目前为止一直在处理的“学生表现”,而第二个是我用随机的id和language score 值创建的名为“LanguageScore”的 CSV 文件(你可以在我的 Github 上找到这个文件)。让我们像以前一样用pd.read_csv() 读取两个文件。
excel_1 = 'StudentsPerformance.csv'
excel_2 = 'LanguageScore.csv'df_excel_1 = pd.read_csv(excel_1)
df_excel_2 = pd.read_csv(excel_2)
现在我们将在df_excel_1 上创建一个id列,这样它就有了一个与df_excel_2相同的列。为此,我们运行以下代码。
df_excel_1 = df_excel_1.reset_index()
df_excel_1 = df_excel_1.rename(columns={'index':'id'})
如您所见,我基于索引创建了一个id列。reset_index() 通过将索引添加为列来删除索引。您可以删除索引做.reset_index(drop=True) ,但是在这种情况下,我将使用.rename()方法重命名这个新的index列。这样,我们就有了一个新的id列。
用查找一个元素。位置[ ]
使用 Pandas 查找元素就像在[] 中编写.loc[] .一样简单,我们必须包含行和列标签。Ler 找到与第 100 行相关的信息。
In [3]: df_excel_1.loc[100, ]
Out [3]:id 100
gender male
race/ethnicity group B
parental level of education some college
lunch standard
test preparation course none
math score 79
reading score 67
writing score 67
我们也可以设定条件。例如,我们想得到学生的数学成绩,其中id等于 100。
In [3]: df_excel_1.loc[df_excel_1['id']==100, 'math score']
Out [3]:
100 79
Name: math score, dtype: int64
这意味着得到 100 分的学生在数学上得到 20 分。
用 pd.merge 或 pd.concat 合并两个表
假设您想将所有参加考试的学生的语言成绩相加。在这种情况下,我们使用pd.merge()。这要求两个表有一个公共列。在这种情况下,该列是id列。
df_excel_3 = pd.merge(df_excel_1, df_excel_2, on='id', how='left')
df_excel_3['language score'].fillna('0', inplace=True)
上面你可以看到需要另一个参数(how)。这表明合并是如何执行的。左右会有df_excel_1和df_excel_2作为最终结果的参考,而inner 只会给出id栏内的常用数据。
您也可以使用pd.concat()达到同样的目的,但是您必须在连接帧之前将id设置为索引,并将帧作为列表包含在内(在括号[]内)
df_excel_3 = pd.concat(
[df_excel_1.set_index('id'), df_excel_2.set_index('id')], axis=1
)
df_excel_3['language score'].fillna('0', inplace=True)
df_excel_3
所有不匹配的值都会收到一个 NaN,但是我们可以通过使用.fillna()方法用任何值替换它。
数据透视表
使用。数据透视表方法
Pandas 的 pivot_table 方法与 Excel 中的方法类似。
假设我们想要获得race/ethnicity 列中所有组的数学和写作分数。
df_excel = pd.read_csv('StudentsPerformance.csv')df_pivot = df_excel.pivot_table(index='race/ethnicity', values=['math score', 'writing score'], aggfunc='mean')
df_pivot
index参数将对‘race/ethnicity’ 列进行分组,而values参数将显示数值。最后,由aggfunc进行计算。在这种情况下,我们选择平均值。结果,我们得到下面的数据透视表。

作者图片
用 Python 的 Matplotlib 或 Seaborn 替换 Excel 图表
Python 包含不同的库,使得可视化效果和 Excel 提供的一样好。
让我们根据上面创建的df_pivot 的结果制作一个简单的柱状图。首先,我们导入matplotlib ,然后我们使用plt.bar()
import matplotlib.pyplot as pltdf_plot = df_pivot.reset_index()
plt.bar(df_plot['race/ethnicity'], df_plot['math score'])
plt.show()
plt.bar() 的第一个参数是标签,第二个是数值。然后我们用plt.show()显示结果

作者图片
在 Python plot 中可以做很多事情,比如给图形添加标签、标题、定制图形的大小和颜色,但这是一个全新的主题。我已经用 Python 制作了一个漂亮的可视化指南,包括线图、条形图、饼状图、直方图、箱线图等。请点击下面的链接查看。
就是这样!现在您已经准备好利用 Python 和 Pandas 提供的所有好处了!本文使用的所有代码在我的 Github 上都有。
https://frankandrade.ck.page/bd063ff2d3】与 3k+人一起加入我的电子邮件列表,获取我在所有教程中使用的 Python for Data Science 备忘单。
下面是我看的学习 Python 的书单。
**https://betterprogramming.pub/4-python-books-i-read-as-a-self-taught-programmer-5a5453840cdb
一旦你掌握了熊猫,这里有一些你可以开始的项目。
https://medium.datadriveninvestor.com/i-used-to-pay-180-yr-for-a-profitable-betting-tool-this-year-i-built-one-in-python-dda1a9b9581f https://medium.datadriveninvestor.com/make-money-with-python-the-sports-arbitrage-project-3b09d81a0098
我还做了一个指南,帮助你建立你的第一个机器学习模型。
**
细胞神经网络维度的可理解解释
实践教程
解释卷积核/滤波器与其输入和输出数组之间的关系

图 1:卷积神经网络的典型架构(自己的创造,灵感来自[1])
如果已经有很多文章涉及这个主题,为什么还要写一篇关于卷积神经网络(CNN)的解释呢?-因为在许多解释中,关于计算卷积层输出深度的关键信息缺失。这种信息的缺乏是一个巨大的障碍,特别是对于初学者来说。
当我了解 CNN 时,我开发了一个分步程序,帮助可视化卷积层,并可靠地计算它们的输出形状。本文介绍了这种方法。在这里,我假设 CNN 对你来说并不完全陌生,而是你正在学习它们。因此,我将额外回答几个在学习 CNN 时经常出现的问题。
准备:问题的定义。
图 1 显示了一个典型的 CNN 架构的可视化方案。关于这一点,这里有一个简短的任务:请精确定义图 1 中的哪些图形元素属于卷积层,哪些不属于卷积层。
正如您可能已经注意到的,CNN 的架构不是通过卷积层来可视化的,而是通过卷积层生成的输入和输出数组来可视化的。为了澄清这一点,图 1 中 CNN 的最后一个卷积层及其输入和输出数组在图 2 中用彩色显示。在这里,所有属于卷积层的图形元素都突出显示为蓝色,而属于卷积层的输入和输出的图形元素都突出显示为橙色。

图 2:CNN 架构的图层(蓝色)和特征地图(橙色)的彩色化[图片由作者创建,如下图所示]
在图 2 中,两个橙色“模块”之间的蓝色连接是卷积层的示意图。这意味着卷积层本身的形状是不可见的。只有输入和输出数组的形状(=维数)用图形表示。(橙色)输入和输出阵列的这种块状表示可能会误导人们认为它们代表卷积层——但这是而不是的情况!
相反,在蓝色连接的“位置”上,多个卷积核(或“过滤器”,只是“核”的不同单词)被应用于输入数组。每个内核生成一个特征映射数组(如何完成,在下面的步骤 3 中解释)。卷积层是应用于输入和输出阵列之间的所有内核的串联。
因此,卷积层是一个多维数组,提供了输入数组到输出数组的变换。因此,将内核表示为一个类似块的对象,即输入和输出形状,会更加直观(图 3):

图 3:卷积层(蓝色)及其输入和输出数组(橙色)的图形“块状”表示(请与图 2 进行比较)
因为内核是一个数组,所以输入数组、输出数组和内核数组的形状可以相互推断。如果两个数组的形状以及参数“padding”和“stride”已知,则可以计算第三个数组的形状。为了进行这种计算,必须理解所有三个阵列的形状和影响它们的参数之间的关系。
第一步:了解不同参数对输出数组形状的影响。
为了更好地理解卷积核和它们的输入输出数组之间的关系,画出它们是非常有帮助的。除了图 3 之外,我建议将空间维度写在它们所属的图形元素旁边(图 4)。

图 4:与图 3 所示相同的卷积层(蓝色),包括其输入和输出数组(橙色),在图形中的相应位置添加了空间参数。
所有数组都由宽度(wᵢ=6 表示输入数组,wₖ=3 表示内核数组,wₒ=4 表示输出数组)、高度(hᵢ=6、hₖ=3 和 hₒ=4)和深度(dᵢ=2、dₖ=2 和 d = 3)来描述。可以使用术语“通道”或“特征图”来代替术语“深度”。为了更好地理解,在图 5 中显示了一个通用的表示。

图 5:可视化卷积层(蓝色)及其输入和输出数组(橙色)的替代方法
但是有两个额外的参数会影响输出形状:
1.零填充 p:这是在应用内核时,在输入数组的边界上添加包含值 0 的额外像素。在图 4 的例子中,没有从图 1 中的信息“有效填充”导出的零填充,意味着填充值为 0。因为零填充在图像周围添加了一个人工的“零边界”,所以我建议将其表示为内核数组周围的虚线边界(图 6)。

图 6:内核数组块周围的虚线边框的零填充示意图
2.内核沿着输入数组的步数。在这个例子中,内核的步幅是 2。图 1 中没有明确提到这个值。但是该值可以从所有先前描述的形状中推断出来。该过程的细节在下面的步骤 2 中描述。为了将 s=2 时的步幅包含在表示中,我建议将其添加为内核数组旁边的向右和向下的箭头(图 7)。在这种情况下,假设沿两个轴的步幅(意味着“向右”和“向下”)相等。

图 7:内核步幅的示意图,用向右下方的箭头表示
这两个参数都是卷积层的属性。它们都可以很容易地包含在其图形表示中(图 8)。

图 8:卷积层(蓝色)及其相应的输入和输出数组(橙色)的图示
这种可视化的一般表示如图 9 所示。

图 9:卷积层(蓝色)的广义表示,包括计算输入和输出所需的所有维度,以及输入和输出的广义维度。
如果您想要计算卷积层的输出形状,您需要输入数组和内核数组的所有参数,如图 9 所示。为了便于理解,让我们介绍一个输入数组(图 10)和内核数组(图 11)的数值示例,我们随后将为其计算输出形状。

图 10:表示输入数组的不同可能性(包括一个数字示例)

图 11:表示内核数组的不同可能性(包括一个数字示例)
正如您在图 10 中看到的,深度(=所有通道)描述了一个数组中特征图的数量。
第二步:计算输出数组的宽度和高度。
图 11 的上卷积核在图 10 的上输入阵列上的应用如下图 12 所示。如此图所示,输出图像的宽度和高度是 2 个像素。因此,原因是卷积核使用四个步骤来覆盖输入数组。当覆盖输入阵列的一个扇区时,执行输入阵列和内核阵列之间的逐元素乘法,并将结果加到一个值。
提示:对于后面的图,提供了更长的解释。为了避免烦人的滚动,我建议你再次打开这个网站,将图 12(或随后的图,分别)放在你正在阅读的文本旁边。




图 12:一个输入阵列上的一个卷积阵列的步骤
此外,在输入数组的边界没有使用零填充。因此,最低行和最后一列没有被卷积阵列覆盖。包含在这一行和这一列中的信息不会被传送到随后的输出数组中。这个过程也在等式 1 中进行数学描述,通过等式 1 计算输出宽度 wₒ和输出高度 hₒ。[2]

等式 1:卷积层的宽度和高度的计算。“向下”步幅和“向下”填充由 pₕ和 sₕ描述,而填充和步幅“向右”由 pᵥᵥ和 sᵥᵥ.描述[2]
在等式 1 中,假设输入阵列(hᵢwᵢ)、核心阵列(hₖwₖ)和输出阵列(hₒwₒ)的宽度和高度的不同空间维度。另外,沿着宽度和高度的不同尺寸的步幅(sᵥᵥ和 sₕ)和不同尺寸的填充(pᵥᵥ和 pₕ)也被假定。通常,使用所有二次阵列,因此,计算被连接成一个等式 2:

等式 2:如果输入阵列和核心阵列是二次的,则计算输出形状。o 是输出数组的宽度或高度,I 是输入数组的宽度或高度,k 是内核的宽度或高度,s 是内核向右或向下的步幅,p 是沿“水平”输入数组边界或“垂直”输入数组边界的填充。[2]
等式 2 的商的结果总是四舍五入。这在数学上用底函数来表示。提示:这些“左括号”很容易被误认为是方括号。不要犯这个错误!
—如果内核不“适合”输入数组—
通过对结果进行四舍五入,以数学方式描述了边界上多余行和列的“丢弃”。这意味着,如果具有特定步幅的内核不适合输入数组,结果会生成一个浮点值。因为输入数组的剩余像素不影响输出数组,所以结果被四舍五入。
对于图 12 所示的示例,等式 2 显示了插入了数值的等式 1:

等式 3:计算图 12 所示卷积层的宽度和高度。
丢弃剩余像素的行为可以通过应用填充来省略。如果在顶部、底部、左边界和右边界应用一个像素的填充,内核将覆盖输入数组的所有值(图 13)。

图 13:输入数组的所有边界上的零填充。
但是根据等式 3,在这个过程中仍然有一行和一列像素丢失。与之前的丢弃相比(图 12),只丢弃了补零的值,这不会导致信息丢失。
—非对称填充—
像素的添加,结果是被丢弃,看起来没什么用。对于这些情况,可以在输入阵列的边界仅添加一行和一列像素(图 14)。因此,等式 1 必须改变(等式 4):在等式 1 中,通过将填充值乘以 2 来悄悄地假定填充的对称性。如果使用非对称填充,则必须分别在图像的顶部和底部以及右侧和左侧插入:


等式 4:用于不对称填充的修改的等式 1

图 14:在输入数组的右边和底部边界填充零。
—输入数组像素的覆盖范围—如图 12 所示,输入贴图的像素经常被内核不同地覆盖,这取决于内核的大小及其步长。图 15 示出了图 12 的上部输入阵列的内核覆盖的数量。绿色标记的像素被卷积核覆盖 4 次。因此,与仅被覆盖一次或两次的像素相比,它对随后的输出阵列具有更大的影响。因此,被更频繁覆盖的像素对输出阵列的影响比被较不频繁覆盖的像素更大。

图 15:图 12 的上部输入阵列的内核覆盖的图形表示,具有(3×3)-内核和沿宽度和高度的步长 2。灰色像素被卷积核覆盖零次并被丢弃。卷积核覆盖黄色像素一次,覆盖红色像素两次,覆盖绿色像素四次。
第三步:计算输出数组的深度
要计算卷积层的深度及其输入数组,你得知道一个简单的规则:输入数组的深度和内核数组的深度必须永远相等。内核通道(=内核数组中的单个二维数组)必须与输入数组中的通道(特征映射)一样多。原因如图 16 所示:

图 16:将内核通道分配给相应的输入通道。两个内核通道形成内核数组,两个输入通道形成输入数组。
我们希望为每个输入通道使用特定的内核通道,因为对输入阵列的第一个二维通道有用的内核可能对输入阵列的其他通道完全无用。因此,每个输入通道使用其“自己的”二维内核通道。因此,对于每个输入通道,内核恰好包含一个二维内核通道。所有内核通道相互堆叠,形成完整的“卷积层”。
提示:输入通道和内核通道的所有对都会影响输出数组结果的每个像素。在图 16 中,上面的输入内核对生成了输出数组的上面的像素。但这是而不是的情况!输出阵列的每个像素都受到所有输入通道和所有内核通道的影响。
为了理解输入数组和内核数组对输出数组的影响,让我们在图 17 中扩展图 8 和图 9 的“绘图方法”:

图 17:单内核卷积运算。根据特定值在该矩阵中出现的频率,修改后的输出矩阵的值被着色。黄色值出现一次,红色值出现两次,绿色值出现四次。
图 17 显示了卷积层在输入阵列上的应用可以表示为两个阵列之间的矩阵乘法。为了执行这个矩阵乘法,输入数组和内核数组的维数必须减少到二维。在这一点上,理解阵列的形状不是固定的是很重要的。每当需要执行计算时,可以对数组进行整形。唯一的限制是不能丢失计算所需的信息。
想象两个矩阵 1 和 2。在这些矩阵之间,应该执行乘法。矩阵由它们的行数和列数来描述。为了连接到数组块的示例,矩阵列的数量是与数组的宽度 w 相同的参数。矩阵的行数与数组的高度 h 相同。因此,矩阵 1 的形状是(h₁ x w₁),矩阵 2 的形状是(h₂ x w₂).)要执行矩阵乘法(等式 W),第一个矩阵的宽度必须等于第二个矩阵的高度。

等式 5:如果 w₁ = h₂,则矩阵乘法
因为参数 w₁和 h₂必须相等,所以它们提供了内核数组和输出数组之间的连接。两个数组都必须转换成这个大小。
应用于输入映射时,每一步都包含完整的卷积核。因此,将矩阵乘法定位在核上是有帮助的。参数 w₁的数值由内核的大小提供。首先,内核通道变平,如图 17 所示。因此,内核的宽度 wₖ、高度 hₖ和深度 dₖ被连接成一个向量(等式 6):

等式 6:计算“扁平”内核的宽度
对于所呈现的示例,“展平的”内核的宽度是 18,这导致 18 维向量(6A 方程):

方程 6A:本例中“展平”内核的宽度
如果内核的宽度 w₁是 18,第二矩阵的高度 h₂也是 18。结果,这些值被插入到等式 5A 中:

等式 5A:带有插入参数的等式 5w₁=h₂= 18
因为我们使用了一个内核通道,所以第一个矩阵的高度 h₁是 1(下一个例子中给出了使用多个内核数组时会发生什么)。因此,等式 5B 示出了将 h₁ = 1 插入等式 5A:

等式 5B:插入参数 h₁ = 1 的等式 5A
通过等式 5B,我们知道我们的输出阵列将具有
1 像素的高度。
最后一个参数 w₂是输出矩阵的宽度。该值由输出数组的宽度 wₒ和高度 hₒ获得。幸运的是,我们已经在第 2 步中计算了这些值。因为高度 h₁将数组的高度限制为 1,所以 wₒ和 hₒ这两个值通过相乘连接在一起,得到维度 w₂(等式 6B)。

等式 6B:矩阵乘法的输出形状的计算
这个结果被插入到等式 5B 中,得到等式 5C。我们看到输出矩阵的高度为 1 像素,宽度为 4 像素。

等式 5C:插入参数 w₂ = 4 的等式 5B
内核数组可以简单地展平成(1 x 18)的形状。相反,输入图像阵列到形状(18×4)的变换不可能通过整形来实现。还记得图 15 吗,它显示了内核覆盖的像素的频率。这个覆盖率必须在
(18×4)数组中表示。被内核覆盖一次的每个值在(18 x 4)-数组中只出现一次。同样,被覆盖两次的每个值在数组中出现两次,被覆盖四次的每个值在数组中出现四次。因此,修改后的输入数组的值根据该值在该矩阵中出现的频率而被着色。黄色值出现一次,红色值出现两次,绿色值出现四次。请注意,数组中不再有“灰色”值。这意味着这个“修改的”输入数组只包括卷积核覆盖的值。
总之,为了实现矩阵乘法所需的矩阵维数,输入图像阵列不是被整形而是被修改。 如果两个数组都是正确的形状,则执行矩阵乘法并产生一个(1 x 4)向量。我们已经知道,输出数组的高度 hₒ和宽度 wₒ都是 2。因此,我们将输出数组从维度(1 x 4)调整为维度(2 x 2 x 1)。
—如果内核数组的深度等于输入数组的深度,如何应用多个内核?—
因为输入数组的深度和内核的深度总是相等的,所以在一个输入数组上应用多个内核需要第四维。这个维度称为“实例数”。因此,从现在开始,阵列的形状由四维来定义(等式 7):

等式 7:描述输入数组、输出数组或卷积核的维数。
实例的数量没有以图形方式显示,但在图 18 中显示为每个“块状”数组下方的紫色数字。

图 18:新维度 n 的图形解释,它是相应数组的实例数。在这个图中,对于输入数组和内核数组,参数 n 是紫色的。注意,内核的 nₖ实例数等于输出数组的深度 dₒ。
在图 18 中,存在一个输入数组和一个输出数组。一般来说,这是一个定义问题,即必须总是恰好有一个输入数组和一个输出数组。这意味着 nᵢ = nₒ = 1。唯一可以自由选择的数字是应用到单个输入数组的内核实例 nₖ的数量。如果应用一个以上的内核,不是深度 dₖ,而是 nₖ的实例数量增加。这似乎很合理,因为对于每个输入通道,一个内核包含一个内核通道。如果一个输入通道应该覆盖不同的内核,这些内核也必须包含每个输入通道一个内核通道。在图 19 中,显示了一个内核的三个实例在一个输入数组上的应用。

图 19:三个内核的卷积运算。根据特定值在该矩阵中出现的频率,修改后的输出矩阵的值被着色。黄色值出现一次,红色值出现两次,绿色值出现四次。
和前面的例子(图 17)一样,在图 19 中,内核的所有实例都被放到一个数组中。与前面的示例不同,所有三个实例都堆叠在彼此的“顶部”。这会产生一个(3 x 18)-数组,其中的每一行都是一个串联的内核实例。在这种情况下,矩阵乘法是(方程式 5D):

方程 5D:图 12 所示例子的矩阵乘法。
最后,(3×4)的输出数组的形状被整形为一个形状
(1×2×2×3)。输出数组必须总是 nₒ = 1,因为根据定义,只允许一个输出数组。与图 17 相同,宽度 wₒ和高度 hₒ为 2 个像素。这导致输出数组的深度为 dₒ = 3,与用于内核实例的数字相同。
从这个例子中,现在可以理解输入阵列的深度 dᵢ、输出阵列的深度 dₒ和内核阵列的深度 dₖ:之间的关系
输入阵列的深度 dᵢ和内核阵列的深度 dₖ必须相等,因为对于输入阵列中的每个特征图,需要单个二维内核阵列(等式 8)。

等式 8:内核的深度 dₖ必须等于输入阵列的深度 dᵢ
与之相比,输出数组的深度 dₒ与内核数组的深度 dₖ无关:相反,输出数组的深度 dₒ,但它等于内核实例的数量 nₖ(等式 9)。

等式 9:输出阵列的深度 dₒ由内核 nₖ的数量产生
因此,通过将输入数组和内核数组深度上的所有值相加,将每个内核步骤的结果连接起来。这通过图 17 和图 19 中输出矩阵下方的计算来表示。添加每个内核步骤的结果。因此,从两个输入数组和两个内核数组的卷积中,每个内核步骤只创建一个结果值。
最后,以下是最重要的信息:
- 卷积层只包含相互堆叠的卷积核,但既不是输入阵列也不是输出阵列。CNN 通常由其输入和输出阵列形状来表示,而不是由其卷积层形状来表示。
- 如果等式 1 的结果是浮点数,则该值被四舍五入。这意味着,剩余的像素被丢弃,它们对生成的要素地图没有影响。
- 输出数组的深度与输入数组和内核数组的深度无关。它等于输入数组上应用的内核实例的数量。
最后,我还有另一个任务:试着画出图 1 的卷积层,如图 17 和图 19 所示,看看你是否能够理解卷积层、输入和输出阵列的形状。解决方案如图 20 所示。

图 20:可视化图 1 所示架构的另一种方法。
我希望你喜欢阅读这篇文章,我希望它有助于你了解 CNN。
参考文献 【1】s .萨哈,卷积神经网络综合指南 ELI5 方式 (2018),towardsdatascience.com
[2] V. Dumoulin 等人,深度学习卷积算法指南 (2018),arXiv: 1603.07285
异常检测多样化领域的初学者综合指南
实践教程
隔离森林、局部异常因子、一类 SVM、自动编码器、稳健协方差估计和时间序列分析

作者图片
异常或异常检测处理数据中不符合预期行为的模式的检测。这些方法几乎应用于所有行业。众所周知的应用领域是检测信用卡和保险欺诈、网络安全、安全相关系统的监控和军事活动的评估。[Cha09a]
由于我将在本文中使用生产中的一个例子,所以我也不想在这里忽略这个重要的应用领域。智能工厂变得越来越敏捷、灵活、多变,因此也越来越复杂。由于这种不断增长的复杂性,操作人员很难监控过程和识别偏差。问题和故障往往发现得太晚,维护间隔选择不正确[Win15]。用于早期异常检测的智能系统可以通过在早期阶段检测到偏差来提供显著的缓解,并避免生产停工。
本文旨在通过回答以下问题,为您提供异常检测领域和所用技术的概述:
- 异常的类型有哪些?如何描述它们的特征?
- 哪些程序和方法用于识别数据集中的异常?怎么才能把他们区分开来?
- 使用了哪些具体的算法,它们是如何工作的?
请不要被术语“异常值”和“异常值”所混淆,我将两者作为同义词使用。
异常的类型
异常在发生时有决定性的不同。基本上,异常可以分为三种类型:
- 点/全局异常
- 集体异常
- 上下文异常
点异常
如果能够将单个数据实例归类为异常相对于其余数据,它被称为点或全局异常。这描述了最简单的异常值类型,也是大多数出版物关注的焦点。
在下图中,点异常显示为二维空间中的单个异常值。数据集可以清楚地分为 3 个集群。由于被标记的数据点不能被分配给任何一个聚类,必须假设该数据点代表一个异常。

二维数据集中点异常的简单示例—作者提供的图像
上下文异常
如果一个实例只在特定的上下文中表现为异常,则称之为上下文异常。上下文由给定数据集的结构决定。[Cha09a]
下面的数据集显示了墨尔本从 1981 年到 1984 年的气温趋势。标记的数据点代表上下文异常。因为大约 30 度和零下 5 度的温度本身并不罕见,但在本课程的其余部分,这些情况可以归类为非常不可能。在夏季的几个月里,气温在零下的范围内,在冬季达到 30 度是不太可能的。

气温记录中的上下文异常(数据集:澳大利亚墨尔本的最低日温度[Aus90] —已修改)
集体异常
如果相关实例的集合可以被识别为数据集其余部分的异常,这些实例描述了所谓的集体异常。将这些实例单独与数据集的其余部分进行比较,这些实例可能不会被识别为异常,但它们在集合中的出现证明了将其指定为异常值的合理性。[Cha09b]
下面的例子用几个月内大致相同水平的温度测量集合来模拟这种情况。

气温记录中的集体异常(数据集:澳大利亚墨尔本日最低气温[Aus90] —作者修改)
哪些方法用于识别异常?
异常检测领域试图识别数据集的异常实例或与大多数数据显著不同的实例。[Zim18]。这通常是指偏离定义的分布模型的数据。最著名的分布函数是正态分布,它可以用来非常精确地描述许多经济和工程过程的测量值的分布。[Fah16,第 83 页][Zim18]
除了这些概率方法,还有基于决策树、距离/密度方法和重建技术的方法。
监督方法也能发挥重要作用
特别是对于带标签的训练数据,基于模型的方法也是一种强大的替代方法。大多数技术过程是循环的,因此由重复出现的信号模式来表示,这些模式可以用回归或时间序列分析来建模。这使得识别与“正常过程的微小偏差成为可能。
下图给出了一个例子。它显示了空气中的声音水平记录旁边运行的铣床。在许多自动化过程中,可以检测到循环信号(在这种情况下,大约每 40 秒重复一次)。这种重复信号可以通过时间序列模型高精度地再现。通过简单地比较预测和记录,这种基于模型的方法可以实现比无监督方法明显更高的准确性。如果训练数据集不包含异常值、包含少量异常值或包含已知数量的异常值,那么这无疑是有帮助的。
该图像显示了形成的信号模型。在这个简单的例子中,在机器附近用锤子敲击来模拟偏差。通过比较模型和实际信号,可以清楚地看出这种偏差。这种方法可以扩展为不同的功能,如频率分析。
因此,如果您想要检测重复信号模式的偏差,对信号进行建模通常是一种强有力的方法。

根据模型声压曲线的偏差检测生产过程中的异常——作者的图像
所用算法的方法有根本的不同
下面的清单试图对不同的方法进行分类。然而,这不应该被看作是一个严格的分组,因为不同的技术使用来自不同领域的方法。
- 概率方法
这些方法是基于对事件发生的某些概率假设。数据点根据其概率分布进行评估。概率非常低的实例被识别为异常值。(例如稳健协方差估计器)【AIG 15,p . 392】【SPE 14】【ott 18】 - 距离和密度方法
无参数方法根据环境考虑和评估数据点。如果在一个数据点周围的区域中有足够多的相似数据点,则该数据被评估为正常。数据的这种相似性通常由数据点之间的距离来表示。 k 最近邻算法就是根据这个原理工作的。[第 16 页,第 207 页][第 18 页] - 聚类方法
这些方法寻找相似对象和结构的分组。实例以这样的方式分组,即一个组内的数据尽可能相似,但是不同分区的数据尽可能彼此不同。不能被分配到任何组的实例被归类为异常值。[Sha13][Hot04] - 重建方法
这些方法试图检测数据中的模式,目标是能够重建无噪声的信号。属于这些方法的已知算法是主成分分析(PCA) 和复制器神经网络(RNN) 。
如前所述,大多数方法旨在对特征空间中的区域进行建模,这些区域描述了所考虑过程的正常行为。位于定义区域之外的数据称为异常数据。然而,一些因素对这种相对简单的方法提出了挑战。在实践中,通常不可能清楚地定义正常范围,正常和异常行为之间的界限并不总是清晰的。
正常和异常行为的所有实例的集合可以通过相应的概率分布来描述。只有极少数情况下,这些分布会彼此明确分开,并允许对正常情况和异常情况进行唯一分类。下图显示了大多数用例的情况(左)和目标最佳情况(右)。在左图中,不可能实现 100%的正确分类。

根据实例的异常分数
和预定义的阈值将实例指定为正常/异常—图片由作者提供(受[Koi18]启发)
在左图的情况下,只能以最大化真阳性率和最小化假阳性率为目标。右图显示了最佳特征提取的目标。如果以这样的方式选择特征,使得正常和异常情况彼此有很大的不同,则分布曲线没有直接的交点。在这种情况下,可以选择灵敏度,使得真阳性率为 1,假阳性率为 0。
对于已经使用的声压记录示例,声学已经提供了一些特征变换的可能性,例如快速傅立叶变换。将信号转换到频域有助于更准确地区分正常和异常事件。
异常检测算法使用异常分数评估各个数据点,作为后续决策的基础。基于预定义的阈值,实例根据其异常分数被分类为正常或异常。

异常分数的阈值定义了系统的敏感度——作者的图像
阈值决定了系统对异常情况的敏感程度,代表了一个超参数。虽然在医学中,即使是与正常状态的微小偏差也会产生巨大影响,因此必须将其视为异常[Cha09a][Hod04],但由于大量的随机扰动,生产环境中系统的灵敏度过高是不合适的。尤其是在所考虑区域的环境中的这些随机影响导致数据中的大的噪声成分,这通常类似于异常并导致错误分类。[Hod04]
对于每个用例,应该对错误分类的后果进行评估。假阳性率(FPR)和真阳性率(TPR)必然是相互权衡的。阈值描述了异常分数的极限值。如果数据点的异常分数超过预定义的阈值,则将其标记为异常。基本上,降低的阈值导致 TPR 和 FPR 的增加。对于大多数用例来说,这是进行权衡的地方。
需要关于该过程的额外知识来确定最佳阈值。在工业中,主要目标是成本最优的阈值。例如,未检测到的异常可能导致设备故障和维修工作。另一方面,具有高假阳性率的系统导致操作员的大量控制努力。为了解释这种权衡,文献经常使用医疗保健的例子。在癌症检测测试中,低阈值是合适的。未能检测到早期疾病会降低患者存活的可能性,而未能检测到健康患者的测试只会导致进一步的测试。因此,高 TPR 的权重高于低 FPR。
新奇与异常检测
在异常检测领域,文献区分了新颖性和异常检测[Sci19b]:
异常值检测
例如,如果训练数据集包含与其余数据差异很大的实例,则这些实例可能是由于数据采集中的错误造成的。对于模型构建,这些数据点是令人不安的,会对模型产生负面影响。它们应该被预先识别并从训练数据集中移除。[Koi18]
新颖性检测
新颖性检测假设训练数据集不包含异常值或包含已知数量的异常值,而是对新实例是否描述异常事件感兴趣。[Koi18]
用于异常检测的算法
各种各样的算法、方法和途径被用于异常检测。以下是该领域已确定方法的汇编:

异常检测算法概述—图片作者提供
异常检测领域流行的算法有稳健协方差估计器、**隔离森林、局部异常因子算法、单类支持向量机、,下面将简要介绍。在深度学习领域,主要使用自动编码器。在时间序列和回归分析的帮助下,可以建立模型,该模型可以再现和预测所考虑过程的行为。
稳健协方差估计量
许多统计程序需要用协方差矩阵来估计数据的分布。[Sci19a]
联合概率分布描述了某些值组合出现的概率。给定一个带有两个随机变量 X 和 Y 的数据集,结果是一个二元概率分布。对于几个随机变量一个所谓的多元分布。[Haz02][Fer03]
稳健协方差估计器算法通常用于异常检测,通过假设数据呈正态分布来检测数据集中的异常值。对于新的数据点,可以基于建模的概率分布来确定测量值的概率。
最常用的分布模型是多元正态分布 ( 高斯分布)。下图显示了两个随机变量 X 和 Y,的二元正态分布,最大值出现在 x = 0,y = 0 。测量的数据点离该点越远,该数据点越不可能代表所考虑的过程的正常行为。如果数据点的预测概率低于定义的阈值,则将其标记为异常值。

多元高斯分布—图片由作者提供
隔离森林
决策树的创建是通过在所谓的叶节点划分数据来迭代完成的。数据的这种划分从树根开始。在回归和分类任务中,每一步的目标都是最大化信息增益 IG 。当在一个节点上以这样一种方式完成数据集的分割时,即实现子节点中杂质的最大减少时,就是这种情况。杂质有不同的定义。例如回归使用均方误差,分类使用熵或基尼系数作为杂质的度量。[Ras18,第 107 页][Has09,第 587 页][Ras18,
第 347 页]
隔离树 则相反,随机分割数据集。创建隔离林包括以下步骤[Har18][Vie19][Liu12]:
1-为了对单个隔离树建模,首先从 d 维特征空间(D ⊂ R^d)中的训练数据集 d 中选择大小为ψ的子集 d’
2 —该部分数据集用于构建隔离树T’。对于第一个分区过程,该算法选择一个随机特征 q 和一个分区点 p,其中 p 在数据集中该特征的最大值和最小值之间。
3 —在这个分割点 p,数据集分割成 D_l 和 D_r
4-重复第二步和第三步,直到每个节点只有一个实例,或者该节点上的所有数据都具有相同的值
下图显示了为二维数据集生长一棵隔离树的前两个除法过程。

一棵孤树的生长——作者图片
设置隔离树后,该方法计算隔离各个点所需的分割步骤。该算法基于隔离异常值所需的步骤明显减少的假设。离群点检测感兴趣的是单个数据点 x 的异常分数 s(x,ψ) ,其由隔离数据点的路径长度来描述。
下图显示了此过程的一个简单示例。虽然仅需要两个除法步骤来隔离数据点 1 和 7,但是实例 5 和 3 在四个除法步骤之后被隔离。如果无限频繁地重复隔离树的创建,隔离 6 和 7 平均所需的步骤数将显著低于其余数据点。

简单的二维隔离树示例—图片由作者提供
从隔离树到隔离林
整个数据集的一部分用于创建每个隔离树。使用 k- 创建数据集的不同部分k-隔离树 T_k 。隔离森林的结果是各个隔离树的路径长度 h(x)的平均值。
由于单个隔离树的最大大小强烈地依赖于所使用的数据集 D’的大小,因此需要归一化来获得有意义且可比较的结果。为此,引入了归一化因子 c(ψ),它描述了不成功搜索的平均路径长度[Liu12]:

然后,异常分数 s 可以计算为:

随机森林如何工作的更详细描述可以在论文 基于隔离的异常检测 中找到。【柳 12】
下图显示了 skicit-learn 隔离森林模块(sklearn.ensemble.IsolationForest)对一维数据集的预测输出。该图显示了定义的决策函数 ( decision_function = -s — offset_)的输出,该输出是使用被求反的异常分数 s 和定义的偏移计算的。在这种情况下,使用默认的偏移值-0.5。因此,对数据点的评估如下:值越低,越不正常。负值被声明为异常值,正值被声明为内部值。

隔离森林:使用异常分数区分内点和外点——图片由作者提供
局部异常因素
局部异常值因子(LOF)算法通过测量给定数据点的局部偏差来查找异常值。该算法基于局部点密度的概念。密度由到 k 个最近邻的距离来描述。通过比较每个实例的密度,可以识别具有相似密度的区域。同时,可以检测到异常值,其显示出明显较低的密度值。
LOF 是基于密度的聚类方法之一,它将聚类描述为对象彼此靠近的区域。对于簇中的对象,局部点密度超过给定的阈值。属于同一集群的对象集合在空间上被分组在一起。
该领域中常用的算法是 DBSCAN ( 基于密度的带噪声应用空间聚类)。DBSCAN 区分核心点、密度可达点和噪声点。核心对象 p 是距离ϵ.至少达到 minPts 数据点的所有数据点

DBSCAN:识别核心对象—作者提供的图像
密度可达对象 q 是没有达到这个密度值,但是从一个核心对象直接可达的对象。这些属性都不适用的数据点称为噪声点,因此不属于该聚类。[Bre00][Zim][Int96][Kir02]
下图显示了核心点、密度可及点和噪声点的逐步识别。

DBSCAN 算法的功能—图片由作者提供
- 识别距离ϵ.至少达到 minPts = 6 的所有核心物体
- 识别密度可达点,该点在ϵ.的距离内到达至少一个核心物体
到目前为止还没有被识别为核心对象或密度可达对象的实例代表噪声点。这些噪声点代表发现的异常值或异常数据点。
下图显示了更真实的数据集的分类。

DBSCAN 应用于真实数据集—作者提供的图像
LOF(局部离群因子)算法采用了 DBSCAN 的部分方法。LOF 还引入了可达性距离的概念。所谓的 k -distance 是指一个物体到其 k-nearest neighbor 的距离。[Kir02]

DBS can:k-最近邻和 k-距离的定义—作者图片
可达性距离由对象 A 和 B 之间的直接距离或对象 B 的k-距离定义。距离 k-距离内的对象被评估为等距,从而产生更稳定的结果。

由于多个对象可能具有相同的距离,在 k 距离中可达到的值集合可能包含 k 个以上的对象。这组邻居被表示为 N_k(A) 。[Als10][Sch14]
对象 A 的局部可达性密度(lrd) 计算如下:

因此,密度是对象 A 到其邻居的平均可达性距离的倒数。
然后将该局部可达性密度与相邻实例的可达性密度进行比较[Bre00]:

值为 1 的 LOF_k(A) 指示所考虑的对象 A 与其邻居相当,即它不是异常值。低于 1 的值表示相邻对象的密度较高,因此是一个内层。异常值用大于 1 的值来描述。在这种情况下,密度低于相邻实例的密度,这表明存在异常值。
单类支持向量机
支持向量机(SVM)概念将低维数据映射到新的高维特征空间。对转换后的数据执行随后的学习过程。在这个空间中,数据可以更容易地分离和分类。[Kun04]
在所谓的线性分离(也称线性分类)中,数据被一条直线或平面分成两组,即所谓的超平面【Kun 04】。由 Vapnik 于 1963 年首次引入,当 d 维数据集的分离由 (d-1)维超平面完成时,线性分类器被称为线性分类器。[jr08][cor 95]

寻找所谓的最大边缘超平面(MMH) ,即与最近点y_i=1和y_i = -1的距离最大的超平面。超平面定义为:

SVM:两类数据集的超平面定义——图片由作者提供
在大多数情况下,数据不能通过简单的线性分类来分离。这是使用基于核函数的非线性分类器的地方,这允许算法在变换的、更高维的特征空间中找到 MMH。下图显示了一个不能用简单的线性分隔符分隔的数据集。在该示例中,通过简单的多项式核(z = x 2 + y2)将数据分离到 d + 1 维特征空间中,其中通过线性平面的分离是可能的。

SVM:特征变换后二维两类数据集的线性分隔符—图片由作者提供
一级分类
schlkopf【Sch01】将 SVM 方法转化为只有一个类别的分类问题。[bou 14][man 01]schlkopf 的方法以数据集和原点之间的距离ρ最大的方式设置超平面[Tax01]。
为此,开发了一种算法,该算法返回一个函数 f ,该函数在一个尽可能小的范围内取值+1(正常实例的范围),并且在该范围内包括训练数据集的大多数数据点。该策略通过一个核 k(x,y) 将初始数据转移到一个更高维的特征空间。在该空间中,数据集与原点分离,使得距离 ρ最大。

一类 SVM:一类支持向量机是如何工作的—图片由作者提供(受[Yan16]、[Gue15]启发)
除了schlkopf方法外,还经常使用税收和关税方法。这里,超平面不采用平面形式,而是用球体【Bou14】包围数据集。
你会发现一个关于一级 SMV 如何工作的更详细的解释。
自动编码器
自动编码器是用于学习高效数据编码的神经网络。[Kra91]他们通过几个隐藏层重建输入信号。因此,代码的尺寸受到限制,迫使尺寸减小,并能够识别模式。
自动编码器包括编码器和解码器。编码器通过忽略信号“噪声”将输入转换成编码。解码器将这种新的表示(编码)转换成原始形式。与降维类似,autoencoder 旨在尽可能接近地复制输入数据集。
因为自动编码器的执行受到限制,迫使它学习对其影响最大的数据特征。目标是通过识别数据中的结构和模式对高维数据进行低维 编码。因此,自动编码器搜索编码器函数 f 和解码器函数 g ,在将 f 应用于 d 维输入空间 X ⊂ R^d 的任意元素 x 然后将 g 应用于结果元素 z 之后,尽可能精确地产生输出元素 x。[Saa18][Pie12]
因此,自动编码器学习并复制最重要的特征,或者换句话说,最频繁观察到的特征。由于“正常”数据通常在训练数据集中所占的比例要高得多,因此它们的复制性非常好。另一方面,很少出现的“异常”数据不会被复制或者复制得不够好。通过简单地比较输入数据和重构数据,可以识别异常值。[An15]

自动编码器模型架构—图片由作者提供
时间序列分析
随时间顺序记录的数据称为时间序列。

如果每个时间点只有一个观察值( n = 1 ),我们称之为单变量时间序列;如果 n > 1 ,我们就说多元时间序列。时间序列分析在经济、股票交易和智能电网控制领域非常重要[Mah16]。由于生产过程也具有循环过程的特征,时间序列分析在制造业中的应用越来越多[Bas07][Keo06][Keo02]。预测时间序列为决策提供了重要的信息。
回归方法对目标变量 y 和几个独立变量 x1、x2、.。。,x_n .自变量的值不受其他特征的影响。【BAC 06】时间序列分析则局限于一个自变量 x, 时间 ,对预测未来目标值感兴趣。这是通过分析过去的数据来完成的。大多数方法旨在识别不同的信号成分:

时间序列的可能组成部分—作者图片
这些分量用于预测没有噪声数据的输出信号。如果预测值和记录信号之间的偏差超过定义的阈值,则历史中的实例或信号部分随后被宣布为异常值/异常。
摘要
希望我能给你一个不同的检测异常技术的概述。基本上,异常检测的领域不限于特定的算法。上面列出的方法只是特别经常使用,应该能让你理解一些基本原理。
然而,对于您自己的用例,您不应该感到受到这些的限制。你应该考虑任何可以让你区分正常的和异常的的统计方法。
如果您觉得这篇文章很有帮助,您还可以找到一篇关于用于回归的概念和算法的类似文章:
</7-of-the-most-commonly-used-regression-algorithms-and-how-to-choose-the-right-one-fc3c8890f9e3>
如果您还不是中级高级会员并打算成为会员,您可以通过以下推荐链接注册来支持我:
https://dmnkplzr.medium.com/membership
感谢您的阅读!
参考
[Als10] Alshawabkeh,m .张,b;加速入侵检测系统的 GPU 上的局部异常因子算法。2010.
【An15】安,Jinwon 使用重建概率的基于变分自动编码器的异常检测。2015.
【BAC 06】巴克豪斯,k .多元分析方法:一个 anwendungsonoritierte einführung。2006.
巴苏,美国;时间序列的自动异常值检测:传感器数据的应用。2007.
[Bou14] Bounsiar,a;梅登,M. G .赫劳斯盖伯。单类支持向量机。2014.
[Bre00]布留尼格,m;克里格尔;Ng,Raymond,Sander,jrg。LOF:识别基于密度的局部异常值。2000.
钱德拉五世;班纳吉;异常检测。2009.
【cor 95】科尔特斯,c;支持向量网络。1995.
[Dei18]戴斯特勒,m;Scherrer,w . Zeitreihen and station re Prozesse。2018.
[Ert16] Ertel,w . grund kurs küNST liche Intelligenz:一种实践或科学。2016.
[Eur10]未来购买力平价的因素,2010 年。网址https://op . Europa . eu/de/publication-detail/-/publication/0 F4 eaca 5-05 f1-4532-937 e-f 504441 f73 e 0
[Fah16]法赫迈尔湖;霍伊曼角;Künstler,r . Statistik:Weg zur 数据分析。2016.
费尔努尼哈根。gemeinsame Wahrscheinlichkeitsverteilung。2003.
[Gue15] Guerbai,y;奇巴尼 Y 比拉勒·哈德贾吉。基于与书写者无关的参数有效使用一类 SVM 分类器进行手写签名验证。2015.
[Har18]哈里里;用于异常检测的密西西比州隔离林。2018.
【has 09】哈斯蒂,t;蒂布拉尼河;统计学习的要素:数据挖掘、推理和预测。2009.
数学百科全书。2002.
霍奇,V. J。异常值检测方法的调查。2004.
hot 04:hot ho,a .与 Hintergrundwissen 一起群集。2004.
第二届知识发现和数据挖掘国际会议论文集。1996.
[Jr 08]Jr gensen,S. E .生态学百科全书。2008.
基奥,e。洛纳迪,s。在线性时间和空间的
时间序列数据库中发现令人惊讶的模式。2002.
基奥,e。林;李;寻找最不寻常的时间序列子序列:算法与应用。2006.
[kir 02]h .-p .哈普特《KDD 研讨会:集群化》。2002.网址https://www . DBS . ifi . lmu . de/Lehre/haupt seminar/SS02/KDD 02/Clustering-peer . pdf
[Koi18]小泉,y;斋藤,s。Uematsu,h;川内,y;基于深度学习和 ney man-Pearson 引理的异常声音的无监督检测。2018.
【kra 91】Kramer,Mark A. “使用自联想神经网络的非线性主成分分析” (PDF)。1991.
机器学习研讨会:支持向量机。2004.网址http://campar . in . tum . de/twiki/pub/Far/machine learning wise 2003/kunze _ ausarbeitung . pdf
刘;丁敬明;基于隔离的异常检测。
2012 年美国计算机学会数据知识发现汇刊。https://cs . nju . edu . cn/Zhou zh/Zhou zh . files/publication/tkdd 11 . pdf
[Mah16] Mahalakshmi,g;南希里黛玉;Rajaram,s .关于时间序列数据预测的调查,2016 年。
马内维茨,法律硕士;用于文档分类的一类支持向量机。2001.
奥托,T. Anomalie-Erkennung 与机器学习。2018.
[Pat18] Patel,A. A .使用 Python 进行实际操作的无监督学习。奥莱利媒体公司,2018。
皮埃尔·巴尔迪。自动编码器、无监督学习和深度架构。2012 年 ICML 无监督学习和迁移学习研讨会会议录。
拉什卡;Mirjalili,v .机器学习与 Python 和 Scikit-Learn 和 tensor flow:Das umfassende Praxis-Handbuch für Data Science,2018 年。
[Saa18] Saalmann,E. Einführung,自动编码器与卷积神经网络。2018.网址https://dbs.uni-leipzig.de/file/Saalmann_Ausarbeitung.pdf
schlkopf,b;j .普拉特;肖-泰勒法官;斯莫拉,美国;估计高维分布的支持。神经计算
[Sch14]舒伯特,e;齐梅克公司;重新考虑局部离群点检测:对局部性的概括观点及其在空间、视频和网络离群点检测中的应用。数据挖掘与知识发现,28,2014。
【Sci19a】2.6。协方差估计-sci kit-learn 0 . 21 . 1 文档,2019 年。
【Sci19b】ScikitLearn。2.7.新颖性和异常值检测-sci kit-learn 0 . 21 . 1 文档,2019 年。网址https://sci kit-learn . org/stable/modules/outlier _ detection . html
[Sha13] Sharafi,a .数据库中的知识发现。2013 年威斯巴登施普林格法赫梅登。
概率学家 Verfahren,2014 年 12 月 4 日。https://www.spektrum.de/
T4【lexikon/geographie/probabilist scis-verfahren/6232 ren/6232
[Tax01] Tax,D. M. J .一级分类。2001.
[Vie19] Vieira,r .隔离森林简介 Rui Vieira,2019。URLhttps://Rui Vieira . github . io/introduction-to-isolation-forests . html
温德曼;迈尔,a;尼格曼岛;弗雷,c;贝尔纳迪,a;顾;普弗罗默;t .斯特克尔;Krüger,m;制造过程的大数据分析。物理学杂志:会议系列,2015。
[Yan16]杨、金宏;邓,廷全。一种用于稳健异常检测的自适应加权单类 SVM。2015 中国智能系统会议论文集。2016
[Zim] Zimek,a .聚类 Teil 2。网址https://www . DBS . ifi . lmu . de/Lehre/KDD/SS14/skript/KDD-3-Clustering-2 . pdf
【Zim18】齐梅克,a;舒伯特,e .离群点检测,2018。
使用 PyTorchGeometric 库和 Open-Graph-Benchmark 的 Amazon 产品推荐数据集对 GraphSage 进行全面的案例研究,并提供实际操作经验
思想和理论
概述
这篇博客文章提供了对graph sage的理论和实践理解的全面研究,这是一种归纳的图形表示学习算法。对于实际应用,我们将使用流行的 PyTorch 几何库和Open-Graph-Benchmark数据集。我们使用ogbn-products数据集,这是一个无向图和未加权图,代表一个亚马逊产品共同购买网络,来预测购物偏好。节点表示亚马逊上销售的产品,两个产品之间的边表示这两个产品是一起购买的。目标是在多类别分类设置中预测产品的类别,其中 47 个顶级类别用于目标标签,使其成为节点分类任务。****
简单来说,这是博客的轮廓:
- 什么是 GraphSage
- 邻近抽样的重要性
- 获得使用 GraphSage 和 PyTorch 几何库的实践经验
- Open-Graph-Benchmark 的亚马逊产品推荐数据集
- 创建和保存模型
- 生成图形嵌入、可视化和观察
启动电源!!
我和 PyTorch Geometric,NVIDIA Triton,ArangoDB 一起做过一个专题为“ 关于图的机器学习:超越欧氏空间的思考”的工作坊。本次研讨会深入探讨了图形数据结构的重要性、Graph ML 的应用、图形表示学习背后的动机、如何通过 Nvidia Triton 推理服务器在生产中使用 Graph ML 以及使用真实应用程序的 ArangoDB。
什么是图形表征学习?
一旦在合并了图的所有实体(节点)之间有意义的关系(边)之后创建了图。想到的下一个问题是找到一种方法来将关于图结构的信息(例如,关于图中节点的全局位置或其局部邻域结构的信息)集成到机器学习模型中。从图中提取结构信息的一种方式是使用节点度、聚类系数、核函数或手工设计的特征来计算其图统计,以估计局部邻域结构。然而,使用这些方法,我们不能执行端到端的学习,即在训练过程中不能借助损失函数来学习特征。
为了解决上述问题,已经采用表示学习方法将关于图的结构信息编码到欧几里德空间(向量/嵌入空间)。
图形表示学习背后的关键思想是学习一个映射函数,它将节点或整个(子)图(来自非欧几里德)作为低维向量空间中的点嵌入(到嵌入空间)。目的是优化这种映射,使得在原始网络中邻近的节点在嵌入空间(向量空间)中也应该保持彼此靠近,同时将未连接的节点推开。因此,通过这样做,我们可以通过学习映射函数来保持嵌入空间内的原始网络的几何关系。下图描述了映射过程,编码器 enc 将节点 u 和 v 映射到低维向量 zu 和 zv :

图形表示学习中的映射过程(src: stanford-cs224w
让我们用 Zachary 空手道俱乐部社交网络的图结构中的一个有趣的例子来更直观地理解这一点。在该图中,节点表示人,并且如果两个人是朋友,则在他们之间存在边。图表中的颜色代表不同的社区。图 A)表示扎卡里空手道俱乐部社交网络,图 B)示出了使用 DeepWalk 方法从空手道图创建的节点嵌入的 2D 可视化。如果你分析这两个图,你会发现从一个图结构(非欧几里德或不规则域)到一个嵌入空间(图 B)的节点映射是以这样一种方式完成的,即嵌入空间中的节点之间的距离反映了原始图中的接近度(保留了节点的邻域结构)。例如,在空手道图中,标记为紫色和绿色的人的群体与彼此远离的紫色和海绿色的群体相比,共享很近的距离。当在空手道图上应用深度行走方法时(为了学习节点嵌入),当学习的节点嵌入在 2D 空间中可视化时,我们可以观察到相同的邻近行为。

图像学分:图形表示学习
我们可以将这些学习到的节点嵌入用于各种机器学习下游任务:
1)它可以用作下游 ML 任务的特征输入(例如,经由节点分类或链接预测的社区检测)
2)我们可以从嵌入中构造一个 KNN/余弦相似图。该图表可用于推荐(如产品推荐)
3)通过使用 U-Map、t-SNE 算法(例如执行聚类)将数据减少到 2 或 3 维,对数据进行可视化探索。
4)数据集比较
5)迁移学习
GraphSage 动机!!
在这篇博客文章/笔记本中,我们将介绍一种 GraphSage (样本和聚合)算法,这是一种归纳(它可以推广到看不见的节点)深度学习方法,由 Hamilton,Ying 和 Leskovec (2017)开发,用于为节点生成低维向量表示的图形。这与以前的图形机器学习方法形成对比,如图形卷积网络或 DeepWalk,它们是固有的直推式,即它们只能在训练期间为固定图形中出现的节点生成嵌入。
这意味着,如果将来图形发展并且新节点(在训练期间不可见)进入图形,那么我们需要重新训练整个图形,以便计算新节点的嵌入。这一限制使得直推式方法无法有效地应用于不断进化的图形(如社交网络、蛋白质-蛋白质网络等),因为它们无法在看不见的节点上进行推广。直推式方法(主要是 DeepWalk 或 Node2Vec)的另一个主要限制是,它们不能利用节点特征,例如文本属性、节点概要信息、节点度等。
另一方面,GraphSage 算法同时利用丰富的节点特征和每个节点邻域的拓扑结构来有效地生成新节点的表示,而无需重新训练。
一些流行的 GraphSage 用例:
1)动态图表:这些图表随着时间的推移而演变,就像来自脸书、Linkedin 或 Twitter 的社交网络图表,或者 Reddit 上的帖子,Youtube 上的用户和视频。
2)通过无监督损失函数生成的节点嵌入可用于各种下游机器学习任务,如节点分类、聚类和链接预测。
3)需要为其子图计算嵌入的真实世界应用
4)蛋白质-蛋白质相互作用图:在这里,经过训练的嵌入生成器可以预测在新物种/有机体上收集的数据的节点嵌入
5) UberEats:它利用 Graph ML 的力量向用户建议他们下一步可能喜欢的菜肴、餐馆和美食。为了提出这些建议,优步·艾特斯使用了 GraphSAGE 算法,因为它具有归纳的性质和扩展到十亿个节点的能力
6) Pinterest:它使用 pin sage(graph sage 的另一个版本)的功能进行视觉推荐(pin 是视觉书签,例如用于购买衣服或其他产品)。PinSage 是一种基于随机游走的 GraphSage 算法,它学习网络规模图中节点(以十亿计)的嵌入。
GraphSage 的工作原理

来源:大型图上的归纳表示学习
GraphSage 的工作过程主要分为两步,第一步是对输入图进行邻域采样,第二步是在每个搜索深度学习聚合函数。我们将详细讨论这些步骤中的每一个步骤,从执行节点邻域采样的需求开始。之后,我们将讨论学习聚合器函数的重要性,这些函数基本上帮助 GraphSage 算法实现了其属性归纳性。
邻域抽样的重要性是什么?
让我们从下面描述的图形卷积网络图(GCNs)的角度来理解这一点。GCNs 是一种算法,它可以利用图形拓扑信息(即节点的邻域)和节点特征,然后提取这些信息以生成节点表示或密集矢量嵌入。下图直观地展示了 GCNs 的工作过程。在左侧,我们有一个样本输入图,其中的节点由它们相应的特征向量(例如,节点度或文本嵌入等)表示。我们首先定义一个搜索深度(K ),它通知算法从目标节点的邻域收集信息应该达到什么深度。这里,K 是一个超参数,它也描述了 GCNs 中使用的层数。
在 K=0 时,GCNs 将所有节点嵌入初始化为它们的原始特征向量。现在,假设我们想要计算目标节点 0 在层 K=1 处的嵌入,然后我们聚集(它是其邻居的置换不变函数)与节点 0 相距 1 跳距离的所有节点(包括其自身)的特征向量(在该时间步或层,我们正在聚集 K=0 处的节点的原始特征表示)。对于目标节点 0 ,GCNs 使用均值聚合器来计算相邻节点特征及其自身特征的均值(自循环)。在 K=1 之后,目标节点 0 现在知道了关于其紧邻的信息;这一过程如下图所示。我们对图中的所有节点重复这一过程(即,对于每个节点,我们在 1 跳邻域上聚合),以便在每一层找到每个节点的新表示。
注意:随着搜索深度的增加,目标节点从其局部邻域聚集特征的范围也增加。例如,在 K=1 时,目标节点知道关于其 1 跳距离的本地邻居的信息,在 K=2 时,目标节点知道关于其 1 跳距离的本地邻居的信息,以及 1 跳距离即高达 2 跳距离的节点的邻居的信息。

GCN 方法的问题
如上所述,GCNs 使用邻域聚合来计算节点表示。出于训练目的,我们可以将目标节点的 k 跳邻域表示为计算图,并且以小批量方式发送这些计算图,以便学习网络的权重(即,应用随机梯度下降)。下图说明了目标节点 0 到 2 跳邻域的计算图。现在,这个问题是:
1) 计算量大:因为对于每个节点,我们需要生成完整的 K 跳邻域计算图,然后需要从其周围聚集大量信息。随着我们深入到邻域(大 K ),计算图变得指数级大。这可能会导致在 GPU 内存中拟合这些大型计算图形时出现问题。
2) 枢纽节点或名人节点的诅咒:枢纽节点是那些在图中度数非常高的节点,例如一个非常受欢迎的名人拥有数百万个连接。如果是这种情况,那么我们需要聚集来自数百万个节点的信息,以便计算中枢节点的嵌入。因此,为中枢节点生成的计算图非常庞大。这个问题图示如下(r.h.s)。

因此,想法不是获取目标节点的整个 K 跳邻域,而是从 K 跳邻域中随机选择几个节点,以便生成计算图。这一过程被称为邻域采样,它为 GraphSage 算法提供了独特的能力,可以将图中的节点扩展到十亿个。因此,使用这种方法,如果我们遇到任何中枢节点,那么我们不会采用它的整个 K 跳邻域,而是从每层或搜索深度 K 中随机选择几个节点。现在,生成的计算图由 GPU 处理更有效。下图显示了在每一跳最多采样 2 个邻居的过程。

为什么 GraphSage 被称为归纳表示学习算法?
GraphSage 是 GCNs 的归纳版本,这意味着它在学习期间不需要整个图结构,并且它可以很好地推广到看不见的节点。它是图形神经网络的一个分支,通过从多个搜索深度或跳跃中采样和聚集邻居来学习节点表示。其归纳属性基于这样的前提,即我们不需要学习每个节点的嵌入,而是学习一个聚集函数(可以是任何可微分函数,如均值、池或 lstm ),当给定来自节点的局部邻域的信息(或特征)时,它知道如何聚集这些特征(通过随机梯度下降进行学习),使得节点 v 的聚集特征表示现在包括关于其局部环境或邻域的信息。
GraphSage 在两个方面不同于 GCNs:即 1)graph sage 不是获取目标节点的整个 K 跳邻域,而是首先采样或修剪 K 跳邻域计算图,然后在这个采样图上执行特征聚集操作,以便为目标节点生成嵌入。2)在学习过程中,为了生成节点嵌入;GraphSage 学习聚合器函数,而 gcn 利用对称归一化图拉普拉斯算子。
下图说明了 GraphSage 节点 0 如何在搜索深度 K=1 处从其采样的本地邻居聚集信息。如果我们观察 r.h.s 图,我们将发现在 K=1 时,目标节点 0 现在具有关于其周围直到 1 跳的信息。

GraphSage 的形式解释
如上所述,GraphSage 的关键概念是学习如何从节点的本地邻域聚集特征信息。现在,让我们更正式地理解 GraphSage 如何使用前向传播在每一层(K)生成节点嵌入。我们借助视觉来理解这一点,然后将这种理解映射到 GraphSage 论文中提到的伪代码。但在此之前,让我们定义一些本文中使用的符号。
定义符号:

如上图所示,在 K=1 时,目标节点 0 聚合来自其本地邻居的信息(特征),最多 1 跳。类似地,在 k=2 时,目标节点 0 聚集来自其本地邻居的信息直到 2 跳,即现在它知道在它的邻居中有什么直到 2 跳。因此,我们可以重复这个过程,其中目标节点 0 从图的更远的范围递增地获得越来越多的信息。我们为原始图(∀v ∈ V)中的每个节点收集信息。让我们添加一些视觉效果来更直观地理解这个迭代过程:
下图描绘了层 K=0 处的目标节点 0 的计算图,此时图中的所有节点都被初始化为其原始特征向量。我们的目标是通过迭代的局部邻域信息收集过程,在层 K=2 找到节点 0 (即 z0)的最终表示。这个迭代过程有时也被称为消息传递方法。
因此,我们可以将此步骤正式表示为:
********
注:由于 medium 不支持下标,我就把隐藏层(h)表示写成(上标)h(下标)。
上标表示-> 第 k层
下标表示->节点 id
邻域聚合(K=1)
由于节点是从图的更深处逐渐收集信息的,所以我们从搜索深度 1…K 开始迭代过程。在 K=1 时,我们将目标节点 0 (1h0)的相邻节点表示聚合到单个向量中,即位于前一层(K-1h2 和 K-1h3)的节点 2 和 3 表示。这里 1h0 是聚合表示。在同一时间步,节点 2、3 和 9 也将聚集来自它们各自的局部邻域的特征向量,直到 1 跳的距离。现在,在这个时间点上,计算图中的每个节点都知道它们的周围环境中有什么样的信息。
因此,我们可以将此步骤正式表示为:

更新
一旦我们实现了聚合表示,即 1h0,下一步将是将该聚合表示与其先前的层表示(0h0)连接或组合。然后通过将它乘以一个权重矩阵 WK 将变换应用于这个连接的输出,你可以认为这个过程类似于将卷积核(可学习的权重矩阵)应用于图像,以便从中提取特征。最后,我们对这个转换后的输出应用非线性激活函数,使其能够学习和执行更复杂的任务。
重要提示:graph sage 算法在每个搜索深度 K 单独学习权重矩阵,或者你也可以说它学习如何在每个搜索深度从节点的邻域聚集信息。
因此,我们可以将此步骤正式表示为:
********
规范化节点嵌入
随后,对节点表示 khv(或此时的步骤 1h0)应用归一化,这有助于算法保持节点嵌入的一般分布。该步骤计算如下:

K=2 时的节点嵌入
完成了从 K=1 的节点的局部邻域收集信息。在 K=2 时,节点探索图的更远的范围,即超出它们的直接邻居,并且查看跳距离 2。我们再次执行节点的本地邻居聚合,但是这一次目标节点 0 现在将具有其在 1 跳和 2 跳距离的邻居的信息。然后,我们再次重复搜索深度 K=2 的更新和归一化过程。由于为了理解 GraphSage 算法的流程,我们已经设置了 K=2 的值,因此,我们将在此停止。在 K=2 之后,计算图中的每个节点由它们各自的最终节点嵌入表示,即 zv 。
此工作流程如下图所示:

现在,我们可以很容易地将我们的理解映射到论文中的以下 GraphSage 算法:

损失函数:学习参数
作者在论文中使用两种不同类型的损失函数记录了结果,如下所示:
无监督情况:如图形表示学习部分所述,目标是优化映射,使得原始网络中邻近的节点在嵌入空间(向量空间)中也应该保持彼此靠近,同时将未连接的节点推开。
监督案例:作者使用常规交叉熵损失来执行节点分类任务。
下面是本文中使用的无监督损失函数:

使用 PyTorch 几何库和 OGB 基准数据集对 GraphSage 进行实际操作!
我们将借助来自Open Graph Benchmark(OGB)数据集的真实数据集,更详细地理解 GraphSage 的工作过程。OGB 是由斯坦福大学开发的一个现实的、大规模的、多样化的图表机器学习基准数据集的集合。
注意了。!!
前面有很多代码,如果你有兴趣接触这些代码,我真的鼓励你这样做,那么我已经准备了一个 google colab 笔记本 供你使用…
资料组
我们使用 obgn-products 数据集,这是一个无向图,表示亚马逊产品联合购买网络。节点表示亚马逊上销售的产品,两个产品之间的边表示这两个产品是一起购买的。节点特征表示取自产品描述的词汇特征。目标是在多类别分类设置中预测产品的类别,其中 47 个顶级类别用于目标标签,使其成为节点分类任务。
让我们从下载必要的库开始
***# Installing Pytorch Geometric*
%%capture
!pip install -q torch-scatter -f https://pytorch-geometric.com/whl/torch-1.9.0+cu102.html
!pip install -q torch-sparse -f https://pytorch-geometric.com/whl/torch-1.9.0+cu102.html
!pip install -q torch-cluster -f https://pytorch-geometric.com/whl/torch-1.9.0+cu102.html
!pip install -q torch-geometric
!pip install ogb
!pip install umap-learn**
导入必要的库
****import** **torch**
**import** **torch.nn.functional** **as** **F**
**from** **tqdm** **import** tqdm
**from** **torch_geometric.data** **import** NeighborSampler
**from** **torch_geometric.nn** **import** SAGEConv
**import** **os.path** **as** **osp**
**import** **pandas** **as** **pd**
**import** **numpy** **as** **np**
**import** **collections**
**from** **pandas.core.common** **import** flatten
*# importing obg datatset*
**from** **ogb.nodeproppred** **import** PygNodePropPredDataset, Evaluator
**from** **pandas.core.common** **import** flatten
**import** **seaborn** **as** **sns**
**import** **matplotlib.pyplot** **as** **plt**
sns.set(rc={'figure.figsize':(16.7,8.27)})
sns.set_theme(style="ticks")
**import** **collections**
**from** **scipy.special** **import** softmax
**import** **umap****
下载并加载数据集
**root = osp.join(osp.dirname(osp.realpath('./')), 'data', 'products')
dataset = PygNodePropPredDataset('ogbn-products', root)**
获取训练、验证和测试索引
***# split_idx contains a dictionary of train, validation and test node indices*
split_idx = dataset.get_idx_split()
*# predefined ogb evaluator method used for validation of predictions*
evaluator = Evaluator(name='ogbn-products')**
让我们检查一下训练、验证和测试节点的划分。
***# lets check the node ids distribution of train, test and val*
print('Number of training nodes:', split_idx['train'].size(0))
print('Number of validation nodes:', split_idx['valid'].size(0))
print('Number of test nodes:', split_idx['test'].size(0))Number of training nodes: 196615
Number of validation nodes: 39323
Number of test nodes: 2213091**
加载数据集
**data = dataset[0]**
图表统计
***# lets check some graph statistics of ogb-product graph*
print("Number of nodes in the graph:", data.num_nodes)
print("Number of edges in the graph:", data.num_edges)
print("Node feature matrix with shape:", data.x.shape) *# [num_nodes, num_node_features]*
print("Graph connectivity in COO format with shape:", data.edge_index.shape) *# [2, num_edges]*
print("Target to train against :", data.y.shape)
print("Node feature length", dataset.num_features)Number of nodes in the graph: 2449029
Number of edges in the graph: 123718280
Node feature matrix with shape: torch.Size([2449029, 100])
Graph connectivity in COO format with shape: torch.Size([2, 123718280])
Target to train against : torch.Size([2449029, 1])
Node feature length 100**
检查唯一标签的数量
***# there are 47 unique categories of product*
data.y.unique()**
从数据集内提供的标签映射将整数加载到真实产品类别
**df = pd.read_csv('/data/products/ogbn_products/mapping/labelidx2productcategory.csv.gz')**
让我们看看一些产品类别
**df[:10]**

创建产品类别和相应整数标签的字典
**label_idx, prod_cat = df.iloc[: ,0].values, df.iloc[: ,1].values
label_mapping = dict(zip(label_idx, prod_cat))*# counting the numbers of samples for each category*
y = data.y.tolist()
y = list(flatten(y))
count_y = collections.Counter(y)
print(count_y)**
邻近抽样
该模块迭代地采样邻居(在每层)并构建模拟 GNNs 实际计算流程的二分图。
sizes:表示我们希望对每层中的每个节点采样多少个邻居。
NeighborSampler保存当前:obj: batch_size,参与计算的所有节点的 id:obj:n_id,通过元组保存二分图对象列表:obj: (edge_index, e_id, size),其中:obj: edge_index表示源节点和目标节点之间的二分边,obj: e_id表示完整图中原始边的 id,obj: size保存二分图的形状。
然后,实际的计算图以相反的方式返回,这意味着我们将消息从一个较大的节点集传递到一个较小的节点集,直到到达我们最初想要计算嵌入的节点。
**train_idx = split_idx['train']
train_loader = NeighborSampler(data.edge_index, node_idx=train_idx,
sizes=[15, 10, 5], batch_size=1024,
shuffle=**True**)**
GraphSage 算法
****class** **SAGE**(torch.nn.Module):
**def** __init__(self, in_channels, hidden_channels, out_channels, num_layers=3):
super(SAGE, self).__init__()
self.num_layers = num_layers
self.convs = torch.nn.ModuleList()
self.convs.append(SAGEConv(in_channels, hidden_channels))
**for** _ **in** range(num_layers - 2):
self.convs.append(SAGEConv(hidden_channels, hidden_channels))
self.convs.append(SAGEConv(hidden_channels, out_channels))
**def** reset_parameters(self):
**for** conv **in** self.convs:
conv.reset_parameters()
**def** forward(self, x, adjs):
*# `train_loader` computes the k-hop neighborhood of a batch of nodes,*
*# and returns, for each layer, a bipartite graph object, holding the*
*# bipartite edges `edge_index`, the index `e_id` of the original edges,*
*# and the size/shape `size` of the bipartite graph.*
*# Target nodes are also included in the source nodes so that one can*
*# easily apply skip-connections or add self-loops.*
**for** i, (edge_index, _, size) **in** enumerate(adjs):
xs = []
x_target = x[:size[1]] *# Target nodes are always placed first.*
x = self.convs[i]((x, x_target), edge_index)
**if** i != self.num_layers - 1:
x = F.relu(x)
x = F.dropout(x, p=0.5, training=self.training)
xs.append(x)
**if** i == 0:
x_all = torch.cat(xs, dim=0)
layer_1_embeddings = x_all
**elif** i == 1:
x_all = torch.cat(xs, dim=0)
layer_2_embeddings = x_all
**elif** i == 2:
x_all = torch.cat(xs, dim=0)
layer_3_embeddings = x_all
*#return x.log_softmax(dim=-1)*
**return** layer_1_embeddings, layer_2_embeddings, layer_3_embeddings
**def** inference(self, x_all):
pbar = tqdm(total=x_all.size(0) * self.num_layers)
pbar.set_description('Evaluating')
*# Compute representations of nodes layer by layer, using *all**
*# available edges. This leads to faster computation in contrast to*
*# immediately computing the final representations of each batch.*
total_edges = 0
**for** i **in** range(self.num_layers):
xs = []
**for** batch_size, n_id, adj **in** subgraph_loader:
edge_index, _, size = adj.to(device)
total_edges += edge_index.size(1)
x = x_all[n_id].to(device)
x_target = x[:size[1]]
x = self.convs[i]((x, x_target), edge_index)
**if** i != self.num_layers - 1:
x = F.relu(x)
xs.append(x)
pbar.update(batch_size)
**if** i == 0:
x_all = torch.cat(xs, dim=0)
layer_1_embeddings = x_all
**elif** i == 1:
x_all = torch.cat(xs, dim=0)
layer_2_embeddings = x_all
**elif** i == 2:
x_all = torch.cat(xs, dim=0)
layer_3_embeddings = x_all
pbar.close()
**return** layer_1_embeddings, layer_2_embeddings, layer_3_embeddings**
实例化模型
**device = torch.device('cuda' **if** torch.cuda.is_available() **else** 'cpu')
model = SAGE(dataset.num_features, 256, dataset.num_classes, num_layers=3)
model = model.to(device)**
加载节点特征矩阵和节点标签
**x = data.x.to(device)
y = data.y.squeeze().to(device)**
培养
****def** train(epoch):
model.train()
*#pbar = tqdm(total=train_idx.size(0))*
*#pbar.set_description(f'Epoch {epoch:02d}')*
total_loss = total_correct = 0
**for** batch_size, n_id, adjs **in** train_loader:
*# `adjs` holds a list of `(edge_index, e_id, size)` tuples.*
adjs = [adj.to(device) **for** adj **in** adjs]
optimizer.zero_grad()
l1_emb, l2_emb, l3_emb = model(x[n_id], adjs)
*#print("Layer 1 embeddings", l1_emb.shape)*
*#print("Layer 2 embeddings", l1_emb.shape)*
out = l3_emb.log_softmax(dim=-1)
loss = F.nll_loss(out, y[n_id[:batch_size]])
loss.backward()
optimizer.step()
total_loss += float(loss)
total_correct += int(out.argmax(dim=-1).eq(y[n_id[:batch_size]]).sum())
*#pbar.update(batch_size)*
*#pbar.close()*
loss = total_loss / len(train_loader)
approx_acc = total_correct / train_idx.size(0)
**return** loss, approx_acc**
划时代!!
**optimizer = torch.optim.Adam(model.parameters(), lr=0.003)
**for** epoch **in** range(1, 21):
loss, acc = train(epoch)
*print(f'Epoch {epoch:02d}, Loss: {loss:.4f}, Approx. Train: {acc:.4f}')***
为推理部分保存模型
我们需要保存推理部分的模型,因为由于 RAM 大小的限制,google colab 不能同时创建两个图形加载器。因此,我们首先使用 train_loader 进行训练,然后使用这个保存的模型对测试数据进行推理。
在这里,您可以将模型保存在 google MyDrive 或本地计算机上。
***#torch.save(model, '/content/drive/MyDrive/model_weights/graph_embeddings/model.pt')*
*# saving model in mydrive*
**from** **google.colab** **import** drive
drive.mount('/content/drive')
fp = '/content/drive/MyDrive/model.pt'
torch.save(model, './model.pt')
torch.save(model, fp)**
推论:让我们检查 GraphSage 感应功率!!
这一部分包括利用经过训练的 GraphSage 模型来计算节点嵌入并对测试数据执行节点类别预测。之后,我们比较了 GraphSage 的 3 个不同层的节点嵌入的 U-Map 可视化,得出了一些有趣的观察结果。
如果读者在我准备的 谷歌实验室笔记本 中运行 GraphSage 的推理部分,会对他/她更有用,以便更好地直观了解 GraphSage 每一层的可视化是如何计算的。
GraphSage 第 1 层节点嵌入可视化

观察
第 1 层的节点嵌入可视化表明,该模型无法很好地区分产品类别(因为不同产品类别的嵌入非常接近),因此我们无法以很高的概率预测/估计未来哪两种产品可以一起购买,或者如果某人购买了一种产品,那么他/她可能也会对另一种产品感兴趣。
GraphSage 第 2 层节点嵌入可视化

观察
在第 2 层,我们可以看到一些独立的产品类别集群正在形成,我们可以从中获得一些有价值的见解,例如电影和电视与 CD 和黑胶唱片、美容与健康和个人护理、视频游戏与玩具和游戏。然而,书和美簇相距甚远。
GraphSage 第三层节点嵌入可视化

观察
在第 3 层,节点表示比第 2 层稍微精细一些,因为我们可以看到一些更远的集群,例如手机和配件与电子产品。
承认
我要感谢 ArangoDB 的整个 ML 团队为我提供了关于博客的宝贵反馈。
想联系我: Linkedin
参考资料(更多学习材料)
- fastgraphml:一个加速 graphml 模型开发过程的低代码框架
- https://www . arango db . com/2021/08/a-comprehensive-case-study-of-graphsage-using-pytorchgeometric/?UTM _ content = 176620548&UTM _ medium = social&UTM _ source = LinkedIn&HSS _ channel = LCP-5289249(原博文)
- 大型图上的归纳表示学习
- http://web.stanford.edu/class/cs224w/slides/17-scalable.pdf
- 图形机器学习宇宙之旅:动机、应用、数据集、图形 ML 库、图形数据库
- https://medium . com/Pinterest-engineering/pin sage-a-new-graph-convolutionary-neural-network-for-web-scale-recommender-systems-88795 a107f 48
- https://eng.uber.com/uber-eats-graph-learning/
- 更多与图 ML 相关的东西
数据质量管理的综合框架
如何监控和维护数据质量,以确保数据符合特定业务用例的特定标准

斯蒂芬·道森在 Unsplash 上拍摄的照片
介绍
大数据正变得越来越流行,它被认为是每个行业的新石油。事实上,人们认为大数据和石油在开采、存储、运输和消费的方式上有一些相似之处。

照片由 Zbynek Burival 在 Unsplash 上拍摄
作为一名车主,当你每周路过加油站时,你可能会明白汽油纯度的重要性。低纯度的汽油可能会随着时间的推移严重损坏您的汽车,尽管它仍然可以让您的汽车行驶。然而,卡车上装有柴油发动机的人可能没有你这么担心,因为柴油的纯度比汽油低。总的来说,不同的发动机或应用需要不同水平的燃油质量。
同样,质量差的数据会对你的组织产生负面影响,尽管你仍然可以从中获得一些见解。然而,不同的用例可能对数据质量有不同的期望,因为追求最高质量的数据总是代价高昂的。这也体现在数据质量的定义中:“数据质量表明数据满足用户的优秀标准或 期望【3】
当人们考虑数据质量时,他们经常提到准确性这个术语。例如,“马克·扎克伯格”是脸书首席执行官的正确名字,但在数据库中,我们可能会看到类似“马克·扎克伯格”、“马克·扎克伯格”或“马克·扎克伯格”这样的名字,这是对原始名字的不准确表示。然而,就数据质量而言,光靠准确性不足以评价数据质量,因为人们还期望数据的完整性、及时性、一致性和独特性。这是衡量数据质量的五个不同维度。
本文的其余部分组织如下。在第一节中,我们介绍了五个数据质量维度的定义和例子,包括准确性、完整性、及时性、一致性和唯一性。第 2 节将提供指标计算的公式,而第 3 节概述了带有数据验证规则的指标的实现。第 4 节描述了管理数据质量的流程,第 5 节概述了该流程中涉及的所有各方的责任。
1.数据质量维度
1.1 准确性
准确性被定义为一个值与其真实生活现象的正确表示之间的接近程度。有两种类型的准确性可以确定:语义准确性和句法准确性[1]。
准确性通常在语义准确性的概念中已知,语义准确性是值 v 与真实值v’的接近程度。比如一个开发者在观察数据库中的记录时,发现了这个{OS:Windows,Creator:Linus Torvalds },{OS:Linux,Creator:Bill Gates }。他立刻意识到这些记录存在语义问题,因为 Linux 的创建者应该是 Linus Torvalds,Windows 的创建者应该是 Bill Gates。**
另一种形式的准确性是语法准确性,即值 v 与特定域中可接受值的语法接近程度。与其将 v 与真实值v’进行比较,我们更感兴趣的是知道该值在语法上是否正确。例如,一个信用卡号码应该有 16 位数字 XXXX XXXX XXXX XXXX ,如果类似1234 1234 1234 1234 1234 0的东西出现在数据库中,我们就会遇到语法准确性问题。
一般来说,语义准确性更严格,但是测量这种准确性的挑战是必须事先知道真实值v’。在上面的例子中,每个人都知道脸书 CEO 的正确姓名,因此在这个场景中可以很容易地评估语义准确性。然而,对于像交易价值这样的东西,例如,客户购买了 300 美元的手表,我们必须知道交易的真实价值,并有一个基础事实来进行比较。情况可能并非如此,因为有时我们只有一个数据源,即使我们有另一个数据源进行比较,我们也必须有一种机制将两个数据源中的对象链接在一起(对象标识)。
另一方面,语法准确性计算起来不太复杂,并且不需要预先的真值。因此,与语义准确性相比,它实际上更适用。然而,还有一些其他的用例可以应用语义准确性。在澳大利亚,澳大利亚统计局的数据可被视为国家信息的基本事实,因此可用于验证人口、经济和收入等许多不同领域的其他数据来源的语义准确性。
1.2 完整性

三种类型的完整性(由我创建的图形)
数据质量的另一个方面是完整性,在这一点上“数据的广度、深度和范围对于手头的任务来说是足够的”[1]被评估。完整性通常与记录(元组完整性)、列(列完整性)或表(关系完整性)中缺失值的数量有关。
1.3 及时性
及时性表示特定应用程序的数据的新鲜程度。有两个因素促成了这一指标:货币和波动性。货币反映数据的新鲜程度。例如,如果客户最近更改了地址,记录将如何更新。如果记录在客户的更改请求后立即更新,则货币被认为是高的,反之亦然。及时性还取决于显示数据随时间变化水平的波动性。与每月更新的数据相比,每天更新的数据具有更高的波动性。例如,出生日期可能具有零波动性,而股票价格将具有极高的波动性。波动性也可以表示为数据保持有效的时间长度。
货币和波动性在对及时性贡献方面可以相互影响。高波动性数据应该是最新的,而低波动性数据不需要高汇率。五天的延迟对于每日更新的数据来说是一个问题,而对于每月或每年更新的数据来说可能不是这样。
1.4 一致性
一致性表示来自相同或不同表的两个或多个列之间的完整性约束的符合性级别。例如,如果国家代码是 AU,则 state 列应该具有下列值之一:VIC、NSW、WA、QLD、TAS、NT、ACT、SA。
1.5 独特性
该维度捕获重复记录的数量。然而,有许多不同层次的重复,每一个都需要不同的方法来衡量。

第一种复制形式(我创造的图形)
最基本的重复形式是当我们有两个记录为每个单元格存储完全相同的值时。如果键列用作主键,这种情况就不太可能发生。然而,有时数据库系统可以选择使用生成的键,而不是数据中现有的有意义的列。在这种情况下,检测重复会稍微复杂一些,因为我们不能仅仅依赖于密钥。简单的方法是逐个单元格地比较这些值,这种方法很容易实现。

第二种复制形式(我创造的图形)
下一种形式的重复是当我们有两个记录存储不完全相同的值,但指的是现实生活中相同的对象或实体。这种差异可能只是由于数据输入过程中的打字错误造成的,也可能是由于来自不同源系统的不同数据格式造成的。对于这种重复,上述幼稚的方法是不适用的。更高级的通用方法是使用文本距离度量,如 TF-IDF(术语频率-逆文档频率)或余弦相似性来度量两个记录之间的相似性。这种方法需要更多的计算,尽管它更智能,并且适用于各种场景。
2.度量计算
给定关系 R 与 K 属性和 N 记录,度量计算如下:

度量计算公式(由我创建的图形)
- 指标可以从 0 到 1 不等 (0 表示差,1 表示好)。
- B 是一个布尔函数,如果满足条件,则返回 1
- vij 是第 j 列第 I 行的值
- 对于及时性指标, T 是数据的年龄, F 是数据的频率。
3.度量实施
数据质量框架将建立在现有数据验证框架之上,所有数据有效性规则都将在该框架中实施。数据验证框架是一套方法和软件库,包含实现有效性规则的工具。例如,数据验证框架可以提供方法或函数来检查列中的数字是否在特定范围内。这方面的一个用例是确保年龄列不包含负值。据我所知,目前还没有广泛使用的或行业标准的数据验证框架,这些框架一般都是为各种业务用例而构建的。然而,欧盟委员会的数据验证方法[5]是关于多层次数据验证的最简明的文件之一。我将感兴趣的读者推迟到[5]以获得关于数据验证的更详细的信息,我们将在另一篇文章中讨论这个主题。

数据验证、数据质量和业务(图由我创建)
在高层次上,数据质量框架将作为治理层,将数据质量方面与业务用例的关键程度联系起来。将会有一个迭代过程来持续维护和提高数据质量,以满足用户的期望,我们将在下一节中讨论这一点。

数据质量仪表板(由我创建的图形)
在实施层面,指标将由数据验证框架进行计算,并存储到中央存储中以供审计和报告。对于特定数据集,将提供一个仪表板来监控数据质量。如果违反有效性规则,也会发送警报。
4.数据质量管理流程
数据质量管理过程必须是一个迭代循环,因为数据质量需要不断维护和改进,以满足用户的期望。该过程分为两个阶段,包括以下五个阶段:

数据质量管理流程(由我创建的图)
第一阶段主要关注数据集的发现部分,以了解低质量数据对业务的影响。它包括自上而下和自下而上的方法。自顶向下的方法收集用户需求、用例信息和使用数据时的难点,而自底向上的方法更接近数据内容,并采用统计分析来识别潜在的数据质量问题。
在第二阶段,来自自上而下和自下而上方法的信息被组合,以定义五个度量中的每一个的阈值。还应该为数据确定与业务相关的规则。
之后,这些规则将在第三阶段被映射为有效性规则,以便稍后在第四阶段使用数据验证框架来实现。同样在第三阶段,将提供第一阶段检测到的任何数据质量问题的解决方案。最后,所有的设计都需要与接口协议保持一致,这样任何违反接口协议中声明的数据标准的行为都应该被检测出来。
第四阶段将是使用数据验证框架实际实施设计,而第五阶段涉及数据输入启用时的操作方面。将持续监控数据质量,并且必须建立事故报告流程,以启动数据质量管理流程的下一个周期。
如上所述,这是一个迭代过程,一般来说,有两种可能的方法来触发新的周期。第一种方法(预防性方法)是定期检查性能,并在一段固定时间后重复该循环。第二种方法(反应式方法)是在出现数据质量问题或事故时开始新的周期。根据使用案例和应用,这两种方法也可以同时应用。
5.职责和工具
本节概述了数据工程团队和数据消费者(流)在数据质量问题上的合作。在整个文档中,可以看到特定用例的数据质量的预期是监控和提高数据质量过程的起点。因此,数据质量管理是一个以业务为中心(或以用户为中心)的过程,业务的需求和期望是操作模型的核心。该模型可以说明如下:

操作模型(由我创建的图形)
在这个模型中,流将处于与客户端交互的最前沿,以收集需求并围绕数据设计用例。流负责数据质量管理过程中的第一阶段和第二阶段,因为它们了解如何使用数据,并且知道数据应该“看起来”是什么样子。
在第三、第四和第五阶段,这些职责在某种程度上由数据工程团队(DE Squad)和 Streams 分担。DE Squad 通过数据验证框架提供实施有效性规则所需的工具,并维护和改进该框架以满足流的要求。DE Squad 还负责仪表板和警报解决方案,Streams 可以轻松地为其特定数据源设置这些解决方案。另一方面,Streams 使用 DE Squad 提供的工具来应用指标和验证规则,并在 DE Squad 的支持下配置警报和仪表板。
数据质量审查可以由 Streams 执行,他们可以请求 DE Squad 向框架中添加新特性,以满足业务用例需求。由 Streams 提出的框架相关事件将由 DE Squad 在可接受的 SLA 范围内处理。
6.摘要
在本文中,我们讨论了数据质量的定义,数据质量的五个维度包括准确性、完整性、及时性、一致性和唯一性。我们已经走过了指标的计算、高级实施、管理数据质量的流程以及该流程中涉及的所有各方的责任。该框架不仅为公司提供了发展和增强以满足其业务需求的基础,还提供了通过预防性和反应性方法维护和提高数据质量的迭代过程。
我鼓励积极的读者分享您组织中当前与数据质量相关的挑战,您如何设法解决这些问题,以及关于此框架可以帮助以不同方式解决您的数据质量管理问题的潜在用例的头脑风暴。
参考
本框架摘自以下文件,稍作修改以满足当前的实际要求。
[1] C. Batini 和 M. Scannapieco,《数据质量概念、方法和技术》, Springer,2006 年。
[2] D. Loshin,《数据质量改进实践指南》,摩根·考夫曼,2010 年。
[3]《数据质量管理:角色、流程、工具》,2019。【在线】。altexsoft.com
[4] I. Mikhailouskaya,《你的数据质量管理指南》,2018 年。【在线】。scnsoft.com
[5] Essnet Validat 基金会,2016 年。数据验证方法 1.0。欧盟委员会。
Python 中的类的综合指南
如何定义一个类并与之交互

Alexander Schimmeck 在 Unsplash 上的照片
我们在这张照片中看到一堆不同的钟。它们有不同的形状、颜色和大小。然而,它们都是一种时钟。我们可以用类似的方式思考 Python 中的类。一个类代表一种类型(时钟),我们可以创建该类型的许多实例(上图中的时钟)。
面向对象编程(OOP)范式是围绕拥有属于特定类型的对象的思想而构建的。在某种意义上,类型是向我们解释对象的东西。
对象的解释对于面向对象的程序设计至关重要。我们需要全面了解:
- 一个物体代表什么
- 对象存储哪种类型的数据
- 我们如何与物体互动
- 我们如何在代码中实现对象
构成对象解释的所有这些点都是用类定义的。Python 中的一切都是整数、列表、字典、函数等类型的对象。我们使用类来定义对象的类型。
在这篇文章中,我们将回顾类的含义,如何在 Python 中创建和使用类,以及使用类会给我们带来什么样的好处。大纲如下:
- Python 中的类是什么?
- 创建一个类
- 定义类方法
- 类与实例变量
- 创建子类
Python 中的类是什么?
类拥有以下信息:
- 数据属性:创建一个类的实例需要什么
- 方法(即过程属性):我们如何与类的实例交互。
使用类有很多好处。我将在整篇文章中提到它们,而不是一次全部列出。
我们一直在使用 Python 中的类。例如,当我们创建一个列表时,我们创建一个 list 类型的实例。
words = ['data', 'science', 'machine', 'learning']
我们实际上对 list 类是如何创建的并不感兴趣。我们只需要知道如何与列表交互,并在代码中有效地使用它们。这就是抽象的理念。
例如,我们可以使用 remove 方法从列表中删除一个条目。
words.remove('data')print(words)
['science', 'machine', 'learning']
创建一个类
下面的代码创建了一个名为 Book 的类。
class Book(): def __init__(self, name, writer, word_length):
self.name = name
self.writer = writer
self.word_length = word_length
init 是一个特殊的函数,在创建类的实例时自动执行。它也被称为类构造函数。
init 函数的参数表示一个类的数据属性。因此,如果我们需要指定 name、writer 和 length 参数来创建 Book 的实例。
注 : Self 是指实例本身。你可以用任何词来代替“自我”,但使用“自我”是一种非常普遍的做法。
让我们创建一个实例。
b1 = Book("Pandas", "John Doe", 100000)print(type(b1))
<class '__main__.Book'>
b1 是属于 Book 类的对象。我们可以通过使用返回对象类型的类型函数来确认它。
我们可以使用下面的方法来访问或修改类的属性。
print(b1.name)
Pandasb1.name = 'NumPy' #updates the name attributeprint(b1.name)
NumPy
定义类方法
Book 类只有数据属性。我们应该添加方法(即过程属性)来使它变得有用和实用。
例如,我们可以实现一个方法,返回给定 fontsize 的页数。我们用字数来规定这本书的长度。该方法将根据长度和字体大小计算页数。
def number_of_pages(self, fontsize=12):
word_length = self.word_length
if fontsize == 12:
words_in_page = 300
else:
words_in_page = 300 - (fontsize - 12) * 10
return round(word_length / words_in_page)
我们在类定义中添加了页数。它根据字数和字体大小计算一本书的页数。
如果我们在类定义中声明的函数需要访问实例的数据属性,我们需要告诉函数如何访问它们。这就是我们在 number_of_pages 函数的第一行中所做的。
我们可以从类或实例中访问方法。这里有一个简单的例子演示了这两种方法。
b1 = Book("Pandas", "John Doe", 100000)b1.number_of_pages()
333Book.number_of_pages(b1)
333
number_of_pages 函数有一个附加的 fontsize 参数。因为指定了默认值(12),所以我们不必显式地写它。但是,我们可以为 fontsize 参数使用不同的值。
b1.number_of_pages(14)
357b1.number_of_pages(fontsize=16)
385
随着字体大小的增加,页数也会增加,这是有意义的。
为了使用 Python 的一些内置函数,我们需要为我们的类定义某些方法。考虑打印功能。
print(b1)
<__main__.Book object at 0x7fa4cf9f7588>
默认情况下,print 函数返回对象的类型和内存位置。但是,我们可以通过在我们的类中实现 str 方法来自定义它的行为。
def __str__(self):
return "<" + self.name + ", by " + self.writer + ">"
我们在上面的类定义中添加了 str 方法。下面是打印函数在我们的类中的工作方式:
print(b1)<Pandas, by John Doe>
类与实例变量
类变量是在类内部而不是在任何函数外部声明的。实例变量是在构造函数 init 方法中声明的。
类变量更加通用,可能适用于一个类的所有实例。另一方面,实例变量更加具体,并且为每个实例单独定义。区分类变量和实例变量非常有用。
考虑我们之前定义的 Book 类。我们经营一家出版公司,对我们出版的书籍有一些标准,如页面宽度和封面颜色。如果我们将它们定义为类变量,我们不必为每个创建的实例显式声明。
class Book(): page_width = 14
cover_color = "blue" def __init__(self, name, writer, word_length):
self.name = name
self.writer = writer
self.word_length = word_length
我们将 page_width 和 cover_color 实现为类变量,因为它们在类定义内,但在任何函数定义外。
让我们创建一个 Book 类的实例。
b2 = Book("Machine Learning", "Jane Doe", 120000)
创建此实例时,我们没有指定类变量。然而,b2 拥有这些变量,我们可以访问它们。
b2.page_width
14b2.cover_color
'blue'
我们可以选择更改特定实例的类变量。
b2.cover_color = 'red'b2.cover_color
'red'
特定实例上的更改对类变量没有任何影响。
Book.cover_color
'blue'
创建子类
我们可以基于不同的类创建一个类。让我们基于“Book”类创建一个名为“ColorBook”的类。
class ColorBook(Book):
ColorBook 是 Book 类的子类。当我们以这种方式创建一个类时,子类从父类复制属性(包括数据和过程属性)。这个概念叫做继承,继承使得 OOP 更加高效和强大。
类似于现实生活中的传承。我们的大部分基因组来自我们的父母或祖先。我们继承了他们。因此,我们和父母有相似之处。
除了从父类继承的属性之外,子类还可以有新的属性。此外,我们可以选择修改或覆盖继承的属性。
让我们定义 ColorBook 类的 init 函数。它将有两个附加参数,即“颜色”指示页面的颜色和“has_image”指示书中是否有图像。
class ColorBook(Book): def __init__(self, name, writer, word_length, color, has_image):
Book.__init__(self, name, writer, word_length
self.color = color
self.has_image = has_image
因为已经在 Book 类中定义了 name、writer 和 word_length,所以我们可以从它那里复制 init 方法。我们只需要定义额外的属性。
注:我们可以自由手工为子类定义每个数据属性。使用 parent 的 init 是可选的。
让我们创建一个 ColorBook 类的实例。
c1 = ColorBook("Seaborn", "John Doe", 90000, "green", True)c1.name
"Seaborn"c1.color
"green"
子类也继承了类变量。
c1.cover_color
"blue"c1.page_width
14
这些方法也是从父类中复制的。对于 Book 类,我们定义了两个方法,它们也可以用在 ColorBook 类的实例上。
c1.number_of_pages()
300print(c1)
<Seaborn, by John Doe>
我们可以选择覆盖从父类继承的数据和过程属性(即方法)。这使得继承更加强大,因为我们必须使用父类中的所有东西。
例如,我们可以修改 ColorBook 类的 str 方法。
def __str__(self):
return "<" + self.name + ", in " + self.color + ">"
print 函数将返回书籍的名称及其颜色。
c1 = ColorBook("Seaborn", "John Doe", 90000, "green", True)print(c1)
<Seaborn, in green>
以下是 Book 和 ColorBook 类的定义。

(图片由作者提供)

(图片由作者提供)
结论
我们在本文中讨论的内容可以被认为是对 Python 类的全面介绍。我们已经提到了类对于面向对象编程的重要性,以及类是如何展示抽象和继承等关键概念的。
对于实践方面,我们已经定义了两个类,并且看到了在创建子类时继承是如何工作的。
关于 Python 类还有很多内容要介绍。一旦你熟悉了基础知识,就可以随意进入更高级的话题了。
感谢您的阅读。如果您有任何反馈,请告诉我。
深度学习中正则化技术的综合指南
了解正则化如何有助于提高模型的性能

欠拟合与过拟合。作者插图。
当我开始我的数据科学道路时,我在处理我的第一个项目时遇到了很多困难。有一系列的步骤来解决这个问题,但我仍然没有一个清晰的概述,我需要大量的耐心和时间来理解关键概念。我学到的第一件事是,在应用任何模型之前,我必须将数据集分成两个独立的集,称为训练集和测试集。起初,我无法理解这一重要程序的真正原因,但在经历了不同的解决问题后,我开始理解这一步的意义。当我们建立一个模型时,我们应该验证这个要求:
一个模型必须能够很好地概括看不见的数据。
这意味着模型不仅要在训练集上做出好的预测,还要在测试集上做出好的预测。我们可能会遇到两种情况:
- 欠拟合:如果模型在训练集上没有做出很好的预测,因此获得很低的训练误差,我们就有欠拟合的问题。例如,我们用线性回归模型来拟合数据,但是该模型过于简单,并且无法捕捉数据中的复杂模式,因为存在违反的潜在假设。因此,如果模型过于简单,不能很好地从训练数据中学习模式,我们就会遇到这个问题。如果训练集太小,也会发生这种情况。
- 过度拟合:它与欠拟合相反,因为模型过于复杂,甚至会捕捉数据中的噪声。在这种情况下,我们会观察到培训和测试评估措施之间有很大的差距。
目标是达到最佳模型,这是欠拟合和过拟合之间平衡的结果。在这篇文章中,我将重点讨论过拟合问题,这个问题可以用几种正则化技术来解决。这些技术具有相关的作用,因为它们限制了模型的复杂性。下面我展示了帮助避免(或至少减少)过度拟合的最常用技术。
正则化技术:
- 提前停止
- L1 正规化
- L2 正规化
- 稀疏编码
- 脱落层
- 批次图层
1.提前停止

提前停车。作者插图。
早期停止是一种正则化形式,以避免在训练神经网络时过度拟合。在第一个时期,训练和测试误差都减少了。但是到了某个时候,培训损失会不断减少,而测试损失开始增加。在这一点上,我们需要使用早期停止来避免这种行为,这种行为会导致训练和测试评估之间的巨大差距。换句话说,当测试误差开始增加时,它停止模型的训练。
2.L1 正则化
L1 正则化、L2 正则化和稀疏编码都属于正则化范畴,称为模型正则化。在所有这些策略中,损失函数中增加了一项,对大型网络的权重施加惩罚,以减少过度拟合。取决于技术,一些权重参数可以被估计为等于零。
在 L1 正则化中,我们将权重参数的 L1 范数的缩放版本添加到损失函数中:

作者插图。
L1 范数只是参数绝对值的总和,而λ是正则化参数,它表示我们希望对权重参数进行多少惩罚。其范围在 0 和 1 之间。
3.L2 正则化
L2 正则化也称为权重衰减,其工作方式与 L1 正则化类似。它不是添加 L1 范数项,而是将权重的平方范数添加到损失函数中:

作者插图。
L2 范数是权重参数平方值的平方根。
4.稀疏编码
稀疏编码允许限制一次可以激活的单元总数。

作者插图。
乍一看,它似乎类似于 L1 正则化,因为其思想是计算绝对值的总和。与 L1 策略不同,我们感兴趣的是可以激活的单位的数量,而不是重量参数!
5.脱落层

辍学层。作者插图。
Dropout 的想法是在每个图形的处理过程中移除输入和隐藏单元。知道一层中的每个节点都完全连接到上面的层,我们在神经网络的训练期间随机地去除一些这样的连接。
值得注意的是,如果它与其他技术一起使用,比如批处理规范化,它可能会产生干扰。为此,最好使用这两种策略中的一种。
6.批量标准化
批次标准化的目标是防止批次获得不同的均值和不同的标准差[1]。诀窍在于在训练期间使用批次平均值和批次标准偏差来标准化每个激活值。一旦训练完成,在训练过程中计算的运行统计数据将用于测试。

正如您从算法 1 中可以注意到的,使层的激活正常化的主要步骤是:
- 计算每个激活 xᵢ的批次均值和批次方差,其中 m 为批次大小
- 使每个激活 xᵢ的平均值为 0,方差为 1,从而使其标准化。为了数值的稳定性,将ε加到批次方差中。
- 分别使用在训练期间学习到的参数γ和β来缩放和移动每个标准化激活。
- 将每个缩放和移位激活 yᵢ传递到下一层
使用这种正则化方法有两个主要优点。首先,成本函数将看起来更加平滑和平衡。因此,神经网络的训练将会更快。
最终想法:
在这篇文章中,我概述了防止过度拟合的最流行的方法。我希望你发现这篇文章有助于更完整地理解正则化在机器学习和深度学习模型中的作用。就个人而言,我发现它们在我的数据科学项目中非常有用。感谢阅读。祝您愉快!
更多相关文章:
https://pub.towardsai.net/k-fold-cross-validation-for-machine-learning-models-918f6ccfd6d https://pub.towardsai.net/understanding-optimization-algorithms-309d8065599d
参考文献:
你喜欢我的文章吗? 成为会员 每天无限获取数据科学新帖!这是一种间接的支持我的方式,不会给你带来任何额外的费用。如果您已经是会员, 订阅 每当我发布新的数据科学和 python 指南时,您都可以收到电子邮件!
分类项目综合指南
刚接触数据科学,想从事您的第一个项目?这里有一个关于你的第一个分类问题的全面的项目演练。
第一部分:数据清理和探索

简介
当我开始我的数据科学之旅时,我记得获得一个资源是多么困难,这个资源提供了一个全面的数据探索指南,将它与所使用的特性工程技术联系起来。总感觉两者之间有脱节。经过一番努力,我决定写一本指南来帮助初学者更好地理解数据探索,以及如何使用从他们的分析中获得的见解来改进他们的特征工程。
在这个项目中,我们将关注 Kaggle 上的葡萄酒质量数据集。该数据集包含两种葡萄酒变体,红葡萄酒和白葡萄酒,它们的物理化学特性(输入)和一个感官输出变量(质量)。我们将应用分类技术对数据进行建模。以下是我们将在本指南中涵盖的内容:
- 数据清理和探索
- 特征工程
- 数据建模和超参数调整
为了让这个指南更容易阅读,并且不会让你被太多的信息淹没,我会把它分成三个部分,如上所述。
我们开始吧!
数据清理
与所有的机器学习项目一样,我们将首先将所有相关的依赖项导入到项目中,并加载数据。
**# import necessary dependencies**
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
sns.set()%matplotlib inline
读入数据。
**# read in the data**
wine = pd.read_csv("./datasets/winequalityN.csv")**# check if data has been loaded correctly**
wine.head(5)

作者图片
看一眼数据,你就会意识到它可能有重复的观察。我们将不得不删除重复,因为这将增加我们的模型过度拟合的可能性。
保持这种想法!我们稍后再处理这个问题。
让我们检查一下数据的形状。这可让您了解数据中存在多少观测值和要素。
**# check the shape of the data**
wine.shape

作者图片
看我们的特征,只看到一堆对我们没有意义的数字。我们不知道这些特征意味着什么,它们之间有什么联系,或者它们对葡萄酒有什么影响。这就是领域知识的重要性。大多数时候,作为一名数据科学家,你要解决的问题是你不知道这个行业是如何运作的。没关系。这就是为什么数据科学家从不单独工作,而是让具有不同专业知识的人参与进来。领域知识,简单地说就是关于某个主题的知识,非常重要,因为它可以让你理解你正在处理的数据。
以下是对这些功能的简短描述:
- 固定酸度
这是葡萄酒中固定酸的含量。葡萄酒中主要的固定酸有酒石酸、苹果酸、柠檬酸和琥珀酸。固定酸被添加到葡萄酒中以稳定葡萄酒(防止微生物生长)并增加一些风味。
2.挥发性酸度
这些是葡萄酒中可蒸馏的酸。主要的挥发性酸是乙酸。挥发性酸度通常被用作葡萄酒腐败(葡萄酒中存在微生物)的指标。
3.柠檬酸
这是葡萄酒中的一种固定酸。它被用作葡萄酒的添加剂来增加酸味和防止铁雾。
4.残糖
这是发酵后剩下的糖,它已经将大部分糖转化为酒精。
5.氯化物
氯化物是葡萄酒咸味的主要来源。由于它们的加工过程,红葡萄酒通常比白葡萄酒具有更高的氯化物浓度。
6.游离二氧化硫
这是葡萄酒中不与其他分子结合的二氧化硫含量。向葡萄酒中添加二氧化硫有助于稳定葡萄酒,并延长葡萄酒的保质期。高浓度的二氧化硫可能会影响葡萄酒的味道。
7.二氧化硫总量
这是葡萄酒中二氧化硫的总量,游离二氧化硫加上任何与葡萄酒中其他分子结合的二氧化硫。
8.密度
这是酒的密度。
9.pH 值
这是葡萄酒的酸度或碱度水平。
10.硫酸盐
增加葡萄酒含硫量的葡萄酒添加剂。
11.酒精
这是葡萄酒中的酒精含量(以百分比表示)。
对这些特性有了一些了解后,让我们开始清理和探索。
我们要做的第一件事是检查任何空值(NaN ),并通过丢弃观察值或特征或输入数据来处理它们。
**# Check for null values**
total_nulls = wine.isnull().sum()
total_observations = wine.shape[0]null_percentages = total_nulls[total_nulls > 0] / total_observations * 100
total_nulls = total_nulls[total_nulls > 0]pd.concat([total_nulls, null_percentages], axis=1, keys=["Total", "Percentage"]).sort_values(by="Total", ascending=False)

作者图片
我们注意到数据中有一些空值,但是,它们只占总观测值的很小一部分(最大值约为 0.2%)。在这种情况下,输入缺失值是安全的。由于它们都是数值,我们将使用要素的中值进行插补,因为与平均值相比,中值受异常值的影响较小。
**# Define columns with null values (NaN)**
null_cols = ["fixed acidity", "pH", "volatile acidity", "sulphates", "citric acid", "residual sugar", "chlorides"]from sklearn.impute import SimpleImputer**# Fill nulls with the median of the column**
median_imputer = SimpleImputer(strategy="median")for col in null_cols:
wine[col] = median_imputer.fit_transform(wine[col].values.reshape(-1,1))
让我们检查并确认所有的空值都已被替换,并且数据中确实没有空值。
**# Check to ensure nulls were imputed**
wine.isnull().sum()

作者图片
记住我们一直保留的关于重复值的想法,现在是探索它的时候了。让我们检查数据中是否有重复的值。如果这样做,我们将保留第一个实例并删除任何其他实例。
**# Check for duplicates and keep the first instance**
duplicates_index = wine[wine.duplicated(keep="first")].index
duplicates_index

作者图片
**# Drop the duplicated indexes**
wine.drop(duplicates_index, axis=0, inplace=True)**# Check to confirm no duplicates**
wine[wine.duplicated(keep="first")]

作者图片
让我们看看数据的新形状。
**# Check the new shape of the data**
wine.shape

作者图片
似乎我们已经丢失了几个观察数据。没关系。这比复制数据要好,因为这会影响我们模型的性能和我们对数据的分析。
这就是我们的数据清理。
数据探索
我们的数据现已准备就绪,可供我们探索和获得见解。探索数据集时的一个关键点是要有一组您希望数据集回答的问题。你获得这些答案的方法将会影响你提取洞察力的技术选择和你所做的任何假设。
对于这个数据集,我们想要找出:
- 红葡萄酒和白葡萄酒的物理化学性质的平均组成是什么?
- 理化性质是如何相互联系的,它们最终是如何影响葡萄酒质量的?
- 考虑到高氯化物含量(咸味)和亚硫酸盐的负面影响,这些如何影响葡萄酒的质量?
- 游离二氧化硫占总二氧化硫的比例是多少,它如何影响其他物理化学性质,尤其是 pH 值、酸度和质量?
- 什么理化特性或它们的组合最能定义优质葡萄酒?
我们将从查看数据中要素的分布开始。
**# Look at the distribution of the data**
fig, ax = plt.subplots(figsize=(11, 11))
wine.hist(bins=50, ax=ax)

作者图片
一些特征,如固定酸度、挥发性酸度和残糖似乎有一个正确的偏差。这表明在特征的上限上存在异常值。由于一些值可能太小,很难在直方图上看到,我们将使用一些统计方法来帮助我们确定一些特征的偏斜度。
**# Look at the skewness of the data**
skew = wine.skew()
pd.DataFrame(data=skew, columns=["Skew"])

作者图片
正偏斜(> 0)表示要素中的右偏斜,负偏斜(< 0)表示左偏斜,偏斜为 0 表示无偏斜(即偏斜类似于高斯分布(正态分布))。从结果中,我们可以确认我们上面确定为偏斜的特征确实是右偏斜的。
当我们将数据传递到 ML 模型中时,我们将需要注意这一点。一些模型假设数据具有正态分布。转换数据以满足这一假设将提高模型的性能。把这个记下来。
知道了我们的特性是如何分布的,让我们看看这些特性是如何相互关联的。相关矩阵将允许我们理解不同特征之间的关系。我们将相关矩阵表示为热图,以减少显示过多数字带来的信息过载。热图让我们一眼就能发现任何有趣的关系。
**# Visualize the matrix on a heatmap**
mask = np.triu(wine.corr())
fig, ax = plt.subplots(figsize=(7,7))
sns.heatmap(data=wine.corr(), annot=True, fmt=".2f", ax=ax, mask=mask)

作者图片
热图的上部三角形被屏蔽,因为这将显示底部三角形的重复信息。最好专注于单一半,以便于分析。从一开始,我们就注意到一些有趣的关系,它们的颜色比其他的更亮。下面是一些关系和为什么会这样的描述。同样,领域知识对于帮助我们理解这些关系是必不可少的。
- 游离二氧化硫和总二氧化硫含量的增加导致挥发性和固定酸度的降低。
酸度越低,葡萄酒越不稳定。亚硫酸盐被添加到酸度较低的葡萄酒中,以帮助稳定它们,防止细菌生长,并延长保质期。没有添加亚硫酸盐的葡萄酒保质期较短,因此需要更高的酸度(更低的 pH 值)作为天然防腐剂。
- 游离二氧化硫和总二氧化硫量的增加导致残糖量的增加。
残余糖是发酵后剩下的东西。为了防止葡萄酒装瓶后糖的进一步发酵,添加亚硫酸盐以稳定葡萄酒并防止二次发酵,二次发酵可能导致瓶子爆炸。
- 固定酸度和挥发酸度越高,葡萄酒密度越高。
葡萄酒中最常见的固定酸是酒石酸、苹果酸和柠檬酸。乙酸是最普遍的挥发性酸。观察它们各自的密度,它们都比水的密度大,酒石酸的密度为 1.79 克/厘米,柠檬酸为 1.67 克/厘米,苹果酸为 1.61 克/厘米,乙酸为 1.05 克/厘米。酸的这种特性对葡萄酒的密度有积极的影响。
- 固定酸度和柠檬酸的增加导致 pH 值降低,而挥发性酸度的增加导致 pH 值升高。
向葡萄酒中添加固定酸(酒石酸、苹果酸和柠檬酸)以增加其酸度,即降低葡萄酒的 pH 值,这不仅有助于增加葡萄酒的风味,还有助于保存葡萄酒,因为大多数细菌无法在高酸性环境中存活。挥发性酸度是葡萄酒腐败的一个指标,这意味着葡萄酒中有细菌将一些固定酸(酒石酸和柠檬酸)和残余糖转化为乙酸。这些细菌在酸性较低(高于 3.6 的高 pH 值)的环境中生存,因此葡萄酒的 pH 值会增加。
- 酒精增加导致残糖减少。
残余的糖是发酵过程的副产品。这些是大多数糖被酵母转化成酒精(乙醇)后的残留物。在这个过程中转化的糖越多,酒的酒精含量就越高,因此很少有糖没有被转化。
- 酒精含量越高,葡萄酒的密度越低。
酒精(乙醇)的密度为 0.789 克/厘米。这使得酒精的密度小于密度为 1 克/厘米的水。葡萄酒中的酒精含量越高,那么葡萄酒就会越淡(密度越小)。
现在,我们已经了解了特性中存在的一些关系,让我们深入了解理化性质和感官输出(质量)之间的关系,这是我们的目标变量。我们将绘制一个条形图来显示与目标相关性最强的特征。
**# Bar plot to visualize what physicochemical properties correlate more with the quality**
fig, ax = plt.subplots(figsize=(9,5))
wine.corr()["quality"].sort_values(ascending=False).plot(kind="bar", ax=ax)

作者图片
忽略质量,因为它是特性与自身的相关性,酒精与葡萄酒质量的相关性最强。葡萄酒的酒精含量越高,就越有可能被评为优质葡萄酒。密度是第二个与质量相关性最强的因素。然而,密度是负相关的。酒的密度越小,它在质量上的排名就越高。
最后,我们来看看葡萄酒质量等级的分布。根据数据集的描述,数据集中的类是不平衡的。我们需要证实这一点,这样我们就可以找出如何处理不平衡,如果它存在。
**# Look at the distribution of classes i.e. quality in wine data** wine.groupby("quality")["quality"].count().plot(kind="bar")

作者图片
正如所描述的,等级严重失衡,一小部分葡萄酒质量极低或高。大多数葡萄酒质量一般。这种不平衡将显著影响分类模型的性能,因为从极端情况中可供模型学习的数据较少。大多数情况下,模型可能会预测大多数类别。仅预测多数类而完全忽略少数类的简单模型将获得更高的准确性,但它在预测少数类方面表现很差。这种不平衡的含义是,当选择我们的分类度量以及如何通过欠采样、过采样或宁滨类来减少类不平衡时,我们需要非常注意。
至此,我们的数据探索到此结束。需要注意的一点是,数据探索不是只做一次就忘了的。这是一个重复的过程,你应该将它整合到你的工作流程中。随着您转向特征工程和数据建模,您将始终了解关于您的数据的新见解,并且您应该深入了解当前的关系以及这对您的机器学习工作流意味着什么。
接下来,我们将看看你如何利用从探索中获得的洞察力来进行特征工程。请继续关注本系列的第 2 部分。
如果你喜欢这个帖子,并且想了解更多关于数据科学和机器学习的知识, 在 Medium 上关注我 。
在 VS 代码中调试 Python 脚本的综合指南

凯文·Ku 在 Unsplash上的照片
入门
了解如何在 10 分钟内高效调试您的脚本!
最常见的是,我们尽最大努力写出好的、干净的代码,这正是我们想要它做的。嗯……至少这是我的想法,老实说,大多数情况下,代码在第一次尝试时并不工作(或者根本不工作)。这就是调试发挥作用的时候了。调试是从任何种类的代码中检测并移除 bug(错误或意外行为)的过程。这一过程绝不仅限于数据科学,它仅仅是从软件工程中借鉴过来的。
许多数据科学家(尤其是那些刚刚开始冒险的科学家)主要使用 Jupyter 笔记本。不要误解我的意思,笔记本是数据科学家的好工具。如果你不相信我,你可以看看杰瑞米·霍华德在这个视频中的观点。
回到主要思想,数据科学家在他们工作的所有阶段都会遇到错误,从 EDA 和摆弄数据或笔记本中的一些初始建模到实际将一些模型投入生产。虽然我们可以用一种创造性的方式生产笔记本(例如,一个 cronjob 每天早上运行一个笔记本),但这绝对不是我们应该走的路,而且几乎违背了数据科学/软件工程工艺的所有最佳实践。相反,笔记本中的代码被重构并存储在一个或多个 Python 脚本中,然后被调度执行。整个“将某些东西投入生产”可以成为一系列文章或一本书的主题,所以我不会走这条路,至少现在不会。
那么,当我们在代码的某个地方有一个 bug 时,会发生什么呢?代码可能会返回一些东西(无论是我们想要的还是不要的),或者可能根本不工作,并在执行过程中抛出一些错误。在笔记本中,我们可以将代码分割成更多的单元,并依次执行它们来检查每一步。我们还可以在代码中放入许多打印语句,只是为了看看是否一切都按预期进行。或者,我们可以使用一些神奇的命令,比如%debug 来打开 Jupyter Notebook 中的交互式调试器窗口。
最后一种方法与我们调试 Python 脚本的方式非常相似。在本文中,我将展示如何使用和代码快速高效地调试 Python 脚本。最棒的是,您可以将这些方法应用于任何代码,无论是单个脚本还是整个库。
在 VS 代码中调试
基础知识
让我们开始调试吧。首先,我们需要写一些代码。您可以在下面看到的简单脚本将完成这项工作。当然,这是对您最有可能使用的脚本的极大简化,但对于演示来说已经足够好了。您还可以看到哪里出了问题,以及代码何时会直接抛出错误。
第一步只是打开 VS 代码,导航到我们想要设置项目的目录,创建一个新的脚本,并粘贴上面要点中的代码。您应该会看到类似这样的内容:

然后,我们可以在终端中运行脚本,方法是按下窗口右上角的绿色 play 按钮(或者右键单击编辑器窗格中的某个位置并选择“在终端中运行 Python 文件”)。我们可以在下面的截图中看到结果。很明显,有什么地方出错了(日志被覆盖了一点,所以有更多的回溯)。

我们可以试着从这里找出它,或者直接进入调试。为此,请按左边的“玩虫子”图标,您将看到以下屏幕。我们稍后将回到调试配置,但现在只需按“运行和调试”。

开始调试后,我们将看到下面的屏幕。

这里有相当多的东西是新的,所以我将通过指向屏幕的某些部分来提供一些清晰度:
Variables Pane —在这里我们可以很容易地检查运行脚本时创建的变量。我们可以看到所有变量及其当前值。因此,在运行我们的脚本的情况下,我们将看到数据帧df、multiplier_list、mult的当前值,等等。在这个窗格中,我们还将看到全局变量和局部变量之间的区别。后者将是在有限范围内可用的(例如,在函数中)。局部变量的一个例子是multiplier,因为它只在multiply_value()内部可用,在函数外部(全局)不可用。
观察窗格—我们可以使用这个窗格作为变量窗格的子集。可能我们的程序有几十或几百个变量,但我们只对其中的一部分感兴趣。然后,当我们将鼠标悬停在监视窗格上时,我们可以通过单击+图标将这些选定的变量添加到该窗格中。这样,我们可以很容易地监控他们的价值观。
调用堆栈窗格 —当我们正在调试的代码有很多内部方法时,这个窗格特别有用。在调用堆栈窗格的帮助下,我们可以在堆栈内部导航,并精确地识别错误来自哪个堆栈。
断点 —一般来说,断点是调试时一个非常重要的概念。当我们在没有指定任何断点的情况下运行脚本时,它将在导致错误的那一行停止(或者如果没有错误,则执行整个脚本)。然而,我们也可能希望早点停止程序,并检查变量在那个特定点的状态。为此,我们可以通过单击编辑器中行号的左侧来放置一个断点,创建的断点将由一个红点指示。当运行带有断点的脚本时,它将在每个断点处停止,并等待我们进一步的命令。在 VS 代码中也有一些不同种类的断点,但是我稍后会回到这个问题。
好吧,这已经很多了。尽管一开始看起来令人畏惧,但随着练习,一切都会变得清晰。现在,只需按绿色的 play 按钮开始调试(您可能需要在这里选择“Python: Current File”)。

我们可以看到,正如预期的那样,窗格中充满了信息。此外,错误会在屏幕上出现的地方弹出,并显示其类型和一些附加信息。这已经很有助于确定原因。让我们看看变量窗格,尤其是局部变量,因为错误发生在函数中。在这里,我们可以看到multiplier的值是“3”,它应该是一个数字,这样乘法才能工作。巨大的成功,尽管我们从一开始就知道。虽然在这种情况下非常简单,因为我们自己定义了列表,但是在更复杂的脚本中,很容易忘记每个变量存储了什么。尤其是当值是从某个地方(例如数据库)填充的时候。
在同一屏幕上,我们还可以看到一些新的东西,即调试工具栏。

它是主工具栏,当我们试图调试代码时,它允许我们沿着代码导航。图标代表以下操作:
- 继续/暂停—暂停或继续调试过程
- 单步执行—移到下一行代码
- 单步执行—在调试期间进入不同的方法内部
- 移出—移至父堆栈
- 重新启动—重新启动调试会话
- 停止—停止调试会话
最后要介绍的关键是调试控制台。在终端中,我们只能看到正在打印/记录的内容以及脚本中断时的错误消息,与之相反,在调试控制台中,我们可以交互工作并探索变量。每当调试器遇到断点或发生错误时,在调试控制台中,我们可以在该步骤执行命令,并使用变量的当前状态。在下图中,我们可以看到我们检查了变量mult的当前值,打印了变量df,并检查了我们是否可以将该列乘以 3。

当脚本中断,我们不完全确定发生了什么,以及我们提出的解决方案是否真的有效时,这个功能非常方便。例如,我们可以在调试控制台中运行以下命令:
df[“value”] * “3”
并观察我们之前遇到的相同错误。但是如果我们跑:
df[“value”] * 3
不会有错误,我们会得到我们真正想要的东西。
这应该足够开始调试你自己的脚本了。然而,我认为在调试一些更复杂的情况时,有两个主题非常值得了解和帮助。
断点的类型
VS 代码提供了三种类型的断点,每种都有不同的用途。要选择它们,我们首先创建一个普通断点,然后右键单击它并选择“编辑断点…”。下面,我描述每种断点的特征:
- 表达式 —当条件满足时,断点将触发并停止代码的执行。在下图中,我们将条件设置为
mult == 2。在“变量”窗格中,我们确实可以看到,当条件满足时,代码停止执行。此外,表达式断点的特点是红点中有“=”。

- 命中次数 —这种断点在被触发 X 次时停止代码的执行。例如,我们可以将这个断点放在 For 循环中,并将其值指定为 2。通过这样做,代码将在与上面的表达式断点完全相同的位置停止。
- 日志消息 —与前两种类型的断点不同,这种断点不会停止代码的执行。它可以用于在调试控制台中将一些消息打印到日志中。我们已经指定消息为
Current mult: {mult},它确实被打印在控制台中。请注意,要从代码(变量)中计算的表达式需要放在花括号中。正如我们所看到的,在预期的错误发生之前,代码的执行没有被中断。

最后要知道的是,你可以通过右击断点并选择“禁用断点”来临时禁用断点。
调试配置文件
调试配置在调试会话期间驱动 VS 代码的行为。配置是在一个launch.json文件中定义的,该文件存储在我们工作区的.vscode文件夹中。要访问 JSON 文件,我们可以在第一次打开 VS 代码中的调试窗口时单击“create a launch.json file ”,或者只需单击用于启动调试会话的绿色 play 按钮旁边的“gear”图标。
使用配置文件我们能做什么?一个常用的例子是设置环境变量。假设我们的代码是使用一些调度程序来部署和运行的,例如,Airflow(它远不止这些,但是现在让我们假设这样的简化是可以接受的)。然后,我们使用一组 env 变量来控制脚本的行为。例如,在 ETL 脚本中,我们可以使用 env 变量来控制是否要缩放特性。
当我们试图在本地调试代码时,我们的系统中不会有这些 env 变量,因为它们是由 Airflow 处理的。因此,一种选择是在全局范围内添加这些,但这可能会导致以后的混乱。或者,我们可以使用launch.json配置文件为调试环境提供一组 env 变量。
为此,我们按“齿轮”图标打开文件,并通过添加第 10 行的内容来修改它。
当我们运行下一个调试会话时,我们可以直接访问RUN_TYPE env 变量,例如,通过运行以下两行:
*import* osprint(os.environ['RUN_TYPE'])## prod
结论
在本文中,我展示了如何快速使用 VS 代码调试 Python 脚本。使用本文中提到的一些技术,您可以快速缩小潜在错误的来源,并在交互式调试控制台中尝试不同的修复方法。
我确信,这项技能非常重要,如果你作为一名数据科学家还没有遇到过调试脚本,那么在你的职业生涯中你会很快遇到的。虽然本文是针对 VS 代码的,但是大多数 ide(例如 PyCharm)都提供了类似的功能。
一如既往,我们欢迎任何建设性的反馈。你可以在推特或评论中联系我。
如果您喜欢这篇文章,您可能还会对以下内容感兴趣:
贝叶斯深度学习综合介绍
弥合基础研究和现代研究之间的差距。

科迪·希斯考克斯在 Unsplash 上拍摄的照片
**Table of Contents**1\. Preamble
2\. Neural Network Generalization
3\. Back to Basics: The Bayesian Approach
3.1 Frequentists
3.2 Bayesianists
3.3 Bayesian Inference and Marginalization
4\. How to Use a Posterior in Practice?
4.1 Maximum A Posteriori Estimation
4.2 Full Predictive Distribution
4.3 Approximate Predictive Distribution
5\. Bayesian Deep Learning
5.1 Recent Approaches to Bayesian Deep Learning
6\. Back to the Paper
6.1 Deep Ensembles are BMA
6.2 Combining Deep Ensembles With Bayesian Neural Networks
6.3 Neural Network Priors
6.4 Rethinking Generalization and Double Descent
7\. Final Words
1.序文
贝叶斯(深度)学习一直让我好奇,也让我害怕。也许是因为它严重依赖概率理论,这可能令人望而生畏。我注意到,即使我知道基本的概率论,我也很难理解并将其与现代贝叶斯深度学习研究联系起来。这篇博客的目的是弥合这一差距,并提供一个全面的介绍。
我不会从基础开始,我会从安德鲁·威尔逊和帕维尔·伊兹迈洛夫(NYU)关于贝叶斯深度学习和推广的令人难以置信的 NeurIPS 2020 论文开始,名为贝叶斯深度学习和推广的概率视角。这篇论文作为一个有形的起点,我们自然会在野外遇到贝叶斯概念。我希望这能使贝叶斯观点更加具体,并说明它的相关性。
我将从论文摘要和引言开始,为文章做准备。当我们遇到贝叶斯概念时,我将从概率以及 ML/函数近似的角度,用大量的直觉给出一个全面的概述。最后,在整篇文章中,我将回到这篇文章并与它联系起来。
我希望你们离开时,不仅能感受到至少一点贝叶斯理论,还能理解这篇论文的众多贡献,以及总体概括;)
2.神经网络泛化(摘要和简介)
如果你的贝叶斯理论有点生疏,那么摘要可能看起来相当神秘。前两句话对我们一般理解贝叶斯 DL 特别重要。中间部分介绍了三个技术贡献。最后两个突出的句子提供了对神秘的神经网络现象的新见解的入门。我将涵盖所有内容,但首先要做的是:论文的导言。

安德鲁·威尔逊和帕维尔·伊兹迈洛夫(NYU)的贝叶斯深度学习摘要和泛化的概率观点
引言中的一个重要问题是神经网络如何以及为什么会泛化。作者认为
“从概率的角度来看,泛化很大程度上取决于两个属性,模型的支持和归纳偏差。”
支持是一个模型可以支持的数据集类的 范围 。换句话说;模型可以表示的函数范围,其中函数试图表示数据生成过程。归纳偏差定义了模型类适合特定数据集类(如图像、文本、数字特征)的程度。作者称之为“支持的分配”。换句话说,模型类性能(~归纳偏差)分布在所有可能的数据集范围内(支持)。
让我们看看作者提供的例子。线性函数具有截断支持,因为它甚至不能表示二次函数。MLP 非常灵活,但它在数据集之间的支持分布过于均匀,因此对于许多影像数据集来说并不令人感兴趣。卷积神经网络在图像识别的支持和归纳偏差之间表现出良好的平衡。图 2a 很好地说明了这一点。

几种模型类型的支持及其分布(感应偏差)。威尔森等人(2020 年)图 2a
纵轴代表我天真地解释为“模型在拟合特定数据集方面有多好”。它实际上是贝叶斯证据,或边际可能性;我们的第一个贝叶斯概念!我们将在下一节深入探讨这个问题。先把思路说完。
一个好的模型不仅需要一个大的支持来代表真实的解决方案,还需要正确的感应偏置来实现解决方案。贝叶斯后验,现在把它当作我们的模型,由于正确的归纳偏差,应该收缩到正确的解。然而,先验假设空间应该足够宽,使得真实模型在功能上是可能的(广泛支持)。下图展示了这三个示例模型。从左至右,我们看到 CNN 为绿色,线性函数为紫色,MLP 为粉红色。

具有不同支持和归纳偏差的模型类型的先验、后验和真实模型之间的关系。有线电视新闻网(b),MLP ( c)和线性模型(d)。威尔森等人(2020)图 2
在引言的这一点上,与摘要的第一句相似,作者强调
“贝叶斯方法的关键区别属性是边缘化而不是优化,其中我们表示通过后验概率加权的所有参数设置给出的解决方案,而不是将一切都押在单个参数设置上。”
探究边缘化与最优化的时机已经成熟,并且拓宽了我们对贝叶斯方法的一般理解。我们将触及诸如后验、先验和预测分布、边际可能性和贝叶斯证据、贝叶斯模型平均、贝叶斯推断等术语。
3 回归基础:贝叶斯方法
我们可以在任何地方发现边缘化是贝叶斯统计的核心。甚至在毕晓普的 ML 圣经模式识别和机器学习中。与贝叶斯观点相反的是频率主义观点。这是你在大多数机器学习文献中遇到的情况。也更容易把握。让我们从那里开始。
3.1 常客
机器学习的常用方法是优化损失函数,以获得模型参数的最佳设置。一个示例损失函数是交叉熵,用于诸如对象检测或机器翻译的分类任务。最常用的优化技术是(随机)梯度下降的变体。在 SGD 中,模型参数沿着损失空间中最陡下降的方向迭代更新。这个方向由损耗相对于参数的梯度决定。期望的结果是,对于相同或相似的输入,这个新的参数设置使得输出更接近地代表目标值。在神经网络的情况下,梯度通常使用称为反向传播的计算技巧来计算。

使用梯度下降沿最陡下降方向导航损失空间。阿米尼等人(2017)图 2 。
从概率的角度来看,常客们正试图最大化T2 概率 p(D|w,M}) 。简单地说:选择我们的参数 w ,使它们在给定我们选择的模型 M 的情况下最大化观察数据集 D 的概率(Bishop,第 1.2.3 章)。为了简单起见,M 经常被省略。从概率的角度来看,(统计)模型只是数据 D 的概率分布(Bishop,第 3.4 章)。比如说;语言模型输出词汇表的分布,表明每个单词成为下一个单词的可能性。事实证明,这种最常见的最大似然估计 ( MLE )获得或“训练”预测模型的方式可以从一个更大的贝叶斯语境中来看待。事实上,MLE 可以被认为是使用均匀先验的最大后验估计(MAP,我将很快讨论)的一个特例。
3.2 贝叶斯主义者
贝叶斯方法的一个重要特性是现实地量化不确定性(T21)。这在要求我们相信模型预测的真实世界应用中是至关重要的。因此,贝叶斯方法定义了参数的全概率分布,而不是参数点估计。我们称之为后验分布。后验概率代表我们对每个参数(设置)值的信念/假设/不确定性。我们使用贝叶斯定理计算后验概率。这个定理是贝叶斯 ML 的核心——因此得名——并且可以使用简单的概率规则导出。

我们首先在参数上指定一个先验分布 p(w) 来捕捉我们的信念关于我们的模型参数在观察任何数据之前应该看起来像什么。
然后,使用我们的数据集,我们可以用可能性 p(D|w) 更新(乘以)我们先前的信念。这种可能性与我们在频率主义方法中看到的数量相同。它告诉我们通过特定的参数设置 w 可以很好地解释观察到的数据。换句话说;我们的模型在拟合或生成数据集方面有多好。可能性是我们的参数 w 的函数。
然而,为了获得有效的后验概率分布,必须对每个参数设置评估可能性和先验之间的乘积,并进行归一化。这意味着将(求和或积分)边缘化于所有的参数设置。归一化常数称为贝叶斯(模型)证据或边际似然 p(D) 。
这些名称非常直观,因为 p(D) 提供了证据来证明我们的模型(即数据)总体上有多好。“整体模型”是指考虑所有可能的参数设置。换句话说:边缘化他们。我们有时会在证据中明确包含模型选择M为 p(D|M) 。这使我们能够用不同的参数空间来比较不同的模型。事实上,当比较 CNN、MLP 和线性模型之间的支持和归纳偏差时,这种比较正是论文中所发生的!
3.3 贝叶斯推理和边缘化
我们现在已经到达了问题的核心。贝叶斯推断是在 w 上寻找(推断)后验分布的学习过程。这与试图通过微分找到最优的 T31、T32、T33 和 T34 形成了对比,微分是常客的学习过程。
正如我们现在所知道的,为了计算完整的后验概率,我们必须在整个参数空间内将边缘化。在实践中,这通常是不可能的(难以处理的),因为我们可以有无限多的这样的设置。这就是为什么贝叶斯方法基本上是关于边缘化而不是最优化的原因。
后验中难以处理的积分导致了学习参数值的不同方法族。代替梯度下降,贝叶斯主义者经常使用抽样方法,如马尔可夫链蒙特卡罗(MCMC),或变分推断;尝试使用更简单、更易处理的分布族来模拟后验概率的技术。类似的技术通常用于生成模型,如 VAEs。一种相对较新的近似复杂分布的方法是标准化流量。
4.实践中如何使用后路?
现在我们已经了解了贝叶斯后验分布,我们实际上如何在实践中使用它呢?如果我们想预测下一个单词,比如说,给定一个看不见的句子,我们称之为yx 呢?
4.1 最大后验概率估计
嗯,我们可以简单地对我们的模型 M 的参数取后验分布,并选择分配给它的概率最高的参数设置 w^ (分布的模式)。这种方法被称为最大后验概率或图估计。但是……如果只是为了满足于另一个点估计而计算我们参数的适当概率分布,这将是相当浪费的,对吗?(除了当几乎所有的后验质量都以参数空间中的一个点为中心时)。因为 MAP 提供了一个点估计,所以它不被认为是完全贝叶斯处理。

最大后验概率估计;不是完全的贝叶斯治疗。英雄等人(2008)
4.2 完全预测分布
成熟的贝叶斯方法是指定一个预测分布
p(y|D,x) 。

这定义了给定新输入 x 和数据集 D 时分类标签 y 的概率。为了计算预测分布,我们需要再次忽略我们的参数设置!我们使用参数设置 w 将每个设置 w 的后验概率乘以给定输入 x 的标签 w 的概率。这被称为贝叶斯模型平均,或 BMA,我们对所有可能的模型(本例中的参数设置)进行加权平均。预测分布是贝叶斯 ML 中边缘化的第二个重要位置,第一个是后验计算本身。直观显示预测分布的方法是简单的回归任务,如下图所示。具体示例请查看这些幻灯片(幻灯片 9–21)。

简单回归任务的预测分布。观察数据点周围的高确定性;其他地方的高度不确定性。亚林加尔(2015) ))
4.3 近似预测分布
正如我们现在所知道的,预测分布中的积分通常是难以处理的,并且至少在计算上是极其昂贵的。使用后验概率的第三种方法是对几个参数设置进行采样,并组合得到的模型(例如近似 BMA)。这实际上被称为预测分布的蒙特卡罗近似!
这最后一种方法隐约让人想起谦逊的常客可能更熟悉的东西:深度合奏。深度集成是通过组合结构上相同但用不同参数初始化训练的神经网络形成的。这与我们在论文中离开的地方非常吻合!记得摘要吗?
“我们表明,深度集成为近似贝叶斯边缘化提供了一种有效的机制,并提出了一种相关的方法,通过在吸引盆地内边缘化来进一步改善预测分布”。
第二次阅读摘要时,投稿应该更有意义。此外,我们现在终于转向贝叶斯深度学习领域!
5.贝叶斯深度学习
贝叶斯神经网络(BNN)是应用于神经网络架构的简单的后验推理。准确地说,为每个权重和偏差指定一个先验分布。然而,由于它们巨大的参数空间,推断后验概率比通常更困难。
那么,为什么贝叶斯 DL 呢?
经典答案是获得不确定性的现实表达或校准。如果类别预测的概率(置信度)与其误分类率一致,则认为分类器已校准。如前所述,这在实际应用中至关重要。
"从神经网络的预测通常过于自信的意义上来说,神经网络经常被错误校准."
然而,我们连续论文的作者,威尔逊和伊兹迈洛夫,认为贝叶斯模型平均也增加了准确性。根据第 3.1 节,贝叶斯观点事实上对神经网络来说尤其是有吸引力!由于它们的大参数空间,神经网络可以表示许多不同的解决方案,例如,它们不被数据指定。这意味着贝叶斯模型平均值非常有用,因为它将各种功能形式或“视角”结合为一。
“神经网络可以代表许多与我们的观察一致的模型。通过只选择一个,在一个经典的程序中,当模型对于一个测试点不一致时,我们失去了不确定性。”
5.1(近似)贝叶斯深度学习的最新方法
许多人最近一直试图将传统神经网络的优点(例如,使用 SGD 和反向传播的计算高效的训练)与贝叶斯方法的优点(例如,校准)相结合。
蒙特卡洛辍学
一种流行且概念简单的方法是蒙特卡洛辍学。回想一下,辍学传统上被用作正规化;它通过在训练期间随机关闭权重来提供神经网络中的随机性或变化。事实证明,辍学可以被重新解释为近似贝叶斯推断,并在测试期间应用,这导致了多个不同的参数设置。听起来有点类似于从后验采样参数来近似预测分布,mh?

原始退出机制。斯里瓦斯塔瓦等人(2014 年)
随机加权平均—高斯(SWAG)
另一条工作路线来自于随机加权平均 (SWA),这是一种优雅的近似集合,它智能地组合同一网络在不同训练阶段的权重(如果你想了解更多,请查看这个或这个博客)。 SWA-Gaussian (SWAG)通过使用 SGD 提供的简单信息来近似后验分布的形状(局部几何形状)来构建它。回想一下,SGD 在参数空间中“移动”,寻找损失空间中的(局部)最优值。为了逼近后部的局部几何形状,他们将高斯分布拟合到 SGD 迭代的第一和第二时刻。矩描述函数或分布的形状,其中零矩是总和,一阶矩是均值,二阶矩是方差。然后,这些拟合的高斯分布可以用于 BMA。
不确定性表征的频率主义替代方案
我显然没有在这里提到至少 99%的领域(例如 KFAC 拉普拉斯和温度缩放用于改进校准),选择上面的例子部分是因为它们与我们的运行文件相关。我将以最近的常客(或者它是……)替代不确定性近似的最后一个例子来结束。这是一种流行的方法,表明人们可以训练深度集成,并使用它来形成预测分布,从而产生校准良好的模型。他们使用了一些我不会深入讨论的附加功能,比如对抗训练来平滑预测分布。点击查看论文。
6.回到报纸上
到现在为止,我们已经准备好回到这篇论文,回顾它的贡献了!他们应该更容易掌握:)
6.1 深度系综是 BMA
与最近的文献(包括我自己)的框架相反,威尔逊和伊兹迈洛夫认为,深度集合不是获取贝叶斯优势的频繁主义者的替代方案。事实上,它们是后验分布的一个很好的近似。因为深度系综是通过 MAP 或者 MLE 再训练形成的,所以可以形成不同的吸引盆地。吸引盆地是损失景观中的一个“盆地”或山谷,它导致一些(局部)最优解。但是,在损失图景中,可能会有,而且通常会有多个最优解或低谷。由系综的不同部分发现的多个吸引域的使用,比集中于在单个吸引域内近似后验的贝叶斯方法产生更多的功能多样性。
6.2 将深度集成与贝叶斯神经网络相结合(第 4 节)
这种使用多个吸引域的想法对下一个贡献也很重要:一种近似预测分布的改进方法。通过将深度系综所具有的多吸引域属性与 SWAG 中的贝叶斯处理相结合,作者提出了一个两全其美的解决方案:
MultiSWAG 结合了多个独立训练的 SWAG 近似,以创建后验高斯近似的混合,每个高斯集中在不同的流域。我们注意到,与标准深度训练相比,MultiSWAG 不需要任何额外的训练时间。”
如果你对具体细节感兴趣,可以看看这篇论文;)
6.3 神经网络先验(第 5 节)
我听到你问,我们如何在数百万个参数中指定一个有意义的先验?事实证明这是一个非常合理的问题。事实上,贝叶斯方法有时会因此受到批评。
然而,在论文的第 5 节中,威尔逊和伊兹迈洛夫提供了证据,表明指定一个模糊的先验,比如简单的高斯分布,实际上可能并不是一个坏主意。
“当与神经网络架构结合时,参数的模糊高斯先验在函数上诱导出具有有用的诱导偏差的分布。”…
…“函数上的分布控制模型的泛化属性;孤立地看,先验参数没有任何意义。
与神经网络的函数形式相结合的模糊先验导致函数空间中有意义的分布。先验本身并不重要,但它对最终预测分布的影响很重要。
6.4 重新思考一般化和双重下降(第 6 和第 7 节)
我们现在已经到达了我在摘要中强调的奇怪的神经网络现象。根据第 6 节,神经网络可以适应随机标签这一令人惊讶的事实实际上一点也不令人惊讶。如果你从支持和归纳偏差的角度来看,就不会了。广泛的支持,数据集的范围对于泛化很重要,T2 p(D | M)>0。事实上,只要我们有正确的归纳偏差,将模型导向一个好的解决方案,拟合随机标签的能力是非常好的。威尔逊和伊兹迈洛夫还表明,这种现象并不神秘地特定于神经网络,高斯过程也表现出同样的能力。
双重下降
第二个现象是双重下降。双重下降是最近发现的现象,更大的模型和更多的数据会出乎意料地降低性能。

图摘自这篇公开的解释深度双重下降的博文
威尔逊和伊兹迈洛夫发现,用 SGD 训练的模型会遭受双重下降,但 SWAG 减轻了这种情况。更重要的是,多重摇摆和深度合奏完全减轻了双重下降现象!这与他们之前讨论的主张一致
"深度集成提供了一个比传统的单盆地贝叶斯边缘化过程更好的近似贝叶斯预测分布."
并强调了边缘化对后发多模式的重要性。
最后的话
你成功了!感谢您从头到尾的阅读。这篇文章变得相当长,但我希望你学到了很多关于贝叶斯 DL。我确实做了。
请注意,我不属于威尔逊,伊兹迈洛夫或他们在 NYU 的小组。这篇文章反映了我自己对他们工作的理解,除了直接从论文中引用的部分。
请随意提问或指出我无疑犯过的错误。我也想知道你是否喜欢这篇文章。你可以在我的网站上找到我的联系方式,在推特上给我发消息,或者在 LinkedIn 上联系我。查看我在 jorisbaan.nl/posts的个人博客获得正确的数学渲染!
一个全面的成熟技术列表,解决您的人工智能之旅中的数据稀缺问题

简·安东宁·科拉尔在 Unsplash 上拍摄的背景照片
机器学习、深度学习、数据科学
当你发现自己处于棘手的数据情况时该怎么办
总的来说,我们知道机器学习特别是深度学习需要大数据才能很好地工作。虽然没有人能明确回答需要多大的数据,但它是针对当前问题的。此外,除了 FAANG(脸书、苹果、亚马逊、网飞和谷歌),大多数其他公司都无法访问这种大数据。
我们为什么需要大数据?
或者说,没有大数据会怎么样?
过度拟合。
如你所知,过度拟合是机器学习中众所周知的现象。当数据不够大时,机器学习模型会过度拟合,在现实世界中不能很好地概括。对于非 ML 的人,让我们试着获得一些关于过度拟合的直觉。
假设你正在准备考试。你拿了一本漂亮的小指南或书,通过记忆书中的每个单词来训练自己应付考试。你有信心你的答案会 100%准确。然而,在考试中,问题是基于学科知识的应用,而不仅仅是理论。在这种情况下,你 100%准确的机会大大减少。
类似地,ML 模型通过记忆小的训练数据而不学习底层模式来过度拟合训练数据,尤其是像神经网络这样的复杂模型。在这种情况下,该模型在训练数据上表现得非常好,但在测试数据或现实世界中却失败得很惨。
另一个重要的考虑是,在小数据集中发现异常值(显著不同的观察值)和检测噪声(随机机会)具有挑战性。同样,这些可能会对您的 ML 模型的性能产生负面影响。
现在我们已经了解了没有大数据的陷阱,让我们试着将自己置于各种 数据环境 中,看看我们能做些什么。我们将涉及许多不同的工具和技术。其中一些技术本身就是很大的话题。目的不是深入研究,而是对给定的技术有一个简单的直觉。
在本文中,我们将查看以下 数据情况 并讨论可能的解决方案。
- 小数据
- 尚无数据
- 稀有数据
- 昂贵的数据
- 不平衡数据
1.小数据
您只处理了几百或几千个例子。
同样,这是具体到手头的问题,但让我们考虑在这里讨论这个数字。因此,让我们来看看当您处理小数据时可以做些什么。
1.1 数据扩充
数据扩充是指通过向数据中添加变量来增加数据点的数量。这种技术可以防止过度拟合,并帮助您的模型更好地泛化。
对于图像数据,可以通过以下方式进行数据扩充
- 修改照明条件
- 随机种植
- 水平翻转
- 应用变换,如平移、旋转或剪切
- 放大,缩小

图像数据扩充(作者提供的图像)
您还可以通过以下方式将数据扩充应用于文本数据
- 回译
- 同义词替换
- 随机插入、交换和删除

反向翻译—文本数据扩充(图片由作者提供)
除此之外,还有为数据集学习最佳数据扩充策略的技术,称为自动扩充。自动增强使用搜索算法寻找最佳策略,使神经网络在目标数据集上产生最高的验证准确性。
1.2 迁移学习
迁移学习是指将知识从数据量巨大的源领域转移到数据量很少的目标领域。例如,在计算机视觉领域中的问题的情况下,像边缘、形状、拐角和强度这样的低级特征可以跨任务共享,从而实现任务间的知识转移。
迁移学习的灵感来自于我们跨任务迁移知识的内在能力。我们在完成一项任务时学到的东西,会以某种方式应用到相关的任务中。

迁移学习(图片由作者提供)
迁移学习成功地应用于图像、文本和音频/语音数据,它本身就是一个大话题。当你处理小数据时,这是一个有用的工具。
1.3 问题减少
减少问题是指将你的问题分解成子问题,并使用已知和已建立的技术解决这些问题。可能是将数据修改为不同的格式,或者将未知问题转换为已知问题。
我们试着用例子来理解一下。
这里显示的例子是音频分类问题。但是数据集太小,无法训练适合音频分类的新的端到端 ML 模型。相反,我们可以通过从音频数据中创建声谱图图像来将此简化为计算机视觉问题。然后,我们可以使用基于最先进的图像分类模型的迁移学习来获得非常高的分类精度。

音频到图像(作者提供的图像)
1.4 建模技术
当您处理少量数据时,在构建模型时,您应该记住以下一些技巧。

xkcd.com—机器学习
- 与神经网络等复杂的黑盒模型相比,使用线性、更简单、可解释的模型(如线性/逻辑回归、朴素贝叶斯、统计)。
- 使用正则化像拉索(L1)、山脊(L2) &弹性网回归。
- 使用集合或模型平均。
- 使用领域专家来执行显式特征工程(特征选择、特征生成和特征消除),而不是依赖自动特征发现。
1.5 合成数据
合成数据是假数据。它符合与其真实对应物相同的模式和统计特性。它具有固有的优势,如
- 隐私和数据保护合规。
- 这是比匿名化数据更好的选择,如果做得不好,就无法保护隐私,如果做得过了头,就会破坏重要信息。
有多种技术可以生成合成数据,例如
1.5.1 合成少数过采样技术(SMOTE & M-SMOTE)

不平衡的数据集(图片由作者提供)
SMOTE 通常用于处理不平衡数据(类别分布不均)的场景。它获取少数类数据点,并在特征空间中由直线连接的任意两个最近的数据点之间创建新的数据点。 M-SMOTE 是几个修改过的 SMOTE 变体之一,它也考虑了少数类的底层数据分布。
1.5.2 生成性对抗网络(GAN)
GANs 可能是市场化程度最高的神经网络。如果训练有素,它们非常擅长生成逼真的合成数据。下图中的人是不存在的。这些图片是用 deepfake 技术生成的。但是你可以想象 GANs 在生成合成数据方面有多好。

下面你可以看到 GANs 的基本架构。发生器和鉴别器是一个典型 GAN 的基本组成部分。生成器的工作是生成假图像,鉴别器检查生成器生成的图像看起来是否和真实图像一样。在训练过程中,生成器非常擅长生成伪造图像,以至于鉴别器无法区分真假数据。

生成性对抗网络(作者图片)
1.5.3 模拟器
模拟器通常用于强化学习环境,在这种环境中,真实世界的模型训练是复杂、昂贵或危险的。
例如,自动驾驶模型在进入真实世界之前,首先在模拟环境中接受训练。

因此,我们研究了处理小数据时的各种选择。我们的下一个数据情况是我们中相当多的人更经常面对的。
2.尚无数据
你有一个重要的问题要解决,但没有任何代表性的数据。
这种情况在由于隐私/保密性/敏感性原因而难以获得数据的行业中很常见。
那么我们有什么选择呢?
2.1 开源数据集
开源数据集特别有用。尤其是在视觉和语言领域的开源数据集之上训练的模型,概括得出奇地好。

谷歌数据集搜索(图片由作者提供)
有相当多的资源可以查找这样的数据集
2.2 在线/增量/流学习
通常,我们以离线方式构建 ML 模型。我们离线获取所有数据,并使用批量数据训练模型。在线学习是从每个数据实例中学习。
在线学习对于在相当多的场景中构建 ML 模型是有用的,其中
- 由于隐私/保密性/敏感性原因,数据难以获取。
- 由于网络障碍,设备必须脱机工作。
- 在给定的时间点,数据集太大而不适合内存。
- 数据的潜在本质导致频繁的数据/概念漂移。
话虽如此,在线学习的实施和监控非常复杂(尤其是在内部环境中),并且需要大量的工程工作。
2.3 联合学习
联邦学习是机器学习中最令人兴奋的领域之一。这是一种在分散数据上训练集中模型的方法。这项技术在医疗和金融领域已经很普遍,由于敏感性和隐私问题,这些领域的数据很难获得。这里的中心思想是,如果你不能向模型发送数据,为什么不把你的(机器学习)模型发送给数据呢?

联合学习(作者图片)
谷歌在 2016 年首次在 Android 上的 Gboard 应用的背景下引入了联邦学习。 Gboard 是一款安卓键盘应用,可以在你打字时预测下一个单词。由于谷歌不可能将用户正在键入的内容发送到他们的服务器,他们将机器学习模型推向数百万移动设备。当移动设备处于空闲模式时,使用本地用户数据在移动设备上完成训练。来自数百万移动设备的学习到的模型权重被推送到联合服务器并被聚集以创建新的全局模型。这个全局模型现在成为新的基础模型,并被推回到所有移动设备。你可以看到,这个新的全球模型现在已经从数百万用户的数据中学习到,而无需共享他们的机密数据。
2.4 安全和私人学习
安全和隐私学习是与机器学习的安全和隐私保护方式相关的各种技术的总称。这些可以与我们上面讨论的联合学习结合使用。前面我提到过,联邦服务器聚合了从数百万移动设备接收到的所有模型权重。
安全聚合用于执行此类聚合。在这里,服务器看不到根据单个数据训练的模型权重,因为模型权重可以反向工程回单个用户数据。安全聚合是一种组合加密结果并仅对聚合进行解密的技术。
多方计算是另一种通过与多方共享数据来执行计算的方法。但任何一方都无法获得原始数据;他们所拥有的只是原始数据中属于他们的部分。
差分隐私通过保证定义的隐私级别,正在迅速成为执行分析和机器学习的首选技术。这比仅仅匿名化数据更可靠。
同态加密是对加密数据进行运算的技术。数据所有者将数据以加密形式发送到服务器,在服务器上对加密数据进行训练或推断。加密的结果被发送回数据所有者,而数据所有者只能对结果进行解密。
机密计算是云服务提供商提供的硬件/软件服务的组合。对于机密计算,数据在静态和传输过程中会被加密,但在使用或处理过程中也会被加密。机密计算环境将数据加密保存在内存和中央处理器(CPU)之外的其他地方。
2.5 战略
我不是这方面的专家,但这些是人工智能领域的初创公司用来获取数据的策略。
- 众包:使用领域专家手动收集和标记数据,或者使用外部平台,如亚马逊机械土耳其或阿彭。
- 用户参与循环:这种方法包括设计一种产品,为用户提供明确的激励,让用户将数据反馈给系统或纠正机器错误。reCAPTCHA 是这种策略的一个众所周知的例子。为了登录应用程序并证明你不是机器人,我们最终标记了一组相当复杂的图像,可能大多数人甚至没有意识到他们正在免费标记数据。

reCAPTCHA(作者图片)
- 提供免费的特定领域云/移动应用:这是有用的,但也有风险,因为开发和推广一款应用程序可能需要大量的时间和精力,该应用程序具有足够强大的用例来迫使用户放弃他们的数据。
- 专家(基于规则的)系统:您可能会得出结论,构建一个数据驱动的解决方案在这一点上是不可能的。相反,使用基于规则的方法构建自动化,并在用户使用这样的系统时收集数据。
- 许可来自第三方数据提供商的数据
- 与大型企业客户合作
- 收购获取数据
3.稀有数据
你的问题包括处理很少发生的现象的数据,例如,工厂中罕见但昂贵的故障事件。
3.1 少量学习
少镜头学习现在吸引了很多注意力,因为它能够从很少的例子中学习和归纳。很少,我指的是两到五个例子。
这个想法的灵感来自于我们从很少的经验中总结的能力。根据示例的数量,它也被称为一次学习、零次学习或通常的 N 次学习。

从很少的经历中学习(作者图片)
让我们考虑一个标准设置中的分类问题。假设您想要构建一个分类器来对狗和猫进行分类。现在,在这种情况下,你的机器学习模型将学习猫和狗特有的特征,在预测时,当你给它一个猫或狗的新图像时,决定将基于它对特定对象的学习。在少量学习的情况下,模型学习如何区分类,而不是学习每个类的单独特征。一般来说,模型学习两个对象如何相似或不同。
少量学习是一个很大的话题。有各种各样的方法(基于增强、基于模型、基于度量、基于优化、基于生成建模等)。)解决少拍学习问题。在处理稀有数据甚至是看不见的数据时,这是一个有用的选择。
4.昂贵的数据
您拥有解决问题的数据,但是标注复杂、昂贵或耗时。

xkcd.com—神经网络
我们知道,最近机器学习的成功主要是由监督学习推动的。监督学习需要高质量的标记数据才能很好地工作。因此,让我们看看在处理这个瓶颈时我们有什么选择。
4.1 自我监督学习
自我监督学习就是通过创造性地利用数据的属性来建立一个假的监督任务,从而绕过人类的标记。如果我们以这样一种方式来表述问题,即我们可以从现有数据中生成几乎无限的标签,并使用这些标签来学习表示,会怎么样?
考虑这个例子。假设您想要对图像数据执行分类。你可以接触到大量的数据,但你只能给少数样本贴标签。你可以通过旋转你的无标签图像 0 度,90 度,180 度,270 度等角度来生成伪标签。,并训练模型来预测这个旋转角度。在学习对旋转角度进行分类的同时,模型学习图像数据的一些重要方面。然后,这种模型可以用于迁移学习,并在小的标记数据上进行微调。

自我监督学习(图片由作者提供)
您还可以通过学习从彩色图像创建灰度图像,或学习从正常图像创建高分辨率图像,或学习通过将裁剪和固定的图像配对来完成图像的缺失部分,来表述该问题。该模型必须了解图像中存在的不同对象和相关部分,以解决这些任务。因此,学习到的表征对下游任务是有用的。
不仅仅是计算机视觉,最近 NLP 的进步也是由自我监督驱动的。像 BERT 这样的最新语言模型是使用自我监督学习方法训练的。
4.2 半监督学习
半监督学习或 SSL 是关于从小的标记数据集和相对较大的未标记数据中学习。SSL 利用未标记的数据来获得对总体人口结构的更多了解。直觉上,未标记的数据有助于在输入数据中找到关于模式的有用信息,以确定新案例的标签。
我们试着用一个例子来理解一下。如下图所示,假设整个数据集中只有四个点用红点和黑点标示。基于这些信息,我们想要估计第二张图中由 x 表示的新数据点的标签。
使用我们基于标签数据的直觉,甚至通过应用一些统计算法,我们大多数人可能会估计它是红色的。但是,如果我们在最后一张图中一起查看带有标记和未标记数据点的整个数据集的分布,明显的聚类很可能会改变我们的估计。

半监督学习(图片由作者提供)
4.3 主动学习
主动学习是半监督学习的一种。它只优先考虑需要标记的混淆样本,并减少从专家那里收集的标签数量。这就是主动学习的工作原理,
- 手动标记训练数据小子样
- 根据这些标记的数据训练模型
- 使用此模型预测未标记数据上的标签
- 根据模型预测对每个未标记的数据点进行评分,并选择具有最大不确定性的数据点
- 人类专家将只标记选定的数据点
- 追加新标记的数据
- 重新训练模型并继续该过程

主动学习(作者的图片灵感来自来源)
Prodigy 是一个基于主动学习的标记工具,有助于减少训练模型所需的标记数据量。

神童(图片由作者提供)
4.3 监管/引导不力
弱监管是指利用组织知识以编程方式标记数据,也称为数据编程。组织知识资源包括内部模型、领域试探法、经验法则、遗留规则、知识图、现有数据库、本体等。我们可以编写多个标记函数,以编程方式标记数据,例如,我们可以编写以下标记函数,根据特定的启发或观察来标记用于垃圾邮件检测的训练数据。




标签功能(通气管,图片由作者提供)
关键思想是标记函数不需要精确,也可以相互关联。像 通气管 这样的弱监督系统可以一致地估计它们的准确度和相关性,然后重新加权和组合它们的输出标签,从而产生高质量的训练标签。浮潜是在斯坦福大学开发的,被谷歌、英特尔、IBM、DARPA 等公司使用。
5.不平衡数据
Y 您拥有的数据具有不平等的类别分布,这导致了不期望的偏差。
5.1 取样和称重
我们已经看到了一种用于处理不平衡数据的技术,即 SMOTE 及其修改版本,它有助于为少数类创建合成数据。除此之外,你可以试试。

过采样和欠采样(图片由作者提供)
- 过采样或上采样少数类。
- 欠采样或下采样多数类。
- 训练模型时,向少数类添加比例权重。
- 尝试采样和机器学习算法的多种组合。
- 寻找优化指标,例如,对于分类问题,使用 F1 得分、精度/召回而不是精度。准确性在处理不平衡数据时不是一个有用的指标。往往倾向于多数阶级的比例。如果你的数据有 95%属于多数类,那么通过把所有东西都归类为多数类,算法可以轻松获得 95%的准确率。使用其他指标,如 F1 分数或精度/召回率也将考虑少数族裔的模型性能。
- 从不同的角度看待问题,例如,将少数类视为异常或异常值,并将异常检测算法用作分类器。
结论
下面的思维导图(点击放大)总结了我们上面提到的所有内容。如前所述,如果你发现自己处于这种 的数据状态 ,在称之为你的项目之路的尽头之前,有大量的选项需要考虑。即使你现在不打算实现机器学习解决方案,也要尽快开始计划数据采集。当处理机器学习问题时,手头有更多的数据几乎总是好的。

解决数据稀缺问题(作者图片)
如果你遇到任何其他工具、技术或策略来处理这种数据匮乏的情况,请将它们添加到评论中。我肯定会考虑加在这里。
参考
[1] Ekin D. Cubuk,Barret Zoph,Dandelion Mane,Vijay Vasudevan,Quoc V. Le,自动增强:从数据中学习增强政策 (2019),CVPR 2019
[2] Google AI 博客,联邦学习:没有集中训练数据的协同机器学习 (2017)
[3]谷歌云博客,介绍带有机密虚拟机的谷歌云机密计算,(2020)
[4] 景龙龙,田英丽,深度神经网络自监督视觉特征学习综述,(2019)
理解 AdaBoost 的综合数学方法
了解 AdaBoost 如何从数学的角度,以全面和直截了当的方式工作。

罗马法师在 Unsplash 上拍摄的照片
在本教程中,我将解释 AdaBoost 是如何通过它所涉及的数学工作的。
在我们开始之前,我建议看看您是否能勾选下面提到的所有先决条件。这些并不是绝对必要的,但会帮助你更有效地学习本指南。
本教程需要的内容:
- 对微积分的基本理解
- 熟悉与监督分类问题
- 对集成学习的基本理解
- 对决策树的基本理解
目录
- 升压和 AdaBoost 简介
- AdaBoost 算法
- AdaBoost 每个阶段的分解
- 结论
升压和 AdaBoost 简介
如果您正在阅读本教程,您可能听说过 XGBoost、LightGBM 或类似的东西。这些在应用机器学习中非常流行,它们来自一个叫做增强集成的算法家族。
顾名思义,提升集成是集成学习中的一个分支。Boosting 通常的工作方式是以一种顺序的方式在数据集的子集上建立多个模型,其中每个模型从以前模型的错误中学习。在你建立了所有的弱模型之后,然后结合他们的预测得到一个“强模型”。
Boosting 引入了将多个弱学习者组合起来得到一个强学习者的流行概念。
模型如何在错误的基础上继续构建,这是每个 boosting 算法所独有的。今天,我们将重点讨论 AdaBoost。
AdaBoost 是 Adaptive Boosting 的缩写,由 Yoav Freund 和 Robert Schapire 创造。它是机器学习的 Boosting 分支中早期成功的算法之一,专门用于二进制分类。
AdaBoost 是一个流行的伟大的算法,当你开始学习 boosting 的时候。
需要注意的一点是,AdaBoost 实际上适用于任何分类器,如逻辑回归、支持向量机等,只要它们是弱学习器。然而,众所周知,AdaBoost 是在称为“树桩”的单深度决策树的上下文中讨论的。在分析 AdaBoost 时,我们将遵循这一约定。
这有几个原因,但这超出了本文的范围。如果你愿意,你可以在这里的讨论中了解更多。.)
重点是,AdaBoost 不一定需要与决策树一起使用,但在实践中通常会这样做,因为 stumps 比其他弱学习器有优势。
AdaBoost 算法
首先,让我们看看 AdaBoost 算法的数学总结。

修改自:t . Hastie,r . TiB shirani 和 j . Friedman(2009)。统计学习的要素:数据挖掘、推理和预测。斯普林格科学与商业媒体。
看起来事情很多,但是请不要抓狂,因为 并没有看起来 那么难。
通读一遍,我们将在下一节讨论细节。
AdaBoost 各阶段分解
AdaBoost 故障的更多初步信息
用于分类的 AdaBoost 是一个监督机器学习问题。它包括使用特征数据(x)和目标标签(y)迭代训练多个树桩。训练后,模型的总体预测来自树桩预测的加权和。

AdaBoost 最终预测结果的可视化
需要注意的一点是,AdaBoost 具体以 -1 或 1 作为目标标签,在预测时会输出-1 或 1 。它不使用通常的 0 和 1 的二进制方法。
考虑到这一点,让我们深入分析一下。
1.初始化权重

AdaBoost 的步骤 1–2a)
在 AdaBoost 中,我们不直接使用数据集进行训练。相反,在每次训练迭代的开始,我们使用权重对数据集进行采样,以获得实际的训练数据。
因此,AdaBoost 的第一步是为每个数据点初始化 1/N 的权重。同样,数据点的权重代表在采样期间选择它的概率。
这方面的一个例子如下:

虹膜数据集的子集作为我们的实际数据集
我们将暂时使用上面的数据集作为我们的玩具示例。
在权重初始化之后,我们进入步骤 2 中的训练迭代循环。
在第一次迭代(m = 1)时,原始数据集中的每个数据点都有相同的机会被选为训练样本。因此,如果我们的数据集如上图所示,有 10 个数据点,那么每个数据点被选中的概率为 0.1。

从原始数据集采样的训练数据集
我们数据集的采样结果
2.训练弱分类器

AdaBoost 的步骤 2b)
在对数据集进行采样以获得当前迭代的训练集之后,使用该训练集来训练弱分类器 K_m。在我们的例子中, K_m 只是一个决策树的树桩。
3.根据错误计算更新参数
3a)Stumps 如何从 AdaBoost 中的错误中学习
AdaBoost 中的 Stumps 通过调整数据集的权重以适应 stumps 在每次迭代中犯的错误,逐步从先前 stumps 的错误中学习。错误分类数据的权重将增加,而正确分类数据的权重将减少。因此,随着我们进入进一步的迭代,训练数据将主要包括经常被我们的树桩错误分类的数据。
例如:

训练组第一个树桩的表现
如果我们的第一个 stump 错误分类数据样本 0,8,5,4,那么在迭代 1 结束时,权重更新导致这些数据样本具有更大的权重。
在权重更新后,您可以从下面的示例中看到,重新采样数据集以获得迭代 2 的训练集将导致包含更多的错误分类数据样本(0,8,5,4)。


左表:基于第一个 stump 在训练集上的表现的数据的新权重||右表:从新权重采样数据集的结果
这里的重量更新并不是准确的数字,它们只是为了证明重量的增加/减少。
直观上,这是有道理的:如果一个树桩无法对某些数据进行正确分类,那么你只需要在“难以分类”的数据上训练更多的树桩。因此,取这些树桩的加权和,让 AdaBoost 表现得非常好。
因此,权重更新对于 AdaBoost 正常工作至关重要。
3b)关于重量更新的更多信息
现在问题来了, 这些权重到底是怎么更新的? 答案就在 AdaBoost 算法的这一段:

AdaBoost 算法的步骤 2c-2e
AdaBoost 算法的步骤 2e)很好地总结了如何在特定迭代 m 进行权重更新,它们被计算为当前权重的乘积,其指数为(-alpha * y * K_m(x)),其中:
- y 表示目标特征的真实值(在我们的玩具数据集例子中,这将是‘物种’列)。
- K_m(x) 是我们在迭代 m 时训练的树桩做出的预测
- alpha_m 是我们对 stump m 的预测能力寄予的信心。
- y 和 K_m(x)可以各自取值-1 或 1 。如果 y 为 1,K_m(x)为 1,或者两者都为-1,那么这意味着训练样本 x 已经被正确分类。yK_m(x)将等于 1。否则,yK_m(x)将等于-1。
- 这与前一小节 a)中的“从错误中学习”的概念一致。
- 当数据被错误分类为、、、 yK_m(x) = -1* 、、时,该数据的、、权重将呈指数增加。 数据分类正确时, yK_m(x) = 1 ,则相反。*******
顺便提一下,当对 AdaBoost 进行整体预测时,我们使用 alpha_m 作为每个树桩预测的权重。
预测= alpha _ 1 * stump _ 1+alpha _ 2 * stump _ 2…这可以用数学方法表示为:

等式 1a。AdaBoost 在迭代 m 时的整体预测,用 C_m(x)表示
Alpha_m 因此是一个重要的参数,实际上是从我们在 AdaBoost 中使用的误差函数中得到的。
3c)从误差函数中导出α
免责声明:这一部分相当长,但我建议从头到尾看一遍,以便在数学上弄清楚 alpha 是什么以及它是如何产生的。
此外,我大量引用了这篇论文来编写本节的注释和推导。我强烈建议你也读一读。
误差函数
AdaBoost 使用的误差函数是指数损失函数。

等式 2a。AdaBoost 的指数损耗
- 首先我们找到每个样本的真值和总体预测之间的乘积。
- 然后我们取这些乘积的所有指数的和,以便计算迭代 m 的误差。
我们可以重新制定 C_m 并将其插入 E_m。这是我们在此步骤后得到的结果:


左:Eq 1b。C_m ||右的重新表述:Eq 2b。将 C_m 的新公式代入误差函数
- 由于 AdaBoost 的预测的加权和性质,我们可以将 AdaBoost 在第 m 次迭代的预测表示为其在前一次迭代(m-1) 的总体预测的和,以及树桩 m 的加权预测[当前迭代的树桩的权重(alpha_m)*当前树桩的预测(K _ m(x))】。
- 将此代入误差函数,得到右边的等式。
现在,在这里稍微偏离一下,我们实际上可以使用样本的真值和样本在迭代 m-1 的整体预测来表示训练样本 I 在迭代 m 的权重:

等式 3。训练样本 I 在迭代 m 时的权重(w_i ^m)
注意,由于 AdaBoost 算法的步骤 2e 中的权重更新公式,这种关系成立。你可以用 C_0(x) = 1/N 代入方程 1,通过归纳来证明。
误差函数的分解
如果我们将其代入等式 2b,我们可以将误差函数表示如下:

等式 2c。使用等式 3 的误差函数(样本权重)
- 同样,回到前面关于权重更新的部分,我们知道 y_i 和 K_m(x_i)的乘积将是 1 或-1 ,这分别取决于样本 x_i 是被正确分类还是被错误分类。
- 有了这些知识,我们可以说-y _ I * alpha _ m * K_m(x_i)=-alpha _ m当 y_i 等于 K _ m(x _ I)【正确分类】,
- 或者-y _ I * alpha _ m * K_m(x_i)= alpha _ m当 y_i 不等于 K _ m(x _ I)【分类错误】。

方程式 2d。错误函数具有由错误分类和正确分类分别构成的组件
- 因此,我们可以将指数误差分解为包含错误分类数据损失的分量和包含正确分类数据损失的分量。
- 这分别由等式 2d 表示。
对事物进行正确分类的损失/错误有点奇怪,因为通常你不会惩罚正确的分类。
但它在 AdaBoost 的误差函数中工作良好,因为只要 alpha_m 大于 0,则-alpha_m 的指数将总是小于 alpha_m 的指数。换句话说,错误分类导致的损失比正确分类导致的损失严重得多。
最小化误差函数
与所有机器学习算法一样,我们的目标是最小化我们已经建立的误差函数。
在这种情况下,我们可以简单地使用一阶导数检验来找到使误差函数最小化的 alpha_m 的值。

方程式 4a。误差函数的导数
在得到 E w.r.t 的导数之前,让我们用更简单的符号表示方程 2d 中的求和分量。

等式 2d 中求和分量的表示
- w 代表在迭代 m 时所有错误分类样本的权重的总和
- t 代表在迭代 m 时所有样本的权重的总和
- 此外,由于我们知道总的权重和包括错误分类和正确分类样本的权重,那么我们可以进一步将正确分类样本的权重和表示如下:

正确分类样本的权重之和的表示
将所有这些重量求和表达式代入方程 2d,然后对 E . w . r . t 对αm 求导,我们得到:

等式 4b。使用 T 和 W 对 E w.r.t 到αm 的导数
使 Alpha_m 成为 Alpha 的主语和解释
接下来要做的就是求解αm,把αm 作为方程的主题。我们可以将等式 4b 转化为以下等式:

等式 4c。操纵方程 4b,使α_ m 在一边。
至此,我们差不多完成了。我们可以直接取 ln,使αm 成为方程的主体,然而,我们将(T-W)/W 表示为(1-W/T) / (W/T)。之后,我们取ε= W/T,然后最后我们可以让α_ m 成为方程的主体。

方程式 4d。求解最小化误差函数的α_ m
- 使误差函数最小化的 alpha_m 的最终值就是 AdaBoost 公式第 2d 步中的值。
- 事实证明,ε只是误分类样本的权重之和(W)与样本的权重之和(T)的比值。
换句话说,ε就是基于样本权重的误分类百分比。
- 这进一步意味着用于树桩 m 预测的权重更新和置信度(权重)的 alpha_m 只是一个考虑了错误分类误差的函数。

α_ m 与ε的关系图
如果我们看一下给定ε值的α_ m 函数的图形,我们可以看到:
- 大的ε值,意味着大的误分类百分比,使得 alpha_m 呈指数下降。
- 我们对 stump m 的预测的信心也呈指数下降。直觉上这很有意义,因为更多的错误=更少的信心。
- 因此 stump m 的预测在整体预测中只会有一个非常小的。
- 自然地,对于小的ε值,相反的随之而来。
4.做出新的整体预测
现在我们已经从数学上直观地理解了 alpha 代表什么,剩下的就是看 AdaBoost 算法的最后一步,即对新数据进行整体预测。

AdaBoost 的步骤 3)
- 我们实际上已经在第 3b 部分的末尾经历了类似的事情。
- 同样,整体预测只是每个树桩预测的总和,由每个树桩的 alpha(AdaBoost 对每个树桩分类数据的能力的信心)加权。
- 我在第 3b 节中遗漏了一个细节,即符号[]部分。这仅仅意味着我们将采用加权和的符号。
- 毕竟 AdaBoost 只预测{-1,1}。只要加权和大于 0 ,那么 AdaBoost 就会预测 1 。相反的情况发生,当加权和小于 0 时,AdaBoost 将预测-1 。
AdaBoost 概述
至此,恭喜你,因为你现在明白了 AdaBoost 是什么,以及它从数学意义上是如何工作的。
作为坚持到最后的奖励,这里有一个总结来概括本文中的所有信息:
- AdaBoost 算法是一种通用的二进制分类算法,其工作原理是将弱学习器组合成强学习器。它最适合决策树的树桩。
- AdaBoost的第一阶段是初始化,在这里我们为采样的数据集初始化权重。
- AdaBoost的第二阶段是训练弱分类器,其中每个分类器被迭代训练,以通过对数据集进行采样从先前分类器的错误中学习,其中较大的权重被分配给错误分类的数据。
- AdaBoost的阶段 3 是根据误差计算更新参数,其中我们计算alpha_m 参数,该参数不仅用于更新下一次迭代(m+1)的权重,还用作分类器 m 预测的权重。
- AdaBoost的第 4 阶段是进行新的整体预测,我们对所有分类器的预测进行求和,然后将其符号作为整体预测{-1,1}。
参考
以下是我用来撰写本文的资源列表:
- https://github . com/rohitash-Chandra/dataminingmath 5836/tree/master/week 8| |了解 AdaBoost 的大量参考文献
- 【http://www.inf.fu-berlin.de/inst/ag-ki/adaboost4.pdf】T2| |精湛讲解 AdaBoost
- https://machine learning mastery . com/boosting-and-AdaBoost-for-machine-learning/| | AdaBoost 入门文章
感谢阅读我的文章!这是我第一次为一个算法写数学教程,所以我真的很感谢对我的写作的任何反馈(批评,我可以改进的地方,等等)。)关于这一点,请随时评论提问或直接在LinkedIn上联系我。
同时,我要特别感谢来自 UNSW 的Rohitash Chandra博士对我的指导和帮助我了解 AdaBoost。
GloVe 的全面 Python 实现
在单台机器上训练完整的手套模型

照片由 Cookie 在 Unsplash 上的 Pom 拍摄
作为一名 NLP 数据科学家,我经常阅读各种主题的论文,包括单词向量、rnn 和变压器。看论文很好玩,给我一种掌握了广泛技术的错觉。但是在复制它们的时候,困难出现了。据我所知,很多 NLP 学习者都碰到过和我一样的情况。因此,我决定开始一系列的文章,集中讨论如何实现经典的 NLP 论文。为此,我还创建了 GitHub 库 T4。
本帖是本系列的第一篇,根据原纸再现手套模型。如前所述,重点纯粹在于实现。关于底层理论的更多信息,请参考原文。
根据这篇论文,手套模型是用单台机器训练的。发布的代码是用 C 写的,对于 NLP 学习者来说可能有些陌生。因此,我对该模型进行了全面的 Python 实现,这符合只用一台机器训练大量词汇的目标。下面几节将逐步介绍实现细节。完整代码是这里是。
第 0 步:准备
培训用数据
对于这个项目,我使用 Text8 数据集作为训练数据。要获得它,我们可以使用 gensim 下载器:
import gensim.downloader as apidataset = api.load("text8")
数据集是一个列表的列表,其中每个子列表是一个代表一个句子的单词列表。我们只想要所有单词的列表,所以用 itertools 将其展平:
import itertoolscorpus = list(itertools.chain.from_iterable(dataset))
好了,现在我们有了训练语料。
存储参数
在处理机器学习模型时,总是有大量的参数需要配置,如数据文件路径、批量大小、单词嵌入大小等。如果管理不好,这些参数会产生大量开销。根据我的经验,我发现最好的方法是将它们都存储在一个名为 config.yaml 的 yaml 文件中。
从 config.yaml 加载配置的代码段
然后对于代码的其余部分,我们可以使用参数 config.batch_size、config.learning_rate 来代替硬编码的值,这也使代码更好。
这就是所有需要的准备工作。让我们开始手套模型的实际两步训练吧!
步骤 1:计算共现配对
创造词汇
为了统计共现对,我们首先需要确定词汇。以下是对词汇表的一些要求:
- 它是出现在语料库中的一组标记。
- 每个令牌都映射到一个整数。
- 如果一个标记不属于语料库,它应该被表示为一个未知标记,或“unk”。
- 为了对共现对进行计数,只需要记号的子集,例如前 k 个最频繁的记号。
为了以结构化的方式满足这些需求,创建了一个词汇表类。该类有四个字段:
- token2index: 将令牌映射到索引的字典。索引从 0 开始,每次添加一个以前看不到的令牌时递增 1。
- index2token: 将索引映射到令牌的字典。
- token_counts: 一个列表,其中 i th 值是索引为 i. 的令牌的计数
- _unk_token: 一个整数,用作未知令牌的索引。默认值为-1。
它还定义了以下方法:
- add(token): 向词汇表中添加一个新的 token。如果以前未见过,则会生成一个新的索引。令牌的计数也会更新。
- get_index(token): 返回令牌的索引。
- get_token(index): 返回索引对应的令牌。
- get_topk_subset(k): 用前 k 个最频繁出现的标记创建一个新词汇。
- shuffle(): 随机打乱所有的记号,使得记号和索引之间的映射被随机化。需要这种方法的原因将在我们实际计算共现配对时揭示。
了解了这一点,我们现在可以看看代码:
词汇课
对于类的实现,我利用了 Python 的 dataclass 特性。有了这个特性,我只需要定义带类型注释的字段,就会自动为我生成 init()方法。我还可以在定义字段时为它们设置默认值。例如,通过设置 default_factory=dict,token2index 默认为一个空 dict。有关 dataclass 的更多信息,请参考官方文档。
现在我们有了词汇课,剩下的问题是:我们如何使用它?基本上有两种用例:
- 从语料库中创建一个词汇表,该词汇表由前 k 个最频繁出现的标记组成。
- 在对共现对进行计数时,使用创建的词汇表将语料库(一个标记列表)转换为整数索引。
我创建了另一个类,矢量器,来协调这两个用例。它只有一个字段, vocab ,指的是从语料库中创建的词汇。它有两种方法:
- from_corpus(corpus,vocab_size): 这是一个类方法。首先,通过添加语料库中的所有标记来创建词汇表。然后,选择顶部最频繁出现的单词来创建新词汇。这个词汇表被打乱并用于实例化矢量器实例。洗牌的原因后面会解释。
- 向量化(语料库):将给定的语料库(一列标记)转换成一列索引。
完整代码如下:
矢量器类
扫描上下文窗口
现在我们有了将所有单词转换成索引的矢量器,剩下的任务是扫描所有的上下文窗口并计算所有可能的共现对。因为共生矩阵是稀疏的,所以使用计数器来计数配对是合理的。关键是(单词 i 的索引,单词 j 的索引),其中单词 j 出现在单词 i 的上下文中。该值是表示计数的浮点数。但是,如果使用这种策略,可能会出现两个问题。
问题 1:如果我们在一次扫描中计算所有同时出现的对,我们可能会耗尽内存,因为不同的数量(单词 i 的索引,单词 j 的索引)可能非常大。
解决方案:相反,我们可以在多次扫描中计数同时出现的配对。在每次扫描中,我们将单词 i 的索引限制在一个小范围内,从而大大减少了不同对的数量。假设词汇表有 100,000 个不同的标记。如果我们在一次扫描中计数所有的对,不同对的数量可以多达 10 个⁰.相反,我们可以在 10 次扫描中计数所有对。在第一次扫描中,我们将 word i 的索引限制在 0 到 9999 之间;在第二次扫描中,我们将其限制在 10000 和 19999 之间;在第三次扫描中,我们将其限制在 20000 到 29999 之间,以此类推。每次扫描完成后,我们将计数保存到磁盘。现在,在每次扫描中,不同对的数量可以和 10⁹一样大,是原始数量的十分之一。
这种方法背后的思想是,我们不是在一次扫描中计算整个共生矩阵,而是将矩阵分成 10 个更小的矩形,然后依次计算它们。下图形象化了这个想法。

左:一次扫描计数|右:多次扫描计数
这种方法是可扩展的,因为随着词汇表大小的增加,我们总是可以增加扫描次数来减少内存使用。主要缺点是,如果使用单台机器,运行时间也会增加。然而,由于扫描之间没有依赖关系,因此可以使用 Spark 轻松实现并行化。但是这超出了我们的范围。
同样,在这一点上,洗牌的原因可以被揭开。当我们创建具有最频繁记号的词汇表时,这些记号的索引是有序的。索引 0 对应最频繁的令牌,索引 1 对应第二频繁的令牌,等等。如果我们继续 100,000 个令牌的示例,在第一次扫描中,我们将计数 10000 个最频繁的令牌对,不同对的数量将是巨大的。而在剩余的扫描中,不同对的数量会小得多。这导致扫描之间的内存使用不平衡。通过改变词汇表,不同的词汇对在扫描中均匀分布,并且平衡了内存使用。
问题 2:从问题 1 的解决方案继续,我们如何将每次扫描的计数保存到磁盘?最明显的方法是在扫描之间将(单词 i 的索引,单词 j 的索引,计数)三元组写入一个共享的文本文件。但是以后使用这个文件进行训练会涉及太多的开销。
解决方案:有一个 python 库,h5py,提供了到 HDF5 二进制格式的 python 接口。它使您能够存储大量的数字数据,并轻松地操作它们,就像它们是真正的 NumPy 数组一样。关于这个库的更多细节,请查看它的文档。
和以前一样,我创建了一个 CooccurrenceEntries 类,它使用建议的解决方案进行计数并将结果保存到磁盘。该类有两个字段:
- 矢量器:从语料库中创建的矢量器实例。
- 矢量化 _ 语料库:单词索引列表。这是使用矢量器对原始语料库(单词列表)进行矢量化的结果。
它有两种主要方法:
- setup(corpus,vectorizer): 这是一个用于创建 CooccurrenceEntries 实例的类方法。通过在语料库上调用矢量器的矢量化方法来生成矢量化 _ 语料库。
- build(window_size,num_partitions,chunk_size,output_directory= " . "):该方法统计 num_partitions 扫描中同时出现的对,并将结果写入输出目录。chunk_size 参数用于使用 HDF5 格式将数据保存在块中。分块保存的原因将在模型训练部分讨论。简而言之,它用于更快地生成训练批次。
实现如下:
CooccurrenceEntrie class
通过抽象词汇、矢量器、共现条目,计算共现对并保存到磁盘的代码很简单:
用于创建培训数据的代码段
第二步。训练手套模型
从 HDF5 数据集加载批次
我们首先需要批量加载 HDF5 数据集中的数据。因为数据可以像存储在 NumPy 矩阵中一样被检索,所以最简单的方法是使用 PyTorch 数据加载器。但是加载每一批都涉及到许多 dataset[i]形式的调用,其中 dataset 是一个 h5py。数据集实例。这涉及到许多 IO 调用,可能会非常慢。
解决方法是加载 h5py。数据集逐块存储到内存中。每个加载的块都是内存中的一个纯 NumPy ndarray,所以我们可以使用 PyTorch 的 Dataloader 对它进行批处理迭代。现在,所需的 IO 调用数量等于块的数量,这要小得多。
这种方法的一个缺点是完全随机的混洗是不可能的,因为包含来自不同区块的数据的批将永远不会生成。因此,为了获得更大的随机性,我们可以以随机顺序加载块,并将 DataLoader 的 shuffle 参数设置为 True。
创建 HDF5DataLoader 类是为了加载批处理。它有五个字段:
- file path:HD F5 文件的路径。
- 数据集 _ 名称:h5py 的名称。文件中的数据集。
- 批量大小:训练批量大小。
- 设备:训练设备,可以是 cpu 或 gpu。
- 数据集:h5py。文件中的数据集实例。
它有两种方法:
- open(): 这个方法打开 HDF5 文件并定位数据集。实际的阅读并不发生在这里。
- iter_batches(): 该方法以随机顺序加载块,并创建 PyTorch 数据加载器来迭代块中的批。
代码如下所示。需要注意的一点是,CooccurrenceDataset 只是 PyTorch 数据集的一个子类,用于索引数据。因为没什么特别的,所以省略了。
HDF5DataLoader 类
编码手套模型
用 PyTorch 实现手套模型很简单。我们在 init()中定义了两个权重矩阵和两个偏置向量。注意,我们在创建嵌入时设置 sparse=True,因为梯度更新本质上是稀疏的。在 forward()中,返回平均批次损失。
手套班
训练手套模型
模型训练遵循标准 PyTorch 训练程序。唯一不同的是,我们使用定制的 HDF5Loader 来生成批处理,而不是 PyTorch 的 DataLoader。以下是培训代码:
模型定型的代码段
唷,我们已经完成了完整的实现。恭喜你!
接下来,我们来训练模型,看看结果!
第三步。结果
对于 Text8 数据集,训练一个历元大约需要 80 分钟。我为这个模型训练了 20 个纪元,花了不止一天的时间来完成。学习曲线看起来很有希望,如果培训继续下去,损失似乎会进一步减少。

学习曲线图
我们也可以做一些单词相似度的任务,看看单词向量是如何表现的。在这里,我使用了 gensim 的 KeyedVectors 类,它允许您在不编写最近邻或余弦相似性代码的情况下做到这一点。这里的相似度评价码是。有关 KeyedVectors 的详细信息,请参考文档。
运行一些简单的相似性任务会显示以下结果:

一些简单相似性任务的结果
正如我们所看到的,其中一些是有意义的,如“计算机”和“游戏”,“联合”和“国家”;有些人没有。但这足以说明问题。在更大的数据集上进行更多时期的训练应该会改善结果。
摘要
手套纸写得很好,很好理解。然而,当谈到实现时,一路上有许多陷阱和困难,尤其是当您考虑到内存问题时。经过相当大的努力,我们最终得到了一个令人满意的在单台机器上训练的解决方案。
正如我在开始时所说,我将继续实施更多的 NLP 文件,并与您分享我的第一手经验。希望你喜欢手套的实现,并在下一个帖子中看到你!
数据科学面试综合统计和概率备忘单
统计和概率的重要术语和方程

目录
- 关于此资源
- 置信区间
- z 统计量与 T 统计量
- 假设检验
- A/B 测试
- 线性回归
- 概率规则
- 贝叶斯定理
- 面试练习题
关于此资源
当我申请数据科学工作时,我注意到需要一个全面的统计和概率备忘单,它超出了统计学的基本原理(如均值/中值/众数)。
因此,strata scratch的创始人内森·罗西迪和我合作报道了数据科学采访中常见的最重要的话题。这些主题更侧重于统计方法,而不是基本的属性和概念,这意味着它涵盖了在现实生活中更实际和适用的主题。
说到这里,我希望你喜欢它!
置信区间
置信区间表示很可能包含感兴趣参数的数值范围。
例如,假设您抽样调查了 5 位客户,他们对您的产品的平均评分为 3.5 分(满分为 5 颗星)。您可以使用置信区间来确定基于此样本统计的总体均值(所有客户的平均评级)。
平均值的置信区间(n ≥ 30)

平均值的置信区间(n < 30)

比例的置信区间

假设检验
假设检验用于确定对于给定的数据样本,某个假设的可能性有多大。从技术上讲,假设检验是一种将样本数据集与总体数据进行比较的方法。
以下是执行假设检验的步骤:
- 陈述你的无效假设和替代假设。重申一下,零假设通常是指一切都和往常一样——没有任何变化。
- 设置你的重要性等级,阿尔法值。这通常设置为 5%,但也可以根据情况和犯 1 型和/或 2 型错误的严重程度设置为其他水平。
- 收集样本数据并计算样本统计量(z 统计量或 t 统计量)
- 计算给定样本统计的 p 值。一旦获得样本统计数据,就可以通过不同的方法确定 p 值。最常用的方法是正态分布的 T 得分和 Z 得分。点击 了解更多 T-score 和 Z-score 。
- 拒绝或不拒绝零假设。
你可以在这里测试你关于假设检验、置信区间和一般统计的知识!
z 统计与 T 统计
知道 z 统计量和 T 统计量是很重要的,因为它们是执行假设检验的步骤中的步骤 3 所必需的(见上文)。
Z-检验是一种使用Z-统计量的正态分布假设检验。当您知道总体方差或不知道总体方差但样本量很大时,可以使用 z 检验。

T 检验是一种假设检验,其 t 分布使用了 t 统计量。当不知道总体方差并且样本量很小时,可以使用 t 检验。您还需要将 t 统计量转换为 p 值的自由度。



作者创建的图像
A/B 测试

作者创建的图像
从最简单的意义上来说,A/B 测试是对两个变量进行的实验,根据给定的度量标准来看哪一个表现更好。从技术上讲,A/B 检验是双样本假设检验的一种形式,这是一种确定两个样本之间的差异是否具有统计显著性的方法。
进行 A/B 检验的步骤与假设检验完全相同,只是根据 A/B 检验的类型,p 值的计算方式不同。
进行的 A/B 测试的类型取决于许多因素,我将在下面介绍这些因素:
注意:我不会涉及这些测试背后的数学,但可以随意查看 Francesco 关于 A/B 测试的文章。
费希尔精确检验
Fisher 测试用于测试离散指标,如点击率(1 表示是,0 表示否)。使用 Fisher's 检验,您可以计算精确的 p 值,但是对于大样本量来说,这种方法的计算成本很高。
皮尔逊卡方检验
当样本量过大时,卡方检验是费雪检验的替代方法。它还用于测试离散指标。
学生的 t 检验
我包括了 t 检验,但没有包括 z 检验,因为 z 检验在现实中通常是不切实际的,因为总体标准偏差通常是未知的。然而,由于我们可以得到样本标准差,t 检验是合适的。
它可以在样本量较大(或观测值呈正态分布),且两个样本具有相似方差的条件下使用。
韦尔奇试验
韦尔奇的 t 检验与学生的 t 检验本质上是一样的,只是当两个样本没有相似的方差时使用。在这种情况下,可以使用韦尔奇检验。
曼-惠特尼 U 检验
Mann-Whitney 检验是一种非参数检验,仅在违反所有先前检验的所有假设时使用。例如,如果您的样本量很小,并且分布不是正态分布,则曼-惠特尼检验可能是合适的。
要了解如何在 Python 中进行这些测试,请查看这个 库 。
线性回归
什么是回归?
回归简单来说就是估计一个或多个自变量(x)和因变量(y)之间关系的统计方法。简而言之,它包括找到代表两个或更多变量的“最佳拟合线”。

作者创建的图像
通过最小化点和最佳拟合线之间的平方距离来找到最佳拟合线,这被称为最小二乘回归。一个残差简单地等于预测值减去实际值。
残差分析
可以进行残差分析来评估模型的质量,也可以识别异常值。一个好的模型应该有一个同方差残差图,这意味着误差值总体上是一致的。

同方差图(左)与异方差图(右)
变量选择
两种非常简单和常见的变量选择方法是向后消除(一次删除一个变量)或向前选择(一次添加一个变量)。
您可以通过计算变量的 p 值来评估变量在模型中是否重要。一般来说,好的变量的 p 值小于或等于 0.05。
模型评估
为了评估一个回归模型,您可以计算它的 R 平方,它告诉我们模型在数据中占了多大的可变性。例如,如果一个模型的 R 平方为 80%,那么数据中 80%的变化可以由该模型来解释。
调整后的 R 平方值是 R 平方值的修改版本,用于调整模型中预测值的数量;如果新项对模型的改进超过偶然的预期,那么它就会增加,反之亦然。
要避免的 3 个常见陷阱
- 过度拟合:过度拟合是一种错误,模型“拟合”数据太好,导致模型具有高方差和低偏差。因此,过度拟合模型将会不准确地预测新的数据点,即使它对训练数据具有高的准确性。当模型中有太多独立变量时,通常会发生这种情况。
- 共线性:这是一个模型中的两个自变量相互关联,最终降低了模型的准确性。
- 混杂变量:混杂变量是指不包含在模型中,但同时影响自变量和因变量的变量。
概率规则
有几个基本属性和四个概率规则你应该知道。这些概率规则是更复杂(但仍然是基本的)方程的基础,如贝叶斯定理,这将在后面介绍。
注意:这并不审查联合概率、事件的联合或事件的交集。如果您不知道这些是什么,请事先复习一下。
基本属性
- 每个概率都在 0 到 1 之间。
- 所有可能结果的概率之和等于 1。
- 如果一个事件是不可能的,那么它的概率为 0。
- 相反,某些事件的概率为 1。
四个概率规则
1。加法规则

2。补充规则

3。条件规则

4。乘法法则

要练习使用这些方程式,可以查看 这个资源 。
贝叶斯定理
贝叶斯定理是一个条件概率陈述,本质上它着眼于一个事件(B)发生的概率,假设另一个事件(A)已经发生。公式如下:

- P(A)是先验,是 A 为真的概率。
- P(B|A)是可能性,给定 A,B 为真的概率。
- P(B)是边缘化还是正常化常数
- P(A|B)是后验。
你会在很多实际问题中发现,归一化常数 P(B)是没有给定的。在这些情况下,您可以使用贝叶斯定理的替代版本,如下所示:

为了更好地理解贝叶斯定理并跟随一些练习题,请查看 这里 。
组合和排列
组合和排列是从集合中选择对象以形成子集的两种略有不同的方式。排列会考虑子集的顺序,而组合则不会。
如果你从事网络安全、模式分析、运筹学等工作,组合和排列是非常重要的。让我们更详细地回顾一下这两者中的每一个:
排列
定义:n 个元素的排列是这 n 个元素以确定顺序*的任意排列。有 n 个阶乘(n!)排列 n 个元素的方式。注意粗体字:顺序很重要!*
一次取 r 的 n 个事物的排列数被定义为可以从 n 个不同元素中取出的 r 元组的数目,并且等于以下等式:

例题:一个 6 位数的车牌有多少种排列?

组合
定义:在顺序不重要的情况下,从 n 个对象中选择 r 的方式的数量。
一次取 r 的 n 个事物的组合数被定义为一个具有 n 个元素的集合中具有 r 个元素的子集的数目,并且等于以下等式:

例题:从一副 52 张牌中抽出 6 张牌有多少种方法?

请注意,这些都是非常非常简单的问题,可能会比这复杂得多,但是您应该很清楚上面的例子是如何工作的!
面试练习题
如果你已经做到了这一步,这里有一些资源可以用来测试你的知识:
- StrataScratch 本质上是 LeetCode 但对于数据科学来说。它有数百个关于统计、Python、Pandas 和 SQL 的问题。StrataScratch 的创始人内森帮我策划和开发了这个速成班,这并不奇怪。
- 如果你还没有看,我之前写了一篇文章“数据科学家的 50 个统计面试问题和答案”我鼓励你去看。
感谢阅读!
如果你坚持到了最后,恭喜你!我希望这有助于你更新和修补你的统计知识。我知道有很多东西要记,但是你用得越频繁,就越不容易丢失。
一如既往,我祝你在数据科学的努力中一切顺利。如果你喜欢这篇文章,我会很感激你给我一个关注!😃
再次特别感谢内森·罗西迪。你可以去看看他的网站,StrataScratch, 这里 。
不确定接下来要读什么?我为你选了另一篇文章:
*</50-statistics-interview-questions-and-answers-for-data-scientists-for-2021-24f886221271>
又一个!
特伦斯·申
Julia 上带跳转的混合整数规划综合研究(上)
实践教程
线性/混合整数编程的一些基础&如何在 MIP 求解器中使用启发式回调。

介绍
计算机科学和运筹学的主要目的之一是有效地解决问题;问题解决是一个我们经常发现非常“特别”的解决方法的领域,它们可能是有效的,但是它们依赖于问题的一些不一定容易注意到的特定属性。
在这一系列的文章中,我们将介绍并发现一种非常通用的思维方式和解决各种问题的方法,这种介绍将从三个方面进行:
- 在理论观点上,我们将研究线性规划和混合整数规划如何帮助我们建模大的组合问题。
- 在实践方面,我们将看到如何使用 API 实例化一个线性程序,并利用我们解决的一些问题来提高求解过程的效率。
- 最后,从操作的角度来看,我们将发现一种非常新的、高效的和用户友好的语言:Julia,更准确地说,我们将发现一个库:JuMP,一种用于数学优化的领域特定的建模语言。
这一系列的文章并没有假设朱莉娅的背景;我认为 Python 背景足以理解我将使用的代码片段。
这个帖子是关于什么的
这篇文章是这个系列的先导,但它也将是它的起点,给你了解可以用来解决大型组合问题的实用技术所需的背景知识。
这也将是我们唯一一篇将纯理论学术问题作为简单应用的文章。
尽管如此,如果你熟悉混合整数编程,这篇文章(更一般地说,这个系列)是一个很好的机会来看看如何使用 Julia,更准确地说,如何用一些接近的方法调整你的精确求解过程。
顺利介绍线性规划(和朱莉娅)
让我们首先介绍线性程序是如何构造的,以及求解器将如何执行求解。为了做到这一点,我们将通过一个简单的例子。
为了使它可视化,我们将举一个例子,在这个例子中,我们将尝试针对一组线性约束来优化两个变量的线性函数。

几何上,如果我们取每一个约束,用等式代替不等式,那么每一个约束都将是一个线方程。这条线会把 R 分成两部分,根据不等式的方向使其中一部分无效。
我们将命名由约束集界定的多面体,在这种情况下,它是多面体,因为它是封闭的和有界的,约束的多面体(或多面体)。
作为对 Julia 的热身,让我们看看如何使用 plot.jl 绘制约束的多面体,plot . JL 是一个“类似 matplotlib”的框架,包 LinearAlgebra 类似于 NumPy。
首先,我们使用 Pkg,它是 Julia 的内置包管理器,来添加所需的包,
using Pkg;
Pkg.add("LinearAlgebra");
Pkg.add("Plots");
Pkg.add("PyPlot");
添加后,我们可以导入它们。
using LinearAlgebra
using Plots
pyplot()
最后一行旨在完成一些可视化的封装图功能(查看文档此处了解更多细节)。
绘制任何函数的一个简单方法是采样点并计算相关图像,这可以在 Julia 中通过以下方式完成:
x_v = LinRange(-2,15,100)
plot([x_v], [x_v .+ 7.5], label ="Y=x + 7.5")
plot!([x_v], [-2x_v .+ 20], label ="Y= -2x + 20")
第一行将从区间[-2,15]中采样 100 个点;这其中的一部分;您需要注意几件事情:
- “剧情”是用来打造线剧情和剧情的!更新创建的地块。
- ".+"是向量加法的元素等价形式。
- Julia 非常方便,即使对于向量,也可以省略系数和变量之间的“*”。
这几行将产生以下情节:

插图(由我绘制)
现在让我们打印约束的多面体:
x_v = LinRange(-2,15,100)
y_v = LinRange(-2,15,100)
plot([0*x_v], [y_v],label ="Y Axis")
plot!([x_v], [0*x_v],label ="X Axis")
plot!([x_v], [0*x_v .+ 7.5], label ="Y=7.5")
plot!([0*x_v .+ 10], [y_v],label ="X=10")
plot!(title = "Polytop of Constraints")

插图(由我绘制)
我添加到图中的灰色区域代表空间的一部分,它满足问题的约束。
现在让我们通过查看向量(1,2)来关注目标函数,向量(1,2)表示线性函数 x+2y 的梯度。

插图(由我绘制)
我添加的每条线都代表一行值相同的点。在梯度方向上走得越远,目标值就变得越大。
我们可以直观地得出结论,最佳解决方案在绿色和粉色线的交点处,所以让我们看看是否使用 JuMP 找到了这个结果。
传统的添加/导入行(我们将使用 GLPK 作为求解器,但没有什么依赖于它)。
Pkg.add("JuMP")
Pkg.add("GLPK")
using JuMP
using GLPK
现在我们声明我们的模型,并从 GLPK 设置优化器:
prgrm = Model()
set_optimizer(prgrm, GLPK.Optimizer)
我们添加变量并精确它们的范围;默认情况下,变量是连续的:
[@variable](http://twitter.com/variable)(prgrm, 0<=x)
[@variable](http://twitter.com/variable)(prgrm, 0<=y)
现在我们创建并添加剩下的两个约束;前两个在变量的范围内;
[@constraint](http://twitter.com/constraint)(prgrm, x <= 10)
[@constraint](http://twitter.com/constraint)(prgrm, y <= 7.5)
最后,我们添加目标函数和精确意义上的优化,在这种情况下,这将是最大化:
[@objective](http://twitter.com/objective)(prgrm, Max, x+2y)
JuMP 的一个有趣的特性,特别是当它与 Jupyter-notebook 一起使用时,我们可以像打印任何变量的内容一样轻松地打印程序,这给了我们以下输出:

现在解决这个问题说起来容易做起来难:
optimize!(prgrm)
之后,我们可以像这样访问优化后的变量值:
value.(x)
value.(y)
因此,我们可以更新我们的先例图,以确认我们的图形分辨率与线:
plot!([value.(x)], [value.(y)], seriestype = :scatter, label="Optimum")
这给了我们:

插图(由我绘制)
单纯形原理
求解线性规划是通过单纯形算法完成的,这是因为一个简单但重要的原理:
在多面体(或更一般的紧凸空间)上优化一个线性函数总是把我们带到一个顶点(更一般的是一个极值点)。
单纯形算法是一种局部搜索过程,它从一个顶点走到另一个顶点以增加目标函数值,直到我们到达一个顶点,在该顶点处每个邻居都具有较低值。
由于优化结束的顶点只取决于目标函数,我们可以尝试为每个多面体顶点找到一个目标函数。
例如,在下面的多面体中(注意,我们添加了一个约束来增加多面体的顶点数)

插图(由我绘制)
我们可以通过不同方向的优化获得任意顶点。

插图(由我绘制)
从连续变量到整数变量:分枝定界法
即使这不完全是求解器的工作方式,你首先要理解混合整数编程的工作方式是分支定界法。
让我们举一个先例,但是把变量限制为整数;可行域不再是多面体内部的灰色区域。尽管如此,我们可以计算可行的整数点,这给了我们下图:

插图(由我绘制)
在灰色中,我们可以看到可行的解决方案,我们可以注意到的第一件事是,一些顶点在整数解中,一些不在整数解中,这种区分是至关重要的,但我们稍后会回到这一点。
分支和绑定过程创建一个称为“枚举树”的树;在每个节点中,它构造一个混合整数规划,并用单纯形算法求解其“线性松弛”,这意味着忽略完整性约束后的同一个规划,从这一点出发,有两种可能的结果:
- 解决方案是整数可行的,因此我们停止决议。
如果约束的多面体有整数顶点,就会发生这种情况。例如,如果我们用目标函数 2x+y 求解前面的混合整数规划的 LP 松弛,我们将发现(10,3),这是一个整数解。
- 解决方案是“分数”,因此我们需要分支。
如果我们试图优化函数 x+2y,这将发生在前面的多面体中,

插图(由我绘制)
现在,让我们看看如何处理这种情况。
分支:
分支是指在枚举树中创建子节点;这些孩子是相同的问题,但是在可行空间的两个分区上解决。
这两个分区是通过添加一个分支约束得到的。
由于一个例子胜过千言万语,对于前面的分数解(5.5,7.5),我们可以选择我们可以分支的变量。如果我们的解中有一个整数和一个分数分量,我们应该分支到分数分量,比如说,我们将分支到 x,所以我们在每个子节点中添加一个约束,
- 第一个子节点将具有约束 x ≤ 5

插图(由我绘制)
- 第二个将继承约束 x≥6

插图(由我绘制)
正如您所注意到的,这些约束的目的是,在每种情况下,排除或切割分数解(您将在本系列的另一部分中发现为什么它是粗体的:)
这最后一个孩子的放松给了我们下面的解决方案。

插图(由我绘制)
红色的解是:(6.0,7.0)整数可行解。
那么,我们结束了吗?良好的..不完全是。
让我们回顾一下我们的枚举树。

插图(由我绘制)
正如我们所看到的,我们只解决了树的三个节点中的两个;然后,我们要求解 P.1 的线性松弛。

插图(由我绘制)
松弛给了我们一个分数解:(5.0,7.5)这意味着如果我们在 y 上分支,枚举树会变成这样。

插图(由我绘制)
为什么我要说会?这个算法的名字是品牌绑定的,到目前为止,我们只是分支,所以现在我们来看看如何绑定。
跳跃:
边界或“探测”过程包括移除树的一个分支,假设我们有一个我们能在其中找到的值的上界(最大化)。
为了有这个上限,我们将引入另一个直观而重要的原理:
LP 的松弛值是其价值的上界。
这很直观,因为松弛的可行域包含了原程序的可行域。
因此,当我们求解 P.1 并获得值为 20 的分数解时,我们确信在根为 P.1 的子树中,我们不会找到任何值大于 20 的解,因为我们已经有了值为 20 的整数解,我们可以探测树的这一部分,之后,我们就完成了。
使用规划求解器自动完成这一切:
你们都应该记住的第一个问题是:“每次我必须解决一个 MIP 时,我都要这样做吗?”答案当然是,不,我必须精确地确定变量是整数,求解器会为我们完成这项工作(以及其他工作)。
[@variable](http://twitter.com/variable)(prgrm, 0<=x, Int)
[@variable](http://twitter.com/variable)(prgrm, 0<=y, Int)
但是你们中最细心的人肯定想知道,如果求解器,在构造枚举树的时候,是从另一个孩子开始的,而这个孩子没有立刻给我们一个整数解呢?
这提出了许多关于如何分支以及如何探索枚举树的问题,但是您可能关注的主要问题是。
我们越早获得整数解,最好是剪枝子树。
但是既然求解者是在内部做这件事,那么理解这一点(以及所有这部分)的目的是什么呢?这是我们将在下面看到的:)
在求解器中使用自制试探法
在很多大型组合问题中,求解器可能找不到整数解,所以它的枚举树一直呈指数增长。
幸运的是,许多组合问题有简单的(通常是贪婪的)试探法,可以快速计算出一个可行的解,因此是一个整数解。
通常使用的解算器为我们提供了一个计算和提交启发式解的界面;这个的目的是给算法一个整数解来执行探测。
让我们试着把这个应用到一个非常经典的例子: 顶点覆盖问题。
无向图 G=(V,E)的顶点覆盖 V '是包含图的每条边的至少一个端点的顶点的集合,通常我们感兴趣的是找到最小的顶点覆盖。
制定输入数据
我们必须考虑的第一个问题是“我们如何表示问题的实例?”,要回答这个问题,我们来举个例子。

插图(由我绘制)
由于边是不加权的,我们可以用形状为(n,n)的邻接矩阵来表示这个问题,其中 n 是节点的数量。
因此,例如,在下面的矩阵中对引用图进行编码:
ADJ_MAT = [
[0 , 0 , 1, 0, 0, 0],
[0 , 0 , 1, 0, 0, 0],
[1 , 1 , 0, 1, 0, 0],
[0 , 0 , 1, 0, 1, 0],
[0 , 0 , 0, 1, 0, 1],
[0 , 0 , 0, 0, 1, 0],
]
(现在,我建议你停止阅读,试着写出顶点覆盖问题的 MIP 公式来检查你的理解)
现在让我们考虑一下我们将要使用的公式:
- 决策变量有哪些?
我们试图为每个顶点决定是否在封面中取它,所以我们有 n 个二元变量,表示是否取对应的顶点。
我们可以创建如下变量:
#WARNING : Everything on Julia is indexed by default starting from #1.
n = size(ADJ_MAT)[1]
#We create a vector of variables indexed by 1 prefixed by x (x1, x2, ..., xn)
[@variable](http://twitter.com/variable)(prgrm2, 0<=x[1:n], Int)
- 有哪些约束条件?
可行的解决方案必须覆盖每条边,所以我们必须为每条边取一个或另一个(或两个)顶点。

可以像这样简单地创建这些约束:
for i in 1:n
for j in 1:n
if(ADJ_MAT[i,j] == 1)
c = [@constraint](http://twitter.com/constraint)(prgrm2, x[i] + x[j] >= 1)
set_name(c,"C")
println(c)
end
end
end
- 我们在优化什么?
目标是尽量减少封面中的顶点数量:

可以这样生成:
[@objective](http://twitter.com/objective)(prgrm2, Min, sum(x))
#Output : 𝑥1+𝑥2+𝑥3+𝑥4+𝑥5+𝑥6
因此,MIP 公式如下:

插图(由我绘制)
这给出了以下解决方案:

插图(由我绘制)
可以解释如下:

插图(由我绘制)
顶点覆盖问题的近似解
在这一部分,我将介绍两种计算顶点覆盖问题可行解的方法;这两个算法有一个迷人的特性:有性能保证的近似。
贪婪算法:
该算法包括取每条边的两个端点,并在每一步移除所有被覆盖的边。
贪婪算法是 2-近似,这意味着它不会返回比最优解差两倍以上的解。
一旦你注意到在最坏的情况下一个顶点就足够了,证明就很简单了。
取整分数解:
顶点覆盖问题还有另一个有趣的性质:它是一个“半整数问题”,这意味着即使在其松弛状态下,变量也是 0 或 1 或 1/2。
获得近似解的另一种方法是通过将 1/2 变为 1 来舍入分数解,这将给出一个可行的解,因为分数已经满足约束,并且我们增加了它的一些变量。
(关于这两种方法有效性的完整证明,你可以阅读的这个课程)
启发式回调上跳
现在,我们如何告诉我们的求解器在每个分数节点调用启发式算法呢?
为此,你必须了解三件事:
- 如何在回调中获取求解器的当前解值?
- 从回调中构建启发式解决方案后,如何提交它?
- 如何注册回调函数,以便规划求解使用它?
我将在下面使用取整近似值的示例中向您解释这些方面:
第一步你要设计你的回电;在回调中,可以使用两种方法:
- callback_value(cb_data,x)从回调数据中获取变量的值(一次只能获取一个)。
- MOI.submit(型号,MOI。HeuristicSolution(cb_data),[x],[v]):该方法提交解 x=v,并返回解的状态,可以是“已接受”、“已拒绝”(由求解器)或“未知”。
因此,对于我们的取整近似值,我们建议回调如下:
function my_callback_function(cb_data)
println("Call to callback")
new_sol = []
precedent = [callback_value(cb_data,x_k ) for x_k in x]
for x_i in x
x_val = callback_value(cb_data, x_i)
x_new = ceil(Int, x_val)
append!(new_sol, x_new)
end
println("Precedent: ", precedent)
println("New: ",new_sol)
status = MOI.submit(
mod, MOI.HeuristicSolution(cb_data), [x_i for x_i in x], [floor(Int, k) for k in new_sol]
)
println("status = ", status)
end
我添加了打印语句来跟踪对启发式算法的调用。
现在我们注册我们的回调:
MOI.set(mod, MOI.HeuristicCallback(), my_callback_function)
因此,当执行优化步骤时,我们可以看到:

第一次后被拒绝的解是因为舍入过程在效率方面很快不足以击败求解器产生的整数解,所以它提出的解被拒绝,但它在第一次迭代中仍然有用。
结论
当你设计一个解决方案时,你必须在精确的或接近的解决方案之间做出选择。尽管如此,正如我们所见,只要对你想要解决的特定问题有良好的直觉,并对解决者的内部行为有良好的理解,我们就能两全其美。
特别感谢皮埃尔·福伊尔霍公关,他启发了我们,帮助我们更好地理解这个领域的深度,并欣赏它的所有细微差别。当然,还有我的伙伴 Louis Grassin,他帮我编写了这篇文章的内容。
Julia 上带跳转的混合整数规划综合研究(下)
实践教程
模型概念及其在批量问题中的应用

照片由 Alexander Tsang 在 Unsplash 拍摄
这个帖子是这个一个的延续;我鼓励你阅读第一个帖子,尤其是如果你没有任何混合整数编程的背景。如果你这样做了:欢迎来到正经事。
这篇文章的目的是展示线性项如何能够模拟广泛的约束,并优化现实生活中问题的便捷函数。
在今天的菜单中,我们将调查以下问题:
- 我们如何在线性程序中建模逻辑约束?
- 同一个问题的所有提法都等价吗?如果不是,我们怎么能说一种配方比另一种好呢?
- 对于一个组合问题,我们能有一个理想的公式吗?
当然,我们将在解决一个经典的工业问题时尝试这样做:批量问题。
批量问题
批量问题(LSP)包括在计划范围内计划生产和库存的批量,以最小化生产、准备和库存成本。
换句话说,想象你必须设定一个工厂的生产计划,以满足客户在每个时间点的需求。不过,每次开始生产时,你都必须支付一笔持续的准备成本和一笔生产成本,这取决于你想要生产的数量。
你可以考虑在期初生产尽可能多的数量,但是储存也涉及成本。
我们还将考虑一个产能约束,它将限制我们可以持有的数量,因此我们将解决产能约束下的批量问题。
实例描述
让我们先来看看如何对这个问题的一个实例建模。首先,我们必须记住,解析是在一个时间范围内进行的,因此每个“时间相关”变量都将由 t ≤ T 来索引。
现在,有哪些依赖于实例的变量是不变的?首先,成本。所以我们把 f 作为准备成本, q 作为单位生产成本,而作为持有成本 k,存储容量也不变。
客户需求将由我们的时间范围大小的向量来描述;每一个需求都将由 dₜ用我们时间范围的一个瞬间来表示。
在下文中,我们将考虑以下实例:
T=10 #Time Horizon
f=1000 #Setup Cost
q=30 #Production Cost
k=10 #Holding Cost
D=[15,10,10,25,30,5,5,50,20,80] #Demand at each step
Imax=100 #Storage Capacity
模型变量
我们有两种可能的行动:现在生产或生产并储存以备后用。
然后,在每个时间步,我们要决定是否启动生产,如果启动,我们要确定生产多少;我们的选择会影响股票的状态,所以我们也必须为它使用一个变量。
这些决定必须在每个时间步做出,所以我们将有 yₜ、pₜ、 Iₜ 、T4 到代表如果我们启动,我们生产多少,在每个时间步结束时我们有多少库存。
变量可以这样实例化:
[@variable](http://twitter.com/variable)(prgrm, p[1:T] >= 0)
[@variable](http://twitter.com/variable)(prgrm,I[0:T]>= 0) #We index from 0 to set an initial storage
[@variable](http://twitter.com/variable)(prgrm, 1 >= y[1:T] >= 0, Int)
模型约束
一个主要的限制是在不超过每个步骤的存储容量的情况下进行生产;这可以这样表述:

在此之后,我们必须记住,任何产品要么是现在的,要么是以后的,所以每次我们生产时,我们生产的超出客户需求的所有产品都被储存起来,以备下一步使用;这是一个“流量约束”,可以写成如下形式:

#We set initial storage to 0 (for example) [@constraint](http://twitter.com/constraint)(prgrm, I[0] == 0) for i in 1:T
[@constraint](http://twitter.com/constraint)(prgrm,I[i-1] + p[i] == D[i] + I[i])
end
还有一个内在的约束:要生产,就要发动生产;这是一个隐含约束( pₜ > 0 暗示yₜ= 1),也是一个展示 Big-M 以及我们如何使用它来建模隐含的极好机会。
变量的大 M 是变量无法达到的值,它有助于制定如下约束:

如果仔细观察此约束,您会注意到,除非 yₜ为真,否则 pₜ不能为非空,如果 yₜ为真,则产量不受此约束的限制,因为 m 的值大于 pₜ.的最大可达值
当然,现在的问题是:如何修复 Big-M?
我们可以像 10⁵一样随意地将其固定为一个不合理的高值,但不建议这样做,原因有二:
- 这可能会在数值上产生问题,因为在求解器内部,例如,假布尔值被表示为非常接近于 0 的值。我的意思是比 10⁻⁵小,所以如果你把 10⁸作为一个大 m,它可能会把你带到一个 y 被设置为 10⁻⁶的结果,求解器把它看作一个布尔值 false。尽管如此,它并没有像预期的那样将 pₜ约束为空。
- 它负面地影响弛豫间隙;我们稍后将回到这一点,但直觉是,它限制了我们必须搜索的区域,因此在添加约束 p≤ 2 和约束 p ≤10⁶之间,第一个更具限制性。
一般来说,我们寻求最小的变量作为大 M;对于这个例子,我们可以有许多不同的选择:
- 因为我们不能生产超过我们能储存的,大 m 可以固定在 Iₘₐₓ.
- 我们从未对生产超过客户总需求的产品感兴趣;我们可以把需求的总和作为所有生产变量的大 M。
- 没有什么迫使我们对所有的 pₜ变量采取相同的大 m;通过应用与前一点相同的推理,我们对生产超过剩余需求的永远不感兴趣。
第二个想法包含在第三个想法中,但我们没有义务在第一个想法和第三个想法之间做出选择:

注意,Mₜ不是一个变量,因为我们知道 Iₘₐₓ和所有的 dᵢ.然后,我们可以在构建程序时计算每个 mₜ;这个 big-M 的另一个积极的副作用是,我们不再需要指定与我们不能生产超过我们可以存储的事实相关的约束。
目标函数
要设计目标函数,我们必须有一种方法来评价一个解的质量;对于这个问题,很简单,我们必须最小化总成本,我们可以这样计算:

为了获得这个成本,我们将使用 Julia 的两个方便的功能;第一个是元素操作,它可以帮助我们将一个向量乘以一个系数:
*p .* 10
#Output : an array of size {T}*
第二个是求和运算符:
*sum(p .* 10)
#Output: 10𝑝1+10𝑝2+10𝑝3+10𝑝4+10𝑝5+10𝑝6+10𝑝7+10𝑝8+10𝑝9+10𝑝10*
所以我们可以得到这样的目标函数:
*[@objective](http://twitter.com/objective)(prgrm, Min, sum(p .* q) + sum(y .* f) + sum(I .* k))*
我们得到的解如下:

更一般的例子
为了一般化,这次让我们假设生产成本和持有成本在时间上是可变的,所以让我们考虑这个例子:
*T = 10
f = 1000
q = [5, 10, 10, 5, 8, 2, 7, 8, 9, 10]
k = [0, 10, 5, 5, 10, 5, 8, 2, 2, 2, 8]
D = [80,30,50,100,5,5,25,100,200,100]
Imax = 200*
请注意,由于在目标函数中仍然有效的元素乘积,它不会改变程序中的任何内容,因此您应该获得以下分配:

减少使用的变量数量
如果我们想一想,股票直接依赖于生产;实际上,在一瞬间 t,它是我们在 t 之前生产的数量减去在 t 之前的需求总和,这样我们就可以在约束条件下代替 Iₜ* 。*

从而获得:

这意味着生产至少要满足每一步的需求。
那么库存变量在制定持有成本时是有用的,但是我们可以很容易地绕过它,只要我们给 cₜ一个包含生产成本和持有成本的成本。

因此,我们得到在 t 时刻生产的每单位产品的成本如下。
该程序可以如下实例化。
*prgrm = Model()
set_optimizer(prgrm, GLPK.Optimizer)
[@variable](http://twitter.com/variable)(prgrm, p[1:T] >= 0)
[@variable](http://twitter.com/variable)(prgrm, 1 >= y[1:T] >= 0, Int)
[@variable](http://twitter.com/variable)(prgrm, I[0:T] >= 0) #We index from 0 to set an initial storage
for i in 1:T
[@constraint](http://twitter.com/constraint)(prgrm,sum(p[1:i]) - sum(D[1:i]) >= 0)
end
for i in 1:T
[@constraint](http://twitter.com/constraint)(prgrm, sum(p[1:i]) - sum(D[1:i]) <= Imax)
end
for i in 1:T
M = min(sum(D[i:end]), Imax)
[@constraint](http://twitter.com/constraint)(prgrm, p[i] <= M*y[i])
end
c = []
for t in 1:T
e = q[t] + sum(k[t+1:end])
append!(c,e)
end
[@objective](http://twitter.com/objective)(prgrm, Min, sum(p .* c) + sum(y .* f))
prgrm*
我们得到以下解:

这很奇怪;就决策变量而言,我们得到了相同的解,但目标值不同。
事实上,我这样做是为了解释两件事,
第一个是,当定义一个时间步长的生产成本时,我们忘记了减去满足客户需求的持有成本,这代表

第二,这对决策变量没有重要性,因为一般来说,优化 f 或优化 f + a 是一回事,因为 a 不依赖于我们的决策(也不依赖于其他任何东西,因为它是一个常数)。
虽然如果我们必须减去它来检查,我们可以验证

作者图片
扩展公式
在上面的公式中,产量由单个聚合变量决定是现在生产还是以后生产。
让我们看看另一个公式,我们将不同的思考方式,将每个时间步的产量分成许多变量 wᵢₜ;每个代表在 I 上的生产,旨在满足 t 上的客户需求。
那么在每个时间步满足需求可以自然地表达为:

设置约束变为

请注意,我没有采用 Iₘₐₓ的界限,因为在特定时间步长的需求小于 Iₘₐₓ的需求是微不足道的(即使是在同一天,我们也必须在交付之前将其存储在某个地方)。
我们可以将容量限制写为:

这段代码可以创建完整的程序:
*prgrm = Model()
set_optimizer(prgrm, GLPK.Optimizer)[@variable](http://twitter.com/variable)(prgrm, w[1:T , 1:T] >= 0)
[@variable](http://twitter.com/variable)(prgrm, 1 >= y[1:T] >= 0, Int)for t in 1:T
for i in 1:t-1
[@constraint](http://twitter.com/constraint)(prgrm, w[t,i] == 0)
end
end
for t in 1:T
[@constraint](http://twitter.com/constraint)(prgrm,sum(w[1:t, t]) == D[t])
end
for t in 1:T
[@constraint](http://twitter.com/constraint)(prgrm, sum(w[t,:]) <= Imax)
end
for t in 1:T
for i in 1:t
[@constraint](http://twitter.com/constraint)(prgrm, w[i,t] <= y[i]*D[t])
end
end
c = []
for t in 1:T
e = q[t] + sum(k[t+1:end])
append!(c,e)
end
e = 0
for i in 1:T
e = e + c[i]*sum(w[i,i:end])
end
[@objective](http://twitter.com/objective)(prgrm, Min, e + sum(y .* f))
prgrm*
这给了我们以下结果:

作者图片
当然,我们得到了相同的结果,而且我们也注意到,正如我前面解释的,一个空值实际上对于 p₆.来说是一个很小的值
配方比较
现在,我将向你们展示一些关于组合问题的 MIP 公式的见解,然后用它们来比较我之前展示的不同公式。
尺寸很重要
像大多数算法一样,单纯形算法的效率取决于其输入的大小。
一般来说,我们所说的列和行是指程序的变量和约束的最终数量。
为什么我们不直接使用变量和约束呢?出于两个原因,
第一个是我们总是可以写一个矩阵形式为的线性程序,如下所示。

比如下面的矩阵。
*A= [ 1 1 9 5;
3 5 0 8;
2 0 6 13]
b = [7; 3; 5]
c = [1; 3; 5; 2]*
会给这个项目

作者图片
因此,变量(相应约束)的数量是矩阵 a 的列(相应行)的数量
第二是行和列的数量可能不同于正式模型中决策变量和问题约束的数量,正如您将在下面看到的。
这也给了我一个绝佳的机会来展示一些线性化技术。
实际上,你已经看到了线性化技术;之前,我们对逻辑含义进行了线性化,但由于这不是您可能感兴趣的唯一逻辑运算符,下面的表格概括了假设 x,y,z 为布尔变量时主要逻辑运算符的线性等价。

另一个我经常线性化的算子是最小(或最大)算子。要在程序中使用 x 和 y 之间的最小值,可以这样做:

作者图片
(如果你想知道,是的,我们可以在任何 Julia 字符串中使用 Latex 符号)
例如,这是一个程序,其中我们有 2 个决策变量和 1 个约束,但有 3 列 3 行。
你可能会问的另一个问题是,“程序中的最小值有什么用呢?”,
用途是多种多样的,但是我能给你的最简单的是我正在考虑写的关于多目标优化的另一篇文章的预览。
例如,想象一下,我们有 10 美元要在两兄弟之间分享。假设 x 和 y 是指定我们给每个兄弟多少钱的变量。
你会有和上面一样的情况,但是要考虑什么目标函数呢?
当然,我们希望给定的钱最大化,所以我们希望最大化 x+y ,但我们直觉上也希望对每个兄弟公平,而这样做的一个方法就是最大化每个兄弟得到的最小。
这给了我们以下。

作者图片
我要介绍的最后一个线性化是产品线性化。
你读书读得好;如果我们有两个布尔变量,我们可以将它们的二次组合线性化如下。

作者图片
综上所述,你可以用线性化技术来表达非线性算子。不过,如果您记住这样做涉及到增加行和/或列的效率成本,这将会有所帮助。
越紧越好
您可能还记得本系列的第一部分,约束是对一行之上或之下的半空间的限制,所以在制定我们的程序时,我们正在枚举限制我们搜索区域的行。
为了形象化,考虑一个组合问题,其中可行的解决方案是绿色的。

作者图片
我们能想到的一个提法,可能是按照虚线分割空间。

作者图片
但是我们也可以想象许多其他的公式,包括其他的线(约束)和围绕绿点,
我们能找到的最好的一个将完美地符合由像实线一样的绿点形成的多面体。

作者图片
为什么会是最好的?因为它的所有顶点都是整数解,所以我们说它完美地描述了可行解的多面体。
但是对于许多问题来说,很难找到一个完美的公式。
我们能认出一个完美的公式吗?
前面说过,线性规划的约束条件可以写成矩阵 A;因此,我们有霍夫曼-克鲁斯卡尔定理。
如果 A 是全幺模矩阵,那么它所定义的约束多面体的每个顶点都是整数。
但是什么是完全幺模矩阵呢?这个定义相当复杂,需要一些线性代数背景知识。事实上,我仍然会在这里给出幺模矩阵的定义,然后给出完全幺模矩阵的定义。
幺模矩阵:
如果一个矩阵是一个行列式为 1 或-1 的方阵,那么它就是幺模矩阵。
如果 A 是整数矩阵,那么我们有 Veinett-Dantzig 定理:
x = A⁻ b 是每个整数向量 b 的整数向量当且仅当 a 是幺模。
全幺模矩阵
完全幺模矩阵是这样一个矩阵(不一定是正方形的),其中每个正方形的非奇异的 子矩阵都是幺模的。
像这样公式化,识别完全幺模矩阵似乎不太容易,但是庞加莱指出了完全幺模矩阵的一个特例。
如果矩阵的系数是 1、0 或-1,并且在每一列中,我们有小于 1 倍的系数 1 和小于 1 倍的系数-1;那么矩阵是完全幺模的。
存在另一个更普遍的属性:
矩阵系数或者是 1,0 或者是-1,每一列具有少于 2 个非空系数,并且行可以被分成两个子集 I₁和 I₂,使得如果两列具有相同符号的系数,则它们应该属于不同的子集,反之亦然。
完美公式的例子
完美描述可行解的多面体的自然公式的一个众所周知的例子是最小成本流问题的公式;它对一般情况有效,但无电容的情况更容易证明,因为它是 Pointcaré定理的直接应用。
该问题包括通过最小化所取边的成本来寻找通过从源 s 到目的地 t 的边的流。

由于每个边出现在两个约束中(每个端点一个),一个具有系数-1,一个具有系数 1,矩阵是完全幺模的,因此公式是完美的。
回到我们的问题
现在我们有了一些比较同一问题的不同公式的背景,让我们比较扩展公式和有能力限制的批量问题的聚合公式。
根据变量的数量
扩展公式有 T 个连续变量。聚合公式只有 T 个连续变量,因此它在渐近上更重要,即随着时间间隔的延长,两个公式之间变量数量的差异不断增加。
尽管如此,由于变量是连续的,这不是一个大的缺点。
另一方面,它们有相似数量的连续变量和约束。
根据配方的紧密度
扩展公式比聚合公式更严密,因为如果我们检查设置约束,我们可以注意到:

这意味着,如果一个解决方案满足聚集公式的设置约束,它将满足扩展公式的设置约束;然后,扩展公式中的设置约束更加严格。
因此,由聚集公式的设置约束定义的约束定义了一个多面体,该多面体包含在由扩展公式定义的多面体中。因此,扩展的公式更严密。
哪种配方更好?
在我看来,是扩展的,因为增加连续变量的数量对于加强公式来说成本并不高。
对于无电容的情况,扩展的公式被证明是理想的公式,这一事实加强了这种直觉。
结论
MIP 提供了一个广泛的建模工具面板,可以线性化几乎所有的东西。这份关于它如何帮助我们对工业问题建模的简要概述向我们表明,理解一些线性建模见解可以提高我们模型的质量和性能。
我确切地说,这篇博文并不是要对线性优化领域进行详尽的研究,这个领域太深了,不可能这么快就涵盖;这只是我在开发或研究 MIP 配方时参考的一些基本概念。
如果你想更深入地理解批量问题及其应用,我推荐这篇文章。
确认
我要感谢我的运筹学老师 Sofia Kedad Sidhoum 和 Pierre Fouillhoux、 Nadym Mallek 、 Ludovic Benistant 、 Racha 萨尔希和 dhtdean 审阅我的文章。
Julia 上带跳转的混合整数规划综合研究(三)
我正在用分支-切割框架解决一个具有指数数量约束的问题

克劳迪奥·施瓦兹在 Unsplash 上的照片
是的,有可能。
即使这非常违反直觉,我们也可以处理带有指数数量约束的线性程序,只要我们有一个实用的(甚至接近的)方法来分离这些约束。
这个故事是这个和这个的延续,在这里我解释了我们如何使用线性规划来解决大型组合问题,现在我们将进入上面的层次,看看我们如何使用可能包含指数数量约束的更强的公式。
为此,我们将了解“分离过程”的含义,以及如何通过在分支-剪切框架中使用框架跳转的“惰性约束回调”来解决第一类非紧凑线性公式。
我们还将看到如何使用分支-剪切框架来加强具有“惰性约束回调”的紧凑公式
TSP(旅行推销员问题)的另一个变体
我选择提出的问题是旅行推销员问题,以说明如何使用分支切割公式;我做出这个选择有两个原因:第一个原因是,在对组合学问题解决感兴趣的人中,这是一个广为人知的问题;第二个原因是,在我看来,这是分支-切割公式在大规模解决问题方面帮助最大的问题。
更准确地说,我选择了有容量限制的车辆路径问题(CVRP)问题,因为它是 TSP 的一个有趣的变种,我们可以更好地想象具体的应用情况。
问题的描述
假设您有许多客户,每个客户的需求都定位在一张地图、一个仓库和一个由 K 辆卡车组成的车队上;您将如何组织交付,以最大限度地降低交付的总成本?
首先,我们将在 Julia 中定义一个表示客户端的类型;客户必须有自己的立场和需求;这可以使用 struct 关键字来完成。
struct Client
position
demand
end;
client1 = Client([0,10], 25)
生成一个随机实例,我们就说坐标在一个 100x100 的正方形里,需求是 15 到 30 之间的整数;以下函数为我们提供了一个随机客户端:
function random_client(pos_range, demande_range)
position = rand(pos_range, (1, 2))
demand = rand(demande_range, 1)
client = Client(position, demand[1])
return client
end;
client1 = random_client(1:100, 15:30);
现在我们有了客户的表示,我们可以定义问题的表示,除了客户之外,它还包含仓库的坐标、卡车数量 m 和它们的容量 Q 。
struct CVRP_Problem
clients
depot
m
Q
end;
因此,这是一个生成随机实例的函数。
function random_instance(n_clients, depot, m, Q)
clients = Dict([(i,random_client(0:100, 10:30)) for i in 1:n_clients])
problem = CVRP_Problem(clients, depot, m , Q)
return problem
end;
现在,为了可视化问题,让我们设置一个函数来显示问题。
function display_problem(problem)
x_pos = [c.position[1] for c in values(problem.clients)]
y_pos = [c.position[2] for c in values(problem.clients)]
scatter(x_pos, y_pos, shape = :circle, markersize = 6, label= "Client")
scatter!([problem.depot[1]], [problem.depot[2]], shape = :square, markersize = 8, label= "Depot")
end;
这个函数为我们提供了一个随机问题的如下图:

作者插图
问题的形式化
因为目标是最小化所有卡车行驶的总距离,而不是卡车的数量,所以解决方案可以用路线中的弧线来表示。
请记住,每个客户只能被一辆卡车访问,所以我们假设最大的需求小于卡车的容量。
所以这个解是每个弧的指示 e=(x,y) 是否被取用,所以决策变量将是每个 e. 的二进制变量 xₑ
现在我们将定义一些适当的符号:
首先,我们定义决策变量和的符号,给定一组边 E,我们定义:

之后,我们定义了“邻居”算子δ

所以,δ⁺是从一个顶点出发的弧线,δ⁻是到一个顶点的弧线;这里有一个例子来阐明这个概念。

作者插图
因此,要构建车辆路线,我们应该使用以下约束来约束解决方案,以确保每个客户端都被访问一次(一个入口和一个出口) :

因为每个客户的需求都低于一辆卡车的容量,如果一个客户被一辆卡车拜访,就足以运送他所要求的数量。
为了约束卡车的数量,我们还应该用下面的约束来约束仓库(顶点 0 ),使得出口不多于卡车的数量 m 。

目标是减少路线的总成本,即旅程所覆盖的总距离。
到目前为止,完整的程序由以下代码给出。
cvrp = Model(GLPK.Optimizer)
x=@variable(cvrp,x[0:length(problem.clients),0:length(problem.clients)],Bin)
@constraint(cvrp, sum(get_out(x, 0)) <= problem.m)
@constraint(cvrp, sum(get_in(x, 0)) <= problem.m)
for i in 1:length(problem.clients)
@constraint(cvrp, sum(get_in(x, i)) == 1)
@constraint(cvrp, sum(get_out(x, i)) == 1)
end;
obj_coef = []
for i in 0:length(problem.clients)
for j in 0:length(problem.clients)
append!(obj_coef, [get_cost(problem, i, j) * x[i,j] ] )
end;
end;
@objective(cvrp,Min,sum(obj_coef))
optimize!(cvrp)
termination_status(cvrp)
objective_value(cvrp)
但是这个公式足以解决车辆路径问题吗?—当然不是,不然这篇文章就太短了。
子图消除
子问题
如果我们运行这个先例程序,我们应该得到以下结果:

作者插图
这是个 bug 吗?—不,不是;下面的解决方案是一个非常适合程序的解决方案。问题出在程序本身,因为他允许子旅行团的存在。
此图显示了另一个子图示例,我们可以看到子图的每个顶点都满足其约束。

作者插图
在满足程序约束的解决方案中可能发现的另一个问题是接受以下解决方案:

作者插图
也不应该,因为旅行是不可实现的,因为旅行所经过的顶点的需求总和超过了一辆卡车的容量。
米勒-塔克-泽姆林不等式
米勒-塔克-泽姆林不等式允许为任何 TSP 变量编写一个紧凑的程序。
我们的想法是为每个顶点创建新的变量 w ,代表到达顶点时卡车中的数量。
然后,我们应该添加一个表示以下内容的约束:
如果弧线(a,b)是在一次游览中取的,那么 a 之前的数量和 b 之前的数量之差应该大于 a 中的需求。
我们自然有 w≤Q。
这可以用下面的不等式来表示:

因为如果 xᵢⱼ = 1,约束变成:

这就是我们所需要的,如果 xᵢⱼ = 0,这个约束给我们:

这是微不足道的,因此不会限制解决方案。
添加 MTZ 约束后,代码变为:
cvrp = Model(GLPK.Optimizer)
x=@variable(cvrp,x[0:length(problem.clients),0:length(problem.clients)],Bin)
@constraint(cvrp, sum(get_out(x, 0)) <= problem.m)
@constraint(cvrp, sum(get_in(x, 0)) <= problem.m)
for i in 1:length(problem.clients)
@constraint(cvrp, sum(get_in(x, i)) == 1)
@constraint(cvrp, sum(get_out(x, i)) == 1)
end;w = @variable(cvrp,w[0:length(problem.clients)])
for (i,vi) in problem.clients
for j in 0:length(problem.clients)
if(i == j)
continue
end;
c = @constraint(cvrp, w[i] - w[j] >= vi.demand - (problem.Q +vi.demand)*(1-x[i,j]))
end;
end;obj_coef = []
for i in 0:length(problem.clients)
for j in 0:length(problem.clients)
append!(obj_coef, [get_cost(problem, i, j) * x[i,j] ] )
end;
end;@objective(cvrp,Min,sum(obj_coef))
对于这个先例,它给了我们:

作者插图
资源能力限制
资源能力约束是替换和/或加强 MTZ 公式的约束族。
这些约束条件表明,如果我们有一组客户 s,他们的需求总和 D = ∑dᵢ (i ∈ S),那么我们至少需要 W(S)辆卡车为他们服务:

这些约束条件可以代替 MTZ 约束条件,因为它们使得涉及子旅游的解决方案无效。
这些不等式的问题在于,它们应该代表客户的每个子集,因此枚举它们将导致不相关的指数枚举。
解决方案是在切割平面方法中使用它们。
分枝切割框架
我们将陆续看到如何使用“用户切割”来替换 MTZ 不等式,然后使用“惰性切割”来加强所获得的公式。但是,首先,我们需要定义一个分离算法。
分离定理
分离算法是为给定的解决方案返回违反的约束(如果有的话)或者证明不存在任何违反的约束的算法。
假设分离算法在多项式时间内运行。在这种情况下,即使约束是指数级的,我们仍然可以优化对求解器的多项式调用。
分离问题
这里的分离问题是找到一个给定的解决方案,一组客户,这样

如果有的话,并注明

这相当于解决以下优化问题:

如果最小值为 0,则意味着不存在任何违反的不等式。
用分离算法分解子行程
我们可以用来消除 MTZ 不等式同时消除子路线的一个有吸引力的属性是,如果一个解决方案是整数的(请参见前面的章节以记住整数和分数解决方案之间的区别),那么验证连接组件上的资源容量约束就足以检查解决方案的有效性。
因此,我们应该通过添加违反的不等式来更新分支限界的工作流,以消除包含子旅程的集成解决方案:

作者插图
添加不等式也称为“切割”解决方案,这种方法是分支定界切割方法的一个示例,或者更简洁地说,是分支切割方法。
实现的分离函数应该放在“用户剪切”回调中,在每个解决方案上运行,直到它不返回任何违反的约束。
现在让我们看看如何在 Julia 中实现它,
首先,让我们看看我们需要的算符,来计算分离问题最小化的函数。
delta 运算符应该扩展到这样的一组顶点。

作者插图
一组顶点 S 的δ是在 S 中恰好有一个端点的边的集合,这可以实现如下:
function delta(problem, S, x)
L = []
for i in S
for j in 0:length(problem.clients)
if !(j in S)
i_min = min(i,j)
i_max = max(i,j)
if !(x[i_min,i_max] in L)
append!(L, [x[i_min,i_max]]);
end;
end;
end;
end;
return L
end;
然后,我们需要一个函数来计算一组客户端的总需求。
function demand_s(problem, S)
d = 0;
for c in S
d = d + problem.clients[c].demand;
end;
return d;
end;
这允许我们定义最小化的函数。
function W(problem, x, cs, cb_data)
L = delta_cb(problem, cs, x, cb_data)
return L - 2*ceil(demand_s(problem, cs)/problem.Q)
end;
最后,为了验证在形成一个子图的连通分支上 W 是否是负的,我们首先需要检测由一个解产生的图中的连通分支。
为此,我们将使用库“LightGraph.jl”,第一步是编写一个函数,从一组 x 变量创建一个图形对象。
function build_simple_graph(problem, x)
g = SimpleGraph(length(problem.clients));
for i in 0:nv(g)
for j in i+1:nv(g)
if(value(x[i,j]) == 1)
add_edge!(g, i, j)
end;
end;
end;
return g;
end;
然后,我们在没有 MTZ 约束的情况下运行,并可视化解决方案。

作者插图
现在,我们计算连接的组件,并查看它们上的函数 W 是否为负,这将违反资源约束。

我们看到,对于没有 MTZ 约束的公式返回的积分解,子图给出了函数 W 的负值。
另一个有趣的注意事项是,此公式具有更好的收敛速度,因此现在我们可以解决更重要的实例,如下所示:

作者插图
请忽略箭头的方向,如这篇文章;我转向使用无向图来减少变量的模型化。
顺便说一下,这是创建线性程序的代码:
cvrp = Model(GLPK.Optimizer)
n = length(problem.clients)
x = @variable(cvrp,x[i= 0:n, j = i+1:n ],Int)
for i in 0:n
for j in i+1:n
if( i == 0)
c = @constraint(cvrp, 0 <= x[i,j] <= 2)
else
c= @constraint(cvrp, 0 <= x[i,j] <= 1)
end;
end;
end;
c= @constraint(cvrp, sum(delta(problem, [0], x)) == 2*problem.m )
for i in 1:n
δ = delta(problem, [i], x)
if(length(δ) == 0)
continue
end;
c = @constraint(cvrp, sum(δ) == 2 )
end;
obj_coef = []
for i in 0:n
for j in i+1:n
append!(obj_coef, [get_cost(problem, i, j) * x[i,j] ] )
end;
end;
@objective(cvrp,Min,sum(obj_coef))
这是我用来检查每个整体解决方案的连接组件是否满足资源约束的回调:
function ressource_constraints(cb_data)
status = callback_node_status(cb_data, cvrp)
if(status == MOI.CALLBACK_NODE_STATUS_INTEGER)
g = build_simple_graph_cb(problem, x, cb_data)
comp = connected_components(g)
for c in comp
f = W(problem, x,c, cb_data);
if(f <0 )
con = @build_constraint(sum(delta(problem, c, x)) >=2*ceil(demand_s(problem, c)/problem.Q))
MOI.submit(cvrp, MOI.LazyConstraint(cb_data), con)
end;
end;
end;
end;
MOI.set(cvrp, MOI.LazyConstraintCallback(), ressource_constraints);
它为我们提供了以下输出:

因此,我们可以看到项目的不断丰富,减少了解决方案中的子项目。
额外奖励:树枝和切割加固
最后,我们将解释使用分支剪切框架的另一种方式。
分离算法现在将用于加强公式。这意味着分离算法不再是约束定义的一部分。
不过,现在,它在优化过程中被调用,以生成新的削减,将收紧公式。
为此,我们必须将分离过程定义为“惰性切割”,使用分支切割作为惰性约束的优点是,我们可以使用元启发式搜索潜在的切割,因为即使我们没有找到所有的切割,也不会导致不可行的解决方案。
例如,在前面的代码中,我们可以添加,我们寻找一组违反资源约束的客户端,即使是部分解决方案。通过这样做,我们创造了额外的削减,将加强趋同。
要了解更多信息,你可以阅读这篇文章,其中禁忌搜索被用来分离 CVRP 问题的资源不平等。
结论
分支-切割方法是考虑组合问题的一种令人兴奋的方法,因为它们需要考虑有效的紧不等式以及如何有效地分离它们。
18 个 Python 片段中 Numpy 的实用摘要

作者图片
用几个 Python 片段总结 NumPy 文档
很长一段时间以来,Numpy 一直是 Python 编码人员进行数组操作的普遍选择,它构建在 C 之上,这一事实使它成为执行数组操作的快速而可靠的选择,并且它一直是机器学习和数据科学工作流的支柱。
在本文中,我想浏览一些 NumPy 代码的基本片段,举例说明它的一些主要功能。
1.创建数组
import numpy as np
new_array = np.array([1,2,3])
print(new_array)
# Output[1 2 3]
2.获取 Numpy 数组的形状、维度和大小
# shape
print(new_array.shape)
# dimensions
print(new_array.ndim)
# size
print(new_array.size)
# Output(3,)
1
3
3.描述 Numpy 数组中元素的类型
array = np.arange(0,10,1)
print(array.dtype)
# Outputint64
4.获取数组中每个元素的字节大小
array = np.array([1,2])
print(array.itemsize)
# Output8
5.在创建时指定数组的类型
array = np.array([[1,2], [3,4]], dtype=complex)
array
# Outputarray([[1.+0.j, 2.+0.j],
[3.+0.j, 4.+0.j]])
6.创建包含占位符内容的数组
# Array of zeros
array = np.zeros((3,4))
print(array)
print("---")
# Array of ones
array = np.ones((1,2))
print(array)
print("---")
# Empty array with shape (2,3)
array = np.empty((2,3))
print(array)
print("---")
# Output[[0\. 0\. 0\. 0.]
[0\. 0\. 0\. 0.]
[0\. 0\. 0\. 0.]]
---
[[1\. 1.]]
---
[[4.67280967e-310 0.00000000e+000 0.00000000e+000]
[0.00000000e+000 0.00000000e+000 0.00000000e+000]]
---
7.创建数字序列
# Creating a sequence from 0 to 42 elements with elements increasing by 1 using np.arange
array = np.arange(0,42,1)
print(array)
print("---")
# Inserting 42 elements between 0 and 100 with np.linspace
array = np.linspace(0,100,42)
print(array)
print("---")
# Output[ 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41]
---
[ 0\. 2.43902439 4.87804878 7.31707317 9.75609756
12.19512195 14.63414634 17.07317073 19.51219512 21.95121951
24.3902439 26.82926829 29.26829268 31.70731707 34.14634146
36.58536585 39.02439024 41.46341463 43.90243902 46.34146341
48.7804878 51.2195122 53.65853659 56.09756098 58.53658537
60.97560976 63.41463415 65.85365854 68.29268293 70.73170732
73.17073171 75.6097561 78.04878049 80.48780488 82.92682927
85.36585366 87.80487805 90.24390244 92.68292683 95.12195122
97.56097561 100\. ]
---
8.Numpy 数学函数
import numpy as np
import matplotlib.pyplot as plt
# sine function
x = np.linspace(0,2*np.pi, 100)
f = np.sin(x)
plt.figure(figsize=(15,7))
plt.subplot(1,3,1)
plt.plot(f, color="green")
plt.title("np.sin(x)")
# cosine function
f = np.cos(x)
plt.subplot(1,3,2)
plt.plot(f, color="blue")
plt.title("np.cos(x)")
# tangent function
f = np.tan(x)
plt.subplot(1,3,3)
plt.plot(f, color="red")
plt.title("np.tan(x)")
plt.show()
# Output

作者图片
9.通过对每个坐标执行函数来创建数组
some_function = lambda x: np.cos(x)+1
array = np.fromfunction(some_function, (100,))
plt.figure(figsize=(15,7))
plt.plot(array, color="green")
plt.title("np.cos(x) +1")
plt.show()# Output

作者图片
10.迭代 Numpy 数组的所有元素
a = np.arange(0,23,1)
for i in a.flat:
print(i)
# Output0
1
2
...
22
11.获取浮点数的底数
np.floor(10.5)10.0
12.用展平数组。拉威尔()
array = np.full(shape=(5,5),fill_value=10)
print(array)
print("---")
print("Flattened array:")
print(array.ravel())
# Output[[10 10 10 10 10]
[10 10 10 10 10]
[10 10 10 10 10]
[10 10 10 10 10]
[10 10 10 10 10]]
---
Flattened array:
[10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10
10]
13.获取数组的转置
array = np.random.random((2,5))
print(array)
print(array.T)[[0.18735704 0.22800582 0.02552177 0.93552346 0.20720663]
[0.74303284 0.1897481 0.91389602 0.23099501 0.07565492]]
[[0.18735704 0.74303284]
[0.22800582 0.1897481 ]
[0.02552177 0.91389602]
[0.93552346 0.23099501]
[0.20720663 0.07565492]]
14.整形用。重塑()和。调整大小()
a = np.random.randint(100,size=(3,4))
print(a)
a_reshaped = np.reshape(a, (1,12))
print(a_reshaped)
# modifying the array itself with .resize()
a.resize((1,12))
# Output[[29 18 39 24]
[53 45 49 8]
[90 75 61 61]]
[[29 18 39 24 53 45 49 8 90 75 61 61]]
15.沿着不同的轴堆叠不同的阵列
a = np.random.random((2,2))
print(a)
b = np.random.random((2,2))
print(b)
# Stacking along the vertical axis (more rows)
print(np.vstack((a,b)))
print(np.vstack((a,b)).shape)
# Stacking along the horizontal axis (more columns)
print(np.hstack((a,b)))
print(np.hstack((a,b)).shape)
# Column stacking
print(np.column_stack((a,b)))
# Output[[0.67028492 0.86322792]
[0.38906266 0.36967583]]
[[0.51419553 0.21937852]
[0.50375453 0.31634597]]
[[0.67028492 0.86322792]
[0.38906266 0.36967583]
[0.51419553 0.21937852]
[0.50375453 0.31634597]]
(4, 2)
[[0.67028492 0.86322792 0.51419553 0.21937852]
[0.38906266 0.36967583 0.50375453 0.31634597]]
(2, 4)
[[0.67028492 0.86322792 0.51419553 0.21937852]
[0.38906266 0.36967583 0.50375453 0.31634597]]
16.将一个数组拆分成几个更小的数组
# Using hsplit, you can split an array along its horizontal axis, either by
# specifying the number of equally shaped arrays to return, or by specifying
# the columns after which the division should occur
# Spliting a into 5 smaller arrays along the horizontal axis
a = np.arange(0, 5, 1)
print("Horizontal split")
print(np.hsplit(a, 5))
print("---")
# Spliting a into 5 smaller arrays along the vertical axis
a = np.random.random((5,5))
print("Vertical split")
print(np.vsplit(a, 5))Horizontal split
[array([0]), array([1]), array([2]), array([3]), array([4])]
---
Vertical split
[array([[0.69059321, 0.55703093, 0.20019592, 0.19697317, 0.37278251]]), array([[0.24597633, 0.87216661, 0.634432 , 0.35326185, 0.03130537]]), array([[0.18063077, 0.45045441, 0.06882852, 0.91273837, 0.07332161]]), array([[0.61738939, 0.11291748, 0.73152623, 0.49177006, 0.95750985]]), array([[0.90212777, 0.53825846, 0.86733505, 0.76165564, 0.17337721]])]
17.创建数组的浅表副本
a = np.array([
[0,1,2,3,4],
[5,6,7,8,9],
[10,11,12,13,14]
])
# The .view() method creates an array object that looks at the same
# data, so it creates a shallow copy of that array
array_object = np.arange(0,10,1)
shallow_copy_object = array_object.view() # shallow copy
print("Array")
print(array_object)
print(f"Id = {id(array_object)}")
print("---")
print("Shallow Copy")
print(shallow_copy_object)
print(f"Id = {id(shallow_copy_object)}")
print("---")
# In this case the array object will change to match all the
# elements in the shallow object that were assigned to the new value
shallow_copy_object[0] = 200
print("After assigment: shallow_copy_object[0] = 200")
print("Array")
print(array_object)
print("Shallow copy")
print(shallow_copy_object)
# OutputArray
[0 1 2 3 4 5 6 7 8 9]
Id = 139980496768528
---
Shallow Copy
[0 1 2 3 4 5 6 7 8 9]
Id = 139980496768720
---
After assigment: shallow_copy_object[0] = 200
Array
[200 1 2 3 4 5 6 7 8 9]
Shallow copy
[200 1 2 3 4 5 6 7 8 9]
18.创建数组的深层副本
# The copy method makes a complete copy of the array and its data.
array_object = np.arange(0, 23, 1)
deep_copy_object = array_object.copy()
print(deep_copy_object is array_object)
print("Array")
print(array_object)
print(f"Array id = {id(array_object)}")
print("---")
print("Deep Copy")
print(deep_copy_object)
print(f"Deep copy id = {id(deep_copy_object)}")
print("---")
# assigning new value to the deep copy will not change the original array
deep_copy_object[0] = 234
print("After assignment: deep_copy_object[0] = 234")
print("Array")
print(array_object)
print("Deep copy")
print(deep_copy_object)False
Array
[ 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22]
Array id = 139980498767472
---
Deep Copy
[ 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22]
Deep copy id = 139980496039824
---
After assignment: deep_copy_object[0] = 234
Array
[ 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22]
Deep copy
[234 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
18 19 20 21 22]
开始练习 Numpy!
通过这些例子,我尝试了一下 NumPy 包中的一些功能,如果您想更深入地了解 NumPy 数组,请在这里查看该项目的精彩文档:
https://numpy.org/devdocs/user/quickstart.html
如果你喜欢这个帖子,加入 Medium ,跟随。还有,订阅我的 youtube 频道在 Tiktok 、推特、LinkedIn&insta gram上和我联系!谢谢,下次再见!😃
https://lucas-soares.medium.com/subscribe
卷积神经网络的概念解释
对用于计算机视觉目的的流行算法的内部工作原理的温和介绍

巴德·赫利松在 Unsplash 上的照片
计算机视觉:机器学习的分支,看似复杂,其实复杂,而且永远复杂。正如 Instagram 的创始人迈克·克里格(Mike Krieger)所说,“计算机视觉和机器学习确实已经开始起步,但对大多数人来说,计算机在查看图像时看到的东西的整个概念相对模糊”。希望到本文结束时,您将从卷积神经网络的角度对引擎盖下发生的事情有所了解。
由于计算机视觉能够解决的光学问题的广度,因此对了解计算机视觉的程序员有很大的需求。使用计算机视觉,我们可以进行面部识别,分析医学图像,实现自动驾驶,最重要的是,在 Snapchat 上使用整洁的过滤器。许多组织正在使用这样的算法来完成惊人的壮举。Veo Robotics 是一家快速增长的初创公司,它创造了 Veo FreeMove,这是一种 3D 安全保护系统,可以使用计算机视觉在制造工作场所实现机器人和人类之间更安全的协作。让我们担忧但又着迷的 Teslas 的自动驾驶功能源于先进的物体探测算法。斯坦福大学的一个研究小组正在利用 X 射线图像来量化患者膝关节骨关节炎的严重程度,使用的是卷积神经网络!关键是,机器学习的这个看似小众的分支植根于大量的应用,包括社交媒体、医疗保健、研究甚至制造业。在这篇博客中,让我们专注于一种用于图像分类的流行算法:卷积神经网络(CNN)。
在数学中,我们知道卷积是两个函数相互作用产生一个新的输出。然而,在计算机视觉领域,卷积是通过输入图像进行过滤以获得更有意义的输出的过程。现在我们已经定义了卷积,让我们考虑一下 Mike 所关心的当给定一幅图像时,计算机实际上看到了什么。看看你手机/桌面上的壁纸。假设它不是一个空白的屏幕,你可以看到定义人、动物、风景或任何物体的外部轮廓和内部细节的边缘。你的大脑使用这些边缘的集合和它们之间填充的颜色来识别物体。CNN 本质上做着同样的事情。当我们给 CNN 一个输入图像时,它会看到一组数字,这些数字类似于所述图像的像素强度。像素亮度范围从 0(黑色)到 255(白色)。我们也可以选择处理 RGB、灰度或黑白图像。为了图像分类的目的,使用黑/白图像将节省大量的计算费用,但是由于许多边缘将不会被表示,所以这不是理想的。根据你正在进行的项目,决定你是想保持彩色图像还是灰度图像是很重要的,尽管后者会节省你的计算成本。这是因为彩色图像对于每种基本颜色(红、蓝、绿)消耗三个字节的存储空间。

高斯模糊应用于广泛用于图像处理领域的 Lenna 测试图像的例子。纳吉·阿诺在 Unsplash 上的照片
高斯模糊
在进入 CNN 之前,让我们再多讨论一下图像预处理。处理图像时,图像中的对象通常会有不清晰的边界。这导致 CNN 看到许多所谓的“假边缘”,最终导致分类、检测等方面的不良表现。因此,有时有必要降低像素强度(模糊图像)。通常的做法是使用 Keras 中的高斯模糊算法来进行这个去噪步骤。
过滤器和要素地图
然后,它使用过滤器水平和垂直扫描图像,以识别边缘。作为将来的参考,一个内核是一个单一的过滤器,即一个 2D 权重数组。一个过滤器由许多内核组成。梯度或像素强度的差异被这些滤波器捕获,其中较大的幅度梯度对应于较大的边缘存在。索贝尔过滤器是一种常用的过滤器,由于其水平和垂直扫描的能力,可以实现这一点。垂直扫描将揭开水平边缘,反之亦然。可以将滤镜想象成一个权重矩阵,它与像素强度相乘,以形成一个捕捉原始图像特征的新图像。然后对这些乘积求和,以获得输出图像的相应像素位置的输出值。这个新图像被称为特征图。通过反向传播,CNN 能够更新包含在这些过滤器中的权重,从而允许创建最能捕捉信息的特征地图。一旦学习了权重,它们就被用于该层中的所有神经元。在神经网络的卷积层中,应用这些过滤器并创建特征图。在 Keras 中,构建卷积层的第一个输入是滤波器的数量。请确保输入的值是 2 的幂。
步幅和衬垫
但是过滤器扫描过程到底是如何工作的呢?很高兴你问了。从概念上讲,正如我们在上面可爱的 gif 中看到的,内核应用于部分输入图像(它只显示了一个内核,尽管这个扫描是由多个内核完成的)。请注意,在这种情况下,内核每次移动一个像素时,会多次读取图像的同一部分。这种移动的幅度被称为步幅。在 Keras 中,步幅是另一个输入。随着我们增加步数,特征图变得更加浓缩。如果输入图像边缘的像素是单独输入的,则不会被过滤器多次读取。因此,填充是为了避免这个问题。填充基本上是在输入图像的最外边缘添加人工值(通常为零),以便滤波器更好地捕捉最外边缘的数据。
解剖美国有线电视新闻网
现在我们已经定义了所有的利基术语,让我们把它们放在彼此的上下文中。使用 Keras 可以很容易地构建 CNN。在您的模型中,您将需要卷积层,如前所述,将滤波器中包含的权重乘以输入图像,生成特征图。一个常用的激活函数是 ReLu。在每个卷积层之后,通常的做法是创建一个池层来降低特征图的维度。这基本上浓缩了模型在输入图像的每个局部区域中捕获的最重要的特征。此外,我们可以使用批量规范化层后,每一层包含一个激活函数。这减少了内部协变量偏移,从而允许更快、更稳定地训练神经网络。最后,在你的模型中包含下降层以避免过度拟合是一个很好的实践。这些层丢弃来自前一层的输出的一部分,并用零替换它们,从而改变后续层的输入。最后,可以应用一个展平层(根据您的需要)来获得一个一维数组,以馈入您的模型的输出层。出于分类的目的, softmax 函数可用于获得一个概率输出,该概率类似于图像属于某个类别。

作者创作的图形
请记住,所有这些信息仅仅是 CNN 的皮毛。我们从鸟瞰的角度来看这个模型。有许多移动部件需要考虑,根据项目的规模,调整模型以获得最佳参数需要大量的时间和成本。我强烈建议自己做一个图像分类项目,以便更好地理解这个模型的工作原理。一如既往,如果您有任何问题,请随时联系我们!
一本食谱:用距离来衡量相似性
一本用距离度量相似性的编码食谱。
介绍
本文提供了一本关于实现量化相似性的距离度量的食谱。我为这本食谱提出的具体用例是查看一系列观察结果,然后选择其中一个观察结果作为参考,最后使用距离度量来识别哪些观察结果与参考观察结果相似。

布鲁诺·沃尔夫在 Unsplash 上拍摄的照片。有多远?在超空间中,接近表示相似。这本编码指南探讨了在测量一个或多个观察之间的相似性时,如何使用距离度量。
使用距离度量来度量相似性并不新颖。实际上,这种数学是聚类、因子分析、成分分析和其他技术的重要组成部分。
当您的分析目标是将参考观察值与其他观察值进行比较时,识别与参考观察值相似的观察值是一个有用的练习。了解哪些其他观察结果在客观上和经验上是相似的是有帮助的——这样你就可以比较苹果和苹果,而不是苹果和橙子。
贯穿这本食谱的是代码片段。在这篇文章的底部是一个支持笔记本暂存空间的链接。后续文章将使用美国教育部关于高等教育机构的数据展示这些技术(敬请关注)。
入门指南
您需要的第一部分代码是标准导入。
**import** **pandas** **as** **pd**
**import** **numpy** **as** **np**
**from** **math** **import** sqrt
**from** **scipy.spatial** **import** distance
**from** **scipy.stats** **import** zscore
从这些进口中,你可以推断出未来的情况。比如我们会用熊猫和 Numpy。我们还需要计算平方根。同样,我们将使用 SciPy 的 zscore 函数。
我将展示如何从头开始计算三个距离度量,包括欧几里德距离、汉明距离和雅克卡距离。对于欧几里得距离,我们将在 SciPy 的帮助下以第二种方式复制这些结果。
数据
这本食谱虚构的例子数据与学院和大学有关。该数据是机构级别的数据。该数据提供了有关该机构的注册人数和学费的信息。
以下数据支持下面的食谱。
data = {'Inst':['Institution A','Institution B',
'Institution C','Institution D'],
'Size':[19000,11500,7750,23000],
'Cost':[22000,19000,12000,10500],
'Accept Rt':[.25,.45,.76,.99],
'isBig':[1,0,0,1],
'isExpensive':[1,1,0,0,],
'isSelect':[1,0,0,1]}df = pd.DataFrame(data)
df
它产生以下输出:

鸣谢:作者提供的虚构数据。
基本原则
在深入研究代码之前,让我们回顾一下基本原理,并激活我们对这个主题的直觉。要了解基本原理,请参考图 1,该图显示了散点图上的四个观察值。
使用假设的数据,图 1 使用机构的规模( y 轴)和机构的年学费( x 轴)显示了四个机构在散点图上的位置。图 1 没有显示具体的单位或比例,因为为了演示,单位和比例是不必要的。

图一。机构 A、B、C 和 d。作者的创作。机构 A 既大又贵。机构 B 规模适中,成本适中。C 机构规模较小,价格实惠。而 D 机构既大又实惠。
假设你研究机构 d,进一步假设你想选择一个比较机构。你会选择哪个机构?在这张图表上找出机构 D 和其他机构之间的字面距离,可以显示出哪家机构最相似。
回到代码
这本食谱的其余部分关注于计算机构 D 和其他潜在的比较机构 A 到 c 之间的距离的代码。
欧几里德距离
计算欧几里得距离的核心是勾股定理。图 2 中的虚线显示了机构 D 和机构 b 之间的距离

图二。机构 A、B、C 和 d。作者的创作。计算机构 D 和机构 B 之间的距离就像 a + b = c 一样简单(勾股定理)。
下面的代码显示了如何计算机构 D 和 B 之间的距离(图 2 中的红色虚线)。
*# Sample data* D = [10,2] # Ref y=10, x=2
B = [3,11] # Comp y=3, x=11
*# Calculate from scratch*
rise = D[0] - B[0] # Find length of segment a
run1 = D[1] - B[1] # Find length of segment bdist = sqrt(rise**2 + run1**2)
print('Distance from scratch : **{}**'.format(dist))
*# Calculate with SciPy assist*
dist = distance.euclidean(D, B)
print('Distance with assist : **{}**'.format(dist))
上面的代码,产生了下面的输出(机构 D & B 之间的距离):
Distance from scratch : 11.40175425099138
Distance with assist : 11.40175425099138
为了使这个过程可重复,并让它支持增加的维度(不止两个,一个 x 和一个 y ),我们定义了以下函数:
**def** euclidian(data, ref, cols):
*'''Calculates Euclidian distance for all observations* *
relative to the referenced observation. Returns a list* *
of euclidian distances.* *data = A dataframe.* *
ref = A reference observation. Specify by axis 0 index.* *
cols = A set of comparison columns.'''* **if** type(ref) == int:
ref = ref
**if** type(ref) == str:
ref = df.index.get_loc(ref) distances = []
ref_observation = data[cols].apply(zscore).iloc[ref] **for** row **in** range(len(data)):
comp_observation = data[cols].apply(zscore).iloc[row]
dist = distance.euclidean(ref_observation,
comp_observation)
distances.append(dist) **return**(distances)
实施:
df['Euclidians'] = euclidian(data=df, ref=3,
cols=['Size','Cost','Accept Rt'])
df[['Inst','Size','Cost','Accept Rt','Euclidians']]
它产生以下输出:

鸣谢:作者提供的虚构数据。
使用机构规模、学费和录取率的度量,该度量显示机构 C 是与机构 D 最相似的机构。还要注意,机构 D 的距离是 0。当然,D 机构也会和自己一模一样(D 机构和自己零距离)。
汉娩距
这种方法的一个优点是它可以很好地处理分类数据。缺点是,为了获得最佳结果,您首先需要将连续数据转换为分类数据。
这些转换降低了衡量标准的可解释性,并且需要大量的规划。该计划包括在将连续数据转换为分类数据时决定在哪里放置切割点。
图 3 显示了分类数据的例子。变量isBig表示学校是大还是小,isExpensive ( isExpe~e)表示学费是否高。而isSelect表示该院校本科录取率是否低于 50%。

图二。作者的创作。虚构的分类数据。
汉明距离通过计算匹配值的数量来工作,计数越高,观察值越接近。
使用这种方法,机构 D 和机构 C 之间的距离为 1。机构 D 和机构 B 之间的距离为 0。那么机构 D 和机构 A 之间的距离是 2。该方法的结果表明机构 A 与机构 d 最相似。
为了实现这些计算,我们定义了一个函数:
**def** hamming(data, ref, cols):
*'''Calculates Hamming distance for all observations*
*relative to the referenced observation. Returns a list*
*of hamming distances.*
*data = A dataframe.*
*ref = A reference observation. Specify by axis 0 index.*
*cols = A set of comparison columns.'''*
**if** type(ref) == int:
ref = ref
**if** type(ref) == str:
ref = df.index.get_loc(ref)
distances = []
ref_observation = data[cols].iloc[ref]
**for** row **in** range(len(data)):
comp_observation = data[cols].iloc[row]
matches = sum([1 **if** x == y **else** 0
**for** x, y **in** zip(comp_observation,
ref_observation)])
distances.append(matches)
**return**(distances)
实施:
df['Hamming'] = hamming(data=df, ref=3,
cols=['isBig', 'isExpensive', 'isSelect'])
df
它产生以下输出:

鸣谢:作者提供的虚构数据。
使用机构规模、学费和录取率的测量值(作为分类测量值),由hamming函数暗示的该测量值显示机构 A 是与机构 d 最相似的机构。使用这些数据和该方法,结果与欧几里德方法产生的结果不同。
Jaccard 索引
Jaccard 索引也适用于分类数据。Jaccard 索引计算距离的方法是,将匹配维度的数量放在维度总数上,然后从 1 中减去该结果。
以下函数实现了 Jaccard 索引计算:
**def** jaccard(data, ref, cols):
*'''Calculates Jarcard index for all observations*
*relative to the referenced observation. Returns a list*
*of Jaccardian distances.*
*data = A dataframe.*
*ref = A reference observation. Specify by the axis 0 index.*
*cols = A set of comparison columns.'''*
**if** type(ref) == int:
ref = ref
**if** type(ref) == str:
ref = df.index.get_loc(ref)
distances = []
length = len(cols)
ref_observation = data[cols].iloc[ref]
**for** row **in** range(len(data)):
comp_observation = data[cols].iloc[row]
matches = sum([1 **if** x == y **else** 0
**for** x, y **in** zip(ref_observation,
comp_observation)])
distances.append(1 - (matches / length))
**return**(distances)
实施:
df['Jaccard'] = jaccard(data=df, ref=3,
cols=['isBig', 'isExpensive', 'isSelect'])
df[['Inst','isBig', 'isExpensive', 'isSelect','Jaccard']]
它产生以下输出:

鸣谢:作者提供的虚构数据。
使用 Jaccard 指数,D 到 C 的距离是. 66,D 到 B 的距离是 1,D 到 A 的距离是. 33。因此,Jaccard 指数表明,机构 D 与机构 A 最为相似,机构 A 同意汉明方法,但不同意欧几里德方法。
结论
最后一个图,图 5 显示了最终的一组结果。图 5 包括一个名为Accept Rt的列,它对应于isSelect,如上所述。图 5 显示了所有观测值与参考观测值(观测值 3)的距离(机构 D-红色方框)。

图 5。最右边的三列提供了机构 D 和所有其他机构之间的距离度量。鸣谢:作者提供的虚构数据。
从这些结果中,如图 5 中最右边的三列所示,我们看到各种方法并不总是一致的。Jaccard 和 Hamming 方法同意机构 A 与机构 d 最相似,而欧几里德距离表明机构 C 与机构 d 最相似。
还要注意,对于 Jaccard 指数和欧几里德距离,较小的结果表示更接近(得分类似于高尔夫)。对于海明距离,较大的结果表明更接近(得分像篮球)。
寻找更多代码?我提供一个配套支持暂存空间的笔记本和储存库。
为了在几十个或几百个观察值之间进行比较,这些距离方法可以帮助快速确定哪些观察值与任何给定的参考观察值相似。然后,通过对该距离进行排序,X 个最接近的观察值可以作为合理的比较组。
https://adamrossnelson.medium.com/membership
感谢阅读
感谢阅读。把你的想法和主意发给我。你可以写信只是为了说声嗨。如果你真的需要告诉我是怎么错的,我期待着尽快和你聊天。推特:@ adamrossnelson| LinkedIn:亚当·罗斯·纳尔逊 |脸书:亚当·罗斯·纳尔逊。
协方差分析
从外部因素对新冠肺炎病例影响的国别分析到未来疫苗接种率的预测
在本文中,我们将介绍
- 变量之间的关系,如一个国家的贫困率,预期寿命率,严格指数,covid 病例和死亡
- 截至 6 月底每个国家的疫苗接种率及其预测
- 我是如何处理数据和分析的(只有 jupyter 笔记本部分)

Covid 分析仪表板—按作者分类的图像
在过去的一年里,新冠肺炎吸引了全世界的注意力。关于这种给世界带来巨大灾难的病毒,还有许多问题没有得到解答。因此,实际上任何一种分析都可以为我们自己和潜在的社会带来更好的理解和有用的见解。因此,我希望下面的分析能让读者更好地了解哪些因素可能会影响某个国家的新冠肺炎感染率和死亡率。现在,请记住,这当然不能让我们看到全貌。例如,在我看来,识字率,人口密度,肥胖率,文化方面,如社会规范,行为等。所有这些都在每个国家的病例或死亡人数的增加中发挥了巨大的作用,但我没有所有的数据,其中一些因素甚至无法用可用的方法来衡量。
开始分析。先说数据。我从数据 中的 我们的世界得到了这个惊人的数据集。各列的描述和来源在 这里 都有。我自己计算了几列数据,即全国疫苗接种率、检测率、感染率和死亡率。使用该数据集的分析范围令人难以置信,本文仅涵盖其中的一部分!

Python 片段基本分析:列列表、数据形状、日期范围和分组
数据由上述 42 列组成,其中“位置”是国家列,每个国家的数据范围从 2020 年 1 月 1 日到 2021 年 4 月 10 日。虽然大部分数据几乎在这一范围开始时就可以获得,但我们知道检测是在很久以后才提供的,疫苗接种更是如此——对大多数发达国家来说是在 12 月下旬。

数据源片段-作者 GIF
内容
- 检测率对艾滋病感染率和死亡率的影响
- 严格性指数对 covid 感染率和死亡率的影响
- 年龄对 covid 相关死亡的影响
- 贫困对预期寿命的影响(与 covid 无关)
- 各国疫苗接种率和未来疫苗接种率预测
1.检测率对 covid 感染率和死亡率的影响
积极的检测对抑制死亡和感染有影响吗?或者高感染率和死亡率一定意味着更多的检测吗?

Tableau 即:测试对病例和死亡的影响——作者图片
注意:不幸的是,嵌入交互式 Tableau 仪表板相当具有挑战性。此处的 vizes 可以跟随https://public.tableau.com/profile/deepika1871#!/vizhome/Covid_Analysis_16180481664000/Vaccinations。
上面是一个可视化的表格,显示了各个国家的感染率和死亡率,并通过检测率进行了着色。我认为“最大值”是一个集合,因为它给出了每一列的最新观察结果。例如,测试率通过以下方式衡量:
测试率=(总测试次数/总人数)100*
因为每个国家的 total_tests 的每个单元是每天测试的聚合和,所以 max testing_rate 将为我们提供该国家的最大测试率以及其最近的测试率。我们可以看到,没有特别明显的模式暗示着检测与感染率或死亡率之间的相关性。让我们来看看以下内容。

Tableau 即:测试对病例和死亡的影响 1 —作者图片
请注意,测试率超过 100%是因为每个测试记录都显示了一个国家的总测试次数,而在这个国家中,一个人可能会被测试不止一次。
左边是一个散点图,代表按国家分列的最近和最高感染率与最近和最高检测率。值得注意的是,虽然它显示了一种微妙的相关性,指向更高的感染率,意味着更高的检测率,但它并没有强烈地这样做。右边分析检测率和死亡率之间关系的散点图也是如此,它看起来比前者更分散。当徘徊在较低的位置时,很明显是中低收入国家,如巴西、秘鲁、哥伦比亚、印度尼西亚等。这表明中低收入经济体的低检测率。让我们看看相关率:

Python 相关性片段-作者图片
虽然不能确定高感染率或高死亡率是否直接意味着更多的检测,但可以肯定的是,国家的经济地位等因素也可能发挥作用。由科学报告研究人员进行的一项有趣的研究确定,死亡率和检测率之间确实存在非常低的相关性,而根据《我们的世界》的数据报告和科学报告【2】,检测率和感染率之间的相关性虽然高于平均水平,但在高收入国家进一步提高。这种相关性差距可能归因于低收入国家缺乏检测资源,导致整体分析出现偏差。哪里缺乏测试,哪里就缺乏对现有案例的了解。在他们的研究中,通过将数据分成低、中、高收入国家的子组,然后进行个体回归分析【3】,弥补了这种差距/偏差。
2.严格性指数对 covid 感染率和死亡率的影响
在 covid 时代,作为一个在实施措施方面“更严格”的国家,感染或死亡情况会有所好转吗?或者高感染率和高死亡率会影响严格指数吗?

Tableau 即:严格指数对病例和死亡的影响—作者提供的图像
正如我们的世界在数据中所定义的,严格指数是一个国家在实施多少 covid 限制方面的严格程度,范围从 0 到 100,0 是最低的。严格性本身表明与死亡率或感染率的相关性相当低,因为用点表示的国家相当分散。死亡率和感染率的极少量差异可由严格性来解释,这可由以下相关统计数据来支持。

Python 相关性片段 1-作者图片
尽管人们会认为随着病例和死亡人数的增加,各国会变得更加严格,但对于一个国家的管理机构来说,严格性似乎是相当主观的。处于最严格范围内的国家(接近 100 个)来自全球各地,与世界其他地区相比,不一定所有国家都有极高的病例/死亡率(RoW)。缺乏文献来支持严格性水平对病例/死亡的影响的研究,这可能是因为该措施是新的。在 covid 病例激增后,各国可能面临更严格的措施,但是,与这一措施相关的统计数据可能无法准确反映现实。正如《我们的数据世界》所言,这是因为"它不能衡量或暗示一个国家应对措施的适当性或有效性。较高的分数并不一定意味着一个国家的反应比指数较低的其他国家更好。【3】
3.年龄对 covid 相关死亡的影响
65 岁以上人口比例高的国家死亡率高吗?

Tableau 即:老龄化对病例和死亡的影响——作者图片
是的,从上面的图中可以合理地说,这里有一个特定的持续模式,即死亡率的大部分差异可以通过是否有很大比例的人口处于 65 岁以上的年龄范围来解释。右下角的点是一个异常值,那里 27%的人口中有很大一部分是 65 岁及以上,然而,他们看到的死亡率与该行相比非常低。这一点不出意料的是日本。这是因为他们在技术上的进步,还是仅仅因为他们在免疫力和健康方面的名声?

Python 相关性片段 2—作者图片
从 python 中提取的相关性统计数据证实了死亡与 65 岁以上人口百分比之间高于平均水平的相关性。有多项研究表明,年龄在 covid 相关死亡率中起着至关重要的作用。一篇这样的科学文章有趣地声称“一些研究人员指出,家庭内社会互动的不同规模和特征、地方性感染和人口年龄中位数等因素影响了新冠肺炎风险和死亡率”【4】。世卫组织报道,不用解释,由于伴随老龄化而来的生理变化和潜在条件,老年人群面临着更大的感染和感染死亡的风险。
4.贫困对预期寿命的影响(与 covid 无关)
尽管与 covid 无关,但如下图所示,贫困率与低预期寿命之间的关联非常有趣。

Tableau,即:贫困对预期寿命的影响——作者图片
从上面的图表可以明显看出,生活在极端贫困中的国家平均预期寿命较低。较暗的蓝色表示贫困率较高,主要位于 50 至 70 岁的预期寿命范围内,而大多数贫困率较低的国家的人口寿命较长,生活富裕。看到一些国家的预期寿命低至 50 至 60 岁,确实令人震惊。下图证明了预期寿命的巨大差异可以用贫困率来解释。

Tableau,即:贫困对预期寿命的影响 1-作者图片
上图非常有力地表明,较低的贫困率解释了较高的预期寿命。下面的相关统计证实了显著的负相关。

Python 相关性片段 3—作者图片
城市研究所和社会与健康中心的一份非常清晰的报告探讨了贫困在预期寿命中所起作用的细节,并提供了引人注目的统计数据来支持一些推论,如"收入较低的人健康状况较差,患病和死亡的风险较高"【6】。不计其数的因素造成了收入和预期寿命之间的差距,从卫生标准到及时寻求医疗护理的能力,到资源的可用性等等。
5.各国疫苗接种率和未来疫苗接种率预测
现在,让我们转到期待已久的各国疫苗接种率分析。请注意,我在计算时考虑了完全接种的人群,即第一剂和第二剂(如果适用)。在仔细研究了上面几节中介绍的一些列之后,我们对数据有了很好的理解,可以进行一些更复杂的分析。

表格,即:按国家、地理地图分类的疫苗接种率——按作者分类的图片
虽然在上面的地图上看不到,但西班牙南部的直布罗陀是疫苗接种率最高的国家(更确切地说是独立领土),达到了 87.4%。第二名是以色列,该国几乎 60%的人接种了疫苗。显然,该行仍有许多工作要做。我们可以看到,美国、摩洛哥、塞尔维亚、智利和其他一些国家也略微领先,大约 20%的人口已经接种了疫苗。
现在我们已经了解了当前的接种率,我将尝试预测两个月后的接种率。我们即将获得自由吗?为了达到大部分限制可以取消的程度,科学家们设定了 65–70%[1]的阈值,这意味着每个国家的这部分人口必须接种疫苗或免疫才能放松限制。我想使用多项式回归来确定哪些国家能够在未来两个月内通过接种疫苗实现这一目标。
请注意,我只是根据当前和历史的疫苗接种率。我绝不是说预测就像他们得到的一样好,因为在现实中,增加变量,如每个国家预定的疫苗交付可能会给我们理想的结果,但我只是用我这里有的东西。事不宜迟,我们开始吧!
导入库
*import pandas as pd
import numpy as np
from matplotlib import pyplot
import matplotlib.pyplot as plt
from matplotlib import rcParams
import seaborn as sns
from IPython.display import display
from sklearn import linear_model
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import PolynomialFeatures
from sklearn.metrics import r2_score
import statsmodels.formula.api as smf
from statsmodels.regression.linear_model import OLS
from sklearn.linear_model import LinearRegression
import math
from math import sqrt
from sklearn.metrics import mean_squared_error
from random import random
import datetime as dt*
准备并分析数据帧
*df = pd.read_csv(r'C:\Users\Deepika\OneDrive\Documents\Professional\owid-covid-data.csv') *#Keeping only relevant columns*
df = df[['iso_code', 'continent', 'location', 'date', 'total_cases', 'new_cases','total_deaths', 'new_deaths','reproduction_rate', 'icu_patients',
'hosp_patients','new_tests', 'total_tests','positive_rate', 'tests_per_case', 'tests_units', 'total_vaccinations',
'people_vaccinated', 'people_fully_vaccinated', 'new_vaccinations','stringency_index',
'population', 'population_density', 'median_age', 'aged_65_older',
'aged_70_older', 'gdp_per_capita', 'extreme_poverty',
'cardiovasc_death_rate', 'diabetes_prevalence', 'female_smokers',
'male_smokers', 'handwashing_facilities', 'hospital_beds_per_thousand',
'life_expectancy', 'human_development_index']]*#A very important step*
df['date'] = pd.to_datetime(df['date'])*
为附加列和一些分析创建函数
**# Creating function for additional columns and some analyses* def analyse_df(df):
df['case_rate'] = (df['total_cases']/df['population'])*100
df['death_rate'] = (df['total_deaths']/df['population'])*100
df['test_rate'] = (df['total_tests']/df['population'])*100
df['admissions_rate'] = (df['hosp_patients']/df['population'])*100
df['critical_rate'] = (df['icu_patients']/df['population'])*100
df['vaccination_rate'] = (df['people_fully_vaccinated']/df['population'])*100
print('Columns: ', df.columns)
print('Dataframe shape: ', df.shape)
print('Date Range', df['date'].min(),df['date'].max())
#get some stats for each country using groupby
stats_df = df.groupby('location')[['date','case_rate','death_rate','test_rate','vaccination_rate',
'admissions_rate','critical_rate','stringency_index',
'population']].agg({"date":['max', 'count'],
'case_rate':'max','death_rate':'max','test_rate':'max','vaccination_rate':'max',
'admissions_rate':'mean','critical_rate':'mean','stringency_index':'mean','population':'mean'}) display(stats_df)
display(df.corr()) sns.heatmap(df.corr(), vmin=-1, vmax=1, center=0,cmap=sns.diverging_palette(20, 220, n=200),square=True)
plt.xticks(rotation=45)
rcParams['figure.figsize'] = 12,8 return df, stats_df*
调用函数
*df = analyse_df(df)[0]
stats_df = analyse_df(df)[1]*
上面的代码片段返回了关于每个国家的一些有趣的利率以及每个变量与另一个变量的相关程度的统计数据。然后,我们必须保存带有新创建的列的数据框(此处为 df ),以供进一步分析。下面的 GIF 提供了对该输出的快速浏览。

Python 分析:df 属性、相关性和相关性图、按列分组的统计数据-按作者分类的图像
建立回归模型
我选择了多项式模型,因为这是分析和预测疫苗接种率上升趋势的最直接的方法。我用 ARIMA 模型尝试了一种更先进的时间序列分析,发现多项式模型的结果更真实、更准确。在接下来的部分,我分享了我的分析代码。请务必阅读解释每一个步骤的评论。
创建模型函数
*def poly(name, group): *# transfrom the date into an integer to be able to fit it into* the model
group['date_transformed'] = group['date'].map(dt.datetime.toordinal) *# Create a range to be able to tell the model later to predict within this range. I want to predict for a range that is 10 points more than half the number of observations in input data.*
Range = group['date_transformed'].max() + round(len(group)/2) + 10
predict_dates = list(range(group['date_transformed'].max() + 1, Range))
*# Build the model
* *# Make sure to transfrom the input data*
x = group['date_transformed'].values[:,np.newaxis]
y = group['vaccination_rate'].values
polynomial_features = PolynomialFeatures(degree=2)
x_poly = polynomial_features.fit_transform(x)
model = LinearRegression()
model.fit(x_poly, y) *# Test the model and its accuracy*
y_poly_pred = model.predict(x_poly)
rmse = np.sqrt(mean_squared_error(y,y_poly_pred))
r2 = r2_score(y,y_poly_pred) *# Save the predictions as a column of the input data*
group['Pred'] = y_poly_pred
group_export = group[['date','vaccination_rate','Pred']].set_index('date') *# View results*
print(name)
print('rmse: ', rmse)
print('r2: ', r2)
return model, polynomial_features, predict_dates, group_export*
模型结果
按位置(国家)对数据进行分组。
**# Create grouped data for access later*
df_grouped = df.groupby(['iso_code','continent','location'])*
将模型应用到分组数据的每个类别中,以获得国别预测。
**# Dictionaries to save the results of the model* dct_original = {}
dct_future = {}*# Access each country data seperately* for name, group in df_grouped: * # Make sure to select countries without NaN values in vaccination_rates*
group1 = group[group['people_fully_vaccinated'].notna()] *# Countries with at least 50 vaccination data points for better predictions*
if len(group1) > 50: *# Save outputs from the function into the following variables*
predict_dates = poly(name, group1)[2]
model = poly(name, group1)[0]
polynomial_features = poly(name, group1)[1]
group_export = poly(name, group1)[3]
group_export['Location'] = name[2] *# Future predictions for the range of dates specified in the function. Again, remember to tranform the input*
Predictions = model.predict(polynomial_features.fit_transform(np.array(predict_dates).reshape(-1,1))) *# Putting the predictions and dates into a dataframe*
Predictions_df = pd.DataFrame({'Future_dates': list(predict_dates),'Predictions': list(Predictions)}) *# Converting the transformed dates to original date format*
Predictions_df = Predictions_df.set_index(Predictions_df['Future_dates'].map(dt.datetime.fromordinal)) *# Add country to the dataframe to identify the data*
Predictions_df['Location'] = name[2]
*# Save input data predictions and future predictions into dictionaries to access later*
dct_original[name] = group_export
dct_future[name] = Predictions_df *#* *Plot current observed, predicted and future predicted data*
plt.figure(figsize=(10,5))
plt.xticks(rotation=45)
plt.title('Model ' + name[2])
plt.xlabel('Date', fontsize=11)
plt.ylabel('Vaccination Rate', fontsize=11)
plt.scatter(group_export.index, group_export['vaccination_rate'])
plt.plot(group_export['Pred'], color = 'g')
plt.plot(Predictions_df[['Predictions']], color = 'm')
plt.legend(['Validation', 'Predictions', 'Train'], loc='lower right')
plt.show() *# View the Actual vs Predicted data and their data count
* print('Observations in Actual Data = %f, Predicted Observations=%f' % (len(group1), len(Predictions)))
print( "\n".join("{} {}".format(x, y) for x, y in zip(predict_dates, Predictions)))*
疫苗接种数据点超过 50 个的 40 个国家中的一些国家的样本输出

Python viz 作者图片
从以上对英国的预测中,我们可以看到,考虑到疫苗接种率数据,RMSE 是相当低的,而 R 显示了高准确率。该预测称,到 6 月初,大约 30%的英国人可以接种疫苗。

python viz 1-作者图片
智利的 RMSE 相当低,而 R 则显示出相当好的准确性。预测表明,潜在的 90%的人口可以在 6 月初接种疫苗。

python viz 2-作者图片
现在,对于美国的预测,我们看到近一半的美国人口可以在 6 月初接种疫苗。尽管这可能是一个合理的预测,但 RMSE 非常低而 R 非常高的模型可能会返回过度拟合的结果。当验证非常接近实际数据,但对看不见的数据点(测试或未来数据)的预测不现实或不切实际时,就会发生过度拟合。在这个特定的场景中,尽管精确度非常高,但是模型产生了良好的结果。当然,考虑到目前的增长率,50%似乎是一个很高的可实现率。然而,对于训练和验证点如此精确和接近的模型来说,经常有可能过度拟合。补救回归模型过度拟合的一种方法是降低多项式的次数。例如,对于这个特定的模型,我们使用的阶数为 2,这是多项式模型的最低阶数。
**# Polynomial degree used before*
polynomial_features = PolynomialFeatures(degree=2)*
替代模型
或者,如果我们试图获得更通用的预测并避免可能的过度拟合,我们可以考虑将阶数从 2 改为 1,这将使其成为线性回归模型。观察该图的线性特征,我们也可以有把握地说,这种趋势的线性模型应该足够了。所以让我们看看线性模型对这个特定国家的预测是否有所不同。我将继续替换美国的多项式模型。我们将使用与多项式模型相同的代码结构,除了这一次,我们将不使用“多项式 _ 特性”。
*for name, group in df_grouped:
if name[2] == "United States": # Only for the US
group1 = group[group['people_fully_vaccinated'].notna()]
group1['date_transformed'] = group1['date'].map(dt.datetime.toordinal) # transform date column into integer to be able to build model
Range = group1['date_transformed'].max() + round(len(group1)/2) + 10
predict_dates = list(range(group1['date_transformed'].max() + 1, Range)) # create a range of dates to make future predictions
x = group1['date_transformed'].values[:,np.newaxis] # input data transformed
y = group1['vaccination_rate'].values *# input train data*
model = LinearRegression()
model.fit(x, y) *# Fitting linear regression*
y_pred = model.predict(x)
group1['Pred'] = y_pred
r2 = model.score(x,y) *# alternatively, r-squared can also be measured this way*
rmse = mean_squared_error(y, y_pred, squared=False)
group_export = group1[['date','vaccination_rate','Pred']].set_index('date')
Predictions = model.predict(np.array(predict_dates).reshape(-1,1))
Predictions_df = pd.DataFrame({'Future_dates': list(predict_dates),'Predictions': list(Predictions)})
Predictions_df = Predictions_df.set_index(Predictions_df['Future_dates'].map(dt.datetime.fromordinal))
plt.xticks(rotation=45)
print(name)
print('rmse: ', rmse)
print('r2: ', r2)
plt.scatter(group_export.index, group_export['vaccination_rate'])
plt.plot(group_export['Pred'], color = 'g')
plt.plot(Predictions_df[['Predictions']], color = 'm')
plt.legend(['Validation', 'Predictions', 'Train'], loc='lower right')*

Python viz 3—作者图片
线性模型的结果显示出比多项式模型更低的统计精度。该模型现在不再显示过度拟合的症状。此处 30%的预测在我们的场景中不太乐观,但关键的一点是,当有可能获得过度拟合的结果时,降低程度可能会产生更通用的结果,因此看不见的数据点的结果更可靠。
6 月初疫苗接种率

Tableau 即:预测疫苗接种值的地理图
以上是最终输出的可视化表格。由于我设置了过滤器,只对当前疫苗接种率至少为 50 天的国家进行预测,因此预测范围缩小到有色人种国家。这个过滤器可以用来获得更多国家的预测,但是,它可能会影响准确性。

作者预测数据框架 GIF
《经济学人》绘制的一幅类似的图像大胆地强调了富人和穷人在疫苗接种收益方面的差距。

来源:经济学家【8】
基于模型的方法的局限性
虽然盲目地遵循基于模型的方法往往很诱人,但我们必须记住,所有预测都是基于假设的,这一次也不例外。这尤其适用于我们几乎无法控制的医疗、经济、政治、社会或行为领域的预测。例如,对于这个特定的模型,以下是限制:
- 如前所述,该模型没有考虑国家的预定交付量,这可能会改善预测。
- 当然,没有足够的新冠肺炎疫苗接种数据让监督模型完全可靠。正如在这个模型的地理可视化中所看到的,许多国家甚至还没有 50 个疫苗接种数据的观察。
- 该模型假设疫苗推广的一致性。然而,对许多国家来说,这一假设已被证明是错误的。一个国家的铺开一致性当然受到 covid 时代不断变化的法律的影响。以阿斯利康在丹麦被禁为例。这种意想不到的外部因素对结果的影响很少能被纳入机器学习模型。
- 随着许多变种被发现,关于疫苗接种是否意味着免疫的问题仍然存在。目前预测模型的结果是否意味着免疫,还不得而知。因此,这项研究假设疫苗接种率仅仅是疫苗接种率,而不是免疫力。
尽管有其局限性,但不用说,在许多情况下,一个好的基于模型的方法比盲目的直觉和猜测要好。它可能不会给我们完美的答案,但它肯定会给我们一个预期的想法,并帮助我们更好地准备和/或做出更好的决定。理想的结果可能是一个健壮的模型和一点受过教育的直觉的结合。
如果你真的跟随并设法获得了你自己的预测,那太好了!我希望他们看起来也一样乐观!同样,这是的数据,这是的画面。感谢阅读!
参考
[1] D. Gypsyamber 和 D. David,什么是群体免疫,我们如何通过新冠肺炎实现群体免疫?(2021 年 4 月),约翰霍普金斯大学彭博公共卫生学院
[2] R. Hannah 等人,冠状病毒(新冠肺炎)检测(2021 年 4 月),数据中的我们的世界
[3]李立林、丁庆洪、胡俊伟、新冠肺炎死亡率与检验次数和政府效率呈负相关(2020 年 7 月),科学报告
[4] T .李记,《新闻医学生命科学》的一项研究(2021 年 2 月)称,人口老龄化的国家新型冠状病毒病毒感染率和死亡率都较高
[5]世卫组织,在新冠肺炎疫情期间支持老年人是每个人的事,(2020 年 4 月),世界卫生组织
[6] H. W .史蒂文,收入和财富与健康和长寿有什么联系?(2015 年 4 月),城市研究所和社会与健康中心
[7] BBC,阿斯利康疫苗:丹麦完全停止推广(2021 年 4 月),BBC 新闻
[8]EIU,EIU 最新疫苗首次展示预测(2021 年 3 月),《经济学家》
R 剧情定制速成班
把剧情从布拉到哇

照片由 Steinar Engeland 在 Unsplash 上拍摄
使用 RStudio 时,您首先要学习的函数之一是plot() 函数,它可以制作简单的 X-Y 图来帮助您可视化数据。在本文中,我们将向您展示如何通过以下方式改进您的绘图:
- 叠加多个图
- 改变颜色和尺寸
- 覆盖趋势线
- 添加地块网格
- 与
plot()的各层合作,让一切都恰到好处
在本帖中,我们将向您展示如何对一个简单的散点图进行这些调整,但是这里介绍的技巧和工具也可以直接应用于其他类型的图——条形图、折线图等等。
让我们从创建一个简单的数据框和一个基本的绘图开始:
df <- data.frame (A = c(1,2,3,4,5,6,7),
B = c(1.2, 2.2, 2.9, 3.9, 5.2, 6, 7.1),
C = c(0.55, 1.1, 1.56, 1.96, 2.45, 3.11, 3.55))
# plot(x,y)
plot(df$A, df$B)

极其乏味的散点图
这将创建最基本的绘图,具有自动缩放的轴和默认轴标签。首先,我们来提高一些基本的审美。自动缩放可能不是首选-在这种情况下,可以用xlim和ylim手动设置。
接下来,我们来加点颜色!col是一种简单的方法,可以通过数百个直观的颜色名称来改变数据点的颜色。完整列表可通过colors()找到。
当我们这样做的时候——默认的开放圆很好,但是我们的点形状有更多的选择。这些有 26 个选项(见这里的),通过设置从 0 到 25 的值来调用。其中最有趣的是允许我们通过使用bg属性来指定一个形状和填充该形状的颜色。
再来一个——让我们用cex来调整数据点的大小。为了调整尺寸,我们指定一个数字比例因子——例如,我们应用cex = 1.2使它们为原始尺寸的 120%。cex是一个强大的工具,它可以让我们调整几乎任何东西的大小——但稍后会有更多的介绍。让我们来看看当我们把它们放在一起时是什么样子:
plot(df$A, df$B, col="blue", xlim=c(0,8), ylim=c(0,8),
pch=25, bg="lightblue", cex=1.5)

一些基本的颜色和形状升级
层层叠叠
想到plot()命令的一个很好的方法是,它设置绘图空间,并且每个后续命令粘贴到它上面。这对我们来说意味着我们可以分段构建一个图,在我们遍历单独的代码行时添加。
对于绘图功能,覆盖我们的数据帧的附加元素进行比较将是有用的。我们可以用points命令在同一个绘图上覆盖另一列数据,这里,像绘图一样,我们设置 X,Y,颜色,符号(pch)等。
手动向我们的绘图添加线条也可以帮助可视化,无论是对于趋势线这样的东西,还是对于设置一个简单的网格。我们用abline添加这些,它们有一些简单的预置
h为水平线v对于垂直线a为 y 轴截距,b为斜率
我们可以用lwd设置线条的线宽,这也可以用来调整我们的点符号轮廓的线宽。线型也可以用lty通过六个名称中的一个来控制(虚线等),如"dashed"或"dotted" ( 这里是完整列表)。
plot(df$A, df$B, col="blue", xlim=c(0,8), ylim=c(0,8),
pch=25, bg="lightblue", cex=1.2, lwd=2)# the second set of points
points(df$A, df$C, col="darkgreen", pch=17, cex=1.6)# horizontal grid lines
abline(h=0:8, col="gray")# vertical grid lines
abline(v=0:8, col="gray")# trendline- a: intercept, b: slope
abline(a=0, b=1, lty="dashed", lwd=2, col="blue")

但是为什么那些点隐藏在网格线后面呢?
细心的人会发现,我们的网格线现在位于我们的点上方——这又回到了plot()的层层叠加方法,每个操作都叠加在前一个之上。为了克服这一点,我们将首先创建一个空白画布,然后按顺序添加我们的层。为此,我们使用NULL来代替plot()中的 x,y 变量。值得注意的是,我们仍然需要在这一步设置我们的自定义xlim和ylim,因为我们以后没有机会了。
# blank canvas
plot(NULL, xlim=c(0,8), ylim=c(0,8))# line overlays
abline(h=0:8, col="gray")
abline(v=0:8, col="gray")
abline(a=0, b=1, lty="dashed", lwd=2, col="blue")#points:
points(df$A, df$B, col="blue", pch=25, bg="lightblue",
cex=1.2, lwd=2)
points(df$A, df$C, col="darkgreen", pch=17, cex=1.6)

这才像话!
我们就要到了,但是现在我们的轴标题并不完全是描述性的,不是吗?在我的下一篇文章中,我将展示如何编辑它们,添加标题、副标题和图例。现在,这里显示的代码可以在我的 github 这里找到。我鼓励你尝试所有的设定,并从定制剧情中获得一些乐趣,让它按照你喜欢的方式进行。
觉得这篇文章有用?在 Medium 上关注我(布莱恩·马蒂斯),以获得我最新帖子的通知!
R 图定制速成班—第二部分
标题、图例、背景颜色等等

r 附带了一个很棒的内置绘图生成工具,不出所料,它被命名为plot()。然而,最基本的情节在视觉上吸引人之前需要很多帮助。在我之前的文章中,我们叠加了多个数据集,改变了数据点的颜色和大小,并添加了趋势线。那只是用plot()定制的冰山一角!在本文中,我将向您展示如何:
- 添加和自定义标题、轴标题和副标题
- 创建数据图例来解释您的数据
- 自定义图表背景颜色
首先,让我们为我们的图创建一些基本数据:
df <- data.frame (A = c(1,2,3,4,5,6,7),
B = c(1.2, 2.2, 2.9, 3.9, 5.2, 6, 7.1),
C = c(0.55, 1.1, 1.56, 1.96, 2.45, 3.11, 3.55))
plot(df$A, df$B)
基本标题
最终的图给了我们一个非常普通的图,没有标题,轴标题为“df\(A”和“df\)B”,对于不熟悉数据表的人来说,这可能没什么意义。我们的第一步是直接定制轴标题,以及添加图表标题和副标题。使用main、xlab、ylab和sub,我们可以分别为图表标题、x 轴标签、y 轴标签和绘图副标题分配新值。现在也是用xlim和ylim为我们的轴设置 x 和 y 范围的好时机。我们通常在主plot()函数中这样调用这些函数:

一些非常简单的英语标题
plot(df$A, df$B, xlim=c(0,8), ylim=c(0,8),
main="Chart Title",
xlab="Series A",
ylab="Series B",
sub="A little subtitle")
内部消息:
虽然图表标题和副标题可以在单独的函数title()中编写,但这会增加坐标轴标题的复杂性,不推荐使用。正如我在上一篇文章中提到的,这些图是通过直接在上面添加每个后续命令的层来构建的。因此,如果我们将xlab和ylab赋值推送到plot()命令之后的title()命令,默认图形将已经生成默认值“df\(A”和“df\)B ”,这两个文本将被覆盖。

不…可读性最好
标题定制
虽然调整标题和轴文本肯定有助于传达图表的信息,但它肯定不会赢得任何风格点。对于这些图表标签项目中的每一个,我们可以使用cex来调整每个元素的具体大小,使用cex.main、cex.sub或cex.lab ( )。lab 指 x 轴和 y 轴标签—x 轴和 y 轴标签必须一起调整)。cex作为默认值的一个比率,这意味着如果你想要标签有 90%那么大,你可以将 cex 设置为 0.90。
接下来,我们调整标签的font样式。这里没有太多的定制,但是我们可以使用 1/5 的代码和1: normal, 2: bold, 3: italic, 4: bold and italic, 5: symbol font。类似于cex,我们把这个叫做font.main、font.sub、font.lab。
没有一点颜色的标签。r 有一个很大的颜色名称库,从控制台调用colors()就可以看到完整的列表。为了设置这些,我们使用相同的术语:col.main、col.sub和col.lab。
我发现,为了保持代码的可读性和组织性,我们要调整的每个标题元素最好只有一行代码。
plot(df$A, df$B, xlim=c(0,8), ylim=c(0,8),
main="Chart Title",cex.main=1.3, font.main=2, col.main="steelblue1",
xlab="Series A",
ylab="Series B", cex.lab=1.1, font.lab=1, col.lab="gray34",
sub="A little subtitle", cex.sub=0.8, font.sub=3, col.sub="deeppink")
甚至没有触及我们的图的数据区域,它已经看起来明显更好!

已经看起来好多了!
背景颜色
给plot()添加一个合适的背景颜色需要一个更迂回的路径。虽然plot()像许多其他元素一样有一个bg(背景颜色)属性,但是颜色也应用于数据区域之外,并延伸到我们的图表标题之下。这绝对不是我们想要的。
相反,我们将使用rect()构建一个定制的彩色矩形,它总是与我们的绘图区域大小完全相同。为此,我们调用一个特殊的函数:par("usr")。该函数返回绘图区四条边界的列表,我们可以用它来设置矩形的顶点。一旦我们设置了这些顶点,我们就可以使用col来设置矩形的颜色。
rect(par("usr")[1],par("usr")[3],par("usr")[2],par("usr")[4],col = "gray")
层,层,层
这种方法的挑战是,我们新创建的矩形将被直接放置在我们的绘图数据上,使其完全模糊。这又回到了plot()命令的分层本质,每个后续命令都直接粘贴在现有图形的顶部,而不考虑已经构建的图形。避免这种情况的唯一方法是构建命令,这样您就可以自下而上地构建命令。因此,当我们转到用plot()制作漂亮的图时,我们通常会将数据点 添加到最后的 。
通过使用NULL代替原始plot()函数调用中的 X 和 Y 数据,在没有实际调用数据的情况下建立我们的图形框架(标题等)变得很平常。然后用我们在这里谈到的的points()命令将数据点分层。
对于彩色背景来说,这是一个很大的工作量,但这种方法也是大多数其他绘图定制(趋势线等)的一个要求,使您的图表在视觉上更具吸引力。
plot(NULL, xlim=c(0,8), ylim=c(0,8),
main="Chart Title",cex.main=1.3, font.main=2, col.main="steelblue1",
xlab="Series A",
ylab="Series B", cex.lab=1.1, font.lab=1, col.lab="gray34",
sub="A little subtitle", cex.sub=0.8, font.sub=3, col.sub="deeppink")#adding our background rectangle:
rect(par("usr")[1],par("usr")[3],par("usr")[2],par("usr")[4],col = "gray")#inserting our data
points(df$A, df$B)

虽然这个灰色背景并不令人兴奋,但是你可以做一些有趣的事情,比如用gradient.rect()代替rect()来添加彩色的渐变阴影。
添加图例
在我们继续添加图例之前,我将首先向我们的绘图添加更多的颜色和数据,以展示我们如何创建复杂的图例。毕竟,当我们在一个情节上只有一组点时,添加一个图例没有太大的意义!使用points()和abline(),我们可以使用第一部分中的技巧定制我们的数据点和趋势线。
plot(NULL, xlim=c(0,8), ylim=c(0,8),
main="Chart Title",cex.main=1.3, font.main=2, col.main="steelblue1",
xlab="Series A",
ylab="Series B, Series C", cex.lab=1.1, font.lab=1, col.lab="gray34",
sub="A little subtitle", cex.sub=0.8, font.sub=3, col.sub="deeppink")
rect(par("usr")[1],par("usr")[3],par("usr")[2],par("usr")[4],col = "lightgray")# grid lines
abline(h=0:8, col="white")
abline(v=0:8, col="white")#fit lines
abline(a=0, b=1, lty="dashed", lwd=2, col="darkslateblue")
abline(a=0, b=0.5, lty="dotdash", lwd=2, col="deeppink") #data points:
points(df$A, df$B, col="darkslateblue", pch=18,
cex=1.8, lwd=2)
points(df$A, df$C, col="deeppink", pch=17, cex=1.6)

为了给这个图添加一个图例,我们首先要选择图例在图表上的位置。在这种情况下,左上角看起来是可用的,所以我们将用legend("topleft")放置图例。作为参考,这里展示了 8 个其他选项:

图例放置关键字
这些名字很容易记住——但不幸的是legend()默认将这些传说放在我们的情节的绝对角落。如果把它们放在离我们的情节框架稍微远一点的地方会更好。幸运的是,我们可以使用inset()来移动它们。
随着我们进一步构建legend(),我们必须复制添加数据点时使用的许多决策,例如它们的点形状(pch)、点大小(pt.cex)、点颜色(col)和数据名称(legend)。是的,你没看错,我们称legend为legend()内。对于所有这些字段,我们通常用c()制作一个分量向量,以捕捉我们绘制的每个系列的属性。最后,我们可以添加title来命名我们的图例框。当然,由于图层排序的原因,图例通常是为情节添加的最后一行代码。将所有这些与上面的代码放在一起,看起来像这样:
legend("topleft", legend=c("Group B", "Group C"),
pch=c(18,17), col=c("darkslateblue", "deeppink"),
pt.cex=c(1.4, 1.3), inset=c(0.05, 0.05), title="Legend")

额外学分
当我们使用 point pch值来设置不同于轮廓的填充颜色时,事情会变得有点棘手。这里我们将使用bg设置points()内的填充颜色,如下所示:
points(df$A, df$B, col="darkslateblue", pch=25,
cex=1.4, lwd=2, bg="steelblue1")
然而,我们不能仅仅将这个bg键复制到我们的图例图中——在这种情况下bg将设置我们的图例框的背景颜色,而不是点填充。这同样适用于我们的点的线宽——在points()命令中,它们是用lwd设置的,但是如果我们在图例中这样做,我们只是结束了图例边界框的线宽设置。
幸运的是,legend()提供了两个特殊的命令来处理这种情况:pt.bg和pt.lwd。综上所述,我们的代码如下所示:
plot(NULL, xlim=c(0,8), ylim=c(0,8),
main="Chart Title",cex.main=1.3, font.main=2, col.main="steelblue1",
xlab="Series A",
ylab="Series B, Series C", cex.lab=1.1, font.lab=1, col.lab="gray34",
sub="A little subtitle", cex.sub=0.8, font.sub=3, col.sub="deeppink")#background
rect(par("usr")[1],par("usr")[3],par("usr")[2],par("usr")[4],
col = "lightgray")#grid lines
abline(h=0:8, col="white")
abline(v=0:8, col="white")#trend lines
abline(a=0, b=1, lty="dashed", lwd=2, col="darkslateblue")
abline(a=0, b=0.5, lty="dotdash", lwd=2, col="deeppink")#points:
points(df$A, df$B, col="darkslateblue", pch=25,
cex=1.4, lwd=2, bg="steelblue1")
points(df$A, df$C, col="deeppink", pch=17, cex=1.6)#legend
legend("topleft", legend=c("Group B", "Group C"), pch=c(25,17),
pt.bg="steelblue1", pt.lwd=c(2,1),
col=c("darkslateblue", "deeppink"), pt.cex=c(1.2, 1.4),
inset=c(0.05, 0.05))

我们到了。在这一点上,你有工具来调整你的阴谋中的一切,让它只是你想要的方式。我鼓励你尝试许多设置和实验。首先,可以在我的 github 这里找到这个练习的源代码。感谢阅读和快乐编码!
深度学习的批判性评价
这个领域已经取得了很大的进步,但是我们知道的越多,就越意识到我们一无所知。

德里克·欧文斯在 Unsplash 上的照片
介绍
每个人和他们的祖父母都在谈论它:人工智能、深度学习(DL)、机器学习、机器人等等……有时所有这些术语都在同一个句子中,有时是同义词。如果有一件事是肯定的,那就是这些科目越来越受欢迎,以至于公众的期望越来越高。面部识别、图像分类器、人工智能与人类游戏(围棋、DOTA)或自动驾驶汽车的重大进展创造了可以理解的炒作。炒作本身也受到首席执行官和领先研究人员过于热情的承诺的推动。
但是,只有当具体的应用程序到位时,宣传才能存在,而这仍然是滞后的。随着时间的推移,一些地区开始慢慢清醒过来。聊天机器人没有实现他们的宣传。自动驾驶汽车和整个神经网络可能是下一个目标。在这篇文章中,我将回顾 Gary Marcus 对深度学习的批判性评价,并通过个人评论和一些资源进行补充。
深度学习系统的挑战
马库斯在他的论文(2018)中讨论了深度学习开放挑战。
深度学习需要大量数据
例如,人类需要一些试验或数据示例来将它们转化为可操作的。
- 马库斯举了一个新词的例子,一个“ schmister ”。在我们的例子中,它被定义为“有一个 10 岁以上 21 岁以下的姐姐”。举个例子,你可以很容易地判断出你是否有朋友,或者你的朋友是否有。更多的是,通过演绎,你可以推断出你的父母可能没有。
马库斯在这里强调了深度学习目前缺乏一种通过明确的口头定义来学习抽象的机制。相反,当有数千、数百万甚至数十亿个训练样本时,它的效果最好,就像 DeepMind 在游戏上的表现一样。
简而言之,在数据稀缺的问题上,深度学习还不是理想的解决方案。然而,深度学习中小数据的出现可能会带来变化。
深度学习是浅层的,转移能力有限
我们回到一个游戏例子,用雅达利的突围。如果你不熟悉的话,这个想法是用垫子来指挥球和打破砖块。一个完美的技术是建造一个隧道,让球穿过墙壁,然后让球来回反弹并摧毁砖块,同时你去享受咖啡。

雅达利突破——来源:维基百科
- 马库斯解释说,“一个模型可能能够击败游戏的主人,就像你刚才做的那样,通过建立一个隧道。但是它不知道什么是隧道,什么是球,什么是墙。该模型只计算了给定场景下的意外情况,解决方案往往是肤浅的。它只会作出微小的改变,如酒吧的位置,墙壁等,使系统无用,并显示其肤浅。”
简而言之,深度学习提取的模式比它们最初出现时更肤浅。
这适用于缺乏对隧道,墙壁的理解,但缺乏适应性(改变游戏内设置)的说法在 2018 年夏天被 OpenAI 的 DOTA 大部分推翻。
深度学习不够透明(又名黑盒)
这是对深度学习的普遍评论和批评。但是有多重要呢?我个人在这个问题上遇到了各种各样的意见。正如作者所言,这真的取决于行业。人们可能想了解一个决定是如何被挑选出来的,了解存在偏见的程度,特别是如果它可能导致重要的生死决定。理解它是如何工作的可能会暴露这些偏见,并帮助我们更好地改进或调试它。
深度学习没有很好地与先验知识结合
马库斯认为,占主导地位的方法是独立的,与潜在有用的知识隔离开来。人们对整合先前已确立的知识没有浓厚的兴趣,比如一座塔是如何倒塌的,以及物理学规则如何在深度学习系统中发挥作用。另一个问题是“如何”整合这些知识。在 DL 系统中,知识通常是特征之间相互关联的结果。正如作者所指出的,这些相关性往往是不透明的,这种知识与量化的陈述相反,如“所有人都会死”。
此外,需要常识的问题对于深度学习来说还不可及,可能需要另一套完整的工具。作者引用了与厄尼·戴维斯(2015)进行的研究,并提出了如下问题:
- 威廉王子和他的小儿子乔治王子谁更高?
- 你能用涤纶衬衫做沙拉吗?
- 如果你把一根大头针插进胡萝卜,它会在胡萝卜上还是大头针上扎一个洞?
为了回答上述问题,人类将使用来自大量不同来源的集成知识,而不是深度学习的方法。
深度学习无法从本质上区分因果关系和相关性
深度学习系统学习复杂的相关性,但不学习因果关系。在这一点上,我不会对 DL 太苛刻,因为许多人类同胞都在努力区分相关性和因果性。
作为一个例子,马库斯使用了一个深度学习系统,该系统发现孩子随着他们学习更多的单词而变大。然而,这并不意味着长高会导致他们学习更多的单词,也不意味着学习新单词会导致他们成长。然而,正如作者所指出的,DL 并没有准备好迎接这样的挑战。
深度学习作为一种近似是不错的,但答案并不完全可信
图像识别仍在改进,随机或有计划的敌对攻击的需要仍然存在。作者举了沙丘被误认为裸体的例子,或者一个黄黑色的正方形被误认为是校车的例子,或者更糟糕的是,一个污损的停车标志被误认为是限速标志。
深度学习很难设计
对技术债务的兴趣很高。为了短期收益而建立一个深度学习系统是微不足道的,但保证它在新数据的替代环境中工作是另一回事。
技术债务是一个激动人心且至关重要的话题。如果你好奇,我推荐你这篇文章(我有点偏心):
聊天机器人、神经网络和自动驾驶汽车已经过时了吗?
这是否意味着我们应该忽略这些领域?不,但我们现在可能要重新审视我们的期望了。聊天机器人具有欺骗性的原因有很多,其中之一是缺乏短期记忆或背景。自动驾驶汽车是炒作正在慢慢消亡的另一个话题。2015 年,《卫报》预测到 2020 年,我们将处于“永久的后座司机”状态。在 2016 年的一篇文章中,商业内幕声称“到 2020 年将有 1000 万辆自动驾驶汽车上路”。其他主要汽车制造商也做出了类似的令人印象深刻的声明。埃隆·马斯克也预测特斯拉将在 2018 年实现这一目标。事实证明,自动驾驶汽车的生产比最初想象的要复杂得多。希望炒作平息后,工作将继续下去,一些突破将在稍后到来。许多领域都取得了进步,就像今天一样,它们无疑是支持和增强我们的工作和生活的伟大工具。
接下来读什么?关于深度学习的更多见解和批评
- 在他的论文 Marcus 中,也讨论了深度学习对分层结构和开放式推理的处理。作者声称,从自然语言处理(NLP)的角度来看,DL 仍然在这两个领域苦苦挣扎。
- 图灵奖获得者和机器学习的先驱 Judea Pearl 在他的书 中讨论了为什么:因果的新科学 理解因果关系将如何彻底改变人工智能。
- 深度学习会受到计算限制的打击吗?一些麻省理工学院的研究人员认为是的。
感谢阅读!喜欢这个故事吗? 加入 Medium 完整访问我的所有故事。
参考
- Marcus,G. (2018),深度学习:批判性评估,https://arxiv.org/abs/1801.00631
- OpenAI Five (2018),https://openai.com/blog/openai-five/
- 戴维斯等人和马库斯等人(2015 年)。人工智能中的常识推理和常识知识。美国计算机协会的通讯,58(9)(9),92-103。
审视数据科学是如何“教授”的

非机构数据科学教育的弱点
答虽然我们可能想得不多,但从教育/教学的角度来看,数据科学(以及广义的计算机科学)尤其有趣。我可能会遭受抽样偏见,但我不认为说数据科学教育比法律、化学和数学等学科更分散、更有据可查、更容易获得是没有道理的;这表现在许多方面:
- 易于访问、低成本的在线出版物和论坛(如“走向数据科学”)使概念和代码更容易掌握和使用。
- 相对于 Udemy 和 Coursera 等平台上的其他学科,数据科学的在线课程非常丰富。
- 相对而言,数据科学比其他学科更容易接近。虽然这肯定不容易,但你不需要花上几年的基础课程来打开你的大门。
- 由于数据科学的数字化本质,获得实践经验比通过实验室实验学习化学更容易。数字环境允许快速实验和可靠快速的反馈信号。像 Kaggle 这样的服务在这方面有所帮助。
- 数据科学的研究通常比其他学科的研究更公开,其他学科的研究通常被置于付费墙之后,因此访问受到限制。数据科学家可以在托管网站上探索大量高质量的论文,如 arxiv 或 paperswithcode 。
由于数据科学相对较高的可访问性,一个人如何着手学习数据科学的途径变得更加复杂和多样化。许多人参加强化训练营和在线课程;其他人则接受更正规的大学教育和学位(你会经常在 LinkedIn 上看到后者抱怨前者)。
然而,数据科学是一个快速发展的领域。不断有新的框架、库、技术、模型和想法出现。一个人不可能获得计算机科学或数据科学的学位,就认为自己已经学得够多了。通过这种方式,“非传统/非机构”教育(即不通过学术机构提供)如在线出版物、在线课程、论文库和论坛对所有数据科学学习者都变得很有价值。
“自己动手”/“亲自动手”/“实际操作”/“真实世界”的教学主题在这些非机构资源中尤其普遍。大多数在线课程和书籍将在描述中使用这些关键字的某种组合,以吸引当前渴望展示这些主题的内容的数据科学社区。

在 Udemy 上搜索展示“实践”/“真实世界”/“实用”教学主题的数据科学课程。
这是数据科学中一个特别有趣的趋势——专注于应用和实现,减少对理论的重视,而不仅仅是让代码工作所需的东西。这是有道理的:数据科学几乎完全是在数字环境中进行的,数据科学学习者已经形成了创造和运行想法的期望。
虽然这是可以理解的,但这种对非机构资源“实用性”的强烈关注可能会导致理解过度。
偏差/方差权衡是一个众所周知的概念模型,用于评估模型的概括能力或“学习质量”。考虑下面显示的同一组点的两种不同拟合:我们可以清楚地看到右边的曲线概括得很差。该算法可能方差太大:它过于重视确保曲线通过数据集中的每个点,而没有考虑这些点之间的区域。

左:很好的概括。右:过拟合,泛化能力差。
我们可以把每一个单独的点看作是一种知识——课程资源的一个组成部分,就像一篇博客文章、在线课程的一个模块、代码教程。理论有助于将各个组成部分统一成对该领域的整体理解或学习。正则化/偏差提醒模型,目的是进行归纳,而不仅仅是通过每个点。
鉴于非机构资源倾向于强调“动手”和“实用”,学习者可能会产生过度的理解:他们学习语法和单个程序如何工作良好;它们将数据集中已经存在的点内在化。然而,这些学习者对新的环境和问题表现出较差的概括能力。事实上,通常根本没有一般化——想象两个相邻点之间没有任何东西,除了对与已经看到的问题非常相似的问题有一点鲁棒性(见下图,右图)。

左:概括能力差。对:不能一概而论!
几乎完全从非机构资源学习数据科学的数据科学家可以在具有常见、标准化、众所周知的问题的职位上表现出色。许多数据科学任务的方法相当公式化,即通过一个步骤、可视化、模型和步骤的清单。获得更制度化教育(相对强调理论的教育)的数据科学家能够创新新的方法和途径来解决以前从未见过的问题。他们通常是处于研究前沿的人,而非机构资源的学生更多地局限于成熟的资源。
这并不是说机构教育优于非机构教育,也不是说一个人必须接受机构教育才能创新。相反,重点是强调非机构资源的一个潜在缺点,这个缺点应该得到解决。这里的问题不是鼓励机构教育还是非机构教育,而是如何将机构数据科学课程成功的因素注入非机构教育。
结合起来,数据科学不仅更容易获得,而且更具创新性。在数据科学前沿工作的数据科学家的分布严重偏向于那些受过机构教育的人。通过重新思考非机构数据科学资源的结构,我们可以发掘更多人的潜力。
当然,这是一项艰巨的任务——但人们应该身体力行,在我最近出版的书《现代深度学习设计和应用开发》中,我试图向打击理解过度迈出一步。(我希望这不会在你的嘴里留下不好的推销味道——这种教学争论的主要焦点是写这本书的动机。)这本书的意图是在直觉和理论的更广泛框架中交织代码和个体概念,放松“中间区域”的参差不齐和混乱,以鼓励在遇到新问题时创新的深度学习解决方案。
如果您有兴趣了解更多信息,请访问 Springer Link:
https://link.springer.com/book/10.1007/978-1-4842-7413-2
感谢您的阅读,请在回复中告诉我您的想法!
如果你对最新的文章感兴趣,可以考虑订阅。如果你想支持我的写作,通过我的推荐链接加入 Medium 是一个很好的方式。干杯!
使用 AWS ECR、SageMaker 和 Lambda 进行定制空间模型部署
关于如何使用 AWS 部署 SpaCy 的教程。

托马斯·伊万斯在 Unsplash 上的照片
背景:
SpaCy 是我最喜欢的 NLP 图书馆之一。我一直在使用 spaCy 执行许多命名实体识别(NER)任务。通常,我们首先需要加载一个特定语言的 spaCy 预训练模型,并使用我们的训练数据集对该模型进行微调。训练过程可以在本地计算机上离线完成,我们甚至可以通过 Flask / Streamlit 在本地托管它来测试微调后的模型性能。
虽然我发现了很多关于使用 Flask / Streamlit 在本地部署 spaCy 模型的很棒的教程,但是关于如何在更大范围内部署的教程并不多,例如,如何使用 AWS 部署 spaCy 模型。
这是一个非常有趣的话题,在做了大量的工作后,我在这篇文章中总结了我的解决方案;希望能对面临同样问题的人有所帮助。
简介:
在本文中,我将解释我的解决方案,说明如何使用 AWS 服务部署自定义空间模型,包括:
- 弹性集装箱登记处
- AWS SageMaker
- 自动气象站λ
- AWS S3 铲斗(可选)
这是我的计划 🧗🏻:
- 首先,原始数据输入可以作为事件发送到 AWS Lambda 。
- 然后,在 AWS Lambda 处理函数中,我们调用一个 SageMaker 端点。端点工作是将事件作为输入,并从部署在 SageMaker 上的微调 spaCy 模型返回预测结果。
- 最后,这个端点预测将作为执行结果显示在 AWS Lambda 结果选项卡上。如果出现错误,我们可以在 AWS CloudWatch 上调试。
- 在真实的场景中,我们可能还想连接一个 AWS API 网关,例如,如果我们想为模型托管一个接口。或者可能将预测结果与另一个 Lambda 连接起来,并集成到生产工作流中。
在上述步骤中,spaCy 模型托管在 AWS SageMaker 上,并且可以作为端点随时调用。为了创建 SageMaker 端点,我们需要首先在 AWS ECR 上创建我们的定制空间模型容器,并准备好 tar.gz 格式的经过训练的模型工件。
此外,要创建一个模型工件,我们可以离线或在线完成。每次我们训练完一个空间模型,训练好的模型会被保存到一个文件夹中。该文件夹通常包含以下文件:
*文件名可能不同
- meta.json
- ner(文件夹)
- 标记器
- vocab(文件夹)
要创建一个模型工件,我们可以简单地将这个文件夹压缩成 tar.gz 格式,并上传到 S3 存储桶。或者,我们也可以通过在 SageMaker 培训部分下创建一个培训任务,用 AWS SageMaker 在线培训模型。但是我们需要提供来自 AWS ECR 的定制训练图像和来自 AWS S3 bucket 的训练数据。
希望您现在对一般的工作流程有一点清楚了。现在,让我们开始创建我们的自定义空间模型容器,并将其上传到 AWS ECR 作为我们的训练图像。
在 AWS ECR 上创建自定义容器
因为 spaCy 不是 SageMaker 内置算法之一。我们必须首先在 AWS ECR 上创建一个空间模型容器,并在 SageMaker 上创建模型端点时将该容器指定为训练映像。
SageMaker 提供了两个选项,其中第一个选项是使用 sage maker 提供的内置算法,包括 KNN、XgBoost、线性学习器等。而另一个选项是使用 ECR 中的自定义 docker 容器。
上面的文章是一个很棒的参考,它启发了我如何部署一个定制的 spaCy 容器。我强烈建议通读这篇文章,并对 docker 容器的内部工作方式有更深入的了解。一开始对我来说理解起来很复杂,但是很有帮助。🧠
简而言之,该部分包括以下步骤:
- 编辑 Github repo 下的文件(主要是 docker 文件,train.py 和 predictor.py )来训练自定义 spaCy 模型,并可以返回模型预测。
- 通过对 spaCy docker 容器执行本地测试来检查计算机终端上的预测结果,如果出现任何错误,则调试代码。
- 最后,将空间容器上传到 AWS ECR。
对于训练文件,目标是修改代码来训练自定义空间模型。将 CSV 格式的数据读取到名为 train_data 的数据帧后,我们需要将数据帧转换为 spaCy model 可以接受的输入格式。我建议阅读我以前写的一篇关于如何为 NER 任务训练空间模型的文章。
本质上,这里我们需要将数据帧转换成以下空间格式:
TRAIN_DATA =
[
('Amazon co ca', {'entities': [(0, 6, 'BRD')]}),
('AMZNMKTPLACE AMAZON CO', {'entities': [(13, 19, 'BRD')]}),
('APPLE COM BILL', {'entities': [(0, 5, 'BRD')]}),
('BOOKING COM New York City', {'entities': [(0, 7, 'BRD')]}),
('STARBUCKS Vancouver', {'entities': [(0, 9, 'BRD')]}),
('Uber BV', {'entities': [(0, 4, 'BRD')]}),
('Hotel on Booking com Toronto', {'entities': [(9, 16, 'BRD')]}),
('UBER com', {'entities': [(0, 4, 'BRD')]}),
('Netflix com', {'entities': [(0, 7, 'BRD')]})]
]
然后,在训练文件中,我们定义我们的 train_spacy 函数来训练模型。train_spaCy 函数类似于下一篇文章中的函数。请注意,本文中的代码是用 spaCy 版本 2 编写的。
https://manivannan-ai.medium.com/how-to-train-ner-with-custom-training-data-using-spacy-188e0e508c6
训练文件示例
在预测器文件中,我们需要编辑预测函数下的代码。预测函数将 test_data 作为输入。test_data 需要是一个数据帧,我们可以将训练好的空间模型应用到包含我们想要预测的文本的列上。
然后,我们可以为模型的预测创建一个新列。如果这是一个 NER 任务,那么新列将包含从每行输入中提取的实体。此外,我们可以调整 predict 函数是只返回预测列表,还是返回包含源列和预测列的整个数据帧。
预测文件
最后,在 docker 文件中,只需根据所需的库相应地编辑代码。对我来说,我添加了RUN pip 3 install-U spacy = = 2 . 3 . 5来安装我使用的 spaCy 的正确版本。
如果你来了,恭喜你!🥳
要将我们的容器图像推送到 Amazon ECR,我们可以遵循里面的代码(来自上面的 文章的 Github)
- 构建 _ 和 _ 推送. sh 文件
- 自带创建算法和模型包笔记本
这些文件在那篇文章中有很好的解释。我们可以简单地运行代码,将我们自己的定制空间容器推到 AWS ECR 上。
直到这一步,我们终于将自定义空间容器上传到了 AWS ECR。这是模型部署的关键步骤。现在,我们将切换到 AWS SageMaker 并创建一个模型端点。
在 AWS SageMaker 上创建一个模型端点
如 AWS SageMaker 开发者指南中所述:
Amazon SageMaker 是一项完全托管的服务,使数据科学家和开发人员能够快速轻松地构建、训练和部署任何规模的机器学习模型。Amazon SageMaker 包括可以一起使用或单独使用的模块,以构建、训练和部署您的机器学习模型。
SageMaker 的界面结构如下:
Sections on the AWS SageMaker...--->Training
------>Algorithms
------>**Training jobs**
------>Hyperparameter tuning jobs--->Inference
------>Compilation jobs
------>Model packages
------>**Models**
------>**Endpoint configurations**
------>**Endpoints**
------>Batch transform jobs...
要创建空间模型端点,我们需要完成以下步骤:
- 在 AWS sage maker-Training 下创建一个培训任务。或者,简单地在本地训练你的空间模型,把它压缩成 tar.gz格式,然后上传到 S3 桶。目标是在这一步准备一个模型工件。
- 在 AWS SageMaker-Inference 下创建一个模型。我们需要指定来自 AWS ECR 的容器图像和来自 AWS S3 存储桶的模型工件。
- 在 AWS SageMaker 推论下创建一个端点配置。我们需要为端点配置指定一个模型。
- 在 AWS SageMaker 推论下创建一个端点。我们需要为端点指定一个端点配置。
如果你在 AWS SageMaker 上面的步骤中有任何问题,下面的教程将会很有帮助!🏂🏻
设计 AWS Lambda 函数
如 AWS Lambda 开发人员指南中所述:
Lambda 是一种计算服务,让您无需配置或管理服务器即可运行代码。Lambda 在高可用性计算基础设施上运行您的代码,并执行计算资源的所有管理,包括服务器和操作系统维护、容量供应和自动扩展、代码监控和日志记录。
就空间部署而言,我们将使用 AWS Lambda 作为接口,可以在用户输入和空间模型端点之间进行通信。具体来说,我们希望将推理文本作为 Lambda 事件输入,并在 Lambda execution 选项卡上接收模型响应。
关于如何在 AWS Lambda 上调用 SageMaker 端点的一些细节可以在与上面相同的教程中找到。本质上,我们希望在 Lambda 上编写一个 python 处理函数,它可以执行以下步骤:
- 输入事件:推理文本列表
- 将事件读入数据帧(可选)
- 将数据帧写入 CSV 缓冲区(保持数据格式的一致性)
- 将缓冲区的值转换成 ASCII 格式(与 AWS 要求保持一致)
- 调用 SageMaker 端点并接收响应
- 将响应解码为 UTF 8 格式
- 将响应读入数据帧(可选)
- 输出响应:模型预测列表
关于创建 CSV 缓冲区或 ASCII / UTF-8 转换步骤的一些细节可能是不必要的,但最终它对我有用。如果你有更好的主意,请告诉我!😸
如果一切正常,现在,我们应该能够看到在 AWS Lambda 执行结果选项卡上,模型预测的列表被打印出来。💯
摘要
这就结束了我关于使用 AWS 部署定制空间模型的文章。概括地说,工作流包括服务:
- 第一步:AWS ECR
- 第二步:AWS S3 铲斗(可选)
- 第三步:AWS SageMaker
- 第四步:AWS Lambda
希望它能帮助/激励任何从事类似工作的人😋
感谢您的阅读!
🏇🏼
相关阅读
https://xoelop.medium.com/deploying-big-spacy-nlp-models-on-aws-lambda-s3-2857bfc143ba *
全球 ML 社区的数据管理平台
走出数据工程,为混乱的数据提供系统的解决方案

米卡·鲍梅斯特在 Unsplash 上的照片
简介
如果有一个机器学习(ML)社区需要立即采取的行动,那就是有效地处理数据危机。我们面临的数据危机是由数据工程的成本和复杂性以及新出现的工具和方法的大杂烩推动的。
关于“民主化人工智能”的讨论已经持续了太长时间,各种倡议和努力跨越了所有的想象空间。然而,关键的 ML 成分,数据,似乎已经从讨论中缺席。冒着指出显而易见的风险,一个正常运行的 ML 管道需要在 ML 民主化之前实现数据的民主化。
很少有组织的 ML 工作没有受到开发和维护数据集所需资源的严重阻碍。从学术界到工业界,从材料科学到法律,存储、管理、共享、查找和使用数据用于 ML 研究和应用的需求是很少有人能够克服的障碍。这严重阻碍了 ML 在学术界和工业界的发展,产生了重大的社会影响,包括阻碍科学研究。
明确地说,数据民主化不仅仅是获取数据,而是赋予需要使用数据的人权力。我们给所需的技能和工具起了个名字——数据工程——丝毫无助于解决问题。虽然完全消除数据工程是不现实的,但必须努力显著减轻它。为了做到这一点,ML 数据管理平台的主要开源设计需要 1)对全球社区(commons)可用,以及 2)可由非编程专家使用。
少数几家世界领先的技术公司拥有资源和专业知识来开发满足其内部需求的数据管理平台。然而,这只会加剧贫富不均的状况。源自云的主流开源设计不太可能,因为主要的云提供商都在构建自己的专有平台。然而,没有什么可以阻止云提供商实现他们自己版本的完全集成到他们堆栈中的通用平台。
要求
当提到平台时,我指的是用于数据管理的一体化解决方案。从高层次来看,数据管理平台需要以下列内容为目标:
- 通过将工程从数据工程中分离出来,为世界各地的数据科学和 ML 用户提供支持。
- 支持管理 ML 中的整个数据生命周期。
- 通过集成整个 ML 工作流程来支持 ML 生命周期。
- 支持主流的 ML 框架。
- 支持各种 ML 数据,包括传感器和仪器数据。
需要识别的内容
虽然这一切听起来很棒,但问题是没有这样的平台存在,所以问题是我们如何到达那里?最近,我一直在与一些个人和组织讨论这个问题,并得出结论认为,需要认识到以下几点:
不是工程问题
必须认识到,数据管理平台缺乏主导设计不是一个工程问题,而是一个超越工程的战略问题。
专业知识和资源
Big tech 拥有开发和维护这样一个平台的专业知识和资源,并且正在进行可行的内部开发工作,以满足他们自己的内部需求。相比之下,初创公司没有资源、经验和用户基础来启动它。为了启动这项工作,应该通过 big tech 内部目前正在开发的一个或多个内部平台的贡献来“资本化”。
初始用户群和采用情况
如果这个平台有任何未来,它需要有一个广泛采用的路线图——包括大技术的内部要求。获得这种采用的最佳方式是通过平台本身的集体协作。由于庞大的内部和外部用户群体,一个大的技术起源将给予该平台必要的采用。庞大的用户群将有助于该平台经得起未来考验,证明开发和维护成本的合理性,并推动对该平台的持续采用和投资。
它还将向更广泛的社区发出信号,表明从 1)人的角度来看,投资学习和采用该平台是值得的——考虑将与该平台相关的培训项目纳入其课程的大学,以及 2)从技术角度来看,创业公司和技术提供支持集成。
协作经济学
数据工程挑战甚至对大型科技公司来说也是一项成本,而合作开发一个通用平台的主导设计将降低大型科技公司的开发成本,对其他所有人来说也是如此。该平台不会成为直接的收入驱动因素,而是通过增加 ML 的应用和采用,为创收生态系统做出有益贡献。这一层次的合作具有经济意义——竞合。
等待中的建筑辩论
面向数据的架构与微服务架构是讨论的一个关键点,至少在短期内可能不会解决。只要有几家公司追求,追求其中一个或两个都是可行的。
接下来的步骤
为了超越空谈,ML 社区内越来越多的团体正在审查发起以下活动的选项:
- 建立参与机制,促进行业在开发和发布数据管理平台方面的协调和合作。
- 认识需求,并提出平台主导设计的要求。
- 审查当前的开发工作和数据库技术,并分享建立此类平台的经验。
- 设计新的解决方案和发展路线图,以应对未来的挑战。
- 协调公共平台的开发和发布。
- 协调平台的维护和未来发展。
总结
数据科学和机器学习社区与领先的技术公司合作,需要整合现有的开发工作,以协调最终发布的公共数据管理平台。
一个用于 ML 的数据管理平台,确保在整个 ML 生命周期中的一致和高质量的数据流,将使构建高质量的数据集和 ML 系统的构建更具可重复性和系统性。
现在是联合起来让数据工程再次变得不酷的时候了。首先要认识到,一组组织在一个平台上合作可以实现更多的目标,因为需求大体相似,如果组内有差异,这些差异会被世界各地的其他组织共享。
相关阅读:
面向大数据的数据清洗方法综述。
https://www . science direct . com/science/article/pii/s 1877050919318885
面向数据编程的演变
https://tborchertblog.wordpress.com/2020/02/13/28/。
分析和缓解 https://vldb.org/pvldb/vol14/p771-mohan.pdf DNN 培训
中的数据停滞
数据科学中的基准和过程管理:我们会走出困境吗?
https://dl.acm.org/doi/10.1145/3097983.3120998
部署机器学习的挑战:案例研究调查
https://arxiv.org/abs/2011.09926v2
DBMS 大概率阴天:企业级 ML
https://arxiv.org/abs/1909.0008410 年预测
面向所有人的数据工程
https://www.sigarch.org/data-engineering-for-everyone/ T4
生产机器学习中的数据生命周期挑战
https://sigmodRecord . org/publications/sigmodRecord/1806/pdf/04 _ Surveys _ polyzotis . pdf
生产机器学习中的数据管理挑战
https://dl.acm.org/doi/10.1145/3035918.3054782
机器学习的数据平台
https://dl.acm.org/citation.cfm?doid=3299869.3314050
软件团队中的数据科学家:最新技术和挑战
https://ieeexplore.ieee.org/document/8046093
机器学习的数据验证
https://mlsys.org/Conferences/2019/doc/2019/167.pdf
检测数据错误:我们在哪里,需要做什么?https://dl.acm.org/doi/10.14778/2994509.2994518T21
用 ML 推理扩展关系查询处理
https://arxiv.org/abs/1911.00231
火鸟:亚特兰大预测火灾风险和优先消防检查。
https://www.cc.gatech.edu/~dchau/papers/16-kdd-firebird.pdf
对于大数据科学家来说,“看门人工作”是 Insights 的关键障碍
https://www . nytimes . com/2014/08/18/technology/for-the-Big-Data-Scientists-Hurdle-to-Insights-Is-guardian-Work . html
朦胧研究:以数据为中心的人工智能
https://github.com/hazyresearch/data-centric-ai
项目建议:机器学习排名服务的数据管理平台
https://research.google/pubs/pub47850/
现代面向数据编程
http://inverse probability . com/talks/notes/modern-data-oriented-programming . html。
数据准备水平。
https://arxiv.org/abs/1705.02245
人、计算机和热得一塌糊涂的真实数据
https://dl.acm.org/doi/10.1145/2939672.2945356
重新思考用于 ML 的数据存储和预处理
https://www . sigarch . org/re thinking-Data-Storage-and-Preprocessing-for-ML/
机器学习的规则:ML 工程的最佳实践
https://developers . Google . com/machine-learning/guides/rules-of-ML
机器学习系统的技术准备水平
https://arxiv.org/abs/2101.03989
tf.data:一个机器学习数据处理框架【https://arxiv.org/pdf/2101.12127.pdf
T22
https://eng.uber.com/uber-big-data-platform/大数据平台:100+Pb,分钟延迟
Zipline: Airbnb 的机器学习数据管理平台
https://Data bricks . com/session/zip line-airbnbs-Machine-Learning-Data-Management-Platform
数据仓库的数据网格方法
数据网格的利与弊,以及组织应该采取的改进数据策略的步骤。

面对快节奏的大数据世界,数据仓库应该如何发展?图片由约翰经许可做。
数据网格是一种具有争议性的新范式,它提供了一种类似于微服务架构等软件工程最佳实践的数据策略。但是,这样对数据对吗?
最初的概念是由 Zhamak Dehghani 提出的,他声称,领域驱动的自助式数据方法是未来的发展方向,即数据的产品方法,在这种方法中,不断评估收集的数据,以确保其相关性、价值和实际使用。
这个概念与拥有一个包含所有公司数据的遗留单片系统并置。Data mesh 提供了一种去中心化的数据管理方法,并将其交给更接近数据及其用途的人。
本文将讨论数据网格的利与弊,以及组织应该采取的改进策略的步骤。我的意见是,这种方法早就应该得到重视,但我们必须谨慎行事,并对最初的概念进行一些可能的修改。
如果我们很快接受了这种新的方法,并把我们的组织连根拔起,我们就部分地错过了这一点。随着数据的增加,我们必须以模块化和尽可能敏捷的方式组织它们。这很有挑战性,因为数据本质上不如软件灵活,原因有二:1)对数据模型的任何微小改变都会产生连锁影响;2)大多数公司都创建了庞大而复杂的数据仓库。创建考虑周到的数据模型需要时间和协作,这些模型能够正确地表示整个组织中的原始数据和适当粒度的数据。

图片由约翰做
什么是数据网格?
数据网格是数据的领域驱动设计(DDD )!在 DDD 中,数据的结构是由组织的域决定的,因此每个域将驱动组织和逻辑。
由于数据可以理解为实体和属性,这两者都是自然领域驱动的,DDD 在这里至少和在软件工程中一样有意义。Zhamak 的数据网格将产品思维放入数据领域,其中数据产品是 API。数据需要明确定义和记录,即“可发现的”
数据网格概念与传统的数据集市有很多共同之处,传统的数据集市是数据仓库中数据的集合,这些数据仓库通常是领域驱动的,由一个小团队以更敏捷的方式管理。它们被用来回答特定的战略问题和发展洞察力。
公平原则能有什么帮助?
数据的公平原则符合数据网格的目标,并在构建数据网格时提供了清晰的检查组件。

图片由约翰·多
这些原则确保特定数据源的数据模型和字典可以在一个中心位置找到(“可找到”),并且访问模式和需求清楚地记录了关于如何请求访问数据源(“可访问”)的信息。很有可能 HIPAA 或 PII 培训需要在某人获得访问权限之前进行,或者访问权限仅限于具有特定职称或类型的员工。这里的要求很简单,访问要求和限制对员工或用户来说并不神秘。
“可互操作”组件要求不同数据源之间的转换机制是清晰的。例如,如果有一个客户域、一个订单域和一个供应商域,那么订单域文档应该清楚地指出如何从订单数据资产连接到客户域和供应商域。简而言之,互操作性确保用户可以从其他域连接到其他域。
最后,“可重用”组件将所有组件集合在一起。创建任何数据资产的目的是能够重用应用程序、工具和报告中的组件。
数据仓库的正确之处是什么?
数据仓库为我们提供了公司数据的完整视图。数据仓库中有两种主要的思想流派:Inmon 企业数据仓库(EDW)方法和 Kimball 维度建模方法。
edw 擅长快速写入和传统的提取、转换和加载(ETL)模式。维度建模带来了更快的读取速度和更少规范化(更多非规范化)的表方法。像 Redshift 或 Azure Synapse 这样的新云数据仓库使得遵循提取、加载和转换(ELT)方法变得更加容易,在这种方法中,数据仓库本身被用来执行转换。因此,数据仓库的功能仍有相当大的灵活性,尤其是市场上较新的数据仓库。
数据仓库有什么问题?
如果您曾经处理过大型的、单一的数据仓库,那么您将会对修改业务逻辑、合并新数据或解决数据中的缺口所涉及的痛苦非常熟悉。它很慢,很麻烦,而且需要一段时间业务才能看到结果。为了理解数据的复杂性,你通常需要和一个在最初概念上工作了 20 年的老手交朋友。如果发生了一些事情,如中彩票、退休或当前组织之外的新机会,大量的数据知识可能会丢失。在我的职业生涯中,我见过许多数据仓库被低总线因子所困扰,也许是因为有太多的东西需要了解。
由于昂贵的存储和相对便宜的计算(相对而言),edw 被设计成以标准化的方式容纳不同的数据。规范化确保了没有冗余和快速写入,但这是有代价的:昂贵的连接和读取。更现代的数据存储方法倾向于远离完全规范化。这意味着,使用第一范式或第二范式通常就足够了,没有必要强制数据符合 6NF 或 DKNF(第六范式或域键范式)的性能要求。
除了数据建模的 EDW 方法,维度建模也是数据仓库中使用的一种技术。
数据网格会出什么问题?
缺乏明确的数据所有权可能是一个巨大的问题。一些复制的数据不一定是问题,但是如果没有明确的团队所有权,数据复制的问题将会恶化。如果没有明确的所有权,通过查看不同的数据产品,您可能会得到相同问题的不同答案。
如果您向组织中的人员提出同样的定量问题,却得到不同的答案,这表明您的数据策略不起作用。这些战略失败通常是管理不善的结果,而不是工程的结果。
我见过竞争团队从这些独立团队构建的数据库系统中计算和预测相同的信息。通过创建一个通用的解决方案,工程团队与业务部门一起建立通用的定义,并寻求解决相互竞争的源系统之间的冲突,可以获得更多。但是创建一个共同的解决方案和事实的来源需要一个团队明确的所有权。
数据网格能带来什么好处?
当处理得当时,数据网格澄清了谁拥有数据,因此谁可以帮助添加额外的特性,提供关于差异和异常的更多信息,并与业务和软件团队合作解决差距。
数据被分解成不需要,在大多数情况下也不应该完全规范化的域。完全规范化的数据不再是必要的,因为不仅存储便宜,规范化还增加了商业智能(BI)和高级分析用例的连接复杂性。相反,在我工作过的大多数团队中,我们更多地选择了“starflake”模式:雪花和星星的组合。这使得我们能够满足其他开发团队、高级分析和报告用例的要求。
电子商务业务可能的领域划分

作者和 John Do 提供的图片
采用数据网格的另一个重要目标和理想结果是域之间有清晰的集成点(公平原则中的互操作部分)。这可以利用一个集成表或 API 端点,但也可以内置到作为 API 公开的或预先计划的事实表中。例如,要将您的客户域连接到您的订单域,您应该在汇总订单详细信息的事实表中包含一个客户 ID。您还可以选择将它包含在主订单表中,以便更有效地连接客户和订单详细信息(不仅仅是高级信息)。

作者图片
上图显示了应如何区分交易和分析,以确保 ACID 合规性和稳健的分析系统(更多信息见下文)。ACID 代表原子性、一致性、隔离性和持久性,是数据库系统中的一个重要考虑因素。
数据仓库的数据网格方法
如果我想要不同谷物的快速数据怎么办?这就是我提出数据网格概念的替代方案的地方。我建议我们不要放弃数据仓库,而是要建立面向领域的数据仓库,以获取最少量的可用数据。如果您在电子商务系统中工作,您希望存储所有订单更新和修改,而不仅仅是当前状态。当前状态可以存储在事实表中,所有这些数据可以直接从表中或在 API 中使用。即使数据是直接从数据库中使用的,读取副本也可以方便地提高读取性能,因为副本不会与主数据库竞争。读取副本并不完全是实时的,但它们最终往往与个位数毫秒延迟一致,这使它们足够接近实时,以满足大多数业务需求。
API 本身不是数据策略,但它们是健壮数据策略的一部分。API 优先和纯 API 策略从错误的问题开始;他们在没有事先询问是否应该的情况下就要求移动数据。没有分析,就很难理解你应该如何做事情,或者你想要构建的应用程序是否有意义。将一个数据库转化成一个 API 比反过来更容易。如果您想从数据中获得洞察力,API 优先或仅 API 不是前进的方向。API 允许应用程序相互对话,因此 API 优先相当于应用程序优先。API 以编程方式传递数据,如果不将数据转移到另一个数据产品中,数据就不能用于分析目的。API 在位置上缺乏计算和互操作性,所以它们离完整的数据策略还很远。
数据库提供了稳定性和存储能力来包含特定域中的所有数据,例如订单。您目前可能有多个包含订单信息的数据库,但是以一种避免破坏性转换的方式进行整合是值得的。破坏性转换是导致数据丢失的数据转换。例如,如果您将订单数据聚合为仅包含当前状态,而忽略所有中间订单更改,那么您就混淆了一些可能有用的数据。
我提出的数据网格版本的关键是,在数据仓库级别,数据应该以最小的粒度存在,根据需要在事实表中滚动。这种方法成功的关键是将数据视为一种产品,它存在于比传统 EDW 更模块化的组件中。以这种方式整合数据库是保持数据尽可能敏捷的一个重要部分。因此,如果出于某种原因需要添加一个列,那么这样做是相对简单的。数据库和 API 开发应该同步进行,以确保满足所有需求,并且开发周期尽可能快。
我见过极其复杂的数据库网络,但是如果齐心协力淘汰遗留数据库(这确实是可能的),您的业务会发展得更加容易。将数据网格的概念用于数据仓库可以帮助系统地淘汰遗留的 edw。
同样值得注意的是(为了证明 API 本身不是数据策略),市场上的所有 BI 工具要么需要数据库连接,要么可能没有足够的 API 连接器。如果他们有一个 API 连接器,由于往返连接的原因,他们往往不能胜任。但是,即使您为可靠的流数据构建了自己的套接字连接,API 也应该与表模式相匹配,以实现数据一致性和易用性。REST APIs 是数据策略的重要组成部分,因为它们促进了更好的安全性,与技术无关,并且可以通过使用堡垒或反向代理进行缓存和扩展。

作者图片
*该 API 将以与 fact_orders 表相同的模式返回数据,但也可以有一个参数来选择单个订单,而不是简单地提取所有可用的数据。
我们如何迁移?
系好安全带。这不会像听起来那么难,但它包括几个关键步骤:
- 记录您当前的系统以及它们包含哪些高级域
- 在模式或表级别分离遗留 edw 和新系统的所有权
- 明确定义哪些数据工程师负责哪些数据领域
- 对不同领域中的一些重复数据感到放心。您仍然应该尽量减少重复,但这不值得为几个在不同领域中使用略有不同的列而争论。
数据工程师应该与利益相关者公开讨论数据颗粒,并在记录级别包含世系。在中构建血统通常就像创建一个“源”列一样简单;在许多情况下,遗留数据和新数据需要一起暂存,并以不同的方式进行转换。哪些数据来自哪个系统需要尽可能透明,以方便地提高数据质量。
将一个遗留的庞然大物迁移到一个(或几个)领域驱动的数据网格需要做大量的工作,但这是值得的。数据不需要成为一场永恒的战斗。所有公司都可以通过这一流程采取措施解决数据问题。
需要分散的数据命令,以便您可以成功地一直到属性级别。您的数据产品和支持团队必须为数据的个人所有者提供明确的界限和指导。这些界限和指导可以包括用于命名一致性的类别词、标准清理脚本、用于处理具有不同延迟要求的数据的方法等。领域团队需要理解他们正在处理的数据的复杂性,以及它与整体业务的关系。对数据进行适当的指挥和控制很难,需要建立信任和心理安全,但这是可能的。
就像 20 世纪 60 年代登上月球一样,整理公司数据很难,但也很有必要。它需要大量的初始努力,但这是值得的努力。那些没有抓住机会的人注定会被落在后面。
Go 列车延误分析的数据管道——ML 与 Rust 的作用
铁锈动力 ML 管道
欢迎回到第 3 部分,这是火车延误分析系列的最后一篇文章。之前,我们构建了 UI 仪表板和具有弹性功能的 API 集成,并将其部署到 Heroku,并将其发布到 Rapid API。
如果您刚刚看到这篇文章,请阅读第 1 部分和第 2 部分。
https://medium.com/geekculture/a-data-pipeline-for-go-trains-delay-analysis-part-2-e5b9ef0ea315
这最后一集将基于 Rust 一贯收集的数据,重点介绍 ML 引擎(管道)。
首先,让我们刷新一下我们的总体高层架构和数据流。

架构—截图/作者版权所有
这一集将基于功能性和非功能性需求来冲洗我们的应用程序设计。作为本文目标的一部分,我们想向您展示,在大多数 ML 项目中:
ML 模型只是ML 项目的一部分,一个成功的 ML 项目应该有整体架构设计、应用程序设计、基于你想要解决的问题选择正确的模型、一致的模型改进/学习、模型部署和整体运营效率,这有时会导致基于你公司环境的运营模型(组织结构)定义。
先说 SFR …

系统功能需求
- ML 引擎应访问数据存储,以使用数据一致地执行模型训练。
- ML 引擎应该可以使用 K 折叠交叉验证来评估机器学习模型的有限数据样本。
- ML 引擎应使用合适的模型根据给定的数据集预测延迟分钟。(注)
- ML 引擎将构建模型并将模型部署到目标 API 服务器。
- ML 引擎构建的模型应该与构建的 API 集成,以执行预测功能,如#3 和#4。
非功能性需求
- ML 引擎对于不同的数据存储应该是可扩展的,在元数据的上下文/条件是一致的。
- ML 引擎应该一致且有效地执行数据训练。(每数据量增长定义的 SLA)
- ML 引擎应该与 API 服务分开部署到不同的服务器上,以减少影响 API 操作的单点故障。
- ML 引擎应该易于维护,以便为未来的 ML 需求添加不同的模型。
- ML 引擎应该在用户中按照定义的节奏运行,每天、每月、每周或每年。
现在我们已经有了足够好的需求捕获,背后的想法是确保我们对您的请求者有正确的期望,它可能是您的客户,可能是您的业务对手,也是我们衡量成功的标准。
注意:在这个用例的实际需求中,我们可能首先需要预测预先给定的日期是否会延迟,但是这个预测将需要所有 Go 火车时刻表的日期集,这意味着我们将需要更多的数据源和数据清理和映射,然后我们可以利用监督分类来预测。
然而,在本文中,我们只关注端到端的 ML 管道。未来我们会花更多的时间来讨论使用 Rust 进行机器学习。
ML 发动机设计
接下来,让我们开始将需求映射到我们的应用程序设计。
如果我们仔细阅读 SFR,#1 与设计模式相关,#2 到#4 是我们在引擎中需要的功能,而#5 是部署策略(与基础平台相关)。
所以让我们更深刻地思考。ML 引擎的功能不应该与我们连接的数据存储不同,这符合策略模式。
根据策略模式,类的行为不应该被继承。相反,它们应该使用接口封装。这与开放/封闭原则 (OCP)是兼容的,该原则提出类应该对扩展开放,但对修改关闭。
在 Rust 中,接口是特征,这意味着它拥有应该被封装以满足我们的#2 到#4 需求的功能,不同的数据存储作为我们的数据源策略。
为了更详细的了解,我们来画一下。

架构—截图/作者版权所有
让我们把这个翻译成我们的代码,它也可以满足我们的 NFR 中的#4。
作者版权
在 Trait 中,您可以看到如下的逻辑顺序序列:
数据摘录
该函数负责连接到数据库以提取数据,因为一些目标格式取决于我们决定的存储。
我们有多种选择,如传统、Hadoop 数据湖、云存储桶(s3、ADLS gen2、GCP 存储)。
为了这个项目的简单,数据量似乎很小,让我们保持 csv 格式。
另一个令人兴奋的决策点将是如何一致且高效地获取数据,这符合我们的 NFR 之一,例如增量或批量加载。
但是,为了满足性能需求,我们将考虑使用复制输出,而不是批量加载(SQL select *)然后在 for 循环中写入文件,为什么?一般来说,这里有几个因素:
- 网络延迟和往返延迟
- PostgreSQL 中每条语句的开销
- 上下文切换和调度程序延迟
- 成本,如果每个迭代器写一次的话(你不是)
COPY-针对批量装载的特定优化。
按副本提取—按作者提取代码片段
将 CSV 读取到数据帧
在 Python ML 世界中,panda dataframe 是最流行的数据分析库,dataframe 是其中的关键组件之一。Rust 比熊猫需要更多的工作,但 Rust 更灵活,性能更好。今天,我想介绍一下 Polars。
Polars 是用 Rust 实现的一个速度惊人的数据帧库,使用Apache Arrow column 格式作为内存模型。
它有一些重要的基准。
https://h2oai.github.io/db-benchmark/
因此,我们将利用这个板条箱来构建数据框架,以准备 ML 模型培训和构建。
阅读 CSV —作者的代码片段
ML 培训和模型构建
我们都准备好了;我们将数据以 csv 格式加载到文件系统中,然后将文件读取到数据帧中。接下来将开始我们的 ML 模型选择和训练。
Rust 有一些完美和方便的 ML 板条箱/框架,其中大部分你可以在这里找到。
https://github.com/vaaaaanquish/Awesome-Rust-MachineLearning
我的选择是 SmartCore,这是一个全面的机器学习和数值计算库,Rust 是我们的用例。该库提供了线性代数、数值计算和优化工具,并实现了一种通用、强大但仍然有效的机器学习方法。
基于我们的 SFR,我们希望基于预先给定的数据集来预测延迟分钟。我们将重点关注的算法应该是线性回归,这是一种统计监督学习技术,通过与一个或多个独立特征形成线性关系来预测量化变量。
对于 SmartCore,根据我们的数据集训练线性回归模型。它将需要 2 个数据帧。一个是特征,它识别和选择最相关的输入变量的子集;另一个是预测目标。
获取特性和目标—作者的代码片段。
一旦我们有了特征和目标数据框架,下一步将是将特征和目标转换为 smartcore 模型培训的可读格式。
将数据帧转换为矩阵
下一步是利用 smartcore 线性回归来构建模型。
构建 RL 模型—作者代码片段
到目前为止,模型训练和构建已经完成:
主流程—作者的代码片段
ML 模型部署
一旦构建了模型,我们就必须将它部署到 API 端点(我们在第 2 部分中创建的)的 API 服务中,以获取和使用它。
对于部署,我们将利用 Github 操作来触发 ML 管道部署,这将源模型从 ML 管道 repo 复制到 API repo,这将根据您在 Github 操作工作流中定义的规则启动 API repo 构建。
https://github.com/marketplace/actions/push-directory-to-another-repository
对于我所有的项目,我将从开发分支开始构建,它被认为是 UAT,然后主分支将是生产。
行动中的 ML 模型
如果您还记得第 2 部分,当我们构建 API 时,我们为扩展任何新端点准备了灵活性,因此在这种情况下,我们将添加一个端点,如下所示:
预测 API 加载模型—作者的代码片段

作者截图
最后的话
在这一系列文章中,我们主要学习了如何从一个想法变成现实。
- 我们从用例、分析、概念、解决方案开始,当然还有编码和构建。
- UI 仪表板构建
- API 集成层构建。
- ML 管道会生锈。
- 扩展 ML 端点的 API。
这是一个独特的构建和学习项目;系列文章中显示的数字、分析和仪表板与 Metrolinx 提供的任何服务或 API 无关,仅是我的分析评论,供我学习使用。
我开始写作之旅已经快三年了。你们的支持是让我不断前进,写出更多精彩学习分享的最重要动力。
https://jayhuang75.medium.com/membership
另外,你可以用下面的链接给我买一杯咖啡,让我有动力在周末进行更多的锻炼和学习。
你能给我买杯咖啡吗?
Go 列车延误分析的数据管道—第 1 部分
周末建设和学习
当我回顾我的文章频率时,我发现“周末建设和学习”最有成效的季节是秋天。我认为原因是秋天的颜色和咖啡。好的。让我们看看这个周末我们将建造和学习什么。

在这个构建和学习系列中,我将带您了解概念、设计、架构和构建,这与编码结合在一起,确保我们对我们最喜欢的编码部分进行端到端的逻辑思考。
用例定义
自从疫情和 WFH 开赛以来,我不需要乘坐 Go Train 去上班,但我仍然通过电子邮件收到了我注册的 Go Train 延迟警报。时不时地,当我更频繁地收到提醒邮件时。我想到的问题是:
- 每天延误多少次?
- 平均延误时间是多少分钟?
- 哪个车站发生的延误最多??
- 如果这些数据可以建立一个模型来预测火车晚点会怎么样?这将是非常整洁的。
- 等等。

约翰·麦克阿瑟在 Unsplash 上的照片
好了,我们来总结一下我们的要求;它分为两项:
- 作为一个用户,我希望看到一个仪表板,它显示关于列车晚点信息的数据元素。
- 作为一个用户,我想预测在即将到来的日期去火车延误。
所以对于这两个需求,最重要的部分是第一个,因为第二个是基于第一个的。让我们分清主次。
技术分析
需求就是需求,是人们对归档器或结果的定义。然而,
- 能做到吗?
- 快速交付成果有什么可用的吗?
- 有什么技术挑战吗?
- 等等。
上面的分析,我们可以称之为业务/技术分析,它将业务语言/需求翻译成 IT/技术语言。这项任务通常由传统公司的业务分析师(BSA)或架构师负责,有一个项目里程碑。
在一些小型创业公司中,它将由 sprints 中的工程师负责。
机器学习管道需要一致的数据输入,以提高准确率。因此,在这种情况下,我们需要知道如何获得最简单快捷的数据。让我们谷歌一下,看看现有的 API 或数据集是否可用,但以可扩展的方式需要。
很快,我发现 Go Train 服务确实存在一些 API /数据集,如下所示:
https://www.metrolinx.com/en/aboutus/opendata/default.aspx
然而,经过一些审查和分析,结论如下:
- 根据我到目前为止的搜索,数据集(GTFS)文件不包含 Go Train Delay 的警报和信息。
- 我们需要注册 API 来访问它;我已经提交给个人使用,但我仍在等待批准。根据数据集和响应数据结构,我没有看到当前分析的延迟时间,这是我们需要的关键信息。
好吧,让我们退一步,我们需要什么?我们需要知道
- 发生日期和时间延迟
- 出发站
- 预定的出发时间
- 目的站
- 目的地预定时间
- 延迟分钟。
但是该信息存在于提醒电子邮件中。酷毙了。我如何持续自动收到这些提醒邮件?
体系结构
该架构将更加关注如何。
这可能是一个先决条件。我用来接收 Go Train 提醒消息的电子邮件是 Gmail。
经过进一步分析,我们发现 Google GMAIL API 提供了访问邮件的权限,甚至还有推送通知功能。这可用于增量数据处理,避免每次繁重的处理。
https://developers.google.com/gmail/api
一旦我们知道了如何获取,现在将关注于获取什么,如果我们可以启动访问并处理电子邮件,只找出去火车延迟电子邮件,然后我们可以检索我们需要的信息作为如下模式的警报电子邮件:
主题: 列车晚点—奥克维尔去 17:32 —联合车站 18:15
奥克维尔 GO 17:32 —联合车站 18:15 的列车预计将延迟 10 分钟到达 15 分钟 从克拉克森 GO 出发,由于较早的轨道检查。我们为延迟道歉。您可以使用 gotracker.ca 来跟踪您的火车,并查看您的火车预计何时到达您的车站:
如您所见,电子邮件标题已经包含了电台和预定时间,并且电子邮件内容具有延迟的 min 属性。
看来我们正走在正确的道路上。
让我们看看数据流。

架构—作者截图
构建,编码
我最近写了很多铁锈;在这个构建和学习中,我将使用 Golang。Golang 专为 Go Train 打造,很酷吧?
玩笑归玩笑,我们选择 Golang 是因为 Google Gmail API 有内置的 Golang API SDK,有助于开发过程。
这是一个专注于以最有效的方式完成工作的极好例子。这不是关于哪种语言/技术是最好的,在我之前的文章中也阐述了这一点。
https://medium.com/swlh/dog-fight-python-vs-golang-vs-rust-for-json-processing-33c1ffe15ab9
让我们开始将它分解为更多的技术细节流程:
- 通过 Gmail API 访问 Gmail 电子邮件的 OAuth 认证。
- 只接收标题为“去火车延误”的电子邮件。
- 处理每封邮件,提取我们需要的数据元素。
- 插入/加载到数据库。
- 使用数据来训练或产生谓词模型。
- 为使用的列车延误谓词公开 API。
在我们加入每一项之前,这里是我们的代码结构。

代码结构—作者截图
1.证明
这应该很简单;然而,我想强调的唯一部分是 OAuth 2.0 VS 服务帐户,以及为什么我们不使用服务帐户身份验证方法。
根据谷歌:
对 Gmail API 的请求必须使用 OAuth 2.0 凭证 授权。当您的应用程序需要代表用户访问 Google APIs 时,例如,当用户离线时,您应该使用服务器端流。这种方法需要将一次性授权码从客户端传递到服务器;此代码用于为您的服务器获取访问令牌和刷新令牌。
使用 credentcial.json,从谷歌 GCP 下载意味着通过 OAuth 过程生成令牌。并将该令牌用于将来的请求。
而且 Google Golang Gmail API 也有现成的代码。
https://developers.google.com/gmail/api/quickstart/go
2.应用程序初始和主序列运行流程
新的初始应用程序—作者代码片段
主运行流程—作者的代码片段
3.仅接收标题为“列车晚点”的电子邮件
内嵌流程电子邮件—按作者分类的代码片段
4.插入/加载到数据库
插入/加载到数据库实现—作者的代码片段
最后,运行输出和结果

跑步-作者截图

加载的表格—作者截图
额外奖励—增量负载
我假设当你关注最终结果时,你可能会问:
等一下;上面的实现只包括初始负载;增量的怎么样?
让我们现在就实施它。
增量应该基于电子邮件的更新,看起来这一切都是在 Google API / SDK 中完成的;我们将看看是否有任何 PubSub 机制可以用来监听电子邮件更新并触发处理,这意味着:
- 创建发布订阅客户端
创建 pubsub 客户端—作者截图
2。听听变化
收听订阅更改—作者截图
3。处理即将到来的新邮件。
处理更改-作者截图
新的最终结果如下。

最终初始和增量加载—作者截图
我们有初始负载,并有所有传入延迟警报的监听器,这可以保持数据库增长,并增加训练数据集的大小和容量。
第一部分的结论
在第 1 部分中,我们从用例、分析、概念、解决方案开始,当然还有编码和构建。
目标状态将是完整的端到端 ML Ops 管道。
在第 2 部分中,我们将基于 ETLed 表,开始构建
- 仪表盘
- ML 模型和用于模型 API 的 API 可以与用户请求交互。
在第 3 部分中,我们将关注 Github 操作(CI/CD)和部署。
感谢你花时间陪我度过我的建设和学习周末。
T2:这是一个独特的构建和学习项目;本文中显示的数字、分析和仪表板与 Metrolinx 提供的任何服务或 API 无关,它只是我的分析注释,供我学习使用。
我开始写作之旅已经快三年了。你们的支持是让我不断前进,写出更多精彩学习分享的最重要动力。
https://jayhuang75.medium.com/membership
另外,你可以用下面的链接给我买一杯咖啡,让我有动力在周末进行更多的锻炼和学习。
你能给我买杯咖啡吗?
https://medium.com/geekculture/a-data-pipeline-for-go-trains-delay-analysis-part-2-e5b9ef0ea315
自动化估价模型的数据科学视角
房地产,AVM
住宅物业统计估价方法欧洲标准综述

Avi Waxman 在 Unsplash 上拍摄的照片
在本文中,我们将讨论和分析欧洲 AVM 联盟于 2019 年 8 月发布的欧洲住宅物业统计估值方法标准。我们的目标是从数据科学的角度给出本指南的摘要。
在 Alexa、Siri、区块链和人工智能(AI)的时代,房地产估价需要人工审查文件,有时还需要专家现场检查,才能得出资产的价格。这是一个容易出错的过程,因为通常没有两个专家同意相同的价格。
自动估价模型(AVM)旨在将这一过程自动化,尽可能消除这些决策中的偏见和主观性。这并不是一个新概念,但随着人工智能在过去几年里因其前所未有的准确性和性能而呈指数增长,反车辆地雷因此越来越受欢迎。关于机器学习的典型教程可能会为您提供一个与房地产相关的数据集。即便如此,只有一小部分房地产企业真正利用了这些系统,更糟糕的是,有些企业甚至不使用数据库来存储与房地产相关的数据。
欧洲标准是开发物业估价模型的指南,特别是在构建此类系统时需要考虑的事项。然而,重要的是,它旨在增加对现有统计估值方法的理解、透明度和清晰度,并选择最合适的方法和评估方法。
如前所述,估价模型的目的是在其他财产价值的基础上估计给定财产的价值,类似于专家的做法。该指南考察了四种主要的不同方法,即房价指数、单参数估值、享乐模型和可比模型。
在深入研究建模方法的细节之前,本指南定义了几个主要概念,以确保所有读者对通篇使用的术语有一个共同的理解。例如,一个关键问题是我们所说的市场价值是什么?对于贷款机构来说,它是在适当的市场营销后,在自愿买方和自愿卖方之间的公平交易中,物业在估价日应该交换的估计金额,其中各方都是在知情、谨慎且没有被强迫的情况下行事。为会计术语,指的是买方在充分考虑其状况和位置,并假设其可以继续使用的情况下,准备支付的价格。
统计估价方法可能有不同的用途,因此在性质上可能不同。具体来说,它可能需要不同程度的细节,准确性和结果的信息。例如,在一个受欢迎的英国房产搜索网站上,房产估价的准确性不如抵押贷款申请中的房产估价重要。对于某些预期用途,所需的详细信息可能会也可能不会提供给统计估价方法。
另一个概念是大众评价。这是投资组合估价的实践,即在给定日期对多项财产进行估价。在所有条件相同的情况下,有时与房地产投资组合中的数据相关联的较小粒度会导致比较大粒度更低的准确性。然而,在某些情况下,评估这些资产所需的时间超过了风险。因此,这使它们成为自动化的完美候选。
该指南规定模型在使用前必须校准。在数据科学中,我们通常使用交叉验证和超参数调整来调整任何自由模型参数,以更好地适应我们的数据;通过这种方式平衡过度拟合的风险(即模型仅学习输入的风险——非常好——并且不能对看不见的数据进行概括)。在指南中,这被称为数学技能和专业知识的要求。此外,同样重要的是要有相关和充分的市场数据。
作为一名 AVM 开发人员,您必须确保数据质量。正如我们所说,坏的数据输入,坏的输出。此外,数据和模型应该定期更新,以反映新的交易并捕捉新的趋势或重要特征。AVM 必须是客观的,符合任何适用的国家法律,开发商必须保持一个开放的第三方审计模型的政策。
指南中涉及的另一个方面是模型性能和模型评估。这表明必须对模型进行测试和验证。因为没有一个模型是完美的,所以必须量化和理解它的误差,以便掌握它的局限性。测试应该复制真实世界的情况,以便结果能够代表这样的场景。事实上,验证模型的数据科学方法是交叉验证。交叉验证是一种在数据子集上训练机器学习模型,并在另一个模型从未见过的子集上进行测试的技术。但是,您可以通过获取不同的数据子集进行训练和测试来重复该过程,而不要使用稍后将被测试的数据来训练模型。换句话说,目标是在一系列样本外数据集中彻底测试模型。这就是所谓的测试集。
该指南还强调了模型旨在预测的一点。在数据科学中,我们称之为目标变量。这不是要价,而是反映在销售价格中的房产实际价值。请注意,这并不总是正确的,因为如果有人需要紧急清算,他们可能不得不出售,但这是评估其价值的最佳方式。
无论我们的目标是预测什么,我们总是需要一种稳健的方法来衡量模型的成功。衡量成功的标准各不相同,但覆盖率是指南中的一个重要概念,它可以捕获在可接受范围内的预测部分。换句话说,这是产生有效结果的案例数除以案例总数的比率。此外,该指南还提到了偏差和离差,这是数据科学和机器学习中的经典偏差和方差权衡。这是关于模型是否总是高估或低估价格,即,它是有偏差的,同时在每个预测与实际价格的距离方面与其预测一致。如果预测总是相差很大,这意味着模型的方差很大。
最后,该指南谈到了估值模型的三种主要技术,其中没有一种利用机器学习本身,而是暗示传统的统计模型。这四种方法是房价指数(HPI)、单参数估值、Hedonic 模型和基于可比的自动估值模型。
实际上,HPI 只是一些指数,记录了某些地区房产的综合统计数据,并不是评估具体房产价值的正确工具。有时 HPI 只是专家的意见,而其他时候则是一段时间内属性的集合。另一种 HPI 方法是一揽子商品法。在一篮子商品方法中,HPI 是根据同一财产在一段时间内的个别价值计算出来的。每个估价的质量仍然取决于篮子中资产的估价质量,并且仍然取决于篮子中有哪些资产。重复销售指数也是一个 HPI,它反映了同一房产在两个或更多时间点的销售价格记录。但是,如果某个特定区域的销量不多,或者销售间隔时间长,会怎么样呢?无论如何,HPI 并没有给出一个房地产的预测,而是给出了一个参考点的潜在变化。
单参数估值是旨在基于单个参数及其地理位置来估计资产价值的模型。对此的概括是享乐模型。
Hedonic 模型是多变量回归模型,其具有作为输入的属性特征,并且从数据中学习参数。这是一种数据科学方法,但它排除了不具有线性或非线性方程形式的模型。
基于可比的 AVM 是选择与某些参考属性最相似的属性的工具。这似乎更像是一种 K-最近邻方法,其中每个属性是 N 维空间中的一个数据点,目的是识别与参考属性最匹配的“K”个其他属性。然而,定义什么是可比的可能是具有挑战性的。它是位置和大小的组合还是所有属性特征的线性组合?那么,在寻找可比资产时,我们要寻找什么样的资产特征?如果没有精确的可比资产,会发生什么?

在数据科学中,我们采用不同的算法和方法来利用我们拥有的历史数据,我们不一定需要选择这四种方法中的一种。
在战略中,当你有四个选择时,你会选择第五个。
这并不是说现有的方法没有用。事实上,它们非常有用,而且可能是相辅相成的。然而,除此之外,还有像随机森林和梯度推进这样的机器学习技术,它们在大多数回归和分类问题中表现良好。有随机非参数过程,捕捉输入和目标变量之间的非线性关系,也有深度学习方法,用于最新的人工智能应用。所有这些都可以捕捉任何单个线性或非线性回归模型都无法捕捉的关系,它们可以为单个属性提供预测,并通过对模型所学习的整个数据集进行概括来解决没有直接可比性的问题。
喜欢这篇文章吗?成为 中等会员 继续无限制学习。如果你使用下面的链接,我会收到你的一部分会员费,不需要你额外付费。
如果您是数据科学的新手,您可能会对以下文章感兴趣:
https://pub.towardsai.net/top-3-books-a-new-data-scientist-should-read-fb87742cafd3
数据科学模板

米利安·耶西耶在 Unsplash 上拍摄的照片
用 6 个简单步骤实践数据科学的模板
实践数据科学
如果我们能够简化实践的过程,发展一项技能会容易得多。对我来说,提高我的数据科学技能需要每周甚至每天练习数据科学工作的不同方面:数据收集、数据清理和预处理、数据可视化、建模等等。
在本帖中,我将用一个实际的例子来分享我实践数据科学的基本模板。
练习周期
简化数据科学技能实践周期的一个好方法是,阐明从数据到洞察力的步骤。我们可以将其视为一个六步流程:
- 查找包含一些问题或挑战的数据集
- 挑一个关于此类数据集的问题 ,可以用基础数据科学来回答
- 清理 数据
- 做 基础分析 在数据上回答你最初的问题
- 以某种标准格式呈现结果 就像一个 jupyter 笔记本
- 重新考虑如果你展示的结果确实回答了你的问题,还有哪些可以改进的地方
实际例子
为了说明这些步骤,让我们挑选一个数据集,并完成前面提到的每个步骤。
1.查找数据集
我在这一步实践数据科学时发现的主要问题实际上是决定使用哪个数据集。我之所以认为这是一个问题,是因为我们要么总是 使用相同的数据集,并随着时间的推移感到厌倦,要么被我们可以使用的潜在数据集的数量 所淹没。
为了避免问题,让我们通过使用 kaggle api 和一个简单的规则来自动化这一部分,以这样一种方式随机化我们对数据集的选择,我们不必花费精力来决定,并且可以更快地进入实际编码。
我遵循 的原则,将所有可以有效自动化的决策自动化。
关键是 要意识到你在实践中花费在决策上的时间和精力 ,看看这些是否可以自动化,而不忽视做出这样的决策所涉及的学习潜力。
现在,我的过程很简单:
- 选择一个与我感兴趣的事物相关的单词。(这次我选择了学习)
- 使用 kaggle api 搜索与该特定单词相关的可用数据集并下载
它不是 100%随机的,但只是足够随机,以至于我没有感觉到会降低我效率的摩擦。使用 kaggle api 从关键字中搜索数据集的命令是:
kaggle datasets list -s learning
输出:
ref title size lastUpdated downloadCount voteCount usabilityRating
kaggle/kaggle-survey-2018 2018 Kaggle Machine Learning & Data Science Survey 4MB 2018-11-03 22:35:07 14892 965 0.85294116
kaggle/kaggle-survey-2017 2017 Kaggle Machine Learning & Data Science Survey 4MB 2017-10-27 22:03:03 22297 821 0.8235294
alopez247/pokemon Pokémon for Data Mining and Machine Learning 715KB 2017-03-05 15:01:26 10102 234 0.85294116
rocki37/open-university-learning-analytics-dataset Open University Learning Analytics Dataset 84MB 2018-10-27 22:30:00 3595 124 0.7058824
rohan0301/unsupervised-learning-on-country-data Unsupervised Learning on Country Data 5KB 2020-06-17 07:45:45 3956 79 0.8235294
.
.
.
.
.
从我得到的选项中,我选择了open-university-learning-analytics-dataset.来下载它,我使用了:
kaggle datasets download rocki37/open-university-learning-analytics-dataset
然后我解压数据并保存到一个名为learning_analytics的文件夹中
unzip -d open-university-learning-analytics-dataset.zip learning_analytics
2.挑一个问题
这一部分比较棘手,因为你可以有一个问题,然后找到一个数据集来回答它,或者你可以有一个数据集,然后在探索它的过程中发现一个问题。
让我们从导入依赖项、加载数据和选择相关表开始。在这里,我已经开始了解一些关于数据和使用哪些表的知识。
现在让我们来看看数据:
df_scores

df_assessments

df_student_info

这个数据集是“开放大学学习分析数据集”,它是一个关于课程和虚拟学习环境数据的匿名数据集。我选择的表是:
- 学生成绩:包含学生在每次评估或考试中的成绩。
- 学生信息:包含学生的信息,例如他们来自哪个地区,他们在参加考试之前学了多少学分,他们是否通过考试等等。
- 评估:关于评估的元数据,如类型、权重和 id。
数据集和每个表的详细描述可以在这里找到。
在查看数据时,我想到了几个问题:
- 学生的平均分是多少?
- 每个地区的平均分是多少?
- 每个评估通过和未通过的分布是什么?
- 为了增加通过考试的机会,提交作品的理想时间是多少?
- 年龄和分数表现有关系吗?
- 我能写一个二进制分类器来预测学生是否会通过给定的关于学生和课程的所有可用信息吗?
- 到那一点为止所学的学分数和学生的表现有关系吗?
如您所见,对于给定的数据集,可以提出许多可能的问题。那么如何开始呢?我应该开始回答所有的问题吗?我应该从最简单的开始吗?
我的规则很简单, 我挑选一个问题或一组问题,最适合我的练习目标和给定一天的可用时间 。
我的意思是,如果我只想做一个快速练习,以保持基本数据分析在我脑海中的新鲜度,我不会选择一个涉及像编写定制神经网络或探索复杂的机器学习模型这样耗时太长的任务的问题。
但是,如果在某一天我想写一些更有趣和复杂的东西,我会选择一个 问题或一组 问题,这些问题形成一个一致的叙述 ,并允许我练习更复杂的建模和分析。这完全取决于你一开始就设定明确的目标!
这里的想法是 挑选一个可以用一个问题 概括的叙述,用更小的问题填充叙述的空隙(就像一段历史的积木),形成一个连贯的结构。
出于本文的目的,让我们保持简单。我的问题会是: 一门课程中负责分数表现的主要因素有哪些?
3.清理数据
现在我选择了我的问题,我可以开始调查数据,找到我关心的答案。为了有效地做到这一点,我想清理数据并做一些基本的预处理。
这里最重要的一点是删除无助于分析的 nan 和列,并将所有列与相关信息合并成一个表。我将使用的助手函数有:
让我们首先找出每个表中有多少个 NaN 条目:
# Output:df_student_info
Column: code_module
Number of NaNs: 0
Column: code_presentation
Number of NaNs: 0
Column: id_student
Number of NaNs: 0
Column: gender
Number of NaNs: 0
Column: region
Number of NaNs: 0
Column: highest_education
Number of NaNs: 0
Column: imd_band
Number of NaNs: 1111
Column: age_band
Number of NaNs: 0
Column: num_of_prev_attempts
Number of NaNs: 0
Column: studied_credits
Number of NaNs: 0
Column: disability
Number of NaNs: 0
Column: final_result
Number of NaNs: 0
***
df_scores
Column: id_assessment
Number of NaNs: 0
Column: id_student
Number of NaNs: 0
Column: date_submitted
Number of NaNs: 0
Column: is_banked
Number of NaNs: 0
Column: score
Number of NaNs: 173
***
df_assessments
Column: code_module
Number of NaNs: 0
Column: code_presentation
Number of NaNs: 0
Column: id_assessment
Number of NaNs: 0
Column: assessment_type
Number of NaNs: 0
Column: date
Number of NaNs: 11
Column: weight
Number of NaNs: 0
***
现在让我们从它们各自的列中删除 NaN 条目,并删除不相关的列:code_presentation和is_banked。
我删除了code_presentation列,因为它指的是给定评估所属演示的简单标识代码,因此理解学生表现中涉及的因素并不重要,类似于is_banked列。
让我们来看看目前为止我们有什么:

作者图片
现在,让我们创建一个名为assessment_type的列,将分数与其各自的评估类别联系起来:
CMA:电脑评卷TMA:导师评卷- 课程的一次或多次考试
我们来看看df_scores:
df_scores

作者图片
很好,现在让我们使用merge()方法将所有内容合并到一个数据帧中,并从score和studied_credits列中删除生成的 NaN 条目:
# Output5768
0
7480
0df_merged

作者图片
最后,让我们将imd_band列改为数字:
4.| 5.基本分析并呈现结果
大家还记得我一开始给自己设的问题: 一门课程中负责分数表现的主要因素有哪些?
鉴于我感兴趣的是与学生分数相关的因素,我们先来看看分数的分布:

现在,让我们来看看不同类型评估的分数:

作者图片
让我们考虑每种评估的平均分数:
# Output:Average score for TMA
72.5633924663878
Average score for CMA
81.02705346888426
Average score for Exam
63.546800382043934
计算机评分评估的平均分数较高,其次是导师评分评估,考试的评分表现最差。
让我们开始挖掘,看看学生学分和分数表现之间的潜在关系。我希望学分越多的学生表现越好。

作者图片
pearsonr(df_merged["score"],df_merged["studied_credits"])# Output(-0.05601315081954174, 1.2536559468075267e-134)
从散点图和简单的皮尔逊相关可以看出,学分数量和分数表现略有负相关,因此没有什么太明显的值得注意,但我们最初的预期与数据不符。
现在,我们来看看提交的天数和性能。我认为过早提交的学生不会做得很好,但是花费太长时间的学生也不会做得很好,最高分在中间。

作者图片
pearsonr(df_merged["date_submitted"],df_merged["score"])# Output(-0.020750337032287382, 6.150386473714662e-20)
我们再次发现一个非常小的负相关,表明最好的成绩不一定是通过花更长时间提交而获得的,反之亦然。
现在让我们来看看通过课程的学生与未通过课程或退出课程的学生的概况。首先,我们创建两个独立的数据帧:
让我们从通过的人数与失败或退出的人数开始比较:
print(f"Number of people that passed the course: {len(df_pass)}")
print(f"Number of people that failed the course: {len(df_fail)}")Number of people that passed the course: 137503
Number of people that failed the course: 56546
我们看到通过这门课程的人比不及格的人多得多。让我们想象一下他们个人资料的不同方面:提交评估所用的时间、教育水平、年龄、学分、地区和imd_band,这衡量了该学生在参加课程时所居住地区的贫困水平(关于这一衡量标准的更多细节可以在这里找到)。
让我们从使用百分比分数来比较性能分布开始。

作者图片
不出所料,不及格的人的分数分布在 60-70 之间出现峰值,而及格的人的分数为 100。
现在让我们看看这两个组的提交时间概况。我们在这里的期望是,失败的学生可能会更快地提交,因为我们之前看到提交的天数和成绩之间有轻微的负相关。

事实上,这就是我们在这里看到的情况,不及格或退学的学生提交评估的速度要快得多,我们可以清楚地看到这两种分布的巨大差异,不及格学生的峰值向左倾斜,大约在 0 到 100 天,及格学生的峰值在 100 到 200 天。让我们通过打印两组的平均提交天数来确认。
# OutputTtest_indResult(statistic=91.87803726695681, pvalue=0.0)
显著的 30 天差异。酷,现在让我们看看教育水平。

我们在这里观察到的最大差异是,与通过课程的学生组(33%)相比,未通过课程的学生组(42.8%)中教育程度低于 A 级的学生比例更高。
现在让我们看看可能的年龄差异:

作者图片
在年龄部门,两个组似乎有相似的概况,失败组有更多 0 至 35 岁的年轻人,而通过组有更多 35 至 55 岁的老年人。
现在让我们看看地理信息——学生上课时居住的地区。

作者图片
在这里,当我们看地区之间的差异时,我们看到一些小的差异,南部地区多 1.6%的人表现较好,西北部地区多 1.7%的人表现最差。
最后,让我们来看一下imd_band,它本质上是对小地区贫困程度的一种衡量,在英国被广泛使用。根据社区和地方政府部的报告,该指数反映了最贫困地区占最不贫困地区的百分比,因此随着数字的增加,这意味着该地区的贫困程度降低,因此 10%的最贫困地区和 90-100%的最不贫困地区。

作者图片
在这里,我们看到最大的差异在 0-10%之间,反映了最贫穷的地区,与及格的人相比,来自英国最贫穷地区的不及格的人要多 4.1%。
与 10-20%组类似,失败的人中有 11.7%来自英国第二贫困地区,而通过的人中有 9.4%。
另一个有趣的方面是,在光谱的另一端,在不太贫困的地区,这些百分比发生了变化,我们看到 10%通过考试的人来自该国不太贫困的地区,而在失败组中这一比例为 7.3%。第二个不太贫穷的地区也是如此,我们看到 10.2%的人通过了考试,而 8.2%的人没有通过考试。
对我来说,这似乎指出了我最初关于分数表现的因素的问题的潜在最佳答案,因此,为了尝试了解这些明显的结果是否实际上有一些可能的影响,让我们看看分数表现和贫困之间的关系。
pearsonr(df_merged["imd_band"],df_merged["score"])(0.08166717850681035, 2.3543308016802156e-284)
在整个学生群体中,我们看到成绩表现和贫困程度之间有 8%的正相关。
让我们分别看看通过和失败案例的这种相关性:
pearsonr(df_pass["imd_band"],df_pass["score"])(0.0642002991822996, 1.6041785827156085e-125)pearsonr(df_fail["imd_band"],df_fail["score"])(0.052778778980020466, 3.5499376419846515e-36)
同样是弱相关性,为了进一步研究这一点,让我们看一下imd_band排名的每个指数值的平均得分表现图,以了解趋势的概况。

作者图片
我们可以观察到,如果我们平均每个指数组的得分,随着地区条件的改善,得分表现有一个非常明显的上升趋势,换句话说,似乎 越富裕的地区得分越高。
当我们观察两组时,我们看到了相似的趋势,但有趣的是,与通过组相比,失败组在 20–70%区间内的波动性更大,在 70–80%区间内的性能跳跃更大。
为了进一步研究这一点,让我们对 imd 排名的总体得分表现拟合一条回归线。

print(f"Slope of the Regression Line: {coef}")# Output
Slope of the Regression Line: [[0.05571216]]
为了确认这些结果,让我们使用statsmodels api 来获得回归测试的 p 值:

作者图片
我们观察到,我们得到了一个很好的拟合,剥夺指数每增加一个点,我们就会看到得分表现增加约 0.055,我们还得到了一个 p 值为< 0.05.
Since we are fitting the regression line to the means of scores we lose information on the variability of the scores across imd ranks but for the purposes of this analysis this is enough evidence to at least suspect that the index of deprivation might be an important factor influencing the performance of the students.
We started with a question and attempted to build a narrative around it, which in this case was to try to understand the factors involved in the scoring performance.
Upon exploring the data we saw that one factor that might play a significant role was the rank of poverty in the area where the students lived while taking the course, and we showed that there was a positive correlation between mean score performance in the course and the index of multiple deprivation in the region, with the least deprived regions performing better than the most deprived.
6. Reconsider
Before wrapping up a data science practice session 的显著 t 检验。我喜欢重新考虑我所做的 ,问自己我使用的统计论据或模型是否确实基于数据并反映了相关信息。
在这个数据集的情况下,我认为他们是为了一个非正式的实践会议,虽然如果这是为了研究或专业报告,在巩固我所做的陈述的论点方面将有更多的工作要做。可以重新考虑的因素:
- 针对整个得分和 imd 等级值绘制一条回归线,以获得贫困和得分表现之间关系可变性的信息
- 调查关于合格组和不合格组之间的统计差异的更详细信息
- 运行随机森林和 xgboost 等机器学习模型,根据可用信息减去实际分数表现,将学生分为及格或不及格
数据科学实践的最后一点对于培养必要的数据分析和统计批判性思维技能至关重要。
有这篇文章源代码的笔记本可以在这里找到。
关于实践的思考
和所有事情一样,如果你练习,你会变得更好。大多数人的问题是他们每天实际练习这些技能的时间,因为像这样的笔记本可能需要几个小时才能完成。
然而,我要说的是,一个好的策略是 练习这个模板,并使用关于为您的数据制定问题的步骤来缩小您的分析范围,以适应您可用的时间段 ,因此,如果您只有 30 分钟,您可以问一个非常简单的问题,并快速回答它,即使论点不会尽如人意。
最重要的是练习收集数据、探索、清理、运行分析的流水线,以讲述历史的方式呈现,即使是非常简单的历史。
如果你喜欢这篇文章,请在 Twitter 、 LinkedIn 上联系我,并在 Medium 上关注我。谢谢,下次再见!😃
参考
数据科学家理解全球经济的方法
全球经济实际上是什么样子的?我使用数据科学的工具来理解和可视化它。
有时我想,如果我关注新闻,我会对大多数事情形成一种有资格的看法。但是新闻报道,嗯,他们报道新闻——改变到不太遥远的过去。这就像透过飞驰汽车的挡风玻璃看世界一样——这与大局无关。此外,我经常有这样的印象,在当前的新闻形势下,有时很难区分观点和事实。所以我想,我应该深入全球经济,看看我是否能从数据中推断出一个基本的理解,而不是一个专家。
为了帮助我的探索,我使用了 python 包 Pymrio ,它可以用来讨论全球经济的投入产出表,还可以计算足迹。投入产出表包含了货币在经济中如何流动的信息。经济中的最终需求推动企业生产商品和服务。企业依靠其他企业的商品和服务来生产自己的商品和服务。这就是所谓的中间需求,而不是最终需求。世界上的货币数量不是恒定的,因此这些表格也代表了经济增加值。部门、消费者和附加值之间的资本流动是通过投入产出表获得的。
世界上大多数国家都有投入产出表。但是他们用的格式不一样,有的不是每年都出。幸运的是,一些研究人员将它们汇编成数据集,以便研究全球经济和计算各种类型的足迹。Pymrio 是一个包,允许用户与这些输入输出表进行交互,或者创建自己的输入输出表。我使用了 EORA26[1,2]数据集,其中包含了 189 个地区从 1990 年到 2015 年的投入产出表,共有 26 个部门。这是一个完整的 EORA 数据库的压缩版本,其中的部门没有被汇总成共同的部门。
Pymrio 允许以不同的方式聚集输入输出表。例如,假设您想将欧盟视为一个单一的地区。然后,Pymrio 可以将相关区域的数据与公共扇区合并成一个区域,而不必担心索引问题。对这些部门也可以这样做。想象一下,你想比较国家,但不关心部门的细节。Pymrio 可以帮助您聚合扇区。
我想了解资金是如何跨境流动的——哪些国家联系紧密,哪些国家联系不紧密?我问自己:全球经济可以从几个相互交易的经济体集群的角度来理解吗?这些部门在全球范围内是如何联系在一起的?
我将所有部门汇总为一个部门,并比较了中间需求中的资金流动。我引入了一个距离度量 1/ T_ij ,其中 T_ij 是中间需求中从区域 i 到区域 j 的货币投入的平均值,反之亦然。通过这种距离度量,我们可以在地图上将每个地区表示为一个点,并在合理的范围内将这些点放置在与两个地区的贸易范围相对应的距离处。我为此使用了多维标度。该地图可以在下面的图 1 中看到。

图一。用 MDS 直接从距离矩阵计算点的位置。利用来自具有完全链接的距离矩阵的分级聚类来执行聚类。图片作者。
由于地图只是从距离上构建的,所以北、南、东、西的概念没有意义。因此,地图可以任意旋转。这张地图看起来像泼溅的墨水。中间区域的密度很高,并且有一些异常值。因此,作为一个整体,全球经济似乎与一些地区保持着良好的联系。
但是图像可能会欺骗我们。所以我尝试了几种不同的聚类方法,发现具有平均和完全链接的层次聚类似乎可以获得一致和合理的聚类,尽管我无法提出一个好的质量度量(如果你有好主意,请告诉我)。
我发现,总体而言,全球经济中有一些非常孤立的异常者。这些在图 1 中用橙色和红色的点表示。然后是点的外部区域,这些点彼此之间没有很好地连接,也不是地图的中心。这些松散连接的点在地图上显示为深蓝色的点。整个经济围绕着一个紧密相连的核心国家,在地图上以青色显示。很难理解图中所有的 189 个点。所以我也把集群放在世界地图上,如图 2 所示。

图二。聚类结果显示在世界地图上(用 Plotly 制作)。图片作者。
我们可以看到异常值是索马里、摩尔达维亚、缅甸、苏丹和南苏丹(橙色)。世界的其余部分或多或少被分成青色和蓝色。松散联系的国家可以在非洲、中东、中美洲和中东的一些地方找到。世界其他地方属于人脉很广的核心。
好了,我们知道了,世界或多或少是一分为二的。那些相互之间有大量交易的国家和那些没有交易的国家。但是在那些交易者中,谁在交易呢?为了找到答案,我使用 NetworkX 将中间需求转换为图表的形式。图由通过边连接的节点组成。我设计了这个图,使得每个节点代表一个区域,并且通过一条向内和向外的边连接到每个其他节点。边缘的方向和权重对应于货币在中间需求的区域之间流动的方向和大小。我的图表是动态的,从某种意义上说,边缘随着时间(年)的变化而变化。NetworkX 不支持这一点,所以我不得不写一些代码来创建一个可以用 Gephi 可视化的动态图形。
图 3 显示了对应于 2014 年的部分图表的屏幕截图。使用弹簧模型(ForceAtlas2)计算节点的位置。每条边被模拟为弹簧,其刚度与边的重量成比例。此外,节点被建模为相同符号的电荷,使得靠近的节点比远离的节点更相互排斥。系统用随机位置初始化,并被允许松弛直到节点不移动。因此,节点之间的相对距离指示了它们连接的强度,但是同样,系统是旋转不变的。节点的大小和颜色对应于资金的总流入和流出。

图 3。2014 年中间需求图的可视化,使用 ForceAtlas 2 通过 Gephi 可视化。图片作者。
我们可以看到,全球经济中似乎有三个主要的贸易中心:美国、德国(DEU)和中国。德国显然是欧盟其他国家的贸易中心。我的祖国丹麦在模型中靠近挪威、瑞典和芬兰,就像它们在地图上一样,但是瑞典似乎比丹麦更靠近德国,尽管它与德国没有共同的边界。
加拿大和美国之间似乎发生了很多贸易,美国似乎向加拿大输送了更多的资金(存在贸易逆差)。美国似乎也对中国存在贸易逆差。看看上届美国政府领导下的贸易战如何改变这种模式将是一件有趣的事情,但不幸的是,这些数据不是最新的。美国和中国与亚洲国家的联系似乎比欧盟更紧密。总的来说,与中国的贸易似乎是这段时间内全球经济中最大的一次质的变化。总的来说,我从图表中了解到,虽然贸易是全球性的,但它也是地方性的。少数几个大型经济体主导着全球贸易,它们最密切的贸易伙伴要么是其他大型经济体,要么是它们的近邻。
这种可视化从地区的角度来看。我们也可以把视角换成部门的视角。这可能让我们能够识别供应链,并大致了解全球范围内资金是如何在各个部门之间流动的。下图 4 显示了所有国家汇总的部门的相应图表。

图 4。2014 年各地区行业中间需求图可视化。图片作者。
同样,节点的大小和颜色表明了这些部门之间的贸易联系有多紧密,而不是这些部门的产出。看这张图,我们可以很容易地识别供应链。举个例子,连锁店“旅馆和餐馆”->“食品和饮料”->“农业”。资金流向很清楚,产品流向也很明显。另一个类似的链条是“建筑业”->“石油、化学和非金属矿物制品”->“采矿和采石业”。有趣的是,所有行业都与“金融中介和商业活动”有着相当密切的联系。这表明所有部门都需要获得资本。因此,这个部门是所有部门中最大的资金分发者。
我觉得有趣的是,所有部门是如何相互依赖的。如果你仔细观察,你会发现许多供应链是分等级的,但大多数是分支的,少数看起来是环状的。作为一名消费者,如果你购买产品,几乎不可能预料到你的消费习惯会如何扰乱这个系统,但事实上,是最终需求总量塑造了并将继续塑造这个行业。
本帖从一个数据的角度关注全球经济。但实际上我更专注于理解气候危机。气候危机的一些解决方案实际上可能来自于对最终需求如何影响全球贸易的理解。这是因为,系统对最终需求微小变化的反应,可以通过用不同部门的排放信息和一点点线性代数来扩展表格来评估。在我的下一篇文章中会有更多的内容:)
有兴趣可以在这里 找到代码 。这是我的第一个帖子。我希望你喜欢它。欢迎留言联系。请注意,我不是经济学家,所以我不知道我的天真分析如何符合专家的意见。
[1] Lenzen M,Kane moto K;Moran D 和 Geschke A (2012) 绘制了世界经济的结构。环境科学&技术46(15)PP 8374–8381。DOI:10.1021/es 300171 x
【2】Lenzen,m .,Moran,d .,Kanemoto,k .,Geschke,A. (2013) 建立 Eora:高国家和部门分辨率的全球多区域投入产出数据库。经济系统研究,25:1,20–49,DOI:10.1080/09535314 . 2013 . 769938
数据科学家的清单
“清单宣言”在数据科学中的应用

伊利亚·巴甫洛夫在 Unsplash 上的照片
阿图尔·加万德(Atul Gawande)是一名美国外科医生,他的四本畅销书《并发症》(Complications)、《更好》(Better)、《身为凡人:医学和最终的意义》(Being veral:Medicine and What Matters in The End)和《清单宣言》(The Checklist Manifesto)获得了麦克阿瑟奖学金(MacArthur Fellowship grant),以及许多其他荣誉,如果你想了解更多,可以阅读《T4 纽约客》(New Yorker)上的他的简历。
虽然他确实是一名外科医生,他的畅销书中有三本是关于医学的,但他在 2009 年写了他的书《清单宣言》,其中他描述了清单的用处以及为什么在各种复杂的情况下清单是有益的。他提供的例子跨越了建筑、航空和医疗保健三个主要领域,其中他认为,在这三个领域中的每一个领域中,清单的使用都导致了管理复杂性和改善建筑项目、飞行或手术结果的各种改进。这些工作通过确保任何任务中涉及的简单/关键步骤被清楚地列出来,以便它们不会被遗漏或误解。外科手术中的一个示例项目是简单地与房间中的所有医务人员确认正在进行正确的手术并且是在正确的身体部位上,这种情况发生的频率会让你感到惊讶,而使用清单消除了这种潜在的错误。然而,这些清单更广泛的适用性是显而易见的,因为它们可以被视为改善任何复杂情况/工作的结果,数据科学很容易适应。
在这些情况下,清单的好处主要来自两个方面:改善团队合作和确保简单的过程被例行执行。在第一种情况下,清单用于确保 a)团队都在同一页面上,并且知道正在发生什么和预期会发生什么,b)团队正在尽可能地沟通和工作。这是通过确保团队了解彼此和他们的工作,甚至通过简单的介绍,并且所有潜在的问题或复杂情况都提前向整个团队强调。在第二种情况下,它通过明确规定所有关键步骤,并赋予个人权力以确保正确执行这些步骤,从而确保这些步骤得以执行。
清单本身必须简短,这样在使用时才不会成为负担,而且要涵盖必须执行的主要任务。这包括可能被遗忘的关键任务和/或问题、导致问题或成功完成任务的基本要求。这些任务可能被广泛接受或被认为是流程的关键,但在现实世界中,由于手头任务固有的复杂性,注意力被转移,可能会被遗忘或错过。这些清单不一定是一成不变的,而是可以不断重复改进,并可以适应特定的情况或环境。

J. Kelly Brito 在 Unsplash 上的照片
那么这和数据科学有什么关系呢?嗯,我们在高度复杂的行业中工作,执行经常会被忽略的常规基本步骤,并且经常在高压环境中工作或有严格的期限。这意味着,虽然我们可能不承认,但在某些情况下,我们的工作可以通过确保我们有一个清单来确保我们确实涵盖了这些基本内容,并且没有错过关键步骤来满足截止日期而得到极大的改进。这可以在不同的阶段出现,以确保简单的事情被完成,这样我们就可以专注于我们每天处理的更复杂的任务,以改善我们的整体分析和结果。这种清单可能出现在数据科学项目的三个关键暂停点:启动、分析和评估。
开始
第一份清单将在项目开始时出现。确切的时间将取决于你如何定义项目的开始,因为它可能是在简报写好的时候,甚至是在这之前。这将涵盖一些最基本的任务,通过设置项目开始所需的环境来确保项目能够顺利进行。这很可能是 Read-Do 格式,因为在进行任何分析之前,它可以像菜谱一样被遵循。这将采用以下格式:
1.设置项目目录(即基本文件夹、数据文件夹、代码文件夹和输出文件夹)
2.摄取并存储数据(无论是在 csv 文件、shapefiles 还是在数据库中)
3.创建环境,安装可能需要的所有软件包、技术和依赖项(即虚拟环境、docker 等。)
但是对于项目中使用的预期技术堆栈来说是高度可定制的)。这只是一个清单,对于使用相同技术堆栈的团队来说是例行公事,但是它可以根据特定的项目而变化。这将意味着,当技术结合在一起时,不会出现进一步的问题。
分析
第二阶段将在进行分析之前进行,可能也是 Read-Do 格式,取决于可能进行的分析类型,即回归、聚类或时间序列分析。很可能这些任务中的每一项都有自己特定的要执行的清单,但是可以观察到一个通用的格式:
1.以最合适的格式显示数据(散点图、直方图、条形图等)。)
2.选择要进行的分析类型(回归、聚类、神经网络)
3.执行各种清洁/准备(标准化、转化、正常化)
4.创建测试和训练数据集
5.执行分析(回归、分类、聚类)
6.检查以确保满足分析的假设
7.调整模型(特征重要性、超参数)
在回归的情况下,这可能是:可视化数据,了解目标变量的分布以选择回归格式(线性、泊松、负二项式),变量标准化,创建测试和训练集,执行分析,检查以确保满足线条件,移除任何不重要的变量。在任何阶段,这些过程中的每一个都可能被错过,但是清单确保如果任何阶段确实需要被跳过,即可变准备阶段,在之前或之后给出这样做的原因,以便稍后可以理解为什么这样做。
完成
最后,一旦项目完成,你有了可交付成果或最终产出,在你把成果交给客户、发表你的发现或传播你的产品之前,很可能会有一个最终清单。与其他两个不同的是,这将采用 Do-Confirm 格式,即任务已经完成,您只需确认它们已经完成,或者确认在这种特定情况下它们不需要完成。这将包括:
- 已计算评估最终指标
- 对模型的稳健性进行了检验
- 该流程已记录在案
- 代码是可复制的格式
- 最终交付物的格式是客户想要的格式
这些清单可以根据您正在进行的特定环境或项目进行调整,但无论以何种形式使用,都意味着简单而关键的步骤肯定会完成,从而腾出更多时间来关注更复杂、更具创造性的问题,从而改善整体结果。
如果在这一点上你不相信,我会建议两件事。首先,读一读阿图尔·加万德的书,这是一本写得非常好的简单快速的读物。其次,试着想想所有项目中需要完成的关键任务,在某些情况下,这些任务没有完成,甚至根本没有完成,做一个清单,看看下次有多少任务被遗漏或做错了,或者更好的是,在整个项目中参考它,看看它为你节省了多少时间和精力,而你下次不必考虑它们了!
[## scikit-learn 决策树分类器简介
towardsdatascience.com](/introduction-to-decision-tree-classifiers-from-scikit-learn-32cd5d23f4d)
数据科学家的梦想:Python、大数据、多处理和 PyCaret
创建和部署大数据解决方案
借助 Python 的多处理模块和 PyCaret,使用所有内核训练多个模型

图片来源:杰森·袁,Unsplash
让我简单地说:如果你在数据分析或数据科学领域,处理大数据的能力已经成为一种绝对的需求。在这篇文章中,我们将学习设计一个解决方案,您可以简单地使用您的笔记本电脑/台式机创建。不需要花哨的云解决方案!
今天,我们将学习以下内容:
1️⃣什么是 Map Reduce
2️⃣什么是 Python 的多处理模块
3️⃣如何训练&并行预测多个模型
📓注意:有多种工具和解决方案可用于处理大数据。这里的目标是理解基础知识,然后继续学习更复杂的工具。我还假设读者具有数据科学的基础知识, PyCaret (一个开源的低代码库),并且能够用 Python 编码。
1️⃣什么是地图简化?
当我们想到要处理大量数据时,我们脑海中自然会浮现出什么?一个显而易见的解决方案是获得一个更强大的机器。这个想法被称为“垂直缩放”你有一台机器,但有更多的权力。然而,这被证明不是一个非常实用的解决方案。另一种方法是“横向扩展”,将多个小型机器放在一起,我们希望使用它们的组合资源来解决我们的问题。为了实现这一点,我们首先需要在机器/计算机之间建立一个网络。你现在不需要担心创建这个网络。然而,在当今世界,创建和维护这样的网络要容易得多,也便宜得多,尤其是有了所有可用的云解决方案。这真的不难。

作者图片
正如我上面提到的,创建网络(虽然很容易)超出了本文的范围,我们仍然可以用自己的个人台式机或笔记本电脑学习和应用这个概念。只要我们有不止一个内核,我们就可以像在网络中一样使用计算机中的内核。毕竟,你自己的电脑是一个迷你的水平缩放解决方案;实际上是一个网络或多个核心!
一旦我们有了合适的网络(横向扩展),我们所需要的就是 将 我们的数据分发给这些机器,使用这些资源处理我们的数据,最后 收集 结果回来。这种分配相当于“映射”,收集相当于“减少”。那是你的地图-缩小!所有著名的大数据解决方案实际上都基于这一概念。如果你问我,“地图”是最难的部分。在这种情况下,必须设计一种策略/逻辑来将数据分发到整个网络,以便您可以解决手头的问题。有些问题很容易解决,有些更难,有些甚至不可能。
让我们用一个非常简单的例子来理解这一点。假设我们有一个大小为 16 的数组/数字列表。我们想计算它的平均值。在正常情况下,我们会做这样的事情:
mean = np.mean([8,1,4,3,2,5,1,2,2,9,7,6,2,4,2,1])
结果将是3.6875。现在想象一下,由于某种原因,我们的计算机没有足够的能力在 one go 中计算它。那么,你能想出一种方法将这个数组分配/映射到所有的内核,然后求解平均值吗?
很简单。我们知道“均值与平均值相同”(只要样本量保持不变)。所以我们可以把我们的数组分成大小相等的更小的数组,发给不同的核,让每个核计算更小数组的均值,返回均值数组,然后计算均值数组的均值!我们刚刚实现了 map(打破&阵列到内核的分布)reduce(将阵列缩小到一个数字并取回它)。

作者图片
在步骤 1 中,我们将数组分成数组子集,并将它们发送到不同的内核。在步骤 2 中,每个内核计算(📓注意:所有内核同时执行计算&不像循环方式!)它得到的子集的平均值。我们将这些结果保存到另一个数组中,该数组现在的大小为 4。在步骤 3 中,我们要求任何一个内核对这个数组执行另一个均值计算。我们返回最终平均值来捕捉/显示结果。(📓注意:如果我们有一个虚拟的计算机网络,第三步会更复杂,因为结果会从每台计算机上收集并提交回主计算机。).这是 map-reduce 的一个小例子。
现在,您可以将此示例推广到一些实时大数据问题。考虑对一个巨大的数据集应用一些聚合函数(也称为分组操作)。如果没有横向集成/ map-reduce,它的执行速度会非常慢。我们可以执行相同的聚合功能,方法是截断数据,然后通过一些逻辑来获得我们想要的东西。那会快得多。
另一个用例是您需要在一个“循环”中执行一些操作,例如,假设您有数千个单独的 CSV 文件,您想将其转换为任何其他文件格式,比如 XLSX。或者你必须为你有数据的每个客户训练单独的模型。使用垂直集成,您将不得不一个接一个地循环运行所有的东西,并且您可以想象执行整个任务将花费的时间。另一方面,如果您应用 map-reduce,您将能够“并行化”操作,并且过程的执行将会快得多!事实上,在我们的例子中,我们将解决和实现完全相同的问题。
📓注意:可能会有数据大小的问题,也就是说,数据太大了,以至于一台机器的内存都装不下。在这种情况下,你需要一个计算机网络,而不是一台机器。该解决方案超出了本文的范围。尽管如此,仍将涉及 map-reduce。
2️⃣什么是 Python 的多重处理模块?
内置的多处理模块/库帮助我们实现我们刚刚讨论的 map-reduce 策略。就这么简单!通过这个库,我们将指导 python 运行独立的并行过程,这将节省我们大量的时间。多重处理库是一个很好的资源,但是后来,又增加了一个名为“并发”的库。这是建立在多重处理库之上的,只是使事情处理起来更简单。尽管我会推荐检查多处理库这里,为了简单起见,我将使用“并发”库。要使用这些库,我们需要理解几个要点。
👉首先是我们将要运行的代码的结构。我们将使用并发模块中的map()函数,我们将为该函数提供两个参数:
1️⃣目标函数:我们希望对我们的数据处理执行的函数。在我们前面的例子中,你可以把它想象成numpy.mean()。
2️⃣迭代器对象:一个类似列表/数组的对象,包含唯一值,通过它可以索引数据。您可以将其视为数据的切片器/分割器。在我们之前的例子中,它是一个列表[1,2,3,4],我们通过它将数据分成 4 个相等的块。
map函数会将迭代器的目标函数和单元素分配给每个内核。由于我们的原始数据位于全局名称空间中(这意味着所有内核都可以随时访问它),我们可以将数据切片(使用单个元素)并在每个内核中对其应用目标函数。向所有内核分配功能和元素是由并发模块自动管理的。
👉我们需要了解的第二件事是输入(或正在使用的数据)、输出(我们想要的结果)的处理,以及在这个过程中我们可能需要使用的所有变量。在没有多重处理的情况下,我们一直使用这种东西,没有任何问题。有了多重处理,事情就不一样了。用目标函数处理数据片的每个核不能真正与其他核共享任何信息/进程片。这个限制的影响是我们不能“容易地”更新变量/输出的值。幸运的是,有一些可用的解决方法。多重处理模块提供了一个“共享”字典,我们可以使用它来收集每个内核工作时的更新信息。或者(如果您的流程允许),我们可以创建一个空的 CSV 文件,然后将从每个内核获得的结果附加到该文件中。请注意,只有当您将此解决方案部署到一台机器上时,此方法才有效。如果你有一个计算机网络,这一招就不管用了。然而,这种方法为我们提供了更多的灵活性。
👉我们需要知道的是这个代码结构不能在 Jupyter 笔记本上运行。我们将需要在 python 模块中编写完整的代码,这仅仅是在一个. py 文件中编写 python 脚本,而不是在一个. ipynb 文件中。
在我们看一个例子之前,我制作的一个短片可以帮助我们理解这个结构:
作者提供的视频
一个简单的例子:
让我们做一个简单的项目,应用我上面解释的概念。假设我有五个不同商店的销售数据。我想计算每个商店的日销售额的平均值。这就好像我想用 mean 函数应用 group by 子句一样。只是这一次,我们将使用并发模块来获得相同的结果。
让我们创建数据集。我们的数据集将包含五个商店,每个商店将有五个每日销售点。在您这边,您可以根据需要增加数据量和商店数量:

作者图片
如果您打印数据集,您将看到类似这样的内容:

作者图片
我们希望转换这些数据,以获得每个商店的日销售额的平均值,最终结果应该是这样的:

下一步是创建实现这种转换的函数。从概念上讲,这是最难的部分,你想要的转换越高级,函数就越难。在我们的例子中,这是一个简单的意思。这个函数应该被构造成好像你正在做一个“for 循环”的和步骤。此外,如上所述,我们需要获得共享字典和 CSV 占位符。

作者图片
我们所需要的就是通过调用map函数,通过我们拥有的并发模块来“执行”这个函数。map该函数将接受目标函数和一个 iterable(在我们的例子中是 list ),它可以使用这个 iterable 将数据和函数切片并分发给所有的内核。对于我们的例子,它就是我们拥有的商店的唯一列表。我们还可以计算运行并行过程所需的时间。

记住,我们需要在一个 python 模块中运行所有这些代码片段,然后通过命令行运行该模块。您可以在自己的终端上运行的完整代码如下:
在终端中运行该文件后,您应该会看到以下输出:

作者图片
我们还创建了一个占位符 CSV 文件,从运行该模块的目录中读取并查看它。您应该得到这样的结果:

就是这样。您成功运行了多处理作业并获得了结果。出于实验目的,您可以增加商店的数量和每日销售额。
⚠️ 警告:不幸的是,对于 Windows 用户来说,上述过程不会按预期运行。但是我们可以通过一些简单的方法达到同样的效果。首先是改变代码的结构。共享字典和占位符 CSV 文件不会为我们存储任何东西。为了取回结果,我们需要从目标函数返回期望的对象(而且你可以返回任何东西!浮点、数组、字典或任何其他对象)。当我们运行 executer 时,它会返回给我们一个生成器,我们所需要的就是用一个循环或列表理解来运行它。以下是您需要进行的更改:

作者图片
第二种方式,也是我最喜欢的(因为它的灵活性)是简单地使用 Window 的 Linux 子系统(WSL)。这是在你的 Windows 上安装 Linux 的一个非常方便和简单的方法,而且不会造成混乱。我有一篇关于它的单独文章,你可以在这里 访问它 。在本文的其余部分,我将假设您已经使用了 Lunix /Mac 系统。
如果您已经理解了这里的所有内容,那么您应该能够以并行的方式设计和运行您自己的解决方案。你可以简单地停下来,开始建造你自己的。然而,本文继续到最后一部分,我们处理一个涉及并发期货和 PyCaret 的真实例子。
3️⃣并行训练和预测多个模型
我们有一家拥有许多商店的零售企业的数据。每个商店都有每日销售记录。在现实世界中,商店里会有许多商品,但是为了简单起见,让我们假设只有一件商品要卖。我们的工作是预测每个商店的销售额。
我准备了一些虚拟数据,你可以通过库访问。在训练数据中,我有 20 个商店的销售,其中每个商店有 30 个每日销售记录。让我们看看数据:

作者图片

作者图片
我们需要预测“sales_qty ”,并可以使用所有其他列作为“特性”。数据是干净的,没有丢失的值。在测试数据集中,我们必须预测“sales_qty”列。下面是测试数据的样子:

作者图片
📓:你可以从我的仓库下载数据和 python 模块。
在通常情况下,我们将简单地按商店进行循环,为每个商店训练单独的模型&保存训练好的模型供以后使用。但我们不想做循环,因为大量的商店和大量的日销售记录可能会让可怜的循环变得太多。我们希望以并行/并发的方式完成这项工作。
🔆策略:我们的策略很简单,和以前一样。我们将制作一个训练函数(目标函数),并制作一个 iterable(商店的唯一列表)。训练功能将包含您想要为单个商店完全训练模型所需的一切。然后,我们将训练好的模型保存在共享字典中,并将预测结果动态保存在 CSV 占位符文件中。我假设您熟悉 PyCaret,我不需要解释它的功能,如果不熟悉,您可以从这里开始学习https://pycaret.org/。这很容易!
下面是可以实现这一目的的代码。它只不过是我们在前面的例子中已经看到的内容,而且我已经用注释对其进行了详细说明,以便于您阅读。
当您在 Linux / Mac 终端中运行这段代码时,您可以检查所有内核的状态,看看这段代码是否使它们变得繁忙。在您的终端中键入htop,您应该会看到类似这样的内容:

图片作者:所有 8 个核心都在忙着训练模型
一旦过程结束,它应该给你完成培训的时间。在我这边,我花了 70 秒训练 20 家商店的模型,进行了 10 次交叉验证,同时从 20 多个模型中选择每一个模型。真快!
最后应该有“predictions_place_holder.csv”文件,它应该是这样的:

作者图片
您还应该能够阅读共享字典,我们将它作为 pickle 文件保存到我们的工作目录中。如果加载 pickle 文件:
with open("saved_models.p","rb") as fp:models = pickle.load(fp)
并检查它,你会注意到,我们有一个训练有素的渠道,每一个商店,其中商店编号是关键。我们可以通过索引model[1092]来访问它,你应该得到这样的结果:

作者图片
这是一个 scikit-learn 学习管道对象,最终包含转换器和估计器(参见“trained_model”),这意味着您可以随时使用点预测方法model[1092].predict()来获得商店 1092 的预测!
这就把我们带到了文章的结尾。我们学习了 map-reduce,Python 的多处理模块及其工作原理,最后我们如何使用并发模块通过 PyCaret 并行训练和保存多个模型。这只是一个开始,随着经验的积累,您可以对处理进行微调,对函数进行因式分解,并深入多处理模块的细节。然而,希望您现在能够更好地处理一些大数据解决方案。我将感谢您的评论、想法和见解!
➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖
你可以在 上关注我 &上联系我 LinkedIn &访问我的github➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖➖
您可能还对以下内容感兴趣:
👉Docker
让您的数据科学生活变得简单👉使用 PyCaret 的自定义估计器,第 1 部分
👉使用 PyCaret 的自定义估算器,第 2 部分
👉轻松获得 Windows 内部的 Linux 系统
数据科学家购买葡萄酒指南
分离出让葡萄酒好喝的化学物质。

戴夫·拉斯托夫斯基在 Unsplash 上的照片
我们不需要了解葡萄酒,我们只需要做一个模型。
T 我喜欢编码的一点是,它让我们可以用数学的方法来研究任何事物,甚至探索有史以来最浪漫的物质之一:酒。
对我来说,这一点尤为重要。我在安海斯-布希公司工作,这是世界上最大的酒精生产商之一。具体来说,我负责开发一款应用程序,我们的销售代表用它来预测哪些品牌可能会在不同的行业卖得最好,比如 CVS、沃尔玛或当地酒吧。
也就是说,我对数据驱动的饮料推荐并不陌生。
但是不要被愚弄了:这并没有使我的调色板比一般人的好。我是开发商,不是酿酒师或侍酒师。虽然他们可以根据可感知的味道提出建议,但我们将把我们的分析限制在葡萄酒的数字特征上。
在这个演示中,我们将使用 Python 来搜索与高品质葡萄酒相关的因素。最后,你将会了解是什么让一瓶酒变得与众不同,以及这些因素是否因其颜色而不同。
我们的数据集中有什么?
2009 年,葡萄牙的科学家收集了大量不同种类的佛得角葡萄酒库存。作为给社区的一份特殊礼物,他们公开了他们的数据集。
它包含 5000 多种葡萄酒的数据,并测量 11 种不同的理化性质。

这些化学物质对消费者到底意味着什么?
固定酸度:赋予葡萄酒酸味。如果太少,葡萄酒尝起来会很“平淡”。
柠檬酸:常用作风味添加剂。增加了新鲜的酸味。
挥发性酸度:与液态柠檬酸不同,挥发性酸度是气态的,闻起来像醋。它的出现不太可能是有意的。
残糖:这是葡萄完成发酵后剩下的糖分。与甜味相关。“干葡萄酒”的含量往往较低。
硫磺:防止细菌生长的添加剂。在我的研究中,有争议的是是否有气味或味道与之相关。
氯化物:盐的量度。
看起来这个数据集中有红色和白色的瓶子。分布均匀吗?


红葡萄酒和白葡萄酒在各质量等级中的分布。
还好我们检查过了!
由此,我们可以看到我们的数据不平衡。对于这个演示,我们只是可视化静态数据,所以虽然过度表示值得注意,但这并不是世界末日。
但如果我们试图用这些数据训练一个机器学习模型,这将是有问题的。为什么?因为某些类别代表过多或不足,所以除非我们进行干预,否则可能会导致我们的模型出现偏差。
不同等级的酒,化学成分有区别吗?
如果品质只是对风味的评级,而风味是化学物质的混合物,那么不同评级的葡萄酒应该有不同的化学物质比例吧?
让我们在我们的红酒瓶上测试这个理论吧!

各等级葡萄酒中的平均化学成分含量
乍一看,化学物质遵循钟形曲线分布,8 星葡萄酒看起来与 4 星葡萄酒没有什么不同。这听起来不太直观。
也就是说,我认为这些数据分散在太多的葡萄酒质量分组中。我在想,如果我们把他们适当地分组,我们会发现一种更有意义的关系。
我们不关心 3 星和 4 星之间的区别,我们想确定是什么导致了 3 星和 8 星之间的跳跃。是什么让一瓶酒如此与众不同?
所以,让我们把一款酒分为“糟糕”、“一般”和“非凡”。

各类红酒中每种化学物质的平均含量。
我们在红酒中看到了什么趋势?
通过更直观地将我们的葡萄酒分组,我们发现了一些有价值的关系。
1。低盐:平均来说,最差的红酒氯化物含量最高。考虑到氯化物的“咸”特性,这是有道理的。“非凡的”葡萄酒最少。
2。酸度问题:“非凡”葡萄酒的挥发性酸度最少,柠檬酸含量最高。鉴于我们对葡萄酒酸的了解,这是有道理的:柠檬酸可能是故意引入的,因为它给葡萄酒带来了令人愉快的味道,但挥发性酸可能是发酵不良的产物。
3。酒精为王:最好的红酒含酒精最多。
现在我们已经有了每一类葡萄酒的简介,有没有什么额外的关系可以帮助我们解释为什么每个快照看起来是这样的?
让我们看看葡萄酒中存在的化学物质之间是否有其他关联。

红葡萄酒不同特性之间的相关性。
有趣的是,这个图表证实了我们刚刚推断的:对于红葡萄酒来说,酒精含量与葡萄酒质量评级(. 48)的相关性相对较强,其次是挥发性酸度(-.39)。
其他一些相互关系可以通过基础化学联系起来。例如,酒精的密度比水小,所以更多的酒精与更低的葡萄酒密度相关是有道理的。
同样,我们预计 pH 值和酸度之间有很强的相关性,因为 pH 值衡量的是一种物质倾向于酸性还是碱性。
难道真的是最好的红酒就是酒精含量最高的那种?
按照我的欧洲同事的说法,“没有”。当我在工作中展示这个笔记本时,他们注意到这个模型没有考虑到“葡萄酒的灵魂——水果味”。
他们有一个伟大的观点,在未来,我真的希望我们有一个记录特定口味的数据集。
在此之前,我非常有信心,挑选酒精含量最高的葡萄酒将增加我们选择高质量葡萄酒的几率,特别是如果它含有高含量的柠檬酸。

一个散点图,比较酒精和柠檬酸含量与样品中每种红酒的质量。最好的葡萄酒集中在右上象限。
但是等等,我们刚刚分析了红酒!高品质白葡萄酒的相关性有什么不同吗?

白葡萄酒不同特性之间的相关性。
很快,我们发现即使是白葡萄酒,酒精含量也与质量有着最大的正相关。
与红葡萄酒相比,柠檬酸的存在与质量的相关性为 20%,白葡萄酒的柠檬酸与质量的相关性为 0%。
我会假设,因为柠檬酸是一种添加剂,被故意添加到红酒中以增强它的味道,它不太可能是一个质量较低的红酒瓶会添加它。
相比之下,即使是质量最差的白葡萄酒,其柠檬酸含量也比普通的红葡萄酒高。
我不能确定这些差异的出现是因为柠檬酸在白葡萄酒中以更高的浓度自然存在,还是在白葡萄酒中添加柠檬酸是一种更常见的做法。
最终,我们需要做更多的研究来解释为什么存在这种相关性的差异。

高等级红葡萄酒和白葡萄酒化学成分的比较。
此外,这两者的特征是完全不同的。
在化学成分方面,优质白葡萄酒的含硫量是红葡萄酒的 2-4 倍。
鉴于我们所了解的硫作为防止腐败的添加剂,除非白葡萄酒含有更多的天然硫,否则发酵白葡萄酒可能需要额外的保护,而不是红葡萄酒(正如红葡萄酒受益于柠檬酸的添加)。
此外,白葡萄酒的氯化物含量是红葡萄酒的一半,但糖分含量却是红葡萄酒的近两倍。它们的酸性也略低。
尽管有这些差异,高品质红葡萄酒和白葡萄酒的酒精含量大致相同。
那么最后,我该买什么瓶呢?
不管是红葡萄酒还是白葡萄酒,我想你会喜欢几乎任何一瓶高酒精含量的葡萄酒。当你购买一瓶白葡萄酒时,柠檬酸的含量不会有什么影响,但它会决定你的红葡萄酒的成败。
虽然这个演示很有趣,但重要的是要承认,我们采用了数学方法来描述世界上最浪漫、最诗意的物质之一。一个品酒师会把这一天描述为“微风习习”,而我们却把它描述为“风速 5 英里每小时”。如此精确,在翻译中可能会丢失一些东西。
也就是说,虽然我们会更自信地选择我们的瓶子,但我觉得我的同事是对的:坐在你的约会对象对面,微笑着说“瓶子不错,你真的可以尝到硫磺”,只是没有同样的味道。
创建和部署应用的数据科学家指南
数据科学家关于创建和部署 web 应用程序资源的快速指南。

来源: Pexels (CCO)的妮娜·乌利科娃。
其他数据科学家和学生经常问我创建和部署应用程序的最佳方式。创建应用程序是个好主意!能够以某种类型的交互格式交付机器学习项目是非常强大的。然而,作为数据科学家,我们通常不是 web 开发方面的专家,因此,从历史上看,我们必须与 web 开发人员合作来创建应用程序(这并没有什么错,而且很多时候我们可能希望专家来创建我们的应用程序)。到 2021 年,由于许多开源开发者,数据科学家能够制作自己的应用程序,而无需花费过多的时间研究 web 开发!为数据科学项目创建应用程序比以往任何时候都更容易。在这里,我分享一些我觉得有用的库和资源。
但是我们不是已经有了创建仪表板的软件了吗?
在进入技术细节之前,在这一点上,如果你是一名数据科学家,你可能会问为什么我说我们从来没有能够创建应用程序,而数据科学家已经部署了超过十年的仪表板。那完全是真的!在许多情况下,仪表板软件,如 Tableau,对于一个项目来说是完全合适和足够的。然而,有些时候它们可能是限制性的。例如,假设您想要部署一个机器学习模型。我从未使用过 Tableau 之类的仪表板软件,但据我所知,如果你想在仪表板中部署一个机器学习模型,这即使不是不可能,也是极其困难的。这就是应用程序的用武之地。它们为数据科学家提供了更大的灵活性来构建他们想要的东西!
既然我们都相信创建应用程序是一项强大的技能,让我们更深入地了解技术细节。
Python 与 R
这里是为数据科学家设计的应用程序库的酷的部分,它们中的许多是我们日常使用的语言,比如 Python 和 R!
对于使用什么软件来创建应用程序,这将取决于您最喜欢使用哪种编码语言来编写——这里,我指的是 Python 和 r。至于哪种编程语言更好,这两种语言都有优点和缺点,但我总是建议学生学习 Python,因为 Python 中有大量的数据科学包,并且根据公司的不同(存在可变性!),当你在工业界有工作的时候,你的团队很可能对 Python 更熟悉(我更喜欢 Python 还有其他几个原因,这里就不赘述了)。但是如果你更熟悉 R,对你来说最好的应用程序库可能是闪亮的。也就是说,我从来没有用过 Shiny,所以我不能说太多,但它在 R 用户中非常受欢迎。
对于 Python 用户(推荐方式,我强烈推荐 Streamlit 。Streamlit 非常容易学习(他们有很棒的教程这里)。使用 Streamlit 的唯一先决条件是熟悉 Python——如果你对学习 Python 感兴趣,我写了另一篇关于它的博文,可以在这里找到。
应用部署
既然您已经知道如何使用 Streamlit 创建应用程序,下一步就是部署您的应用程序。您采取的部署方法将取决于(1)隐私限制和(2)应用程序的复杂性。
简化共享

来源: Streamlit
如果您能够公开您的代码和应用程序,一种简单的部署应用程序的方法是直接通过 Streamlit 共享。我以前从未真正使用过 Streamlit 共享,但是从浏览文档来看,这听起来相对容易。然而,我相信你只能分享有限数量的应用程序。
Docker 和云服务

来源: Docker
另一个选择是将你的 Streamlit 应用打包到一个 Docker 容器中(如果你从未听说过 Docker,可以看看这篇 T2 的博客文章),并将你的容器部署在一些云服务上,比如 AWS。这里的,是一篇博客文章,解释了如何用一个相对简单的应用程序做到这一点。根据你的应用程序中包含的机器学习模型(如果有的话),有多少用户将使用你的应用程序,如果你需要用户的反馈,应用程序部署可能会很快变得复杂。幸运的是,像 AWS 和 GCP 这样的云服务可以在这些情况下提供帮助,但要适应这些服务还需要一些努力。实际上有一个非常好的教程,可以清楚地了解这些复杂性,可以在这里找到。
公司特定的部署服务和其他服务
最后,您工作的公司可能有一些其他类型的部署服务,这些服务可能用于部署 Streamlit 应用程序。当然,如果这些选项可用的话,值得一试。
结论
从这篇博文中可以看出,创建和部署应用程序相对容易(嗯,大部分情况下!),这使得现在成为一名数据科学家非常令人兴奋!我希望你觉得这篇文章有用,并快乐编码!
获取 2022 年新冠肺炎数据的数据科学家指南
有时候,完成一件事的最好方法是自己动手。

用于显示县数据的自定义 Grafana 仪表板。(图片由作者提供)
在疫情的早期,数据科学家和分析师构建 COVID 跟踪仪表板是一种流行的做法。许多公共 API 被启动,但是这些服务一个接一个地被离线。截至 2021 年 12 月,New York Times GitHub repository仍然是最后的高质量 COVID 数据源之一,但即使是这个数据源也不全面,在您的项目中使用起来可能会很尴尬(它是通过 CSV 文件分发的)。
尽管聚合器停止发布精选数据,各州卫生部门继续努力报告各县的 COVID 统计数据。例如,天气频道在显示当前 COVID 信息时,将每个州的卫生部门作为其数据源。

Weather.com 直接从美国获取数据。(图片由作者提供)
比起依赖像《纽约时报》这样的中间人,你可以很容易地编写自己的脚本直接从美国获取。在本文中,我将向您展示如何快速接收县的 COVID 数据。
动机
几周前,一位客户向我咨询如何自动跟踪特定位置的新冠肺炎数据。两年来,他一直手动搜索并将数据输入到一个大型电子表格中,这个过程占用了他在实际工作的大量时间。
我承诺我会研究我们的选择,很快我意识到大多数流行的 API 已经被关闭了。我从《纽约时报》开始搜集信息,尽管我们需要的几个县没有出现在数据集中。然后我考虑去浏览天气频道,但是我突然明白了:
为什么我不能直接从美国获取数据?毕竟,这正是 NYT 这样的聚合网站正在做的事情。
所以经过几个小时的研究,我写了一些插件并拼凑了一个 Grafana 仪表盘(如上图)来可视化我的发现。我的客户非常高兴,所以我决定在这篇文章中写下我的发现。
获取数据
为了将数据导入 Pandas,让我们使用内置的 SQLite 数据库sql:local来存储我们的数据。您可以随时使用自己的数据库,例如,如果您想要构建自己的 Grafana 仪表盘。
我已经将我的提取器脚本发布为[covid](https://github.com/bmeares/covid) 海泡石插件——一种用于提取数据的 Python 模块格式。

该插件允许您按县摄取 COVID 数据。(图片由作者提供)
我在插件的 GitHub 库上提供了使用信息,下面我将更深入地介绍我是如何决定获取数据的。首先从 PyPI 安装海泡石并安装covid插件。
$ pip install -U --user meerschaum
$ mrsm install plugin covid
用连接器plugin:covid和实例sql:local注册一个管道,当出现提示时,输入与您的县对应的 FIPS 代码列表。
$ mrsm register pipe -c plugin:covid -m cases -i sql:local❓ Please enter a list of FIPS codes separated by commas: 08031,48113,45043,45045,45007,37107,37021,47157,47147
现在我们已经设置好了管道,让数据流动起来。运行sync pipes命令获取数据——第一次可能需要一分钟左右。
mrsm sync pipes -i sql:local

同步来自科罗拉多州、德克萨斯州和纽约时报的九个县的数据。(图片由作者提供)
就是这样!可以用--loop和--min-seconds连续同步,-d或--daemon标志作为后台作业运行命令。
mrsm sync pipes -i sql:local --loop --min-seconds 3600 -d
敲进管子里
下面是一个片段,演示了访问管道数据的方法:
接入您的管道并检索数据帧。(作者代码)
它是如何工作的
这是文章的一部分,我拉开帷幕,说出我的秘密。covid插件本身只执行其他插件并组合它们的结果;例如,[US-covid](https://github.com/bmeares/US-covid) 插件从《纽约时报》获取:
从纽约时报获取数据。(作者代码)
从特定州获取数据的难度各不相同——例如,加利福尼亚州提供 CSV,因此解析类似于 NYT:
从加州卫生部门下载数据。(作者代码)
但是其他州就没这么简单了。[TX-covid](https://github.com/bmeares/TX-covid) 插件下载并解析包含评论的 XLSX 电子表格,并为每天添加一个新列。解析这种深奥的格式是一件非常头痛的事情,这说明了为什么 Meerschaum 的插件系统如此强大。

德克萨斯州在一个格式奇怪的电子表格中分发 COVID 数据。(图片由作者提供)
解析德州的 COVID 数据需要一些技巧。(作者代码)
像加利福尼亚一样,佐治亚州以 CSV 形式分发数据,但是您首先需要提取一个 ZIP 文件来获得您正在寻找的数据集。下面是我从[GA-covid](https://github.com/bmeares/GA-covid/) 插件中得到的解决方案:
您需要提取一个 ZIP 文件来解析 Georgia 的数据。(作者代码)
最后,Colorado 公开了一个 RESTful API,感觉比其他州少了很多 jank(尽管这个 API 看起来并不完全是功能性的,所以它并不是没有 jankiness 的一部分)。以下节选自[CO-covid](https://github.com/bmeares/CO-covid/) 插件:
获取科罗拉多的数据看起来比原来容易。(作者代码)
结论
海泡石已经非常模块化了,而[covid](https://github.com/bmeares/covid/) 插件也以类似的方式工作。如果你想投稿,你可以为你的州编写并发布一个插件,并在[covid](https://github.com/bmeares/covid/pulls) 插件库中打开一个 PR。
你可能注意到我写插件的时候用了duckdb。我主要是从 SQL 的角度考虑,DuckDB 很好地弥合了 SQL 和 Pandas 之间的差距。要在海泡石插件中使用第三方库,将依赖项添加到一个全局required列表中(类似于requirements.txt),以plugin:开头的需求被视为海泡石插件。
COVID 插件所需的依赖项。(作者代码)
我希望这篇文章对如何获取新冠肺炎数据有所启发。没有人知道各州将保持他们的 COVID 报告多长时间,但进入 2022 年,这些插件可以帮助您的项目提供动力,就像他们为我的客户所做的那样。
识别和解决数据质量问题的数据科学家指南
为你的下一个项目尽早这样做将会节省你几周的努力和压力

米凯尔·布隆维斯特摄于佩克斯
如果你在人工智能行业工作过,接触过真实世界的数据,你就会理解这种痛苦。无论数据收集过程多么简化,我们将要建模的数据总是杂乱无章的。
根据 IBM 的说法, 80/20 法则同样适用于数据科学。数据科学家 80%的宝贵时间都花在简单地查找、清理和组织数据上,只有 20%的时间真正执行分析。
争论数据并不好玩。我知道这是至关重要的,“垃圾输入垃圾输出”和所有这些,但是我就是不喜欢清理空白、修复正则表达式和解决数据中不可预见的问题。
根据[谷歌研究](http://compounding events causing negative, downstream effects from data issues):“每个人都想做模型工作,但不是数据工作”——我被指控有罪。此外,本文还介绍了一种称为数据级联的现象,这种现象是由潜在数据问题引发的复合事件,会造成不利的下游影响。
实际上,到目前为止,问题有三个方面:
- 大多数数据科学家不喜欢清理和争论数据
- 只有 20%的时间可用于进行有意义的分析
- 数据质量问题,如果不及早处理,将级联和影响下游
这些问题的唯一解决方案是确保数据清理简单、快速、自然。我们需要工具和技术来帮助我们这些数据科学家快速识别和解决数据质量问题,以利用我们在分析和人工智能(我们真正喜欢的工作)方面的宝贵时间。
在本文中,我将展示一个这样的开源工具,它有助于根据预期的优先级提前识别数据质量问题。这个工具的存在让我如释重负,今天我迫不及待地想和你分享它。
ydata-拯救质量

作者截图
ydata-quality 是一个开源的 python 库,用于在数据管道开发的多个阶段评估数据质量。该库直观,易于使用,,你可以直接将其集成到你的机器学习工作流程中。
对我个人来说,这个库很酷的一点是可以对数据质量问题进行基于优先级的排序(稍后将详细介绍),当我们的时间有限,并且我们希望首先解决影响较大的数据质量问题时,这很有帮助。
让我向您展示如何使用混乱数据的真实示例。在本例中,我们将:
- 加载混乱的数据集
- 分析数据质量问题
- 深入挖掘提出的警告
- 应用策略来减轻它们
- 检查半清理数据的最终质量分析
在安装任何库之前,最好使用 venv 或 conda 为项目创建一个虚拟环境。完成后,在终端上键入以下命令来安装库:
pip install ydata-quality
现在您的环境已经准备好了,让我们继续看例子。
一个真实世界的混乱例子

暂时忍受一下混乱。(Pixabay 的照片来自 Pexels)
在这个例子中,我们将使用转换后的人口普查数据集,您可以从这个 GitHub 存储库中下载。你可以在这本 Jupyter 笔记本中找到本教程使用的所有代码。我建议您要么克隆存储库,要么下载笔记本来跟随示例。
步骤 1:加载数据集
作为第一步,我们将加载数据集和必要的库。请注意,该包有多个模块(偏差和公平性、数据预期、数据关系、漂移分析、错误数据、标记和缺失)用于单独的数据质量问题,但我们可以从DataQuality引擎开始,它将所有单独的引擎包装到一个类中。
from ydata_quality import DataQuality
import pandas as pddf = pd.read_csv('../datasets/transformed/census_10k.csv')
第二步:分析质量问题
这应该是一个漫长的过程,但是DataQuality引擎在抽象所有细节方面做得非常好。只需创建主类并调用evaluate()方法。
# create the main class that holds all quality modules
dq = DataQuality(df=df)# run the tests
results = dq.evaluate()
我们将收到一份关于数据质量问题的报告。
让我们仔细分析一下这份报告:
- 警告:这些包含数据质量分析期间检测到的问题的详细信息。
- 优先级:对于每个检测到的问题,都会根据问题的预期影响分配一个优先级(较低的值表示高优先级)。
- 模块:每个检测到的问题都与模块执行的数据质量测试相关联(例如:数据关系、重复等)。)
综上所述,我们注意到已经确定了五个警告,其中一个是高优先级问题。“Duplicates”模块检测到,我们有一个完全重复的列,需要修复。为了更深入地研究这个问题,我们使用了get_warnings()方法。
只需输入以下内容:
dq.get_warnings(test="Duplicate Columns")
我们可以看到针对我们想要解决的问题的详细输出:
[QualityWarning(category='Duplicates', test='Duplicate Columns', description='Found 1 columns with exactly the same feature values as other columns.', priority=<Priority.P1: 1>, data={'workclass': ['workclass2']})]
根据评估,我们可以看到列workclass和workclass2完全是重复的,这会对下游产生严重的后果。
步骤 3:使用特定模块分析质量问题
全面了解数据质量需要多个视角,因此需要八个独立的模块。尽管它们被封装在DataQuality类中,但有些模块不会运行,除非我们提供特定的参数。
例如,DataQuality 类没有执行偏倚和公平性质量测试,因为我们没有指定敏感特性。但是这个库的美妙之处在于,我们可以把它当作一个独立的测试来执行。
让我们通过执行偏倚和公平性测试来更好地理解它。
from ydata_quality.bias_fairness import BiasFairness#create the main class that holds all quality modules
bf = BiasFairness(df=df, sensitive_features=['race', 'sex'], label='income')# run the tests
bf_results = bf.evaluate()
当我们运行上面的代码时,我们生成了另一个特定于所选模块的类似报告。
从报告中,我们了解到,我们可能有一个代理特征泄漏了有关敏感属性的信息,并且敏感属性的特征值严重不足。为了研究第一个警告,我们可以使用针对特定测试的get_warnings()方法过滤来获取更多细节。
bf.get_warnings(test='Proxy Identification')
我们可以看到针对我们想要解决的问题的详细输出:
[QualityWarning(category='Bias&Fairness', test='Proxy Identification', description='Found 1 feature pairs of correlation to sensitive attributes with values higher than defined threshold (0.5).', priority=<Priority.P2: 2>, data=features
relationship_sex 0.650656
Name: association, dtype: float64)]
基于详细的警告,我们检查了列relationship和sex,注意到一些关系状态(例如,丈夫、妻子)是特定于性别的,因此影响了相关性。我们可以将这些分类值改为中性的(例如,已婚)。
步骤 4:解决确定的问题
让我们实际一点。我们永远不可能拥有 100%干净的数据。这一切都是为了在有限的时间内解决最具影响力的问题。作为一名数据科学家,这是一个需要根据您的限制做出的决定。
对于这个例子,让我们的目标是没有高优先级(P1)的问题,并解决至少一个偏见和公平的警告。基于警告的简单数据清理函数如下所示:
我们删除了重复的列work_class2并替换了relationship的值,使其更加通用和中性。
如果你想做进一步的数据清理,请随意。如果您选择进一步发展,我很想看看数据清理是什么样子的。请记住,您是数据科学家,决定权始终在您手中。
步骤 5:运行最终质量检查
您可以跳过这一步,但是当我通过另一个最终检查来检查我处理的数据时,我会感到安心。我强烈建议您也这样做,这样您就可以在完成数据清理转换后了解数据的状态。
您可以简单地首先调用质量引擎和evaluate()方法来再次检索样本报告。下面是我们清理完数据后,DataQuality引擎和BiasFairness引擎的报告。
我们可以从上面的两个报告中推断出,我们的高优先级问题已经得到解决,而另一个较低优先级的问题也按照我们的目标得到了解决。
总结想法

数据质量的存在让我如释重负。(图片由 Freepik 上的 Cookie_studio 拍摄)
听着,仅仅因为我们讨厌清理数据并不意味着我们停止这样做。这是机器学习工作流不可或缺的一个阶段,解决方案是将 ydata-quality 等有价值的工具和库集成到我们的工作流中。
在本文中,我们学习了如何使用开源包来评估数据集的数据质量,既可以使用DataQuality主引擎,也可以通过特定的模块引擎(例如BiasFairness)。此外,我们看到了QualityWarning如何提供一个高层次的严重性度量,并为我们指出引发警告的原始数据。
然后,我们基于数据质量问题定义了一个数据清理管道来转换杂乱的数据,并观察它如何解决我们想要的警告。
该库由位于 YData 的团队开发,其使命是提高人工智能行业的数据质量。还有问题吗? 加入友好的 slack 社区 直接向开发团队提问(你也可以在那里找到我!)
我们一定能一起改进这个库,你的反馈意味着这个库能解决你未来的大部分紧迫问题。我迫不及待地想看到你使用库,并在社区中听到你的反馈。
Python 虚拟环境数据科学家指南
它们是什么,有哪些选择,我们为什么需要它们?

路易斯·恩古吉在 Unsplash 上拍摄的照片
Python 编程语言有许多不同的版本。类似地,所有的 Python 库也有多个版本,使用特定版本的 Python,并且大多数依赖于其他包来运行,这被称为一组依赖。
您承担的每个数据科学项目都可能需要自己独特的一套第三方 Python 包。虚拟环境充当自包含环境,封装了 Python 版本和项目的所有依赖项。创建新的虚拟环境是开始任何新的数据科学项目时通常采取的第一步。
创建新的虚拟环境是开始任何新的数据科学项目时通常采取的第一步。
这些环境不仅有助于在您自己的机器上保持项目的依赖关系是有组织的和自包含的。它们还使得人们之间共享项目变得容易。虚拟环境保留关于所有这些项目依赖关系的信息。当其他人将您的项目下载到他们的本地机器上时,他们可以使用此信息来重新创建您的项目所依赖的确切环境。
入门指南
谈到 Python 的虚拟环境,有几种选择。每种工具都有自己的优缺点。作为一个数据科学家,很可能最终会找到自己的个人首选(我的是 pipenv)。然而,在你的职业生涯中,很可能你会不时地用到几个,这取决于你工作的公司或团队。因此,了解可用的核心工具是很有用的。
在本文中,我将介绍创建 Python 虚拟环境的三个主要工具,并简要介绍如何使用它们。
按照本教程,你需要确保你已经安装了 Python 和 pip。为此,请从命令行运行以下命令。
python --version
pip --version
如果你还没有安装 Python,这个指南给出了我发现的最好的一套说明。
本文中给出的所有代码示例都旨在从命令行运行。
Venv
Venv 是一个创建轻量级虚拟环境的模块,作为 Python 编程语言的标准。如果你已经安装了 Python 3,那么你不需要安装任何额外的东西来使用这个工具。
为了创建一个新的环境,我们首先需要创建一个目录来存储您的项目。在您的终端中运行以下命令。
mkdir myproject && cd myproject
接下来,我们在该目录中创建虚拟环境。您可以将单词env替换为虚拟环境的另一个名称。
python3 -m venv env
默认情况下,这将使用最新版本的 Python 创建一个虚拟环境。在撰写本文时,这是 3.7。然而,如果您需要一个特定的 Python 版本,您可以指定它,如下所示。
python3.5 -m venv env
该工具将创建一个名为 env 的新目录,其结构类似于下图所示。

作者图片
下一步是激活环境。
source env/bin/activate
现在,您将在终端中看到以下内容。

作者图片
要在完成项目工作后停用环境,只需运行。
deactivate
可以使用 pip 将新的包安装到激活的虚拟环境中。下面的代码将熊猫安装到环境中。
pip install pandas
我们可以看到 pandas 包及其依赖项已经安装到虚拟环境的site-packages文件夹中。

作者图片
为了使虚拟环境可以被其他人复制,我们需要一种方法来记录所有的第三方包和它们所使用的版本。用 venv 做到这一点的最简单的方法是在项目根目录下创建一个新的需求文件。
为此,我们运行以下代码。
pip freeze > requirements.txt
pip freeze命令列出了环境中安装的所有第三方包和版本。然后将输出写入一个名为 requirements.txt 的新文件中。
这个需求文件现在可以被其他人用来重新创建项目需要运行的确切环境。另一个用户可以下载项目,使用 venv 创建他们自己的环境,并使用需求文件安装所有需要的包。
Pipenv
Pipenv 是另一个创建 Python 虚拟环境的工具,它提供了一个更灵活、更丰富和更安全的方法。Pipenv 通过一个叫做 pipfile 的东西自动管理项目依赖关系。pipenv 无需手动创建和维护 requirements.txt 文件,而是在创建虚拟环境时创建 pipfile,并在安装或升级第三方库时更新它。
此外,当创建新的 pipenv 虚拟环境时,它还会生成一个名为 pipfile.lock 的文件。如前所述,许多第三方库依赖于其他库的特定版本来运行。这些被称为相互依赖。pipfile.lock 文件记录了这些相互依赖关系,并确保某些包不会自动升级,否则会破坏项目。
如果您使用的是 macOS,安装 pipenv 的最佳方式是使用 Homebrew,因为这会将 pipenv 及其所有依赖项安装到一个隔离的虚拟环境中,因此它不会干扰您的 Python 安装。或者,也可以通过 pip 安装。
brew install pipenv#orpip install pipenv
****T5【pipenv】工作流 ****
要开始使用 pipenv,首先要创建或克隆一个项目目录。
mkdir mypipenvproject && cd mypipenvproject
接下来,我们运行以下命令来安装 pipenv 环境。pipenv 环境将与项目目录同名。
pipenv install
如果您想要创建一个使用特定 Python 版本的环境,您可以使用此命令。
pipenv install --python 3.6
这将安装环境并创建一个 pipfile 和 pipfile.lock 文件。
要激活环境,只需键入以下命令。
pipenv shell
要安装一个新的第三方软件包,我们使用以下方法。
pipenv install package-name#e.g.pipenv install pandas
如果我们现在打开 pipfile,我们将看到添加了以下内容。如果我们安装了 pandas 的特定版本,该版本将包含在该文件中。

作者图片
要退出 pipenv 虚拟环境,只需键入exit。
分享你的项目
使用 pipenv,当另一个用户想要在他们的本地机器上重新创建您的项目时,他们可以简单地克隆您的项目,并从项目主目录运行以下命令。
pipenv install
pipenv shell
这将在 pipfile 中安装所有依赖项,并激活虚拟环境。
康达
Conda 是 Python 的开源包和环境管理器。这是科学界安装软件包和管理环境的一个流行选择。因此,它值得包含在一篇关于数据科学虚拟环境的文章中。
如果你还没有安装 Conda,你可以通过运行conda -V来检查,你可以在链接找到详细的安装说明。
要使用 conda 创建一个虚拟环境,只需从您的项目主目录运行以下命令。
conda create -n myenvname python**=3.7**
要激活环境,我们可以运行以下命令。
conda activate myenvname
安装软件包。
conda install -n myenvname package#e.g.conda install -n myenvname pandas
要停用环境,请运行。
conda deactivate
与 pipenv 不同,conda 不会自动创建文件来跟踪项目需求。如果你想和别人分享你的 conda 环境,你需要创建一个。您可以通过运行以下命令来实现这一点。
conda list --explicit > spec-file.txt
这将创建 conda 所称的规格文件,它非常类似于 venv 中的需求文件。
通过运行以下命令,该文件可用于重新创建环境。
conda create --name mynewenvname --file spec-file.txt
虚拟环境有助于数据科学家组织项目、保持独立性并实现与其他人的协作。本文涵盖了 Python 虚拟环境的三个最流行的选择。值得一试,看看哪一个是你的首选。
有关创建可再现数据科学项目的最佳实践的更多信息,请参见我之前的文章。
**
感谢阅读!
我每月都会发一份简讯,如果你想加入,请点击此链接注册。期待成为您学习旅程的一部分!**




浙公网安备 33010602011771号