TowardsDataScience-博客中文翻译-2020-二十五-

TowardsDataScience 博客中文翻译 2020(二十五)

原文:TowardsDataScience Blog

协议:CC BY-NC-SA 4.0

利用图论从新闻中构建社会网络

原文:https://towardsdatascience.com/building-a-social-network-from-the-news-using-graph-theory-by-marcell-ferencz-9155d314e77f?source=collection_archive---------20-----------------------

理解报纸中的社会关系

来源:我

序文

谁是对新闻最有影响力的人?政客、公司和名人的庞大网络到底是什么样子的?梅根汗·马克尔和阿尔戈斯有什么关系?

如果你曾经发现自己躺在床上睡不着,想知道以上任何一个问题,你就点击了正确的文章。因为我们将探索一种新颖的方式来呈现每天每时每刻通过新闻频道和观点文章反馈给我们的大量信息:我们将建立一个新闻人的社交网络。

为什么

我们生活在一个媒体和信息的时代,理解错综复杂的新闻的重要性不能被夸大。在之前的一篇文章中,我概述了利用社交媒体为任何组织造福的价值,并展示了一种使用 Twitter 数据的方法。如果 Twitter 是消费者的喉舌,那么它的耳机就是在听新闻。事实上,如今许多推特用户只是简单地分享新闻文章,以此来表达他们的赞同(或愤怒)。

知道你的组织、产品或老板是否出现在新闻中并不困难,但是知道他们出现在新闻中时在谁的公司会给你宝贵的洞察力,让你知道他们正被那些讨厌的记者们团结在什么样的人群中。

从更技术性的角度来看,构建数据的图形表示(即社交网络)为数据科学应用开辟了一系列可能性,例如找到最中心的节点(有影响力的人)或根据节点的边缘(连接)识别节点群(人的集团)。

简单来说,网络图看起来很酷。

什么

构建我们的社交网络的基本前提有两个,而且非常简单:

  1. 如果在同一篇文章中提到两个人,他们就是朋友
  2. 文章提到同样的两个人越多,他们作为朋友就越亲密。

让我们举个例子:如果一篇文章提到了唐纳德鲍里斯,另外两篇独立的文章提到了唐纳德迈克,我们会说唐纳德是鲍里斯的朋友,唐纳德也是迈克的朋友,只是多了一倍。因此,我们将构建一个如下所示的社交网络:

示例图形表示

我们不仅得到了友谊组的图像表示,我们还可以开始看到隐藏的关系:虽然鲍里斯和迈克没有在同一篇文章中提到,但我们可以肯定地猜测这两者是有关系的(并且他们通过他们共同的朋友唐纳德有关系)。我们还可以看出,唐纳德是团队中的老大,对迈克和鲍里斯都有影响力。

这只是三篇(非常短的)新闻报道的一个简单例子。你可以想象我们可以从数百份关于数千人的文件中获得什么样的见解。为了实现这一点,我们将使用两种技术,现在我将简单介绍一下。

命名实体识别

我们的首要任务是在文章中识别感兴趣的个人,如果我们事先不知道要找的是谁,这就不那么容易了(否则我们可以使用简单的字符串搜索)。幸运的是,命名实体识别存在。

命名实体识别是从文本中提取信息的自然语言处理任务;顾名思义,它识别实体,比如非结构化数据中的专有名词。它使用在大型文档集上训练的统计模型来实现这一点;该模型学习根据实体作为单词出现的上下文来识别和分类实体。我们将使用这些模型中的一个来为每篇文章列出被模型标记为人物组织的单词。

图论

一旦我们有了每篇文章的实体列表,我们将把它们组织成一个图形结构。根据定义,图是一组顶点和边:

其中 G 是我们的图,由一组顶点 V (或节点)和一组边 E (或链接)组成。

本文并不打算作为图论的入门,但我确实想强调图的几个重要属性,我们会发现这些属性在以后的练习中很有用:

  1. 图可以是有向的,也可以是无向的——有向图的节点是用一个方向链接起来的(令人惊讶),而链接的方向与无向图无关。在我们的例子中,我们将把我们的网络设置为一个无向图,因为在同一篇文章中单独提到 Mike 和 Don 并不能给出这两个实体之间信息方向的任何指示。
  2. 图可以是连通的,也可以是不连通的 — 一个图是连通的,如果你能描绘出一条从任一节点到任一其他节点的路径。我们上面的图是连通的,因为所有节点都以某种方式与所有其他节点相连;如果我引入第四个节点,杰瑞米,他没有在任何文章中与唐、迈克或鲍里斯一起被提及,他将与任何现有的节点都没有联系,我们的图将变得没有联系。
  3. 节点具有中心性度量 —这些度量描述了节点在网络中的重要性。其中一个度量标准(我们将在接下来的分析中使用)是特征向量中心性,它根据每个节点连接的其他重要节点的数量为每个节点分配一个分数。特征向量中心性的一个著名用例是谷歌的 PageRank 算法
  4. 图可以有集团 —集团是图(或子图)的子集,其中所有节点对都是相连的。这些是你学生时代友谊小组的数学表示。在我们的例子中,如果迈克也和鲍里斯有联系,他们就会形成一个小团体。

怎么做

关于实体和图形,我已经漫谈得够久了——现在让我们进入实质。

数据收集

到目前为止,我还没有触及我实际上打算从哪里得到我吹嘘的所有这些文章。当然,有许多新闻 API 可以用来从新闻中提取信息。我使用的资源来自于 GDELT 项目,这是一个免费的、开放的平台,可以实时监控世界各地的事件。

在 Google Jigsaw 的支持下,GDELT 项目用 100 多种语言监控世界上几乎每个国家每个角落的广播、印刷和网络新闻…

他们进行了许多非常有趣的活动和分析(说真的,检查一下),但我们会对他们的原始数据感兴趣,他们也完全免费提供这些数据(谢谢 GDELT)。他们每天发布包含当天发生的数千个事件的 CSV,但更重要的是,他们包括报道该事件的新闻来源的 URL。

我使用了包含 3 月 26 日发生的事件的文件,并过滤了英国一些最受欢迎的新闻提供商发布的新闻。我以这样一种方式限制了我的搜索,在某种程度上缩小了我们将要处理的实体的数量,并且也集中于对英国主流读者来说重要的东西。这样做仍然让我从以下来源获得了 805 篇文章:

一旦我有了 805 个 URL,我就使用 Goose 从每个网页中提取内容。同样,这不是本文的重点,所以我不赘述。如果您有兴趣跟随,有无数的资源会告诉您如何去做(包括 Goose 文档)。

提取每篇文章的标题和内容使我能够将我的数据组织成一个漂亮的熊猫数据框架:

一个漂亮的熊猫数据框

识别实体

如果你读过我以前的文章(我之前已经无耻地链接过了),你就会知道我对 Flair 库的喜爱,它用几行代码提供了最先进的 NLP 解决方案。Flair 使用神经语言模型( Akbik 等人,2018 )为文本数据分配标签,在这个过程中击败了大多数以前的模型的准确性。天赋对我们来说已经足够好了。天赋是一种福气。让我们兜一圈。

我们将告诉 Flair 的句子标记器预测句子中的实体:

鲍里斯和唐纳德去西雅图见微软。

!pip install flairimport torch
from flair.data import Sentence
from flair.models import SequenceTaggertagger = SequenceTagger.load('ner')sentence = Sentence('Boris went to Seattle with Donald to meet Microsoft')tagger.predict(sentence)for entity in sentence.get_spans('ner'):
  print(entity)

这产生了四个实体(如预期的那样):

PER-span [1]: “Boris”
LOC-span [4]: “Seattle”
PER-span [6]: “Donald”
ORG-span [10]: “Microsoft”

它成功地识别了所有四个实体,并且能够识别两个人(PER)、一个位置(LOC)和一个组织(ORG)。

Flair 的句子对象有一个有用的。to_dict()方法,它允许我们以更健壮的方式提取实体:

sentence.to_dict(tag_type='ner')

给我们一个字典,里面有句子中每个实体的所有有趣的属性:

{'entities': [{'confidence': 0.999889612197876,
  'end_pos': 5,
  'start_pos': 0,
  'text': 'Boris',
  'type': 'PER'},
 {'confidence': 0.9988295435905457,
  'end_pos': 21,
  'start_pos': 14,
  'text': 'Seattle',
  'type': 'LOC'},
 {'confidence': 0.9999843835830688,
  'end_pos': 33,
  'start_pos': 27,
  'text': 'Donald',
  'type': 'PER'},
 {'confidence': 0.8911125063896179,
  'end_pos': 56,
  'start_pos': 47,
  'text': 'Microsoft',
  'type': 'ORG'}],
 'labels': [],
 'text': 'Boris went to Seattle with Donald to meet with Microsoft'}

我们在上面说过,我们只关心人和组织,所以我们将把字典组织成一个漂亮的数据框架,并过滤掉这两个:

sent_dict = sentence.to_dict(tag_type='ner')df_ner = pd.DataFrame(data={
  'entity': [entity['text'] for entity in sent_dict['entities']],
  'type': [entity['type'] for entity in sent_dict['entities']]
 }
)df_ner[df_ner['type'].isin(['PER', 'ORG'])]

另一个漂亮的数据框

构建图表

为了构建实际的社交网络,我们将使用久经考验且值得信赖的 NetworkX 包。它的 Graph() 类需要(至少)一个图的边列表,所以我们将把实体列表合并成一个成对连接列表。

我们将使用 itertools 的组合功能,在给定一个项目列表的情况下,找到所有可能的组合。首先,我们将按字母顺序对我们的实体进行排序——这是为了确保在我们找到的每一对中(现在和以后),按字母顺序排列的上级实体出现在左侧,并且我们不会重复成对的 A-BB-A ,例如:

from itertools import combinationsdf_ner = df_ner.sort_values('entity')combs = list(combinations(df_ner['entity'], 2))df_links = pd.DataFrame(data=combs, columns=['from', 'to'])df_links

另一个漂亮的熊猫数据框

我们已经准备好创建图表并绘制它了!

import networkx as nxG = nx.Graph()for link in df_links.index:
  G.add_edge(df_links.iloc[link]['from'],
             df_links.iloc[link]['to'])nx.draw(G, with_labels=True)

我们的第一张图表

好,现在用真实数据做

以上所有内容都可以封装在一个很好的函数中,应用到我们的每一篇文章中。你会注意到几处增加——首先,我在这个过程中做了一些基本的清理,删除了换行符之类的,我还删除了代词,它有时会被识别为命名实体。这里的另一个关键步骤是,给定一个实体(比如说 Boris Johnson),我只取他们的姓,因为个人通常只被称为他们的姓。这一步骤确保鲍里斯·约翰逊和约翰逊不会被算作两个不同的实体。

def get_ner_data(paragraph): # remove newlines and odd characters
  paragraph = re.sub('\r', '', paragraph)
  paragraph = re.sub('\n', ' ', paragraph)
  paragraph = re.sub("’s", '', paragraph)
  paragraph = re.sub("“", '', paragraph)
  paragraph = re.sub("”", '', paragraph) # tokenise sentences
  sentences = tokenize.sent_tokenize(paragraph)
  sentences = [Sentence(sent) for sent in sentences] # predict named entities
  for sent in sentences:
    tagger.predict(sent) # collect sentence NER's to list of dictionaries
  sent_dicts = [sentence.to_dict(tag_type='ner') for sentence in sentences] # collect entities and types
  entities = []
  types = [] for sent_dict in sent_dicts:
    entities.extend([entity['text'] for entity in sent_dict['entities']])
    types.extend([entity['type'] for entity in sent_dict['entities']]) # create dataframe of entities (nodes)
  df_ner = pd.DataFrame(data={'entity': entities, 'type': types})
  df_ner = df_ner[df_ner['type'].isin(['PER', 'ORG'])]
  df_ner = df_ner[df_ner['entity'].map(lambda x: isinstance(x, str))]
  df_ner = df_ner[~df_ner['entity'].isin(df_contraptions['contraption'].values)]
  df_ner['entity'] = df_ner['entity'].map(lambda x: x.translate(str.maketrans('', '', string.punctuation))) df_ner['entity'] = df_ner.apply(lambda x: x['entity'].split(' ')[len(x['entity'].split(' '))-1] if x['type']=='PER' else x['entity'], axis=1) df_ner = df_ner.drop_duplicates().sort_values('entity') # get entity combinations
  combs = list(combinations(df_ner['entity'], 2)) # create dataframe of relationships (edges)
  df_links = pd.DataFrame(data=combs, columns=['from', 'to']) return df_ner, df_links

我们准备将我们的函数应用于每篇文章的内容。请注意,我花了大约 40-50 分钟在 Google Colab 上运行我的 800 篇文章:

df_ner = pd.DataFrame()
df_links = pd.DataFrame()for content in tqdm(df_day['content']):
  try:
    df_ner_temp, df_links_temp = get_ner_data(content)
    df_ner = df_ner.append(df_ner_temp)
    df_links = df_links.append(df_links_temp) except:
    continue

我把 try-catch 放在那里,以防文章中没有实体被识别,DataFrame.append()方法失败。我知道这不是好的做法。我还是做了。

有了我们庞大的实体链接列表,我们就可以开始构建图表了。为了让事情更容易管理,我将只保留至少在两篇独立文章中出现过的链接:

df_links = df_links.groupby(['from', 'to']).size().reset_index()df_links.rename(columns={0: 'weight'}, inplace=True)df_links = df_links[df_links['weight'] > 1]df_links.reset_index(drop=True, inplace=True)df_links.sort_values('weight', ascending=False).head(10)

3 月 26 日的头条新闻链接(和一个漂亮的数据框)

到目前为止,发现最多的联系是鲍里斯·约翰逊和国民保健服务之间的联系,其次是马特·汉考克和国民保健服务。哈里王子、梅根、安德鲁·科莫和唐纳德·特朗普也获得了荣誉提名。

不过,让我们建立一个更全面的图景。我们将选取出现在 6 篇文章中的链接(相信我,这是你仍然可以从一个情节中做出一些东西的最低数量),并像以前一样绘制图表:

df_plot = df_links[df_links['weight']>6]df_plot.reset_index(inplace=True, drop=True)G_plot = nx.Graph()for link in tqdm(df_plot.index):
  G_plot.add_edge(df_plot.iloc[link]['from'],
                  df_plot.iloc[link]['to'],
                  weight=df_plot.iloc[link]['weight'])pos = nx.kamada_kawai_layout(G_plot)
nodes = G_plot.nodes()fig, axs = plt.subplots(1, 1, figsize=(15,20))el = nx.draw_networkx_edges(G_plot, pos, alpha=0.1, ax=axs)nl = nx.draw_networkx_nodes(G_plot, pos, nodelist=nodes, node_color='#FAA6FF', with_labels=True, node_size=50, ax=axs)ll = nx.draw_networkx_labels(G_plot, pos, font_size=10, font_family=’sans-serif’)

3 月 26 日新闻中的关系网

每个点代表一个实体——一个人或一个组织——两个点之间的链接意味着它们一起出现在至少两篇独立的文章中。

我们可以立即看到一些利益集团 NHS 和约翰逊确实处于事情的中心,但我们可以在地图的右上角看到王室成员,在下半部分看到美国政治家。

现在你知道了——我们已经从真实的新闻数据中建立了一个社交网络。让我们从中提取一些真知灼见吧!

洞察力

既然我们已经完成了繁重的工作,有趣的部分可以开始了。我们会问我们的图表一些问题。

我们的网络中有多少人和关系?

让我们从简单的开始——我们将使用 nodes()和 edges()方法来查找我们的社交网络中实体和联系的数量:

n_nodes = len(G.nodes())n_edges = len(G.edges())print(f'There were {n_nodes} entities and {n_edges} connections found in the network.')

这告诉我们在网络中发现了 2287 个实体和 8276 个连接。

网络里大家都认识吗?

换句话说,我们的图是连通的吗?如果不是,每个子图有多大?

nx.is_connected(G)

这个命令返回 False ,告诉我们图中并不是所有的节点都相互连接。

subgraphs = [G.subgraph(c) for c in nx.connected_components(G)]subgraph_nodes = [sg.number_of_nodes() for sg in subgraphs]df_subgraphs = pd.DataFrame(data={
  'id': range(len(subgraph_nodes)),
  'nodes': subgraph_nodes
})df_subgraphs['percentage'] = df_subgraphs['nodes'].map(lambda x: 100*x/sum(df_subgraphs['nodes']))df_subgraphs = df_subgraphs.sort_values('nodes', ascending=False).reset_index(drop=True)df_subgraphs

每个连接组件中节点的百分比

我们超过 95%的节点属于一个大型连接集群。有趣的是,在我们的网络中似乎没有独立的、同样大的图;绝大多数实体都以某种方式相互联系。

但是这些人是怎么认识的呢?

我们将使用最短路径算法来找出这一点,但首先我们需要做一些内务。我们将只使用属于 95%的彼此都认识的节点来重新创建我们的图(我们希望确保我们的图是连接的),我们还将向我们的边添加一个新属性 inverse_weight 。这将是我们原始权重(即提到这两个实体的文章数量)的倒数,这将有助于我们的最短路径算法将高权重(更常见的连接)优先于低权重。

sg = subgraphs[np.argmax(np.array(subgraph_nodes))]df_links_sg = nx.to_pandas_edgelist(sg)df_links_sg['inverse_weight'] = df_links_sg['weight'].map(lambda x: 1/x)G = nx.Graph()for link in tqdm(df_links_sg.index):
  G.add_edge(df_links_sg.iloc[link]['source'], 
  df_links_sg.iloc[link]['target'],
  weight = df_links_sg.iloc[link]['weight'],
  inverse_weight = df_links_sg.iloc[link]['inverse_weight'])

我们现在可以计算任意两个实体之间的最短路径,最终— 最终— 给了我们期待已久的答案,即梅根汗·马克尔与零售商店 Argos 有什么关系。

source='Markle'
target='Argos'path = nx.shortest_path(G,
  source=source,
  target=target,
  weight='inverse_weight')path

这给了我们从 MarkleArgos 的实体链:‘Markle’,‘Charles’,‘Johnson’,‘Sainsbury’,‘Argos’。

于是梅根汗·马克尔和查尔斯王子出现在一篇文章中,查尔斯王子和鲍里斯·约翰逊出现在另一篇文章中,以此类推…

让我们来看看这些是哪些文章:

df_path = pd.DataFrame([(path[i-1], path[i]) for i in range(1, len(path))], columns=['ent1', 'ent2'])def get_common_title(ent1, ent2):
  df_art_path = df_articles[(df_articles['content'].str.contains(ent1)) & (df_articles['content'].str.contains(ent2))] df_art_path.sort_values('date', ascending=False).head(1) return df_art_path.iloc[0]['title']df_path['titles'] = df_path.apply(lambda x: get_common_title(x['ent1'], x['ent2']), axis=1)for title in df_path['titles']:
  print(title)

从梅根到阿尔戈斯的物品路线

上图展示了我们从梅根一直到阿尔戈斯的路线,一次一条:

  1. 梅根与查尔斯王子在一篇文章中谈到她禁止哈里王子去看望后者。
  2. 查尔斯王子与鲍里斯·约翰逊一起出现在一篇关于冠状病毒检测呈阳性的文章中。
  3. 鲍里斯在《塞恩斯伯里》杂志的一篇文章中谈到了商店里的抢购现象。
  4. 塞恩斯伯里出现在一篇文章中,Argos 的商店在封锁期间仍然开放。

谁是新闻上最有影响力的实体?

去更严肃的水域。我们可以找到每个节点的中心性度量,以了解它们对网络的影响程度。换句话说,我们将根据每个人或组织与多少其他有影响力的人或组织一起出现来给他们打分。我们在前面的段落中回忆过,这可以通过使用特征向量中心性度量来实现:

nodes = []
eigenvector_cents = []
ec_dict = nx.eigenvector_centrality(G, max_iter=1000, weight='weight')for node in tqdm(G.nodes()):
  nodes.append(node)
  eigenvector_cents.append(ec_dict[node])df_centralities = pd.DataFrame(data={
  'entity': nodes,
  'centrality': eigenvector_cents
})

将网络中最有影响力的 20 个实体形象化可以得出:

一个实体的酒吧越长(越绿),他们在新闻中的影响力就越大。

NHS 和 Boris Johnson 在对英国新闻的影响力方面领先,考虑到我们在新冠肺炎的处境,这并不奇怪。

有没有一群人经常一起出现在新闻中?

换句话说,我们能识别我们网络中的派系吗?记住,集团是图中的一组节点,其中每对节点都是相连的。我们将使用的算法称为k-团社区 ( 帕拉等人,2005 ),它允许我们通过定义 k ,或形成团所需的最少节点数,在网络中找到紧密连接的集群。值得注意的是,一个节点可能非常受欢迎,并且属于多个派系。

设置 k = 12 给我们 9 个好的和可管理的小团体:

from networkx.algorithms.community.kclique import k_clique_communitiescliques = list(k_clique_communities(G, 12))

如前所述,我们还将计算由集团形成的每个子图中的特征向量中心性(这也将基于边权重),并可视化结果:

派系阴谋

每对图代表一个集团。在左边的图中,我们看到了节点在网络中的位置,在右边,我们看到了集团中最有影响力的节点。

这使我们能够通过经常一起出现的人群来识别新闻中的核心话题:

  • 小团体 0: 美国政治和商业实体
  • 派系 1: 澳大利亚学术机构
  • 派系 2: 英国军事实体
  • 派系 3: 英国零售商
  • 派系 4: 英国政治组织
  • 小团体 5: 伦敦运输公司
  • 小团体 6: 英国政治家
  • 小团体 7: 更多的英国政治家
  • 小团体 8: 英国王室

外卖食品

  • 我们收集了一些最受欢迎的英国报纸在 3 月 26 日发表的 800 多篇文章
  • 我们使用最先进的命名实体识别算法来提取文章中同时出现的人和组织
  • 我们以图表的形式组织数据,产生了一个超过 2000 个实体和超过 8000 个连接的社交网络
  • 我们发现我们网络中超过 95%的实体都是相互连接的
  • 我们发现梅根汗·马克尔能联系到阿尔戈斯吗
  • 我们发现 NHS、鲍里斯·约翰逊和查尔斯王子是当天新闻中最有影响力的实体
  • 我们发现了 9 个不同的实体群体,主要是经常一起出现在新闻中的政客和政府机构

这花了一些时间和文字,所以如果你已经做到这一步,我推荐你——我希望这是值得的。这个练习仅仅触及了图形数据结构的皮毛。我打算在后续文章中进一步讨论这些数据,但是现在,我将把它留在这里。

事后思考

我做错什么了吗?我能做得更好吗?我做得好吗?

请随时在 LinkedIn 上联系我;如果你对我的工作感兴趣,我总是很乐意接受挑战或者只是聊聊天。

如果你想自己玩代码,请点击链接到我的 Google Colab 笔记本:

** [## 谷歌联合实验室

编辑描述

colab.research.google.com](https://colab.research.google.com/drive/1V28-kAtF8rW-Tb3QAYWQ3p6SU4jI7Rv9) [## 谷歌联合实验室

编辑描述

colab.research.google.com](https://colab.research.google.com/drive/13DlDmZyQvLt8F-HrDU5dqiMg3_RuXNYx)**

用 Python 构建语音识别器

原文:https://towardsdatascience.com/building-a-speech-recognizer-in-python-2dad733949b4?source=collection_archive---------17-----------------------

使用谷歌云语音 API 将您的音频文件转换为文本

照片由像素皮克斯拜拍摄

在这篇文章中,我将向您展示如何使用 Python 将音频文件转换成文本文档。语音识别就是这种转换的过程。它在现实世界中很常用。比如谷歌的 Home Mini、亚马逊的 Alexa、苹果的 Siri 等个人语音助手,只是我们所知道的比较流行的一些。

语音识别通过说话代替打字来帮助您节省时间。它帮助我们不用写一行代码就能和我们的设备交流。这使得技术设备更容易接近和使用。语音识别是在现实世界中使用人工智能的一个很好的例子。

这里有一个来自 Google I/O 2019 活动的场景。谷歌助手在行动!

在这篇文章中,我们将创建一个简单的语音识别模型,可以从音频文件中检测句子,然后我们将这些句子导出到文本文档中。在以后的文章中,我将向您展示另一个很好的语音识别示例,您可以将您的语音实时转换为文本格式。

你准备好了吗?让我们开始编码吧!

导入库

首先,让我们安装模块,以便我们可以在程序中导入和使用它。SpeechRecognition 模块支持多种识别 API,Google Speech API 就是其中之一。你可以从这里了解更多关于这个模块的信息。

pip install SpeechRecognition

现在我们可以导入库了

import speech_recognition as sr

创建识别器

在这一步中,我们将创建识别器实例。

r = sr.Recognizer()

导入音频文件

将音频文件导入我们的程序时,文件扩展名很重要。我已经用其他几种格式测试了我的代码,但是“wav”格式的效果更好。您可以使用在线文件转换器网站将您的音频文件格式转换为 wav。

例如,如果您正在使用 Macbook 的语音备忘录进行录制,音频文件将保存为 m4a 格式。在谷歌上搜索:“在线转换 m4a 文件为 wav 文件格式”。你会发现很多好网站。

音频文件是导入文件的功能。 Sr 是语音识别模块。

audio_file = sr.AudioFile('test.wav')

识别语音

我们使用的是 recognize_google 方法,这是来自 google 的云语音 API 的语音识别,如介绍中所述。

with audio_file as source: 
   r.adjust_for_ambient_noise(source) 
   audio = r.record(source)result = r.recognize_google(audio)

将结果导出到文本文档中

在下面的代码中,我们创建并打开一个文本文件。然后导出我们在前面代码中得到的结果。你会看到“准备好了!”在您的终端中。

with open('test.txt',mode ='w') as file: 
   file.write("Recognized text:") 
   file.write("\n") 
   file.write(result) 
   print("ready!")

密码

# importing the module
import speech_recognition as sr# define the recognizer
r = sr.Recognizer()# define the audio file
audio_file = sr.AudioFile('test.wav')# speech recognition
with audio_file as source: 
   r.adjust_for_ambient_noise(source) 
   audio = r.record(source)result = r.recognize_google(audio)# exporting the result 
with open('test.txt',mode ='w') as file: 
   file.write("Recognized text:") 
   file.write("\n") 
   file.write(result) 
   print("ready!")

使用 Python 的机器学习项目

建立人脸检测比你想象的要容易得多。

[## Python 中的简单人脸检测

如何使用 OpenCV 库检测图像中的人脸

towardsdatascience.com](/simple-face-detection-in-python-1fcda0ea648e)

语音到文本,开始音频识别的伟大项目。

[## 使用 Python 将您的语音转换为文本

使用麦克风将您的语音实时转换为文本

towardsdatascience.com](/convert-your-speech-to-text-using-python-1cf3eccfa922)

恭喜你。您已经用 Python 创建了自己的语音识别程序。希望你喜欢这篇教程,并在今天学到一些新东西。练习编码技能的最好方法是制作有趣的项目。在未来的帖子中,我想分享另一种语音识别技术,它可以实时检测您的声音并将其转换为文本。关注我的博客保持联系。

原载于 2020 年 5 月 17 日https://sonsuzdesign . blog

用 Python 构建语音翻译器

原文:https://towardsdatascience.com/building-a-speech-translator-in-python-8ff190212b49?source=collection_archive---------14-----------------------

使用谷歌翻译 API 将你的演讲翻译成多种语言

照片由亚历山大·奈特派克斯拍摄

在这篇文章中,我将向你展示如何使用 Python 将你的演讲翻译成不同的语言。语音翻译将记录你的讲话,然后理解你在说什么,并翻译成你喜欢的语言。翻译过程完成后,您可以自定义您的代码,使其显示翻译后的文本。这是一个伟大的项目,你会喜欢工作,甚至可能给你的朋友和家人留下深刻印象。

想象一下,当你想和一个来自不同国家的人交流时,你必须雇佣一个会说两种语言的人来做所有的翻译工作。以前是这样,但是随着互联网和技术的发展,交流变得越来越容易了。现在,我们可以使用谷歌翻译在几秒钟内翻译我们想要的任何东西,这难道不令人兴奋吗?YouTube 也在为一些视频这样做。他们利用人工智能将视频中的演讲实时转换成不同的语言。这个很神奇,值得分享。所以我想展示如何使用 Python 来实现这一点。这样你就有了会说多种语言的私人翻译。🙂

我们开始吧!

我想提一下,在这个项目中,我们将同时练习多个主题。这是一个结合你的技能,创造更好的东西的好方法。首先,我们将使用语音识别器来教我们的程序理解我们的语音,然后我们将它转换成文本。其次,我们将定义一个翻译器,将文本转换成我们喜欢的语言。最后,我们将使用一个文本到语音的模型,这样我们的程序就可以大声说话。

导入库

首先,让我们安装我们将在这个程序中使用的模块。我们将安装 SpeechRecognition、Pyttsx3 和 Googletrans 模块。SpeechRecognition 和 Googletrans 模块是由我们在 Google 的朋友创建的。所有这些模块都可以免费安装和使用,这是编程行业发展如此迅速的一个重要原因。下面是 pip 同时安装多个模块的代码:

pip install speechrecognition pyttsx3 googletrans

是的,就是它。使用 pip 安装模块非常容易。如果您想了解这些模块的更多信息,这里有它们的文档页面的链接。

1.语音识别

定义识别器

r = sr.Recognizer()

定义您的话筒

在定义麦克风实例之前,我们将选择输入设备。可能有多个输入设备插入您的计算机,我们需要选择我们打算使用哪一个。你知道机器是假人,你必须准确地告诉他们该做什么!使用下面的代码,你将能够看到你的输入设备。

print(sr.Microphone.list_microphone_names())

这里你可以看到我检查输入设备的结果。我建议您在定义麦克风之前运行这个脚本,因为您可能会得到不同的结果。该脚本返回一个带有输入名称的数组列表,对我来说我想使用“内置麦克风”,所以数组列表的第一个元素。定义麦克风代码将如下所示:

mic = sr.Microphone(device_index=0)

识别语音

如前所述,我们将使用识别 _ 谷歌方法,这是一个由我们在谷歌的朋友创建的语音识别模型。多亏了他们!

with mic as source: 
   r.adjust_for_ambient_noise(source) 
   audio = r.listen(source) result = r.recognize_google(audio)

如果想在翻译之前检查结果,可以在代码中添加下面一行。

print(result)

[## 用 Python 构建语音识别器

使用谷歌云语音 API 将您的音频文件转换为文本

towardsdatascience.com](/building-a-speech-recognizer-in-python-2dad733949b4)

2.构建翻译器

定义翻译器

这是这个项目有趣的部分。你会有很多选择。如果你愿意,你可以像我一样,闭上眼睛,想象一个你希望能去的国家,看看那里的人说什么语言。是的,这是从一堆不同的语言中选择的一种方法。🙂

现在,您可以将该语言放入 destination 属性中,如下所示。

p = Translator() 
k = p.translate(result, dest='french')

在下面的代码中,我们将把翻译的结果转换成文本格式,这将有助于我们的文本到语音模块正常工作。因为在以前的代码中,结果是作为对象存储的。

translated = str(k.text)
print(translated)

3.文本到语音引擎

定义文本到语音引擎

engine = pyttsx3.init()

我们刚刚将模块定义为一个引擎。现在是告诉我们的程序朗读翻译文本的时候了,但在此之前,我们必须定义语言。下面是查看语言列表及其 id 的代码,我们在定义翻译器时将需要这些代码。我建议在进入下一步之前,在您的终端中运行这段代码。

engine = pyttsx3.init() 
voices = engine.getProperty('voices') 
for voice in voices: 
  print("Voice:") 
  print(" - ID: %s" % voice.id) 
  print(" - Name: %s" % voice.name) 
  print(" - Languages: %s" % voice.languages) 
  print(" - Gender: %s" % voice.gender) 
  print(" - Age: %s" % voice.age)

声音

定义演讲者语言

复制您想要使用的语言的 ID,并将其粘贴到我们的程序中。我们使用 setProperty 方法来定义说话者语言。

fr_voice_id = "com.apple.speech.synthesis.voice.thomas" engine.setProperty('voice', fr_voice_id)

最后一步:让奇迹发生

engine.say(translated)engine.runAndWait()

恭喜你。您已经使用 Python 创建了自己的语音翻译器。希望你喜欢这篇教程,并在今天学到一些新东西。练习编码技能的最好方法是制作有趣的项目。

本文已由专业翻译 Chema 翻译成西班牙语。你可以在这里找到文章。知识是普遍的。

关注我的博客走向数据科学以获得灵感。

原载于 2020 年 6 月 8 日https://sonsuzdesign . blog

构建成功的数据计划

原文:https://towardsdatascience.com/building-a-succesful-data-initiative-c04725b3a70?source=collection_archive---------37-----------------------

成功的数据计划使用可扩展的简单架构。大多数大型数据项目都会失败。为什么数据计划会失败?

Serghei TrofimovUnsplash 上拍摄

  • 团队在没有明确业务目标的情况下开始构建
  • 团队试图使用它更不了解的应用程序来解决它不完全了解的问题
  • 团队追逐过于复杂和不必要的大数据“闪亮物体”技术

如何在 AWS 上构建大规模数据才能成功?

  • 使用数据管道分离执行和业务流
  • 通过成熟的事件系统(如社交网络和 SQS)推动摄入
  • 对于需要大数据吞吐量的问题,只使用“大数据”系统

从明确的业务目标开始

数据计划通常始于分散在孤岛和系统中的大量数据。

如果您的业务目标是使数据的某个方面对决策者有用,那么决策者(一个人)必须理解该数据的价值。一个常见的数据点是,短期记忆可以保存七个正负两个项目

因此,从大数据的角度来看,挑战在于分解数百万、数十亿、甚至数万亿行数据,这些数据甚至超过了人类思维所能理解的七个加或减两个项目的可操作数据点。

你可能有许多这样的数据点,但是知道它们是什么——那是你的领域分析。

一旦分析完成,您就可以专注于技术目标了。

选择许多工程师理解的通用技术。

AWS 上的服务列表是字母汤。许多大数据副总裁的愿望通常相当于字母汤。成功的项目始于可靠、简单的技术和清晰的关注点分离,这样,如果一项简单的技术需要复杂的替换,它就可以被替换。

将数据接收到数据仓库需要什么?

  1. 数据暂存库
  2. 活动基础设施
  3. 数据管道技术
  4. 一个或多个数据库

将数据转化为行动数据点需要什么?

  1. 一种无处不在的编程语言— Python
  2. 汇总和交付数据的报告系统
  3. 允许业务用户对聚合进行切片和切块的自助式 BI 系统
  4. 分析数据的数据科学软件

成熟、经济高效的数据堆栈

数据堆栈是必不可少的,它会根据现有的技术和平台而变化。堆栈上的预算应该反映组件的性质。

把钱花在

  1. 一个好的数据库— 雪花
  2. 一个不错的自助 BI 前端— Tableau

为什么要在这两个技术上花钱呢?一个好的数据库将为您的数据提供一个 SQL 接口,您的团队将能够利用他们现有的 SQL 知识从数据中获取价值。一个好的 BI 环境将向业务用户开放数据,使他们在访问数据时能够自助服务。

在…上省钱

  1. 数据集成工具— AWS 阶跃函数AWS Lambda 函数
  2. 事件技术— 将社交网站扇出到 SQS 的 SQS
  3. 数据科学和分析— 熊猫

为什么 AWS Step 函数和 Lambda 函数优于成熟的数据集成工具?因为它将推进一个可扩展的架构,不会将你的团队束缚在与 Pentaho、Informatica、Talend、SSIS 等工具相关的不良实践中。这些工具基于昂贵的服务器、供应商锁定和专有集成接口。

现代集成通常使用 REST,在不使用 REST 的地方,集成点被很好地记录和发布。

例如:

本质上,任何数据集成平台的数据连接器都是公共的、免费的,并且通常比数据集成提供商提供的连接器质量更好。

选择具有成熟技术的简单架构。

AWS 有几项核心技术应该构成您的数据计划架构的基础

数据暂存库——【https://aws.amazon.com/s3/】T4

数据暂存存储库不仅仅是一个简单的文件夹结构,需要仔细考虑。该存储是私有的,支持 SQL 数据库和数据仓库。它也可以被认为是“数据湖”

端到端数据产品(图片由作者提供)

事件基础设施SNS 和 SQS ,应用事件, CloudWatch 事件

许多来源发布的事件。外部应用程序可以写入事件存储库,API 可以写入外部应用程序。在内部,AWS Cloudwatch 事件可以按计划运行,甚至可以从 SQS 的帖子中触发。

使用简单通知服务允许单个事件点服务一个或多个事件队列。

在构建应用程序时,CloudFormation 模板允许您为预定事件安排事件规则。

ScheduledEventRule:
  Type: "AWS::Events::Rule"
  Properties:
    Description: "Scheduled event to trigger Step Functions state machine"
    ScheduleExpression: cron(30 10 ? * MON-SUN *)
    State: "ENABLED"
    Targets:
      -
        Arn: !Ref BusinessAnalyticsPipeline
        Id: !GetAtt BusinessAnalyticsPipeline.Name
        RoleArn: !GetAtt ScheduledEventIAMRole.Arn

数据管道技术Step 函数AWS Lambda with Python

数据管道技术本质上将加载过程映射到并行管道的映射列表中。取代传统的数据集成工具需要一个状态机来描述数据流,并允许您将数据流与处理分开。

数据可伸缩性应该在管道中构建并行性,映射的负载应该通过在加载数据时并行运行多个管道来利用自然的无服务器可伸缩性。

使用迭代器和步进函数构建映射管道(图片由作者提供)

一个可扩展的数据库——关系数据的 Postgres】或者数据仓库的雪花

雪花可能很昂贵,但它的优势是将计算模型与数据模型分离开来,以扩展到多个 TB/小时的数据加载吞吐量

雪花将通过 AWS 从 S3 源登台,并在 Azure 或谷歌云中有等效的舞台商店。

它将运行大规模分布式查询,并且是面向列的,并将使用标准 SQL 支持任何 Tableau 或分析查询。

让您的数据更有价值

有价值的数据需求

  1. 性能—您需要速度快
  2. 质量—您需要在发布前清理数据
  3. 文档—您需要一个数据字典

在让数据变得有价值的每一步,用户需求都是第一位的。领域模型应该抓住用户的需求。

性能:理解领域模型如何映射到物理数据存储

事件数据将非常庞大,而且不会干净。清洁的第一步是

  1. 了解数据的分布键

分布键将成为数据上大型并行操作的自然键。如果您的域模型集中了一个人,那么对于您的分布密钥来说,这是一个很好的选择。你怎么认出这把钥匙的?它几乎会出现在 SQL 中的每个连接中。

在雪花中,这将是你的聚类键:https://docs . snow flake . com/en/user-guide/tables-clustering-keys . html

雪花会自动对你的表进行聚类:https://docs . snow flake . com/en/user-guide/tables-auto-re clustering . html

质量:发布前清理数据

在专门用于清理数据的 Step 函数中开发管道。

为编写 SQL 测试

  1. 填补缺失数据
  2. 删除重复项
  3. 删除陈旧和错误的数据

清理数据内容

  1. 使用开放标准验证地址数据,如谷歌地理编码
  2. 使用您了解的 Python 工具清理数据

为客户构建自助数据访问

当组织急需数据时,数据的消费者需要自助访问。在构建自助式数据访问端点时,数据项目早期阶段的域模型至关重要。

把你的领域模型变成一个数据字典

领域模型是构建合适的数据字典的指南。我建议在一个简单的 React 组件中利用 JSON 对象定义来为我们提供的数据生成数据字典。

在自助 BI 工具中发布模型

许多 BI 工具允许用户自助使用您的数据。我通常使用 Tableau,因为它是 BI 工具事实上的标准。它非常灵活和强大,即使价格过高。

如果价格太高, React Google Charts API 在 React 应用程序中直接嵌入了许多优秀的可视化组件。

最后

对于任何组织来说,数据仓库都是一个昂贵且有风险的步骤,但是如果您有条不紊地遵循清晰的步骤,您就可以成功。

  • 从清晰、可实现的业务目标开始
  • 通过领域建模来为您的仓库建模
  • 选择开发社区和您的团队充分理解的技术
  • 使用 SQL 数据库作为您的报告基础
  • 通过 BI 工具为您的用户提供自助访问

用计算机视觉和回溯构建一个数独求解应用程序

原文:https://towardsdatascience.com/building-a-sudoku-solving-application-with-computer-vision-and-backtracking-19668d0a1e2?source=collection_archive---------20-----------------------

使用在合成数据上训练的自定义 OCR 从截图中解决数独,然后回溯。

贾森·汤普森Unsplash 上拍照

数独是一种基于逻辑的谜题,通常以 9x9 网格和 1 到 9 位数的 3x3 子网格的形式出现。这个难题的有效解决方案的条件是,在任何行、列或 3×3 子网格中,没有数字被使用两次。

可能的 9x9 网格的数量是 6.67×10^21,所以根据最初的拼图,找到一个解决方案有时是具有挑战性的。在这个项目中,我们将构建一个 Streamlit 应用程序,它可以在给定一个屏幕截图的情况下自动解决数独难题。

我们将首先构建一个对象字符识别模型,它可以从数独网格图像中提取数字,然后使用回溯方法来解决这个问题。最终的应用程序将通过一个易于使用的 Streamlit 应用程序进行访问。

数独 python 表示和求解器的第一个版本大部分都是从这个 Repo:【https://github.com/RutledgePaulV/sudoku-generator中获取和修改的

目标字符识别

图片来源:https://en.wikipedia.org/wiki/Sudoku

一旦我们有了拼图的图像,我们需要提取所有写在那里的数字,以及它们的位置。

为此,我们将训练一个数字检测器模型,然后训练一个数字识别器模型。第一个会告诉我们一个数字出现在图像的什么地方,第二个会告诉我们是哪个数字。我们还将获得这两项任务的数据集。

探测器模型

我们将使用的检测器模型基于具有跳跃连接的全卷积神经网络,与我们在以前的项目中使用的非常相似,例如:

如果你想了解更多关于图像分割的知识,你可以阅读这两篇文章。

这个模型的目标是输出一个二进制掩码,告诉我们,对于输入图像的每个像素,它是否是数字的一部分。

识别器模型

从上面的网格中提取的字符

识别器模型的作用是接受单个数字作为输入,并预测它是集合{1,2,3,4,5,6,7,8,9}中的哪一个。它主要是一个卷积网络,但输出是一个具有 softmax 激活的全连接层。

数据集

为了训练上述两个网络,我们需要带注释的数据。我们可以生成一个合成的数据集,而不是手动注释一堆数独网格,因为它花费不多,并且希望它能起作用😉。

为了有一个真实的数据集,我们使用多种类型的字体、大小、背景颜色、网格元素…

生成图像的示例

因为我们从头开始生成这些例子,所以我们可以获得图像中每个数字的位置和类别的所有细节。

最终 OCR 结果

追踪

我们将使用回溯来解决数独。这种方法允许我们一步一步地构建树状的候选解,然后如果我们发现子树不能产生可行的解,就修剪这棵树。

我们在数独游戏中的做法如下:

  • 对于每个单元格,我们计算可以用来填充给定网格状态的单元格的可能值。我们可以通过消去法很容易地做到这一点。
  • 我们按照可能值的数量对单元格进行排序,从最低到最高。
  • 我们遍历第一个未填充的单元格,给它分配一个可能的值,然后分配给下一个,依此类推…
  • 如果我们最终得到了一个可行的解,我们就返回它,否则我们就回到上一个被赋值的单元格,把它的状态改成另一个可能的值。有点像深度优先树搜索。

数字定义了遍历的顺序。来源:https://commons . wikimedia . org/wiki/File:Depth-first-tree . SVG

如果在探索了这棵树所有可能的叶子之后,我们找不到解决方案,那么这个数独就是无解的。

回溯的好处是保证找到一个解或者证明一个不存在。问题是,虽然它在 9x9 数独网格中通常很快,但它在一般情况下的时间复杂度是惊人的。

实现(有些操作,如排序,是在“Board”类中执行的):

def backtracking_solve(board):
    # Modified from [https://github.com/RutledgePaulV/sudoku-generator/blob/master/Sudoku/Solver.py](https://github.com/RutledgePaulV/sudoku-generator/blob/master/Sudoku/Solver.py)
    set_initially_available(board.cells)
    to_be_filled = board.get_unused_cells()
    index = 0
    n_iter = 0
    while -1 < index < len(to_be_filled):
        current = to_be_filled[index]
        flag = False
        possible_values = board.get_possibles(current)
        my_range = range(current.value + 1, 10)
        for x in my_range:
            if x in possible_values:
                n_iter += 1
                current.value = x
                flag = True
                break
        if not flag:
            current.value = 0
            index -= 1
        else:
            index += 1
    if len(to_be_filled) == 0:
        return n_iter, False
    else:
        return n_iter, index == len(to_be_filled)

该应用程序

我们使用 Streamlit 构建应用程序。该应用程序需要允许我们上传图像,解决数独,并显示结果。

文件上传:

Streamlit 提供了一种使用 st.file_uploader 创建文件上传小部件的简单方法。

file = st.file_uploader("Upload Sudoku image", type=["jpg", "png"])

OCR:

我们应用检测器和识别器模型来创建网格。

grid = img_to_grid(img, detector_model, recognizer_model, plot_path=None, print_result=False)

求解:

我们使用回溯来解决数独。

n_iter, _ = backtracking_solve(to_solve_board)

显示结果:

我们通过指定 unsafe_allow_html=True 在一个漂亮的 Html/Css 表中显示结果。

html_board.markdown("<center>" + to_solve_board.html() + "</center>", unsafe_allow_html=True)

最终结果:

结论:

在这个小项目中,我们在 Streamlit 中构建了一个数独求解应用程序。我们在这个过程中训练了一个定制的 OCR 模型,并使用回溯来解决实际的数独网格。

代号:https://github.com/CVxTz/sudoku_solver/

使用自然语言处理构建自杀推特分类器

原文:https://towardsdatascience.com/building-a-suicidal-tweet-classifier-using-nlp-ff6ccd77e971?source=collection_archive---------32-----------------------

使用自然语言处理来预测推特上的自杀意念。

多年来,自杀一直是全球范围内的主要死亡原因之一,根据维基百科的数据,2015 年,自杀导致全球 82.8 万人死亡,比 1990 年的 71.2 万人有所增加。这使得自杀成为全球第十大死亡原因。越来越多的证据表明,互联网和社交媒体能够影响与自杀相关的行为。使用自然语言处理,机器学习的一个领域,我建立了一个非常简单的自杀意念分类器,它可以预测一个文本是否有自杀倾向。

分析了 Tweet 的 WordCloud

数据

我使用了一个我在 Github 上找到的 Twitter 爬虫,通过删除标签、链接、URL 和符号对代码做了一些修改。每当它从 Twitter 上抓取数据时,数据都是基于包含如下单词的查询参数抓取的:

沮丧,绝望,答应照顾,我不属于这里,没有人值得我,我想死等等。

虽然有些文本与自杀毫无关系,但我不得不手动标注了大约 8200 行推文的数据。我还获取了更多的 Twitter 数据,并且能够将我以前拥有的足够我训练的数据连接起来。

构建模型

数据预处理

我导入了以下库:

import pickle
import re
import numpy as np
import pandas as pd
from tqdm import tqdm
import nltk
nltk.download('stopwords')

然后,我编写了一个函数来清理文本数据,删除任何形式的 HTML 标记,保留表情字符,删除非单词字符,最后转换为小写。

def preprocess_tweet(text):
    text = re.sub('<[^>]*>', '', text)
    emoticons = re.findall('(?::|;|=)(?:-)?(?:\)|\(|D|P)', text)
    lowercase_text = re.sub('[\W]+', ' ', text.lower())
    text = lowercase_text+' '.join(emoticons).replace('-', '') 
    return text

之后,我对 tweet 数据集应用了 preprocess_tweet 函数来清理数据。

tqdm.pandas()df = pd.read_csv('data.csv')
df['tweet'] = df['tweet'].progress_apply(preprocess_tweet)

然后,我使用。split()方法,并使用词干将文本转换为它们的根形式。

**from** **nltk.stem.porter** **import** PorterStemmer
porter = PorterStemmer()
**def** tokenizer_porter(text):
    **return** [porter.stem(word) **for** word **in** text.split()]

然后我导入了停用词库来删除文本中的停用词。

**from** **nltk.corpus** **import** stopwords
stop = stopwords.words('english')

在单个文本上测试函数。

[w for w in tokenizer_porter('a runner likes running and runs a lot') if w not in stop]

输出:

['runner', 'like', 'run', 'run', 'lot']

矢量器

对于这个项目,我使用了哈希矢量器,因为它与数据无关,这意味着它的内存非常低,可扩展到大型数据集,并且它不在内存中存储词汇字典。然后,我为哈希矢量器创建了一个记号赋予器函数

def tokenizer(text):
    text = re.sub('<[^>]*>', '', text)
    emoticons = re.findall('(?::|;|=)(?:-)?(?:\(|D|P)',text.lower())
    text = re.sub('[\W]+', ' ', text.lower())
    text += ' '.join(emoticons).replace('-', '')
    tokenized = [w for w in tokenizer_porter(text) if w not in stop]
    return tokenized

然后我创建了散列矢量器对象。

from sklearn.feature_extraction.text import HashingVectorizervect = HashingVectorizer(decode_error='ignore', n_features=2**21, 
                         preprocessor=None,tokenizer=**tokenizer**)

模型

对于该模型,我使用了随机梯度下降分类器算法。

**from** **sklearn.linear_model** **import** SGDClassifier
clf = SGDClassifier(loss='log', random_state=1)

培训和验证

X = df["tweet"].to_list()
y = df['label']

对于模型,我用了 80%用于训练,20%用于测试。

from sklearn.model_selection import train_test_split
X_train,X_test,y_train,y_test = train_test_split(X,
                                                 y,
                                                 test_size=0.20,
                                                 random_state=0)

然后,我用我们之前创建的哈希矢量器将文本数据转换成矢量:

X_train = vect.transform(X_train)
X_test = vect.transform(X_test)

最后,我将数据与算法相匹配

classes = np.array([0, 1])
clf.partial_fit(X_train, y_train,classes=classes)

让我们测试一下测试数据的准确性:

print('Accuracy: %.3f' % clf.score(X_test, y_test))

输出:

Accuracy: 0.912

我有 91%的准确率,这很公平,之后,我用预测更新了模型

clf = clf.partial_fit(X_test, y_test)

测试和预测

我在模型中添加了文本“我要自杀,我厌倦了沮丧和孤独的生活”。

label = {0:'negative', 1:'positive'}
example = ["I'll kill myself am tired of living depressed and alone"]
X = vect.transform(example)
print('Prediction: %s\nProbability: %.2f%%'
      %(label[clf.predict(X)[0]],np.max(clf.predict_proba(X))*100))

我得到了输出:

Prediction: positive
Probability: 93.76%

而当我用下面的文字“这么热的天,我想吃冰淇淋,逛公园”时,我得到了下面的预测:

Prediction: negative
Probability: 97.91%

该模型能够准确预测这两种情况。这就是如何建立一个简单的自杀推特分类器。

你可以在这里找到我写这篇文章用的笔记本

感谢阅读😊

使用 NLTK ft 构建文本规范化器。POS 标签

原文:https://towardsdatascience.com/building-a-text-normalizer-using-nltk-ft-pos-tagger-e713e611db8?source=collection_archive---------19-----------------------

标记化和文本规范化是自然语言处理技术的两个最基本的步骤。文本规范化是将单词转换成其词根或基本形式以标准化文本表示的技术。请注意,基本形式不一定是有意义的词,我们将在本博客结束时看到这一点。这些基本形式被称为词干,这个过程被称为词干。一种专门的获取词干的方法叫做词汇化,它根据单词所属的词类(POS)家族使用规则。

照片由 Jelleke VanooteghemUnsplash 拍摄

为什么文本规范化很重要?

文本规范化对于信息检索(IR)系统、数据或文本挖掘应用以及 NLP 管道是至关重要的。为了促进更快和更有效的信息检索系统,索引和搜索算法需要不同的单词形式——派生的或屈折的——简化为它们的规范化形式。此外,它还有助于节省磁盘空间和语际文本匹配。

词干化与词汇化

推导词条的过程处理单词所属的语义、形态和词性(POS ),而词干指的是一种粗略的启发式过程,它砍掉单词的词尾,希望在大多数时候都能正确实现这一目标,并且通常包括去除派生词缀。这就是为什么在大多数应用程序中,词汇化比词干化有更好的性能,尽管缩小到一个决策需要更大的视野。

在 Python 的 NLTK 库中, nltk.stem 包同时实现了词干分析器和词汇分析器。

够了!现在让我们深入研究一些文本规范化代码…

会出什么问题呢??!

注: 这里的 是 GitHub 上完整的 jupyter 笔记本。

Unsplash 上由 Max Duzij 拍照

第一步:标记化

**import** nltk
**import** stringhermione_said = '''Books! And cleverness! There are more important things - friendship and bravery and - oh Harry - be careful!'''*## Tokenization***from** nltk **import** sent_tokenize, word_tokenize
sequences = sent_tokenize(hermione_said)
seq_tokens = [word_tokenize(seq) for seq in sequences]*## Remove punctuation*no_punct_seq_tokens = []
**for** seq_token **in** seq_tokens:
    no_punct_seq_tokens.append([token for token in seq_token if token not in string.punctuation])print(no_punct_seq_tokens)**Output:**
[['Books'],
 ['And', 'cleverness'],
 ['There',
  'are',
  'more',
  'important',
  'things',
  'friendship',
  'and',
  'bravery',
  'and',
  'oh',
  'Harry',
  'be',
  'careful']]

太棒了。

第二步:标准化技术

我们从词干开始:

# Using Porter Stemmer implementation in nltk
**from** nltk.stem **import** PorterStemmer
stemmer = PorterStemmer()
stemmed_tokens = [stemmer.stem(token) **for** seq **in** no_punct_seq_tokens **for** token **in** seq]
print(stemmed_tokens)**Output:**
['book',
 'and',
 'clever',
 'there',
 'are',
 'more',
 '**import**',
 'thing',
 'friendship',
 'and',
 '**braveri**',
 'and',
 'oh',
 '**harri**',
 'be',
 'care']

于是我们有了第一个问题——在 16 个令牌中,高亮显示的 3 个并不好看!

所有这三个都是粗略的启发式过程的产物,但是“Harry”到“harri”是误导性的,尤其是对于 NER 应用程序,并且对于“import”来说“重要的”是信息丢失。“勇敢”到“勇敢”是一个词干的例子,它没有任何意义,但仍然可以在信息检索系统中用于索引。一个更好的例子是——争论,争论,争论——所有这些都变成了“争论”,保留了原始单词的意思,但它本身在英语词典中没有任何意义。

现在,让我们来假设一下:

**from** nltk.stem.wordnet **import** WordNetLemmatizer
**from** nltk.corpus **import** wordnetlm = WordNetLemmatizer()
lemmatized_tokens = [lm.lemmatize(token) **for** seq **in** no_punct_seq_tokens **for** token **in** seq]
print(lemmatized_tokens)Output:
['**Books**',
 'And',
 'cleverness',
 'There',
 'are',
 'more',
 '***important***',
 'thing',
 'friendship',
 'and',
 '***bravery***',
 'and',
 'oh',
 '***Harry***',
 'be',
 'careful']

这里,前面三个词干不正确的单词看起来更好。但是,“书”应该像“物”之于“物”一样成为“书”。

这是我们的第二个问题。

现在,WordNetLemmatizer.lemma()接受一个参数 pos 来理解单词/令牌的 pos 标签,因为单词形式可能相同,但上下文或语义不同。例如:

print(lm.lemmatize("Books", pos="n"))
Output: 'Books'print(lm.lemmatize("books", pos="v"))
Output: 'book'

因此,从 POS tagger 获得帮助似乎是一个方便的选择,我们将继续使用 POS tagger 来解决这个问题。

但在此之前,我们先来看看用哪个——stemmer?lemmatizer?两者都有?要回答这个问题,我们需要后退一步,回答如下问题:

  • 问题陈述是什么?
  • 哪些功能对解决问题陈述很重要?
  • 这个特性会增加计算和基础设施的开销吗?

上面的例子是 NLP 从业者在处理数百万代币的基本形式时面临的更大挑战的一个简单尝试。此外,在处理庞大的语料库时保持精度,并进行额外的检查,如词类标记(在这种情况下)、NER 标记、匹配单词包中的标记(BOW)以及拼写纠正,这些都是计算开销很大的。

步骤 3: POS 标签员进行救援

让我们对已经词干化和词汇化的标记应用 POS tagger 来检查它们的行为。

**from** nltk.tag **import** StanfordPOSTagger

st = StanfordPOSTagger(path_to_model, path_to_jar, encoding=’utf8')## Tagging Lemmatized Tokenstext_tags_lemmatized_tokens = st.tag(lemmatized_tokens)
print(text_tags_lemmatized_tokens)Output:
[('Books', '***NNS***'),
 ('And', 'CC'),
 ('cleverness', 'NN'),
 ('There', 'EX'),
 ('are', 'VBP'),
 ('more', 'RBR'),
 ('important', 'JJ'),
 ('thing', 'NN'),
 ('friendship', 'NN'),
 ('and', 'CC'),
 ('bravery', 'NN'),
 ('and', 'CC'),
 ('oh', 'UH'),
 ('Harry', '***NNP***'),
 ('be', 'VB'),
 ('careful', 'JJ')] ## Tagging Stemmed Tokenstext_tags_stemmed_tokens = st.tag(stemmed_tokens)
print(text_tags_stemmed_tokens)Output:
[('book', '***NN***'),
 ('and', 'CC'),
 ('clever', 'JJ'),
 ('there', 'EX'),
 ('are', 'VBP'),
 ('more', 'JJR'),
 ('import', 'NN'),
 ('thing', 'NN'),
 ('friendship', 'NN'),
 ('and', 'CC'),
 ('braveri', 'NN'),
 ('and', 'CC'),
 ('oh', 'UH'),
 ('harri', '***NNS***'),
 ('be', 'VB'),
 ('care', 'NN')]

发现突出的差异?

  • 第一个标记————带词干的标记是 NN(名词),带词干的标记是 NNS(复数名词),比较具体。
  • 第二, Harry — 错误地源于 harri ,结果词性标注者未能正确地将其识别为专有名词。另一方面,词汇化的标记正确地将哈利分类,并且词性标注者明确地将其识别为专有名词。
  • 最后, harribraveri——尽管这些词在英语词汇词典中没有任何位置,但它们应该被归类为 FW。这个我现在没有解释。

步骤 4:为令牌标记构建 POS 映射器

我首先创建了如下的 POS 映射器。该映射器用于根据树库位置标签代码到 wordnet 的参数。

**from** nltk.corpus.reader.wordnet **import** VERB, NOUN, ADJ, ADVdict_pos_map = {
    # Look for NN in the POS tag because all nouns begin with NN
    'NN': NOUN,
    # Look for VB in the POS tag because all nouns begin with VB
    'VB':VERB,
    # Look for JJ in the POS tag because all nouns begin with JJ
    'JJ' : ADJ,
    # Look for RB in the POS tag because all nouns begin with RB
    'RB':ADV  
}

步骤 5:在解决问题的同时构建规格化器

  1. 识别专有名词,跳过处理并保留大写字母
  2. 识别单词的词性标签所属的词性家族——NN、VB、JJ、RB,并传递正确的变元化参数
  3. 获取词汇化记号的词干。

这是最后的代码。我使用 st.tag_sents()来保持序列的顺序(逐句嵌套的标记)

带词干

normalized_sequence = []
**for** each_seq **in** st.tag_sents(sentences=no_punct_seq_tokens):
    normalized_tokens = []
    **for** tuples **in** each_seq:
        temp = tuples[0]
        **if** tuples[1] == "NNP" **or** tuples[1] == "NNPS":
            **continue**
        **if** tuples[1][:2] **in** dict_pos_map.keys():
            temp = lm.lemmatize(tuples[0].lower(), 
                                pos=dict_pos_map[tuples[1][:2]])
        temp = stemmer.stem(temp)
        normalized_tokens.append(temp)
    normalized_sequence.append(normalized_tokens)
normalized_sequence**Output:**
[['book'],
 ['and', 'clever'],
 ['there',
  'be',
  'more',
  'import',
  'thing',
  'friendship',
  'and',
  'braveri',
  'and',
  'oh',
  'be',
  'care']]

不带词干

normalized_sequence = []
for each_seq in st.tag_sents(sentences=no_punct_seq_tokens):
    normalized_tokens = []
    for tuples in each_seq:
        temp = tuples[0]
        if tuples[1] == "NNP" or tuples[1] == "NNPS":
            continue
        if tuples[1][:2] in dict_pos_map.keys():
            temp = lm.lemmatize(tuples[0].lower(), 
                                pos=dict_pos_map[tuples[1][:2]])
        normalized_tokens.append(temp)
    normalized_sequence.append(normalized_tokens)
normalized_sequence**Output:**[['book'],
 ['And', 'cleverness'],
 ['There',
  'be',
  'more',
  'important',
  'thing',
  'friendship',
  'and',
  'bravery',
  'and',
  'oh',
  'be',
  'careful']]

在这个过程中,我还有两个重要的不同之处,一是源于进口,二是源于勇敢。虽然“braveri”相当明确,但“import”却是一个挑战,因为它可能意味着不同的东西。这就是为什么,一般来说,词干化比词元化具有更差的性能,因为它增加了搜索词的歧义性,因此检测到实际上并不相似的相似性。

注: 这里的 是 GitHub 上完整的 jupyter 笔记本。

[1]https://NLP . Stanford . edu/IR-book/html/html edition/stemming-and-lemma tization-1 . html

用 Python 构建一个 Twitter 机器人

原文:https://towardsdatascience.com/building-a-twitter-bot-with-python-89959ef2607f?source=collection_archive---------12-----------------------

了解如何一步一步地构建机器人

你能想象拥有一个根据一周中的不同日子发布不同推文的推特机器人吗?这在 Python 和 Tweepy 中是可能的。继续阅读这个故事,你会发现如何用 Python 构建一个 Twitter 机器人。

照片由莎拉·库菲Unsplash 拍摄

建立一个 Twitter 开发账户和 Tweepy

首先,我们需要安装 tweepy 。我们可以使用 pip 命令轻松地在终端中安装它:

pip install tweepy

太好了,现在我们已经安装了 PythonTweepy 。接下来,我们需要创建一个 Twitter 开发者账户

Twitter 提供了一个开发者平台,该平台提供了一个 API 来管理和执行我们的 Twitter 帐户中的某些活动。可以看一下 Twitter API 文档。我们可以通过这个 API 执行多项任务。请看下面其中的一些:

  • 发布检索的推文
  • 关注不关注用户
  • 发布直接消息

然而,在我们能够使用 Twitter API 端点之前,我们需要创建一个开发人员帐户并生成我们的 API 密钥。可以在这里直接申请开发者账号。在回答了几个关于您计划如何使用 API 和接受 Twitter 开发者协议的问题后,您将被授予访问开发者仪表板的权限。

一旦您被授权访问仪表板,登录开发人员网站并创建您的第一个应用程序。这一步将自动生成您的消费者 API 密钥和您应该保密的访问令牌:

Twitter API 键

从 Twitter 开发平台,我们能够编辑应用程序权限。我已经授予我的应用程序读取、写入和发送直接消息的权限。

请注意,开发者帐户应该链接到您想要激活 bot 的 Twitter 帐户。例如,我使用我的 Crypto News Twitter 账户,在那里我创建了我在本文中描述的 Twitter bot。

用 Python 和 Tweepy 构建一个 Twitter 机器人

好了,让我们开始构建我们的 Twitter 机器人。如前所述,我们将使用 Tweepy 库,这将使我们的工作更加容易。

首先,我们进口 tweepy 。Tweepy 将通过我们的秘密密钥为我们管理 API 的认证。

如下面的代码摘录所示,我们通过创建一个 OAuthHandler 实例来处理我们的登录 ,通过传递消费者密钥和消费者秘密作为参数。

接下来,为了能够向 API 发出请求,我们应该发回一个访问令牌。出于这个原因,我们使用 auth.set_access_token 方法在会话期间存储访问请求令牌。

最后,我们准备好通过 Python 管理我们的 Twitter 账户。请注意,我已经包含了 XXX 而不是我真正的密钥。用你的秘密密钥替换掉 XXX ,你可以从你的 Twitter 开发者仪表板中获得。

import tweepy 
import datetime consumer_key = 'XXXX' 
consumer_secret = 'XXXX' 
access_token = 'XXX' 
access_token_secret = 'XXXX' auth = tweepy.OAuthHandler(consumer_key, consumer_secret) auth.set_access_token(access_token, access_token_secret)api = tweepy.API(auth)

我们的变量 api 是我们存储认证设置的地方。我们将使用它向 Twitter API 发出所有请求。

我们已经完成了工作的一半。接下来,我们可以使用update _ status 方法来发布推文。

这个 twitter 机器人的想法是根据一周中的日期发布不同的推文。这可以通过使用 if 语句来存储 tweet 以根据当天发布来轻松完成。让我们在代码中构建逻辑。

*import datetimedef publictweet():
    if datetime.date.today().weekday() == 0:
        tweettopublish = 'Hi everyone, today is Monday.   #Monday '
    if datetime.date.today().weekday() == 1:
        tweettopublish = 'Enjoy your Tuesday.  #Tuesday'
    if datetime.date.today().weekday() == 2:
        tweettopublish = 'Third week of the Week. #Wednesday'
    if datetime.date.today().weekday() == 3:
        tweettopublish = 'Thursday. I cannot wait for the Weekend'
    if datetime.date.today().weekday() == 4:
        tweettopublish = 'Friday...Finally'
    if datetime.date.today().weekday() == 5:
        tweettopublish = 'Great it is Saturday #weekend #Saturday'
    if datetime.date.today().weekday() == 6:
        tweettopublish = 'Sunday morning...#Weekend #enjoy ' api.update_status(tweettopublish)
   print(tweettopublish)*

我们使用datetimelibrary 和 weekday 方法来知道今天是星期几。例如,如果今天是星期日,我们运行下面的代码,我们将得到 6 作为结果:

*print(datetime.date.today().weekday())# above code will print 6 if today was Sunday, 0 if today was Monday, 1 if today was Tuesday and so on.*

因此, if 语句中只有一个将为真。因此,这将是我们将保存在变量 tweettoppublish 中的 Tweet。

最后,通过方法update _ status(tweettopublish),我们能够在我们的 Twitter 时间轴中发布推文。正如你在下面看到的,今天是星期天,因此,通过运行 publictweet 函数,API 正在定位星期天的 tweet。

*publictweet()*

用 Python 构建一个 Twitter 机器人

Python Twitter Bot 中包含的其他功能

除了发布推文,我们还可以使用 Python Twitter Bot 执行一些其他很酷的任务。例如,我们可以:

包扎

只用了几行代码,我们就成功地构建了一个令人惊叹的 Twitter 机器人,并且现在已经在我们的 Twitter 账户上运行了!

享受你的新机器人,并随时扩展它的新功能!感谢您的阅读!

原载于 2020 年 2 月 2 日 https://codingandfun.com的* *

使用 Twitter 数据构建用户分类器

原文:https://towardsdatascience.com/building-a-user-classifier-using-twitter-data-283dfd0c0e59?source=collection_archive---------31-----------------------

实践教程

使用随机森林学习方法构建分类器。用 Twitter 数据训练它。

德国巴伐利亚的森林。我拍的照片。

Twitter 平台提供了大量的数据。一个机器学习算法需要大量的数据来建立一个好的模型。因此,我们通过使用 Twitter 数据进行训练来构建 ML 预测器是一个好主意。

无论用户是否有特定的政治观点,我们都将建立一个分类器。我们使用 Twitter 推文、用户和朋友数据来构建分类器。此外,我们将使用随机森林作为我们的学习算法,使用 Python 作为我们的开发平台。

步骤:

  1. 识别和收集 Twitter 数据。
  2. 给数据贴标签。
  3. 做特色工程。
  4. 转换数据。
  5. 训练模型。
  6. 评估模型。

识别和收集 Twitter 数据

我们需要收集推文、用户和用户的朋友。我们以前的文章已经讨论了如何实现这一点。结果在关系数据库中。下图显示了数据库模式。

收集 Twitter 数据库模式。由 dbdiagram.io 构建和生成

这里我们有 Tweet 表存储所有的 Tweet(和 retweets)。标签表保存了推文中发现的标签。用户表包含所有发 tweet 或 retweet 的用户。 User_Friend 表存储每个用户和他们的朋友(他或她关注的用户)之间的关系。有 HashtagTweet 表和 TweetUser 表等表关系。我们将使用这些关系编写一个 SQL 查询来将三个表连接在一起。

稍后,我们将在下一节讨论 User_class0User_class1

给数据贴标签

我们把用户分成两类,保守的和非保守的。我们需要一种给用户贴标签的方式。我们是这样做的:

  • 首先,我们列出流行的标签。
  • 然后,我们识别这些词中哪些可能与我们感兴趣的用户类别相关,保守。
  • 接下来,我们可以在数据库中查询包含这些单词的推文。发布推文的用户会很保守。
  • 其余的用户可能是非保守的。

我们将保守派放入 User_class1 ,将非保守派放入 User_class0

我们收集了 2020 年 10 月 22 日的 Twitter 推文、标签和用户,地点是泰国。我们收到了 240 万条推文。通过使用该标准,我们发现了 3463 名保守用户和 47775 名非保守用户。在我们的数据集中,我们从班级 1 中随机选择 3000 人,从另一个班级中随机选择 5000 人。将它们混洗并分成训练和测试数据集。训练得到了 70%,测试得到了所有数据的 30%。

做特征工程

我们有一个基本的想法,即观点相似的用户可能会追随或倾听同一组影响者。对于每个用户,我们使用用户的朋友作为特征。因为一些用户可能有大量的用户(在 Twitter 上被称为朋友),我们会将功能中的朋友数量限制在某个特定的数量,如 500。

我们根据训练数据集中的追随者数量选择前 500 位朋友作为我们的特征。

转换数据

我们需要做一些工作来将数据转换成特征向量。我们从 Twitter 收到的朋友数据如下:

假设我们的数据包括 A、B 和 c。此外,最好的朋友是 W、X、Y 和 z。要转换这一点,我们需要执行以下操作:

  • 只保留我们数据集上的用户。例如,删除包含用户 D 和 e 的行。

  • 只保留朋友向量中的朋友(顶级朋友)。例如,删除包含朋友 U 和 v 的行,输出应该如下所示:

  • 做交叉列表。输出将是我们需要的特征向量。例如,如果用户 A 跟随 W、X 和 Y。用户 B 跟随 W 和 Y。用户 C 跟随 Y 和 z。特征向量应该是这样的:

训练模型

一旦我们得到了用于训练和标签输出的特征向量,训练分类器就简单了。以下是部分 Python 代码:

X_train 表示训练数据的特征向量。 y_train 是已知输出

  • 我们实例化了随机森林分类器。
  • 我们传递训练数据和已知输出来拟合模型。

就是这样。分类器准备好了。

评估模型

下一步是使用测试数据运行模型,获得预测,并将结果与已知输出进行比较。 X_test 代表测试数据的特征向量。 y_test 为已知输出。最后, y_pred 是模型预测。从结果来看:

  • ROC AUC 得分为 0.93。这很好。
  • 一级精度为 92%。这意味着在所有的模型中,92%的模型显示它们是 1 类。
  • 一级召回率为 79%。这意味着该模型可以识别 79%的第一类。
  • 准确率相当不错,达到了 89%。但这意义不大,因为数据不平衡。

特征重要性

随机森林算法也可以识别特征的相对重要性。每个值的范围从 0 到 1。所有重要度之和为 1。以下 Python 代码显示了如何从学习模型中检索要素重要性,并绘制最高值图表。

因为在我们的模型中,特征是用户的朋友,所以这张图显示了影响者的重要性。正如我们所看到的,前几个特性的值明显高于其余的,然后逐渐降低,最后接近零。

此外,只要再多一点代码,我们应该能够列出顶级用户的屏幕名称。因此,我们可以看到这些影响者的相对重要性。

特征数量

我们将功能(朋友)的数量设置为 500。我们可以使用更少或更多吗?最佳值是多少?我们有一些代码来获得下面不同数量特征的 ROC AUC 得分:

从图中可以看出, ROC AUC 分数在特征数超过 500 后没有太大的提高。所以,500 应该没问题。

结论

我们从 Twitter 上收集推文、标签、用户和用户的朋友。使用标签将用户分为两类。我们使用用户的朋友作为我们的特征。将收集的数据转换成适当的特征向量。我们使用随机森林分类器作为学习算法,并用我们的数据训练它。
测试成绩看起来不错。 ROC AUC ,精度,召回率高。这意味着我们的方法运行良好,应该能够自信地预测结果。

本文完整 python 代码此处

构建可视化搜索引擎

原文:https://towardsdatascience.com/building-a-visual-search-engine-9451580afcf7?source=collection_archive---------38-----------------------

视觉搜索作为谷歌图片或 Pinterest Lens 的一部分已经存在了一段时间。随着它在电子商务中越来越受欢迎,通过允许客户简单地上传他们正在寻找的东西,而不是通过大量的属性过滤器,帮助商家提高他们的销售额,我决定看看如何从头开始构建这样的可视化搜索引擎,然后使用 AnnDB

架构概述

让我们简单看一下,为了提供最基本的视觉搜索服务,我们需要什么。

架构图

我们的服务公开了客户端可以查询的 HTTP API。在幕后,我们需要一个模型来获得图像的矢量表示,然后可以存储在索引中,供以后查询。对于索引,我们可以采用一种简单的方法,按原样存储向量,然后对每个查询进行强力搜索。这非常简单,但是效率相当低(O(n))并且很慢,因为我们是用查询向量来计算索引中所有(潜在的高维)向量的距离。一种更好的方法是使用一种索引结构,这种索引结构将对向量空间进行划分,并且不需要比较整个索引。

模型

正如我上面提到的,我们需要一些方法将图像转换成它们的矢量表示。这些矢量表示应该具有这样的性质,即视觉上相似的图像应该最小化它们的矢量(潜在)表示相对于某个度量(欧几里得)的距离,反之亦然,视觉上不相似的图像应该最大化该度量。一种简单的方法是使用来自预训练图像分类模型的隐藏层的特征。然后,我们可以使用例如三重损失对自己的数据进行微调,或者使用自动编码器设置从头开始训练模型。
出于本文的目的,我将使用 ResNet50 模型的最后一个隐藏层的特性。

索引

强力方法不是一个好主意,因为它不能扩展到超过几千项的数据集,所以我们将使用索引。有许多指数可以选择(看看这个基准),但我们将使用骚扰,因为它有一个很好的 Python API。Annoy 是由 Erik Bernhardsson 开发的,根据回购协议,它被 Spotify 用于音乐推荐。它使用位置敏感散列法,该散列法基本上通过在每个节点处随机采样(n+1)维空间中的超平面来递归构建搜索树,然后基于向量与该超平面的(带符号)距离的符号将项目分配给左/右子节点。

HTTP API

API 是视觉搜索服务中最不令人兴奋但却非常必要的部分。我们将使用 Flask 来公开一个/search端点,该端点接受图像作为查询并返回 JSON 编码的结果集。

把所有的放在一起

第一部分是从我们的图像中建立索引。我们将加载每个图像,获取它的向量表示,并将其存储在索引中。添加完所有图片后,我们需要建立索引并保存到磁盘上。

第二部分是搜索 HTTP API 本身。如上所述,它公开了一个接受图像作为查询的端点,调用模型来获取它的向量表示,然后查询我们在第一部分中构建和保存的索引来获取潜在空间中的最近邻居。

如需完整示例,请查看此回购:https://github . com/marekgalovic/articles/tree/master/visual-search

结论

正如我们在上面描述的例子中看到的,构建一个概念验证的可视化搜索解决方案非常容易。然而,对于生产就绪的解决方案,我们需要构建更多。

  • 需要复制搜索服务,以改善负载下的延迟并确保高可用性。
  • 复制搜索服务提出了一个问题:是用它复制整个索引,还是将索引本身分成多个部分,并实现一个定制的 map-reduce 式搜索过程。
  • 使用许多开源近似最近邻库的另一个大缺点是,一旦建立了索引,就不能修改,并且任何添加、更新或删除都需要重建索引,这可能是计算密集型的并且成本高。
  • 临时操作几乎是不可能的,或者需要在选择的库和期刊索引重建的基础上进行粗略的扩展。

AnnDB 近似最近邻数据库

AnnDB 是一个分布式近似最近邻数据库,支持即席数据修改,无需重建索引。通过对数据进行内部分区,它可以扩展到多台服务器,使用 Raft 共识算法来实现复制和一致性,并使用 map-reduce like search 来实现低延迟和高吞吐量。

使用 AnnDB Cloud ,只需几行代码就可以实现生产就绪的可视化搜索解决方案。更多信息见https://anndb.comhttps://docs.anndb.com

如果你喜欢这篇文章,请把它推荐给其他人,展示一些❤。此外,如果您有任何问题,请在下面留下评论或在社交媒体上联系。
https://twitter.com/@marek_galovic
https://github.com/marekgalovic
https://marekgalovic.com

免责声明:我是 AnnDB 的作者

为学习构建易受攻击的 Rails 应用程序

原文:https://towardsdatascience.com/building-a-vulnerable-rails-application-for-learning-2a1de8cf98d5?source=collection_archive---------61-----------------------

了解损坏的 DevSecOps 进程中的安全缺陷

介绍“Hammer”:一个易受攻击的 Ruby on Rails 应用程序的开源示例

一个黑客的公寓——胡安·卡西尼的艺术作品

答任何人都可以学习做技术“黑客”,这篇文章将带你看一个你可以自己实现的开源例子。

当你想到“黑客”这个词时,它会唤起什么形象?你会想到一个技术高超、意志坚定的计算机安全对手躲在他(或她)的地下室里,试图穿透你的防御并窃取你的数据吗?在 2020 年,这个词传达了一种更加邪恶的恶意语气。现在这个术语有一系列的分类,包括“黑帽子”、“白帽子”和“灰帽子”。它们通过意图和许可来区分。然而,情况并非总是如此。黑客攻击曾经是所有美好事物的信号。有趣的是,这个术语起源于 1961 年麻省理工学院的模型铁路俱乐部。成员们黑进了他们的列车组以修改它们的功能。通过这一点,我们可以把它的意思扩展到理解黑客是如何在幕后工作的。它正在调整技术,以新的和创造性的方式使用它。我喜欢追溯这个词的原意。黑客是最纯粹最高尚的形式。本着下面这个指南的精神,你可以享受一个关于技术黑客的教程。至少,探索一个可以反映生产系统中有缺陷的安全方法的应用程序是很有趣的。请随意复制这个回购协议,并根据您自己的想法进行扩展。

我最近偶然发现了一个有一些安全缺陷的迷人的网络应用程序。每隔一段时间,你就会发现一些让你兴奋不已的事情,去学习新的东西。我不得不回去,把我所看到的分解开来,然后按照我自己的理解重新创造。现在我不是专业的软件工程师。但是我绝对喜欢学习和理解事物。这是开发人员如何通过代码或部署环境引入风险的一个很好的例子。这个项目的精神是为所有人共享一个学习应用程序。介绍Hammer——用 Ruby on Rails 编写的 web 应用程序的一个易受攻击的例子。在这篇文章中,我将带你创建锤子。你准备好了吗?让我们开始吧。但首先,概述一下什么是 锤子 其实是:

Ruby on Rails

  • 用 Ruby on Rails 编写的易受攻击的 web 应用程序。
  • 100% FOSS InfoSec 社区贡献,可在此下载。
  • 使用 Rails 搭建技术模拟 MySQL 数据库后端的生产 web 应用程序部署。
  • docker——编写自动化部署或手动构建指令。Docker 容器使用 MariaDB 数据库后端。
  • 自举管理仪表板好看的用户界面。
  • 包含使用信用卡和用户凭证部署模拟生产用户数据的 python 脚本。
  • 包含一个错误配置的 Ruby on Rails gem。
  • 隐藏在申请中的复活节彩蛋。
  • 生产配置的配置步骤,包括加密 TLS 证书
  • https://preprod.rtcfingroup.com演示锤子应用(请善待我的应用)

它既好看又脆弱!

F 或自动 Docker 构建部署,请跳至“ Docker 部署 ”部分。否则,您可以按照下面指定的这些手动构建步骤。注意,这是在 Ubuntu Linux 18.04 上构建和测试的。

手动构建步骤(Ubuntu Linux 18.04)

Sstep 1:安装 MySQL。这些步骤在 Ubuntu Linux 18.04 上得到了验证。

*$sudo apt update$sudo apt install mysql-server mysql-client libmysqlclient-dev -y*

运行MySQL _ secure _ installation脚本。

*$sudo mysql_secure_installation*

下面是一个指南,展示了我在创建这个应用程序时的脚本选择。请随意选择您认为最适合自己的安全要求。因为这是一个故意不安全的应用程序,所以应该仔细考虑需要更好安全性的其他部署类型:

  1. 验证密码插件?
  2. 新密码? <输入密码>
  3. 删除匿名用户?
  4. 不允许远程 root 登录?
  5. 远程测试数据库?
  6. 重新加载权限表?

安装 Ruby 版本管理器

*$sudo apt-get install software-properties-common -y$sudo apt-add-repository -y ppa:rael-gc/rvm$sudo apt-get update$sudo apt-get install rvm -y*

创建一个新用户,并将他们添加到 rvm 组。

*$useradd -m -s /bin/bash -G rvm <username>*

通过/etc/sudoers 委派权限。编辑/etc/sudoers 中的行,为该用户添加权限。为了便于更改,您可以简单地镜像与 root 用户相同的权限。

*# User privilege specificationroot ALL=(ALL:ALL) ALL<username> ALL=(ALL:ALL) ALL*

更改用户密码(duh)

*sudo passwd <username>*

以该用户身份登录,并在以该用户身份登录时执行剩余的步骤。

使用 rvm 安装 Ruby 2.6.3(以新用户身份登录)

*$rvm install 2.6.3*

S 步骤 4: 克隆 hammer repo 并运行“捆绑安装”

*$git clone [https://github.com/iknowjason/hammer](https://github.com/iknowjason/hammer)
$cd hammer
$bundle install*

S 第五步:编辑config/database . yml中的数据库配置文件。

更改的密码字段中的密码,以匹配您在步骤 1 中为 root 密码指定的密码。

运行 mysql 客户端连接 MySQL,更改权限。在<密码>中指定您在上面和步骤 1 中输入的相同密码。

*$sudo mysqlmysql> ALTER USER ‘root’@’localhost’ IDENTIFIED WITH mysql_native_password BY ‘<PASSWORD>’;mysql> exit*

验证您现在可以作为 root 用户进行身份验证。在提示符下输入您的 root 密码:

*$mysql -u root -pmysql> exit*

用 rails 应用程序数据填充 MySQL 数据库。

*$cd /home/<username>/hammer$rake db:create$rake db:migrate*

安装最新的节点。

*$curl -sL [https://deb.nodesource.com/setup](https://deb.nodesource.com/setup)_10.x -o nodesource_setup.sh$sudo bash nodesource_setup.sh$sudo apt install nodejs -y*

安装最新的 yarn,然后为这个应用程序实例重新构建页面。

安装最新纱线:

*$curl -sS [https://dl.yarnpkg.com/debian/pubkey.gpg](https://dl.yarnpkg.com/debian/pubkey.gpg) | sudo apt-key add -$echo 'deb [https://dl.yarnpkg.com/debian/](https://dl.yarnpkg.com/debian/) stable main' | sudo tee /etc/apt/sources.list.d/yarn.list$sudo apt update && sudo apt install yarn -y*

运行 rake 命令编译页面:

*$bundle exec rake assets:precompile$bundle exec rake webpacker:compile*

S步骤 9: 在默认端口 3000 上启动应用程序

*$rails s -b 0.0.0.0*

通过使用浏览器检查默认页面来验证它是否正常工作— URL 为http://:3000

确认页面没有任何错误。(表格将不显示数据)

http://:3000/用户

http://:3000/信用卡

您应该会在默认页面上看到:

默认页面

您应该会在用户页面上看到:

用户页面

S 步骤 10: 用提供的 Python 脚本添加模拟用户和敏感信用卡数据。首先,通过输入< CTRL-C >来中断应用。

安装 python 模块:

*$sudo apt-get install python3-pip -y$sudo python3 -m pip install Faker$sudo pip3 install pymysql*

在应用程序的工作目录中,切换到 python_scripts 目录。

*$cd /home/<username>/hammer/python_scripts*

编辑 gen_users.pygen _ credit data . py脚本。更改以下变量以匹配您在步骤 5 中指定的 MySQL DB 实例:

数据库用户名

数据库密码

数据库名称

注意:如果使用了config/database . yml中的默认值,那么 DB_NAME 应该是rack vuln _ development

修改 gen_creditdata.py 脚本的示例

运行两个脚本:

*$./gen_users.py$./gen_creditdata.py*

变回 rails 应用程序的工作目录。再次启动应用程序。

*$cd /home/<username>/hammer$rails s -b 0.0.0.0*

用你的浏览器验证你可以访问/用户* 和/信用卡 的网址,这次他们渲染了一些数据!*

http://:3000/用户

http://:3000/信用卡

用户页面现在的样子。

S 为了获得更好的产品体验,您可以使用一个根 CA 颁发的 TLS 证书来提供应用程序,该证书托管在使用 Nginx 反向代理的域中。只有当您有域名和 DNS 记录解析到您的站点时,才能继续前进。为此,您可以使用 cloud Linux VPS 提供商(如 Digital Ocean 或 Linode)轻松创建一个新主机。

Sstep 12:安装 Nginx 用于反向代理和 TLS 隧道终止。

*$sudo apt install nginx -y*

安装让我们加密证书机器人

*$sudo add-apt-repository universe$sudo add-apt-repository ppa:certbot/certbot$sudo apt-get update$sudo apt-get install certbot python-certbot-nginx -y*

SS步骤 14 :运行 certbot 创建 TLS 证书

*$sudo certbot --nginx*

回答问题:

您的电子邮件地址:

同意服务条款:

愿意分享您的电子邮件:

输入域名:<host.domain.com></host.domain.com>

注意:上面这个步骤很重要。确保 certbot 能够自动部署 TLS 证书。

重定向:(我总是选择“2”来启用重定向)

重新启动并验证 nginx

*$sudo service nginx restart$sudo service nginx status*

S 步骤 16: 编辑 nginx 配置,配置 Nginx 反向代理。这将配置 Nginx 将 TLS 终止的流量转发到运行在本地主机上的 Rails Puma 应用服务器。下面的配置应该与您的特定环境相匹配。

编辑/etc/nginx/sites-可用/默认**

替换 server_nameroot 指令,以匹配您的 FQDN 和您的 rails 应用程序的目录。更换主机返回 301 参数,以匹配您的 FQDN。下面的例子可以作为一个模板,但需要根据您的环境进行修改。

*upstream app { server 127.0.0.1:3000;}server { server_name prod.rtcfingroup.com; root /home/rails/hammer/public; try_files $uri [@app](http://twitter.com/app); location [@app](http://twitter.com/app) { proxy_pass [http://app](http://app);
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header Host $http_host;
        proxy_redirect off;
    } error_page 500 502 503 504 /500.html;
    client_max_body_size 4G;
    keepalive_timeout 10; listen 443 ssl; if ($scheme != "https") {
        return 301 [https://$host$request_uri](/$host$request_uri);
    } ssl_certificate <PATH>
    ssl_certificate_key <PATH>
    include <PATH>
    ssl_dhparam <PATH>}server {
    if ($host = prod.rtcfingroup.com) {
        return 301 [https://$host$request_uri](/$host$request_uri);
    }     listen 80;
    server_name prod.rtcfingroup.com return 404; }*

保存文件,重新启动 nginx 服务,并验证它正在运行

*$sudo service nginx restart$sudo service nginx status*

S tep 18: 启动 rails app 监听本地主机,端口 3000。

*$cd /home/<username>/hammer$rails s*

S tep 19: 浏览你的新 FQDN(即 https://host.domain.com)验证一切正常。 Nginx 应该终止 TLS 连接,并重定向到监听端口 3000 的 Rails Puma 应用服务器!

现在,您的 Rails 应用程序使用了 Nginx 反向代理!

Docker 部署步骤

获取 docker-compose

*$sudo apt-get update
$sudo apt-get install docker-compose -y*

S 步骤 2: 使用 git 获取回购

*$git clone [https://github.com/iknowjason/hammer.git](https://github.com/iknowjason/hammer.git)*

S 步骤 3: 运行 docker-compose 命令来构建并启动容器

*$cd hammer$sudo docker-compose build$sudo docker-compose up*

如果第一次尝试出现数据库错误,再次运行 docker-compose up。

*$sudo docker-compose up*

使用浏览器导航到您主机上的端口 3000。

http:// :3000

我希望您喜欢构建这个脆弱的演示应用程序。在这个易受攻击的应用程序中,有许多有价值的经验教训可以借鉴。我欢迎任何评论、建议和合作。如果您有改进它的想法,或者是有想法的 Rails 开发人员,请联系我。今后还会有更多的经验教训。

使用 Openweathermap 和 Flask 构建天气应用程序

原文:https://towardsdatascience.com/building-a-weather-app-using-openweathermap-and-flask-ed7402239d83?source=collection_archive---------10-----------------------

了解如何使用 API,并使用 Python 和 Flask 构建一个简单的应用程序

jaros aw kwoczaa 在 Unsplash 上拍摄的照片

在本文中,我们将学习使用 web API 的基础知识。我们将使用 Python 和 Flask 创建一个简单的后端,调用 Openweathermap API ,并在网页中呈现结果。

如果你想跳过这篇文章并访问代码,请前往 GitHub repo

获取 API 密钥

https://home.openweathermap.org/users/sign_up 注册一个免费计划,如果你还没有的话。然后访问 API 密钥部分https://home.openweathermap.org/api_keys获取您的密钥。您可以使用默认密钥,也可以根据需要生成一个新密钥。您可能需要等待一段时间来验证您的密钥。

Openweather 仪表板

安装要求

我在这个项目中使用 Python 3.6,所以要确保你已经安装了 3.6 或更高版本。除此之外,我们将只需要用于 web 的 Flask 和用于进行 API 调用的 requests 。创建一个名为 requirements.txt 的新文件,并添加依赖项。您的文件应该如下所示:

Flask==1.1.1
requests==2.22.0

现在使用下面的命令来安装需求:

pip install -r requirements.txt

或者如果你只是想安装 Flask 和请求

pip install Flask requests

如果 pip 被 Python 2.7 占用,linux 和 mac 可能需要使用 pip3 而不是 pip 和 sudo。

进行 API 调用

浏览文档,最简单的请求方式是使用城市名。发出请求的 url 将如下所示

[http://api.openweathermap.org/data/2.5/weather?q=city_name&APPID=](http://api.openweathermap.org/data/2.5/weather?q={city}&APPID={API_KEY)your_api_key

您可以复制粘贴 url 并用适当的值替换 city_nameyour_api_key 来查看结果。

来自 Openweather API 的示例响应

如果你想看到一个格式化的 JSON 响应,你可以使用一个叫做 JSONView 的 chrome 扩展。在浏览器中,您的响应将如下所示。

使用 JSONView 格式化 JSON 响应

所有显示的数据都非常直观。温度显示在键内。名称id 都可以用来查询 API。

创建烧瓶应用程序

Flask 是一个简单、轻量级的 web 框架。它可以用来创建最小的 web 应用程序和 API。创建一个名为 weather_app.py 的文件并初始化我们的 Flask 应用程序。

在上面的代码片段中,我们导入了 Flask 并创建了它的一个实例。 @app.route() 用于创建路线。这里我们为主页创建一个,主要是在浏览器的 h1 标签中呈现“欢迎使用天气应用程序”。我们稍后将添加更多路线。在 if 块中,我们使用 debug=True 运行应用程序,这告诉 Flask 应用程序正在调试模式下运行。在调试模式下运行的好处是,如果我们在应用程序中进行更改,Flask 会自动检测这些更改,并为我们重新加载服务器。进入生产时,确保将此设置为

通过在终端/cmd 中键入以下命令来运行服务器

python weather_app.py

如果你同时安装了 python 2 和 3(通常在 linux 和 mac 上),你将不得不使用 python3 而不是 python

python3 weather_app.py

你会看到类似这样的东西

* Debug mode: on
* Running on [http://127.0.0.1:5000/](http://127.0.0.1:5000/) (Press CTRL+C to quit)
* Restarting with stat
* Debugger is active!
* Debugger PIN: 123-456-789

访问 http://127.0.0.1:5000/ 你会在 h1 看到“欢迎使用天气 app”。要退出服务器,按下 CTRL + C

添加搜索路线

现在,我们需要一种方法来输入一个城市名称并获取该城市的当前天气。为此,我们将不得不创建另一条路由,让我们称之为 /city ,它将城市的名称作为参数,并返回温度。

现在要测试 API,请访问 url

http://127 . 0 . 0 . 1:5000/城市?q =加德满都

您将在浏览器中看到类似这样的响应:

Current temperature of Kathmandu is 15.0 ℃

我们在 url 中作为查询传递的任何内容都将作为字典存储在 request.args 中。这里, q=kathmandu 是查询字符串,因此 request.args 将保存 {"q": "kathmandu"} 。然后我们使用 get 方法通过键访问字典。你可以在这篇教程中了解更多关于字典的知识。获取当前温度时也是如此。

current_temperature = response.get('main', {}).get('temp')

你可以把加德满都换成你想要的城市。城市列表和 id 可以从这个链接下载。

最终的 weather_app.py 文件应该如下所示。

至此,我们已经创建了一个简单的应用程序,它可以获取某个城市的当前温度。这篇文章应该让你了解 Flask 是如何工作的,以及如何在 Python 中使用 API。你可以从我的 GitHub repo 获取代码。

接下来呢?

从这里开始,您可以扩展功能并向应用程序添加更多功能,例如

  1. 添加搜索按钮
  2. 使用模板和一个好看的界面
  3. 显示城市列表和各自的 id
  4. 按 id 和名称查询
  5. 显示其他数据,如最高、最低温度、湿度、风力等

如果您在学习本教程时有任何问题,或者需要帮助添加更多功能,请告诉我。

构建 Web 应用程序来部署机器学习模型

原文:https://towardsdatascience.com/building-a-web-application-to-deploy-machine-learning-models-e224269c1331?source=collection_archive---------0-----------------------

所以我们已经建立了我们的 ML 模型——现在呢?如何使用 Flask 离开 Jupyter 笔记本并进入 Web 应用程序!

如果你已经学习深度学习有一段时间了,你可能已经用你的 Jupyter 笔记本做了一些非常酷的模型,从图像识别到语言翻译。你可能想让你的朋友试用你的模型(或者甚至可能以此成立一家公司),但是送他们一台 Jupyter 笔记本并不是你真正想要的。你如何构建一个 Web 应用来部署你的机器学习模型?

这篇文章的重点是围绕我们的机器学习模型构建一个 web 应用程序,供其他人尝试。我们将学习一些 Web 编程技术,如 HTML 和 Flask,以及在 DigitalOcean 的 Ubuntu 服务器上部署它。

本教程结束时,其他人(来自世界各地)将能够尝试您的简单图像识别软件。您的 web 应用程序将类似于我构建的这个页面:

[## 给自己的形象分类!

157.230.156.140](http://157.230.156.140/)

这是网络应用的首页:

一旦你选择了一个文件(请选择一个小于 500kb 的文件),你可以点击“上传”。假设我选择了之前在笔记本中使用的猫图像,我将被重定向到包含我的预测的页面:

预测图像中的内容

为了快速上手,我们将跳过一些最佳实践;但是希望,我们会在后面的帖子中回到这个话题!

先决条件:

这篇文章假设你熟悉构建自己的图像识别模型;您可以参考此处的指南来加快速度:

[## 建立你的第一个卷积神经网络来识别图像

一步一步的指南,建立自己的图像识别软件与卷积神经网络使用 Keras 上…

medium.com](https://medium.com/intuitive-deep-learning/build-your-first-convolutional-neural-network-to-recognize-images-84b9c78fe0ce)

一旦你完成了上面的教程,你应该有一个. h5 文件和一个模型来将图像分为 10 个不同的类别(飞机、汽车、鸟、猫、鹿、狗、青蛙、马、船和卡车)。如果你有模型文件,我们可以开始围绕它创建一个 Web 应用程序!

如果你想在任何时候看到我们在这篇文章中将要做的事情的完整代码,请点击这里查看 GitHub。

在数字海洋上设置您的 Web 服务器

【注意:由于本部分依赖于第三方服务 DigitalOcean,说明可能会随着时间的推移而变化】

这篇文章的目的是教你如何在 Web 上部署一个简单的 Web 应用程序供他人尝试。为此,我们需要一个 Web 服务器,它就像一台计算机,可以向其他请求信息的计算机发送信息(网页)。我们将在 DigitalOcean 上建立一个 Web 服务器,您可以在这里使用我的推荐链接(或者直接访问网站):

[## 数字海洋—云计算,大规模简化

为开发者和企业提供一个可靠、易用的虚拟服务器云计算平台…

m.do.co](https://m.do.co/c/e2093d3a5967)

在该链接中,在右侧面板上创建一个帐户:

键入您的电子邮件地址和密码,然后点击绿色的“创建您的帐户”按钮

然后,DigitalOcean 将引导您完成创建帐户的步骤:

如果您使用了推荐链接,输入电子邮件地址和密码后,欢迎页面

点击确认邮件中的链接后,您需要输入您的账单信息。输入您的账单信息后,您将进入此页面创建您的第一个项目:

键入项目名称和项目用途后,您可以向下滚动到底部,忽略其他问题。单击底部漂亮的蓝色开始横幅:

然后,您应该会到达这个登录页面:

数字海洋的登录页面

我们需要的网络服务器在数字海洋中被称为“水滴”。将鼠标悬停在标题“创建一个液滴”下方的“加号”上,您会看到:

现在点击蓝色的“创建水滴”按钮。您应该会看到这个页面:

默认的(上面突出显示的)是 Ubuntu,这也是本教程的基础。

如果你向下滚动选择一个计划,你可以选择你希望你的网络服务器有多强大。您选择的方案越昂贵,您就可以处理更大的图像,并且处理速度更快。我建议选择 10 美元/月的,因为我们需要一些内存来处理图像。然而,5 美元/月的价格应该足够试用本教程了:

如果您继续向下滚动,还可以选择您希望服务器位于哪个物理数据中心。我选择了 SF(2):

您可以暂时忽略此页面上的其他选项。向下滚动到最底部,给你的 droplet 命名,然后点击绿色的“创建”按钮。

你的 droplet 需要一点时间来初始化,但是一旦你下了,你应该看到这是你的首页:

请注意,有四个数字(157.230.156.140)将是我们的 droplet 的 IP 地址。一个 IP 地址就像任何网址一样(比如www.intuitivedeeplearning.com)),所以你可以把它输入到地址栏中。如果你想给它一个域名,这样其他人就可以在地址栏中输入英文单词,而不是一系列的四个数字,你必须购买它并单独设置。

此时,您会收到一封类似这样的电子邮件:

来自数字海洋的电子邮件

复制您的密码,然后返回数字海洋控制台。如果你点击水滴边上的三个点,会出现一个弹出窗口。

当你点击水滴边上的三个点时弹出

点击“访问控制台”。这将打开一个窗口,允许您与您的 Web 服务器通信。

控制台屏幕

现在,输入你电子邮件中的用户名和密码。用户名应该是“root”。密码不会显示,但是如果您单击 Cntrl-V 并输入,您应该能够访问。系统会提示您更改密码。输入您当前的密码(Cntrl-V,Enter ),然后键入新密码两次。

键入您的 droplet 的新密码

完成后,您应该会看到这样的屏幕:

现在不要被控制台吓倒。控制台看起来很吓人,但它确实是一种通过键入非常具体的命令来与计算机对话的方式。例如,通过键入以下命令,尝试询问您的服务器使用的是哪个 Python 3 版本

python3 --version

然后点击回车。您应该会看到类似这样的内容:

然后计算机会告诉你你的服务器使用的 Python 版本,对我来说现在是 Python 3.6.7。

现在,是时候创建你的第一个 HTML 文件了。HTML 是一种网络浏览器能理解的语言,它会把 HTML 代码变成你现在正在看的网页。为了创建我们的文件,我们将需要使用类似文本编辑器的东西,如记事本,但在您的服务器上。在本教程中,我们将使用一个名为 nano 的编辑器。

我们的第一个网页将被称为 index.html,这是你网站的首页。现在,向您的服务器键入以下命令:

*nano index.html*

在控制台中,它看起来像这样:

这个命令意味着用编辑器 nano 打开文件【index.html。由于index.html不存在,它会为你创建文件。现在,您应该会在屏幕上看到类似这样的内容:**

正如底部所示,这是一个新文件,你可以在里面输入东西。你可以在这个类似记事本的应用程序中键入任何句子,例如:

**This is my first Webpage!**

您的控制台将如下所示:

现在,我们将通过单击 Cntrl-X 退出该记事本。在底部,他们将询问您是否希望保存您的文件:

您必须保存您的更改,因此请键入“Y”。现在,他们会问你你的文件名是什么:

只需点击键盘上的回车键,就大功告成了!现在,这可能看起来比你的标准记事本不太用户友好,但请耐心等待一段时间。

现在你已经在你的网络服务器上有了一个 HTML 文件,你需要做的就是“打开”你的网络服务器。为此,请键入以下内容:

**python3 -m http.server 80**

“80”是服务 HTTP 文件的标准端口。您的控制台现在应该看起来像这样:

现在,你的网站上线了!转到您的 droplet 的 IP 地址,这是您在 DigitalOcean 中看到的 droplet 旁边的四个数字。你应该可以看到你的第一个网页,这是你刚才通过 nano 编辑器在index.html中写的内容。

祝贺您,您已经在 DigitalOcean 上安装了您的第一台 Web 服务器!要停止网页并返回输入命令,只需按 Cntrl-C。现在,我们准备在我们的 Web 服务器上构建我们的第一个机器学习 Web 应用程序!

使用 Flask 创建 Web 应用后端

您的 Web 应用程序可以被视为两个部分——后端和前端。将“后端”应用程序视为操作背后的大脑——您可能看不到它,但计算都在这里完成。在这里,我们定义了用户如何与您的 web 应用程序交互的逻辑,以及用户的输入会发生什么。这也是我们运行机器学习模型的地方。另一方面,“前端”定义了用户看到、触摸和体验的内容。这就像是你的 Web 应用程序的外观,也是你的用户直接与之交互的部分。

在这一节中,我们将定义后端——我们将首先创建“大脑”,然后我们将看到如何适应“脸”。特别是,我们将:

  1. 加载我们的机器学习模型;
  2. 定义当他在主主页上传照片时会发生什么;和
  3. 将我们的机器学习模型应用于图像,并在单独的“预测”页面中向用户显示结果。

在下一节中,我们将定义用户如何查看两个网页——主页和“预测”页面。但是现在,让我们关注后端,我们将使用 Flask 作为构建它的框架。

Flask 是一个简单的 web 应用程序框架,我们可以用它来构建 web 应用程序的后端并快速入门。在本教程中,我将充分解释如何围绕您的机器学习模型创建 web 应用程序。不幸的是,我不会解释 Flask 代码和结构的每一行;你可以在这里了解更多关于烧瓶的信息。

首先,让我们安装烧瓶。为此,我们首先必须安装 pip3,这有助于我们在服务器上安装其他 Python 包,如 Flask。在您的控制台中,键入

**apt install python3-pip**

您的控制台应该如下所示:

一旦你点击回车,它会告诉你将要安装的软件包。键入“Y”继续。现在已经安装了 pip3,在控制台中输入命令:

**pip3 install Flask**

您的控制台应该如下所示:

安装烧瓶的命令

成功的安装是什么样子的

当我们安装 Flask 时,请确保安装我们之前在服务器上安装的所有 Python 包:

**pip3 install keras
pip3 install tensorflow
pip3 install scikit-image**

现在,我们将在服务器中组织我们的文件。我们将为我们的 web 后端创建一个 Python 文件,我们称之为imgrec_webapp.py(像前面一样使用nano)。然后我们会有两个文件夹,一个存储上传的图片(我们称之为uploads),另一个存储你的前端的 HTML 文件(称为templates)。要创建一个文件夹,输入mkdir folder_name到控制台,在那里“文件夹名”可以被你想要的名字代替。因此,需要键入的两个命令是:

**mkdir uploads
mkdir templates**

现在,我们需要将我们的模型my_cifar10_model.h5上传到服务器。由于模型在我的本地计算机中,不幸的是,我们不能使用控制台从我的桌面上传模型。相反,我们将使用 Windows PowerShell。如果您使用的是 Windows,您可能已经安装了这个应用程序:

打开 Windows Powershell 后,键入以下命令以上传您的模型,并确保替换三角括号中的说明:

**scp <where your model is stored>\my_cifar10_model.h5 <Droplet IP address>:~**

在我的例子中,我会键入:

**scp C:\Users\user\Desktop\my_cifar10_model.h5 root@157.230.156.140:~**

如果是第一次从 Powershell 连接,会出现这样的提示。只需点击“是”。

键入您的密码,一旦完成,它应该看起来像这样:

现在你的模型在水滴上了!现在,回到你之前访问服务器的数字海洋控制台。或者,如果您喜欢 PowerShell 界面,您可以使用 PowerShell 连接到您的服务器,如下所示:

**ssh root@<your Droplet IP>**

对我来说,应该是:

**ssh root@157.230.156.140**

瞧啊。您不再需要去 DigitalOcean 获取您的服务器控制台。一旦你进入你的控制台,输入ls(代表列表)以确保你已经得到了所有的文件和文件夹:

如果您使用 Powershell,应该看到什么

如果使用 DigitalOcean 控制台,您应该看到什么

现在我们已经设置好了一切,我们可以(最终)开始编写我们的后端应用程序了。打开你创建的 Python 文件,imgrec_webapp.py。像往常一样,我们将导入一些您稍后需要的必要函数:

**import os
from flask import Flask, request, redirect, url_for, render_template
from werkzeug.utils import secure_filename**

然后,我们创建一个 Flask 应用程序的实例,它只有一行代码:

**app = Flask(__name__)**

我们稍后将在 Flask 应用程序中添加更多内容。现在,让我们加载我们已经保存在先决教程 my_cifar10_model.h5中的机器学习模型。我们调用以下函数,并像这样加载模型:

**from keras.models import load_model
from keras.backend import set_session
from skimage.transform import resize
import matplotlib.pyplot as plt
import tensorflow as tf
import numpy as npprint("Loading model")global sess
sess = tf.Session()
set_session(sess)
global model
model = load_model('my_cifar10_model.h5')
global graph
graph = tf.get_default_graph()**

现在我们已经运行了模型,让我们添加到 Flask web 应用程序中。我们要做的第一件事是声明当 web 应用程序到达某个 URL 时会做什么。为了定义它,我们使用了@app.route()函数。对于我的主页,我将使用默认的 URL(即我的 URL 不会有任何附加的“后缀”),所以我的第一个参数将是'/'。用户将以两种方式与我的主页进行交互:

  1. 将网页从服务器加载到他的浏览器中
  2. 上传他的图像并发送到服务器

这需要两种不同的与页面交互的方法:交互#1 将使用“GET”(一种主要供用户从资源请求数据的方法),而交互#2 将使用“POST”(一种主要供用户向服务器发送数据以创建或更新资源的方法)。因此,下面的代码块表示,如果我在交互#2,将文件保存在我的“uploads”文件夹中,并转到“prediction”页面(并传入参数 filename)。如果没有,我在交互#1,只是渲染网页“index.html”。

**[@app](http://twitter.com/app).route('/', methods=['GET', 'POST'])
def main_page():
    if request.method == 'POST':
        file = request.files['file']
        filename = secure_filename(file.filename)
        file.save(os.path.join('uploads', filename))
        return redirect(url_for('prediction', filename=filename))
    return render_template('index.html')**

现在,我们必须创建一个“预测”页面,并定义当用户在该页面时 Web 应用程序应该做什么。我们再次使用@app.route()函数,假设用户位于/prediction/<filename>子目录,将<filename>传递给我为该页面定义的 Python 函数。

在此预测页面中,我将:

  1. 根据文件名读取图像,并存储为my_image
  2. 将图像大小调整为 32x32x3,这是我的模型读取它的格式,并将其存储为my_image_re
  3. 使用该模型,预测图像落入不同类别的概率,并将其放入变量probabilities
  4. 找到前三个最可能类的标签和概率,并将其放在predictions
  5. 加载我的网页predict.html的模板,给他们第四步的预测。

这五个步骤反映如下:

**[@app](http://twitter.com/app).route('/prediction/<filename>')
def prediction(filename):
    #Step 1
    my_image = plt.imread(os.path.join('uploads', filename))
    #Step 2
    my_image_re = resize(my_image, (32,32,3))

    #Step 3
    with graph.as_default():
      set_session(sess)
      probabilities = model.predict(np.array( [my_image_re,] ))[0,:]
      print(probabilities)#Step 4
      number_to_class = ['airplane', 'automobile', 'bird', 'cat', 'deer', 'dog', 'frog', 'horse', 'ship', 'truck']
      index = np.argsort(probabilities)
      predictions = {
        "class1":number_to_class[index[9]],
        "class2":number_to_class[index[8]],
        "class3":number_to_class[index[7]],
        "prob1":probabilities[index[9]],
        "prob2":probabilities[index[8]],
        "prob3":probabilities[index[7]],
      }#Step 5
    return render_template('predict.html', predictions=predictions)**

所有这些应该都很熟悉,如果您需要复习,请访问教程:

** [## 建立你的第一个卷积神经网络来识别图像

一步一步的指南,建立自己的图像识别软件与卷积神经网络使用 Keras 上…

medium.com](https://medium.com/intuitive-deep-learning/build-your-first-convolutional-neural-network-to-recognize-images-84b9c78fe0ce)

最后,我们使用端口 80 运行应用程序,这是访问网页的标准端口。在 Python 文件的末尾键入以下内容:

app.run(host='0.0.0.0', port=80)

现在我们有了 Web 应用程序的主干。你可能已经注意到我们仍然缺少一些不同的部分。特别是,我们已经调用了 HTML 网页,比如index.htmlpredict.html,但是我们还没有构建它们!我们将在下一节中这样做。

还要注意,在这个基本教程中,我们跳过了许多好的实践。如果你知道怎么做(或者谷歌一下),你可能想尝试结合这些实践。):

  • 在 Anaconda 环境中创建我们的 Web 应用程序(就像我们之前在 Anaconda Navigator 中所做的那样)
  • 捕捉错误,比如有人没有上传任何文件,或者文件名为空
  • 正在检查人们正在上载的链接扩展。jpg '文件**

使用 HTML 创建 Web 应用程序前端

当用户与您的 web 应用程序进行交互时,他们是通过 web 页面进行交互的,这也是我们将在本节中构建的内容。在控制台中键入以下命令,进入templates文件夹:

**cd templates**

创建我们需要的两个模板文件,index.htmlpredict.html。如果您键入ls,您应该会在templates下看到这两个文件:

我们使用一种叫做超文本标记语言(HTML)的语言,它只是告诉浏览器如何呈现你所拥有的内容。概括一点来说,我们用“标题”或“段落”这样的标签来标记内容,然后告诉浏览器如何显示带有这些类型标签的内容。例如,如果我想将单词“Hello”标记为“header 1”类型,我将编写:

**<h1>Hello</h1>**

通常,当您开始标记(即<h1>)时,您也必须关闭标记以显示其结束位置(即</h1>)。我们边走边学,那就从index.html开始吧。为了提醒您该页面的外观,我们尝试构建如下内容:

我们从这一行开始index.html:

**<!doctype html>**

这告诉浏览器预期的文档类型(即 HTML)。然后我们用一个<html>标签开始文档,我们将在最后关闭它。现在,我们还定义了文档头,它包含了文档的“元数据”。在这里,我定义了标题并链接了一个引导样式表,其他人已经为各种标签定义了样式。因此,我可以把大量的样式留给别人为我们编码的东西,除非有一些方面我需要定制。

**<html>
<head>
    <title>Classify your Image!</title>
    <link rel="stylesheet" href="[https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css](https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css)" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
</head>**

现在,我们定义身体:

**<body style="background-color:black"><div id="maintext" style="position:absolute; margin:auto; width:700px; height:200px; text-align:center; top:0; bottom: 0; left: 0; right: 0;">  
  <h1 class="text-light mb-2"> Upload your image to be classified! </h1>
  <h4 class="text-light mb-5">(Please upload images less than 500kb in size)</h4>
  <form method=post enctype=multipart/form-data>
     <p class="text-light">
        <input type=file name=file>
        <input type=submit value=Upload>
     </p>
  </form>
</div></body>
</html>**

首先,我告诉 HTML 我想要黑色的背景颜色。然后,我有一个div容器,这样我就可以一起包含下面的内容。我希望容器位于页面的中间,具有一定的高度、宽度和边距,所以我在样式中定义了这一点。在我的div容器中,我有三个主要的内容块:

  1. 大标题(h1)写着“上传你的图片进行分类!”(注意:mb-2在样式里只是说在底部加一个“中等大小”的页边距)
  2. 小标题(h4)写着“(请上传小于 500kb 的图片)”
  3. 一个“表单”,允许我上传文件,然后提交该表单。我们使用 POST 方法提交表单(还记得我们是如何定义后端的吗?)

一旦我在我的div容器中定义了这三个内容片段,我就关闭了 body 和 html 标签,我就完成了!请注意,将您的代码重新组织成 CSS 文件和 HTML 文件是一个很好的做法,这样我们就可以将样式与内容分开——出于本教程的目的,我们采用了一些快捷方式!

现在,我们转到predict.html页面。记住页面应该是什么样子的:

对于 HTML 代码,头部仍然与index.html相同,但是在主体中引入了一些新元素:

**<!doctype html>
<html>
<head>
    <title>Your Prediction!</title>
    <link rel="stylesheet" href="[https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css](https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css)" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
</head><body style="background-color:black"><div id="maintext" style="position:absolute; margin:auto; width:700px; height:300px; text-align:center; top:0; bottom: 0; left: 0; right: 0;">
  <h1 class="text-light mb-5"> Your Prediction </h1>
  <p class="text-light"> My algorithm gave this prediction: </p>
  <table class="table-bordered text-light" style="width:100%">
  <tr>
    <th>Rank</th>
    <th>Class</th>
    <th>Probability</th>
  </tr>
  <tr>
    <td>Most Likely:</td>
    <td>{{ predictions.class1 }}</td>
    <td>{{ predictions.prob1 }}</td>
  </tr>
  <tr>
    <td>2nd Most Likely:</td>
    <td>{{ predictions.class2 }}</td>                                                                                                                                       <td>{{ predictions.prob2 }}</td>                                                                                                                                      </tr>                                                                                                                                                                   <tr>
    <td>3rd Most Likely:</td>
    <td>{{ predictions.class3 }}</td>
    <td>{{ predictions.prob3 }}</td>
  </tr>
  </table><a href="{{ url_for('main_page') }}"><button class="button mt-3">Try again?</button></a></div>
</body>
</html>**

在我的 div 容器(与index.html中相同)中,我现在有以下内容:

  1. 标题(h1)写着“你的预测”
  2. 一段(p)说“我的算法给出了这个预测:”
  3. 三列四行的表格。表格中的每一行都标有<tr>。对于每一行,标题用<th>标记,每一列的文本用<td>标记。请注意,我的预测内容不是固定的——它取决于我的模型给我什么,这取决于用户上传了什么。这是我需要与后端交互的地方。如果你还记得,在我的预测后端,函数的最后一行是return render_template(‘predict.html’, predictions=predictions)。这意味着当我渲染“predict.html”时,我将变量predictions传递给我的前端,这些变量将出现在我的前端,无论我在哪里使用双花括号{{ ... }}调用它们。因此,{{ predictions.class1 }}将自动填充模型预测为最可能的类别。
  4. 一个将我链接回主页的按钮。我使用{{ url_for(‘main_page’) }}是因为main_page是我的后端中定义主页的函数的名称。因此,无论 URL 是什么,都会在这里自动填充。

我们做到了!同样,把你的代码重新组织成 CSS 文件和 HTML 文件是一个好的做法,这样我们就可以把风格和内容分开。这样,例如,如果我们想要一致地改变 div 的样式,我们不需要同时使用index.htmlpredict.html来改变它。

运行您的 Web 应用程序!

现在,剩下要做的就是运行 Web 应用程序。回到控制台,键入以下内容:

**python3 imgrec_webapp.py**

你会看到一大堆警告信息,但是你可以忽略它们。最重要的是,在最后,你会看到这个屏幕:

现在,您可以在浏览器中输入 droplet 的 IP 地址,然后点按“回车”。

将此替换为您的 droplet 的 IP 地址

您应该看到您的应用程序启动并运行,您应该能够上传一个小图像并获得一个预测!请注意,第一张图片总是需要很长时间,所以请耐心等待!之后会更快!

****综合摘要:在这篇文章中,我们通过以下方式围绕我们的图像识别模型编写了一个 web 应用程序:

  • 在云上设置(数字海洋)
  • 使用 Flask 创建 Web 应用后端
  • 使用 HTML 创建 Web 应用程序前端

恭喜你!您已经构建了您的第一个 web 应用程序。我们离构建一个生产级的 web 应用程序还很远,如果我们要扩大规模,我们还没有加入许多必要的实践。但至少我们有一些 Web 应用程序运行的外观,我们可以将这些实践合并到我们的基础框架中。

****下一步:在我们的下一个编码伴侣第 3 部分(即将发布的链接),我们将探索如何编写我们自己的递归神经网络(RNNs)来处理自然语言!请务必在此处对 RNNs 有一个直观的了解:

直观深度学习第三部分:用于自然语言处理的 RNNs】

注:这个故事最早出现在《直觉深度学习》中:

** [## 直觉深度学习

我们用直观的方式解释前沿的深度学习概念!非技术初学者,深度学习的学生…

medium.com](https://medium.com/intuitive-deep-learning)**

构建 Youtube 音乐推荐器

原文:https://towardsdatascience.com/building-a-youtube-music-recommender-bbd1b3431da4?source=collection_archive---------31-----------------------

从无到有:向你推荐 youtube 音乐视频

概观

在这篇文章中,我将对一个简单的向自己推荐 Youtube 音乐视频的解决方案的一些细节进行评论。你可以通过 Youtube 音乐推荐查看 Heroku 上部署的解决方案。我知道,这不美。也许有一天我会发展我的前端技能。

然后,这里的主要思想是从零开始到部署。这个过程主要包括以下所有步骤:

  1. 删除 Youtube 页面上的视频数据;
  2. 从每个页面中提取视频信息;
  3. 将来自每个视频的数据预处理成单个数据集;
  4. 手动标记一些样本,主动学习其余的样本;
  5. 从数据集中提取要素;
  6. 训练一个随机森林和一个 LightGBM 模型并集成它们;
  7. 通过 Heroku 建立一个简单的应用程序来服务模型。

此外,如果你想查看代码,我会引用每个步骤中使用的脚本。

[## lmeazzini/YouTube-推荐者

这是一个简单的 youtube 视频推荐器。解决方案可以在 Heroku 上看到,网址是…

github.com](https://github.com/lmeazzini/youtube-recommender)

我们开始吧!

删除 Youtube 页面上的视频数据

首先,我们需要数据!为了获得一些数据,我通过在 youtube 搜索上查询来废弃 Youtube 页面。因为我正在寻找推荐给自己的音乐,所以我使用了六个查询:

民谣摇滚、古典吉他、原声摇滚、管弦乐摇滚、大提琴歌曲和巴西摇滚

然后,我保存了每个查询的前 100 页。这在“search_data_collection.py”脚本中完成。

从每个页面中提取视频信息

现在我们有了来自 Youtube 搜索页面的数据,我们需要从这些页面上的视频中提取信息。为此,我用 BeautifulSoup 制作了一个解析器。

这个解析器只保存 600 页中每个视频的链接、标题和查询。此外,由于有可能的视频被复制,我放弃了重复。这在“search_data_parsing.py”脚本中完成。

将来自每个视频的数据预处理成单个数据集

从解析的数据中,我们能够对每个视频进行网络剪辑。这里有很多信息,我们能得到的大部分特征都来自这一步。因为这是一个简单的解决方案,而且我没有花太多时间来设计特性,所以我只使用了几种可能性。

总共有 2277 个视频,那么这个脚本需要相当长的鳄龙时间来运行,大约 2 小时。脚本名为“video_data_processing.py”

可能通过主动学习手动标记一些样本

这项工作中最单调乏味的部分。我必须得到标签。因此,我在 sheets 上打开了上一步生成的 raw_data 文件,添加了一个“y”列,并用 0 或 1 手动填充了 1000 行。

使用标记的数据,可以训练随机森林模型和下一步提取的特征,并使用该模型来识别哪些样本最难分类(~0.5 输出),然后我手动标记了 200 个通过主动学习识别的样本。

主动学习有助于降低标记成本。然而,我期望花费的标注时间比我想象的要少,所以我结束了手动标注。

从视频数据中提取特征

现在,我们数据集的每一行都是一个视频及其数据,如标题、浏览量、作者、描述、视频高度、视频宽度和其他信息。从这些字段中,我做了特征工程,提取出一些可以对后面有帮助的特征。

这样,我提取了浏览量、日均浏览量、视频分辨率,用标题串做了一袋字(BoW)。当然,还有更多的特征可以提取,尤其是从视频描述和视频标签中。但是,我把更深层次的特征工程留给了以后的作品。

训练一个随机的森林和一个 LightGBM 模型,并使它们变得安全

利用提取的特征和标签,我们可以在训练和验证中分割数据集。我选择不做测试集,把最终的验证留给生产数据。

首先,我训练了一个 LightGBM 模型,用贝叶斯优化优化了它的参数以及一些数据预处理参数(比如 BoW 的 n_gram 范围)。然后,用基于贝叶斯搜索的相同预处理参数训练随机森林模型。更多细节,可以查看实现。我寻找这两个模型的集合,给每个模型一个权重。

为了衡量模型的表现,我选择不使用精度、召回率和 F1 值。由于问题涉及到一个建议,最终的输出应该是最相关的建议的排序(输出更接近 1)。然后我用了 average_precision_score,roc_auc,log_loss 来更好的评估模型。

此外,我做了一个学习曲线分析(LCA ),以检查所使用的数据是否足以进行一致的预测。ROC 曲线和 LCA 可以在下图中看到。特征提取和建模在“final_model.py”上进行

通过 Heroku 构建一个简单的应用程序来服务模型

现在我们有了模型,我们可以创建一个应用程序来预测生产数据。由于我们的数据来自 youtube 搜索,如果我们使用用于生成数据集的相同查询,我们可能会获得用于训练/验证模型的相同或几乎相同的视频。然后,我修改了大多数查询,以便在应用程序中获得不同的视频。

这个过程包括一个用 Flask 构建的应用程序(app.py)。这个脚本负责调用函数来丢弃新数据,处理新数据,并使用保存的模型来预测输出,最后生成。html 文件来显示最终输出。废弃的数据被预处理并存储在 SQLite 数据库中,然后被查询以进行预测,这样以后我可以向数据库中插入新数据,并轻松地重新运行应用程序。

为了部署它,我使用了 Docker 和 Gunicorn。部署文件夹中有一个包含该配置的 docker 文件。您可以轻松地在本地运行 docker,甚至通过 Heroku 部署它,我已经按照 Heroku 文档上的指南完成了。

您可以在下面看到部署的应用程序截图:

然而,如果你现在打开它,音乐列表可能会与截图相同或非常相似。这可能是由于音乐不会随着时间的推移而发生很大变化,模型只是在我需要时进行重新分类;没有重新训练计划。

最后

看看生产中的模型预测,有明显的偏差。很多推荐的视频标题上都有“官方音乐视频”这一串。大概这是一个没有太大意义的重功能。可以进行许多改进来提高模型性能并更好地理解预测。

希望这篇文章能给人以启发,并能帮助到任何阅读它的人。同样,这篇文章并没有描述每一步,但是,代码可以在 GitHub 获得。

[## lmeazzini/YouTube-推荐者

这是一个简单的 youtube 音乐/视频推荐器。您可以在 Heroku 上检查已部署的解决方案…

github.com](https://github.com/lmeazzini/youtube-recommender)

与罗伯塔一起建造人工智能 8 号球

原文:https://towardsdatascience.com/building-an-ai-8-ball-with-roberta-2bfbf6f5519b?source=collection_archive---------39-----------------------

人工神经网络能回答是/否问题吗?迹象表明是的。

ai8ball 装置及作者照片

概观

在这篇文章中,我将向你展示我如何利用互联网的力量和当前的机器学习技术构建一个可以回答是/否问题的人工智能 8 球。我将从系统的高级描述开始,然后进入组件的细节以及 Python 代码的摘录。我将展示如何获得否定回答的百分比和置信度的百分比作为输出。最后,我会给出如何运行 AI 8 球的说明,来回答大家自己的问题。

背景

你用过神奇的 8 号球吗?这是美泰公司在 50 年代推出的玩具,至今仍在销售。这很简单。这是一个超大的 8 号球,回答是/不是问题。它有一个漂浮在黑暗液体中的 20 面骰子,使用随机选择的“魔法”来回答问题。在二十个可能的答案中,有五个表示没有,像“很值得怀疑”,五个表示可能,像“回复朦胧,再试一次”,十个表示有,像“是一定的”。

早在 2007 年,我就制作了一个 AI 8 球的早期原型,如上图,并在麻省理工学院 Stata 中心展示了它,作为名为 COLLISIONcollective C11 的群展的一部分。我的第一个版本使用了一个人工神经网络(ANN ),以今天的标准来看,它非常小,只有 1056 个神经连接。我的新版本使用了一个叫做大型罗伯塔模型的人工神经网络,它有 3.55 亿个神经连接。

人工智能 8 球

AI 8-Ball 是使用自然语言处理(NLP)的几个现有组件构建的。该系统的核心是大型的 RoBERTa 模型,我使用 Google AI 的 BoolQ 数据集对其进行了微调,以回答是/否问题[2]。

用户可以键入任何问题,系统从维基百科、纽约时报和 BoolQ 数据集收集支持文本段落。我使用了一个叫做 PyTextRank 的系统来从问题中提取关键词,这些关键词被用来查找维基百科和 NYT 的文章。我还使用 Google 的通用句子编码器[3]在 BoolQ 数据集中检查类似的问题。如果有非常接近的匹配,我使用相关的文本作为第三段。

所有的段落和问题都被发送到罗伯塔模型中进行推理。每个推断返回三个百分比:是、否和置信度。将具有最高置信度的推断作为答案报告给用户。下图显示了包含一个示例问题和结果的组件。

AI 8 球元件图

AI 8 球组件和细节

伯特和罗伯塔

BERT 是由 Google 构建和训练的 NLP 模型的名称。该名称代表变压器的双向编码器表示。该系统设计用于执行常见的 NLP 任务,如机器翻译、文档摘要和文本生成。更多关于伯特的信息可以在 Rani Horev 的帖子这里找到。

对于这个项目,我使用的是 RoBERTa 模型,这是对原始 BERT 的改进。它是由华盛顿大学和脸书大学的研究人员设计和训练的。RoBERTa 的增强包括用更大的批次训练更长的模型,用更长的序列使用更多的数据,以及其他提高准确性的变化[4]。

BoolQ 数据集

BoolQ 是一个来自 Google AI 的数据集,包含大约 16K 个带有相应是/否问题的文本示例[3]。每个条目包含一个问题、标题、答案和一段文字。该数据集可用于训练和测试回答是/否问题的 NLP 系统。例如,下面是其中一个条目。

{"**question**": "did the cincinnati bengals ever win a superbowl", "**title**": "Cincinnati Bengals", "**answer**": false, "**passage**": "The Bengals are one of the 12 NFL teams to not have won a Super Bowl as of the 2017 season; however, they are also one of 8 NFL teams that have been to at least one Super Bowl, but have not won the game."}

照片由阿米莉娅·斯平克Unsplash 上拍摄

微调人工智能 8 球

我使用 BoolQ 数据集对 RoBERTa 模型进行了微调,从 Michael Vincent 的帖子中的代码开始。我修改了他的代码,使用了一个更大的模型,并设置了超参数,如批量大小和时期数,以允许它在 Google Colab 中进行训练。我更新的训练代码是这里

运行推理

下面的 Python 代码展示了如何使用 RoBERTa 来回答是/否问题。例如,Wikipedia 文章中关于神奇 8 球的第一段与六个是/否问题一起被传递到模型中。

运行推理的代码

文本和问题都被标记化并传递到模型中。它返回一个包含是和否部分的向量。使用 PyTorch 中的 softmax 函数可以得出答案为是或否的概率。置信水平可以从向量的长度中导出。以下是六个问题的结果:

推理的结果

它似乎正确地回答了所有问题。请注意置信度是如何变化的,但并不总是与是/否的百分比一致。例如,文章的第一句话清楚地说明了神奇的 8 号球是一个球体,因此第一个问题的答案在是和置信度方面都得到了高分。然而,文章中关于库存的句子用代词“它”指代神奇的 8 号球,这就是为什么关于库存的问题的答案有点不确定。我们将在以后映射到经典魔术 8 球答案时使用此功能。

PyTextRank

该系统使用一种称为 TextRank 的算法从用户的问题中提取关键词和短语[5]。它基于著名的 PageRank 算法。为网络搜索而发明的。你传入一个文本块,它返回按相关性排序的关键短语。下面是一些示例代码,展示了如何从一个问题中提取关键词。

提取关键字的代码

Question: **Can a computer beat a grandmaster chess player?**
Keywords: **a grandmaster, chess player, a computer**

你可以看到它在提取关键短语方面做得很好。

蒂莫西·埃伯利在 Unsplash 上的照片

获取文本段落

现在我们有了关键词,我们可以使用它们来查询维基百科和纽约时报,看看是否有任何相关的文章可以用作该问题的文本段落。此外,我们将使用一种搜索算法,使用一种 k -d 树,简称为 k- 维树,一种用于快速搜索多维数据的数据结构,在 BoolQ 数据集中找到最近的问题[6]。如果匹配接近,我们将使用相应的文本作为第三段。下面是执行所有三个搜索并将它们传递给 RoBERTa 模型以获得结果的 python 代码。

收集段落的代码

现在我们有了三个结果,我们将使用这段代码来看看哪一个具有最高的置信度。

代码以找到最有把握的结果

作者根据麦克·李希特“无视频魔术 8 号球”在 2.0 的 CC 下授权的照片插图

经典魔术 8 球答案

上面的算法给了我们两个输出参数:百分比 yes 和百分比 confidence。为了将这两个参数映射到最接近的经典魔术 8 球答案,我首先使用通用序列编码器将这 20 个答案映射到多维空间中。然后,我将维度减少到两个,并旋转这些点,使其与一个否定到肯定的水平轴和一个低到高置信度的垂直轴对齐。进行映射的代码是这里的。下图显示了结果。

神奇 8 球答案图表

使用此代码将最终答案的两个参数映射到经典的 Magic 8-Ball。

将结果映射到经典 8 球答案的代码

下面是 AI 8-Ball 对样题的输出。

Question: **Can a computer beat a grandmaster chess player?**Checking the Wikipedia.
Checking the New York Times.
Checking the BoolQ Dataset.The AI 8-Ball's Answer: **It is certain.**Yes: 99.52%
No: 0.48%
Confidence: 100%
Source: BoolQ Dataset
Passage: Chess programs running on commercially-available desktop computers had convincing victories against human players in matches in 2005 and 2006\. Since that time, chess programs running on commercial hardware - more recently including mobile phones - have been able to defeat even the strongest human players.

以 99.52%的肯定率和 100%的置信度,映射到最接近的答案“肯定”。

运行人工智能 8 球

你可以在 Google Colab 上运行 AI 8-Ball。请注意,你需要一个谷歌帐户来运行它。

  1. 点击链接,此处
  2. 登录您的谷歌账户,如果您尚未登录,请点击
  3. 单击第一个运行单元格按钮(悬停在[ ]图标上并单击播放按钮)。将出现一条警告,指出此笔记本不是由 Google 创建的。
  4. 点击无论如何都要运行初始化系统。下载数据集和配置微调的 RoBERTa 模型大约需要 3 到 5 分钟。
  5. 选择一个问题或键入您自己的问题,然后点击第二个 Run cell 按钮,查看 AI 8-Ball 如何回答。
  6. 重复步骤 5,询问其他问题。

AI 8-Ball 在 Google Colab 上运行

未来的工作

对 AI 8-Ball 的可能改进包括使用语音到文本来提问,并添加动画来揭示答案。这些添加可能会给这个项目带来更多“神奇”的感觉。

感谢

我要感谢詹尼弗·林、奥利弗·斯特林佩尔、瓦伊德·呼罗珊·加萨布和马赫萨·梅斯加兰对这个项目的帮助。

源代码

这个项目的所有源代码都可以在 GitHub 上获得。源在 CC BY-SA 许可下发布。

归属共享相似

参考

[1]刘,迈勒奥特,纳曼戈亚尔,杜,曼达尔乔希,陈,奥梅尔利维,,卢克泽特勒莫耶,韦塞林斯托扬诺夫,“罗伯塔:稳健优化的贝特瑞训练方法”,2019,

[2] Christopher Clark,Kenton Lee,张明蔚,汤姆·科维亚特科夫斯基,迈克尔·科林斯,克里斯蒂娜·图坦诺娃,《BoolQ:探索自然是非问题的惊人难度》,2019 年,【https://arxiv.org/pdf/1905.10044.pdf

[3]丹尼尔·瑟尔、·杨、孔胜义、南华、尼科尔·利姆蒂亚科、罗慕尼·圣约翰、诺亚·康斯坦、马里奥·瓜哈尔多-塞斯佩德斯、史蒂夫·袁、克里斯·塔尔、宋云轩、布赖恩·斯特罗普、雷·库兹韦尔,《通用句子编码器》,2018 年,

[4]雅各布·德夫林(Jacob Devlin),张明蔚(Ming-Wei Chang),肯顿·李(Kenton Lee),克里斯蒂娜·图坦诺娃(Kristina Toutanova),《伯特:用于语言理解的深度双向变形金刚的预训练》,2018 年,https://arxiv.org/pdf/1810.04805.pdf

[5] Rada Mihalcea,Paul Tarau,《文本等级:将秩序带入文本》,2004 年,https://web . eecs . umich . edu/~ Mihalcea/papers/Mihalcea . em NLP 04 . pdf

[6] Songrit Maneewongvatana,David M. Mount,“聚类点集的近似最近邻搜索分析”,1999 年,https://arxiv.org/pdf/cs/9901013.pdf

为了无限制地访问 Medium 上的所有文章,成为会员,每月支付 5 美元。非会员每月只能看三个锁定的故事。

构建人工智能驱动的可搜索视频档案

原文:https://towardsdatascience.com/building-an-ai-powered-searchable-video-archive-a4721a72e126?source=collection_archive---------45-----------------------

在这篇文章中,我将向你展示如何使用机器学习和谷歌云来构建一个人工智能驱动的、可搜索的视频档案——不需要任何经验。

想看这个故事吗?看看这个视频。

我最喜欢的应用之一绝对是谷歌照片。除了将我珍贵的照片备份到云端,它还让我所有的照片和视频都可以使用机器学习进行搜索。因此,如果我在照片应用程序中键入“游泳池”,它会返回所有它认为是游泳池的东西:

如果你只是想使用人家的软件,这一切都很好,但那有什么乐趣呢?今天,我们将为视频构建我们自己版本的 Google 相册。

不是没有理由的,有很多很好的理由来建立你自己的视频档案。首先,这很有趣。第二,你可以添加 Google 相册目前不支持的功能,尤其是视频。就像通过人们所说的话(文字记录)进行搜索一样,如果你需要找到有人说“现在我们有了它”或“哦,sh*t”的所有剪辑,第三,构建自己的应用程序可以让你更容易地与其他软件集成,并控制如何存储和处理你的数据。例如,我在谷歌云上构建了我的存档后端,这让我可以利用谷歌云的隐私、安全和合规保证

我的可搜索视频存档看起来像这样:

它存储并索引了我所有的家庭视频(大约 126 GB)。使用机器学习,特别是视频智能 API ,我能够进行各种分析,包括自动分割长视频,识别对象和场景,转录音频,以及提取屏幕上的文本。

该应用程序最终非常擅长搜索可爱的瞬间。使用计算机视觉,它识别场景和物体,如“婚礼”、“烟火”、“表演”、“婴儿笑”、“家庭装修”、“过山车”,甚至“迪士尼世界”:

它也可以搜索抄本。这就是我如何找到我的第一步的片段,因为在这些片段中,我的父母说了类似这样的话,“看,戴尔正在迈出她的第一步!”:

最后,该工具能够搜索任何屏幕上的文本,如这些球员衬衫上的“Mets”和“New York”字样或背景中的“Bud”海报:

这个视频档案最终成为了一份相当不错的父亲节礼物,尤其是因为今年我没能亲眼见到我的父亲。

在这篇文章中,我将向你展示如何建立你自己的档案,就像这样。但是如果你想直接跳到代码,可以看看 ML Github repo 的制作过程。

用于视频处理的机器学习架构

这个应用程序分为两部分,前端和后端。后端是结合使用谷歌云、T2、Firebase 和一个叫做 T4 的工具来构建的。前端是用 Flutter 构建的,这是一个用于构建网络和移动应用的框架,但也可以很容易地成为 React 或 Angular 或 iOS 或 Android 应用。

后端架构看起来像这样:

当我构建用机器学习标记或索引数据的应用程序时,我一直在使用这种架构或管道。它是这样工作的:

  1. 首先,数据(在这种情况下,是一个单独的视频)被上传到一个谷歌云存储桶。
  2. 上传启动了云功能(这就像 AWS lambda,即运行在云中的一小块代码)
  3. 云函数调用视频智能 API 开始视频分析
  4. 视频智能 API 将其结果作为 JSON 写入第二个存储桶
  5. 这些写入的数据又会启动第二个云函数,解析 JSON 并将其写入一个更方便的数据存储——在本例中是 FirestoreAlgolia

从这里,我的前端 Flutter 应用程序可以与后端对话,并搜索用户查询。如果您对这些技术不熟悉,不要担心——我稍后会深入介绍。

还有几个步骤我无法放入图表中。例如,我用 Video Intelligence API 对一些很长的视频文件做了一些预处理,将它们分成较小的片段,并识别屏幕上显示的时间戳。此外,我还编写了一个云函数,专门用于拍摄上传的视频并为其生成缩略图(查看该函数)。

将视频从硬盘快速传输到云存储

但首先,在我们进入杂草之前,让我们谈谈从 Google Drive 到云存储的数据传输。理论上,将数据从硬盘转移到存储器应该很快,因为所有数据都可以留在谷歌的网络内。但令人沮丧的是,在实践中,没有巧妙的方法来完成转移。幸运的是,我在菲利普的这篇文章中发现了一个巧妙的漏洞。诀窍是使用一个的 Colab 笔记本——一个免费的、教育性的谷歌工具,用于在云中运行 Python 代码——来完成转移。它快速,简单,非常有效!

视频智能 API

让这个项目成为可能的关键工具是由谷歌云构建的视频智能 API 。它获取云存储中的视频路径,并吐出其他内容:

  • 音频转录(即“自动字幕”)
  • 已知物体(如飞机、海滩、雪、自行车、蛋糕、婚礼)
  • 屏幕文字(即街道标志、t 恤、横幅和海报上的文字)
  • 镜头变化
  • 显式内容检测

这些数据可以用作索引,我们可以用它来搜索特定的视频。

价格

如果你是我,你的第一个想法是,当然,但我打赌它超级贵。我分析了 126 GB 的视频或大约 36 个小时,我使用这个 API 的总成本是 300 美元,这有点贵。以下是每种分析的成本明细:

我惊讶地得知,大部分成本来自一种单一类型的分析——检测屏幕上的文本。其他所有东西总计只有大约 80 美元,这很有趣,因为屏幕上的文本是我提取的最不有趣的属性!所以给你一个建议:如果你预算有限,也许可以不考虑这个功能。

现在澄清一下,我为我收藏的每个视频运行了一次视频智能 API。对于我的归档使用案例,这只是前期成本,而不是经常性成本。

使用 API

一旦你将数据上传到一个云存储桶,使用视频智能 API 就变得非常简单。(没听说过储物桶?它基本上只是一个存储在谷歌云中的文件夹。)对于这个项目,调用 API 的代码位于video _ archive/functions/index . js中,如下所示:

*const videoContext = {
    speechTranscriptionConfig: {
      languageCode: 'en-US',
      enableAutomaticPunctuation: true,
    },
  };

  const request = {
    inputUri: `gs://VIDEO_BUCKET/my_sick_video.mp4`,
    outputUri: `gs://JSON_BUCKET/my_sick_video.json`,
    features: [
      'LABEL_DETECTION',
      'SHOT_CHANGE_DETECTION',
      'TEXT_DETECTION',
      'SPEECH_TRANSCRIPTION',
    ],
    videoContext: videoContext,
  };

  const client = new video.v1.VideoIntelligenceServiceClient();

  // Detects labels in a video
  console.log(`Kicking off client annotation`);
  const [operation] = await client.annotateVideo(request);
  console.log('operation', operation);*
  1. 第 1 行,我们创建了一个videoContext,其中包含 API 的一些配置设置。这里我们告诉工具音轨将会是英文的(en-US)。
  2. 第 8 行,我们创建了一个请求对象,将视频文件的路径作为inputUri传递,将我们希望结果写入的位置作为outputUri。请注意,视频智能 API 会将数据作为json写入您指定的任何路径,只要它在您有权写入的存储桶中。
  3. 在第 12 行,我们指定希望 API 运行什么类型的分析。
  4. 在第 24 行,我们开始一个视频注释请求。有两种方法可以做到这一点,一种是同步运行函数并等待代码中的结果,另一种是启动后台作业并将结果写入 json 文件。视频智能 API 几乎实时分析视频,因此 2 分钟的视频需要大约 2 分钟来分析。因为时间有点长,所以我决定在这里使用异步函数调用。

如果你想在自己的电脑上快速使用这个 API,可以从官方的 Google Cloud Node.js sample repo 中试用这个示例

回应

当 API 处理完一个视频后,它将结果写成 json,如下所示:

*{
  "annotation_results": [ {
    "input_uri": "/family_videos/myuserid/multi_shot_test.mp4",
    "segment": {
      "start_time_offset": {

      },
      "end_time_offset": {
        "seconds": 70,
        "nanos": 983000000
      }
    },
    "segment_label_annotations": [ {
      "entity": {
        "entity_id": "/m/06npx",
        "description": "sea",
        "language_code": "en-US"
      },
      "segments": [ {
        "segment": {
          "start_time_offset": {

          },
          "end_time_offset": {
            "seconds": 70,
            "nanos": 983000000
          }
        },
        "confidence": 0.34786162
      } ]
    }, {
      "entity": {
        "entity_id": "/m/07bsy",
        "description": "transport",
        "language_code": "en-US"
      },
      "segments": [ {
        "segment": {
          "start_time_offset": {

          },
          "end_time_offset": {
            "seconds": 70,
            "nanos": 983000000
          }
        },
        "confidence": 0.57152408
      } ]
    }, {
      "entity": {
        "entity_id": "/m/06gfj",
        "description": "road",
        "language_code": "en-US"
      },
      "segments": [ {
        "segment": {
          "start_time_offset": {

          },
          "end_time_offset": {
            "seconds": 70,
            "nanos": 983000000
          }
        },
        "confidence": 0.48243082
      } ]
    }, {
      "entity": {
        "entity_id": "/m/015s2f",
        "description": "water resources",
        "language_code": "en-US"
      },
      "category_entities": [ {
        "entity_id": "/m/0838f",
        "description": "water",
        "language_code": "en-US"
      } ],
      "segments": [ {
        "segment": {
          "start_time_offset": {

          },
          "end_time_offset": {
            "seconds": 70,
            "nanos": 983000000
          }
        },
        "confidence": 0.34592748
      } ]
    },*

响应还包含文本注释和转录,但它真的很大,所以我没有全部粘贴到这里!要使用这个文件,您需要一些代码来解析它,并可能将结果写入数据库。你可以借用我的代码来获得这方面的帮助。下面是我解析 json 的一个函数的样子:

*/* Grab image labels (i.e. snow, baby laughing, bridal shower) from json */
function parseShotLabelAnnotations(jsonBlob) {
  return jsonBlob.annotation_results
      .filter((annotation) => {
      // Ignore annotations without shot label annotations
        return annotation.shot_label_annotations;
      })
      .flatMap((annotation) => {
        return annotation.shot_label_annotations.flatMap((annotation) => {
          return annotation.segments.flatMap((segment) => {
            return {
              text: null,
              transcript: null,
              entity: annotation.entity.description,
              confidence: segment.confidence,
              start_time: segment.segment.start_time_offset.seconds || 0,
              end_time: segment.segment.end_time_offset.seconds,
            };
          });
        });
      });
}*

用 Firebase 构建无服务器后端

为了真正让视频智能 API 成为一个有用的视频档案,我必须围绕它建立一个完整的应用程序。这需要某种后端来运行代码、存储数据、托管数据库、处理用户和认证、托管网站——所有典型的 web 应用程序的东西。

为此,我求助于我最喜欢的开发工具套件之一, Firebase 。Firebase 是一种构建应用程序的“无服务器”方法。它为常见的应用程序功能(数据库、文件存储、性能监控、托管、身份验证、分析、消息传递等)提供支持,因此开发人员无需为整个服务器或虚拟机付费。

如果你想自己运行我的项目,你必须创建自己的 Firebase 帐户和项目才能开始(这是免费的)。

我使用 Firebase 运行我所有的代码,使用 Firebase 的云函数。您可以上传一个或一组函数(使用 Python 或 Go 或 Node.js 或 Java ),这些函数运行以响应事件——一个 HTTP 请求、一个 Pub/Sub 事件,或者(在本例中)当一个文件上传到云存储时。

你可以看看我在这个文件里的云函数。下面是一个例子,展示了每次文件上传到YOUR INPUT VIDEO BUCKET时,如何运行一个 Javascript 函数(在本例中是analyzeVideo)。

*const functions = require('firebase-functions');

exports.analyzeVideo = functions.storage
    .bucket(YOUR_INPUT_VIDEO_BUCKET)
    .object()
    .onFinalize(async (object) => {
      await analyzeVideo(object);
    });*

一旦你安装了 Firebase 命令行工具,你就可以通过命令行将你的函数部署到云端,这些函数应该写在一个名为index.js的文件中,运行:

firebase deploy --only functions

我还使用 Firebase 函数构建了一个搜索 HTTP 端点:

*/* Does what it says--takes a userid and a query and returns
relevant video data */
exports.search = functions.https.onCall(async (data, context) => {
  if (!context.auth || !context.auth.token.email) {
    // Throwing an HttpsError so that the client gets the error details.
    throw new functions.https.HttpsError(
        'failed-precondition',
        'The function must be called while authenticated.',
    );
  }

  const hits = await utils.search(data.text, context.auth.uid);
  return {'hits': hits};
});*
  1. 在第 3 行,我使用functions.https.onCall注册了一个新的 Firebase 函数,当发出一个 HTTPS GET 请求时就会触发这个函数。
  2. 在第 4 行,我检查调用我的 HTTPS 端点的用户是否已经过身份验证,并且已经注册了一个电子邮件地址。认证很容易用 Firebase 设置,在我的项目中,我已经用 Google 登录启用了它。
  3. 在第 12 行,我调用我的搜索函数,传递新用户注册时 Firebase 生成的 userid context.auth.uid,当他们到达我的端点时传递这个 userid。
  4. 在第 13 行,我返回搜索结果。

快速简易搜索

接下来,我需要一种方法让用户搜索我的视频档案。因为他们不知道确切地搜索什么术语或使用什么短语,所以我需要我的搜索实现是智能的。它应该容忍错别字,并根据用户的查询准确猜测用户想要什么(就像谷歌搜索一样)。

我被自己实现搜索功能的想法吓坏了(讽刺的是,因为我在谷歌工作!),直到我偶然发现一个搜索 API 建了一个叫 Algolia 的公司。Aloglia 允许你上传 json 格式的数据(这是我曾经用过的数据格式!),然后从您的应用程序(或者从他们的控制台,如果您正在调试)查询这些数据。以下是 Algolia 控制台的外观:

它自动处理错别字,正如你在上面看到的,我把“父亲节”这个词拼错了。

该工具还有许多不同配置选项的。您可以调整哪些 json 字段是可搜索的,搜索结果应该如何排序,允许多少输入错误,等等:**

如果你想看一些代码样本,可以看看video _ archive/functions/algolia . js。下面是用 Javascript 进行搜索查询的代码:

**exports.search = async function(query, userid) {
  const client = algoliasearch(
      process.env.ALGOLIA_APPID,
      process.env.ALGOLIA_ADMIN_APIKEY,
  );
  const index = client.initIndex(process.env.ALOGLIA_INDEX);
  const res = await index.search(query, {
    tagFilters: [userid],
    attributesToRetrieve: ['videoId', 'transcript', 'text', 'entity', '_tags'],
  });

  if (!res.nbHits) return [];
  return res.hits
      .filter((hit, index) => {
        return res.hits.findIndex((h) => h.videoId == hit.videoId) == index;
      })
      .map((hit) => {
        return {videoId: hit['videoId']};
      });
};**
  1. 在第 2 行,我提供了我的凭证并创建了一个搜索客户机。
  2. 在第 6 行,我指定了我想要搜索的数据集或“索引”。
  3. 在第 7 行,我开始了一个搜索查询,指定了查询文本(例如“生日”)、过滤依据的“标签”(我使用标签将数据与用户相关联),以及我想要接收的 json 字段。
  4. 第 14 行看起来有点复杂,但是我只是过滤重复的电影 id 并格式化一个 json 响应。

颤动的前端

因为我不是一个非常好的前端开发人员,所以我决定以这个项目为借口学习 Flutter,这是谷歌的一个新平台,用于编写可以在任何地方(web,Android,iOS)运行的代码。总的来说,我玩得很开心,觉得设计 Flutter 应用程序比 CSS 简单多了。以下是最终结果:

我只是搭建了一个 web app,这次不是 iOS 或者 Android。

你可以在 repo 的 Flutter 文件夹中查看所有的前端代码,但是因为我是新手,所以不能保证它是“正确的”;).

这就是你如何建立一个人工智能驱动的视频档案!有问题或意见吗?在推特上 Ping 我

原载于 2020 年 6 月 12 日 https://daleonai.com。****

使用 Slash GraphQL 构建类似亚马逊的推荐引擎

原文:https://towardsdatascience.com/building-an-amazon-like-recommendation-engine-using-slash-graphql-9635f0ce49b1?source=collection_archive---------31-----------------------

【TL;DR;开始使用 Dgraph 的 Slash GraphQL 产品,并连接到一个 Spring Boot 应用程序,它将充当一个简单的 RESTful 推荐服务。]

早在 2000 年代初,我就在做一个项目,实施一个由 Art Technology Group (ATG)开发的电子商务解决方案,该公司现在归甲骨文公司所有。ATG 迪纳摩产品是一个令人印象深刻的解决方案,因为它包括持久层和场景模块。当时,像 Target 和 Best Buy 这样的公司使用 Dynamo 解决方案,利用场景模块向客户提供建议。

例如,Dynamo 电子商务解决方案足够智能,可以记住客户何时将产品添加到购物车中,然后又将其删除。作为一种激励,场景服务器可以被设计为在未来的访问中向相同的客户提供适度的折扣,如果他们将产品重新添加到他们的购物车中并在接下来的 24 小时内购买它。

从那时起,我就一直想创建一个简单的推荐引擎,这也是我撰写这份出版物的目标。

关于建议示例

我想保持简单,为推荐引擎创建一些基本的领域对象。在本例中,解决方案将为音乐艺术家提供推荐,底层艺术家对象非常简单:

@AllArgsConstructor@NoArgsConstructor@Datapublic class Artist { private String name;}

在一个真实的系统中,会有更多的属性需要跟踪。然而,在这个例子中,艺术家的名字就足够了。

正如人们所预料的,客户将对艺术家进行 1 到 5 分的评分,其中 5 分代表可能的最高分。当然,有可能(也在意料之中)客户不会给每个艺人打分。客户将(再次)由一个非常简单的客户对象表示:

@AllArgsConstructor@NoArgsConstructor@Datapublic class Customer { private String username;}

客户对艺术家进行评级的概念将在以下评级对象中得到体现:

@AllArgsConstructor@NoArgsConstructor@Datapublic class Rating { private String id; private double score; private Customer by; private Artist about;}

在我正常的 Java 编程工作中,我可能会对我的对象使用私人客户客户和私人艺术家艺术家,但是我想遵循图形数据库所使用的模式,在图形数据库中,我使用像‘by’和‘about’这样的变量..随着文章的继续,这一点会变得更加清楚。

图斜线图

随着图形数据库的流行,我觉得我创建推荐引擎的探索应该使用图形数据库。毕竟,GraphQL 已经成为与服务讨论图形的流行语言。虽然我对图形数据库只有一些了解,但我的分析似乎得出结论,图形数据库是这个项目的正确选择,并且通常是现实世界中提供建议的服务的来源。当数据(节点)之间的关系(边)和数据本身一样重要时,图数据库是一个很好的解决方案,推荐引擎就是一个很好的例子。

然而,由于我刚刚开始使用图形数据库,我当然不想担心启动容器或在本地运行 GraphQL 数据库。相反,我想找到一个 SaaS 供应商。我决定使用 Dgraph 的完全托管后端服务,名为 Slash GraphQL 。这是一个托管的本地 GraphQL 解决方案。Slash GraphQL 服务于 2020 年 9 月 10 日刚刚发布,可以使用以下链接启用。该平台提供了一个适用于本文的免费试用版(然后移动到 9.99 美元/月的固定费用,最高 5GB 的数据)。

https://slash.dgraph.io/

启动此 URL 后,可以使用常规授权服务创建新帐户:

在我的例子中,我创建了一个名为“spring-boot-demo”的后端,最终产生了下面的仪表板:

斜线graph QL 仪表盘

开始的过程是快速和免费的,使得配置 Slash GraphQL 服务毫不费力。

配置斜杠图表 QL

与任何数据库解决方案一样,我们必须编写一个模式并将其部署到数据库中。使用 Slash GraphQL,这既快速又简单:

type Artist { name: String! @id @search(by: [hash, regexp]) ratings: [Rating] @hasInverse(field: about)}type Customer { username: String! @id @search(by: [hash, regexp]) ratings: [Rating] @hasInverse(field: by)}type Rating { id: ID! about: Artist! by: Customer! score: Int @search}

事实上,我最初的设计比它需要的要复杂得多,而且修改背后的工作量也比我想象的要容易得多。我很快开始意识到作为一名开发人员不需要太多努力就能改变模式的价值。

有了这个模式,我能够快速填充一些基本的艺术家信息:

mutation { addArtist(input: [ {name: “Eric Johnson”}, {name: “Genesis”}, {name: “Led Zeppelin”}, {name: “Rush”}, {name: “Triumph”}, {name: “Van Halen”}, {name: “Yes”}]) { artist { name } }}

同时,我添加了一些虚构的客户记录:

mutation { addCustomer(input: [ {username: “Doug”}, {username: “Jeff”}, {username: “John”}, {username: “Russell”}, {username: “Stacy”}]) { customer { username } }}

因此,这五个客户将使用下表为七位艺术家提供评级:

评级流程的示例如下所示:

mutation { addRating(input: [{ by: {username: “Jeff”}, about: { name: “Triumph”}, score: 4}]) { rating { score by { username } about { name } } }}

配置并运行斜杠 GraphQL 数据后,我现在可以切换到 Spring Boot 服务了。

斜率一评级算法

2005 年,丹尼尔·雷米尔和安娜·麦克拉钦的一篇研究论文介绍了 Slope One 系列的协同过滤算法。这种简单的基于项目的协作过滤看起来非常适合推荐服务,因为它考虑了其他客户的评级,以便对给定客户未评级的项目进行评分。

在伪代码中,推荐服务将实现以下目标:

  • 检索所有艺术家的可用评级(由所有客户提供)
  • 从数据中创建一个地图,这是一个客户地图,包含所有艺术家和他们的评级
  • 1 到 5 的评分将被转换为介于 0.2(最差评分为 1)和 1.0(最佳评分为 5)之间的简单范围。

创建客户地图后,SlopeOne 评级处理的核心将通过调用 Slope One 类来执行:

  • 填充用于跟踪不同客户评分差异的地图
  • 填充地图>用于跟踪相似评级的频率
  • 使用现有地图创建一个地图>,其中包含未对给定客户评级的项目的预计评级

在本例中,随机选择了一个客户,并对 Map > projectedData map 中的相应对象进行分析,以返回以下结果:

{ “matchedCustomer”: { “username”: “Russell” }, “recommendationsMap”: { “Artist(name=Eric Johnson)”: 0.7765842245950264, “Artist(name=Yes)”: 0.7661904474477843, “Artist(name=Triumph)”: 0.7518039724158979, “Artist(name=Van Halen)”: 0.7635436007978691 }, “ratingsMap”: { “Artist(name=Genesis)”: 0.4, “Artist(name=Led Zeppelin)”: 1.0, “Artist(name=Rush)”: 0.6 }, “resultsMap”: { “Artist(name=Eric Johnson)”: 0.7765842245950264, “Artist(name=Genesis)”: 0.4, “Artist(name=Yes)”: 0.7661904474477843, “Artist(name=Led Zeppelin)”: 1.0, “Artist(name=Rush)”: 0.6, “Artist(name=Triumph)”: 0.7518039724158979, “Artist(name=Van Halen)”: 0.7635436007978691 }}

在上面的例子中,“Russell”用户是随机选择的。在查看原始表格时,罗素只提供了《创世纪》、《齐柏林飞船》和《拉什》的评分。他唯一真正钦佩的艺术家是齐柏林飞船。该信息包含在 ratingsMap 对象和 resultsMap 对象中。

resultsMap 对象包括其他四位艺术家的预计评分:埃里克·约翰逊、是的、黛安芬和范·海伦。为了使事情变得更简单,有效负载中包含了一个 recommendationsMap,其中仅包含未被 Russell 评级的艺术家。

根据其他评论,推荐服务将略微倾向于埃里克·约翰逊,得分为 0.78,接近五分制中的四分。

推荐服务

为了使用推荐服务,Spring Boot 服务器只需要运行并配置为连接到 Slash GraphQL 基于云的实例。Slash GraphQL 仪表板上的 GraphQL 端点可以在 application.yml 中指定为 slash-graph-ql.hostname,或者通过${ Slash _ GRAPH _ QL _ HOSTNAME }环境变量传入值来指定。

可以使用以下 RESTful URI 调用基本建议引擎:

GET — {spring-boot-service-host-name}/recommend

该操作由推荐控制器配置,如下所示:

@GetMapping(value = “/recommend”)public ResponseEntity<Recommendation> recommend() { try { return new ResponseEntity<>(recommendationService.recommend(), HttpStatus.OK); } catch (Exception e) { return new ResponseEntity<>(HttpStatus.BAD_REQUEST); }}

它调用推荐服务:

@Slf4j@RequiredArgsConstructor@Servicepublic class RecommendationService { private final ArtistService artistService; private final CustomerService customerService; private final SlashGraphQlProperties slashGraphQlProperties; private static final String RATING_QUERY = “query RatingQuery { queryRating { id, score, by { username }, about { name } } }”; public Recommendation recommend() throws Exception { ResponseEntity<String> responseEntity = RestTemplateUtils.query(slashGraphQlProperties.getHostname(), RATING_QUERY); try { ObjectMapper objectMapper = new ObjectMapper(); SlashGraphQlResultRating slashGraphQlResult = objectMapper.readValue(responseEntity.getBody(), SlashGraphQlResultRating.class); log.debug(“slashGraphQlResult={}”, slashGraphQlResult); return makeRecommendation(slashGraphQlResult.getData()); } catch (JsonProcessingException e) { throw new Exception(“An error was encountered processing responseEntity=” + responseEntity.getBody(), e); } }…}

请注意——快速浏览一下这段代码可能会忽略的一点是,能够拉出一个子图来执行推荐的功能和简易性。在上面的示例中,“slashGraphQlResult.getData()”行将子图提供给“makeRecommendation()”方法。

RATING_QUERY 是检索评级对象所需的斜杠 GraphQL 格式。RestTemplateUtils.query()方法是静态实用程序类的一部分,目的是保持干爽(不要重复):

public final class RestTemplateUtils { private RestTemplateUtils() { } private static final String MEDIA_TYPE_GRAPH_QL = “application/graphql”; private static final String GRAPH_QL_URI = “/graphql”; public static ResponseEntity<String> query(String hostname, String query) { RestTemplate restTemplate = new RestTemplate(); HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.valueOf(MEDIA_TYPE_GRAPH_QL)); HttpEntity<String> httpEntity = new HttpEntity<>(query,   headers); return restTemplate.exchange(hostname + GRAPH_QL_URI, HttpMethod.POST, httpEntity, String.class); }}

一旦检索到 slashGraphQlResult 对象,就会调用 makeRecommendation()私有方法,该方法返回以下推荐对象。(上面以 JSON 格式显示了这一点):

@AllArgsConstructor@NoArgsConstructor@Datapublic class Recommendation { private Customer matchedCustomer; private HashMap<Artist, Double> recommendationsMap; private HashMap<Artist, Double> ratingsMap; private HashMap<Artist, Double> resultsMap;}

结论

在本文中,使用新的模式创建了一个 Dgraph Slash GraphQL 实例,并加载了示例数据。然后,Spring boot 服务利用这些数据作为基本的推荐引擎。对于那些对完整源代码感兴趣的人,请查看 GitLab 资源库:

从成本角度来看,我对 Slash GraphQL 提供的结构印象颇深。新账户屏幕显示我每月有 10,000 信用点数可以免费使用。在我使用 Slash GraphQL 为本文构建原型和创建一切的整个过程中,我只使用了 292 个学分。对于那些需要超过 10,000 信用点的用户,每月 45 美元可获得 100,000 信用点,每月 99 美元可获得 250,000 信用点。

第一次使用图形数据库确实呈现了一个小的学习曲线,我确信通过继续这个练习,我可以学到更多的东西。当我更多地了解我的需求时,我觉得 Slash GraphQL 改变模式的能力超出了我的预期。作为特性开发人员,这是应该认识到的一个非常重要的方面,尤其是与传统数据库的相同场景相比。

在我的下一篇文章中,我将在这个过程中引入一个 Angular(或 React)客户端,它将直接与 GraphQL 和运行在 Spring Boot 的推荐服务接口。

(经原作者许可发表,约翰·维斯特

用 Tensorflow 构建神经网络

原文:https://towardsdatascience.com/building-an-ann-with-tensorflow-ec9652a7ddd4?source=collection_archive---------8-----------------------

如何使用 TensorFlow 构建神经网络的分步教程

Unsplash 上由halance拍摄的照片

如果你是人工神经网络的新手,你可以在下面的链接中查看我的关于人工神经网络介绍的博客

[## 人工神经网络导论

你深度学习的第一步

towardsdatascience.com](/introduction-to-artificial-neural-networks-ac338f4154e5)

在本教程中,我们将在时尚 MNIST 数据集上构建一个人工神经网络,该数据集由 70,000 幅图像组成,其中 60,000 幅图像属于训练集,10,000 幅图像属于测试集。每幅图像高 28 像素,宽 28 像素,总共 784 像素,有 10 个标签与之相关联。像素值是 0 到 255 之间的整数。每行是一个单独的图像,我们有 785 个列标签,包括一个类标签。在该数据集中,每个图像都与下面提到的一个标签相关联。

  • 0 → T 恤/上衣
  • 1 →裤子
  • 2 →套头衫
  • 3 →着装
  • 4 →外套
  • 5 →凉鞋
  • 6 →衬衫
  • 7 →运动鞋
  • 8 →包
  • 9 →踝靴

安装 Tensorflow

如果你的 PC 或笔记本电脑没有 GPU,你可以使用 Google Colab,或者使用 Jupyter Notebook。如果您使用您的系统,请升级 pip,然后安装 TensorFlow,如下所示

tensorflow.org

导入依赖项

在上面几行代码中,我只是导入了这个过程中需要的所有库

数据再处理

加载数据集

这里我们将使用 load_data() 方法加载时尚 mnist 数据集。

正常化

这里,我们将每个图像除以最大像素数(255),这样图像范围将在 0 和 1 之间。我们将图像标准化,以便我们的人工神经网络模型训练得更快。

重塑

我们将得到一个 2d 数组作为输出,其中第一维对应于图像的索引,第二维是包含图像所有像素的向量

在二维数组中,我们将使用整形将二维数组转换为一维向量。这里我们传递两个参数

  • -1 →我们需要重塑所有的形象
  • 28*28 →合成矢量的大小(784 像素→ 1D)

照片由 NASAUnsplash 上拍摄

建筑安

在建立模型之前,我们需要做的第一件事是创建一个模型对象本身,这个对象将是一个名为 Sequential 的类的实例。

添加第一个完全连接的层

如果你不知道层的类型和它们的功能,我建议你看看我的博客《人工神经网络导论》,它让你知道你应该知道的大部分概念。

超参数:

  • 神经元数量:128
  • 激活功能:ReLU
  • 输入 _ 形状:(784,)

这意味着该操作的输出应该具有 128 个神经元,其中我们应用 ReLU 激活函数来打破线性,并且 input_shape 是(784,)。我们使用 model.add() 方法添加所有这些超参数。

添加辍学层

这是一种正则化技术,我们将一层中的神经元随机设置为零。这样,某些百分比的神经元将不会被更新,整个训练过程是漫长的,我们有更少的机会过度拟合。

添加输出图层

  • 单位=课程数量(MNIST 时装公司为 10)
  • activation = softmax(返回类的概率)

编译模型

这意味着我们必须将整个网络连接到一个优化器,并选择一个损失。优化器是一种工具,它将在随机梯度下降过程中更新权重,即将您的损失反向传播到神经网络中。

训练模型

我们使用 model.fit()方法来训练模型,我们在方法内部传递三个参数,它们是

输入→ x_train 是馈入网络的输入

输出→这包含 x_train(即 y_train)的正确答案

次数→表示您要用数据集训练网络的次数。

评估模型

我们将通过将模型应用于测试集来评估模型的性能。这里,evaluate 方法返回两个参数,一个是预测在测试集中引起的损失,另一个是准确性。

我们的数据达到了 87.2%的准确率。目前就这些,希望你实现了你的第一个深度学习模型。在我的下一篇博客中,我们将一起使用卷积神经网络实现 MNIST 模型。如果你想知道卷积神经网络的概念,可以看看我下面的博客

[## 卷积神经网络简介

关于卷积神经网络如何工作的直觉

medium.com](https://medium.com/dataseries/introduction-to-convolutional-neural-networks-5a227f61dd50)

使用 Terraform 构建 ApiGateway-SQS-Lambda 集成

原文:https://towardsdatascience.com/building-an-apigateway-sqs-lambda-integration-using-terraform-5617cc0408ad?source=collection_archive---------11-----------------------

UnsplashMax Duzij 拍摄的照片

Terraform 是一个非常棒的基础设施建设工具。该工具用于安全有效地构建、更改和版本控制基础设施。Terraform 是 HashiCorp 提供的基础设施代码。

在使用 Terraform 构建我正在使用 Amazon Web Services (AWS)设计的项目时,我遇到了设置 API 网关端点的需求,该端点获取记录,并将它们放入触发 Lambda 函数的事件源的 SQS 队列中。

在本帖中,我将与您分享构建这一基础设施所需的每个步骤。这篇文章假设你熟悉 Terraform 代码和 AWS 服务。

使用 LucidChart 工作空间制作的图表:https://www.lucidchart.com/pages/?noHomepageRedirect=true

变量

首先,让我们看看项目中使用的输入变量。变量存储在文件 varibles.tf.

这些变量的使用使得在不同的环境中部署服务变得非常容易。将环境变量更改为 prd(又名生产),将创建所有具有相应环境名称的服务。

简单队列服务(SQS)

首先创建 SQS 资源。

ApiGateway

在创建 ApiGateway 资源之前,让我们首先定义权限,以便 API Gateway 拥有向 SQS 队列发送消息的必要权限。我通常会创建一个名为 policies 的文件夹,其中包含我将在项目中使用的所有策略,我建议你也这样做,这将保持你的代码整洁有序。

├── iam.tf
├── policies: // all policies created
│   ├── api-gateway-permission.json
│   └── lambda-permission.json

这些权限将赋予 API Gateway 创建和写入 CloudWatch 日志的能力,以及从 SQS 队列中放置、读取和列出数据的能力。

创建了 ApiGateway-SQS 交互所需的所有权限后,我们就可以开始创建端点了。

我们的端点将有一个连接到根的路径:/form_score,带有一个 API Gateway POST 方法。我们还需要一个名为query_string_parameter的 unity,通过 HTTP 请求传递。为了验证请求参数中是否存在query_string_parameter,创建了一个验证器。

现在,我们可以定义将记录转发到 SQS 队列的 API 网关集成。为了将方法、主体、查询参数和路径参数从端点转发到 SQS 消息,添加了一个request_template

对于与 SQS 的集成,请求的 HTTP 头必须与Content-Type: application/x-www-form-urlencoded在一起。

还应该为成功的响应绘制 SQS 响应。在这个项目中,SQS 响应按原样返回给 apiGateway,但是您也可以对其进行更改,以便返回一个自定义消息,例如:'Message Added to SQS successfully'。这里,我们为成功的请求定义了一个 200 处理程序。

最后,我们可以添加 API Gateway REST 部署来部署我们的端点。添加了重新部署触发器。此配置计算 API 的 Terraform 资源的散列,以确定应触发新部署的更改。

希腊字母的第 11 个

当用 terraform 构建 lambda 时,我喜欢将 lambda 代码从基础代码中分离出来,这样可以使代码更干净、更有条理。

├── lambda: folder for lambda code
│   ├── handler.py
│   └── sqs-integration-dev-lambda.zip
├── lambda.tf

在构建我们的 lambda 之前,我们必须创建必要的权限,以便它可以读取来自 SQS 的消息并写入 CloudWatch 日志。

然后,我们可以建立我们的 lambda。

最后,我们需要添加一个权限,以便 SQS 可以调用 lambda。我们还需要添加一个事件源,以便当新消息到达队列时,SQS 可以触发 lambda。

结论

设置好一切后,我们准备将所有更改应用到我们的 AWS 帐户。部署后,我们将拥有一个公共端点,它将使用 Lambda 函数向 SQS 写入数据,并从中消费。

这是一个强大的管道,可以用在不同的场景中。你可以在这里的查看完整的代码和额外的分析!

参考

[## 阿弗洛施的要点

我很难找到一个像样的例子,说明如何使用 lua nginx 模块来获取环境变量并传递…

gist.github.com](https://gist.github.com/afloesch) [## 哈希公司的 Terraform

使用 Terraform 以代码形式交付基础架构协作和共享配置发展和版本化您的…

www.terraform.io](https://www.terraform.io) [## 在 Terraform 中将 Lambda 设置为来自 SQS 的事件源

本指南将向您展示如何设置 AWS Lambda,以使用 SQS 作为 Terraform 的事件源。

medium.com](https://medium.com/appetite-for-cloud-formation/setup-lambda-to-event-source-from-sqs-in-terraform-6187c5ac2df1)

从零开始构建问答系统的应用程序

原文:https://towardsdatascience.com/building-an-application-of-question-answering-system-from-scratch-2dfc53f760aa?source=collection_archive---------8-----------------------

初学者的循序渐进指南

阿尔弗莱德·西斯莱/公共领域来自维基媒体

问答(QA) 系统是对用英语、汉语等自然语言表达的问题给出适当答案的系统。例如,假设一个用户问“亚伯拉罕·林肯什么时候被暗杀的?”在这种情况下,问答系统预计返回“1865 年 4 月 15 日”。下面是一个问答配对的例子。

来自演讲和语言处理第 25 章的问答配对示例。

一个问题回答系统将帮助你有效地找到信息。一般来说,当我们在网上寻找一些信息时,我们会使用搜索引擎来搜索相关的文档。但是,因为它们向您显示文档,所以您必须阅读文档并确定它们是否包含您需要的信息。真麻烦。因此,商业搜索引擎有一个问题回答功能,让你可以有效地找到信息。

必应搜索问答。

问答系统有多种模式,但主要使用两种模式:基于知识的和基于信息检索的问答系统。人类在回答一个问题时,首先尝试用自己的知识来回答问题。如果他们不能回答这个问题,他们就在网上或书中寻找答案。问答系统类似于人类。前者对应于基于知识的系统,后者对应于基于信息检索的系统。

两种质量保证系统。

在本帖中,我们将构建一个基于信息检索的问答系统。首先,我们将问题转换成搜索查询。接下来,我们使用 MediaWiki API 来获取文档。最后,我们使用预先训练的 BERT 从文档中提取答案。整个系统可以分为四个部分:问题处理、文档检索、段落检索和答案抽取。

基于 IR 的问答系统架构来自语音和语言处理第 25 章。

在这篇文章中,我只展示了重要的部分,但是整个系统可以在下面的 GitHub 库中找到。代码很小,很容易理解。请看一看:

该员额的其余部分组织如下:

  1. 询问处理
  2. 文档检索
  3. 段落检索
  4. 答案抽取

步骤 1:查询处理

问题处理将问题转换成搜索查询。通常会删除停用词和具有特定词性的词。此外,随着深度学习技术最近的发展,创建可以很好地表达句子意思的向量已经成为可能,因此还使用了将问题转换为向量并将其用作查询的方法。

这一次,我们将使用 spaCy 生成一个搜索查询。首先,对问题进行解析,并标注词性标签。接下来,我们根据词类标签删除单词。具体来说,除专有名词(PROPN)、数字(NUM)、动词(VERB)、名词(NOUN)和形容词(ADJ)之外的单词将被删除。比如问题“亚马逊的创始人是谁?”生成查询“创始人亚马逊”。

查询处理的一个例子。

请看看 QueryProcessor 类:

步骤 2:文档检索

在文档检索中,我们将使用生成的查询来检索相关的文档。在后续的处理中,由于会从这些文档中提取答案,所以需要尽可能地搜索可能包含答案的文档。与问题处理类似,深度学习技术允许我们将文档转换为向量[1]。

这一次,我们将搜索维基百科。维基百科提供了一个叫做 MediaWiki API 的 API。我们可以使用 API 来搜索与查询相关的文档。这里的过程包括两个步骤。首先,我们发送一个查询来获取相关页面的列表。然后我们获取各个页面的内容。

文献检索流程。

请看看 DocumentRetrieval 类:

第三步:段落检索

在段落检索中,文档被分成更小的单元(段落),例如句子和段落,并且选择可能包含答案的段落。如果文档很短,这是不必要的,但是如果文档很长,段落选择是有效的,因为我们不知道查询匹配了文档的哪一部分。通常,后续的答案抽取过程需要很长时间才能完成,因此选择段落还有加快整个系统速度的好处。

这一次,我们将选择与问题相似的段落。为了计算相似性,我们使用 BM25 来创建问题和段落的向量。一旦我们创建了向量,我们就可以使用点积和余弦相似度来计算问题和文章之间的相似度。因为 BM25 功能强大,所以经常被用作段落检索的基线。

请看看 PassageRetrieval 类:

步骤 4:答案抽取

答案提取从段落中提取答案。这里,问题和段落被输入到答案提取模型,并且模型输出带有分数的答案偏移。然后,它根据分数对答案进行排序,并将分数最高的答案作为最终答案呈现给用户。

从文本中提取答案。在这种情况下,对于“流域内有多少平方公里的雨林被转化?”,模型将返回“5,500,000”作为答案。来自小队 2.0

这一次,我们将答案抽取公式化为上下文感知问答,并使用 BERT 进行求解。通过将问题和文章输入到 BERT 中,我们可以得到答案的偏移量。已知 BERT 可以很好地解决答案抽取,在 SQuAD 数据集上表现优于人类[2][3]。

鸣谢:伯特:语言理解深度双向变压器前期训练

请看看 AnswerExtraction 类:

让我们看一下应用程序。以下是一个问题回答的例子。当“谁是亚马逊的创始人?”发布后,应用程序将问题转换为查询,通过 MediaWiki API 检索文档,选择段落并从中提取答案。因此,应用程序返回“杰夫·贝索斯”作为首选答案:

GitHub 存储库中可用的示例应用程序。

为了进一步改进

作为进一步改进的第一步,有一种方法可以通过以更复杂的方式嵌入问题和文档来提高文档检索性能。众所周知,使用 BERT 或通用语句编码器而不是使用 BM25[1][4][5]对它们进行矢量化可以提高性能。

还提出了一种方法,其中信息检索和答案提取被联合训练[6]。这次分别进行信息检索和答案抽取。因为信息检索不同于问题回答,所以系统是局部优化的。通过同时训练信息检索和答案抽取,系统可以得到很好的优化。

当然,你可以使用更好的答案抽取模型。根据小队 2.0 排行榜显示,有几款比 BERT 性能更好。比如艾伯特[7],伊莱克特拉[8],罗伯塔[9],XLNet[10]等等。你可以很容易地使用这些模型,因为 Huggingface transformers 支持它们。更多细节见变压器文档

GitHub 资源库:

参考

  1. 为段落重新排序调查 BERT 的成功与失败
  2. BERT:用于语言理解的深度双向转换器的预训练
  3. 小队:100,000+用于文本机器理解的问题
  4. 面向开放领域问答的密集段落检索
  5. ReQA:对端到端答案检索模型的评估
  6. 弱监督开放领域问答的潜在检索
  7. ALBERT:一个用于语言表达自我监督学习的 Lite BERT
  8. ELECTRA:将文本编码器预先训练成鉴别器而不是生成器
  9. RoBERTa:一种稳健优化的 BERT 预训练方法
  10. XLNet:用于语言理解的广义自回归预训练

构建事件驱动的数据管道,将数据从亚马逊 S3 复制到 Azure Storage

原文:https://towardsdatascience.com/building-an-automated-data-pipeline-to-copy-data-from-amazon-s3-to-azure-storage-2a1b8d1ac6eb?source=collection_archive---------18-----------------------

实践教程

使用 AWS Data Wrangler、亚马逊 S3 库存、亚马逊 S3 批处理操作、Athena、Fargate 和 AzCopy 进行多云批量文件传输

@jsweissphotoUnsplash 上的照片

您的企业可能需要定期将大量数据从一个公共云转移到另一个公共云。更具体地说,您可能会面临需要多云解决方案的要求。本文介绍了一种使用亚马逊 S3 库存、亚马逊 S3 批处理操作、Fargate 和 AzCopy 实现从 AWS S3 桶到 Microsoft Azure Blob 存储容器的自动化数据复制的方法。

方案

您的公司每天在内部生成新的 CSV 文件,压缩后总大小约为 100GB。所有文件的大小为 1-2gb,需要在每晚凌晨 3 点到 5 点之间的固定时间窗口上传到亚马逊 S3。在所有文件上传到 S3 后,您的企业决定将这些 CSV 文件从 S3 复制到 Microsoft Azure 存储。您必须找到一种简单快捷的方法来自动化数据复制工作流。

为了完成这项任务,我们可以建立一个数据管道,使用 AWS Data Wrangler、亚马逊 S3 库存、亚马逊 S3 批处理操作、Athena、Fargate 和 AzCopy 定期将数据从 S3 复制到 Azure 存储。

下图展示了管道解决方案的高级架构:

艾毅的建筑图

我们将涵盖的内容:

  • 创建一个包含私有和公共子网、S3 端点和 NAT 网关的 VPC。
  • 创建 Azure 存储帐户和 blob 容器,生成 SAS 令牌,然后添加防火墙规则以允许从 AWS VPC 到 Azure 存储的流量。
  • 在 S3 存储桶上配置每日 S3 库存报告。
  • 使用 Athena 仅过滤 S3 库存报告中的新对象,并将这些对象的存储桶名称和对象关键字导出到 CSV 清单文件中。
  • 使用导出的 CSV 清单文件创建 S3 批处理操作 PUT 复制作业,该作业将对象复制到配置了生命周期策略到期规则的目标 S3 存储桶。
  • 设置一个 Eventbridge 规则,调用 lambda 函数来运行 Fargate 任务,该任务将目标桶中具有相同前缀的所有对象复制到 Azure 存储容器。

先决条件

  • 设置 AWS 帐户
  • 设置 Azure 帐户
  • 安装最新的 AWS-CLI
  • 安装自动气象站 CDK-CLI
  • AWS CDK 的基本了解
  • 对 Docker 的基本了解

我们开始吧!

创建来源和目标 S3 时段

我们使用 CDK 在 AWS 上构建我们的基础设施。首先,让我们创建一个源桶来接收来自外部提供者或内部的文件,并设置每日清单报告,提供对象和元数据的平面文件列表。

接下来,创建一个目标存储桶作为临时存储,并在前缀/tmp_transition上配置生命周期策略到期规则。所有带有前缀的文件(如/tmp_transition/file1.csv)将被复制到 Azure,并在 24 小时后被生命周期策略删除。

使用以下代码创建 S3 存储桶。

创建 AWS VPC

接下来,我们需要创建具有公共和私有子网的 VPC、NAT 网关、S3 端点,并附加一个端点策略,该策略允许访问 Fargate 容器,我们将数据复制到 Azure 的 S3 桶就位于该容器中。

现在使用下面的代码定义您的 VPC 和相关资源。

创建 NAT 网关时,将在 AWS 中创建一个弹性 IP 地址。我们将需要 IP 地址来在步骤 3 中设置 Azure 存储防火墙规则。

正在部署 Azure 存储帐户

为了简化资源管理,我们可以使用 Azure 资源管理器模板(ARM 模板)在我们的 Azure 订阅级别部署资源。

我会假设你已经有一个 Azure 订阅设置。我们将使用云外壳来部署资源组、Azure 存储帐户、容器和防火墙规则,以允许来自特定 IP 地址的流量。

点击 Azure 门户标题栏的云壳图标,就会打开云壳。

运行以下命令进行部署:

az group create --name **examplegroup** --location australiaeastaz deployment group create --resource-group **examplegroup** --template-uri [https://raw.githubusercontent.com/yai333/DataPipelineS32Blob/master/Azure-Template-DemoRG/template.json](https://raw.githubusercontent.com/yai333/DataPipelineS32Blob/master/Azure-Template-DemoRG/template.json)  --parameters storageAccounts_mydemostroageaccount_name=**mydemostorageaccountaiyi** --debug

一旦部署了模板,我们就可以通过探索 Azure 门户的资源组来验证部署。所有部署的资源都将显示在资源组的概述部分。

让我们为我们的存储帐户创建一个防火墙规则:

  • 首先,转到我们刚刚部署的存储帐户。
  • 其次,点击设置菜单中的防火墙和虚拟网络。
  • 第三,检查您是否选择了允许来自选定网络的访问。
  • 然后,要授予对互联网 IP 范围的访问权限,请输入 AWS VPC 的公共 IP 地址(步骤 2)并保存。

然后,我们将生成共享访问签名(SAS)来授予对 Azure 存储资源的有限访问权限。

在 Cloudshell 中运行以下命令:

**RG_NAME**='**examplegroup**'
**ACCOUNT_NAME**='**mydemostorageaccountaiyi**' 
**ACCOUNT_KEY**=`az storage account keys list --account-name=$ACCOUNT_NAME --query [0].value -o tsv`
**BLOB_CONTAINER**=democontainer**STORAGE_CONN_STRING**=`az storage account show-connection-string --name $ACCOUNT_NAME --resource-group $RG_NAME --output tsv`SAS=`az storage container generate-sas --connection-string $STORAGE_CONN_STRING -n $BLOB_CONTAINER --expiry '**2021-06-30**' --permissions **aclrw** --output tsv`echo **$SAS**

我们将获得授权 (a)dd (d)elete (r)ead (w)rite 访问 blob 容器democontainer所需的 sa 和 URL。

se=2021-06-30&sp=racwl&sv=2018-11-09&sr=c&sig=xxxxbBfqfEppPpBZPOTRiwvkh69xxxx/xxxxQA0YtKo%3D

让我们回到 AWS,将 sa 放到 AWS SSM 参数存储中。

在本地终止符中运行以下命令。

aws ssm put-parameter --cli-input-json '{
  "Name": "**/s3toblob/azure/storage/sas**",
  "Value": "**se=2021-06-30&sp=racwl&sv=2018-11-09&sr=c&sig=xxxxbBfqfEppPpBZPOTRiwvkh69xxxx/xxxxQA0YtKo%3D**",
  "Type": "**SecureString**"
}'

定义 Lambda 函数和 AWS 数据管理器层

现在,让我们来看看λ函数。我们将创建三个 lambda 函数和一个 lambda 层:

  • fn_create_s3batch_manifest 和 DataWranglerLayer
  • fn _ 创建 _ 批处理 _ 作业
  • fn _ 流程 _ 转移 _ 任务

fn_create_s3batch_manifest 和 AWS Data Wrangler 层

这个 lambda 函数使用 AWS Data Wrangler 的 Athena 模块来过滤过去 UTC 日期的新文件,并将文件列表保存到 CSV 清单文件中。

将下面的代码复制到 CDK stack.py,从 这里 下载awswranger-layer zip 文件。

然后用下面的代码创建./src/lambda_create_s3batch_manifest.py:

在上面的代码中,我们使用 Athena query 创建 Glue 数据库、表,并每天向该表添加一个分区。然后 lambda 执行 except query 来返回两个日期分区之间的差异。

注意start_query_execution是异步的,因此不需要等待 Lambda 中的结果。一旦查询被执行,结果将作为 CSV 文件保存到s3_output=f"s3://{os.getenv('DESTINATION_BUCKET_NAME')}/csv_manifest/dt={partition_dt}"

fn_create_batch_job 和 S3 通知

在本节中,我们将创建一个 lambda 函数fn_create_batch_job,并使亚马逊 S3 能够在一个 CSV 文件被添加到亚马逊 S3 Bucket /csv_manifest前缀时发送一个通知来触发fn_create_batch_job。将下面的代码放到 CDK 的 stack.py 中:

用下面的代码创建./src/lambda_create_batch_job.py:

Lambda fn_create_batch_job函数创建 S3 批处理操作作业,将 CSV 清单中列出的所有文件复制到 S3 目的地桶/tmp_transition prefix

S3 批处理操作是亚马逊 S3 的一项数据管理功能,可以让您大规模管理数十亿个对象。要启动一个 S3 批处理操作作业,我们还需要设置一个 IAM 角色S3BatchRole和相应的策略:

fn_process_transfer_task 和 Eventbridge 自定义规则

我们将创建一个 Eventbridge 定制规则,通过 AWS CloudTrail 跟踪 Amazon EventBridge 中的 S3 批处理操作作业,并将处于完成状态的事件发送到目标通知资源fn_process_transfer_task

Lambda fn_process_transfer_task然后会以编程方式启动一个 Fargate 任务,将前缀为/tmp_transition的文件复制到 Azure 存储容器democontainer

用以下代码创建./src/lambda_process_s3transfer_task.py:

现在,我们已经设置了无服务器部分。让我们转到 Fargate 任务并处理数据复制。

创建 AWS Fargate 任务

我们将创建:

  • 安装了带有 AzCopy 的 ECR 映像。 AzCopy 是一个命令行实用程序,可以用来将 blobs 或文件复制到存储帐户或从存储帐户复制。
  • 具有 Fargte 任务的 ECS 集群。

让我们开始吧。

  1. 构建 ECS、ECR 和 Fargate 堆栈。

2.构建一个 Docker 镜像并在那里安装 Azcopy。

注意,要使用来自 AWS 的 AzCopy 传输文件,我们需要在容器中设置 AWS 凭证。我们可以使用以下方法检索 AWS 凭据:

curl [http://169.254.170.2/$AWS_CONTAINER_CREDENTIALS_RELATIVE_URI](http://169.254.170.2/$AWS_CONTAINER_CREDENTIALS_RELATIVE_URI)

3.将 Docker 图像推送到 ECR

eval $(aws ecr get-login --region ap-southeast-2 --no-include-email)docker build . -t YOUR_ACCOUNT_ID.dkr.ecr.ap-southeast-2.amazonaws.com/YOUR_ECR_NAMEdocker push YOUR_ACCOUNT_ID.dkr.ecr.ap-southeast-2.amazonaws.com/YOUR_ECR_NAME

太好了!我们有我们需要的!你可以在我的 Github Repo 中找到 CDK 项目的完整解决方案。克隆存储库并部署堆栈:

cd CDK-S3toblob 
pip install -r requirements.txt
cdk deploy

一旦成功创建了堆栈,导航到 AWS CloudFormation 控制台,找到我们刚刚创建的堆栈,并转到 Resources 选项卡以找到部署的资源。

现在是时候测试我们的工作流程了;转到 S3 源斗demo-databucket-source。在不同的文件夹中上传尽可能多的文件(前缀)。等待 24 小时,等待下一份库存报告生成;然后,你会看到整个管道开始运行,文件最终会被复制到 Azure democontainer

我们应该可以看到 Fargate 任务的日志,如下图所示。

我们还可以使用 CloudWatch Container Insights 对 ECS 资源进行监控、故障排除和设置警报。

结论

在本文中,我介绍了从 AWS S3 到 Microsoft Azure Storage 的自动化数据复制方法。我带你了解了如何使用 CDK 部署 VPC、AWS S3、Lambda、Cloudtrail、Fargte 资源,向你展示了如何使用 ARM 模板部署 Azure 服务。我向您展示了如何使用 AWS Wrangler 库和 Athena query 来创建表和查询表。

我希望这篇文章对你有用。你可以在我的 GitHub repo 中找到完整的项目。

构建自动化机器学习管道:第三部分

原文:https://towardsdatascience.com/building-an-automated-machine-learning-pipeline-a74acda76b98?source=collection_archive---------56-----------------------

机器学习

训练和评估模型,解释模型结果和最终结论

David EspinaUnsplash 上拍摄的照片

在这个系列文章中,我们将我们的课程设置为构建一个 9 步机器学习(ML)管道,并使用 DockerLuigi 来自动化它——只剩下一步了😉。我们将最终组装这些步骤,以便它可以在任何生产系统上运行,而不需要专门的配置和设置。

我们遵循直观的 ML 管道来构建葡萄酒评级预测器:

  1. 理解、清理和格式化数据
  2. 探索性数据分析
  3. 特征工程和预处理
  4. 设置评估指标并建立基线
  5. 基于评估度量选择 ML 模型
  6. 对所选模型执行超参数调整
  7. 训练和评估模型
  8. 解释模型预测
  9. 得出结论并记录工作

这个问题本身是一个监督回归问题,因为我们试图使用定义的特征集来确定(目标变量)。是一个介于 80 和 100 之间的数字,表示葡萄酒的质量。

我们的原始数据看起来像这样:

我们从阐述业务问题和解释我们的目标开始:

该项目的目标是使用样本数据集建立一个葡萄酒评级预测器,以显示良好的预测是可能的,也许是作为一个更大项目的概念证明。

通过第一个笔记本中所涵盖的管道的前三个步骤,我们清理、理解并格式化了数据集。此外,我们添加并转换了一些特性,为最终在任何 ML 模型中使用的训练和测试数据集做好了准备:

的第二个笔记本中,我们选择的评估指标为【MSE】,这是一个适合我们问题的指标。然后,我们使用 集合评估指标&建立基线 中的均方差计算逻辑计算基线(结果为 9.01 )。

然后,我们测试了几种不同的 ML 算法,并决定在 选择基于评估指标的 ML 模型 步骤中使用随机森林回归器 form scikit-learn 构建葡萄酒评级预测器。我们对所选算法进行了超参数调整,并在 对所选模型进行超参数调整 步骤中进一步改进了我们的模型性能。

我们已经显著降低了基线 MSE,从 9.01 降低到 4.99,从而实现了 45%的改善!

在本文中,我们将完成剩下的最后三个步骤:我们将在测试集上评估我们的模型并解释预测。我们将在最后一步总结整个流程并报告亮点。

这篇文章背后的代码可以在这个笔记本里找到。GitHub 上提供了完整的项目:

[## cereniyim/葡萄酒评级预测模型

在这个项目中,我为一个在线葡萄酒卖家构建了一个葡萄酒评级预测器。这款葡萄酒预测器旨在显示良好的…

github.com](https://github.com/cereniyim/Wine-Rating-Predictor-ML-Model)

页(page 的缩写)s:我鼓励你在我们开始之前阅读以前的文章,如果你还没有的话!

在我们开始之前:

我们将要使用的数据集在[notebooks/transformed](https://github.com/cereniyim/Wine-Rating-Predictor-ML-Model/tree/master/notebooks/transformed)中提供。我们在之前的笔记本中保存的模型可在与本笔记本相同的目录中找到。

在本笔记本中,我们将需要以下 Python 库:

让我们加载数据集和模型:

X_train = pd.read_csv("transformed/X_train.csv")
y_train = pd.read_csv("transformed/y_train.csv")

X_valid = pd.read_csv("transformed/X_valid.csv")
y_valid = pd.read_csv("transformed/y_valid.csv")

test_features = pd.read_csv("transformed/test_features.csv")
test_target = pd.read_csv("transformed/test_target.csv")random_forest_model = (pickle
                       .load(
                           open('random_forest_model.sav', 
                                'rb')))

随机森林模型的参数

使用我们在第二篇文章中使用的相同函数将它们转换成数组:

X = pd.concat([X_train, X_valid]) 
y = pd.concat([y_train, y_valid])X_array = convert_features_to_array(X) 
y_array = convert_target_to_array(y)X_test_array = convert_features_to_array(test_features)
y_test_array = convert_target_to_array(test_target)

7.训练和评估模型

训练集 将用于映射特征(X_array)和目标(y_array)之间的模式,并构建葡萄酒评级预测器。这个过程叫做 训练模型 或者 建立模型 。映射将通过在我们的随机森林模型上调用的fit方法来完成。

random_forest_model.fit(X_array, y_array)

在评估模型之前,实际目标值(y_test_array)从测试集(X_test_array)中拿出来,用作与预测值的比较。两者都将用于使用模型拟合期间发现的模式生成葡萄酒的点/ 等级。

将测试集输入模型并生成预测称为 评估模型。 当我们在我们的随机森林模型上调用predict方法时,预测的数组就生成了。

predictions = random_forest_model.predict(X_test_array)

让我们看看我们的模型在测试集(我们的模型从未见过的训练数据集的一部分)上的表现如何:

8.解释模型预测

作为数据科学家,我们是机器学习技术的解释者。我们应该能够解释解决方案是如何工作的,以及需要进一步改进的地方。这发生在 解释模型预测 步骤。

我们将从以下角度解释模型预测:

  1. 可视化单个决策树
  2. 点的实际值与预测值进行比较
  3. 查看特性的重要性
  4. 检查模型的单个预测

8.1.可视化单个决策树

我们已经在上一个笔记本中定义了随机森林算法的工作原理:

随机森林回归器 是一种集成算法,它一次构建多个决策树,并在数据集的各种子样本和各种特征子集上训练它们。

我们的随机森林算法由 200 棵决策树组成。如果我们一次绘制所有的树,它可能不会给我们提供太多的信息。然而,可视化单个决策树足以理解葡萄酒评级预测器如何产生预测。

让我们将随机森林回归算法中决策树的前 3 个节点可视化,以进一步理解我们的算法是如何工作的:

看这张图,你可以把决策树想象成一系列的是/否问题,像流程图一样被问和回答。这个特定的树是使用总共 8948 个数据点中的 5645 个数据点构建的,这是完整数据集的随机子样本。

通过询问区域 _1 是否小于 320.5 来启动根节点。可以容易地认识到,初始 MSE 与 MSE 的基线估计相同。预测是 88.425。

根节点被分成两个分别由 5018(初始子样本的 88%)和 627 个数据点(初始子样本的 12%)组成的真和假子节点。

根节点的假子节点询问价格是否小于 30.5,并使用初始子样本的 12%来回答这个问题。与前一个节点相比,MSE 略有改善,这表明节点更加连贯。预测是 87.818。

这个过程迭代地继续,直到到达叶节点。当节点的 MSE 没有进一步提高时,或者当达到叶节点的指定标准(对于我们的模型,在叶节点中具有最少 2 个样本)时,该过程停止。

8.2.将 P 的实际值与预测值进行比较

为了理解实际的和预测的有什么不同,让我们用柱状图将它们并排可视化:

PlotPredictedVSActual(predictions, y_test_array)

回想一下呈正态分布,95%的位于 82.5–94.5 范围内。我们的模型成功预测了该范围内的,但未能预测低于 84 的点和高于 94 的点,它们是分布的不太频繁的值。这也是预测分布较窄的原因。

这是我们的葡萄酒评级预测器的一个更进一步和有趣的改进领域,但我们现在不会触及它。

8.3.查看特性的重要性

评估模型的另一种方法是查看模型认为哪些特性最重要。以下是每个特征在确定一个时的相对贡献值:

PlotFeatureImportances(random_forest_model, test_features.columns)

根据模型,最强的预测因子是价格。其次是 region_1yearvarietytaster_name 用于预测葡萄酒的

这些符合我们在 探索性数据分析 步骤中得出的初步观察结果。

价格是葡萄酒质量和年份的指标。因此,我们期望它成为该模型的一个基本特征。

前 5 大功能重要性

前 5 个特征占总重要性的 80%。这难道不是帕累托法则的又一个美丽例子吗?

从提取的特征来看,年份是其中最重要的,葡萄酒是否起泡是点数最不重要的预测因素。

另一个重要的观察结果是,从品种描述中提取的与音符和味道相关的特征对我们的预测器来说成为中等重要的特征。综合运用自然语言处理和情感分析可能有助于从描述变化特征中发现更多有价值的特征。

到目前为止,我们已经从总体的角度解释了我们的葡萄酒评级预测器的预测。现在,让我们从我们的模型中检验一个单独的预测。

8.4.检验单个预测

我们将从一个名为 LIME(本地可解释的模型不可知解释)的解释器库中获得一些帮助来观察一个预测。这个库在数据点浓缩的地方创建一个近似的局部模型,并使用这个模型来解释任何 ML 算法的单个预测。

我们将观察从残差的最大值中检测到的最不正确的预测。然后,我们将创建LimeTabularExplainer对象。

*# find the residuals and wrong prediction*
residuals = abs(predictions - y_test_array)
wrong = X_test_array[np.argmax(residuals), :]*# Create a lime explainer object*
explainer = lime.lime_tabular.LimeTabularExplainer(
    training_data = X_array, 
    mode = 'regression',
    training_labels = y_array,
    feature_names = test_features.columns)

下面的代码块将描述葡萄酒评级预测器是如何得出错误预测的。它预测的是 90.3 分,而实际是 82 分。这是该模型最不正确的预测:

*# Display the predicted and true value for the wrong instance*
print('Prediction: **{}**'
      .format(random_forest_model.predict(wrong.reshape(1, -1))))
print('Actual Value: **{}**' 
      .format(y_test_array[np.argmax(residuals)]))

*# Explanation for wrong prediction*
wrong_exp = explainer.explain_instance(
    data_row = wrong, 
    predict_fn = random_forest_model.predict)

*# Plot the prediction explaination*
figsize(20, 10)
plt.rcParams['font.size'] = 24wrong_exp.as_pyplot_figure()
plt.title('Explanation of a Wrong Prediction', size = 24)
plt.xlabel('Effect on Prediction', size = 24)

该图显示了每个特征对错误预测示例的影响。积极的贡献用绿色表示,消极的贡献用红色表示。

由于价格是最强的预测值,并且该示例中的价格高于 40,这显著增加了模型对点数的预测。另一方面,国家是红色是白色通过减少来平衡预测。这是一个很好的例子,说明机器学习算法也可能像我们人类一样陷入常见的陷阱。你难道没有买过昂贵的酒来保证高质量吗🙂

其实是吗?图像来源:黑书系列—第一季第三集
blackbooks.gifglobe.com上的愤怒的葡萄

9.得出结论并记录工作

因为我们是机器学习技术的解释者,所以根据我们的受众来定制我们的信息和交付内容是很重要的。特别是,如果你向你的经理或客户展示你的结果,你必须非常简洁和相关。你可能期望他们会对你的推理和结果更感兴趣,所以在展示你的发现时,要关注他们😉

如果你还记得第一篇文章,我在 Data Revenue 建立了这个项目作为 ML 工程师面试过程的带回家作业。作业的目标是通过观察葡萄酒等级预测器的结果来决定是否建立一个全面生产的解决方案。因此,我准备了一份报告,介绍了我的决定和 9 步 ML 流程中最关键的几个要点,并得出了结论。

我提议依靠构建完整的生产解决方案

给定样本数据集、确定的功能集和调整的随机森林回归器,答案是,完全生产解决方案适用于这些。

该模型显著降低了基线 MSE(45%),并在测试集上产生了 4.93 MSE。这是构建全面生产解决方案的依据。

..并且强调了最重要的两个发现:

-葡萄酒评级预测器可以推断出葡萄酒的分数,其合理方差(MSE)为 4.9。

-最有用的点数预测器被确定为价格、区域 _1、年份、品种和品尝者 _ 姓名。

..并且我指出了进一步改进的地方:

扩展预测范围:以便模型可以预测不太频繁的目标值。当前的样本数据集主要是来自最主要国家(美国、意大利、法国、西班牙)的葡萄酒,输入更多来自非当前国家的数据可能有助于模型更好地学习不太频繁点的决定因素。

利用 NLP 提取更强的预测因子:目前最强的预测因子是“价格”。从与葡萄酒味道和注释相关的描述中提取更多特征可以帮助创建像价格这样的强预测器,从而减少模型的预测误差。

你可以在这里找到完整的报道

它最终成为一份全面而简洁的报告。此外,我把代码放在一边,因为报告是为一个假设的客户准备的,他的统计学和 ML 知识是有限的。

你可以把你的 Jupyter 笔记本转换成文档,为你自己的项目或学习目的准备一份类似的总结报告(我准备了 PDF 格式)。您可以使用File > Download As中提供的各种下载选项。HTML 或 LaTeX 格式很容易使用。(如果您的机器上安装了 LaTeX Studio ,那么您可以稍后将其转换为 pdf 格式)。你可以在这里阅读更多关于那个的内容。

Jupyter 笔记本中提供了一些下载选项

我们应该永远记住,数据科学中的“科学”是有目的的,最终是为了非科学的目的。我们的工作主要用于支持商业决策过程或解决现实世界的问题。展示结果(口头或书面)并根据你的听众调整语言将是你拥有的一项有价值的技能。

完整的 9 步 ML 管道的结论

从构建原始数据到理解它们的意义,我们走过了漫长的道路。我们从清理和格式化数据开始,然后我们训练和调整选择的模型(随机森林回归)。通过评估模型和解释其结果,我们总结了 9 步流程。最后,我们在一份报告中强调了该项目的最重要成果。

  1. 理解、清理和格式化数据
  2. 探索性数据分析
  3. 特征工程和预处理
  4. 设置评估指标并建立基线
  5. 基于评估度量选择 ML 模型
  6. 对所选模型执行超参数调整
  7. 训练和评估模型
  8. 解释模型预测
  9. 得出结论并记录工作

在本系列文章中,我们按顺序遵循了这些步骤。然而,当我在这个项目中工作的时候,我有 20%的时间在这些步骤之间来来回回。总而言之,我相信建立一个机器学习解决方案是一个迭代和实验的过程,所以不要犹豫改进和重温以前的步骤!

特别感谢

在这里我要感谢威尔·科尔森。他是我写数据科学/机器学习项目的灵感来源之一。

我第一次从的一篇文章中了解到这个管道,并根据我自己的目标和问题进行了调整。现在,我在构建任何类型的机器学习/数据科学项目时,都使用这条管道作为指南。它帮助我组织我的思维过程,并看到机器学习的各个元素是如何连接的!

我们还有最后一篇文章要写,它将使用 DockerLuigi 来自动化这条流水线。下周敬请关注!

[## 构建自动化机器学习管道:第四部分

利用 Docker 和 Luigi 实现管道自动化

towardsdatascience.com](/building-an-automated-machine-learning-pipeline-part-four-787cdc50a12d)

感谢阅读🙂请随意使用这个管道,代码和用于你自己的项目。

对于评论或建设性的反馈,您可以通过回复、 TwitterLinkedin 联系我!

在那之前保持安全和健康👋

构建自动化机器学习管道:第四部分

原文:https://towardsdatascience.com/building-an-automated-machine-learning-pipeline-part-four-787cdc50a12d?source=collection_archive---------29-----------------------

机器学习

利用 Docker 和 Luigi 实现管道自动化

免责声明:本文章系列不是关于 Docker 和 Luigi 的教程。这是“构建自动化机器学习管道”系列文章的最后一篇,该系列文章重点关注构建端到端的 ML 管道,并展示如何使用这两种工具的某些元素来自动化它。如果你阅读了上面链接中的前几篇文章,这篇文章会对你更有意义。

照片由埃里克·克鲁尔Unsplash 上拍摄

在这个系列文章中,我们将我们的课程设置为构建一个 9 步机器学习(ML)管道,并使用 Docker 和 Luigi 将其自动化。

  1. 理解、清理和格式化数据
  2. 探索性数据分析
  3. 特征工程和预处理
  4. 设置评估指标并建立基线
  5. 基于评估度量选择 ML 模型
  6. 对所选模型执行超参数调整
  7. 训练和评估模型
  8. 解释模型预测
  9. 得出结论并记录工作

由于这个管道,我们构建了我们的 ML 解决方案,并将其命名为 葡萄酒评级预测器 ,因为我们试图使用样本数据集来推断用表示的葡萄酒质量。在第一篇文章中,我们将葡萄酒评级预测器的要求定义为:

-可以理解因为我们的观众可能对统计学和 ML 了解有限。

-高性能因为完整的生产数据集可能有数百万行。

-自动化,可以在任何生产系统上运行,不需要专门的配置和设置。

到目前为止,我们已经满足了的可理解性并解决了的性能——在某种程度上:

  • 我们选择了一个可理解的模型,随机森林回归器,作为葡萄酒评级预测器的底层 ML 算法。此外,我们已经在第二篇文章和第三篇文章中解释了随机森林回归器的工作原理及其超参数
  • 我们通过对随机森林回归器执行超参数调优,构建了一个性能模型。尽管如此,我们仍然有它的空间。

今天,我们将戴上软件工程师的帽子,解决最后一个需求— 自动化。我们将从 ML 管道中采取以下步骤,在 Docker 容器上运行这些步骤,并将它们与 Luigi 任务连接起来。(不要担心,我们将在整篇文章中详细阐述它们🙂)

ML 管道的自动化流程

如果你已经注意到,我们有两个额外的步骤,因为一个典型的现实世界的 ML 管道开始于从一个源获取数据。此外,我们包括了训练-测试数据集分割,其余的是 ML 管道的已知步骤。

为了运行上面的流程,我们将首先查看 Docker 和 Luigi,并理解它们在本系列文章的上下文中的用法。因此,我们将从解释这些工具及其必要元素开始。

然后,我们将使用这些工具来连接这些点。最终,我们将把这些碎片放在一起,运行流程,这将完成我们的自动化任务!

  1. 什么是 Docker,它是如何用于葡萄酒等级预测的?
  2. 什么是 Luigi,为什么选择它作为编排工具?
  3. 把 Docker 和 Luigi 的碎片放在一起
  4. 运行和自动化管道!

您可以在这里找到 GitHub 资源库:

[## cereniyim/葡萄酒评级预测模型

在这个项目中,我为一个在线葡萄酒卖家构建了一个葡萄酒评级预测器。这款葡萄酒预测器旨在显示良好的…

github.com](https://github.com/cereniyim/Wine-Rating-Predictor-ML-Model)

在我们开始之前,还有一个免责声明:

免责声明:库的结构和download_data.pyutil.pydocker-clean.shdocker-compose.yml文件提供给我作为代码挑战的基础。剩下的代码是我写的。

1.什么是 Docker,它是如何用于葡萄酒等级预测的?

Docker 是什么?

维基百科对 Docker 的官方定义如下:

Docker 是一套平台即服务产品,它使用操作系统级虚拟化来交付名为容器的软件包中的软件。

我将它定义为为你的应用程序/软件/数据科学/ML 项目创建虚拟环境,这样它可以在任何有 Docker 的系统上无缝运行。

我们如何用 Docker 创建虚拟环境?

经由容器。它们是运行您的应用程序的独立虚拟环境,对于我们的情况,是 ML 解决方案。

我们如何创建 Docker 容器?

来自图像。它们是用于创建容器的模板。它们包含关于底层操作系统、环境和库的信息,以及版本。

我们如何创建 Docker 图像?

通过阅读来自档案的指令。它是一组用户可以在命令行上调用来创建图像的指令和命令。你可以用docker build Dockerfile -t <image_name>命令在你的系统中创建一个本地镜像。

图像和容器是如何连接的?

容器图像的运行实例。运行映像时,您会创建一个容器,它是一个隔离的环境。

我们是如何使用 Docker 构建葡萄酒评级预测器的?

我们使用 Docker 构建虚拟环境并管理库的依赖关系,这样我们就可以无缝地运行流程。下面的 自动化流程 被设计成每个盒子在一个单独的 Docker 容器上运行。

自动化流程

葡萄酒等级预测库的结构

我相信你已经注意到了存储库中一些我们还没有提到的目录和文件。您可以将download_datamake_datasetclean_dataextract_featurestransform_dataimpute_datatrain_modelevaluate_model目录视为自动化流程中盒子的边界,该流程包含 docker 文件和 Python 文件形式的源代码。

在看如何创建 Dockerfile 之前,我们先来了解一下base_docker在这里的用法。

在自动化流程中使用基本 Docker

自动化流程的每一步都在一个单独的容器上运行。然而,它们是用 Python 编写的模块,它们之间唯一的区别是使用的库。因此,base_docker用于指定公共环境变量并安装所需的 Python 版本和库。它也有助于葡萄酒评级预测器的性能,只需安装一次所需的软件包,而不是每次都安装在单独的容器上。

如何写 Dockerfile 文件

当创建 docker 文件时,就像在您的机器上创建本地环境一样开始执行这些步骤。考虑到您的目标是使用 Python 部署 ML 解决方案,您可以在 Docker Hub 中的现有 Python 映像之上构建您的映像。这里很重要的一点是选择一个适合你情况的现有 Python 图像,因为有几个版本和大小可供选择。

FROM python:3.7-slim
ENV LC_ALL=C.UTF-8
ENV LANG=C.UTF-8
ENV PYTHONPATH=/opt/orchestrator:$PYTHONPATH

COPY requirements.txt /opt/base_docker/

RUN pip install -r /opt/base_docker/requirements.txt

WORKDIR /opt/base_docker/

选择了slim变量,并指定了 Python 版本3.7,与我的本地系统中的 Python 版本相同。

requirements.txt 截图

ENV命令用于更新容器安装的底层操作系统的PATH环境变量(对于我们的例子是Python 3.7-slim)。

带有requirements.txt中提到的版本的库被复制并与pip_install一起安装。除了前面提到的 Python 库之外,还安装了click——稍后将详细介绍它的用法。

作为最后一条指令,Docker 容器的工作目录用WORKDIR 命令定义。

现在,我们的base_docker已经准备好了,从自动化流程中探索任何docker 文件就足够了,因为所有其他docker 文件都是用相同的逻辑编写的。这里有一个来自clean_data的例子:

FROM code-challenge/base-dockerCOPY . /opt/clean_data/

WORKDIR /opt/clean_data/

现在我们的基地形象变成了base_docker。复制clean_data目录中的每个文件,并创建 Docker 容器的工作目录。

是时候看看 Spotify 心爱的 Luigi 了!

2.什么是 Luigi,为什么选择它作为编排工具?

什么是路易吉?

来自文件的官方定义如下:

Luigi 是一个 Python 包,可以帮助您构建批处理作业的复杂管道。它处理依赖关系解析、工作流管理、可视化、处理故障、命令行集成等等。

它基本上是一个编排工具,将许多任务缝合在一个有向无环图(DAG)结构中:

有向无环图示例。图片来源:维基百科

为什么选择它作为编排工具?

当你构建一个 ML 解决方案,随着规模的增长,这很容易变得复杂和混乱。Luigi 是一个健壮的工作流管理工具,可以防止混乱和复杂。

Luigi 如何作为一个编排工具工作?

Luigi 有两个基本构件:任务目标

什么是 Luigi 任务?

工作流的步骤是任务,通常是一个单独的工作单元,在这里完成计算。Luigi 工作流建立在任务之上。

Luigi 任务是如何连接的?

每个任务都与一个目标相关联。目标可以是文件、工作流中的检查点或任务生成的任何类型的输出

自动化流程中的每一步都是一项任务

我们正在转储data_root中每个任务生成的输出:

在流程中,除了初始任务download_data之外,每个任务都依赖于前一个任务的输出。每个文件都作为一个Parameter传递,这样它就可以被工作流中的后续步骤使用。

每个任务产生的输出

在设计工作流时,Luigi 推荐原子结构:每个任务应该有一个单独的文件作为输出,就像在download_datatrain_model任务中一样。

对于其余的任务,我们用SUCCESS旗模拟原子性。当我们在训练和测试数据集上执行计算时,在将输出文件转储到目录中之后,我们创建一个SUCCESS标志作为任务的输出。这样,下一个任务只检查是否存在一个SUCCESS标志。

我们如何定义任务依赖关系?

任务依赖是用requires()方法定义的。以下是 Luigi 文档中的任务概要:

Luigi 任务的概要。图片来源: luigi.readthedocs.io

对于我们的例子,任务依赖是显而易见的,因为我们定义了一个线性的自动化流程。

我们如何运行 Luigi 工作流?

不幸的是,Luigi 没有触发机制。如果要运行 Luigi 工作流,可以使用命令行指定模块名称和项目目录中的最后一项任务:

luigi --module <modul_name> <task_name> 

当工作流被触发时,Luigi 检查上一步的输出是否存在。如果没有,那么它向后检查前一步骤的输出是否存在。对于我们的例子,这将是train_modelimpute_datatransform_dataextract_featuresclean_datamake_datasetdownload_data

如果来自流程中任何一步的任务输出存在,Luigi 将从它离开的地方继续流程。这是一个非常有用和重要的特性,可以防止 ML 管道在包含部分数据时崩溃。

现在我们知道了 Luigi 和 Docker 的基本元素,我们可以继续完成这幅画了!

3.把 Docker 和 Luigi 的碎片放在一起

我们将把 Docker 和 Luigi 的片段放在docker-compose.ymlorchestrator目录中。

数据收入的令人敬畏的工程师提供的效用函数已经使我能够连接 Docker 和 Luigi 的片断。util.py中可用的函数和类在连接 Docker 和 Luigi 的葡萄酒评级预测器中发挥了重要作用。例如,DockerTask —我们作为参数在任务之间传递的对象— 通常在 Luigi 中不能作为参数使用。

首先,让我们看看orchestrator目录中的task.py,在那里我使用实用程序构建了自动化流程。那么,让我们来理解一下docker-compose.yml的目的。

在 Task.py 中定义任务

  • Luigi 参数:每个任务都以一个 Luigi Parameter对象开始,我们在其中指定任务的输入/输出文件和目录。例如,我们使用测试集评估 ML 模型的最后一个任务EvaluateModeltest_featurestest_target和经过训练的模型文件(model.sav)作为输入 Luigi Parameter。由于这个任务创建了多个文件作为输出(PredictionsVSActuals.pngFeatureImportances.png),我们还定义了一个SUCCESS标志来模拟原子性。
  • Image: 我们定义使用哪个 Docker 图像来创建容器。
  • 依赖任务:如果有依赖任务,在这里用requires()方法定义。EvaluateModel的从属任务是TrainModel
  • 命令:容器要执行的命令。此处命令用相关参数调用evaluate_model.py中可用的源代码。
  • 输出:output()方法返回一个或多个目标对象(输出文件或SUCCESS标志)

回想一下,流程中每一步的源代码都以 Python 文件的形式存在于各自的目录中。我们来看看train_model的源代码:

在定义了助手函数convert_features_to_arrayconvert_target_to_array之后,我们定义了必要的点击命令。

点击,如官方文件中所定义:

这是一个 Python 包,用于以可组合的方式用尽可能少的代码创建漂亮的命令行界面。

如何使用点击命令。图片来源:click.palletsprojects.com

我们使用 Click 从命令行向 python 脚本传递参数。最后,我们使用我们在训练数据集上的 对所选模型步骤 执行超参数调整中决定的微调参数来训练模型。最后,我们将训练好的模型保存在model.sav文件中作为输出。

Docker 编写器的使用

根据 Docker 文档编写 Docker 的官方定义如下:

Compose 是一个定义和运行多容器 Docker 应用程序的工具。使用 Compose,您可以使用一个 YAML 文件来配置您的应用程序的服务。然后,只需一个命令,您就可以从您的配置中创建并启动所有服务。

这个定义适合我们的情况,因为我们在包括base_docker在内的 9 个容器上运行自动化流程。你可以在这里阅读更多关于docker-composeT20 的用法和命令。

这里对我们来说重要的部分运行 Luigi 任务的命令写在这里,所以当我们通过docker-compose运行容器时,它将通过luigi — module task EvaluateModel-scheduler-host luigiid命令触发自动化流程。

4.运行和自动化管道!

这是关键时刻!我们将运行自动化流程,并自动化 9 步 ML 管道。这是最后一步,再坚持一分钟😉

1。打开您的终端并导航到项目目录

对我来说这是:

cd GitHub/Wine-Rating-Predictor-ML-Model

2.构建 Docker 容器

我们将使用一个 shell 脚本来构建 Docker 容器build-task-images.sh,而不是一个接一个地构建多个 Docker 容器

./build-task-images.sh 0.1

下面的消息显示容器构建成功。因此,我们可以触发自动化流程了。

**Successfully tagged code-challenge/evaluate-model:0.1**

4.从 Docker 编写触发自动化流程:

我们将通过以下方式触发工作流

docker-compose up orchestrator

在写这个命令时,Luigi 检查每个任务是否完成,并输出以下信息:

**Checking if EvaluateModel(
    no_remove_finished=False,
    in_test_features_csv=/usr/share/data/interim/test_features.csv,
    in_test_target_csv=/usr/share/data/interim/test_target.csv,
    in_trained_model=/usr/share/data/output/model.sav,
    out_dir=/usr/share/data/output/, 
    flag=.SUCCESS_EvaluateModel)** **is complete****Checking if TrainModel(
   no_remove_finished=False,
   in_train_features_csv=/usr/share/data/interim/train_features.csv,
   in_train_target_csv=/usr/share/data/interim/train_target.csv,
   out_dir=/usr/share/data/output/)** **is complete****Checking if ImputeData(
    no_remove_finished=False,
    in_train_csv=/usr/share/data/interim/train_transformed.csv,
    in_test_csv=/usr/share/data/interim/test_transformed.csv,
    out_dir=/usr/share/data/interim/, 
    flag=.SUCCESS_ImputeData)** **is complete****Checking if TransformData( 
  no_remove_finished=False,
  in_train_csv=/usr/share/data/interim/train_features_extracted.csv,
  in_test_csv=/usr/share/data/interim/test_features_extracted.csv,
  out_dir=/usr/share/data/interim/, 
  flag=.SUCCESS_TransformData)** **is complete****Checking if ExtractFeatures(
    no_remove_finished=False,
    in_train_csv=/usr/share/data/interim/train_cleaned.csv,
    in_test_csv=/usr/share/data/interim/test_cleaned.csv,
    out_dir=/usr/share/data/interim/, 
    flag=.SUCCESS_ExtractFeatures)** **is complete****Checking if CleanData(
    no_remove_finished=False,
    in_train_csv=/usr/share/data/interim/train.csv,
    in_test_csv=/usr/share/data/interim/test.csv,
    out_dir=/usr/share/data/interim/, 
    flag=.SUCCESS_CleanData)** **is complete****Checking if MakeDatasets(
    no_remove_finished=False,
    in_csv=/usr/share/data/raw/wine_dataset.csv,
    out_dir=/usr/share/data/interim/, 
    flag=.SUCCESS_MakeDatasets)** **is complete****Checking if DownloadData(
    no_remove_finished=False, 
    fname=wine_dataset, 
    out_dir=/usr/share/data/raw/,
    url=**[**https://github.com/datarevenue-berlin/code-challenge-2019/releases/download/0.1.0/dataset_sampled.csv**](https://github.com/datarevenue-berlin/code-challenge-2019/releases/download/0.1.0/dataset_sampled.csv)**)** **is complete**

然后一个接一个地运行任务。对我们来说另一个重要的信息是:

**INFO:evaluate-model:Mean square error of the model is: 4.95**

成功运行后,您会看到一个🙂:

奖励点:让我们看看 Luigi 是如何从
的流程中恢复的。假设您准备了训练数据集并运行了自动化流程,然后您将从训练模型开始构建接下来的步骤。Luigi 不会重复成功运行之前的步骤,在这种情况下只会运行TrainModelEvaluateModel任务。

结论

我会说哇!

这是一个跨越 4 篇文章和一个月⛵️的旅程

我们从头开始,通过第一、第二和第三篇文章分析了 9 步 ML 管道的每一步。

[## 构建自动化机器学习管道:第一部分

数据清理、探索性数据分析和特征工程步骤

towardsdatascience.com](/building-an-automated-machine-learning-pipeline-part-one-5c70ae682f35) [## 构建自动化机器学习管道:第二部分

设置评估指标&建立基线、选择算法和执行超参数调整…

towardsdatascience.com](/building-an-automated-machine-learning-pipeline-part-two-1d3c86e6fe42) [## 构建自动化机器学习管道

训练和评估模型,解释模型结果和最终结论

towardsdatascience.com](/building-an-automated-machine-learning-pipeline-a74acda76b98)

在这最后一篇文章中,我们介绍了 Docker 和 Luigi 的相关元素,并解释了它们对于葡萄酒评级预测器的重要性和用途。我们完成了我们的综合管道的最后一个缺失部分,并对 ML 解决方案进行了试运行——就像它在生产系统上运行一样。

特别感谢

我想是时候感谢我的另一半了。在我的数据科学探险中,他是我最宝贵的导师、编辑和学习伙伴!

他帮助我克服了转行的起起落落。此外,我要感谢他对我完成这个系列文章的支持,以及他为我提供的勇气。谢谢你,亲爱的❤️

感谢阅读🙂请随意使用这个管道、代码和用于你自己的项目。

对于评论或建设性的反馈,您可以通过回复、 TwitterLinkedin 联系我!

在我们走之前,我还想分享一些我用来准备这个项目的有用资源。下一篇文章再见👋

了解 Docker 和 Luigi 更多信息的建议资源:

构建自动化机器学习管道:第一部分

原文:https://towardsdatascience.com/building-an-automated-machine-learning-pipeline-part-one-5c70ae682f35?source=collection_archive---------21-----------------------

机器学习

数据清理、探索性数据分析和特征工程步骤

劳拉·佩鲁奇在 Unsplash 上拍摄的照片

在我的数据科学学习之旅中,我通过阅读关于走向数据科学的文章所学到的东西与我从在线课程和各种编码挑战中学到的一样多。跟踪这些不同的学习资源,伴随着他们对机器学习(ML)的解释,对我帮助很大。今天,我将带着我在整个旅程中提炼出的对 ML 应用程序的部分观点,开始一个系列文章。它将有希望展示如何用初学者友好的语言和结构构建一个自动化的 ML 管道。

我们将遵循一个直观的 ML 管道来构建 一个预测器来评定葡萄酒质量 :

  1. 理解、清理和格式化数据
  2. 探索性数据分析
  3. 特征工程和预处理
  4. 设置评估指标并建立基线
  5. 基于评估度量选择 ML 模型
  6. 对所选模型执行超参数调整
  7. 训练和评估模型
  8. 解释模型预测
  9. 得出结论并记录工作

第一篇文章涵盖了步骤 1、2 和 3。这篇文章背后的代码可以在这个笔记本里找到。本系列的第二篇和第三篇文章将涵盖整个管道。

在我们构建好管道之后,第四篇文章将关注如何用 DockerLuigi 自动化 葡萄酒评级预测器 。这一部分尤其令我兴奋,因为它向我介绍了一些概念和工具,让我了解了 ML 解决方案是如何在生产系统上运行的。

声明:这种自动化的核心结构和实用程序来自于一个编码挑战,这是我最近在一家名为 Data Revenue 的神奇公司接受 ML 工程师面试过程的一部分。我没有得到这份工作,因为他们在寻找一个在工程方面更有重点和经验的人,而我在科学方面更有经验。最终,我戴上了软件工程师的帽子,在面试过程中学到了很多。

GitHub 上为没有耐心的人提供了完整的项目:

[## cereniyim/葡萄酒评级预测模型

在这个项目中,我为一个在线葡萄酒卖家构建了一个葡萄酒评级预测器。这款葡萄酒预测器旨在显示良好的…

github.com](https://github.com/cereniyim/Wine-Rating-Predictor-ML-Model)

问题定义和目标:

在这个项目中,我们提供了一个样本数据集。在现实世界的场景中,人们可能从网上资源如 winemag 中获得(也许scrap)这些数据。

该项目的目标是建立一个葡萄酒评级预测器使用样本数据集来显示良好的预测是可能的,也许是作为一个更大项目的概念证明。

我们将使用葡萄酒评级预测器的结果来确认 ML 应用程序是商业问题的有效解决方案,例如为卖家优化葡萄酒组合。

我们应该永远记住,数据科学中的“科学”是有目的的,最终是为非科学家服务的。一个典型的项目有一些隐含的基本需求。

我们的葡萄酒评级预测器应该是可以理解的,因为我们的观众可能对统计学和 ML 了解有限。此外,它应该是高性能的,因为完整的生产数据集可能有数百万行。最后,它应该是自动化的,可以在任何生产系统上运行,不需要特殊的配置和设置。

在开始之前

这个项目的数据可以在存储库中的[data_root/raw/wine_dataset](https://github.com/cereniyim/Wine-Rating-Predictor-ML-Model/blob/master/data_root/raw/wine_dataset.csv)中找到。现在,我们将使用其中的 90%作为训练集,剩下的 10%作为测试集。它们在data_root/interim中都有[train.csv](https://github.com/cereniyim/Wine-Rating-Predictor-ML-Model/blob/master/data_root/interim/train.csv)[test.csv](https://github.com/cereniyim/Wine-Rating-Predictor-ML-Model/blob/master/data_root/interim/test.csv)

我们将使用 Python 3.7,并将需要本笔记本中的以下库:

1.理解、清理和格式化数据

在这一步中,我们将熟悉数据集,理解数据集传达的内容,并通过删除冗余的行和列来清理数据集。

让我们将训练和测试数据集加载到数据框中,并查看训练数据集的一些行:

train = pd.read_csv("../data_root/interim/train.csv") 
test = pd.read_csv("../data_root/interim/test.csv")
train.sample(5)

数据集中有一些空行,我们只有两个数字变量:点数价格

点数是我们试图预测的因变量,并将目标称为。其余的是可用于确定目标的独立变量,将被称为 特征

我们试图掌握自变量和因变量之间的关系,并使用这种关系来生成预测,这使得葡萄酒评级预测器成为一个 监督回归机器学习模型 :

  • 监督 : 数据集包括葡萄酒评级(标为)和可用于确定的自变量。
  • 回归 : 我们的目标变量——由 80 到 100 的连续整数组成。

如果您曾经考虑过在超市购买哪种葡萄酒,大多数列名都是不言自明的。然而,我认为理解一种特定的葡萄酒如何获得和特定领域的特征是很重要的。

品酒师给葡萄酒打分。同一个品酒师可能会给一款葡萄酒不同的分数。当品酒师给一款酒打分时,她/他还会提供一份关于该酒的注释和味道的描述。

**标志是葡萄园的名称,region _ 1&region _ 2特征描述了葡萄酒的葡萄生长的地方。这是一瓶巴洛 2005 巴鲁格红葡萄酒的样子:

图片来源: Winemag 上巴洛 2005 巴鲁格红的截图

现在,让我们看看训练和测试数据集的数据类型和非空(非 null)数:

*train.info()*

*test.info()*

这是一个有趣的数据集,因为 90%的特征是 【分类】— 变量,它们位于对象数据类型中。ML 模型只能处理数字数据和非空值。因此,我们将在 特征工程&预处理 中开发一些策略来处理我们模型中的分类数据和缺失值。

1.1。数据集的缺失值:

下面的函数将使我们能够观察每个特性的缺失值的百分比。

**

缺失值超过 50%的特征不太可能为我们的模型提供重要信息。因此, region_2 将从两个数据集中删除。

在此示例数据集中,目标值没有缺失值。如果我们在目标中有缺失的值,我们会删除所有缺失的值,以免扭曲目标的现有分布。

1.2。非数字特征的基数:

基数 是一个分类特征所具有的唯一值的数量。让我们来看看我们的训练数据集:

*object_columns = (train
                  .select_dtypes(include="object")
                  .columns)

**for** column **in** object_columns:
    print("**{}** has **{}** unique values."
          .format(column, 
                  train[column]
                  .nunique()))*

我们可以推断一个特征有一个

  • 高基数 如果在 9000 行中有 1000 行以上:描述称号头衔酒厂。**
  • 中等基数 如果超过 100 行:region_1品种
  • 低基数 如果少于 100 行:国家地区 _2,* taster_nametaster_twitter_handle。*

当我们训练模型时,高基数特性可能会引起问题。即使我们将分类特征的每个唯一值转换成新特征,它们也可能带来维数灾难。因此,我们将使用高基数要素来添加新要素,然后将它们从数据集中移除。

请记住,这是一个样本数据集。因此,我们不能保证在训练模型时已经看到了每个分类特征的所有可能值。如果我们的葡萄酒评级预测器在测试集中遇到一个新值,它将抛出错误。这就是为什么我们要在 特征工程&预处理中重新审视这一点。

1.3。移除共线特征:

如果两个变量之间存在线性关联,则称它们为 共线 。在分类特征的情况下,如果它们提供相同的信息来预测目标,则它们被称为 共线

共线特征降低了测试集的泛化性能和模型的可解释性。回想一下,我们对葡萄酒评级预测器的要求之一是可解释性。

在我们的样本数据集中, taster_nametaster_twitter_handle 表示共线性。在确保 taster_twitter_handle 的所有值都被 taster_name ( 此处未显示,但在笔记本中检查过)覆盖后,它被移除以构建一个良好且可解释的葡萄酒评级预测器。

1.4。了解&清理&格式数据:

到目前为止,我们已经初步了解了数据集,并发现了要移除的要素:

  • **称号酒厂由于基数高
  • region_2 由于缺失值超过 50%
  • taster_twitter_handle 由于共线性

此外,训练集包含一些重复的行。除了上面的列表,我们将删除它们,并用下面的函数清理数据:

*drop_columns = ["designation", "winery", "taster_twitter_handle", "region_2"]train_cleaned = CleanDatat(train, drop_columns, "points")
test_cleaned = CleanData(test, drop_columns, "points"*

现在,我们将使用train_cleaned.csvtest_cleaned.csv探索性数据分析 中进一步可视化和探索特性和目标。

2.探索性数据分析

探索性数据分析(EDA)是我们详细查看变量并可视化特征和目标之间关系的步骤。在这一步,我们将寻找和跟踪目标的强限定词的线索。在这一步的最后,我们将决定在 特征工程&预处理 中处理哪些特征。

首先,我们将检查(目标)、数字特征以及它们之间的联系。然后,我们将研究低基数特性及其与目标的关系。当我们发现有价值的见解时,我们将通过 EDA 的步骤详细阐述它们。

2.1。积分分布:

分布 是对变量范围以及数据在该范围内如何分布的描述。通过绘制直方图,我们可以很容易地观察到分布情况:

*figsize(10, 8)
plt.rcParams['font.size'] = 14
plot_histogram(train_cleaned, "points")*

正如随机变量所预期的那样,点显示正态分布(分布中明显出现钟形曲线)。点数范围分布在 80 和 100 之间,平均值为 88.45,中位数为 88。此外,标准偏差——数据范围分布的度量值为 3.03,导致方差为 9.1 的平方偏差。基于正态分布,我们可以自信地说,95%的点位于 82.5–94.5 范围内。

2.2。价格分布:

当我们绘制价格的直方图时,它显示了一个模糊的画面,因为它高度右偏(笔记本中有)。为了得到一个合理的值,我们将在 0–200 范围内观察它:**

**figsize(10, 8) 
plt.rcParams['font.size'] = 14
plot_histogram(train_cleaned, "price", 200) 
plt.xlim(0,200)**

这仍然是一个右偏或正偏分布,其中右尾较长,大部分值集中在左侧。价格的中位数是 25,平均价格是 35.5。**

为了使用价格作为预测值,我们需要假设收到的不会引起任何价格变化。否则,这将导致数据泄漏,因为我们将在稍后的时间点包含一个预测器。这可能会破坏我们对的未来预测。**

尽管如此,假设价格不受品酒师分配的点数的影响,它是葡萄酒质量和年份的指标。因此,我们期望它成为该模型的一个基本特征。

2.3.积分和国家:

为了可视化几个变量的关系,我们将使用 密度图 ,其中我们绘制了分类特征的每个唯一值的目标分布。 密度图 可以解释为直方图的连续和平滑版本,其容易传达分布的形状。

为了保持图表的可解释性,我们将为出现次数超过 100 次的国家绘制的分布图。

**# make a list of countries that has most occurences
countries = (train_cleaned["country"]
             .value_counts())

freq_countries = list(
    countries[
        countries.values > 300]
    .index)

freq2_countries = list(
    countries[
        (countries.values <= 300) &
        (countries.values >= 100)]
    .index)**
**figsize(20, 10)
plt.rcParams['font.size'] = 14

*# plot points distribution for most frequent countries*
plt.subplot(1, 2, 1)
plot_distribution(train_cleaned, "points",
                  freq_countries, "country")

plt.subplot(1, 2, 2)
plot_distribution(train_cleaned, "points",
                  freq2_countries, "country")**

葡萄酒生产市场由美国、法国、意大利和西班牙主导,这从地块上很容易获得。国家是确定葡萄酒点数的区分特征。然而,也有一些例外:美国&葡萄牙或者奥地利&德国。

2.4.不同国家的积分和价格

假设价格是点数的基本决定因素,我们将探讨点数价格国家的关系。

***# create dataframe of most frequent countries*
points_freq_countries = train_cleaned[
    train_cleaned
    .country
    .isin(freq_countries)]figsize(20, 10)
plt.rcParams['font.size'] = 14

*# plot a scatterplot of Points and Price*
plt.subplot(1,2,1)
sns.scatterplot(x='price', y='points',
                hue='country', 
                data=points_freq_countries,
                alpha=0.7)
plt.xlabel("Price", size=14)
plt.ylabel("Points", size=14)
plt.title("Points vs Price for Most Frequent Countries", 
          size=16)

*# plot a scatterplot of Points and Price*
plt.subplot(1,2,2)
sns.scatterplot(x='price', y='points', 
                data=train_cleaned,
                alpha=0.7)
plt.xlabel("Price", size=14)
plt.ylabel("Points", size=14)
plt.title("Points vs Price for all Countries with price range 0-200", size=16)
plt.xlim(0,200)**

左侧的图表显示了在整个价格范围内,主要国家生产的葡萄酒的价格的关系。右边的图显示了所有国家在 0 和 200 价格范围内的点数和价格关系。

两个图都表明价格之间存在很强的正相关性,这证明价格是一个重要的预测指标。另一个有价值的发现是,有两种来自意大利的葡萄酒和一种来自法国的葡萄酒得到了品酒师的满分,它们的价格都超过了 200 英镑。

2.5.点和品尝者姓名

,代表从品酒师处收到的特定葡萄酒的分数。因此,不同的品酒师可能对葡萄酒有不同的诠释。为了研究这一点,我们将绘制一个小提琴图。

小提琴图我最喜欢的图传达了数据形状的分布和汇总统计分别是小提琴在外面,细长的长方形在里面。

**figsize(14, 8)
plt.rcParams['font.size'] = 14

f = sns.violinplot(data=train_cleaned,
                   x="taster_name",
                   y="points")

f.set_xticklabels(f.get_xticklabels(),
                  rotation=90)

plt.title("Points from Different Tasters",
          size=16)**

每个品酒师对点数的汇总统计和分配都是独一无二的,导致上面的小提琴不同。看这张图,我们期望它是模型的第二个最重要的预测器。

2.6.探索性数据分析的结果

我们探讨了积分价格国家品酒师姓名以及它们之间的关系。除此之外,品种、区域 _1省份将保留在最终特征列表中,因为:

  • 品种区域 _1 是区分葡萄酒中葡萄的主要因素,从而决定了葡萄酒的味道和质量**
  • 结合提供位置信息**

此外,我们在观察到一个正态分布并确定了它的均值和方差,这将有助于我们在设置评估度量&建立基线。** 我们预计价格品酒师姓名是决定点数的两个最重要的预测因素。**

现在,我们将继续进行期待已久的 特征工程&预处理 部分。

3.特征工程和预处理

特征工程 是从原始特征中提取和转换信息并潜在地创造出更多有用特征的过程。我们将利用我们在前面的部分中了解到的特性,并将这些信息编码到新的特性中。由于我们的特征大多是分类的,我们将在描述种类标题中搜索数字或二进制特征。

然后,我们将准备两个数据集,用作我们的葡萄酒评级预测器的输入。这个过程称为 特征预处理 。这是一个必要的步骤,因为 ML 模型只能处理数字数据和非缺失值。

建立一个好的葡萄酒评级预测器从识别最有用的特征开始,以确定。在这一步的最后,我们将确定最有效的特征集,这将提供一个好的和可理解的 葡萄酒评级预测器 作为回报。我们将分别遵循特征提取、分类特征转换和缺失值填充。

3.1.特征抽出

  • 描述: 它包含了关于葡萄酒的颜色、味道和音符的信息(比如甜、干)。使用下面的功能从描述中搜索和提取与味道和颜色相关的单词。如果描述中存在与味道或颜色相关的词,则该词的相应特征被赋予值 1,否则为 0。

  • 标题:包含酒的生产年份。如果标题中没有年份信息,则年份被指定为 0。**

  • 品种:包含不同类型的葡萄是否混合的信息。 is_blend 如果有几个葡萄品种,则赋 1,否则赋 0。

现在,使用上面的功能和下面的搜索关键字,我们将添加新的功能。

***# create search terms for new features* 
*# to be extracted from description*
is_red_list = ["red", "Red", "RED",
               "noir", "NOIR", "Noir",
               "black", "BLACK", "Black"]

is_white_list = ["white", "WHITE", "White",
                 "blanc", "Blanc", "BLANC",
                 "bianco", "Bianco", "BIANCO",
                 "blanco", "Blanco", "BLANCO",
                 "blanca", "Blanca", "BLANCA"]

is_rose_list = ["rose", "ROSE", "Rose",
                "rosé", "Rosé", "ROSÉ"]

is_sparkling_list = ["sparkling", "SPARKLING", "Sparkling"]

is_dry_list = ["dry", "Dry", "DRY",
               "dried", "Dried", "DRIED"]

is_sweet_list = ["sweet", "Sweet", "SWEET"]

desc_extracting_dict = {
    "is_red": is_red_list,
    "is_white": is_white_list,
    "is_rose": is_rose_list,
    "is_sparkling": is_sparkling_list,
    "is_dry": is_dry_list,
    "is_sweet": is_sweet_list
}**
**train_features_added = ExtractFeatures(train_cleaned) test_features_added = ExtractFeatures(test_cleaned)**

经过特征工程后,我们在train_features_addedtest_features_added数据框中有 14 个特征:国家、省份、地区 _1、 品种、价格、 年份、 *品酒师 _ 姓名、is _ 红、is _ 白、is _ 干、is _ 甜、is _ 闪闪、is_blend。*

3.2.特征预处理

作为特征预处理步骤的第一部分,分类特征(国家省份地区 _1品酒师 _ 姓名品种)将被转化为数字特征。我们选择了 顺序编码 方法进行转换,这是一个从 1 开始连续地将正整数按照出现的顺序分配给特定特性的每个唯一值的过程。

请记住,这是一个样本数据集,因此我们必须考虑这个样本数据集中不存在但可能出现在测试数据集中的可能值。为了管理它们,我们将使用来自 category_encoders 库的序数编码器。

当我们将handle_unknown参数设置为"value"时,ordinal_encoder对象会将测试集的第一次看到的值赋为-1。然后,我们在训练集上运行fit方法,ordinal_encoder对象将给定特征的值映射到整数。然后,我们对两个数据集运行transform方法,根据拟合过程中完成的映射完成转换过程,并返回全数字数据集。

**ordinal_encode_cols = ["country", "province", "region_1", "variety",  "taster_name"]data_encoded = EncodeCategoricalData(train_features_added,
                                     test_features_added)
train_encoded = data_encoded[0]
test_encoded = data_encoded[1]**

作为特征预处理的第二部分,我们将处理缺失值。一个简单的选择是放弃它们。然而,我们已经只有 8500 个数据点来训练模型。因此,我们将继续填充它们。填补特征缺失值的过程称为 插补 。我们将针对不同的功能使用以下策略:

  • taster_name :因为我们期望 taster_name 是最重要的特征之一,所以它被赋予一个常数值(0 代表“未知的品尝者”)。这样,我们可以保持每个品尝者在确定时的效果。**

  • 价格年份:用中间值估算,因为它们是数字列,中间值不受极值的影响。**

  • 国家地区 _1品种:用最频繁值估算,因为它们是分类的,并且每个特征的最频繁值对齐。分别是美国、加州、纳帕谷和黑皮诺。**

在输入期间的一个重要考虑是,无论选择什么输入策略,训练集和测试集都被输入相同的值。例如,训练数据集中价格的中值是 25,并且该值被分配给训练数据集和测试数据集的缺失值,以防止数据泄漏到测试集中。

**transformed_data = ImputeMissingValues(train_encoded, test_encoded)

train_features = transformed_data[0]
train_target = transformed_data[1]
test_features = transformed_data[2]
test_target = transformed_data[3]**

在进入下一篇文章和下一节之前,我们将保留训练集的一部分作为验证集,以提供对模型的公正评估。

训练数据集将用于训练机器学习模型。 训练一个机器学习模型 就是我们的模型掌握特征和目标之间的模式,并利用它们进行进一步预测的过程。

验证数据集 —训练数据集的保留部分将用于衡量我们的模型将如何推广到第一次看到的数据集。该衡量标准将成为 根据评估指标 部分选择 ML 模型的客观选择标准之一。

我能听到你在想为什么我们不使用测试集来选择模型呢?因为,如果我们为此目的使用测试集,我们将选择一个经过训练完全适合我们的测试集的有偏模型。此外,模型性能不具有代表性。

当我们在 列车上评估我们完全指定的模型和评估模型 时,测试装置将发挥作用。

根据上面的信息,让我们通过随机分离 25%的训练集来创建验证集:

**X_train, X_valid, y_train, y_valid = train_test_split(
    train_features, 
    train_target, 
    test_size=0.25, 
    random_state=42)**

存储库中的[notebooks/transformed](https://github.com/cereniyim/Wine-Rating-Predictor-ML-Model/tree/master/notebooks/transformed)下提供了训练、验证和测试数据集。它们都准备好被输入葡萄酒评级预测器了!

结论

在本文中,我们完成了机器学习流水线的前三个步骤。在定义了问题和目标之后,我们

  1. 通过移除冗余和共线要素清理数据;和重复行
  2. 浏览数据以详细了解目标、功能及其关系
  3. 增加了更多有用的功能(希望如此!),准备并转换了两个数据集

我们还完成了从训练集创建验证集的关键步骤。

第二篇文章将展示如何选择评估指标、建立基线、选择 ML 模型并通过优化所选模型的评估指标来调整其超参数(步骤 4、5 和 6)。

** [## 构建自动化机器学习管道:第二部分

设置评估指标&建立基线、选择算法和执行超参数调整…

towardsdatascience.com](/building-an-automated-machine-learning-pipeline-part-two-1d3c86e6fe42)

第三篇文章将分别用训练数据集和测试数据集对模型进行训练和评估。它将展示如何解释一个 ML 模型的预测并得出结论(步骤 7、8 和 9)。

[## 构建自动化机器学习管道

训练和评估模型,解释模型结果和最终结论

towardsdatascience.com](/building-an-automated-machine-learning-pipeline-a74acda76b98)

上一篇文章将使用 DockerLuigi 来自动化这条流水线。

[## 构建自动化机器学习管道:第四部分

利用 Docker 和 Luigi 实现管道自动化

towardsdatascience.com](/building-an-automated-machine-learning-pipeline-part-four-787cdc50a12d)

感谢阅读😊现在文章系列完成了,你可以阅读下一篇了!

对于评论或建设性的反馈,您可以通过回复、 TwitterLinkedin 联系我!

在那之前保持安全和健康👋**

构建自动化机器学习管道:第二部分

原文:https://towardsdatascience.com/building-an-automated-machine-learning-pipeline-part-two-1d3c86e6fe42?source=collection_archive---------43-----------------------

机器学习

设置评估指标&建立基线、选择算法和执行超参数调整步骤

照片由 Zachariah HagyUnsplash 上拍摄

在这一系列文章中,我们将我们的课程设置为构建一个 9 步机器学习(ML)管道(我们称之为葡萄酒评级预测器)并将其自动化。最后,我们将观察每个步骤在生产系统中是如何聚集和运行的。

我们正在研究一个监督回归问题。我们希望开发一个高性能的,可理解的,好的葡萄酒评级预测器,它可以预测,一个葡萄酒质量的衡量标准。

在第一篇文章中,我们定义了构建葡萄酒评级预测器背后的问题和动机。然后,我们通过可视化特征和目标之间的关系以及 理解&清理&格式数据探索性数据分析 步骤来详细查看数据。

特征工程&预处理 步骤中,我们添加了新的更有用的特征。此外,我们准备了在模型的训练和评估期间使用的训练和测试数据集。作为第一篇文章的最后一步,我们从用于模型选择的训练数据集创建了验证数据集。

在本文中,我们将完成以下步骤:

4.设置评估指标并建立基线

5.基于评估度量选择 ML 模型

6.对所选模型执行超参数调整

这篇文章背后的代码可以在这个笔记本里找到。GitHub 上提供了完整的项目:

[## cereniyim/葡萄酒评级预测模型

在这个项目中,我为一个在线葡萄酒卖家构建了一个葡萄酒评级预测器。这款葡萄酒预测器旨在显示良好的…

github.com](https://github.com/cereniyim/Wine-Rating-Predictor-ML-Model)

请随意分享,分叉,并利用这个回购为您的项目!

在我们开始之前:

我们将要使用的数据集在[notebooks/transformed](https://github.com/cereniyim/Wine-Rating-Predictor-ML-Model/tree/master/notebooks/transformed)中提供

  • [X_train](https://github.com/cereniyim/Wine-Rating-Predictor-ML-Model/blob/master/notebooks/transformed/X_train.csv)由特色(国家、省份、地区 _1、 品种、价格、 年份、 品酒师 _ 姓名、正 _ 红正 _ 白正 _ 玫瑰正 _ 干正 _ 甜、正 _ 闪亮、正 _ 混合[y_train](https://github.com/cereniyim/Wine-Rating-Predictor-ML-Model/blob/master/notebooks/transformed/y_train.csv)目标(组成**
  • 由特征组成的[X_valid](https://github.com/cereniyim/Wine-Rating-Predictor-ML-Model/blob/master/notebooks/transformed/X_valid.csv)和由目标组成的来验证模型

在本笔记本中,我们将需要以下 Python 库:

让我们将数据集加载到数据帧中,并使用以下函数转换为数组:

X_train = pd.read_csv("transformed/X_train.csv")
X_train_array = convert_features_to_array(X_train)X_valid = pd.read_csv("transformed/X_valid.csv")
X_valid_array = convert_features_to_array(X_valid)y_train = pd.read_csv("transformed/y_train.csv")
y_train_array = convert_target_to_array(y_train)y_valid = pd.read_csv("transformed/y_valid.csv")
y_valid_array = convert_target_to_array(y_valid)

4.设置评估指标并建立基线

如果这是一场 Kaggle 竞赛,我们将跳过这一步,因为我们将获得评估指标。

然而,在数据科学/机器学习的实际应用中,评估指标是由数据科学家根据利益相关者对 ML 模型的期望来设置的。这就是为什么这是一个重要的步骤。

在我们决定评估指标后,为了量化我们的初始动机——建立一个好的葡萄酒评级预测器,并与我们的模型性能进行比较,我们将形成一个基线。

4.1.设置评估指标

均方误差 (MSE) 设定为评价指标。它是残差平方和的平均值,其中 残差 是目标变量的预测值减去实际值。

换句话说,模型的评估是通过查看平方误差(残差)有多大来进行的。

图像致谢:对 Dataquest.io 的均方误差计算

我们选择 MSE 是因为它是可解释的,类似于方差,并且是 ML 模型中广泛使用的优化标准。(例如线性回归、随机森林)

4.2.建立基线

一个 基线 可以解释为利用专家知识或几行代码生成一个对目标值的天真猜测。它还有助于衡量 ML 模型的性能。

如果构建的模型(葡萄酒评级预测器)不能超过这个基线,那么选择的 ML 算法可能不是解决这个问题的最佳方法,或者我们可能想要重新访问管道的先前步骤。

回想一下(目标)在 80 和 100 之间呈正态分布。平均值为 88.45,方差为 9.1。

我们将通过以下方式形成基线:

  • 使用这些统计数据和
  • 计算验证集中每个点与训练数据集平均值之间的差异,然后取差异平方和的平均值(计算 MSE 的类似方法):
*# set baseline as mean of training set's target value*
baseline = (np
            .mean(
                y_train_array))*# calculate MSE baseline*
mse_baseline = (np
                .mean(
                    np.square(
                        baseline - y_valid_array)))

点的方差和基线误差几乎相等,这不是巧合。您可以将这个基线 MSE 视为手动计算的方差,其中包含来自我们数据集的一个较小的集合。

这个数字(9.01)将伴随我们进入下一步— 在我们测试不同的 ML 算法时,根据评估指标 选择一个 ML 模型。

5.基于评估度量选择 ML 模型

我总是发现尝试几种背后有不同原理的算法是有用的,因为我相信这整个过程也包括实验!(“科学家”作为数据科学家的一部分现在有意义了🙃)

在搜索最佳算法时,我们将观察算法的 MSE 和运行时间的改进(在单元格的开始使用%%time magic ),并将算法的 MSE 与我们的基线 MSE 进行比较。同时,我们将牢记葡萄酒评级预测器的可理解性和性能要求。

我们将尝试一种线性算法、两种基于距离的算法和两种基于树的算法,从最简单到最复杂依次为:

  • 线性回归
  • k-最近邻回归量
  • 支持向量回归机
  • 随机森林回归量
  • 光梯度增强回归器

我们将用训练集来训练它们,并将它们的泛化性能与验证集进行比较。下面的函数将为我们完成这项工作。

在这一步的最后,我们将详细说明所选择的算法是如何工作的。

5.1.线性回归:

线性回归略微降低了基线指标,表明它不是一个好的预测器的候选。

5.2.k-最近邻回归量

基于距离的模型使用欧几里德距离(或其他距离度量)进行训练,因此变化的范围会导致基于距离的模型生成不准确的预测。为了应用基于距离的算法,我们在笔记本中的之前对数据集进行了标准化缩放。

K-最近邻回归元表现优于线性回归。然而,MSE 仍然很高,表明该算法也不是一个好的预测器。

5.3.支持向量回归机

支持向量回归机在较高的运行时间表现优于 k 近邻回归机。总之,MSE 降低了 35%,表明该算法可能是构建良好预测器的候选算法。

5.4.随机森林回归量

随机森林回归器比支持向量回归器在更短的运行时间内表现更好。它降低了 MSE 44%,取代了好预测列表中的支持向量回归机。

5.5.光梯度增强回归器

轻度梯度推进回归器(轻度 GBM)在所有试验模型中表现最佳。它还降低了基线 MSE 45%,并表明它是在较低运行时间的良好预测器的潜在候选。

作为总结:

这可能不是一个公平的算法选择,因为我们只用默认的超参数来训练它们。然而,这是流水线的实验步骤,这就是为什么在 对所选模型 执行超参数调整步骤中,light GBM 和 random forest 算法都有进一步改进的机会。(您可以在笔记本中找到它)在本文中,出于可理解性和性能要求,我们将使用随机森林回归器,我们将仅对其进行详细说明。

在进入下一步之前,让我们了解一下随机森林回归器的工作原理:

随机森林回归器 是一种集成算法,一次构建多个决策树,并在数据集的各种子样本和各种特征子集上训练它们。它对数据集和特征子样本的随机选择使得该算法更加鲁棒。

决策树 使用树状结构进行预测。它将一个数据集分解成越来越小的子集,同时一个相关的决策树被增量开发。最终结果是一个有决策节点和叶节点的树。决策节点有两个或多个分支,每个分支代表被测试的特性的值。叶节点代表对目标的最终决策。

6.对所选模型执行超参数调整

超参数 是数据科学家或 ML 工程师定义的一组参数,其值不受模型训练过程的影响。另一方面, 模型的参数 在训练过程中被模型搜索优化,并受数据集影响。

我喜欢贾森·布朗利关于机器学习掌握的这段引文,以防止两者混淆:

"如果你必须手工指定一个模型参数,那么它可能是一个模型超参数."

我们最简单模型中的一个参数示例:线性回归模型的系数,这些系数通过模型训练进行优化。

我们选择的模型中的一个超参数的例子:在随机森林模型中构建的树的数量,这是由我们或 scikit-learn 指定的。

超参数调优 是定义、搜索并可能进一步提高模型性能的过程。我们将用 随机搜索k 倍交叉验证来搜索最佳参数集。

6.1.随机搜索

随机搜索 是在每次迭代中随机搜索定义的参数组合,并比较定义的得分(均方误差,针对此问题)的过程。它速度快,运行时效率高,但由于要搜索已定义的超参数的随机组合,您可能并不总能找到最优的超参数集。

我们将使用hyperparameter_grid字典搜索随机森林回归方程的以下超参数:

  • n_estimators:模型中使用的树的数量,默认为 100。
  • min_samples_split:分割内部节点所需的最小样本数,默认值为 2。
  • min_samples_leaf:叶子节点需要的最小样本数,默认值为 1。
  • max_features:寻找最佳分割时要考虑的特征数量,默认值为自动。
*# define search parameters* 
n_estimators = [100, 200, 300, 500, 1000] 
min_samples_split = [2, 4, 6, 10] 
min_samples_leaf = [1, 2, 4, 6, 8] 
max_features = ['auto', 'sqrt', 'log2', **None**]*# Define the grid of hyperparameters to search* 
hyperparameter_grid = {
    "n_estimators": n_estimators,               
    "min_samples_split": min_samples_split,                       
    "min_samples_leaf": min_samples_leaf,                         
    "max_features": max_features}

6.2. K 倍 交叉验证

K 重交叉验证 是用于在完整训练数据集上评估模型性能的方法。对于给定的 K ,数据集被平均划分,而不是将数据集分成训练集和验证集的两个静态子集。然后用 K-1 个子集训练模型,并在第 K 个子集上迭代测试。这一过程使得模型对过度拟合更加健壮——在文章的最后会有更多的介绍。

图片鸣谢:在 scikit-learn 上对 k 倍交叉验证的可视化解释

为了使用随机搜索和 k 倍交叉验证来执行超参数调整,我们将添加训练和验证数据集,并从现在开始继续使用一个训练集。

*# add dataframes back for to perform random search and cross-validation*
X = pd.concat([X_train, X_valid])
y = pd.concat([y_train, y_valid])X_array = convert_features_to_array(X)
y_array = convert_target_to_array(y)

由于我们在训练数据集中只有不到 10.000 行,因此我们将执行 4 次折叠交叉验证,以便在每个折叠中有足够数量的数据点。我们将在[RandomizedSearchCV](https://scikit-learn.org/stable/modules/generated/sklearn.model_selection.RandomizedSearchCV.html)对象中整合随机搜索和 k 倍交叉验证:

rf_random_cv = RandomizedSearchCV(
    estimator=rf,
    param_distributions=hyperparameter_grid,
    cv=4, 
    n_iter=25,
    scoring='neg_mean_squared_error',
    n_jobs=-1, 
    verbose=1,
    return_train_score=**True**,
    random_state=42)rf_random_cv.fit(X_array, y_array)

使用fit方法,我们开始搜索在hyperparameter_grid中为每个超参数定义的值的随机组合。同时,在每一次迭代中为每一个折叠计算"neg_mean_squared_error"。我们可以通过调用best_estimator_方法来观察确定的最佳参数集:

超参数调整后,最佳超参数集确定为:

  • n_estimators : 200
  • min_samples_split : 4
  • min_samples_leaf : 2
  • max_features:‘sqrt’

让我们看看这些超参数是否会帮助我们进一步提高随机森林回归的 MSE。

6.3.超参数调整后再访随机森林回归方程的 MSE

rf_random_cv_model = rf_random_cv.best_estimator_

MSE 从 5.41 降低到 4.99,调整后的模型运行时间为 1.12 秒,低于初始随机森林模型的运行时间(1.89 秒)。超参数调优不仅提高了评估指标,还降低了我们的运行时间。

给定样本数据集、确定的特征集和调整的随机森林回归器,我们已经成功地构建了一个好的葡萄酒评级预测器,而不会陷入欠拟合和过拟合区域!

关于 过拟合和欠拟合 的几点注记

虽然这个主题本身是另一篇文章,但我认为在这里讨论是很重要的,因为我们提到了这些概念。

过度拟合 发生在 ML 模型完美拟合(或记忆)训练数据集,而不是抓住特征和目标之间的共同模式的时候。一个过度拟合的模型会有一个 高方差 ,我发现这种情况和一个易碎的玻璃房子有相似之处。它是为其目前的条件而完美建造的,但如果条件改变,它就不太可能存活。

你还记得交叉验证使我们的模型对过度拟合更加稳健吗?

背后的原因是,我们的模型在训练期间在每个折叠中看到不同的数据集——就像一所房子已经遇到不同的天气条件。这提高了泛化性能,使模型更能抵抗过拟合。

欠拟合 发生在 ML 模型由于没有足够的数据点或特征而无法掌握特征与目标之间的关系时。它的表现可能和对预测的随机猜测一样糟糕。一个欠拟合的模型会有一个偏高,我把这种情况想成是搭建了一半的棚子。它应该经历一些建设,以服务于它的目的。

你还记得线性回归模型只显示基线 MSE 有轻微的改善吗?

这是一个不适合的例子。线性回归模型产生了与基线 MSE 几乎相同的 MSE,并且未能理解特征和目标之间的关系。

总之,过拟合和欠拟合都会降低机器学习模型的泛化性能,并导致不令人满意的评估度量水平。虽然我们没有测试调优的随机森林回归器,特别是过度拟合的情况,但 45%的改进表明我们处于欠拟合和过度拟合之间的细微差别。

在得出结论之前,让我们将模型保存在目录中,这样我们可以从上一篇文章和笔记本中停止的地方继续。

*filename = 'random_forests_model.sav'
pickle.dump(rf_random_cv_model, 
           open(filename, 'wb'))*

结论

在本文中,我们完成了机器学习流水线的中间步骤。在简要回顾了首件和目标之后,我们

  1. 由于其在 ML 算法中的广泛使用和可解释性,将评估度量设置为均方误差。
  2. 建立了与均方误差计算一致的基线,结果为 9.01。
  3. 尝试了几种不同的最大似然算法,并选择了随机森林回归,它在 1.93 秒的运行时间内报告了 5.4 的 MSE。
  4. 微调了随机森林回归器的超参数,与初始随机森林模型相比,性能提高了 8%(MSE:4.99,运行时间为 1.12 秒)。

如果我们考虑我们最初的动机(建立一个好的葡萄酒预测器)和基线 MSE (9.01),当我们接近管道的末端时,我们正走在正确的道路上。

我们已经显著降低了基线 MSE,从 9.01 降低到 4.99,结果提高了 45%!

第三篇文章将从加载微调后的随机森林回归模型开始,并重点关注使用测试集对模型进行评估,以及评估的结果。(步骤 7、8 和 9)。

* [## 构建自动化机器学习管道

训练和评估模型,解释模型结果和最终结论

towardsdatascience.com](/building-an-automated-machine-learning-pipeline-a74acda76b98)

最后一篇文章将使用 DockerLuigi 来自动化这条流水线。

[## 构建自动化机器学习管道:第四部分

利用 Docker 和 Luigi 实现管道自动化

towardsdatascience.com](/building-an-automated-machine-learning-pipeline-part-four-787cdc50a12d)

感谢阅读😊

对于评论或建设性的反馈,你可以联系我的回复, TwitterLinkedin

在那之前保持安全和健康👋*

用 Python 构建简单的 GUI(图形用户界面)

原文:https://towardsdatascience.com/building-an-easy-gui-graphical-user-interface-with-python-1c62ce6342e0?source=collection_archive---------11-----------------------

有时我的代码需要从未见过命令行的人来使用。以下是如何让他们(和你)的生活更轻松的方法。

如果你不是软件工程师,你可能会和不懂编程的人一起工作。然而,您的 Python 解决方案可以提高团队的效率,并将工作量减少 10 倍。但是如果你也需要教别人如何执行呢?对于许多人来说,使用终端或其他用户界面可能看起来过于陌生和复杂。

在本教程中,我将介绍为 Python 脚本创建 GUI 的过程。我们将使用 Python 包 Gooey ,这将使设计漂亮而简单的用户界面变得极其容易,对任何有电脑的人来说都很熟悉。您将需要:

  1. Python 和 IDLE
  2. 设计技巧(开玩笑, 这个包真的是那个神奇的 )

我们将运行一个例子,在这个例子中,我想打开一个 GUI 窗口,终端用户可以在这个窗口中搜索他所在地区的餐馆,并从程序中获得“推荐”。

这是一个简单的想法,但它很好地展示了 Gooey 可以做的很酷的事情。除此之外,我还发现 Gooey 对于上传文件、通过脚本运行文件以及自动将文件再次下载到你电脑中的指定文件夹非常有用。你可以通过添加

widget="FileChooser"

变成您论点之一!

下面是我的 GUI 最终的样子:

安装

Gooey 不是为 Jupyter 设计的,所以我在这个项目中使用了 IDLE。如果你安装了 Python,你应该也已经有了 IDLE。要安装 Gooey,您应该在您的终端(OS)/命令提示符(Windows)中运行:

pip install Gooey

根据您的计算机上是否安装了更多的 Python 2 和 3,您需要运行:

pip3 install Gooey ## if you want to use python 3

我们还需要安装 wxPython 包,以便 Gooey 可以使用它来创建我们的接口。如果您想从头构建一个界面,学习更多关于 wxPython 的知识是一个好主意。

pip install wxPython==4.0.7 ## type pip3 instead to use python 3

如果你以前没有用过 IDLE,你也应该 pip 安装你将在你的脚本中使用的所有其他包(比如 pandas,numpy,xlsxwriter 等等)

结构

GUI 脚本看起来非常像普通的 Python 脚本,但是需要注意一些结构。我们将从进口开始:

import pandas as pd
from gooey import Gooey, GooeyParser
import numpy as np
from random import randint

我们将通过定义几个参数来启动脚本:

# the program name will show on your GUI window
# you can also set the sizing of the window (default_size)
# you can choose the color of your program using HEX codes 
# (header_bg_color) and (body_bg_color)
# the (navigation) parameter refers to the design of the window[@Gooey](http://twitter.com/Gooey)(program_name="robot restaurant recommendations",
       default_size=(710, 700),
       navigation='TABBED', 
       header_bg_color = '#FF0000',
       body_bg_color = '#FFF900')

我们的脚本将由函数构成,从定义窗口外观的函数开始,以及我们想从用户那里收集什么信息。

我们的第一个函数将定义解析器并传递窗口参数。

def parse_args():
    parser = GooeyParser()

我们的第一个辩论组将是餐厅价格类别—“我们今天感觉如何?”,并给用户从\(到\)$$$的五个选项和一个将返回随机选择的“让我吃惊”选项。

# first we create a section of window called **Price**price_group = parser.add_argument_group('Price')price_group.add_argument("prices",
        metavar='How fancy are we feeling today?',
        action = 'store',
        choices=['$', '$$','$$$','$$$$','surprise me'],
        default='$')# we should set a default value if no option is chosen

接下来,我们需要知道他们在哪里,这样我们就可以推荐附近的餐馆。我们称这个组为邮政编码组。我们还在参数的末尾添加了一个验证器。验证器将评估用户输入,如果给出的邮政编码长度不是 6 位,它将不允许用户启动程序(非常重要的功能!).它甚至可以输出一条红色消息,告诉我们需要修复什么。

# first we create a section of window called **Postcode**postcode_group = parser.add_argument_group('Postcode')postcode_group.add_argument('postcode',
                    metavar='What is your postcode?',
                    action='store',
                    help = "6 digit postcode",
                    gooey_options={
                         'validator': {
                             'test': 'len(user_input) == 6',
                             'message': 'robot needs a 6 digit postcode!'
                             }
                         })

请记住,元变量。add_argument_group 名称将出现在您的 GUI 窗口中,因此尽可能保持它们的标题简单明了。

论点。add_argument_group 将以粗体显示在窗口中,作为部分标题,即邮政编码

元变量将出现在最终用户输入之前,用于解释什么是想要的输入,即你的邮政编码是什么?

我们结束函数,指定我们希望它返回什么:

args = parser.parse_args()
return args

一般来说,Gooey GUI 的结构看起来有点像这样:

import gooey
#all other imports @Gooey(program_name="Restaurants",default_size=(710,700),navigation='TABBED', all other parameters...)def parse_args():
    parser = GooeyParser() group1 = parser.add_argument_group('group 1')
 group1.add_argument(metavar='a message to the user',
                    action='store',
                    help = "help the user understand what you want")# add as many groups and arguments as necessary # specify the return of the function args = parser.parse_args()
 return argsdef my_second_function():# your python script here return something# as many functions as necessary # time to call the functions and execute the script
if __name__ == '__main__':# execute the window args = parse_args()
    my_second_function(variables)
    ## and so on...# THE END

例如:餐馆

Gooey 上有许多小部件、可定制的元素和动作。对于餐馆的例子,我们将应用其中的一些,但是一定要检查他们的 GitHub 中所有的无限的可能性

如前所述,这是我们的 parse_args 函数:

import pandas as pd
from gooey import Gooey, GooeyParser
import numpy as np
from random import randint[@Gooey](http://twitter.com/Gooey)(program_name="robot restaurant recommendations",
       default_size=(710, 700),
       navigation='TABBED', header_bg_color = '#FF0000',
       body_bg_color = '#FFF900')def parse_args():
    parser = GooeyParser()
    price_group = parser.add_argument_group('Price')
    price_group.add_argument("prices",
        metavar='How fancy are we feeling today?',
        action = 'store',
        choices=['$', '$$','$$$','$$$$','surprise me'],
        default='$')
    postcode_group = parser.add_argument_group('Postcode')
    postcode_group.add_argument('postcode',
                    metavar='What is your postcode?',
                    action='store',
                    help = "6 digit postcode",
                    gooey_options={
                         'validator': {
                             'test': 'len(user_input) == 6',
                             'message': '6 digit postcode!'
                             }
                         })args = parser.parse_args()
    return argsif __name__ == '__main__':
    args = parse_args()

    prices = args.prices
    postcode = args.postcode

但截至目前,它并没有向用户返还任何东西。为了做到这一点,我们需要创建更多的函数。让我们首先编写一个将给出的邮政编码与文件中的邮政编码相匹配的变量(我们将在if _ _ name _ _ = = ' _ _ main _ _ ':函数中定义这些变量并导入文件)

match_postcode 函数将接收邮政编码和一个数据帧,清除数据帧的“zipcode”列中的任何空格(以避免用户输入和邮政编码之间的任何不匹配),仅根据匹配用户邮政编码的行过滤数据帧,并将其返回,以便我们可以在下一个函数中使用该数据帧!

def match_postcode(postcode,df):
    df.zipcode = df.zipcode.str.replace("\s","")
    restaurants = df[df['zipcode'] == postcode]
    return restaurants

还记得我们有价格选择吗?每当你把这个论点传递给胶粘的 T21,你可能需要做一个 if 语句来定义每个选择的不同结果。对于这个程序,我们只需要在用户选择“给我惊喜”时生成一个随机价格,所以在这个例子中,我只使用了一个 if 语句。

def match_price(prices, restaurants):
    if prices == 'surprise me':
        price_len = randint(1,4)
    else:
        price_len = len(prices)
    print(price_len)
    restaurants['length'] = restaurants['prices'].str.len()
    restaurants = restaurants[restaurants['length'] == str(price_len)]
    return restaurants

接下来,我们希望 Gooey 在一个弹出窗口中向我们显示推荐,这将引起最终用户的注意。所以我们定义了接下来的两个函数:一个用于当在参数范围内找到餐馆时,另一个用于当我们不能推荐任何东西时。在这里,我们在本地导入 wx 以避免它干扰 Gooey

这是一个使用 wxPython 定制 Gooey 的定制函数,但是它也可以在 Gooey 库上获得。

def show_recommendations(message):
    import wx
    app = wx.App()
    dlg = wx.MessageDialog(None, message, 'What about this one?', wx.ICON_INFORMATION)
    dlg.ShowModal()def show_error_message(message):
    import wx
    app = wx.App()
    dlg = wx.MessageDialog(None, message, 'Whoops', wx.ICON_INFORMATION)
    dlg.ShowModal()

最后但同样重要的是,我们需要将所有这些功能付诸行动:

首先,我们定义变量 prices 和 postcode,这是我们在脚本开始时从用户那里存储的。接下来,我们(在全球范围内)读取一个数据帧,该数据帧包含通过 Yelp API 获得的阿姆斯特丹餐馆的数据(查看我的其他文章了解更多信息!).之后,我们必须调用 match_ functions ,并将它们的返回值赋给一个新的数据帧。

if __name__ == '__main__':
    args = parse_args() prices = args.prices
    postcode = args.postcode

    df = pd.read_excel("yelp-ams.xlsx")
    df2 = match_postcode(postcode,df)
    df3 = match_price(prices, df2)

为了向用户显示推荐,我们将该列转换成一个名为推荐的列表(然后转换成一个字典来删除任何重复的内容),然后再转换回一个列表。如果列表不为空,我们调用 show_recommendations 函数,如果为空,我们调用 show_error_message 函数!

recommendations = df3.name.tolist()
    mylist = list( dict.fromkeys(recommendations) )
    if mylist != []:
        for restaurant in mylist:
            show_recommendations(restaurant)
    else:
        show_error_message('Sorry! No restaurants found')

如果你已经做到这里,谢谢你的阅读!一定要检查一下 Gooey Github 以了解更多关于它的所有功能,我在本教程中只提到了这些。希望这个图形用户界面可以帮助你和你的同事!

利用迁移学习构建易于使用的细分工具

原文:https://towardsdatascience.com/building-an-easy-to-use-segmentation-tool-with-transfer-learning-e2421fe915c?source=collection_archive---------42-----------------------

我们如何利用迁移学习构建一个易于使用的图像分割工具

黄珍妮伊恩·亨特-伊萨克威廉·帕尔默 — 8 分钟阅读

在新图像上训练图像分割模型可能会令人望而生畏,尤其是当您需要标记自己的数据时。为了使这项任务更容易和更快,我们建立了一个用户友好的工具,让您在一个 Jupyter 笔记本上建立整个过程。

使用 klib 加速您的数据清理和预处理

由安德烈亚斯·坎兹 — 8 分钟读完

klib 包提供了许多非常容易应用的函数,这些函数具有合理的默认值,可以在几乎任何数据帧上使用,以评估数据质量、获得洞察力、执行清理操作和可视化,从而使 Pandas 数据帧的使用更加轻便和方便。

资料来源:Unsplash.com

数据科学面试蓝图

Leon Chlon — 9 分钟阅读

在我准备离开舒适的咨询工作几个月后,Deliveroo 取消了我的数据科学经理职位,我没有太多的安全网可以依靠,也不会失业太久。我将分享帮助我在 FaceBook 上获得两个数据科学家职位的一切,希望它可以帮助你们中的一个人,他们也发现自己处于几个月前我所处的不幸境地。

为印刷电路板构建端到端缺陷分类器应用程序

原文:https://towardsdatascience.com/building-an-end-to-end-deep-learning-defect-classifier-application-for-printed-circuit-board-pcb-6361b3a76232?source=collection_archive---------9-----------------------

利用图像处理和深度学习检测、提取和分类电路板缺陷

印刷电路板 (PCB)在大多数电子产品中,沿着导电轨道机械地支撑和连接元件。它们的流行是现代电子工业的基础,其全球市场超过 600 亿美元。

多氯联苯容易出现各种各样的缺陷,阻碍正常生产,给公司造成损失。短路、杂散、鼠咬和针丨孔丨等缺陷会导致电流泄漏和开路等问题,迅速降低性能或使 PCB 变得无用。PCB 制造商必须努力发运无缺陷产品,以保持竞争力并确保客户满意度。

雇用故障分析师以确保缺陷保持在最低水平。但是手工检查和诊断缺陷是具有挑战性的。有多种缺陷需要检测,对错误的容忍度极低,并且可靠地识别和处理有缺陷的单元需要大量的专业知识。考虑到培训新分析师的时间以及确保可靠性所需的认知负荷,需要一种自动检测和分类 PCB 缺陷的解决方案。

在这篇文章中,我们将建立一个 PCB 缺陷分类器,自动检测和分类印刷电路板中的缺陷。我总是鼓励人工智能从业者构建完整的产品,而不是把模型扔给开发人员和其他工程师。你的机器学习必须和一个完整的应用协同存在;因此,只有通过它在整体产品中的生存,你才能确定你是在为用户而不是工程师建造。

播放下面的视频来看看我们将在本文中构建的应用程序:

在本文中,我将检查应用程序的后端前端代码我将遍历所有与缺陷检测提取预测相关的代码。因此,机器学习方面将被完全覆盖。然而,为了使本文保持合理的篇幅,我将只回顾实际应用程序管道代码和前端代码的高级方面。请注意,整个应用程序及其代码都可以在 GitHub 上获得,因此读者可以访问所有内容。我鼓励其他人在他们认为合适的时候克隆和参与这个项目。

这永远是最好的学习方法。我欢迎需要支持的人在评论 版块提问(你也可以在 Twitter 上@我,这样支持更公开地惠及他人)。

让我们开始吧。

这些碎片

编码环境

网络服务

图书馆

笔记本

产品(我们将构建这些产品)

挑战

当试图自动化缺陷检测和分类时,我们面临许多挑战。其中一些已经在导言中提到。更完整的列表包括:

  • ****单位缺陷低容差
  • ****多种缺陷类型来说明;
  • ****自动化解决方案所需的低推理时间
  • ML 模型的训练数据受限(通常是窄的、特定领域的、小的);
  • 传统系统依赖手工制作的功能和重要的专业知识
  • 别人经常使用的方法私人

我们需要一种方法来应对这些挑战,同时支持现实世界中的应用,有效地检测、提取和预测 PCB 中的缺陷类型。

方法

考虑到我们面临的挑战,使用一种混合** 方法来构建我们的分类器应用程序是有意义的,它结合了引用方法非引用方法。**

参考方法**:将检查过的PCB 板(测试板)与没有缺陷的参考模板图像(模板板)进行比较,以允许分析人员识别差异。

非基准法 : 检查PCB 板是否符合设计原则,不依赖模板图像。

参考方法实现起来相对简单,但是有很大的存储需求(保存所有图像)。非参考方法没有存储问题,但是在发现缺陷的数量上受到限制。

构建一个自动化参比和非参比方法的产品可能不会减少存储和计算需求,但如果人工检测和分类错误减少,我们将从增加和可靠的通量中获得巨大收益。

使缺陷分类成为可能

虽然我们可以尝试训练我们的深度学习模型来对整个电路板上存在的缺陷进行分类,但这将是一个挑战。看一下下面的例子测试模板** PCB 板:**

图 1 参考方法中使用测试和模板图像来识别缺陷。请注意左侧图像相对于右侧图像的差异。

左边的板子是我们的模型需要分类的。通过与模板进行比较,我们可以看到单个缺陷。但是我们的模型能够可靠地对缺陷组合进行分类的可能性有多大呢?这门课叫什么?存在多少独特的缺陷组合?

为了减轻模型的负担,我们将首先从测试图像中提取单个缺陷。这可以通过创建差分图像提取图像的不同部分来实现。

以下是我们可以使用 Python 中的图像处理生成的差分图像:

图 2 通过图像相减生成的差分图像。我们使用图像差分来自动突出测试图像中的缺陷。

注意缺陷在红色中突出显示。上图没有显示所有的缺陷,因为在本文中我们只关注提取 3 种类型。捕获其他类型的缺陷是相当琐碎的,我将在后面讨论这个问题。

由于我们有一个突出测试图像中缺陷的差分图像,我们也可以使用图像处理来提取图像的彩色部分:

图 3 缺陷提取。我们将使用图像处理来提取差分图像中突出显示的部分。

这大大降低了我们深度学习分类器的负担。通过提取单个缺陷,我们将问题从复杂的识别问题转化为类似于数字识别的问题(这个问题已经在深度学习中得到了很好的解决)。这也意味着我们使用迁移学习更有可能成功,因为在 OCR 领域有许多成功的模型。稍后我会更详细地讨论迁移学习。

用这种方法我们可以概括出5的主要努力:

  1. 缺陷提取
  2. 缺陷标签
  3. 缺陷分类
  4. 构建 PCB 缺陷分类器应用程序
  5. 运行/测试应用程序

设置

前往Google Colab开始一个新的 Python 3 笔记本。这将是我们的建模环境。这是一个完全在云端运行的 Jupyter 笔记本环境。****

Google Colab 允许免费访问能够运行 GPU 的环境。Colab 还使安装和导入库变得轻而易举。这就是后端即服务(BaaS ),它对快速原型制作有很大的帮助。

你有两种选择将笔记本电脑放入你自己的谷歌实验室环境。

  1. 你可以简单的上传GitHub 项目中包含的笔记本:

2.或者你可以在 Google Colab 中挂载一个驱动器,克隆 defect_classifier repo,从你的 Google drive 中打开笔记本。

2.1 打开新的 Python3 笔记本

2.2 在 Google Colab 上挂载一个驱动

在第一个单元格中运行(播放按钮,或 Mac 上的 shift-return):**

您将看到一个 URL 。点击它,获得授权码,回到你的笔记本并输入。您现在可以在 Colab 中访问您的 Google Drive:

2.3 克隆回购

…然后将克隆的笔记本复制到您安装的驱动器中:

2.4 从驱动器打开笔记本

现在在你的 Google drive 中找到这个笔记本,就像你找到其他文件一样,右键点击,然后选择 open with Google Colab。

设置硬件加速

我们将要求图形处理单元 ( GPU )在合理的时间内训练我们的深度学习模型。我们可以通过点击顶部的运行时,选择更改运行时类型,选择硬件加速器下的 GPU ,点击保存,在 Colab 中设置硬件加速。****

********

第 1 部分:缺陷提取

首先,我们需要构建缺陷提取管道。为了使用更少的代码,我创建了一个小的库,它将抽象大部分的图像处理。我计划继续添加到这个库中,以保持关注。关于用 R 和 Python 创建库的直观而详细的教程,请看这里的。

现在让我们导入库:

安装并导入 tetryonai

该库可以在 GitHub 上免费获得。我们可以使用 pip 从 GitHub 安装库,如下所示:

**!pip install git+https://github.com/sean-mcclure/tetryon_ai.git**

请注意!在通常的 pip 命令之前。这是做交互计算时常用的“bang”符号。它允许我们的 Google Colab 环境访问底层 shell。

我们现在可以导入库:

**from tetryonai.main import ***

您也可以点击左侧的文件图标,这样我们就可以看到我们正在处理的文件(根据需要点击刷新):**

创建文件夹

让我们创建文件夹来保存我们的差异图像、提取的缺陷以及测试我们的模型所需的保留图像。我们将使用 tetryonai 的目录 函数:

****directory**(**{
    "choice" : "make",
    "directory_path" : "difference_img"
})**directory**(**{
    "choice" : "make",
    "directory_path" : "extracted_defects"
})**directory**(**{
    "choice" : "make",
    "directory_path" : "hold_out"
})**

克隆 PCB 数据集

我们将需要访问大量的测试和模板 PCB 板图像。这些都可以通过 DeepPCB 项目在 GitHub 上获得。这为我们提供了从线性扫描 CCD 获得的大量图像。有关更多详细信息,请查看此项目。

通过在下一个单元中运行以下命令,将 DeepPCB 项目克隆到您的 Colab 环境中:

**!git clone https://github.com/tangsanli5201/DeepPCB.git**

点击文件窗格中的刷新来查看新的 DeepPCB 文件夹。您可以展开文件夹,查看有多少图像可用。**

对于这个项目,我们将只使用如上所示的组临时和测试图像。这些是先前在图 1-3 中描述的图像。

将 PCB 温度和测试图像路径存储在 Dict 中

让我们写一些 Python 来存储来自我们的 DeepPCB 文件夹的 group00041 temp 和测试图像路径,放在一个 dict 中。我们将在下一步中使用它来区分我们的图像:

所有 PCB 图像的差异

现在我们将使用图像处理来区分每对临时和测试图像,使用我们刚刚创建的 dict 中指定的路径。我们还需要展示一组这些图像,以便在我们的应用程序中进行测试。为了方便起见,我将这些操作合并到一个代码块中:

我们使用 tetryonai 的 copy_files 函数将必要的文件复制到我们之前创建的名为 hold_outtarget_directory中。我使用 20%的split_percentage作为坚持集。在上面的代码中,一旦 20%的图像被保留,剩下的就用 tetryonai 的 subtract_images 函数减去。

如果我们刷新我们的文件窗格并检查我们的 difference_imghold_out 文件夹,我们将看到已检查的图像。您也可以打开 difference_img 中的一些图像来确认减影过程有效(如图 2 所示)😗***

从差异图像中提取缺陷

我们现在准备好提取出现在差异图像中的高亮缺陷。我们将使用 tetryonai 的extract _ contours _ from _ image函数来读入每幅差分图像,提取每幅图像的缺陷,缺陷保存到我们在开始时创建的 extracted_defects 文件夹中:****

损坏/不可读的图像将被跳过提取 _ 轮廓 _ 来自 _ 图像功能。这仍然给我们留下了大量的图像来训练我们的模型。

extract _ contours _ from _ image函数使用轮廓提取差分图像的高亮部分。将轮廓视为连接所有具有相同颜色或强度的连续点(沿边界)的曲线。轮廓是形状分析和物体检测/识别的有用工具。你可以在 OpenCV 上阅读更多关于轮廓的内容。****

刷新文件窗格并检查 extracted_defects 文件夹。您会注意到,已经为从每个差分图像中提取的每组轮廓创建了单独的文件夹。

我们现在将所有提取的缺陷合并到一个名为 all_extracts 的目录中。现在让我们创建目录:

****directory**(**{
    "choice" : "make",
    "directory_path" : "all_extracts"
})**

我们编写一些 Python 来遍历提取的 _ 缺陷目录,并且使用 tetryonai 的 move_files 函数移动任何 PNG 文件:**

刷新文件窗格并检查 all_extracts 文件夹将显示所有提取的缺陷。

为土耳其语压缩提取的缺陷

在第 2 部分中,我们将把标签应用到我们提取的缺陷上(“突化”)。因此,我们需要下载所有提取的缺陷图像,以便与我们接下来创建的标签应用程序一起使用。所以让我们压缩所有提取的缺陷以便于下载:

!zip -r /content/extracted_defects.zip /content/all_extracts/

如果你刷新你的文件面板,你会看到一个新的 extracted_defects.zip 文件被创建。

我们的标签应用程序还需要一个 JSON 文件,其中包含我们 zip 文件夹中图像的文件名。让我们写一些 Python 来抓取 all_extracts 目录中的所有文件名:

保存我们刚刚创建的 JSON 文件:

with open('extracted_defects.json', 'w') as outfile:
    json.dump(file_names_and_sizes, outfile)

刷新文件窗格,下载 extracted_defects.zipextracted_defects.json 文件:

暂时将这些文件保存到你的桌面

在第一部分中,我们区分了一大组测试和模板 PCB 图像,使用轮廓提取了差异图像中的缺陷,并创建了 2 个文件,我们需要使用我们接下来创建的标签应用程序。让我们现在做那件事。

第 2 部分:缺陷标签

有了我们提取的缺陷图像集,我们就有了一个相当大的数据集来用于训练图像分类的深度学习模型。但是像任何有监督的机器学习方法一样,我们的训练数据需要标签。因为我们提取了我们的图像,我们显然没有这些标签可用。

图像分类需要大量的标注数据,使得手工标注成为一项艰巨的任务。但是图像分类的庞大数据先决条件通常并不妨碍节目。这是因为有大量公开可用的模型已经在大型基准图像集上进行了训练。只要领域不是太不同我们可以使用少得多的数据来训练预训练模型。我们能使用更少的标记数据,结合现有的图像分类器,成功地对提取的缺陷进行分类吗?

我的打赌是肯定的,但只有一种方法可以找到答案。这是一个制造新的通用产品的好机会。该产品将允许我们为一组图像创建标签。

产品:缺陷 Turker

T T 由于这篇文章已经够长了,所以我就不详细介绍创建缺陷图克尔的步骤了。这是一个简单的应用程序,允许我们将标签应用到它的img文件夹中的任何图像。

太多时候,人们急于寻找现有的解决方案,而产品并不能解决他们真正的需求。大量的时间(和金钱)最终被浪费在试图迫使现有产品应对特定的挑战上。定制软件开发是非常强大的,但只有当它不被人员过多的项目和过度设计的解决方案所抑制时。快速原型制作是一项非常有益的技能。

我们不会在 Colab 上做标记,因为它目前没有明显的方法在其环境中托管 web 应用程序。因此,我们将在本地运行缺陷检测器。****

缺陷 Turker 可在 GitHub 上获得。通过在终端会话中运行以下命令,将应用程序克隆到您的本地机器上:

git clone [https://github.com/sean-mcclure/defect_turker.git](https://github.com/sean-mcclure/defect_turker.git)

使用一个终端会话来克隆缺陷生成器应用程序并启动一个简单的 web 服务

我还使用下面的代码行启动了一个简单的 web 服务😗***

**python3 -m http.server**

在我们的浏览器打开应用程序之前,让我们添加我们之前创建的提取的缺陷图像(extracted _ defects . zip)和 JSON 文件(extracted _ defects . JSON)到应用程序中。这些文件在我的桌面上,所以我双击 ZIP 文件来解压缩它的内容:****

..并在终端中的新标签中运行以下代码行,从 defect_turker 目录中:****

**mv ../content/all_extracts/* ../defect_turker/img/**

确认我们提取的缺陷图像在应用程序的 img 文件夹中:

我们还需要包含所有图像名称的 JSON 文件。现在移动这些:

**mv ../extracted_defects.json ../defect_turker/img/**

我们现在准备给我们的图像贴标签。让我们启动应用程序。由于我们的 web 服务器已经启动并运行,我们只需将浏览器指向[http://localhost:8000/](http://localhost:8000/)

如果需要更改 web 服务器上的端口号,只需将端口添加到完整命令的末尾即可(例如 python3 -m http.server 5555)

您应该看到以下内容:

你的第一张图片可能不是这里显示的。我们可以看到总共有 1333 张图片。标签太多了。我决定把这一天分开,一次做几百个。理想情况下,我们会将这项任务分配给团队中的几个人。

产品创意:创建这个应用程序的一个版本,允许多人完成标记,然后将结果重组到一个 CSV 文件中。

第一个图标是显示每种缺陷类型的标签的图例。参考这个来了解哪个缺陷有什么标签。

原来我们的图像差分只提取了 3 种类型的缺陷:

  • 马刺
  • 假铜
  • 短的

只提取了 3 种类型的原因与我们的图像差异是如何建立的有关。我们也可以很容易地检测到其他类型的缺陷,但是在本文中,我将把我们的应用保持在这三种缺陷类型上。详情见最后的注释。

现在我们可以开始标签标签:

只需点击与所展示图像的标签相对应的按钮。一旦我们标记了所有的图像(或者你想要多少就有多少),我们就点击右上角的图标下载一个 CSV 文件,该文件可用于训练我们的深度学习模型。

CSV 文件包含我们应用的每个图像名称和标签。将该文件保存为 image_labels.csv ,并将其保存在您的桌面上,用于第 3 部分。

我们现在准备好训练我们的深度学习 PCB 分类器。

第 3 部分:缺陷分类

我们回到 Colab 笔记本,使用我们用 Defect Turker 创建的 CSV 文件来训练我们的深度学习模型。我们将使用fastai来训练我们的深度学习模型。

研究机构 fast.ai 致力于通过他们的课程软件研究社区,让深度学习变得更加容易。他们的库 fastai 是一个强大的开源 API,位于 PyTorch 之上。深度学习领域的最新研究突破被嵌入到 fastai 库,这意味着我们可以访问尖端的深度学习模型,这些模型比许多其他 DL 库更准确、更快;同时编写更少的代码。

安装 fastai

**!pip install http://download.pytorch.org/whl/cpu/torch-1.0.0-cp36-cp36m-linux_x86_64.whl!pip install fastai**

导入:

**from fastai.vision import ***

上传带标签的数据(CSV 文件)

我们将上传用第 2 部分中的缺陷图创建的 image_labels.csv 文件。在 Colab 中运行下面一行来上传文件:

**from google.colab import files
files.upload()**

您可能需要运行两次,因为第一次运行通常会失败。

我们现在将使用 tetryonai 的 csv_to_dataframe 函数将 image_labels.csv 转换为数据帧:

**image_labels_frame = **csv_to_dataframe**(**{
    "path_to_csv" : "image_labels.csv"
})**

让我们检查一下我们标签之间的等级平衡。首先,我们将使用 tetryonai 的 get_feature_counts 函数获得每个标签的示例数:

**labelled_counts = **get_feature_counts**(**{
    "data_frame" : image_labels_frame,
    "feature" : "label"
})**

…然后使用 Matplotlib 创建我们的情节:

…这提供了:

类别平衡缺陷类型标签之间。

注意:如果“标签”出现在图表中,在创建视觉之前在单元格中运行del labelled_counts[‘label’]

我们的“多数类”不是,而 3 种缺陷类型相当平衡。我们可以 下采样 多数类,但是可能不需要。我们将继续使用我们的标签数据,并且仅在需要时重新调整我们的训练数据的平衡。

为培训 CSV 创建新目录

我们将使用的 fastai 训练方法需要为我们想要预测的每个类别单独标记 CSV 文件。让我们创建一个名为training_csvs的文件夹来保存这些单独的文件:

****directory**(**{
    "choice" : "make",
    "directory_path" : "training_csvs"
})**

…现在让我们使用我们从上传的 image_labels.csv 文件创建的image_labels_frame数据框架,为每个类创建单独的 CSV 文件

首先,我们为每个班级创建个人列表:

…然后我们将这些列表转换成每个类的数据帧:

现在让我们为 CSV 文件创建单独的文件夹:

现在,我们可以将各个 CSV 文件移动到相应的文件夹中:

将提取的缺陷图像添加到 CSV 文件夹:

我们显然需要图像来训练我们的图像分类器。这些将是我们在第 1 部分中提取的缺陷图像,压缩到名为 extracted_defects.zip 的文件中,并添加到我们的缺陷分类器中进行标记。

我们可以只使用当前在我们的 Colab 环境中的extracted_defects.zip文件,但是如果我们在几天内完成这个项目,我们可能已经从第 1 部分重新开始并重新运行我们的管道。这显然会重新创建我们的压缩图像文件,使得我们之前标记的 CSV 文件( image_labels.csv )中的文件名与我们的压缩图像文件名不匹配。

鉴于以上所述,让我们删除当前在 Colab 中的extracted_defects.zip文件,并上传我们知道我们与缺陷 Turker 一起使用的 extracted_defects.zip 文件。

从 Colab 中删除压缩图像:

!rm extracted_defects.zip

****上传用于贴标:

files.upload()

extracted_defects.zip 移动到 training_csvs 文件夹中:

**move_files**(**{
    "file_paths" : ["extracted_defects.zip"],
    "target_directory" : "training_csvs"
})

****从 extracted_defects.zip 中提取所有图像到 training_csvs 文件夹中:

import zipfilewith zipfile.ZipFile("training_csvs/extracted_defects.zip", 'r') as zip_ref:
    zip_ref.extractall("training_csvs/")

现在我们可以使用 image_labels.csv 文件将所有提取的缺陷图像移动到它们适当的训练文件夹中:

为了清理,让我们删除任何未被标记的剩余 PNG 文件(缺陷图像):

**directory**(**{
    "choice" : "remove",
    "directory_path" : "training_csvs/content",
    "force" : True
})

读入用于训练分类器的图像数据

我们应该检查的图像数据以确认它们被正确读入:

data.show_batch(rows=4, figsize=(7, 8))

我们可以看到标签与图像匹配(杂散、杂散、短路和非缺陷图像都与所示标签匹配)。

下载预先训练好的分类器:迁移学习

我们现在准备训练我们的分类器。如果我们想要得到足够好的结果来创造人们使用的产品,我们需要一个相当深入的架构。深层网络(大量堆叠的层)允许比其浅层对应物更深的表示,并且已经显示出在图像识别任务中表现得非常好。

我们将从火炬视觉下载一个预训练的** PyTorch 模型。我们想使用一个预先训练好的模型来减少训练时间和 CPU 需求。预训练模型已经在大型基准数据集上进行了训练,以解决与我们类似的问题。这叫做 迁移学习 。**

下图显示了我们将使用的 ResNet34 架构。该架构基于本文中讨论的研究。

为了从 fastai 下载预训练的 ResNet34 模型,我们运行以下程序:

from fastai.metrics import error_rate # 1 - accuracy
learn = create_cnn(data, models.resnet34, metrics=error_rate)

让我们根据缺陷数据来训练我们的模型。

关于缺陷数据的培训

通过使用预训练模型,这意味着在我们实施之前,所有的权重都已经被训练。我们称这种设置为冻结模型,默认情况下所有层都冻结(除了最后一层)。冻结防止训练有素的重量被修改,这正是我们想要的,因为我们正在寻求实施迁移学习

我们在大量超参数中使用默认值。用最少的工作量来看看我们的模型能有多好是有意义的。

我们需要确保我们的笔记本电脑正在利用 GPU 硬件加速。我们在一开始将硬件加速器设置为 GPU 时就这样做了。如果您在本文开始后重新启动了笔记本电脑,请确保它仍处于设置状态。

我们通过 Nvidia 的 CUDA API 访问 GPU 硬件。这使得我们能够在合理的时间内利用并行计算来训练我们的模型。

fastai 的fit_one_cycle方法使用了 Leslie Smith 的 1cycle policy ,为我们提供了一种更快速的训练复杂模型的方法。你可以在这里了解更多关于这项政策的信息。

我们将为 10 个时期训练我们的模型。一个“时期”是我们的学习算法在整个训练数据集中工作的次数(训练数据中的每个例子都有机会更新模型参数)。

Epoch 与批量不同,后者是模型更新前处理的样本数。

我们通过调用 fastai 的fit_one_cycle方法,指定 10 个时期,开始只训练模型的最后一层😗***

defaults.device = torch.device('cuda')
learn.fit_one_cycle(10)

下面显示了训练期间的前 3 个时期:

PyTorch 的 ResNet34 深度学习模型的前 3 个时代(为清晰起见加速)。

全部训练大约需要 40 分钟。请记住,该培训将离线执行,因此培训时间对我们的产品来说并不重要(而推理时间才是)。

您的最终结果应该与此类似:**

我们的错误率相当低,这很好。对于使用默认超参数的冻结模型来说还不错。但是让我们看看我们是否能改进结果。

解冻模型

现在我们将解冻网络,这意味着我们的模型中的所有权重现在都可以更新。我们可以在我们学习对象中调用 fastai 的 unfreeze()方法:**

*learn.unfreeze()*

因为我们已经“解冻”了我们的模型,所以在所有层上使用相同的学习速率不再有意义。深度模型中的不同层受益于所谓的“差异”学习率,其中根据它们在网络中的应用位置来使用不同的速率。要使用不同的学习率,我们首先需要找到一个合适的范围。

找到一个好的学习率范围

为了找到一个合适的学习率范围,我们将绘制模型的损失与增加的的关系图。我们可以这样做,首先运行:**

*learn.lr_find()*

我们试图观察和理解 线索 这些线索有时会在训练中出现。通过这样做,我们可以通过其超参数潜在地 调整 我们的架构,从而减少 所需的时期 。这使我们能够 避免 运行完整的网格随机搜索以找到好的超参数。****

我们可以通过运行以下程序来绘制学习率与损失的关系图:**

*learn.recorder.plot()*

典型的方法是在误差变得更糟之前,取正好低于最低点的值。我们将使用在 3 个 10^–5 和 3 个 10^e-4 之间的范围**

以 D 不同的学习率进行训练

我们现在将再次运行fit_one_cycle,使用max_lr参数来指定学习率的范围。让我们使用不同的学习速率跨 4 个时期训练我们的解冻模型:

*learn.fit_one_cycle(4, max_lr=slice(3e-5, 3e-4))*

请注意,我们实现了更低的误差。当然,如果不首先检验预测的质量,这几乎毫无意义。**

检查混淆矩阵

像往常一样,我们检查混淆矩阵(CM ),看看是否有任何关于错误分类的危险信号。我们的模型可能会遇到某些类型的缺陷。**

我们看到大多数值位于 CM 的特征对角线上。看起来我们的模型在预测缺陷方面相当有效。我们将在我们的产品中使用这种模式。

保存并导出训练好的模型以进行推理

现在我们已经有了一个具有合理精确度的训练模型,我们想要下载*它作为一个对象,可以用于推理(对真实数据的预测) :***

*learn.**save**('defect_classifier')
learn.**load**('defect_classifier')
learn.**export**('defect_classifier.pkl')*

的。 pkl 文件应该出现在 training_csvs 文件夹中。下载这个:**

正在下载。pkl* 文件***

大小大约是 84MB 。

用保留的文件名创建 JSON 文件

回想一下,在第 1 部分中,我们创建了 extracted_defects.json 文件,并将它上传到 Defect Turker 进行标记。我们现在将为我们构建的实际应用程序创建一个文件名为第 4 部分:

…并保存这个文件 test_temps.json :

*with open('test_temps.json', 'w') as outfile:
    json.dump(test_temps, outfile)*

下载文件:**

最后,让我们压缩下载用于 PCB 缺陷分类器的展示图像:**

..,而照常从* Colab 下载 :***

我们将在第 4 部分中使用这些文件。

我们现在准备构建我们的 PCB 缺陷分类器应用程序。

第 4 部分:构建 PCB 缺陷分类器应用程序

我们已经达到了这个项目(以及任何真实项目)的最终目标,那就是为最终用户创建一个真正的应用程序。正如我在介绍中所说的,围绕我们的机器学习创造产品是唯一真正的测试,我们的工作是否有超越一群工程师的效用。

我还声明,为了保持这篇文章的合理篇幅,我不会涵盖所有的管道和前端编码。

产品:PCB 缺陷分类器

这是我们在本文中想要创建的主要应用程序。我们研究了这种产品对印刷电路板领域的最终用户有用的原因。让我们来看看制作这款产品所需的主要部件,构建后端和前端组件。

用户流程和主要功能

  • 用户获取下一组 PCB 图像(测试和模板);**
  • 用户检查* PCB 图像;***
  • 用户运行差分/提取/预测流水线;**
  • 用户检查预测;**
  • 用户查看记录的费用。**

用户也可以根据其组织的制造情况设置缺陷成本**

创建 Web 服务

我们将使用 Flask 来创建一个轻量级的 web 服务。在 Flask 和 Azle 之间,在后端调用我们的 Python 函数将会很容易。有很多关于如何创建 Flask web 服务的教程,所以我不会在这里详述这些步骤。同样,所有代码都可以在 GitHub 上获得。

管道

我们将使用 Colab 环境中使用的主要管道功能来创建产品管道的后端。

从上面的数字中,我们知道我们需要:

  • 图像差分
  • 图像提取
  • 缺陷预测

我们将使用 Azle 来调用classifier.pyimage_differencing.pyutility.py中包含的函数(通过app.py)。

下面是需要的主要* 功能:***

图像差分:

***subtract_images**(**{
    "image_path_1": image_path_1,
    "image_path_2": image_path_2,
    "write_path": "diff_img/diff_" + id + ".png"
})*

缺陷提取:

***extract_contours_from_image**(**{
    "image_path": path_to_diff,
    "write_path": "contours/" + id + "/",
    "hsv_lower": [0, 150, 50],
    "hsv_upper": [10, 255, 255]
})*

缺陷预测:

***def classifier**(image_path):
    learn = load_learner('model')
    img = open_image(image_path)
    prediction = learn.predict(img)
    return(str(prediction))

**def predict**(image_path):
    prediction = classifier(image_path)
    return(prediction)*

前两个是我们在 Colab 环境中用于处理测试和模板图像的相同的 tetryonai 函数。

我将创建一个名为 api 的目录,保存主 Flask 文件(app.py)和与我们的管道相关的文件:

***api** └── app.py
└── classifier.py
└── image_differencing.py
└── utility.py*

utility.py内的实用函数只是帮助应用运行所需的一些基本操作。

UI 元素

在 Azle 中创建应用程序支持快速原型开发。这里是主要部分:

添加 按钮:

*az.**add_button**("target_class", target_instance, {
    "this_class": "my_button",
    "text": "FETCH BOARDS"
})*

显示画面画面:**

*az.**add_image**("target_class", target_instance, {
    "this_class": "my_image",
    "image_path": "https://bit.ly/1L574po"
})*

调用****API

滑块

获取应用程序

通过在终端中运行以下命令,从 GitHub 克隆应用程序:

*git clone [https://github.com/sean-mcclure/defect_classifier.git](https://github.com/sean-mcclure/defect_classifier.git)*

我们需要添加以下文件:

  • 新的 PCB 板图像在模型训练期间没有使用;**
  • 包含所有新板名的 JSON 文件;
  • 经过训练的 PCB 缺陷分类器型号**

将图像文件添加到缺陷分类器应用程序

回想一下在第 3 部分中,我们创建并下载了文件 test_temps.jsonhold_out.zip 。将这些文件放在你的桌面上(如果还没有的话),然后解压hold _ out . zip 文件夹。**

现在我们可以添加所有的保留图像到我们的应用程序中,方法是将它们移动到 test_temps 文件夹中。从克隆的应用程序内部(假设应用程序被克隆到桌面):**

*mv ../Desktop/hold_out/* test_temps/*

…以及 test_temps.json 文件:

*mv ../Desktop/test_temps.json test_temps/*

现在我们有:

***test_temps** └── 000410009_temp.jpg
    └── 000410009_test.jpg
    └── 00041012_temp.jpg
    └── 00041012_test.jpg
    ...
    └── test_temps.json*

我们的应用程序现在可以访问我们在 PCB 板制造场景中预期的图像。

将模型添加到应用程序

在第 3 部分,我们保存了。 pkl 文件。这是我们训练过的缺陷分类器模型。让我们将它添加到应用程序的模型文件夹中:**

*mv ../Desktop/**defect_classifier.pkl** model/*

我们需要重命名我们的模型对象,因为 fastai 的load_learner方法期望该对象被称为“export.pkl”:**

*mv model/defect_classifier.pkl model/**export.pkl***

让我们来看看我们的 PCB 缺陷分类器是如何工作的!

第 5 部分:运行/测试应用程序

启动服务

我们在 Flask 中构建了 web 服务。我们像往常一样称之为:

*python api/app.py*

您应该在您的终端中看到以下内容:**

** Serving Flask app "app" (lazy loading)
* Environment: production
   WARNING: This is a development server.
 * Debug mode: on
 * Running on [http://127.0.0.1:5000/](http://127.0.0.1:5000/) (Press CTRL+C to quit)
 * Restarting with stat
 * Debugger is active!
 * Debugger PIN: 622-284-701*

我们可以看到我们正在调试器模式下运行。这是理想的,因为我们对 Python 所做的任何更改都会导致服务自动重启。这对原型非常有利。**

现在我们需要启动一个 web 服务来显示应用程序的前端。在您的终端中运行以下命令:**

*python3 -m http.server*

然后打开您的浏览器以:**

*[http://localhost:8000/app/](http://localhost:8000/app/)*

我们的第一个屏幕如下所示:

用户的第一步是运行获取电路板,这将引入存储在 test_temps 中的 PCB 图像(我们的保持图像)。**

这将获取测试图像和模板图像。制造商需要对这些 PCB 板进行测试,以确定是否存在任何缺陷。

运行管道

现在,用户运行管道,这将:

  • 减去图像以创建差分图像;
  • 提取差分图像中包含的缺陷;
  • 预测每个提取缺陷的缺陷类别;
  • 将预测成本添加到成本汇总中。**

这是我们在开始时看到的更完整的视频,展示了我们如何针对我们的管道进行多次运行,每次都能获得准确的预测:

太美了。我们可以看到,差异和缺陷提取几乎是瞬间完成的。对于每个提取的缺陷,预测被设置为 1000 毫秒,这对于我们的演示来说似乎是合理的。这对 PCB 制造商来说够快了吗?

F F 特色创意:可以向用户呈现一个滑块来设置预测时间。基于制造吞吐量,设置一个更低的预测时间可能是有意义的。当然,这必须与我们预先训练好的模型(推理时间)做出预测的速度相平衡。建立一个显示最佳推理时间的图表是一个很好的实验,可以运行并展示给客户。

提高

我们可以做很多事情来改进我们的产品。虽然模型的准确性已经很好了,但是我们没有尝试其他的缺陷类型。在这些缺陷上模型性能会一样好吗?如果这个产品需要以高得多的速度预测呢?牺牲一些来之不易的准确性,使用运行更快推理的浅层网络值得吗?

以下是我们可以尝试的一些事情:

  • 数据扩充
  • 其他超参数设置
  • 更多数据
  • 不同的架构

通过不同的/额外的产品功能,我们还可以构建各种用户体验。让用户选择将糟糕的预测标记为不正确是否有意义(将此信息反馈给模型)。除了成本之外,计算关于预测缺陷的额外信息怎么样?也许是创造有趣的可视化总结。通过拥抱快速原型,我们不需要预先知道这些问题的答案;我们可以用真正的产品来构建、展示和按需改变。

摘要

在本文中,我们创建了一个端到端的 PCB 缺陷分类器应用程序。我们使用图像处理来区分测试和模板 PCB 图像集,使用轮廓提取缺陷,并使用深度学习对提取的缺陷进行分类。然后,我们创建了一个前端应用程序,允许用户运行我们的机器学习管道并查看模型预测。

像这样的应用程序有助于自动执行手动、耗时且容易出现人为错误的任务。目的不是取代人类专家,而是增加他们的努力,以便他们可以专注于工作中更具战略性和创造性的方面。

如果您有任何问题/需要支持,请使用评论部分。我可以帮助其他人为他们的特定目的修改这些应用Azle 问题可以上栈溢出**

您可能还喜欢:

*** [## 学习建立机器学习服务,原型真实的应用程序,并部署您的工作…

在这篇文章中,我将向读者展示如何将他们的机器学习模型公开为 RESTful web 服务,原型真实…

towardsdatascience.com](/learn-to-build-machine-learning-services-prototype-real-applications-and-deploy-your-work-to-aa97b2b09e0c) [## 创建 R 和 Python 库的分步指南(在 JupyterLab 中)

r 和 Python 是当今机器学习语言的支柱。r 提供了强大的统计数据和快速…

towardsdatascience.com](/step-by-step-guide-to-creating-r-and-python-libraries-e81bbea87911) [## 机器学习工作流的 GUI 化:快速发现可行的流水线

前言

towardsdatascience.com](/gui-fying-the-machine-learning-workflow-towards-rapid-discovery-of-viable-pipelines-cab2552c909f) [## 用 D3 Observable 创建 Web 应用程序

什么是 D3 可观测?

towardsdatascience.com](/creating-web-applications-with-d3-observable-d5c53467ff12) [## 用 D3.js 从玩具视觉过渡到真实应用

我们经常孤立地学习技术和方法,与数据科学的真正目标脱节;至…

towardsdatascience.com](/combining-d3-with-kedion-graduating-from-toy-visuals-to-real-applications-92bf7c3cc713)

笔记

  1. 你会注意到我们只得到 3 种缺陷类型。这是因为差异的工作方式。它只挑选黑色差异的缺陷。其他缺陷,如老鼠咬,等等。需要进行白色差分。这可以通过简单地改变差分函数中图像路径的顺序来实现。因此,如果您想要检测、提取和预测所有的缺陷,那么运行两个版本。
  2. 如果您想删除轮廓中的文件夹,请在终端中运行 rm -r contours/*。如果你想删除 diff_img 中的所有图像,运行rm diff_img/*

参考

  1. 2014 年全球 PCB 产量估计为 602 亿美元
  2. 用于缺陷检测和分类的 PCB 数据集
  3. GitHub 上的唐三里 5201/deep PCB***

使用 Python 构建图像卡通化 Web 应用程序

原文:https://towardsdatascience.com/building-an-image-cartoonization-web-app-with-python-382c7c143b0d?source=collection_archive---------20-----------------------

使用 OpenCV 和 Streamlit 创建您自己的 Adobe Lightroom 式 web 应用程序来制作图像漫画

作者提供的图片来源

作为一个狂热的街头摄影爱好者,他几乎每个周末都在城市里游荡,寻找一些照片,Adobe Lightroom 总是我的首选软件,用来编辑我的原始照片,使其更加“不可拍摄”。

这让我想到,如果我能以某种方式创建自己的简单图像编辑软件,那不是很棒吗?

OpenCV 这样的开源计算机视觉库和 Streamlit 这样的开源 app 框架的出现,让我实现了这个想法。使用不到 100 行代码,您可以构建自己的图像卡通 web 应用程序,以某种方式模仿 Adobe Lightroom 的功能。

在本文中,我想向您展示如何使用 OpenCV 和 Streamlit 构建一个简单的 web 应用程序,根据滤镜将您的图像转换为卡通般的图像。

是什么让一幅图像成为漫画?

要将一幅图像转换成一幅漫画,我们通常需要做两个主要步骤:边缘检测和区域平滑。

边缘检测的主要目的显然是强调图像的边缘,因为通常卡通图像具有轮廓分明的边缘。同时,区域平滑的主要目的是去除不重要的颜色边界,并减少图像的噪声或颗粒,使图像不那么像素化。

根据不同的过滤器,我们可以获得不同的图像卡通化结果。在本文中,将有四种不同的过滤器:

  1. 素描
  2. 细节增强
  3. 双边过滤器
  4. 铅笔边缘

接下来,我想向您展示如何应用每种过滤器,以及您希望从每种过滤器中获得什么样的结果。

铅笔草图过滤器

使用铅笔草图过滤器,您的图像将被转换为草图,就像您的图像是用铅笔绘制的一样。下面是使用 OpenCV 将您的图像转换成铅笔草图的完整代码。

令人惊讶的是,我们可以使用 OpenCV 仅用三行代码将我们的图像转换为铅笔素描般的图片。现在让我一行一行地解释我们的图像到底发生了什么。

在第一行中,我们使用 OpenCV 中的cvtColor()函数将图像从彩色转换成灰度。我认为这很简单,这条线的结果是我们的图像变成了它的灰度表示。

接下来,我们使用高斯模糊图像。通过模糊灰度图像,我们基本上是在平滑图像,试图减少图像的噪声。另外,模糊是我们检测图像边缘的必要步骤。

为了模糊图像,我们可以使用 OpenCV 中的GaussianBlur()函数。我放在GaussianBlur()函数中的(25,25)是内核的大小。

因为我们使用高斯模糊,所以内核中像素值的分布遵循正态分布。核的数量越大,标准偏差就越大,因此模糊效果就越强。下面是不同内核大小的模糊结果的例子。

基于不同核大小的模糊效果

最后一步是将原始灰度图像与模糊灰度图像分开。分割图像给我们两个图像的每个像素之间的变化率。模糊效果越强,每个像素的值相对于其原点的变化就越大,因此,它给了我们更清晰的铅笔素描。

下面是使用铅笔素描过滤器的结果。

铅笔草图过滤器实现示例

细节增强滤波器

简而言之,细节增强滤镜通过锐化图像、平滑颜色和增强边缘,给我们带来卡通效果。下面是用这个滤镜将你的图像转换成卡通的完整代码。

第一步和前面一样,我们需要将图像转换成灰度图像。

接下来,不使用高斯模糊,而是应用中值模糊。为此,我们使用 OpenCV 中的medianBlur() 函数。中值模糊的工作原理是计算与内核重叠的像素值的中值,然后用计算出的中值替换其中心像素值。然而,如果你愿意,你可以使用高斯模糊。

接下来,我们需要检测图像的边缘。为此,通过 OpenCV 的adaptiveThreshold() 函数应用自适应阈值。自适应阈值的主要目标是根据被核重叠的像素的平均值,将图像的每个区域中的每个像素值转换成全黑或全白。

下面是自适应阈值对模糊图像的可视化效果。

左:自适应阈值之前—右:自适应阈值之后

为了使图像看起来更清晰,我们可以使用 OpenCV 的detailEnhance()滤镜。有两个参数需要指定为实参:

  • sigma_s:控制邻域的大小,该值将被加权以替换图像中的像素值。值越高,邻域越大。这导致更平滑的图像。
  • sigma_r:如果你想在平滑图像的同时保留边缘,这很重要。小值仅产生非常相似的颜色进行平均(即平滑),而差异很大的颜色将保持不变。

最后,我们使用自适应阈值的结果作为掩模。然后,根据遮罩的值合并细节增强的结果,以创建具有良好定义的边缘的清晰效果。

下面是细节增强过滤器的示例结果。

细节增强滤波器实现示例

双边过滤器

使用双边滤镜的一个很大的优点是,我们可以平滑图像和颜色,同时保留边缘。下面是用双向过滤将你的图像转换成卡通风格的完整代码。

如果你仔细观察,所有的步骤都与细节增强滤镜中的相似,但是这次我们没有使用detailEnhance() 函数,而是使用 openCV 中的bilateralFilter()函数。

调用这个函数时需要传递的参数和用detailEnhance()传递的参数一样,只有一个额外的参数,就是内核大小d。首先,您需要指定图像源,然后是控制平滑效果和保留边缘的dsigma_ssigma_r值。

下面是使用双边过滤器的结果示例。

双边滤波器实现示例

铅笔边缘过滤器

铅笔边缘滤镜创建一个只包含重要边缘和白色背景的新图像。为了应用这个过滤器,下面是完整的代码。

前两步与其他过滤器相同。首先,我们将图像转换成灰度图像。接下来,我们用 25 的内核大小模糊图像。

现在不同的是下一步,我们应用拉普拉斯滤波器来检测边缘。取决于核的大小,拉普拉斯滤波器中的值可以不同。如果想了解更多关于不同核大小的拉普拉斯滤波器的值,可以在这里阅读

拉普拉斯过滤器所做的是,它将以比对象内部的灰度级和图像背景更亮和更暗的强度来强调对象的边缘。下面是应用拉普拉斯滤波器前后的示例。

左:应用拉普拉斯滤波器之前—右:应用拉普拉斯滤波器之后

接下来,我们反转拉普拉斯滤波器的结果,使得较暗的灰色变得较亮,反之亦然。最后,通过应用 openCV 中的threshold()函数,我们可以将灰度图像转换成全黑或全白,这取决于我们指定的阈值。

下面是铅笔边缘过滤器的结果示例。

铅笔边缘过滤器实施示例

使用 Streamlit 构建图像漫画 Web 应用程序

在我们创建了图像卡通化过滤器的代码后,现在我们准备创建一个图像卡通化 web 应用程序。

作为第一步,我们需要把我们已经创建的图像卡通化过滤器的所有代码放入一个函数中,以便于访问。注意,到目前为止,我们已经对每个参数值进行了硬编码,比如内核的大小等等。

现在,我们不再对每个参数值进行硬编码,而是让用户使用滑块根据自己的偏好指定一个值。为此,我们可以使用 Streamlit 的streamlit.slider()函数。下面是其实现的例子。

 gray = **cv2.cvtColor**(img, cv2.COLOR_BGR2GRAY)scale_val = **st.slider**('Tune the brightness of your sketch (the higher the value, the brighter your sketch)', 0.0, 300.0, 250.0)kernel = **st.slider**('Tune the boldness of the edges of your sketch (the higher the value, the bolder the edges)', 1, 99, 25, step=2)gray_blur = **cv2.GaussianBlur**(gray, (kernel, kernel), 0)cartoon = **cv2.divide**(gray, gray_blur, scale= scale_val)

使用这个滑块,您可以创建一个交互式图像卡通化 web 应用程序,就像 Adobe Lightroom 一样。每次调整内核和其他参数的值时,图像卡通化的结果都会实时改变和更新。

我们可以在上面创建的每个图像卡通化过滤器中应用这个streamlit.slider()来替换硬编码的参数值。

接下来,我们需要添加一个小部件,这样用户可以上传他们自己的图像,他们想转换成卡通。为此,我们可以使用 Streamlit 的streamlit.file_uploader()函数。要在我们的 web 应用程序中添加一些文本,我们可以使用 Streamlit 的streamlit.text()streamlit.write()功能。

在用户上传了他们的图像之后,现在我们需要显示图像,使用图像卡通化过滤器之一编辑图像,并显示卡通化的图像,以便用户知道他们是否想要进一步调整滑块。为了显示图像,我们可以使用 Streamlit 的streamlit.image()功能。

下面是如何用不到 100 行代码构建图像卡通化 web 应用程序的实现。

现在,您可以打开提示符,然后进入包含上述代码的 Python 文件的工作目录。接下来,您需要用下面的命令运行代码。

streamlit run your_app_name.py

最后,你可以在本地电脑上玩你的图像卡通化网络应用程序了!下面是 web 应用程序的示例。

web 应用程序示例(来源:作者)

部署您的 Web 应用程序

此部分完全是可选的,但是如果您想要部署您的 web 应用程序,以便其他人也可以访问您的 web 应用程序,您可以使用 Heroku 部署您的 web 应用程序。

要将您的 web 应用程序部署到 Heroku,首先要免费创建一个 Heroku 帐户,然后下载 Heroku CLI。

接下来,您需要在 Python 文件所在的目录中创建另外四个文件,它们是:

  • requirements.txt: 这是一个文本文件,告诉 Heroku 构建 web 应用程序需要哪些依赖项。因为在我们的 web 应用程序中,我们使用了四个不同的库:opencvnumpyPillowstreamlit,那么我们可以将所有这些库及其版本写入 requirements.txt
opencv-python==4.3.0.36
streamlit==0.63.0
Pillow==7.0.0
numpy==1.18.1
  • setup.sh: 这是在 Heroku 上设置您的配置的文件。为此 setup.sh 文件编写以下内容。
mkdir -p ~/.streamlit/                       
echo "\                       
[server]\n\                       
headless = true\n\                       
port = $PORT\n\                       
enableCORS = false\n\                       
\n\                       
" > ~/.streamlit/config.toml
  • Procfile: 这个文件告诉 Heroku 应该执行哪些文件以及如何执行。为 Procfile 编写以下内容。
web: sh setup.sh && streamlit run cartoon_app.py
  • Aptfile :这是 Heroku 构建包的文件,使 OpenCV 能够在 Heroku 中运行。为 Aptfile 编写以下内容。
libsm6                       
libxrender1                       
libfontconfig1                       
libice6

接下来,打开命令提示符,然后转到 Python 文件和这四个附加文件的工作目录。在这里,输入heroku login,你就可以登录你的 Heroku 账户了。

现在你可以通过输入heroku create your-app-name来创建一个新的应用。要确保您在新创建的应用程序中,请键入以下内容:

heroku git:remote -a your-app-name

接下来,您需要在新创建的应用程序中添加一个 buildpack,以便 OpenCV 能够在 Heroku 上运行。要添加必要的构建包,请在 Heroku CLI 上键入以下内容。

heroku create --buildpack https://github.com/heroku/heroku-buildpack-apt.git

现在你已经准备好了。接下来,您需要通过键入git init后跟git add .git commitgit push heroku master命令来初始化一个空的 git。

git init
git add .
git commit -m "Add your messages"
git push heroku master

之后,部署过程将开始,等待此部署过程可能需要一些时间。最后,Heroku 将为您新部署的 web 应用程序生成一个 URL。

就是这样!现在,您已经构建了自己的图像卡通化 web 应用程序,它模仿了 Adobe Lightroom 的功能。

你可以在这里 看到这个图片漫画化 web app 部署版本的例子

或者你可以在我的GitHub页面看到这张图片漫画化的完整代码。

为您的图像识别模型构建一个图像摄取界面应用程序

原文:https://towardsdatascience.com/building-an-image-taking-interface-application-for-your-image-recognition-model-973b121cc9d9?source=collection_archive---------32-----------------------

图片来自 Pixabay

探索模型的实际应用

通常,数据科学家会为图像识别建立一个模型,查看精确度,如果精确度足够高,就认为工作已经完成。自从我 13 岁开始学习机器,我就一直不明白这一点。为什么要花费所有的时间来构建最好的模型——仅仅是为了满足于一个数字?

在本文中,我将提供代码,并带您了解如何通过构建一个拍照界面来与模型进行交互,从而真正完成您的项目。

安装 cv2 (OpenCV)

我们将用于拍摄图像的库是cv2。因为cv2不能在 Kaggle 这样的在线平台上工作,所以必须在你的电脑上本地完成。但是,仍然可以在 Kaggle 上训练模型的权重,以.h5文件的形式下载(对于 Keras/TensorFlow)并加载。

在 Anaconda 或命令提示符下,键入

conda create -n opencv python=3.6

这在 Python 版中创建了一个名为opencv的新环境,可以用您正在使用的任何版本来替换它。

最上面的几行应该是这样的。

接下来,输入

pip install opencv-python

您已经成功安装了cv2!现在你可以开始拍照了。

使用 CV2 拍摄图像

首先,导入库。

import cv2

接下来,我们必须创建一个VideoCapture实例。您可以测试该实例是否能够连接您的相机(如果不能,请检查您的设置以确保应用程序可以访问它)。

cap = cv2.VideoCapture(0)
if not (cap.isOpened()):
    print("Video device not connected.")

最后,该拍照了。如果你想控制什么时候拍照,第一行指定一个任意的变量和输入。除非输入内容(如按“enter”),否则程序无法继续,下一行将拍摄图片。拍摄图像时,您可能会看到网络摄像头指示灯短暂亮起。第三行关闭连接,第四行销毁所有访问相机的实例。

arb = input('Press enter to take picture.')
ret, frame = cap.read()
cap.release()
cv2.destroyAllWindows()

来自图像的数据存储在frame中。它可以用下面的代码转换成一个数组:

cv2_im = cv2.cvtColor(frame,cv2.COLOR_BGR2RGB)

调用cv2_im.shape时,输出为(480, 640, 3)。因此,图像(对于我的相机)是 480×640 像素(3 代表“深度”——每个像素中的三个值解释了要包含多少红色、绿色和蓝色来创建最终的像素颜色)。

现在你的图像已经被转换成了一个数组,matplotlibimshow()可以显示它了。

import matplotlib.pyplot as plt
plt.imshow(cv2_im)
plt.show()

干得好!

完整代码:

import cv2
import matplotlib.pyplot as pltcap = cv2.VideoCapture(10)
if not (cap.isOpened()):
    print("Video device unconnected.")
arb = input('Press enter to take picture.')
ret, frame = cap.read()
cap.release()
cv2.destroyAllWindows()
cv2_im = cv2.cvtColor(frame,cv2.COLOR_BGR2RGB)
plt.imshow(cv2_im)
plt.show()

根据模型标准格式化

卷积神经网络只接受固定大小的图像,例如(100, 100, 3)。有几种方法可以做到这一点。

要保留图像的比例长度,裁剪图像可能会有所帮助。

一般语法是

plt.imshow(cv2_im[y_upper_bound:y_lower_bound,x_lower_bound:x_higher_bound])

其中“上”和“下”由图像上的位置决定(y的“上”在图像的上方,x的“上”在更右边)。

举个例子,

plt.imshow(cv2_im[100:400,100:400])

这会将照片裁剪成方形。

但是,尺寸仍然是 300 乘 300。为了解决这个问题,我们将再次使用枕头:

pil_image = Image.fromarray(cv2_im[100:400,100:400])
width = 100
height = 100
pil_image = pil_image.resize((width,height), Image.ANTIALIAS)

NumPy 自动将枕头图像转换成数组。

import numpy as np
cv2_im_new = np.array(pil_image)

查看新图像:

plt.imshow(cv2_im_new)

好多了!图像的新形状是(100, 100, 3)——非常适合我们的模型。

在模型中运行

现在我们有了 NumPy 数组,只需简单地将它传递给模型。

model.predict(cv2_im_new)

基于此,用一些手动编码来标注图像的真实世界标签,它们可以被标注在一个标题中:

plt.imshow(cv2_im_new)
plt.title('Hand Gesture: '+classification)

感谢阅读!

在本教程中,您学习了如何实现一个简单的拍照界面来查看您的机器学习模型的真实应用。

构建一个增量推荐系统

原文:https://towardsdatascience.com/building-an-incremental-recommender-system-8836e30afaef?source=collection_archive---------11-----------------------

理想情况下,推荐系统应该能够适应发生的变化。

Nathália Rosa 在 Unsplash 上拍摄的照片

Learning Rate 是一份时事通讯,面向那些对 AI 和 MLOps 世界感到好奇的人。你会在每周五收到我关于最新人工智能新闻和文章的更新和想法。订阅这里

这个故事的第二部分现在通过下面的《走向数据科学》发表。

[## 构建增量推荐系统:第二部分

自信地超越最先进的技术!

towardsdatascience.com](/building-an-incremental-recommender-system-part-ii-2d0e782b2cf6)

要求或先决条件

虽然我会尽量减少数学术语,但这个故事希望读者熟悉用户-项目交互矩阵、矩阵分解、嵌入空间等概念,以及基本的机器学习术语。这个故事不是对推荐系统的介绍。这是对它们的增量变体的介绍。无论如何,这个故事是写给你可能称之为机器学习和推荐系统世界的初学者的。

推荐系统的矩阵分解

介绍

开始一个机器学习项目,数据科学家收集数据,处理数据,训练模型,并将其部署到生产中。当模型的性能开始恶化时,数据科学家通常会从头开始重复这个循环。只是这一次,他们有了新的数据示例来更新模型并提高其性能。然而,这通常是一种适得其反且效率低下的做法,尤其是对于那些对当前数据做出决策至关重要的业务领域。

现在,进入推荐系统的世界,用户的偏好会根据季节、预算、流行趋势等频繁变化。此外,到来的客户和新库存会产生所谓的冷启动问题,即系统没有足够的信息来将消费者与产品或服务相匹配。理想情况下,推荐系统应该在这些变化发生时适应这些变化,修改其模型以始终代表当前状态,同时要求数据只经过一次。这就是增量学习的思想。

这个故事是一个系列的第一部分,在这个系列中,我们通过实践的方法来探索如何将增量学习的思想应用于推荐系统。在第一部分中,我们使用基于 Pytorch 构建的 python 库,名为 CF Step ,重现了 joo Vinagre 等人在“快速增量矩阵因式分解,用于仅含正反馈的推荐”出版物中给出的结果。接下来,我们将通过应用几个技巧来达到更高的目标。

快速增量矩阵分解

我们将要实现的算法使用隐式的、仅肯定的反馈。让我们试着揭开这两个关键词的神秘面纱。隐性反馈是指用户从来没有对自己互动过的项目表达过直接的意见,比如评分。隐式反馈的一个例子是,客户购买了多少次产品,或者他们看了多少分钟的电影。客户购买产品或使用服务越多,我们就越有信心认为这是偏好的表现。仅正面反馈是一个通常与隐性反馈联系在一起的术语。这是因为,在隐性反馈的情况下,我们很少知道什么构成了负面互动。一个用户不与一个项目交互并不意味着什么。想象一个超市里的消费者。如果客户还没有购买特定产品,我们无法确定原因。

回到我们的实现,仅正意味着用户-项目交互矩阵*R* 仅包含布尔值,其中true值指示偏好,false值被视为缺失。这个假设有两个主要含义:保持了*R*的稀疏性,因为在训练期间仅使用正反馈,并且false值对于任何用户-项目交互都是完全有效的推荐候选。

算法和方法

现在让我们更仔细地看看本文中提出的增量随机梯度下降(ISGD)算法。

ISGD —增量新币

我们拥有的数据是元组或用户-项目交互。记住,这些都是积极的互动。算法的输入是三个数字;feat,用户或项目嵌入空间的维度,λ,正则化系数,η,学习率。该算法的输出是两个嵌入矩阵:用户的*A*和项目的*B*。这些矩阵的维数分别是number_of_users x feat代表*A**number_of_items x feat* 代表*B* 。然后我们有几个不同的步骤:

  • 检查活动用户是否已知。如果没有,创建一个具有随机潜在特征的新用户,从具有平均值0和标准偏差1的正态分布中抽取。对活动项目执行相同的操作。
  • 计算损失。因为我们只需要处理正反馈,目标永远是1。因此,我们只需从1中减去我们的预测。
  • 使用通用更新规则更新活跃用户的潜在特征(用户嵌入矩阵中的参数)。对活动项目执行相同的操作。
  • 转到下一个数据点。这样,我们可以处理任意长度的流数据。

实施和评估

对于这个实现,我们将使用 CF Step Python 库和众所周知的 Movielens 数据集。CF Step 是一个开源库,用 python 编写,构建于 Pytorch 之上,支持增量学习推荐系统的快速实现。这个图书馆是欧洲研究项目“云应用”的副产品。您可以通过运行以下命令轻松安装该库:

pip install cf-step

接下来,下载 movielens 1m 数据集并在方便的位置提取ratings.dat 文件,例如 Linux 中的tmp文件夹。对于这个实现,我们只需要这个文件。其余的文件(users.datmovies.dat)包含关于用户和电影的元数据。我们将使用 pandas 加载内存中的文件:

如您所见,我们将用户和电影 id 转换为类别,以便提取类别代码。现在,我们不必为嵌入矩阵生成创建单独的词汇表。我们只需要处理用户和电影代码,而不是 id。最后,我们总是通过这个数据帧在代码和 id 之间建立联系,以找到原始用户和电影。现在,让我们看看我们正在处理的独特用户和电影的数量。

如果我们打印这些数字,我们会看到我们正在与 6040 个独特的用户和 3706 个独特的电影。接下来,我们将按时间戳对数据进行排序,以模拟流事件。

正如我们所讨论的,该算法只支持正反馈。因此,我们将把5 的评分视为正面反馈,并放弃任何其他评分。我们希望用1 来标识喜欢的东西,用0来标识不喜欢的东西,并创建一个名为preference的新列来保存它们。然后,我们可以只过滤preference == 1

下面,让我们初始化我们的模型。为此,我们需要一个模型架构、一个目标函数(即损失函数)和一个优化器。我们将使用SimpleCF网络作为模型架构,这是 CF Step 提供的内置神经网络架构。对于目标函数,我们将使用一个简单的lambda函数,它接受一个预测和一个目标,并从目标中减去预测。在我们的例子中,目标总是1。对于优化器,我们将使用 Pytorch 的 SGD 实现。我们选择的因子数是128,学习率是0.06。现在我们准备初始化Step模型。

评估方法如下:

  • 通过对前 20%的数据进行训练来引导模型。
  • 使用recall@k作为度量,模拟数据流并评估模型的性能。
  • 如果用户是已知的,则进行预测并为该预测计算recall@k。然后,使用这种用户-项目交互,递增地训练算法。
  • 如果用户是未知的,只需使用这种用户-项目交互来递增地训练算法。

为此,让我们获取前 20%的数据,创建数据加载器并批量拟合模型。

然后,我们获取剩余的数据并创建一个不同的数据集。

最后,使用recall@10模拟流并评估模型。这一步在 GPU 上需要五到六分钟。

我们可以使用下面的代码来可视化我们的训练结果。为此,我们将使用5k滑动窗口的移动平均值,就像他们在出版物中所做的那样。我们可以看到,该图遵循了论文中针对 movielens 数据集给出的结果。要保存模型,使用model.save()内置方法并传递一个有效路径。

结论

在这个故事中,我们展示了增量学习在推荐系统中的重要性,并复制了由 joo Vinagre 等人发表的“快速增量矩阵分解用于仅正面反馈的推荐”的结果。我们介绍了 CF Step python 库,这是一个开源库,可以快速实现增量学习推荐系统。在下一章中,我们将更进一步,尝试提高算法的精确度。

我叫 Dimitris Poulopoulos,是希腊比雷埃夫斯大学BigDataStack的机器学习研究员和博士(c)。我曾为欧洲委员会、欧盟统计局、国际货币基金组织、欧洲中央银行、经合组织和宜家等主要客户设计和实施人工智能和软件解决方案。如果你有兴趣阅读更多关于机器学习、深度学习和数据科学的帖子,请在 twitter 上关注我的**LinkedIn@ james2pl******

构建增量推荐系统:第二部分

原文:https://towardsdatascience.com/building-an-incremental-recommender-system-part-ii-2d0e782b2cf6?source=collection_archive---------42-----------------------

自信地超越最先进的技术!

Unsplash 上由 Thibault Penin 拍摄的照片

克里斯·安德森在他 2004 年题为《长尾理论》的文章中说,我们正在离开信息时代,进入推荐时代。除非我们有办法过滤我们每天吸收的过量信息,只保留对我们重要的信息,否则可能的选择会变成噪音。

在本系列的第一部分中,我们讨论了推荐系统应该理想地适应变化。为此,我们使用 Pytorch 上构建的 python 库 CF Step 实现了一个推荐系统,它方便了增量推荐系统的编译。

[## 构建一个增量推荐系统

理想情况下,推荐系统应该能够适应发生的变化。

towardsdatascience.com](/building-an-incremental-recommender-system-8836e30afaef)

我们将我们的模型应用于 Movielens 1m 数据集,我们能够重现相关出版物的结果;Joao vina GRE 等人提出的用于推荐的快速增量矩阵分解,仅提供正面反馈

在这个故事中,我们的目标是走得更远,获得更好的结果。以前,我们能够得到平均召回率@10 ≈ 0.05,这是论文中展示的结果。让我们看看我们能否用一个简单的技巧战胜它!

Learning Rate 是一份时事通讯,面向那些对 AI 和 MLOps 世界感到好奇的人。你会在每周五收到我关于最新人工智能新闻和文章的更新和想法。订阅这里

充满信心地建设

为了获得更好的结果,我们需要使用整个数据集。这样做有望给我们提供更多的信息,特别是在训练过程中包含负面例子的方法。在本系列的第一部分中,我们只利用了 Movielens 数据集中的正面例子。这些是用户与电影的互动,用户给电影打了 5 颗星。

为了在我们的指标上获得更好的性能,我们需要使用整个数据集,并专门开发一种方法,以某种方式允许我们包括负面评级。

然而,根据我们的隐式反馈假设,我们应该有办法将表示偏好的评级值转换为表示置信度 的数字。换句话说,我们必须用数学来表达,如果用户u给电影i打了一颗星,我们肯定用户u绝对讨厌电影i。另一方面,我们有点确信用户喜欢一部评分为 4 星的电影。三星级这个棘手的案例又是怎么回事呢?

置信函数

置信度函数只是一个试探,它将某个用户-项目交互的评级作为输入,并产生一个新的实数值。

我们传递给函数的评级可以是我们拥有的任何隐含信息。例如,它可以是消费者购买特定产品的次数、观众观看电影的时间或任何其他对问题有意义的提示。我们的工作就是设计这样一个函数。

损失函数

在我们在第一部分中讨论的例子中,我们针对我们的问题优化了一个损失函数;只有正面的反馈。

目标总是 1(由于仅正的假设),并且预测评级来自对应于活动用户的向量与对应于活动项目的向量的点积。

既然我们想包含整个数据集,目标并不总是等于 **1** 。可以是正反馈的 **1** 和负反馈的 **0** 。因此,我们首先定义返回目标的偏好函数。

因此,就像之前我们在数据集中定义一个新的偏好列,如果评级大于3则取值1,否则取值0

最后,损失函数也应该改变以说明二进制目标。因此,我们将使用的是一个加权变量的二元交叉熵函数。

如果偏好是1,那么括号内等式的第一部分仍然有效,它乘以我们的置信水平。因此,如果偏好是0,括号内的第二部分保留下来,并乘以置信度函数的结果。

直觉上,我们对损失函数说,如果我们对自己通过偏好函数( **0** **1** )分配的目标非常有信心,而你弄错了,请多注意这个错误,这很重要!另一方面,如果我们对我们作为目标值传递的 **1** **0** 不那么确信,无论如何都要使用它,但是如果你弄错了也不要太担心。想象一下我们之前看到的 3 星评级的案例。我们将0作为目标,但是我们非常怀疑这是真的。

为此,让我们设计一个支持我们直觉的简单置信函数。

我们想通过这个函数传达的是,如果我们得到的评分是15,那么我们分别是对目标有信心的α分、0分或1分。然而,当我们有一个额定值3时,我们对我们计算为偏好的0没有任何信心,因此我们用0.1α乘以损失。数字α只是我们适应数据的一个超参数。现在,让我们看看所有这些措施的实施情况,并获得结果。

实施和评估

该实现与第一部分中的相似,因此我们将在此强调其变化。

在这种情况下,我们希望创建 preference 列,但是使用整个数据集,而不仅仅是正面的例子。此外,我们现在认为 4 级也是正面例子。

接下来,我们需要在代码中定义我们在上一节中设计的简单置信函数。

接下来,我们需要定义我们的模型,以便利用置信度函数。幸运的是,CF Step 的 API 已经接受了这样的函数。默认情况下,它定义了一个总是返回1lamda函数,所以没有权重。现在我们将传递我们指定的函数。此外,我们将True传递给SimpleCF网络的binary关键字,告诉它在输出上使用 sigmoid 激活。

传递给SimpleCFab关键字参数只是我们用于嵌入初始化的均匀分布的开始和结束限制。

其他一切与第一部分相同。为了比较这两种方法,我们将一起绘制结果。蓝线代表我们仅使用正例得到的结果,橙线显示使用置信度函数的整个数据集的结果。显然,第二种方法更优越。我们刚刚超越了出版物中呈现的结果!

结论

这个故事从我们在第一部分中离开的地方开始了构建一个增量推荐系统的主题。我们提出了一个置信函数的概念,它允许我们使用整个数据集并包含负面评价。我们展示了如何使用 CF Step 库,最重要的是,我们超越了第一部分的结果。

有许多方法可以设计一个置信度函数,算法的性能显然取决于我们的选择。此外,我们还可以使用其他方法来获得更好的结果。如果我们能在下面的故事中用 NMF 学的电影嵌入,那么迁移学习呢?这留给读者作为练习,因为 CF Step 支持加载预先计算的嵌入矩阵并冻结它们,所以它们不能被进一步训练。

[## 从电影情节中提取性本恶

如何使用非负矩阵分解得到不仅仅是你的电影摘要?

towardsdatascience.com](/extract-a-films-inherent-vice-from-its-plot-f43525abf0dd)

我叫 Dimitris Poulopoulos,是希腊比雷埃夫斯大学BigDataStack的机器学习研究员和博士(c)。我曾为欧洲委员会、欧盟统计局、国际货币基金组织、欧洲中央银行、经合组织和宜家等主要客户设计和实施人工智能和软件解决方案。如果你有兴趣阅读更多关于机器学习、深度学习和数据科学的帖子,请在 twitter 上关注我的**LinkedIn@ james2pl******

构建一个交互式仪表板,用 Python 模拟冠状病毒场景

原文:https://towardsdatascience.com/building-an-interactive-dashboard-to-simulate-coronavirus-scenarios-in-python-ed23100e0046?source=collection_archive---------45-----------------------

最终仪表板

在本文中,我将带您从头开始使用 Python Plotly/Dash 库构建整个仪表板——不需要任何 Plotly/Dash 方面的经验。我不会深入研究用于模拟的模型;你可以复制粘贴那部分。我已经在这里(背景)和这里(确切模型)解释过了。

你可以在这里查看我们将在本文中构建的仪表板,源代码在这个库中的文件 app.py 中。

让我们开始吧!Plotly/Dash 让您可以用很少几行代码构建基于 web 的反应式仪表盘。你只需要 Python,不需要 Javascript/HTML/CSS 等。创建漂亮的仪表板,并显示来自 Python 函数和脚本的数据非常简单。

非常短的 Dash 游戏攻略

网上有很多很棒的 Dash 教程,所以我就长话短说。

简单的应用程序

首先,这里有一个非常简单的 Dash 应用程序,直接来自官方网站。首先,将下面的代码保存在文件 app.py

然后,打开终端,进入 python3 app.py

以下是您应该看到的内容:

现在,打开网络浏览器,进入我圈出的地址。你应该知道这个:

请注意,在我们没有做任何事情的情况下,情节已经是反应式的了——这就是 Dash 的威力。现在再看一下代码。应该很容易看出哪些行对应于网页的哪些部分

我们实际上只需要一个额外的功能来编写我们的应用程序,其余的只是扩展简介中的简单示例。

复试

我们在屏幕上有一个可视化,但是用户只能,而不能改变他或她所看到的。这就是回调的用武之地:回调允许我们构建与用户输入协同工作并对其做出反应的应用程序——方法如下:

让我们实现一个输入,从用户那里获取文本并打印出来(同样,取自 Plotly 示例页面):

从代码中的注释可以清楚地看到发生了什么。每当用户输入一些东西时,我们得到输入并打印出来。同样,运行它(和以前一样的方式)应该会得到这样的输出(不可否认不是太漂亮):

当然,我们将使用一些其他功能(滑块、不同的图表和日期选择器等),但核心功能和设置保持不变:

  1. 指定一个布局,说明显示什么内容在哪里
  2. 实现回调来更新用户输入的内容
  3. 运行应用程序

数据

正如我在文章的开头所说的,我不会在这里解释这个模型,因为我已经在这篇这篇文章中解释过了——你可以随意查看这些来获得对这个模型的理解,但是,你不需要这样做来理解这篇文章。

下面是模型的代码(所有需要的导入都在顶部):

您可以从代码中复制并粘贴,但是我已经在文章末尾链接了整个源代码,所以真的没有必要。

这里重要的事情是不要被代码中的等式分散注意力,而是要关注控制流:

你不必关心函数 deriv ,它只是在模型内部用来计算一些东西。同样的道理也适用于逻辑 _R_0 。仪表板(和您)唯一重要的功能是型号。这个函数接收用户输入并返回图形数据,所以这就是回调函数的工作。

布局

让我们定义用户在屏幕上的什么地方可以看到什么。我们将使用一个外部样式表,这基本上意味着屏幕上的所有元素看起来都很好,而不需要我们摆弄 CSS。

首先,我们需要控件的布局(这是用户可以按下和调整的所有东西),我们分开做是为了保持代码的整洁:

我们现在已经定义了这里用红色圈出的所有内容:

通过阅读注释并将代码与输出图片进行比较,应该很容易看出不同组件做了什么。

现在让我们定义实际的布局,也就是说,我们告诉 Dash 我们刚刚定义的这些控件应该在哪里,图表应该在哪里,以及我们希望在页面顶部有什么样的标题和描述:

很好,我们现在有了数据、控件和告诉我们在哪里显示控件和数据的布局。现在我们唯一缺少的就是回调。

复试

我们不想对用户应用到输入中的每一个微小变化都做出反应,因为那会使我们的程序很快崩溃。相反,您可能已经注意到控件下面有一个“应用”按钮,所以我们只在按钮被按下时才改变输出(即触发回调)。这就是为什么与开始的例子不同,我们的回调看起来像这样:

首先,我们列出了输出——这只是我们的图表。然后,我们只有一个输入,即蓝色的“应用”按钮,因此我们的回调仅由它触发。然后我们有几个状态,,这些都是用户可以更改的参数。总而言之,这意味着我们接下来要定义的函数(在回调时被触发)

  1. 当用户按下“应用”按钮时,
  2. 将用户可以调整的各种参数的当前状态作为输入(按照上面定义的顺序,即初始日期、人口……),以及
  3. 应该返回四个数字:一个是主图,一个是死亡率图,一个是 R 值,一个是一段时间内的死亡数,按此顺序。

让我们定义这样一个函数:

这非常简单:将回调中定义的状态作为输入,使用上面的模型函数来计算模拟,并将回调中定义的所有图形作为输出返回。

只有两行代码不见了:

if __name__ == '__main__':
    app.run_server(*debug*=True)

仅此而已。像以前一样通过终端启动应用程序,您应该会看到仪表盘!

你可以在这个库的 app.py 文件中找到代码。如果你也想在网络上发布你的应用,我强烈推荐这个教程。如果你现在想理解和扩展这里使用的模型,请阅读这个

编者注: 迈向数据科学 是一份以数据科学和机器学习研究为主的中型刊物。我们不是健康专家或流行病学家,本文的观点不应被解释为专业建议。想了解更多关于疫情冠状病毒的信息,可以点击 这里

决策树算法的直观解释

原文:https://towardsdatascience.com/building-an-intuition-for-the-decision-tree-algorithm-75e0786e86d?source=collection_archive---------32-----------------------

贝瑟尼·扬奇克在 Unsplash 上的照片

它是如何在引擎盖下工作的

决策树是现代机器学习中最重要的概念之一。它们不仅是解决分类和回归问题的有效方法,也是随机森林和梯度推进等更复杂算法的基础。

从概念上讲,决策树非常简单。我们将数据集分成越来越小的组,试图使每个组尽可能“纯净”或“同质”。一旦我们完成分割,我们使用最终的组对看不见的数据进行预测。

这里有一个非常简单的例子,我们根据以前的销售情况和年龄来预测即将购买的客户:

作者图片

请注意,这相当于一系列 if/then 语句:

如果上一年的销售额<为 100 美元,那么归类为“否”

如果上一年销售额≥100 美元年龄< 50,归类为“否”

如果上一年销售额≥100 美元年龄≥50,归类为“是”

很简单。

棘手的部分是 如何到达 那里。什么是好的拆分?我们如何决定在什么上拆分?我们什么时候停止分裂?

在这篇文章中,我将详细介绍这三个问题的答案,让你的决策树直觉变得真实。

什么是好的拆分?

成功的决策树能够很好地将数据“分割”成同类的组。因此,为了构建一个好的决策树算法,我们需要一个评估分裂的方法。

评估分割质量的最流行方法之一被称为“基尼系数”。

作者图片

解释基尼系数不纯很简单。值为 0 表示组是完全同质的。换句话说,组中的所有记录都属于同一个类。另一方面,值 0.5 意味着组在类之间完全分离。

当我们评估潜在的分裂时,我们希望选择基尼系数最低的一个。最接近完美“纯洁”的那个)。

计算基尼系数有点复杂,但它有助于巩固决策树分裂背后的直觉。

下面是根据一组数据计算基尼系数的公式。注意,值“I”表示我们的数据集中的类(即。二进制分类中的 1 或 0)。

作者图片

下面用简单的英语告诉你它是如何工作的。首先,我们计算落入每个类别(p)的记录的百分比。然后我们将这些数字平方并相加。最后,我们从 1 中减去这个数。

因为决策树将数据分成多个组,所以我们的最后一步是计算每个组的基尼系数的加权平均值。

请注意“组”(在决策树术语中也称为“节点”)和“类”之间的区别。组或节点是我们在执行剥离时创建的。类是我们的目标变量——我们试图预测的东西。

让我们用一个玩具问题来看一个例子,在这个例子中,我们试图预测即将购买的客户。下面是我们的数据集:

作者图片

首先,我们需要选择要拆分的列和值。让我们使用前一年的销售额≥ $100。

接下来,我们将我们的数据集分成两组——一组是之前销售额≥100 美元的数据集,另一组是之前销售额为

Third, we apply the Gini Impurity formula to each node.

Image by author

Finally, we take the weighted average of the two numbers. Because each node has the same number of records (3) this amounts to a simple average:

Image by author

Note that our first node (where prior sales < $100) has a Gini Impurity of 0 because all of the records fall into the same class. Our second node, on the other hand, has a Gini Impurity of .44 because class membership is divided.

Now that we know how to evaluate the quality of each split, we can begin to understand how the decision tree algorithm is built.

How do we decide what to split on?

In our above example, we evaluated the Gini Impurity on one potential split. But how do we know this was the right choice? And how do we know what to split on next?

The simple answer is that we can’t know unless we try all options. So that’s exactly what we do.

At any given node, we loop through all of the unique values in all of our columns and choose the one with the lowest Gini Index. By definition, this is what makes the decision tree a 贪婪算法的数据集——在这一组中,我们在每一步都选择最佳选择。

为了说明这一点,请注意,在我们的示例数据集中,决策树将评估 10 个潜在的拆分:

作者图片

一旦我们在这 10 个选项中确定了最低的 Gini 杂质,我们就进行分割,然后在每个结果节点上运行相同的过程。

我们什么时候停止分裂?

我们快到了。最后一步是弄清楚什么时候停止分裂。

但是首先,我们为什么要停下来?要回答这个问题,请想一想,如果我们对拆分没有任何限制,并且我们有一个复杂的数据集,会发生什么情况…

我们的算法有可能找到许多奇怪的特征组合,以达到每个节点的同质性。当然,这将是教科书式的过度拟合。换句话说,这些决策规则可能会在看不见的数据上表现糟糕,因为它们太复杂了。

为了解决这个问题(至少部分地),我们设置了何时停止分割的规则。有很多规则我们可以借鉴;下面是两个最常见的:

最大深度:这里我们设定了树的深度限制。深度只是衡量我们离树的起点有多远。例如,如果我们将最大深度设置为 2,我们将在分割两次后停止扩展:

作者图片

最小样本分割:这是为了分割,节点中必须包含的最小记录数。如果我们将最小样本数设置为 50,那么我们将停止拆分任何少于 50 条记录的节点。

结束语

当我们把所有这些放在一起时,决策树是很容易理解的。我们获取一个数据集,并不断分割它。每次我们分裂时,我们会查看所有可能的选项,并选择一个创建最纯粹群体的选项。最后,我们添加一些规则来管理何时以及如何停止拆分。

基于树的模型的奇妙之处在于,它们巧妙地建立在彼此的基础上。现在我们有了决策树背后的直觉,我们可以开始把它们放在一起,以创建更复杂和有效的算法。这正是我将在下一篇文章中深入探讨的。

P.S. →如果你想了解更多关于 SQL、Python、数据科学的知识,订阅我的 免费邮件列表

用 Python 构建投资模型

原文:https://towardsdatascience.com/building-an-investing-model-with-python-44d779adb6bf?source=collection_archive---------35-----------------------

用 Python 构建基于财务比率的模型

在这篇文章中,我们将使用 Python 建立一个投资模型,根据财务比率找出有吸引力的股票。我们将筛选纳斯达克交易所的所有科技股。然后,我们将得到每只股票的主要财务比率。最后,基于一个定制的排名标准,我们将建立一个模型来寻找最有吸引力的股票。

照片由 突发 发自 像素

让我们从构建投资模型的 Python 代码开始:

使用 Python 的投资模型

在我之前的帖子中,我将使用financialmodelingprep API,它在注册后每月提供一些免费的 API 调用。以下是我们根据财务比率构建投资模型时将遵循的主要步骤:

  1. 向财务 API 发出 Get 请求,以提取在纳斯达克交易的所有公司的股票并满足我们的选择标准(即科技行业和大于特定规模)
  2. 遍历第 1 点筛选的每个公司,并提取财务比率
  3. 将每个公司的每个比率添加到一个 Python 字典中,然后创建一个熊猫数据框架
  4. 建立一个投资模型排名标准并根据我们的模型列出最有吸引力的股票

1.提取纳斯达克代码

正如我在以前的一篇文章中所提到的(例如,如何找到高股息股票),我们将提取在纳斯达克交易的公司股票并将其添加到一个 Python 列表中。请注意,您将需要在 financialmodelingprep 中创建一个帐户,以便每天获得一些免费的 API 调用。

这是一个好主意,选择在同一个部门和类似的规模经营的公司。这将确保这些公司之间可以进行比较。因此,我们选择市值超过 100 亿美元的科技公司。

import requests
import pandas as pdcompanies = []
demo = 'your api key'marketcap = str(1000000000)url = (f'[https://financialmodelingprep.com/api/v3/stock-screener?marketCapMoreThan={marketcap}&betaMoreThan=1&volumeMoreThan=10000&sector=Technology&exchange=NASDAQ&dividendMoreThan=0&limit=1000&apikey={demo}'](https://financialmodelingprep.com/api/v3/stock-screener?marketCapMoreThan={marketcap}&betaMoreThan=1&volumeMoreThan=10000&sector=Technology&exchange=NASDAQ&dividendMoreThan=0&limit=1000&apikey={demo}'))#get companies based on criteria defined about
screener = requests.get(url).json()
print(screener)[{'symbol': 'AAPL', 'companyName': 'Apple Inc.', 'marketCap': 1526030790000, 'sector': 'Technology', 'beta': 1.228499, 'pri
ce': 352.08, 'lastAnnualDividend': 3.08, 'volume': 42532806, 'exchange': 'Nasdaq Global Select', 'exchangeShortName': 'NASD
AQ'}, {'symbol': 'MSFT', 'companyName':....

2.纳斯达克公司的财务比率

接下来,我们从上一节获得的纳斯达克公司中提取所有财务比率。对于我们的投资模型,我们将使用以下选定的财务比率。

add selected companies to a list
for item in screener:
 companies.append(item['symbol'])

#print(companies)value_ratios ={}
#get the financial ratios
count = 0
for company in companies:
 try:
  if count <30:
   count = count + 1
   fin_ratios = requests.get(f'[https://financialmodelingprep.com/api/v3/ratios/{company}?apikey={demo}').json()](https://financialmodelingprep.com/api/v3/ratios/{company}?apikey={demo}').json())
   value_ratios[company] = {}
   value_ratios[company]['ROE'] = fin_ratios[0]['returnOnEquity']
   value_ratios[company]['ROA'] = fin_ratios[0]['returnOnAssets']
   value_ratios[company]['Debt_Ratio'] = fin_ratios[0]['debtRatio']
   value_ratios[company]['Interest_Coverage'] = fin_ratios[0]['interestCoverage']
   value_ratios[company]['Payout_Ratio'] = fin_ratios[0]['payoutRatio']
   value_ratios[company]['Dividend_Payout_Ratio'] = fin_ratios[0]['dividendPayoutRatio']
   value_ratios[company]['PB'] = fin_ratios[0]['priceToBookRatio']
   value_ratios[company]['PS'] = fin_ratios[0]['priceToSalesRatio']
   value_ratios[company]['PE'] = fin_ratios[0]['priceEarningsRatio']
   value_ratios[company]['Dividend_Yield'] = fin_ratios[0]['dividendYield']
   value_ratios[company]['Gross_Profit_Margin'] = fin_ratios[0]['grossProfitMargin']
   #more financials on growth:[https://financialmodelingprep.com/api/v3/financial-growth/AAPL?apikey=demo](https://financialmodelingprep.com/api/v3/financial-growth/AAPL?apikey=demo)
   growth_ratios = requests.get(f'[https://financialmodelingprep.com/api/v3/financial-growth/{company}?apikey={demo}').json()](https://financialmodelingprep.com/api/v3/financial-growth/{company}?apikey={demo}').json())
   value_ratios[company]['Revenue_Growth'] = growth_ratios[0]['revenueGrowth']
   value_ratios[company]['NetIncome_Growth'] = growth_ratios[0]['netIncomeGrowth']
   value_ratios[company]['EPS_Growth'] = growth_ratios[0]['epsgrowth']
   value_ratios[company]['RD_Growth'] = growth_ratios[0]['rdexpenseGrowth']

 except:
  pass
print(value_ratios)

请注意,我们已经包含了一个 if 语句,将模型中包含的公司数量限制为 30 家。请随意取消这一限制。

下面是我们获得的回复样本,包含我们所有纳斯达克公司的财务比率列表。我们已经将它们添加到 Python 字典中,以便能够轻松地访问它们。

Value Ratio Dictionary outcome:
{'AAPL': {'ROE': 0.6106445053487756, 'ROA': 0.16323009842961633, 'Debt_Ratio': 0.7326921031797611, 'Interest_Coverage': 18.382829977628635, 'Payout_Ratio': 0.25551976255972203, 'Dividend_Payout_Ratio': 0.25551976255972203, 'PB': 12.709658271815046, 'PS': 4.420393881402446, 'PE': 20.81351450883162, 'Dividend_Yield': 0.012276627402416805, 'Gross_Profit_Margin': 0.3781776810903472, 'Revenue_Growth': -0.020410775805267418, 'NetIncome_Growth': -0.07181132519191681, 'EPS_Growth': -0.003330557868442893, 'RD_Growth': 0.1391542568137117}, 'MSFT': {'ROE': 0.3834652594547054, 'ROA': 0.13693658482111698, 'Debt_Ratio': 0.6428970253632798, 'Interest_Coverage': 5.881980640357408, 'Payout_Ratio':0.35196228338430174, 'Dividend_Payout_Ratio': 0.3519622833843017, 'PB': 10.52384979966774, 'PS': 8.557532401484389, 'PE': 27.444076197757386, 'Dividend_Yield': 0.012824708722134454, 'Gross_Profit_Marg

3.创建包含财务比率的熊猫数据框架

下一步,我们将把 Python 字典转换成熊猫数据帧。这样做的原因很简单,就是从 Pandas 提供的处理我们的数据的所有优秀功能中受益:

DF = pd.DataFrame.from_dict(value_ratios,orient='index')
print(DF.head(4))

具有财务比率的数据框架

4.创建我们的排名投资模型

到目前为止,我们已经成功地将选定的纳斯达克公司的所有财务比率添加到 Pandas 数据框架中。

现在是时候创建我们的投资模型,将股票从更有吸引力到不太有吸引力进行排序。排名标准完全取决于我们。我建议每个财务比率的排名标准如下。请根据您的喜好随意更改它们。

正数表示该比率是一个好兆头。数字越大,该比率在我们的投资模型中的相关性就越强。例如,ROE(股本回报率)将比 ROA(资产回报率)有更大的影响,因为 1.2 大于 1.1。

另一方面,负数会降低股票的吸引力。例如,较高的市净率(PB)可能表明公司被高估。所以我给 PB 比分配了一个负数。

如果你想了解更多关于财务比率的知识,请看看我之前的一些解释了其中大部分的帖子。

#criteria ranking
ROE = 1.2
ROA = 1.1
Debt_Ratio = -1.1
Interest_Coverage = 1.05
Dividend_Payout_Ratio = 1.01
PB = -1.10
PS = -1.05
Revenue_Growth = 1.25
Net_Income_Growth = 1.10

此外,在计算我们的排名之前,我们需要将财务比率标准化,以便进行比较:

#mean to enable comparison across ratios
ratios_mean = []
for item in DF.columns:
 ratios_mean.append(DF[item].mean())#divide each value in dataframe by mean to normalize values
DF = DF / ratios_mean

太好了,我们快到了。最后一步是在我们的 Pandas 数据框架中添加一个新列,包含排名因子:

DF['ranking'] = DF['NetIncome_Growth']*Net_Income_Growth + DF['Revenue_Growth']*Revenue_Growth  + DF['ROE']*ROE + DF['ROA']*ROA + DF['Debt_Ratio'] * Debt_Ratio + DF['Interest_Coverage'] * Interest_Coverage + DF['Dividend_Payout_Ratio'] * Dividend_Payout_Ratio + DF['PB']*PB + DF['PS']*PS

现在,我们可以简单地打印数据框架,包括我们投资模型中每只股票的排名因子:

print(DF.sort_values(by=['ranking'],ascending=False))

使用 Python 的投资模型

从上图中我们可以看到,根据我们选择的标准,我们的投资模型预测 CSCO 将是投资的最佳股票,得分为 48 分。当然,这只是一个模型,在投入资金之前,需要对股票进行进一步的分析。

包扎

我们在这篇文章中已经谈了很多。我们创建了一个基于关键财务比率和定制排名因子的投资模型。

这种投资模型特别有助于初步确定一些潜在的好投资,以便我们对它们进行重点分析。例如,我们可以更深入地分析一些排名最佳的公司的财务比率。我们可以应用杜邦方法来分析净资产收益率。或者用戈登增长模型对公司进行估值。

我想强调的使用财务比率的一个限制是,财务比率大多是使用最新可用的公开数据计算的。因此,如果一家公司的财务数据是 3 个月前发布的,那么使用资产负债表或损益表数据的财务比率可能已经过时。尤其是如果发生了影响公司运营的重大事件。

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

原载于 2020 年 7 月 5 日 https://codingandfun.com

挖掘新冠肺炎·卡格尔竞赛科学论文以建立对病毒的理解

原文:https://towardsdatascience.com/building-an-understanding-of-viruses-by-mining-covid-19-scientific-corpus-bdd7427d6a8a?source=collection_archive---------61-----------------------

图片鸣谢:https://upload . wikimedia . org/Wikipedia/commons/thumb/8/82/新型冠状病毒 _ 无 _ 背景. png/1200 px-新型冠状病毒 _ 无 _ 背景. png

在新冠肺炎疫情,艾伦人工智能研究所(Allen Institute for AI)与白宫和其他各方合作,发表了一场关于 Kaggle 的比赛,他们为这场比赛准备了超过 16.7 万篇关于冠状病毒的科学论文(其中 79 万篇提供了全文)。伴随着数据,17 个任务被发布;组织者要求参赛者探索科学语料库,并回答关于各种冠状病毒(新冠肺炎、SARS、火星等)潜伏期的问题(以及其他许多问题)。)、传播的季节性、病毒的物理性质,如粘附在表面或病毒脱落、环境的作用等等(完整列表可在此处获得:https://www . ka ggle . com/Allen-institute-for-ai/CORD-19-research-challenge/tasks)。

方法

当我第一次开始考虑回答这些问题时,我认为某种问答引擎可能会很有趣:一个在语料库上训练的 ML 模型,当被问及以下问题时,它能够找到论文(和/或相关段落)的模式和聚类:使人对感染冠状病毒免疫的抗体水平是多少?病毒在空气中存活多久?各种冠状病毒的死亡率是多少?…

显然,当一个人想要分析一个文本语料库时,他需要一个 NLP(自然语言处理)工具。一种常见的方法是通过使用一些单词嵌入技术(word2vec 非常流行)来降低维度并提供语料库的密集单词表示,然后使用 LDA 进行主题挖掘(本质上是构建分段模型)。这里可以找到一种将这两种方法联系在一起的有趣方法:https://multithreaded . stitchfix . com/blog/2016/05/27/LDA 2 vec/# topic = 26&lambda = 0.86&term =

在这个系列中,我们将采取一种略有不同的方法:为了减少维度并提供更好的单词表示,我们将训练来自谷歌的 BERT(来自变压器的双向编码器表示)模型。一旦训练完成,我们将从模型中提取嵌入层来替代 word2vec。

由于我们正在使用深度学习模型,为了保持在同一领域内并相对容易地重用来自 BERT 的嵌入层,我们将在 PyTorch 中开发一个 SOM(自组织地图,也称为 Kohonen 网络),它将处理我们任务的无人监管部分——论文分割。

硬件

BERT base 有 1.1 亿个参数,在每一批提交给网络后都会进行调整,由于训练神经网络需要大量的矩阵计算,GPU 是一个显而易见的选择。虽然我通常使用 NVIDIA 的 Titan RTX (我非常喜欢这款硬件…),但我最近收到了一台联想的 ThinkPad P53 移动工作站机器进行测试,我对这台机器在我离开 Titan 时所做的事情感到非常惊讶…我不会提供这台机器的详细规格,只是说它可以配置 NVIDIA Quadro RTX 5000 w/16GB RAM,这对我们的目的来说应该足够了(更多详细规格,我将参考我最近的)

在软件栈方面,我们将使用 PyTorch、NVIDIA RAPIDS 和 Python 宇宙中的其他工具来帮助我们完成任务。

该系列

由于我们将从零开始训练 BERT,然后在 PyTorch 中从零开始构建 SOM,我认为将整个大故事分成一系列小故事比编写一个单一的整体文本更有意义。因此,下面是我如何看待这个系列的大纲(可能会有变化,因为有时我会忘乎所以,我可能需要进一步打破东西);我们将遵循构建任何机器学习模型的黄金流程:

  1. 在下一个故事中,我们将首先熟悉数据。我们还将看看需要多少清理工作;我在处理数据时的第一条规则是与“无罪推定”的方法截然相反的:在我检查数据之前,我从不相信它,尤其是如果有人告诉我它已经是干净的了;有罪直到被证明是无辜的!
  2. 一旦我们熟悉了数据(而且是干净的!)然后,我们将转到构建字典并准备用于训练的数据集:标记文本并准备标签;在这种方法中,我们将预测句子中被屏蔽的单词。
  3. 准备好训练(和测试)数据集后,在下一个故事中,我们将介绍如何训练和测试 BERT。
  4. 接下来是从训练好的模型中提取嵌入层,然后SOM 附加到嵌入层上,以构建我们的分割模型。
  5. 最后,我们将准备用于推理的模型,其中它应该用语料库中包含与所提问题相关的信息的段落列表进行回复。

敬请期待!

通过挖掘新冠肺炎科学语料库建立对病毒的理解

原文:https://towardsdatascience.com/building-an-understanding-of-viruses-by-mining-covid-19-scientific-corpus-f1d27c33b48?source=collection_archive---------42-----------------------

第 1 部分:熟悉数据

在我发表这篇文章的时候,我们正进入新冠肺炎冻结世界以来的第九个月。自 1 月初以来,我们都有不同的经历:我们中的一些人很幸运,被锁在自己的房子里,能够远程工作,过着相对不变的生活,我们中的一些人没有这样的奢侈,一些人不幸去世。

来源:https://unsplash.com/photos/EAgGqOiDDMg/download?force=true

冠状病毒已经存在了几十年,但迄今为止很少有像新冠肺炎病毒这样致命和容易传播的。今年早些时候,AI2 艾伦人工智能研究所(Allen Institute for AI)和一个由研究机构组成的联盟与白宫一起策划了一个自 19 世纪以来发表的关于冠状病毒的科学论文语料库,并提供了一个 Kaggle 竞赛来分析它并回答一些关于病毒不同方面的问题,如它如何传播,或它如何影响活的有机体。

虽然我没有直接参加挑战本身,这个系列试图通过建立深度学习 BERT-EM-SOM 模型(与自组织地图相关的 BERT 嵌入层)来更好地了解冠状病毒。正如在宣布这个系列的的帖子中提到的,在这一期中,我们将专注于理解论文的语料库,并将清理元数据,以便我们可以在一个干净的基础上工作。

数据

这些数据可以从几乎每天都在更新的 AI2 S3 数据库中下载。下面的代码片段下载数据并将其提取到数据目录中。

在本例中,我们使用 2020 年 8 月 24 日发布的数据集。阅读这一天的元数据显示,应该有 233,539 篇论文讨论冠状病毒相关的主题。我们将测试数据集是干净的这一假设,即所有 233,539 个数据集都在作为归档一部分的 JSON 文件中找到,我们没有任何重复项,也没有丢失信息。

但首先,让我们熟悉一下数据集的基础知识。

基础数据探索

在这个故事的剩余部分,我们将在使用 NVIDIA RAPIDS 的原生 cuDF 和使用 T2 BlazingSQL 的 SQL 之间进行常规切换。大部分时间我都在用泰坦 RTX 卡。然而,我很幸运仍然拥有租借的联想 P53 移动工作站和 NVIDIA Quadro RTX 5000 GPU ,所以当我最近和我的孩子露营时(适当地保持社交距离并戴上面具!)我仍然能够创作这个故事。这是另一款出色的硬件,我强烈推荐给所有想要在旅行或向客户或朋友展示工作时使用 RAPIDS、BlazingSQL 或 CUDA 的 GPU 爱好者。看看这个美女!

来源:联想,经许可使用

作为一个完整的披露——这篇文章不是由英伟达或联想赞助的,我也没有从他们那里得到报酬——我只是在测试他们借给我进行这项研究的设备。为此我非常感激!

这个故事和我其他故事中的所有代码都可以在 github 上找到。

metadata.csv 文件中有 19 列;下面是一个例子。

大多数列都是 id,比如 cord_uiddoi 等等。然而,对我们来说更有趣的是,我们将关注

  • 标题摘要作者我们将使用来查看是否有重复
  • 显示文章发表时间的
  • source_x 显示文章的来源
  • 期刊栏列出了发表文章的期刊
  • pdf_json_files 显示带有论文正文的文件的位置;我们将使用它来查看我们是否有任何丢失的文件,以便我们可以排除它们。

首先,让我们看看 AI2 研究人员收集这些文章的最常见来源是什么。

上面的代码片段简单地获取元数据 cuDF 数据帧,按 source_x 列分组,并打印出一个表。

因此,最常见的来源是世卫组织,其次是 Medline 和 PMC。接下来,我们将看到通过 Elsevier、MedRxiv、ArXiv 和 BioRxiv 发布的文章。这些将占语料库中所有论文的大部分。

顺便说一下,可以使用 BlazingSQL 用 SQL 创建相同的表。

这显示了 RAPIDS 生态系统的美妙之处:如果你熟悉 pandas 或 SQL,你就拥有了在数据上释放 GPU 巨大能力所需的所有工具,甚至不用多想(因为你没有时间…看这个:https://www . LinkedIn . com/posts/tome KD _ blazingsql-notebooks-private-preview-activity-6710308422451126272-wXCb!).

现在让我们来看看这些论文是在哪里发表的。

因此,许多文章都是在 bioRxiv 上预先发表的,但我们也在《BMJ》、《公共科学图书馆综合版》、《病毒学杂志》、《柳叶刀》和《自然》上看到了很多。换句话说,不出所料。

好吧,时间呢?让我们看看论文是什么时候发表的。

上面的代码片段生成了下面的图表。

来源:作者

有趣但并不令人意外的是,在 2002/2003 年左右发表的论文数量有所上升,因为这很可能与 SARS 疫情有关。

你可能想知道为什么我把 2020 年排除在外……好吧,我最初把它放在图表上,但它实际上是让其余时间序列的相形见绌。我认为下面的图表应该解释为什么。

来源:作者

自 1816 年至 2019 年(含),作为该语料库一部分发表和报告的论文总数约为 9 万篇。在 2020 年的近 9 个月里,全球研究人员发表了超过 14 万篇关于冠状病毒的论文和其他信息。因此,我决定,排除 2020 年的报告论文数将使上面的时间序列图更清晰。

清除

现在是我们查看数据本身的时候了。我喜欢对任何数据集首先做的是检查缺失的观察值。

遗漏的观察

每个数据集都有一些。这些可能是简单的遗漏或遥测问题(我们可以估算或删除它们),也可能是我所说的有效缺失值,即根本不应该存在的值。有效缺失观察的一个例子是,在这个特定的数据集中,以及当论文没有在 arxiv 上发表时的 arxiv_id

使用 SQL 创建一个包含丢失值百分比的表非常容易。我是这样做的。

代替解释这里发生了什么:我使用了在上面要点的第 3–7 行中创建的两个 SQL 查询。如果您将这些查询打印出来,它们将如下所示。

最终的 SQL 查询只是返回列的列表及其相应的缺失值百分比。

正如所料,许多 ID 列有许多遗漏的观察值,但是我们不会在后面的分析中用到这些,所以不要担心。然而,缺少 pdf_json_files 列几乎有 60%的值丢失……这绝对是一个问题,因为我们需要能够找到论文的主体。因此,我们应该删除这些遗漏的观察。

在上述操作之后,我们在语料库中得到大约 94,000 篇论文。重新运行前面的查询会产生以下每列缺失值百分比的细分。

我们仍然错过了一些标题、作者或摘要,但这没关系——这不应该妨碍我们的方法。更重要的是,我们可以找到这个文件,然后用 cord_uid 链接它,返回相关的文件。

复制

接下来,我们将把目光转向寻找重复,这是任何数据集的另一个常见问题(我还没有找到一个没有重复的原始数据集)。查询元数据表,我在每一列中得到如下数量的重复记录。

因此,在 pdf_json_files 栏中,我们有超过 1200 个重复的标题、282 个重复的摘要和 6 个重复的链接。让我们从那些开始。

查看上表,我们可以清楚地看出前 3 篇论文不是重复的——它们只是错误地指向同一个 json 文件。然而,剩下的文件肯定是重复的。因为本专栏总共只有 6 个重复项,所以我们将使用。来自 cuDF 的 dropna()功能。

接下来,让我们看看重复的标题。允许我们创建重复行的快速列表的查询如下所示,以及标题重复的前 10 条记录。

你可以看到,这些是真正的重复记录:不知何故,它们与 doi 标识符不同,但标题、摘要和作者大多匹配。但是,由于有超过 1200 个重复的记录,我们不想删除所有这些记录,将使用。drop_duplicated()方法从每个副本中保留一条记录。

数据文件夹中缺少 JSON 文件

最终检查:让我们看看是否真的可以在 data 文件夹中找到 metadata.csv 文件中列出的所有文件。为了尽可能提高效率,我们将采用的方法是列出data/2020–08–24/document _ parses/pdf _ JSON目录中的所有文件,从这些文件中创建一个 cuDF/BlazingSQL 表,然后将它们与我们已有的重复数据删除数据集连接起来。

因此,实际上非常令人惊讶的是,我们在 metadata.csv 文件中有链接到的 12.5k 文件,但在磁盘上找不到,还有存在于磁盘上但在 metadata.csv 文件中无法引用的 5k 文件。在这种情况下,我决定丢弃所有丢失的文件,从而只保留我在 metadata.csv 文件和磁盘上都能找到的 87,438 个文件。

有罪直到被证明无罪

这部分到此结束。正如我在本系列的介绍中提到的,我总是以怀疑的态度对待我得到的每个数据集,直到我向自己证明它足够干净,可以用于进行分析或构建 ML 或 DL 模型。

在下一期文章中,我们将着眼于从磁盘中读取文件,并使用来自 RAPIDS 的 subword_tokenizer 来标记文本,为第 3 部分中的 BERT 训练做准备。

此外,如果您想尝试本系列中介绍的任何代码,请访问 https://app.blazingsql.com/的并发布一个免费集群!

用 Kaggle 构建分析管道

原文:https://towardsdatascience.com/building-analysis-pipelines-with-kaggle-c745671d273e?source=collection_archive---------42-----------------------

不仅仅是竞争

卢克·切瑟在 Unsplash 上的照片

Kaggle 是最受欢迎的数据科学和机器学习入门网站之一。数据科学界的大多数人都使用过或者至少听说过它。Kaggle 是一个众所周知的举办机器学习竞赛的网站,虽然这是该平台的一大部分,但它可以做得更多。

今年有了新冠肺炎开放研究数据集(CORD-19) ,我有机会更加一致地使用这个平台。老实说,Jupyter 笔记本和基于 GUI 的开发并不是我的首选方法(Vim 对我来说已经足够好了)。但是在过去的几个月里,我对这个平台的能力印象深刻。本文概述了 Kaggle 笔记本、Kaggle API,并演示了一种构建自动化分析管道的方法。

笔记本电脑

Kaggle 笔记本

Kaggle Notebooks 是一个云托管的 Jupyter 笔记本环境。笔记本可以用 Python 或 r 构建。笔记本在 Docker 容器中执行,我们可以把它们看作一个逻辑包。笔记本可以包含数据分析项目的所有逻辑,也可以链接在一起构建模块化组件。笔记本可以公开共享,也可以保密。

笔记本电脑可以访问多个 CPU 内核和大量内存。另外还可以增加GPUTPU,可以加速深度学习模型的训练。对于一项免费服务来说,可用的资源是非常可观的。在一家大型云提供商上安装一台类似的主机是一笔相当大的成本。

笔记本使用几种不同的方法读取数据。主要途径是通过数据集。任何拥有帐户的人都可以上传数据并创建自己的数据集。Kaggle 上已经有大量公开的数据集。与笔记本电脑一样,数据集可以公开共享,也可以保密。笔记本可以有一个或多个数据集作为输入。此外,其他笔记本的输出可以用作输入,从而允许构建笔记本链。

创建新笔记本

Kaggle 笔记本副本

可以使用上图中显示的“新建笔记本”按钮创建空白笔记本。一旦创建了一个笔记本,就会有一个编辑器可以用来构建逻辑。本例将复制一个现有的笔记本,重点介绍运行笔记本的方法。

我们将使用 CORD-19 报表生成器笔记本。点击参考链接后,我们可以通过“复制和编辑”按钮复制笔记本。这将创建一个笔记本作为 <你的 Kaggle 用户名>/cord-19-report-builder。请注意,以下所有链接将显示大卫·梅泽蒂作为用户名,请替换为您的 Kaggle 用户名。

执行笔记本

Kaggle 笔记本编辑

复制笔记本会把我们带到编辑笔记本的界面。在该屏幕上,我们还可以添加/删除输入、修改设置以及保存/运行笔记本。

上面的示例显示了从另一个笔记本导入实用程序脚本。这是一个强大的功能,允许在笔记本之间共享功能,避免复制/粘贴样板代码。该示例有两个输入数据集和一个笔记本作为输入。

点击“保存版本”按钮将执行笔记本中的逻辑并保存一个新版本。有多种选择:

Kaggle 笔记本保存

如果在编辑时笔记本已经完全运行,“快速保存”工作良好。否则,应使用“保存并运行全部”。

Kaggle API

如果我们有几个笔记本,我们希望在添加额外的逻辑时偶尔更新,那么已经描述的内容通常就足够了。

对于更复杂的情况或频繁使用的情况,Kaggle 有一个全功能 Python API 可用。Kaggle API 可以通过 PyPi 获得:

pip install kaggle

API 有关于如何设置授权和创建访问密钥以启用使用的文档。一旦设置了 API,下面显示了一个通过 API 运行笔记本的简单示例:

kaggle kernels pull *davidmezzetti*/cord-19-report-builder -p cord-19-report-builder -mkaggle kernels push -p cord-19-report-builder

这运行两个命令。第一个是提取 cord-19-report-builder 笔记本,将它和它的元数据存储在一个名为 cord-19-report-builder 的目录中。第二个运行笔记本。

一旦运行了上面的命令,我们就可以到 Kaggle 来监控作业的进度。下图显示了版本屏幕,从中我们可以看到笔记本电脑的新版本正在运行。点击版本文本(在右上角突出显示)即可调出该屏幕。

Kaggle 笔记本新版本

自动化管道

Kaggle API 功能强大,允许在主 web 界面之外运行笔记本(以及许多其他功能)。可以围绕它构建脚本,以支持更复杂的功能以及与外部流程的交互。

有了 CORD-19 数据集,支持这项工作的笔记本电脑数量增加到了 10 多台,每次有新数据进来时都需要刷新。最重要的是,数据集移动到了每天更新的程度,笔记本电脑相互依赖(即一个需要在另一个运行之前运行)。

对于这个用例,完全自动化显然是必要的。为了实现自动化管道,NeuML 创建了 kernelpipes 项目

内核管道可以通过 pip 安装:

pip install git+https://github.com/neuml/kernelpipes

kernelpipes 使用 Kaggle API 顺序或并行执行一系列笔记本。可以添加检查,以便仅在更新了源时才允许运行管道。此外,管道有一个内置的 cron 调度特性来支持连续执行。下面是 YAML 的一个简单管道例子。

# Pipeline name                       
name: pipeline# Pipeline execution steps
steps:
  - kernel: *davidmezzetti*/cord-19-report-builder                       
  - status: 2.5m

假设上述内容保存在名为 pipeline.yml 的文件中,它可以如下运行:

pipeline.yml 执行

这个简单的管道每 2.5 分钟执行一次记事本并检查完成状态。一旦内核完成,进程将退出。

基本管道配置

名字

name: <pipeline name>

必填字段,命名管道

日程安排

schedule: cron string

允许通过调度程序运行作业的可选字段。根据偏好,可以使用 System cron 来代替它。与系统 cron 相比,内部调度程序的一个优点是,在前一个作业正在运行时,不会产生新的作业。例如,如果某个作业计划每小时运行一次,而一次运行需要 1.5 小时,它将跳过第二次运行,并在第三个小时重新开始。

步伐

支票

check: /kaggle/dataset/path

允许根据数据集更新状态有条件地运行管道。检索数据集元数据,并将最新版本与上次运行的版本进行比较,仅当数据集已更新时才允许处理继续进行。如果数据集没有本地元数据,将继续运行。

核心

kernel: /kaggle/kernel/path

返回在/kaggle/kernel/path 指定的内核

状态

status: <seconds|s|m|h>

在指定的持续时间内检查前面内核步骤的状态。

持续时间示例:10 代表 10 秒,30 代表 30 秒,1 代表 1 分钟,1 代表 1 小时。

更复杂的例子

为了给出一个复杂用例的概念,下面是一个用于处理 CORD-19 数据集的完整管道。

# Pipeline name
name: CORD-19 Pipeline# Schedule job to run @ 12am, 10am, 3pm local time
schedule: "0 0,10,15 * * *"# Pipeline execution steps
steps:
  - check: allen-institute-for-ai/CORD-19-research-challenge
  - kernel: davidmezzetti/cord-19-article-entry-dates
  - status: 1m
  - kernel: davidmezzetti/cord-19-analysis-with-sentence-embeddings
  - status: 15m
  - kernel: davidmezzetti/cord-19-population
  - kernel: davidmezzetti/cord-19-relevant-factors
  - kernel: davidmezzetti/cord-19-patient-descriptions
  - kernel: davidmezzetti/cord-19-models-and-open-questions
  - kernel: davidmezzetti/cord-19-materials
  - kernel: davidmezzetti/cord-19-diagnostics
  - kernel: davidmezzetti/cord-19-therapeutics
  - kernel: davidmezzetti/cord-19-risk-factors
  - status: 2.5m
  - kernel: davidmezzetti/cord-19-task-csv-exports
  - kernel: davidmezzetti/cord-19-study-metadata-export
  - kernel: davidmezzetti/cord-19-most-influential-papers
  - kernel: davidmezzetti/cord-19-report-builder
  - kernel: davidmezzetti/cord-19-forecasting-articles
  - kernel: davidmezzetti/cord-19-mice-trials
  - kernel: davidmezzetti/cord-19-bcg-vaccine
  - kernel: davidmezzetti/cord-19-digital-contact-tracing-privacy
  - status: 2.5m

上面的例子一天运行 3 次。在执行之前,它将数据集的版本与前一次运行的版本进行比较,如果没有变化,则该过程退出。否则,笔记本会一直启动到状态步骤。此时,kernelpipes 将等待所有笔记本完成后再继续。

在上面的配置中,cord-19-article-entry-dates 开始,kernelpipes 将每分钟检查一次,直到完成,然后启动 cord-19-analysis 笔记本,每 15 分钟检查一次完成情况。一旦完成,下一个系列的笔记本会并行启动,kernelpipes 会等待所有操作完成,以此类推。

每次 CORD-19 数据集更新时,该管道都会刷新,无需任何用户操作。它有效地使一系列“活”笔记本随着新的新冠肺炎文章的增加而不断更新。

结论

Kaggle 平台在许多不同的领域带来了很多东西,本文只是触及了表面(微型课程看起来也很棒)。总的来说,我对这套功能印象非常深刻,并且能够设计一个复杂的全自动数据分析管道。将来用 Kaggle 构建时,请记住这些特性!

使用 Flask、SQLAlchemy 和 Heroku 构建和部署登录系统后端

原文:https://towardsdatascience.com/building-and-deploying-a-login-system-backend-using-flask-sqlalchemy-and-heroku-8b2aa6cc9ec3?source=collection_archive---------16-----------------------

有了 Heroku Postgres 数据库

作者图片

现在几乎所有的网站都有登录系统,如果你是新用户,你可以注册,如果你是现有用户,你可以登录。如果您试图为您的应用程序从头开始构建这个系统,可能会有许多移动的部分。本文给出了为登录系统创建和部署 REST API 的逐步概述,但一般来说,它可以扩展到任何 REST API。

与本文相关的所有代码都可以在下面的 repo 中找到:

[## aakanksha-ns/flask-heroku-log in

用 Flask 编写的 API 使用 Heroku Postgres 数据库注册用户并登录帐户。部署在 Heroku …

github.com](https://github.com/aakanksha-ns/flask-heroku-login)

1)创建一个 Heroku 账户并创建一个新的应用程序

你需要做的第一件事是在 Heroku 上创建一个账户,如果你还没有的话。

登录您的帐户,选择Create new app选项创建一个新的 Heroku 应用程序。

仪表板->新建->创建新应用程序(按作者分类的图片)

2)将 Heroku Postgres 添加到应用程序

现在您需要将 Postgres 添加到应用程序中。导航到您刚刚创建的应用程序页面,选择Configure Add-ons选项,搜索Postgres并提供它。

仪表板->“您的应用程序”->配置附加组件(图片由作者提供)

3)安装 Heroku CLI、PostgreSQL 和 PopSQL

Heroku CLI

为了能够从命令行与 Heroku 交互,您必须使用以下命令安装 Heroku CLI(假设您已经安装了 Homebrew ):

$ brew tap heroku/brew && brew install heroku

PostgreSQL 和 PopSQL

为什么我们需要它们:你可以从你的 Flask 应用程序中创建你的数据库表。然而,在我的实现中,我使用 PostgreSQL 在本地创建了数据库和表,并将其推送到 Heroku,因为我发现与 SQLAlachemy 相比,使用普通的 SQL 编辑器(我使用了 PopSQL)创建表更简单。我只使用 Flask 和 SQLAlchemy 向数据库添加元素或从中检索元素。

安装 Postgres : brew install postgres

启动 PostgreSQL 服务器:

创建数据库和用户: Postgres 设置了一组默认用户,但他们是超级用户帐户——他们可以做任何事情,包括删除数据库。最好创建只能访问特定数据库的新用户:

$ psql postgresCREATE ROLE aakanksha WITH LOGIN PASSWORD ‘blah’;
CREATE DATABASE bookstore;
GRANT ALL PRIVILEGES ON DATABASE bookstore TO aakanksha;

安装 PopSQL: PopSQL 是一款非常流行且易于使用的 SQL 编辑器。从https://popsql.com/下载应用程序并安装。

将 PopSQL 连接到数据库:您必须提供类似port number (postgres 默认使用 5432)、database name等细节。为了让 PopSQL 能够访问您的 PostgreSQL 数据库。您只需输入以下详细信息:

(图片由作者提供)

4)创建表并将数据库推送到 Heroku

在 PopSQL 草稿栏上创建表:

只有在创建时出现错误时,才执行“删除表格”命令

推到 Heroku:

$ heroku pg:push bookstore DATABASE --app example-app-medium

5)安装所需的库

既然数据库已经设置好了,我们可以继续 API 部分了。确保安装了所有必需的库。您可以将我使用过的以下库列表存储到一个文本文件(requirements.txt)中,并执行它后面的命令:

certifi==2020.4.5.1
click==7.1.1
Flask==1.1.2
flask-heroku==0.1.9
Flask-SQLAlchemy==2.4.1
itsdangerous==1.1.0
Jinja2==2.11.1
MarkupSafe==1.1.1
psycopg2==2.8.5
SQLAlchemy==1.3.16
Werkzeug==1.0.1
gunicorn==19.5.0
flask_cors

pip install -r requirements.txt

6)设置环境变量以访问 Heroku 数据库

因为您必须通过 flask 应用程序访问 Heroku 上的远程数据库,所以要建立到它的连接,您需要数据库 URL。因为远程数据库的 URL 可能由于许多原因而改变,所以最好的做法是在应用程序启动时总是从相应的 Heroku 应用程序获取数据库 URL 配置变量。您可以通过设置环境变量来实现这一点。

因为这个 URL 是特定于一个特定项目的,而这个项目被限制在一个特定的目录中,所以把它添加到全局环境变量中是没有意义的。这就是 direnv 的用武之地。这是一个环境切换器,允许根据当前工作目录加载/卸载环境变量。

要为 Flask 应用程序设置变量,您必须将以下命令添加到名为.envrc的新文件中,并将其放在项目的根目录下:

export DATABASE_URL=$(heroku config:get DATABASE_URL -a example-app-medium)

创建.envrc文件后,运行$ direnv allow激活变量。

5)设置烧瓶应用程序配置

下面是您在根目录下的config.py文件中需要的一些基本配置:

import osclass Config(object):SQLALCHEMY_TRACK_MODIFICATIONS = True
SECRET_KEY = os.urandom(24)
SQLALCHEMY_DATABASE_URI = os.environ['DATABASE_URL']

6)设置 application.py 和 app 目录

以下是我的文件夹结构:

(图片由作者提供)

默认情况下,Flask 将一个应用命名为“应用”,所以你需要一个application.py文件来运行这个应用。如果您愿意,您可以更改应用程序的名称(使用$ export FLASK_APP = ‘your file name'.py)

我喜欢把所有与 Flask 相关的代码放在一个叫做app的子目录中,而不是放在根目录下。

我的[application.py](https://github.com/aakanksha-ns/flask-heroku-login/blob/master/application.py)长得像:

from app import application

在 app 子目录中,有我的[__init__.py](https://github.com/aakanksha-ns/flask-heroku-login/blob/master/app/__init__.py)(将磁盘上的目录标记为 Python 包目录的初始化脚本):

from flask import Flask
from config import Config
from flask_sqlalchemy import SQLAlchemy
from sqlalchemy import create_engine# Initialization
application = Flask(__name__)
application.config.from_object(Config)DB_URI = application.config['SQLALCHEMY_DATABASE_URI']
engine = create_engine(DB_URI)from app import routes

API 代码位于routes.py文件中。

7)创建 API 并在本地测试

要查看完整的routes.py代码,请点击以下链接:

https://github . com/aakanksha-ns/flask-heroku-log in/blob/master/app/routes . py

对于登录系统,您需要两种方法:

注册

该方法在account表中为新用户创建一个条目。您可以进行额外的检查,以确保usernameemail不在表格中。

因为出于安全考虑,将原始密码存储在数据库中是不安全的,所以您可以使用[werkzeug](https://techmonger.github.io/4/secure-passwords-werkzeug/)库生成一个密码散列,并存储该散列而不是真正的密码。

签到

这个方法只是检查输入的密码是否与给定用户名的原始密码匹配

完成后,您可以使用$ flask run启动 flask 服务器,并使用 Postman 测试您的 API:

(图片由作者提供)

8)创建过程文件

Procfile 是一种机制,用于声明 Heroku 平台上应用程序的 dynos 运行哪些命令。

您可以使用[gunicorn](https://vsupalov.com/what-is-gunicorn/)来运行您的 web 应用程序的多个实例,确保它们是健康的,并根据需要重新启动它们,在这些实例之间分配传入的请求,并与 web 服务器通信。

web: gunicorn application:application

9)将代码推送到 Github Repo,将 Repo 连接到 Heroku app

要部署您刚刚创建的 API,最简单的方法是将您的代码推送到 Github repository,并将这个 repo 连接到您的 Heroku 应用程序:

(图片由作者提供)

9)部署 Flask 应用

您可以设置自动部署或从 Heroku 仪表板手动部署 Flask 应用程序。如果在部署过程中有任何错误,它们将显示在构建日志中。部署完成后,您可以使用 Postman 点击 API,并看到它按预期工作。如果部署后出现错误,您可以这样查看日志:

(图片由作者提供)

10)创建用户界面

如果您有兴趣为您的登录系统创建一个前端,您可以查看我的基于 React 和 Redux 的 UI:

[## aakanksha-ns/书店

这个项目是用 Create React App 引导的。在项目目录中,您可以运行:在…中运行应用程序

github.com](https://github.com/aakanksha-ns/book-store)

下面是我已经构建的应用程序的一些截图:

(图片由作者提供)

(图片由作者提供)

(图片由作者提供)

参考资料:

构建和部署字母识别系统

原文:https://towardsdatascience.com/building-and-deploying-an-alphabet-recognition-system-7ab59654c676?source=collection_archive---------14-----------------------

使用 Anvil 将卷积神经网络(CNN)模型部署到网站中

部署后

在本文中,我将向您展示如何使用卷积神经网络(CNN)构建一个字母识别系统,并使用 nvil.works 部署它。在这篇文章的最后,你将能够创建一个如上所示的系统的精确复制品。

目录

卷积神经网络

CNN 实施

砧座集成

卷积神经网络

我们先来了解一下卷积神经网络到底是什么。卷积神经网络(CNN)是一种广泛用于图像识别和分类的神经网络。

CNN 是多层感知器的规则版本。多层感知器通常意味着全连接网络,即一层中的每个神经元都与下一层中的所有神经元相连。

CNN 由以下几层组成:

卷积层:一个大小为 3×3 或 5×5 的“内核”通过图像,并计算原始像素值与内核中定义的权重的点积。然后,该矩阵通过激活函数“ReLu”,该函数将矩阵中的每个负值转换为零。

Shervine Amidi 解释卷积层操作的图像

汇集层:一个大小的“汇集矩阵”例如 2X2 或 4X4,经过矩阵以减小矩阵的大小,从而仅突出图像的重要特征。

有两种类型的池操作:

  1. 最大汇集 是一种汇集类型,其中汇集矩阵中存在的最大值被放入最终矩阵中。
  2. 平均池是一种池,计算池内核中所有值的平均值,并放入最终矩阵中。

通过堆栈溢出解释最大和平均池的图像

(注意:在 CNN 架构中可以有多个卷积和池层的组合来提高其性能。)

全连通层:最后的矩阵再展平成一个一维向量。这个向量然后被传递到神经网络中。最后,输出层是附加到图像的不同可能标签的概率列表(例如,字母 a、b、c)。获得最高概率的标签是分类决策。

Shervine Amidi 显示全连接层的图像

CNN 实现

让我们从导入 Jupyter 笔记本中的库开始实现,如下所示:

***import** **numpy** **as** **np**
**import** **matplotlib.pyplot** **as** **plt**
**from** **keras.preprocessing.image** **import** ImageDataGenerator
**from** **keras.preprocessing** **import** image
**import** **keras**
**from** **keras.models** **import** Sequential
**from** **keras.layers** **import** Conv2D, MaxPooling2D, Flatten, Dense, Activation
**import** **os**
**import** **pickle***

然后,让我们导入包含从 az 的图像的 2 个数据集,用于训练和测试我们的模型。你可以从下面链接的我的 GitHub 库下载数据集。

*train_datagen = ImageDataGenerator(rescale = 1./255,
                                   shear_range = 0.2,
                                   zoom_range = 0.2,
                                   horizontal_flip = **True**)

test_datagen = ImageDataGenerator(rescale = 1./255)

train_generator = train_datagen.flow_from_directory(
    directory = 'Training',
    target_size = (32,32),
    batch_size = 32,
    class_mode = 'categorical'

)

test_generator = test_datagen.flow_from_directory(
    directory = 'Testing',
    target_size = (32,32),
    batch_size = 32,
    class_mode = 'categorical'

)*

ImageDataGenerator 生成批量的张量图像数据,通过使用 rescale 以 1/255 的因子进行缩放,将 0–255 范围内的 RGB 系数转换为 0 到 1 之间的目标值。

剪切范围用于随机应用剪切变换

zoom_range 用于图片内部随机缩放。

水平翻转用于随机水平翻转一半图像。

然后我们使用从目录中一个接一个地导入图像。flow_from_directory* 并对其应用 ImageDataGenerator。***

然后,我们将图像从其原始大小转换为我们的 target_size ,并声明 batch_size count,其中表示一次迭代中使用的训练样本的数量。

然后,我们将 class_mode 设置为分类,表示我们有多个类(a 到 z)要预测。

接下来,我们建立我们的 CNN 架构。

*model = Sequential()
model.add(Conv2D(32, (3, 3), input_shape = (32,32,3), activation = 'relu'))
model.add(MaxPooling2D(pool_size = (2, 2)))

model.add(Conv2D(32, (3, 3), activation = 'relu'))
model.add(MaxPooling2D(pool_size = (2, 2)))

model.add(Flatten())
model.add(Dense(units = 128, activation = 'relu'))
model.add(Dense(units = 26, activation = 'softmax'))

model.compile(optimizer = 'adam', loss = 'categorical_crossentropy', metrics = ['accuracy'])

model.summary()*

我们首先创建一个顺序模型,允许我们使用逐层定义 CNN 架构。增加功能。**

我们首先在输入图像上添加一个带有 32 个大小为 3X3 的过滤器的卷积层,并将其通过“relu”激活函数。**

然后,我们使用大小为 2X2 的池执行 MaxPooling 操作。

然后再次重复这些层,以提高模型的性能。

最后我们展平我们的合成矩阵,并将其通过由 128 个节点组成的密集层。然后连接到由 26 个节点组成的输出层,每个节点代表一个字母表。我们使用 softmax 激活将分数转换为归一化的概率分布,具有最高概率的节点被选为输出。**

一旦我们的 CNN 架构被定义,我们使用 adam optimizer 编译模型。

最后,我们训练我们的模型如下。

*model.fit_generator(train_generator,
                         steps_per_epoch = 16,
                         epochs = 3,
                         validation_data = test_generator,
                         validation_steps = 16)*

模型训练后达到的准确率为:93.42%

现在让我们试着测试我们的模型。但在此之前,我们需要定义一个函数,该函数为我们提供与结果相关联的字母表。

***def** get_result(result):
    **if** result[0][0] == 1:
        **return**('a')
    **elif** result[0][1] == 1:
        **return** ('b')
    **elif** result[0][2] == 1:
        **return** ('c')
    **elif** result[0][3] == 1:
        **return** ('d')
    **elif** result[0][4] == 1:
        **return** ('e')
    **elif** result[0][5] == 1:
        **return** ('f')
    **elif** result[0][6] == 1:
        **return** ('g')
    **elif** result[0][7] == 1:
        **return** ('h')
    **elif** result[0][8] == 1:
        **return** ('i')
    **elif** result[0][9] == 1:
        **return** ('j')
    **elif** result[0][10] == 1:
        **return** ('k')
    **elif** result[0][11] == 1:
        **return** ('l')
    **elif** result[0][12] == 1:
        **return** ('m')
    **elif** result[0][13] == 1:
        **return** ('n')
    **elif** result[0][14] == 1:
        **return** ('o')
    **elif** result[0][15] == 1:
        **return** ('p')
    **elif** result[0][16] == 1:
        **return** ('q')
    **elif** result[0][17] == 1:
        **return** ('r')
    **elif** result[0][18] == 1:
        **return** ('s')
    **elif** result[0][19] == 1:
        **return** ('t')
    **elif** result[0][20] == 1:
        **return** ('u')
    **elif** result[0][21] == 1:
        **return** ('v')
    **elif** result[0][22] == 1:
        **return** ('w')
    **elif** result[0][23] == 1:
        **return** ('x')
    **elif** result[0][24] == 1:
        **return** ('y')
    **elif** result[0][25] == 1:
        **return** ('z')*

最后,让我们按如下方式测试我们的模型:

*filename = r'Testing\e\25.png'
test_image = image.load_img(filename, target_size = (32,32))
plt.imshow(test_image)
test_image = image.img_to_array(test_image)
test_image = np.expand_dims(test_image, axis = 0)
result = model.predict(test_image)
result = get_result(result)
print ('Predicted Alphabet is: **{}**'.format(result))*

该模型正确预测输入图像字母为‘e’。

砧座整合

Anvil 是一个允许我们用 python 构建全栈 web 应用的平台。它让我们更容易将机器学习模型从 Jupyter 笔记本变成 web 应用程序。

让我们从在 anvil 上创建一个帐户开始。完成后,用材料设计创建一个新的空白应用程序。

点击此链接,了解如何使用 anvil 的分步指南。

右边的工具箱包含了所有可以拖动到网站上的组件。

所需组件:

  1. 2 个标签(标题和副标题)
  2. 图像(显示输入图像)
  3. 文件加载器(上传输入图像)
  4. 突出显示的按钮(预测结果)
  5. 标签(查看结果)

拖放这些组件,并根据您的要求进行排列。

为了添加标题和副标题,选择标签并在右侧的属性部分,转到名为“文本”的选项,如下所示(以红色突出显示),并键入标题/副标题。

完成用户界面后,进入如上所示的代码部分(以绿色突出显示),并创建一个新函数,如下所示

*def primary_color_1_click(self, **event_args):
      file = self.file_loader_1.file
      self.image_1.source = file
      result = anvil.server.call('model_run',file)
      self.label_3.text = result
      pass*

当我们按下预测按钮时,该功能将执行。它将从文件加载器上传输入图像,并将其传递给 jupyter 笔记本的“model_run”功能。该函数将返回通过标签组件(label_3)显示的预测字母表。

现在剩下要做的就是将我们的 anvil 网站连接到 jupyter 笔记本上。

这需要实施如下两个步骤:

  1. 导入 Anvil 上行密钥:点击设置按钮然后点击上行,点击启用上行密钥并复制密钥。

在您的 jupyter 笔记本中粘贴以下内容:

***import** **anvil.server**
**import** **anvil.media**
anvil.server.connect("paste your anvil uplink key here")*

2.创建一个函数“模型运行”,预测网站上传的图像。

*@anvil.server.callable
**def** model_run(path):
    **with** anvil.media.TempFile(path) **as** filename:
        test_image = image.load_img(filename, target_size = (32,32))
        test_image = image.img_to_array(test_image)
        test_image = np.expand_dims(test_image, axis = 0)
        result = model.predict(test_image)
        result = get_result(result)
        **return** ('Predicted Alphabet is: **{}**'.format(result))*

而且,是的!!!!现在你可以回到 anvil,点击运行按钮,发现一个完全完成的字母识别系统。

您可以在我的 GitHub 资源库中找到源代码和数据集。

*** [## sakshibutala/CNN _ 字母识别

该算法与 anvil 网站集成,anvil 网站识别给定输入图像中存在的字母表。…

github.com](https://github.com/sakshibutala/CNN_AlphabetRecognition)

参考

[## 卷积神经网络教程:从基础到高级- MissingLink.ai

卷积神经网络卷积神经网络架构是深度学习的核心,它是…

missinglink.ai](https://missinglink.ai/guides/convolutional-neural-networks/convolutional-neural-network-tutorial-basic-advanced/) [## CS 230 -卷积神经网络说明书

你想看看你母语的备忘单吗?可以在 GitHub 上帮我们翻译一下!卷积…

stanford.edu](https://stanford.edu/~shervine/teaching/cs-230/cheatsheet-convolutional-neural-networks) [## Keras 文档:图像数据预处理

从目录中的图像文件生成 tf.data.Dataset。如果您的目录结构是:然后调用…

keras.io](https://keras.io/api/preprocessing/image/)***

构建和部署端到端假新闻分类器

原文:https://towardsdatascience.com/building-and-deploying-end-to-end-fake-news-classifier-caebe45bd30?source=collection_archive---------59-----------------------

变更数据

罗马克拉夫特在 Unsplash 拍摄的照片

在这个智能手机和互联网的数字时代,假新闻像野火一样传播,它看起来就像真新闻,对社会造成了很大的损害。因此,在本教程中,我们将构建一个假新闻分类器,并将其作为一个 web 应用程序部署在云上,以便任何人都可以访问它。它不会像谷歌或 facebook 的假新闻分类器那样好,但根据从 Kaggle 获得的数据集,它会相当不错。

在我们开始之前,为了让你有动力,让我向你展示一下在本教程结束时你将能够构建的 web 应用程序 假新闻分类器 现在你已经看到了最终产品,让我们开始吧。

:我假设你熟悉基本的机器学习技术、算法和软件包。

我将本教程分为三个部分:

  1. 探索性数据分析
  2. 预处理和模型训练
  3. 在 Heroku 上构建和部署 Web 应用程序

现在,如果您是初学者,我建议您安装 Anaconda 发行版,因为它附带了数据科学所需的所有软件包,并设置了一个虚拟环境。

如果你想跟随这个教程,这里是我的 GitHub 上的源代码链接:https://github.com/eaofficial/fake-news-classifier

你可以在这里获得数据集或者你可以克隆我的 GitHub 库。

1.探索性数据分析

照片由元素 5 数码Unsplash 上拍摄

在项目目录中创建一个名为 eda.ipynb 或 eda.py 的文件。

我们将首先导入所有需要的包。

**#Importing all the libraries*
**import** **warnings**
warnings.filterwarnings('ignore')
**import** **numpy** **as** **np**
**import** **pandas** **as** **pd**
**import** **matplotlib.pyplot** **as** **plt**
**import** **seaborn** **as** **sns**
**import** **nltk**
**import** **re**
**from** **wordcloud** **import** WordCloud
**import** **os***

现在,我们将首先使用pd.read_csv()读取假新闻数据集,然后我们将探索该数据集。

在上述笔记本的单元格 4 中,我们统计了每个主题中的样本假新闻的数量。我们还将使用 seaborn count plot sns.coountplot()绘制其分布图。

我们现在将绘制一个词云,首先将所有新闻连接成一个字符串,然后生成标记并删除停用词。文字云是一种非常好的可视化文本数据的方式。

正如您在下一个单元格中看到的,现在我们将 true.csv 作为真实新闻数据集导入,并执行与我们在 fake.csv 上执行的步骤相同的步骤。您会注意到真实新闻数据集中的一个不同之处是,在列中,有一个出版物名称,如 WASHINGTON (Reuters) ,由连字符(-)分隔。**

看起来真实的新闻是可信的,因为它来自一家出版社,所以我们将从新闻部分中分离出出版物,以使本教程的预处理部分中的数据集一致。现在,我们将只探索数据集。

如果您继续下去,可以看到新闻主题列在真实和虚假新闻数据集中的分布是不均匀的,所以我们稍后将删除该列。我们的 EDA 到此结束。

现在我们可以用你们期待已久的东西弄脏我们的手了。我知道这部分令人沮丧,但 EDA 和预处理是任何数据科学生命周期中最重要的部分

2.预处理和模型训练

卡洛斯·穆扎Unsplash 上的照片

在这一部分中,我们将对我们的数据执行一些预处理步骤,并使用之前从 EDA 中获得的见解来训练我们的模型。

预处理

要按照本部分中的代码操作,请打开 train ipynb 文件。所以,不再多说,让我们开始吧。

像往常一样导入所有的包并读取数据。我们将首先从真实数据文本栏中删除路透社。由于有些行没有路透社,所以我们将首先获得这些指数。

从文本中删除路透社或推特信息

  • 文本只能在“—”处拆分一次,它总是出现在提及出版物来源之后,这给了我们出版物部分和文本部分
  • 如果我们没有得到文本部分,这意味着没有给出该记录的出版细节
  • Twitter 上的推文总是有相同的来源,一个最长 259 个字符的长文本
***#First Creating list of index that do not have publication part*
unknown_publishers = []
**for** index,row **in** enumerate(real.text.values):
    **try**:
        record = row.split(" -", maxsplit=1)
        *#if no text part is present, following will give error*
        record[1]
        *#if len of publication part is greater than 260*
        *#following will give error, ensuring no text having "-" in between is counted*
        **assert**(len(record[0]) < 260)
    **except**:
        unknown_publishers.append(index)**

用一行代码总结一下,上面的代码所做的是获取真实数据集中不存在发布者的 text 列的索引。

现在我们将把路透社从文本栏中分离出来。

***# separating publishers from the news text*
publisher = []
tmp_text = []
for index,row **in** enumerate(real.text.values):
    if index **in** unknown_publishers:
        *#add text to tmp_text and "unknown" to publisher*
        tmp_text.append(row)

        publisher.append("Unknown")
        continue
    record = row.split(" -", maxsplit=1)
    publisher.append(record[0])
    tmp_text.append(record[1])**

在上面的代码中,我们遍历 text 列并检查 index 是否属于,如果是,那么我们将文本添加到 publishers 列表中。否则,我们将文本分为出版商和新闻文本,并添加到各自的列表中。

***#Replace existing text column with new text*
*#add seperate column for publication info*
real["publisher"] = publisher
real["text"] = tmp_text**

上面的代码非常简单明了,我们添加了一个新的 publisher 列,并用不带 Reuter 的新闻文本替换了 text 列。

我们现在将检查真实和虚假新闻数据集中的文本列中是否有任何缺失值,并删除该行。

如果我们检查假新闻数据集,我们会看到有许多行缺少文本值,整个新闻都出现在title列中,因此我们将合并titletext列。

**real['text'] = real['text'] + " " + real['title']
fake['text'] = fake['text'] + " " + fake['title']**

接下来,我们将向数据集添加类,删除不必要的列,并将数据合并为一个。

***# Adding class info* 
real['class'] = 1 
fake['class'] = 0*# Subject is diffrent for real and fake thus dropping it* *# Also dropping Date, title and Publication* real.drop(["subject", "date","title",  "publisher"], axis=1, inplace=**True**) fake.drop(["subject", "date", "title"], axis=1, inplace=**True**)*#Combining both into new dataframe* data = real.append(fake, ignore_index=**True**)**

删除停用字词、标点符号和单字符字词。(在任何 NLP 项目中非常常见和基本的任务)。

模特培训

矢量化:Word2Vec

尼克·莫瑞森在 Unsplash 拍摄的照片

Word2Vec 是使用浅层神经网络学习单词嵌入的最流行的技术之一。它是由托马斯·米科洛夫于 2013 年在谷歌开发的。单词嵌入是最流行的文档词汇表示。它能够捕捉文档中单词的上下文、语义和句法相似性、与其他单词的关系等。

如果你想了解更多,点击 这里

让我们创建我们的 Word2Vec 模型。

**#install gensim if you haven't already
#!pip install gensim
import gensim*#Dimension of vectors we are generating*
EMBEDDING_DIM = 100
*#Creating Word Vectors by Word2Vec Method*
w2v_model = gensim.models.Word2Vec(sentences=X, size=EMBEDDING_DIM, window=5, min_count=1)*#vocab size*
len(w2v_model.wv.vocab)
*#We have now represented each of 122248 words by a 100dim vector.***

这些向量将被传递给 LSTM/GRU,而不是单词。1D-CNN 可以进一步用于从向量中提取特征。

Keras 有一个名为“嵌入层的实现,它将创建单词嵌入(向量)。因为我们是用 gensim 的 word2vec 做的,所以我们会将这些向量加载到嵌入层中,并使该层不可训练。

我们不能将字符串传递给嵌入层,因此需要用数字来表示每个单词。

记号赋予器可以用数字来表示每个单词

***# Tokenizing Text -> Repsesenting each word by a number*
*# Mapping of orginal word to number is preserved in word_index property of tokenizer**#Tokenized applies basic processing like changing it yo lower case, explicitely setting that as False*
tokenizer = Tokenizer()
tokenizer.fit_on_texts(X)X = tokenizer.texts_to_sequences(X)**

我们创建了单词索引和向量之间的映射矩阵。我们用它作为嵌入层的权重。嵌入层接受单词的数字符号,并向内层输出相应的向量。它向下一层发送一个零向量,用于将被标记为 0 的未知单词。嵌入层的输入长度是每个新闻的长度(由于填充和截断,现在是 700)。

现在,我们将创建一个序列神经网络模型,并在嵌入层中添加从 w2v 生成的权重,还添加一个 LSTM 层。

***#Defining Neural Network*
model = Sequential()
*#Non-trainable embeddidng layer*
model.add(Embedding(vocab_size, output_dim=EMBEDDING_DIM, weights=[embedding_vectors], input_length=maxlen, trainable=False))
*#LSTM* 
model.add(LSTM(units=128))
model.add(Dense(1, activation='sigmoid'))
model.compile(optimizer='adam', loss='binary_crossentropy', metrics=['acc'])**

现在让我们使用sklearn train_test_split方法将数据集分成训练集和测试集。

让我们使用model.fit(X_train, y_train, validation_split=0.3, epochs=6)来训练模型。这需要一些时间,在我的机器上花了大约 40 分钟,所以坐下来喝点咖啡,放松一下。

训练完成后,我们将在test数据集上进行测试,并使用classification_report()方法生成报告。

哇,我们获得了 99%的准确性,具有良好的精确度和召回率,因此我们的模型看起来很好,现在让我们将它保存在磁盘上,以便我们可以在我们的 web 应用程序中使用它。

3.构建和部署 web 应用程序

照片由沙哈达特·拉赫曼Unsplash 上拍摄

在这一部分,我不会讲太多的细节,我建议你仔细阅读我的代码,它非常容易理解。如果你一直坚持到现在,你必须有相同的目录结构,如果没有,那么只需改变app.py文件中的路径变量。

现在将整个目录上传到 GitHub 存储库中。

我们将在 Heroku 上托管我们的 web 应用程序。如果你还没有,在 Heroku 上创建一个免费帐户,然后:

  1. 点击创建新应用程序
  2. 然后选择一个名称
  3. 选择 GitHub,然后选择您想要保留的存储库
  4. 点击部署。

嘣,它完成了,你的假新闻分类器现在是活的。

结论…

如果您已经完成了,那么恭喜您,现在您可以构建和部署一个复杂的机器学习应用程序了。

我知道这很难理解,但你能走到这一步还是值得称赞的。

****注意:该应用程序适用于大多数新闻,只需记住粘贴整段新闻,最好是美国新闻,因为数据集被限制为美国新闻。

如果我们还没有见过面,我是 Eish Kumar 你可以在 Linkedin 上关注我:https://www.linkedin.com/in/eish-kumar/

关注我更多这样的文章。

建立和评估分类 ML 模型

原文:https://towardsdatascience.com/building-and-evaluating-classification-ml-models-9c3f45038ef4?source=collection_archive---------21-----------------------

必须阅读建立良好的分类 ML 模型

马丁·桑切斯在 Unsplash 上的照片

机器学习中有不同类型的问题。有些可能属于回归类(有连续的目标),而另一些可能属于分类类(有离散的目标)。有些可能根本没有目标,您只是试图通过创建聚类,根据数据点的固有特征来区分数据点,从而了解数据的特征。

然而,这篇文章不是关于机器学习的不同领域,而是关于一件很小但很重要的事情,如果不小心,它可能会对您的可操作化分类模型以及最终的业务造成“谁知道是什么”的影响。因此,下一次当工作中有人告诉你她/他的模型给出了~93.23%的准确率时,在问出正确的问题之前,不要上当。

那么,我们如何知道什么是正确的问题呢?

这个问题问得好。让我们通过研究如何以正确的方式构建和评估分类模型来尝试回答这个问题。每个研究机器学习的人都知道所有经常使用的分类指标,但只有少数人知道用来评估分类模型性能的正确指标。

因此,为了让您能够提出正确的问题,我们将详细讨论以下概念(对于分类模型):

  1. 数据分发(培训、验证、测试)
  2. 处理阶层失衡。
  3. 模型评估指标的正确选择

数据分布

在为训练集、验证集和测试集拆分数据时,应该始终记住这三者必须代表相同的总体。例如,在所有数字图像都是灰色(黑白)的 MNIST 数字分类数据集中,您对其进行了训练,并实现了几乎 90%的验证准确性,但是您的测试数据具有各种颜色的数字图像(不仅仅是黑白)。现在,你有一个问题。无论你做什么,总会有数据偏差。你不能完全摆脱它,但是你能做的是在你的验证和测试数据集中保持一致性。这是验证集和测试集分布不同的一个例子。

分割数据的正确策略

您的测试数据集必须始终代表真实世界的数据分布。例如,在二元分类问题中,假设您要检测一种罕见疾病(1 类)的阳性患者,其中整个数据集的 6%包含阳性病例,那么您的测试数据也应该具有几乎相同的比例。确保您遵循相同的分布。这不仅仅是分类模型的情况。它适用于所有类型的 ML 建模问题。

培训、验证和测试拆分的正确顺序

应该首先提取测试数据集,而不将任何数据泄漏到剩余数据中,然后,验证数据必须遵循测试数据中的分布。这两次分裂后剩下的部分进入训练。因此,将整个数据集划分为训练集、验证集和测试集的正确顺序是从整个数据集中按特定顺序获取测试集、验证集和训练集。

正确的比例

在机器学习社区中有 70-20-10 的比例,但这只是在你拥有平均数据量的情况下。例如,如果您正在处理一个图像分类问题,并且您有大约 1000 万张图像,那么进行 70–20–10 分割将是一个坏主意,因为数据量如此之大,以至于要验证您的模型,甚至 1%到 2%的数据就足够了。因此,我宁愿选择 96–2–2 分割,因为您不希望通过增加大小来增加不必要的验证和测试开销,因为在验证和测试中使用总数据的 2%就可以实现相同的分布表示。此外,在进行分割时,确保不要用替代品取样。

处理阶级不平衡

在任何分类问题的情况下,对模型性能影响最大的是每个类别对总成本贡献的损失量。某一类别的实例数量越多,该类别的总损失贡献就越高。一个类别对总成本的损失贡献与属于该类别的实例数量成正比。这样,分类器更加集中于正确地分类那些对损失函数的总成本贡献更多的实例(即,来自多数类的实例)。

以下是我们解决阶级不平衡的方法:

  1. 加权损失
  2. 重采样

加权损失

在二元交叉熵损失中,我们有以下损失函数:

二元交叉熵损失

该模型输出给定示例属于正(y=1)类的概率。并且,基于上述二元交叉熵损失函数,计算每个示例的损失值,最后,总成本被计算为所有示例的平均损失。让我们通过编写一个简单的 python 脚本进行一个简单的模拟来更好地理解它。让我们生成 100 个地面真相标签,其中 25 个属于正(y=1)类,其余的是负(y=0)类,以说明我们微小实验中的类不平衡。此外,我们将为每个示例生成属于正类的随机概率值。

import numpy as np
import random***# Generating Ground truth labels and Predicted probabilities***
truth, probs = [], []
for i in range(100):
    ***# To maintain class imbalance***
    if i < 25:
        truth.append(1)
    else:
        truth.append(0)
    probs.append(round(random.random(),2))
print("Total Positive Example Count: ",sum(truth))
print("Total Negative Example Count: ",len(truth) - sum(truth))
print("Predicted Probability Values: ",probs)**Output:**
Total Positive Example Count:  25
Total Negative Example Count:  75
Predicted Probability Values:  [0.84, 0.65, 0.11, 0.21, 0.31, 0.05, 0.44, 0.83, 0.19, 0.61, 0.28, 0.36, 0.46, 0.79, 0.74, 0.58, 0.65, 0.8, 0.05, 0.39, 0.08, 0.45, 0.4, 0.03, 0.41, 0.75, 0.46, 0.49, 0.94, 0.57, 0.38, 0.7, 0.07, 0.91, 0.85, 0.91, 0.72, 0.28, 0.0, 0.55, 0.61, 0.55, 0.81, 0.98, 0.9, 0.36, 0.65, 0.91, 0.26, 0.1, 0.99, 0.48, 0.34, 0.96, 0.68, 0.21, 0.28, 0.37, 0.8, 0.27, 0.87, 0.93, 0.03, 0.95, 0.25, 0.63, 0.2, 0.45, 0.05, 0.7, 0.91, 0.85, 0.56, 0.61, 0.4, 0.35, 0.6, 0.27, 0.08, 0.85, 0.14, 0.82, 0.22, 0.41, 0.85, 0.72, 0.91, 0.5, 0.55, 0.89, 0.39, 0.92, 0.24, 0.07, 0.52, 0.88, 0.01, 0.01, 0.01, 0.31]

现在,我们有了基本事实标签和预测概率,使用上述损失函数,我们可以计算两个类别的总损失贡献。在计算对数值之前,在预测概率中加入了一个非常小的数字,以避免由于未定义的值而产生的误差。[log(0) =未定义]

***# Calculating Plain Binary Cross-Entropy Loss***
pos_loss, neg_loss = 0, 0
for i in range(len(truth)):
    ***# Applying the binary cross-entropy loss function***
    if truth[i] == 1:
        pos_loss += -1 * np.log(probs[i] + 1e-7)
    else:
        neg_loss += -1 * np.log(1 - probs[i] + 1e-7)
print("Positive Class Loss: ",round(pos_loss,2))
print("Negative Class Loss: ",round(neg_loss,2))**Output:**
Positive Class Loss:  29.08
Negative Class Loss:  83.96

正如我们可以看到的,两个类别的总损失有很大的差异,负类别在损失贡献方面领先,该算法在技术上更关注负类别,以从根本上减少损失,同时将其最小化。这时,我们通过使用以下加权损失函数为总损失计算分配一个权重,来欺骗模型,使其相信不真实的情况:

加权二元交叉熵损失

这里,“Wp”和“Wn”分别是分配给正和负类别损失的权重,可以计算如下:

Wp =负样本(y=0)总数/样本总数

Wn =正(y=1)例总数/例总数

现在,让我们通过将权重添加到计算中来计算加权损失:

***# Calculating Weighted Binary Cross-Entropy Loss***
pos_loss, neg_loss = 0, 0***# Wp (Weight for positive class)***
wp = (len(truth) - sum(truth))/len(truth)***# Wn (Weight for negative class)***
wn = sum(truth) / len(truth)for i in range(len(truth)):
   ** *# Applying the same function with class weights.***
    if truth[i] == 1:
        pos_loss += -wp * np.log(probs[i] + 1e-7)
    else:
        neg_loss += -wn * np.log(1 - probs[i] + 1e-7)
print("Positive Class Loss: ",round(pos_loss,2))
print("Negative Class Loss: ",round(neg_loss,2))Output:
Positive Class Loss:  21.81
Negative Class Loss:  20.99

太神奇了!不是吗?通过分配正确的权重,我们成功地显著减少了两个类别之间的损失贡献差异。

重采样

这是另一个你可以用来对抗阶级不平衡的技术,但是这不应该是你使用的第一个技术。重采样有三种方式:

  1. 通过过采样少数类
  2. 或者通过欠采样多数类
  3. 或两者各适量

过采样可以通过对替换的少数类进行随机采样来实现,也可以通过使用 SMOTE 等技术综合生成更多的样本来实现。过采样在一定程度上是有帮助的,因为在一定量之后,您将复制数据中包含的信息。这可能会给你两个类的理想损失贡献,但在验证和测试时会失败。但是,如果你有大量的数据和不平衡,你应该选择欠采样而不替换多数类。

有时,当数据量一般,并且类别不平衡不严重时,人们会同时使用这两种技术。因此,他们对少数类进行过采样,对多数类进行一定量的欠采样,以达到平衡。

现在,你知道当有人来找你,说我有大约 93.23%的准确率时,你应该思考并询问数据中的类别比例以及使用的损失函数的类型。然后,你应该想知道仅仅测量准确性是否是正确的方法。或者还有别的!

度量标准的正确选择

至少当你在研究一个机器学习模型时,总会有更多的东西,但只有当你有东西可以比较时,才可能知道你什么时候想要更多。一个标杆!一旦你有了一个基准,你就知道你想要多少改进。

但是为了提高性能,您需要知道在您试图解决的业务问题中,哪个度量是正确的性能指标。例如,如果您试图解决一个肿瘤检测问题,其目标是检测肿瘤是恶性的(y=1)还是良性的(y=0)。在这种情况下,你需要明白在现实世界中良性病例比恶性病例多。因此,当你得到数据时,你会有一个很大的阶级不平衡(当然,除非你真的很幸运)。因此,准确性作为一个衡量标准是不可能的。现在的问题是,什么更重要?以检测患者是否患有恶性肿瘤或良性肿瘤。这是一个商业决策,你应该总是咨询领域专家(在这种情况下,专家医生),通过问这样的问题来理解业务问题。如果我们更关心有效地检测恶性肿瘤,即使我们有一些假阳性(基础事实:良性,模型预测:恶性),但我们需要尽可能少的假阴性(基础事实:恶性,模型预测:良性),那么回忆应该是我们选择的度量标准,但如果反之亦然(在这个特定的情况下永远不可能),精确度应该是我们的选择。

有时,在商业环境中,存在需要对两个类别进行有效分类的问题,因此您可能希望优化 F1 分数。为了解决这个权衡,我们应该尽可能地最大化精确召回曲线下的区域。

此外,结果必须以具有度量上限和下限的置信区间的形式传达,以便使用在各种样本上进行的所有实验,对模型在总体上的行为有一个公平的想法。

总结一下,下面是这篇文章的主要内容:

  • 在构建分类模型时,数据分布是至关重要的,人们应该总是首先正确地开始测试分布,然后按照这个顺序进行验证和训练。
  • 应该适当地处理类不平衡,以避免对实时数据产生非常糟糕的结果。
  • 只有当你选择正确的度量标准来评估你的模型时,你才能正确地评估它的性能。从业务专业知识到模型本身的技术细节,有很多因素可以帮助我们决定正确的指标。

感谢您阅读文章。

建立和管理数据科学团队

原文:https://towardsdatascience.com/building-and-managing-data-science-teams-77ba4f43bc58?source=collection_archive---------25-----------------------

由于数据数量和种类的大幅增加,大多数组织已经开始使用数据科学来了解其业务绩效并做出运营决策。一些组织刚刚开始研究数据科学,而另一些组织已经进行了大量投资,数据科学团队遍布全球业务部门。无论您的组织成熟度如何,挑战仍然存在,即如何最好地构建和管理数据科学团队,以便他们能够扩展以满足您组织不断增长的需求。

这篇文章是关于我在建立和管理一个数据科学团队的过程中所学到的东西,既有我自己在过去几年的经验,也有向该领域其他人学习的经验。

去飞溅

数据科学团队

重要的是要认识到,建立一个数据科学团队并不像雇用数据科学家并让他们获得数据那么简单。尽管对于机器学习(ML)模型的初始原型来说,这似乎已经足够了。但是,要使模型在真实环境中工作,您需要注意使模型生产就绪所需的其他部分。机器学习工作流可能涉及许多相互依赖的步骤,从数据准备和分析,到训练,到评估,到部署等等。数据科学团队涉及许多跨职能技能,以使其从原型阶段成为现实世界的解决方案。为了做到这一切,数据科学团队由数据科学家、数据架构师/工程师、机器学习工程师和软件开发人员组成。让我们简单讨论一下这些角色。

角色

数据科学团队中有许多角色和职责重叠的名称,但大致可以分为:

数据科学家利用机器学习和数据挖掘技术解决商业问题。数据科学家还负责使用统计方法、流程和算法从数据中提取见解。任务包括数据预处理,分析,实验,可视化和交流结果。

机器学习工程师结合了软件工程和建模技能。训练、监控和维护模型的一切都是 ML 工程师的工作。

数据架构师/工程师- 负责实施、测试和维护大数据和大型分布式系统的基础设施组件。

软件开发人员负责获取一个已部署的模型,并准备好通过 REST API 提供服务,还可能涉及一些前端接口,因此软件开发人员可以帮助完成所有这些任务。

数据注释和质量保证(QA)- 数据是任何数据科学团队成功的关键。拥有一支训练有素的数据标注团队可以提供显著的价值,尤其是在迭代机器学习模型测试和验证阶段。机器学习是一个迭代的过程。您需要验证模型预测,还需要准备新的数据集并丰富现有数据集以改进算法的结果。您可以雇佣或外包数据注释,但挑战仍然是在标记数据和验证结果方面保持一致性。

研究科学家- 如果你的团队在一些核心人工智能领域工作,如对话式人工智能、计算机视觉、机器人、强化学习、图形模型等。你可能需要雇佣有博士或核心研究背景的人。

数据科学经理- 负责招聘和建立数据科学团队,展示团队能力,与高级管理层沟通,开发团队可以遵循的流程,帮助团队沟通并保持事情进展。

简而言之,目标是建立一个跨职能的数据科学团队,使您的组织能够从数据中获得洞察力,并构建生产就绪模型。

管理数据科学团队

管理数据科学团队不同于典型的软件开发团队,这是因为数据科学需求往往会因为任务的探索性而更加模糊。精确地计划一个项目的时间或最终结果是非常困难的。

以下是成功管理数据科学团队需要应对的一些关键挑战。

  1. 壮大团队:最初你可能需要一个小团队,主要从事一些分析工作,或者想出一些你可以向高级管理层推销的想法。但是你很快就会意识到,要把这个想法变成一个产品,你的团队还需要很多其他的技能。目标应该是将数据科学团队发展成为负责设计、实施和维护产品的完整产品团队。作为一个产品团队,数据科学团队可以试验、构建并直接为公司增加价值。
  2. 区分工作的优先级:我发现团队不时会收到大量的分析报告或其他数据处理请求。这些临时请求耗费大量时间,并影响长期项目和其他关键交付。重要的是区分工作的优先次序,并为这些特别的任务分配正确的优先级。在我们的团队中,我们创建了一个特别的请求积压,并增加了这些任务的优先级。然后,团队可以更好地管理这些紧急请求,而不会牺牲重要任务的时间。
  3. 数据质量:第一个问题是:你得到的数据是否正确?您可能有大量可用的数据,但这些数据的质量并不确定。为了创建、验证和维护高性能机器学习模型的生产,您必须使用可信、可靠的数据来训练和验证它们。你需要检查数据的准确性质量。数据标注的准确性衡量标注与事实的接近程度。数据标注的质量关乎整个数据集的准确性。确保所有标注器的工作看起来相同,并且标注在数据集上一致准确。
  4. 工具:工具扮演着重要的角色,因为它们允许你自动化。您应该使用相关工具来完成繁重的工作,运行脚本来自动执行查询和处理数据,以节省一些时间,这些时间反过来可以用于提高团队的工作效率。数据科学团队致力于解决挑战性问题。自动化重复性的每周报告可以帮助工程师专注于一些新的挑战性问题。在我们的团队中,我们制作了一个标记数据的工具,并向数据注释团队公开了该工具。这确实有助于我们检查数据的一致性,并在不同成员之间共享工作,快速完成贴标任务。
  5. 流程:数据科学团队项目以研究为导向,或者从大量研究活动开始,很难预测他们需要多长时间才能完成。此外,建模、数据处理等许多活动通常由一个人完成,因此传统的协作工作流不适合。你必须确定一种最适合你的团队的方法。就像在我们的案例中,我们在 JIRA 混合使用看板和 Scrum 板。对于研究活动,数据探索/分析,探索 ML 模型采用看板模式,而作为模型的产品化,你可以作为一个 Scrum 团队工作。所以基本上你的数据科学家、研究科学家和 ML 工程师大多以看板模式工作,而数据工程师、软件工程师则以 Scrum 模式工作。评估各种选项,看看什么最适合您的团队和项目。

及时处理好这些挑战是很重要的,否则会导致团队的低积极性、低生产率,并可能导致人员流失。

建立和管理数据科学团队是一个持续的学习曲线,因为行业仍在寻找建立有效和标准方法和流程的方法。

感谢阅读!

使用 DAX 在 PowerBI 中构建和优化多元线性回归

原文:https://towardsdatascience.com/building-and-optimizing-multiple-linear-regression-in-powerbi-using-dax-ed468e1ec2ae?source=collection_archive---------17-----------------------

本文主要介绍如何在 PowerBI 中使用 DAX 完成多元线性回归进行时间序列分析。接下来,我将把“岭回归”和“套索回归”的思想引入模型优化。

多重线性的公式推导

多元线性回归公式如下:

图片由 维基

DAX 不能执行矩阵运算,所以回归公式参考了克利姆定律。以二元回归为例,其原理是通过计算数据集,得到β0,β1,β2 的最优解。公式推导如下:

图片由 百科

我们只能通过根据这个公式在 DAX 中创建新的度量来完成回归。

关于本文的数据集

本文数据集来自 Tableau 论坛(Superstore。xls)

作者图片

其中“订单日期”将是第一个自变量,“销售额”将是因变量(即预测目标)。

构建多元线性回归

从下图可以看出,销售数据是以年为单位形成的,所以我们可以用“月”作为第二个自变量。

作者图片

基于上述线性回归公式,代码如下:

Binary Linear Regression = 
VAR VT =
FILTER (
SELECTCOLUMNS(
SUMMARIZE(ALLSELECTED('Date'),'Date'[Date],'Date'[Month Number]),
"VT[X1]", 'Date'[Date],
"VT[X2]", 'Date'[Month Number],
"VT[Y]",'Orders'[Qty]
),
AND (
NOT ( ISBLANK ( VT[X1] ) ),
AND(
NOT ( ISBLANK ( VT[X2] ) ),
NOT ( ISBLANK ( VT[Y] ) )
))
)
VAR Average_X1 =
AVERAGEX ( VT, VT[X1] )
VAR Average_X2 =
AVERAGEX ( VT, VT[X2] )
VAR Average_Y =
AVERAGEX ( VT, VT[Y] )
VAR Sum_X1_2 =
SUMX ( VT, (VT[X1] - Average_X1) ^ 2 )
VAR Sum_X2_2 =
SUMX ( VT, (VT[X2] - Average_X2) ^ 2 )
VAR Sum_X1Y =
SUMX ( VT, (VT[X1] - Average_X1) * (VT[Y] - Average_Y))
VAR Sum_X2Y =
SUMX ( VT, (VT[X2] - Average_X2) * (VT[Y] - Average_Y))
VAR X12 = 
SUMX( VT, (VT[X1] - Average_X1)*(VT[X2] - Average_X2))
VAR Beta1 =
DIVIDE (
Sum_X1Y*Sum_X2_2 - sum_x2y*X12,
Sum_X1_2*Sum_X2_2 - X12 ^ 2
)
VAR Beta2 =
DIVIDE (
Sum_X2Y*Sum_X1_2 - sum_x1y*X12,
Sum_X1_2*Sum_X2_2 - X12 ^ 2
)
VAR Intercept =
Average_Y - Beta1 * Average_X1 - Beta2 * Average_X2
VAR RESULT = 
SUMX (
SUMMARIZE('Date','Date'[Date],'Date'[Month Number]),
Intercept + Beta1 * 'Date'[Date] + Beta2 * 'Date'[Month Number]
)
RETURN
RESULT

你可以看到下图中有一条红色的曲线:这是我们刚刚建立的二元线性回归模型。

评价模型

现在模型已经建立,我们需要评估它。

我引入两个指标:

1.RMSE(均方根误差)—越小越好

图片由 维基百科

2.r(拟合优度)—通常越接近 1 越好

图像由 维基 组成

根据上面的公式,可以完成如下代码。

RMSE = 
VAR 
VT =
SUMMARIZE(
ALLSELECTED('Date'),'Date'[Date],'Date'[Month Number])
RETURN
SQRT(
divide(
SUMX(VT,
    ('Orders'[Binary Linear Regression] - 'Orders'[Qty]) ^ 2),
COUNTROWS(VT)))--------------------------------------------------------------------------------------------------
R^2 = 
VAR 
VT =
SUMMARIZE(ALLSELECTED('Date'),'Date'[Date],'Date'[Month Number])
VAR
ESS = 
SUMX(VT,POWER('Orders'[Binary Linear Regression]-AVERAGEX(VT,[Qty]),2))
VAR
TSS = 
SUMX(VT,POWER([Qty]-AVERAGEX(VT,[Qty]),2))
RETURN
DIVIDE(ESS,TSS)

运行代码后,我们知道模型的拟合效果并不乐观。

作者图片

优化模型

我们知道线性回归优化主要有两种方法:套索回归和岭回归。这两种方法分别在模型中加入了 L1 & L2 正则化。我们不谈复杂的统计,我们只关心如何用 DAX 高效的解决模型优化的问题。

实际上,这两种方法都是通过增加一个惩罚项来降低模型的某些系数。前者可以直接把不重要的系数压缩到 0。所以这个算法本质上是对模型中每个β系数的选择性压缩。另外,DAX 本身并不是算法的语言。因此,考虑到代码运行的性能,我们可以简单地将模型的系数乘以一个变量来达到效果,而不需要进行复杂的求导计算。在 PowerBI 中创建参数作为该变量的值,这样我们就可以在可视化界面中调整该变量的值,实现模型的手动优化。因此,我们修改了原始回归模型的代码:

Manual Binary Regression = 
VAR R = '_Slope'[Regular factor Value]
VAR A = 'α'[α Value]
VAR B = 'β'[β Value]
VAR VT =
FILTER (
SELECTCOLUMNS(
SUMMARIZE(ALLSELECTED('Date'),'Date'[Date],'Date'[Month Number]),
"VT[X1]", 'Date'[Date],
"VT[X2]", 'Date'[Month Number],
"VT[Y]",'Orders'[Qty]
),
AND (
NOT ( ISBLANK ( VT[X1] ) ),
AND(
NOT ( ISBLANK ( VT[X2] ) ),
NOT ( ISBLANK ( VT[Y] ) )
))
)
VAR Average_X1 =
AVERAGEX ( VT, VT[X1] )
VAR Average_X2 =
AVERAGEX ( VT, VT[X2] )
VAR Average_Y =
AVERAGEX ( VT, VT[Y] )
VAR Sum_X1_2 =
SUMX ( VT, (VT[X1] - Average_X1) ^ 2 )
VAR Sum_X2_2 =
SUMX ( VT, (VT[X2] - Average_X2) ^ 2 )
VAR Sum_X1Y =
SUMX ( VT, (VT[X1] - Average_X1) * (VT[Y] - Average_Y))
VAR Sum_X2Y =
SUMX ( VT, (VT[X2] - Average_X2) * (VT[Y] - Average_Y))
VAR X12 = 
SUMX( VT, (VT[X1] - Average_X1)*(VT[X2] - Average_X2))
VAR Beta1 =
DIVIDE (
Sum_X1Y*Sum_X2_2 - sum_x2y*X12,
Sum_X1_2*Sum_X2_2 - X12 ^ 2
) * A 
VAR Beta2 =
DIVIDE (
Sum_X2Y*Sum_X1_2 - sum_x1y*X12,
Sum_X1_2*Sum_X2_2 - X12 ^ 2
) * B
VAR Intercept =
Average_Y - Beta1 * Average_X1 - Beta2 * Average_X2
VAR Result = 
SUMX (
SUMMARIZE('Date','Date'[Date],'Date'[Month Number]),
Intercept + Beta1 * 'Date'[Date] + Beta2 * 'Date'[Month Number]
)
RETURN
Result * (1-1/R)

代码运行如下(注意:调整后的模型显示在深蓝色曲线中,RMSE 和 R 也被更改为该模型的评估值):

作者图片

(注:“α”用于调整第一个参数;“β”用于调整第二个参数)

到目前为止,我们已经完成了二元线性回归模型的手动优化,我们可以调整参数,然后通过观察 RMSE 和 R 来评估结果。

本文本该到此结束,但后来觉得这种手动参数调整有点盲目,我们应该对调整参数有一个直观科学的参考,以提高使用效率。所以我做了一个模型优化参考图,我们以第一个自变量(订单日期)为例,以它的参数为自变量(X 轴),R 和 RMSE 为因变量(Y 轴),这样,我们可以直观地为模型优化提供如下参考:

作者图片

(注:由于 RMSE 的值远大于 R 的值,上图中的 RMSE 值被压缩为原来的十分之一,以使 RMSE 和 R 更容易观察到。当然也可以考虑用两轴图)

摘要

到目前为止,我们已经知道如何使用 DAX 进行多元线性回归和模型优化。代码虽然长,但也不难理解。你可能会奇怪为什么我们不用 R 语言做线性回归?是的,在 PowerBI 中使用 R 语言进行回归更容易。但是 PowerBI 服务目前只支持部分 R 库,如果使用报表服务器版本,截至 2019 年 4 月,Power BI 服务(RS 版)不支持任何 R 库(包括用 R 语言开发的自定义 visual)。除非只需要使用桌面版,否则只能选择使用 DAX 来完成所有建模工作。另外,这是加深你对 DAX 和线性回归理解的好机会。

如果你有更好的方法用 PowerBI 实现时间序列预测,希望得到你的分享。

附:三元线性回归代码

Ternary Linear Regression = 
VAR VT =
FILTER (
SELECTCOLUMNS(
SUMMARIZE(
    ALLSELECTED('Date'),
    'Date'[Date],'Date'[Month Number],
    DateFactorTable[DateFactor]),
"VT[X1]", 'Date'[Date],
"VT[X2]", 'Date'[Month Number],
"VT[X3]", 'DateFactorTable'[DateFactor],
"VT[Y]",'Orders'[Qty]
),
AND (
NOT ( ISBLANK ( VT[X1] ) ),
AND(
NOT ( ISBLANK ( VT[X2] ) ),
AND(
NOT ( ISBLANK ( VT[X3] ) ),
NOT ( ISBLANK ( VT[Y] ) )
))
))
VAR N =
COUNTROWS ( VT )
VAR L11 = 
SUMX(VT,'VT'[X1]^2)-DIVIDE(SUMX(VT,'VT'[X1]^2),N)
VAR L22 = 
SUMX(VT,'VT'[X2]^2)-DIVIDE(SUMX(VT,'VT'[X2]^2),N)
VAR L33 = 
SUMX(VT,'VT'[X3]^2)-DIVIDE(SUMX(VT,'VT'[X3]^2),N)
VAR L12 = 
SUMX(VT,'VT'[X1]*'VT'[X2])-DIVIDE(SUMX(VT,'VT'[X1]*'VT'[X2]),N)
VAR L13 = 
SUMX(VT,'VT'[X1]*'VT'[X3])-DIVIDE(SUMX(VT,'VT'[X1]*'VT'[X3]),N)
VAR L32 = 
SUMX(VT,'VT'[X3]*'VT'[X2])-DIVIDE(SUMX(VT,'VT'[X3]*'VT'[X2]),N)
VAR L10 = 
SUMX(VT,'VT'[X1]*'VT'[Y])-DIVIDE(SUMX(VT,'VT'[X1]*'VT'[Y]),N)
VAR L20 = 
SUMX(VT,'VT'[X2]*'VT'[Y])-DIVIDE(SUMX(VT,'VT'[X2]*'VT'[Y]),N)
VAR L30 = 
SUMX(VT,'VT'[X3]*'VT'[Y])-DIVIDE(SUMX(VT,'VT'[X3]*'VT'[Y]),N)VAR D4 = 
L30*L12*L11*L22-L30*L12^3-L11*L22*L13*L20+L13*L20*L12^2-
L11*L20*L30*L22+L11*L20^2*L13+L12*L10*L30*L22-L12*L10*L13*L20
VAR D3 = 
L32*L13*L11*L22-L32*L13*L12^2-L12*L33*L11*L22+L12^3*L33-
L30*L22*L12*L13+L13^2*L20+L11*L32*L20*L22-L11*L32*L13*L20VAR BETA3 = 
DIVIDE(D4,D3)VAR D2 = 
(L12*L13-L11*L32)*BETA3+L11*L20-L12*L10
VAR D1 = 
L11*L22-L12^2VAR BETA2 = 
DIVIDE(D2,D1)VAR D0 = 
L30-BETA2*L32-BETA3*L33VAR BETA1 = 
DIVIDE(D0,L13)VAR Intercept =
AVERAGEX(VT,VT[Y]) - BETA1 * AVERAGEX(VT,VT[X1]) - 
BETA2 * AVERAGEX(VT,VT[X2]) - 
BETA3 * AVERAGEX(VT,VT[X3])
VAR Result = 
SUMX (
SUMMARIZE('Date','Date'[Date],'Date'[Month Number],
    DateFactorTable[DateFactor]),
Intercept + 
BETA1* 'Date'[Date] +
BETA2 * 'Date'[Month Number] + 
BETA3 * 'DateFactorTable'[DateFactor]
)
RETURN
RESULT

结束~

在 R 中构建和测试股票投资组合

原文:https://towardsdatascience.com/building-and-testing-stock-portfolios-in-r-d1b7b6f59ac4?source=collection_archive---------11-----------------------

利用数据科学做出更明智的投资决策

照片由 Unsplash 上的 Aditya Vyas 拍摄

在本文中,我们将研究如何获取股票数据,分析数据以做出投资决策,并将结果可视化。

随着最近进入市场的散户投资者激增,新交易员拥有通过分析股票长期表现来比较股票的工具比以往任何时候都更重要。在本帖中,我们将使用三家熟悉的公司——星巴克、嘉年华和苹果——的股票数据来构建一个投资组合,考察其历史表现,并将其与标准普尔 500 指数进行比较。

安装库

R 中三个非常有用的财务分析包是quantmod,从雅虎财经中提取股票数据;PerformanceAnalytics,构建和测试投资组合;和dygraphs,来制作我们数据的交互式和信息可视化。如果您没有安装这些包,您可以使用下面的代码将它们安装并加载到您的 R 环境中。

安装和加载所有必需的软件包

编写一个计算股票收益的函数

一旦我们安装并加载了软件包,我们就可以编写一个函数来获取我们个股的月度回报数据。我们的函数有两个参数:ticker,股票的符号,和base_year,我们想要开始分析数据的年份。

使用雅虎财经数据计算股票月回报率的函数

如果您不熟悉 R 及其软件包,这个函数可能会显得有点复杂。下面是每一行的解释:

  • 第 4 行: 将我们的股票代码传递给getSymbols()函数,以从 Yahoo Finance 获取我们的股票数据

  • 第 10–12 行: 使用 R 的内置paste0()Sys.Date()函数来创建一个字符串,我们可以在括号之间传递该字符串,以便只选择我们的基准年和今天日期之间的观测值

  • 第 15 行: 计算我们调整后的收盘价数据的月度算术回报

  • 第 18 行: 将我们的月度回报数据分配给 R 的全球环境,以确保我们以后可以通过其股票代码访问它

使用我们的函数并可视化返回结果

写完函数后,现在我们可以计算三只股票的月收益率了。我们需要调用我们的函数四次——一次针对我们想要分析的每只股票,一次针对标准普尔 500——这样我们就有了一个基准来判断每只股票的月度表现。然后,我们可以将我们所有的月度回报合并到一个时间序列对象中,并使用dygraphs软件包查看每个对象最近几年的表现(点击这里查看交互式版本)。最后一行打印过去五个月的返回数据,其输出如下所示。

获取单个股票数据,将其合并,并生成可视化结果

为我们的三只股票和标准普尔 500 绘制月收益图

我们新创建的 returns 对象的最后五行

分析投资组合构成

从我们的returns数据集,我们可以了解到每只股票在过去几年中相对于标准普尔 500 指数的表现。例如,2020 年 3 月,当新冠肺炎蔓延的消息搅动金融市场时,标准普尔 500 指数下跌约 12.5%,而嘉年华公司当月市值损失超过 60%。相比之下,苹果的亏损只有 7%左右。使用corrplot::corrplot(cor(returns), method = number)生成一个关联矩阵来表示这些股票的收益如何相互关联。

使用 corrplot 包生成的相关矩阵

投资组合管理的一个基本原则是,你应该选择彼此相关性低的股票。你不会希望你投资组合中的所有股票总是一起涨跌——这可能会让你面临你可能想要避免的过度波动,特别是如果这是一个退休账户,其中保本是你的主要关注点。由高度相关的股票组成的投资组合会受到非系统性风险的影响,这种风险源于每只股票固有的公司特有风险。

从我们的相关矩阵中,我们观察到我们所有的股票都是正相关的,尽管程度不同。苹果和星巴克的相关性很弱(0.27),但是嘉年华和标准普尔 500 的相关性很高(0.71)。重要的是,我们所有的股票都与市场有着相当高的正相关性,这意味着它们在大多数月份都会随市场波动。

构建我们的投资组合并评估绩效

我们可以使用PerformanceAnalytics软件包为我们的股票分配权重,并根据它们建立一个假设的投资组合。在下面的代码中,我们假设我们将三分之一的资金投资于星巴克,三分之一投资于嘉年华,三分之一投资于苹果,暂时不包括 S & P 500。Return.portfolio()函数允许我们从returns对象传入我们的个股数据以及它们的权重。我们可以将wealth.index参数设为 TRUE,以显示 2015 年投资于我们投资组合的 1 美元将如何随时间增长。然后,我们可以对标准普尔 500 遵循同样的过程,排除weights论点。在将我们的数据合并到一个xts对象中后,我们可以使用另一个图表将我们的投资组合与标准普尔 500 指数进行对比(交互式版本此处为):

构建我们的投资组合

将我们的投资组合与基准进行比较

如果我们在 2015 年初向我们的星巴克-嘉年华-苹果投资组合投资 1 美元,并且没有动它(即,没有重新平衡),我们的投资组合价值将几乎翻倍(92.31%)。这超过了标准普尔 500 指数的表现,该指数在同一时期的回报率仍高达 73%左右。当然,我们可以对我们的代码做一些小的调整,以改变投资组合的权重,增加额外的股票(或其他资产,如政府债券和贵金属),并尝试重新平衡。有了 R 和它的库,我们构建和测试投资组合的能力实际上是无限的。

结论

我希望我已经让获取和分析股票数据的过程不那么可怕了。使用上面的代码,我们能够导入大量的财务信息,构建投资组合,检查其组成,并分析相对于基准的历史表现。在以后的文章中,我将探索使用 R 及其库分析财务数据的其他方法。

感谢阅读!

关于我

我叫克里斯蒂安·金凯,在马里兰大学帕克分校学习金融和经济学。如果你喜欢这个帖子,可以考虑一下 跟随我 上媒!

用 Python 构建 Android 应用程序:第 1 部分

原文:https://towardsdatascience.com/building-android-apps-with-python-part-1-603820bebde8?source=collection_archive---------1-----------------------

使用 Python 构建 Android 应用程序的分步指南

介绍

Unsplash 上由 Hitesh Choudhary 拍摄的照片

你是否对开发 android 应用但 Java 不是你的伴侣感到好奇?Android universe 主要是使用 Java、Kotlin、Flutter 和 Corona 使用 Lua 脚本语言(主要是游戏引擎,用于愤怒的小鸟等游戏)构建的,但最近,Python 已经进入了每个领域,Android 也不例外。在这一系列文章中,我们将看看如何设置所需的环境,开发 android 应用程序的基础,参考文档,以及如何推进您的项目。

基维——教父

Python 中的 Android 开发之所以成为可能,仅仅是因为一个用于开发移动应用和其他多点触控应用软件的开源 Python 库,这个库就是 Kivy。2011 年首次发布,2019 年稳定发布!Kivy 不仅支持 android 应用程序开发,而且其应用程序可以在 IOS、Linux、OS X、Windows 和 Android 上运行。它是用 Python 和 Cython 编写的,大部分核心开发人员来自俄罗斯。

我们将在应用程序的前端大量使用 Kivy,但使用另一个包,我们为什么需要这个包将很快被涵盖。

设置环境

为新项目建立一个新的环境通常是一个好的实践,如下所示:

  1. 它有助于维护不同库的不同版本。例如,ML flow 需要较低版本的 Numpy,当您尝试在基本目录中安装 ML flow 时,它会与预安装的库冲突,并使管理不同版本变得困难。
  2. 有助于隔离定制代码,并使在任何平台上部署应用程序变得更加容易。

我使用 Conda 包管理器来创建和管理我的环境。您可以使用任何其他的包管理器,但是为了跟随我,您可以使用 Conda(参见这个指南来设置 Conda 和 anaconda )。打开 Conda 终端,键入:

conda create -n name-of-env python=version

将“环境名称”替换为您的自定义名称和您选择的“版本”,但要大于 3.5。我会用 Python 3.7。要列出所有 Conda 环境,请键入:

conda info --envs

输出如下所示:

作者图片

这里是 Conda 备忘单的链接,如果你有兴趣了解更多的话。现在,检查完这里的名称后,像这样激活环境:

conda activate nameofenv

我们准备安装所需的库。当我们使用 python 时,pip 是安装和管理 python 包的好方法。要安装 Kivy 及其依赖项,请逐个键入以下命令:

pip install kivy
pip install kivy-deps.angle
pip install kivy-deps.glew
pip install kivy-deps.gstreamer
pip install kivy-deps.sdl2

一个额外的提示:创建一个名为 requirements.txt 的文件,将上面几行复制到该文件中,将该文件放在一个已知的位置并运行终端:

pip install requirements.txt

它将一次性安装所有的依赖项!

我们准备开发一些很棒的应用程序,但是这里有一个问题。一开始,我告诉过你我们需要一个额外的包来和 Kivy 一起使用。现在只需安装它,我们将在文章的后面讨论为什么。

pip install kivymd

我们来编码吧!

你期待已久的部分来了。我使用 Pycharm IDE 进行编码,因为它对我来说很容易,但是您可以根据自己的意愿使用 VSCode、Sublime 或 spyder。在开始之前,我们需要了解以下几点:

  1. 一个 android 应用程序有一个前端(UI/UX)或交互部分,用户在那里与你的应用程序交互,所有的输入都通过这一层给出。
  2. 输入被传送到后端层,是我们的 python 代码。这个后端层控制流、处理输出和屏幕上显示的内容。
  3. 在这里,面向对象编程被大量使用,大部分编程将使用这一概念来完成,所以如果你缺乏这方面的知识,那么我建议你观看 Keith Galli 在 OOP 上的视频

现在,快速打开您的 IDE,从 Hello World 的基本代码开始!

运行该程序后,您将看到以下输出:

首次输出

让我一行一行地解释这段代码:

  1. 第一行从 Kivy 库中导入基础应用程序。
  2. 现在,我们需要在屏幕上显示一些文本,为了显示任何文本,我们使用标签功能,正如我所说,这些是 UI/UX 的东西,我们将从“kivy.uix”目录中导入它们。
  3. 代码的第三行(忽略空白)是我们的 OOP 概念发挥作用的地方。我们从“kivy.app”导入的应用程序是该应用程序的基类。这意味着 Kivy 设置了运行我们的应用程序的所有必要的东西,我们需要继承这个应用程序类,改变它,并构建我们的应用程序。这个类的名字应该以大写字母开头,它也可以作为应用程序的名字,以后可以更改,所以你可以给它起任何你想要的名字。
  4. def 构建功能是 app 的切入点。这里定义的所有东西都将首先构建,第一个屏幕或主屏幕在这里传递。它返回标签,并且它有一个值为“Hello World”的文本属性。点击阅读更多关于标签的信息。
  5. 然后最后一行调用这个主类并运行它。

基维的问题

这就是我们如何构建我们的第一个应用程序,但你有没有注意到一件事,背景自动是黑色的,文本是白色的?我甚至没有在代码中提到这一点。Kivy 默认取。现在我们进入有趣的部分,让我们用 Kivy 构建一个简单的没有增强的按钮:

它的输出如下:

一个简单的按钮在基维

这是一个非常没有吸引力的外观,想象你正在使用一个有这样界面的应用程序。我会卸载该应用程序,甚至不会评价它!在 Kivy 中增强特性是一个乏味的过程,需要大量代码。不相信我?查看代码,创建一个放置在中心的矩形平面按钮,带有蓝色边框、蓝色文本和白色背景:

使用 Kivy 的矩形扁平按钮。来源:阿特雷·巴特

不要理解代码,因为这已经超出了您的范围,现在只需看看输出:

作者图片

现在看起来是不是很好看!

Kivymd 简介

现在我们已经谈了很多关于 Kivy 的内容,我们知道它提供了构建应用程序的平台。KivyMD 是一个材质设计兼容小部件的集合,可以与 Kivy 一起使用,并且尽可能接近 Google 的材质设计规范,而不会牺牲易用性或应用程序性能。它基于 Kivy,更容易编码。它与 Kivy 非常相似,只是在每个元素和小部件的开始处添加了 MD,此外它还有各种各样的其他新元素。现在看看 Kivymd 中生成相同输出按钮的代码:

尝试运行这个脚本,您将看到与长 Kivy 代码返回的输出相同的输出。

更多需要考虑的事情

  1. 性能问题:你将开发的应用程序在你的本地机器上可以完美运行,但当你试图在 android 上运行它时,动画就不那么流畅了。此外,因为它仍然作为 python 的实例运行,所以速度很慢。
  2. 转换为 android: 其中一个主要任务是将 python Kivy 应用程序转换为 android 包(APK),这只能在 Linux 操作系统上完成。像 python-to-android、Android SDK、推土机这样的附加包很重,需要大量时间来构建和调试应用程序。所有的 Kivy 和 Kivymd 库都被转换成 Cython,这些被 Android 系统使用。这个过程通常以高精度完成。
  3. 仍在开发中:这是一个开源项目,仍有大量工作在进行中。总是尝试更新软件包,这样你就不会面临任何问题。

结论和下一步

这是用 python 构建 android 应用程序的入门课。我们学习了什么是 Kivy,为什么,以及如何使用环境,用 Kivy 构建了一个基本的应用程序,用一个按钮代码的例子比较了 Kivy 和 Kivymd。在下一篇文章中,我们将继续我们的旅程,探索 Kivymd 中的各种其他关键元素。如果你喜欢这篇文章,请在 medium 上关注我,这样你就会收到关于即将发布的部分的通知。说完了,再见!

你可以在这里找到我:
LinkedIn: 链接

GitHub: 链接

更新:第二部分在这里:

[## 用 Python 构建 Android 应用程序:第 2 部分

使用 Python 构建 Android 应用程序的分步指南

medium.com](https://medium.com/swlh/building-android-apps-with-python-part-2-1d8e78ef9166)

用 Python 构建 Android 应用程序:第 3 部分(结论)

原文:https://towardsdatascience.com/building-android-apps-with-python-part-3-89a455ee7f7c?source=collection_archive---------8-----------------------

使用 Python 构建 Android 应用程序的分步指南

在前两部分中,我们已经看到了 Kivy 和 Kivymd 如何让使用 Python 开发应用程序变得超级容易,尽管它也有缺点。我们已经介绍了应用程序开发的基础知识,如何显示文本、接受输入以及使用按钮使我们的应用程序具有交互性。我们还看到了 Kivy 支持的各种 UI/UIX 元素,以及如何使用 Kivy 字符串构建器轻松实现它们,Kivy 字符串构建器以分层格式编写,不需要任何显式的导入语句。如果你没有读过前面的部分,我建议你看一看,以便更好地理解。

[## 用 Python 构建 Android 应用程序:第 2 部分

使用 Python 构建 Android 应用程序的分步指南

medium.com](https://medium.com/swlh/building-android-apps-with-python-part-2-1d8e78ef9166) [## 用 Python 构建 Android 应用程序:第 1 部分

使用 Python 构建 Android 应用程序的分步指南

towardsdatascience.com](/building-android-apps-with-python-part-1-603820bebde8)

在这一部分中,我们将涵盖 Kivymd 中所有剩余的常用元素,在另一部分中,我们将构建我们的 capstone 应用程序,该应用程序将使用 weather API 获取天气相关信息,将该应用程序转换为 Android APK,并将其部署在在线平台上。这不是很酷吗?

迈克·什切潘斯基在 Unsplash 上的照片

MDDialog

上次我们构建了一个基本的应用程序,它将密码作为输入,与我们的关键字“root”进行比较,并在屏幕上以文本形式显示成功或失败的消息。如果弹出一个对话框,不仅显示操作的结果,还给出更多的选项,该怎么办?这种类型的功能可以在 MDDialog 的帮助下完成。让我们看看它的实现:

查看进行比较时对话框是如何调用的

输出如下所示:

作者 GIF

可以在对话框中修改各种属性,如:

  1. 各种类型的事件,包括 on_pre_open、on_open、on _ pre _ dissolve、on _ dissolve
  2. 对话框的“标题”。在示例中,密码检查是框的标题。
  3. 框的“文本”。这里我们以成功或失败为文本。
  4. 控制盒子的“半径”,使其为圆形、椭圆形。
  5. 添加按钮,列表,使它成为一个可以接受输入的表单,等等。

点击阅读更多关于这些功能的信息。

MDList(一行、两行和三行列表项目)

列表是应用程序开发中最常用的实体之一。它是带有图像的文本的垂直连续索引。MDList 与滚动视图和列表项相结合,构成一个功能列表。Kivymd 中有不同类型的列表项,从 OneLineListItem 到 ThreeLineAvatarIconListItem。以下示例涵盖了列表中所有类型的项目:

看看它的输出:

所有不同类型的列表项

头像和图标之间的基本区别是,图标大多是预定义的,免费提供,它们不需要通过源代码明确引用。头像更多的是一个被引用到个人资料的图像,虽然它也可以用在列表项目中。这些项目包含可以使用 MDLabel 的文本属性进行修改的文本。

MDDataTables

数据表用于以行和列的形式显示表格数据。这是 Kivymd 提供的一个简单而强大的工具。它可以与按钮或任何其他动作相链接。行项目可以与复选框相结合,以表格格式制作待办事项列表。您可以添加分页,控制要显示的行数。让我们检查它的代码:

有趣的结果是:

作者 GIF

MDToolbar

你们都已经看到在 android 应用程序中,我们有一个顶部剥离的部分,在那里应用程序的名称被提到,在左边有三行,在右边有三个点。它被称为工具栏,在应用程序中,让它更容易访问和组织不同的操作是非常重要的。像往常一样,Kivymd 带有选项,这个也有两个选项:顶部工具栏和底部工具栏。两者都有各自的优势和特点,但是在这个系列中,我们将关注顶部工具栏。如果您想了解底部工具栏的更多信息,请点击此链接。一个简单的工具栏编码如下:

作者图片

现在,如果你点击 3 点菜单,它会崩溃的应用程序,因为它需要一个回调。回调定义了应该对特定事件采取什么操作。要做一个功能菜单,我们需要一个叫导航抽屉的东西,我们来看看是什么。

MDNavigationDrawer

这就是事情变得有趣的地方。现在,我们终于可以在我们的应用程序中构建一个包含不同部分的菜单来浏览了。导航抽屉使我们可以在应用程序中访问不同的目的地。在我展示代码解释之前,这里还有一个概念。Kivymd 提供了不同类型的布局。布局是一种可以安排所有元素的蓝图。MDToolbar 通常放置在 BoxLayout 中。点击了解更多关于布局的信息。现在仔细看看这段代码:

作者 GIF

在这里,我将 MDToolbar 放在了一个 BoxLayout 中,而在 MDNavigationDrawer 中,我使用了一个 AnchorLayout,这样我就可以在菜单栏中有一个简介部分。在这一部分之后,我们有了包含两个目的地的 MDList,Home 和 About。现在我们已经在 android 应用程序的旅程中走了这么远,让我们看看如何使用不同的屏幕并使用相同的 MDNavigation bar。

切换屏幕(屏幕管理器)

顾名思义,ScreenManager 是用来管理我们希望包含在应用程序中的所有屏幕的。它用一个唯一的 id 保存所有的屏幕,这个 id 可以用来在动作上切换屏幕。为了在所有屏幕中使用相同的工具栏,我们将把屏幕管理器和 NavigationDrawer 放在 NavigationLayout 中。现在,如果我们转到导航栏并点击“关于”或“信息”,什么也不会发生,但我们可以将其绑定到切换屏幕。只需参考屏幕管理器的 id,将当前屏幕更改为您想要的屏幕。请看这里的例子:

作者 GIF

结论和下一步

在本系列的这一部分中,我们讨论了应用程序开发中使用的更多元素。我们学习了如何使用对话框使应用程序交互,如何创建工具栏,以及在特定的动作/事件上切换屏幕。在接下来的一篇文章中,我将制作一个应用程序,将其转换为 APK 并部署在云平台上,请继续关注那篇文章。如果您喜欢这个开发系列,请务必在 medium 上关注我,这样您就可以收到关于即将发布的文章的通知,如果您有任何疑问,请发表评论,就这样说吧,再见!

LinkedinGithub

更新:Kivy apps 的转换/部署文章现已上线!

[## 将 Python 应用程序转换为 APK 的 3 种方法

结束构建 Python 系列的 Android 应用程序!

towardsdatascience.com](/3-ways-to-convert-python-app-into-apk-77f4c9cd55af)

在稀疏的热编码数据上构建自动编码器

原文:https://towardsdatascience.com/building-autoencoders-on-sparse-one-hot-encoded-data-53eefdfdbcc7?source=collection_archive---------14-----------------------

维罗妮卡·贝纳维德斯在 Unsplash 上的照片

适用于在 PyTorch 中嵌入稀疏独热编码数据的损失函数的实践回顾

自 1986 年引入以来[1],通用自动编码器神经网络已经渗透到现代机器学习的大多数主要部门的研究中。自动编码器已经被证明在嵌入复杂数据方面非常有效,它提供了简单的方法将复杂的非线性依赖关系编码成简单的矢量表示。但是,尽管它们的有效性已经在许多方面得到了证明,但它们通常不能再现稀疏数据,尤其是当列像热编码一样相关时。

在这篇文章中,我将简要讨论一个热编码(OHE)数据和通用自动编码器。然后,我将介绍使用自动编码器对一个热编码数据进行训练所带来的问题的用例。最后,我将深入讨论重建稀疏 OHE 数据的问题,然后讨论我发现在这些条件下工作良好的 3 个损失函数:

  1. 共同嵌入损失
  2. 索伦森-戴斯系数损失
  3. 个体 OHE 成分的多任务学习损失

—解决上述挑战,包括在 PyTorch 中实现它们的代码。

一个热编码数据

One hot 编码数据是一般机器学习场景中最简单但经常被误解的数据预处理技术之一。该过程将具有‘N’个不同类别的分类数据二进制化为 N 列二进制 0 和 1,其中在第‘N’个类别中出现 1 表示观察值属于该类别。使用sci kit-Learn onehotencode 模块,这个过程在 Python 中很简单:

**from** sklearn.preprocessing **import** OneHotEncoder
**import** numpy **as** np# Instantiate a column of 10 random integers from 5 classes
x = **np.random.randint**(5, size=10).reshape(-1,1)**print**(x)
>>> [[2][3][2][2][1][1][4][1][0][4]]# Instantiate OHE() + Fit/Transform the data
ohe_encoder = **OneHotEncoder**(categories="auto")
encoded = ohe_encoder.**fit_transform**(x).**todense**()**print**(encoded)
>>> matrix([[0., 1., 0., 0., 0.],
           [0., 0., 0., 1., 0.],
           [0., 0., 1., 0., 0.],
           [0., 0., 0., 1., 0.],
           [0., 0., 1., 0., 0.],
           [1., 0., 0., 0., 0.],
           [0., 0., 1., 0., 0.],
           [0., 0., 1., 0., 0.],
           [0., 0., 0., 1., 0.],
           [0., 0., 0., 0., 1.]])**print**(**list**(ohe_encoder.**get_feature_names()))** >>> ["x0_0", "x0_1", "x0_2", "x0_3", "x0_4"]

虽然简单,但是如果你不小心的话,这种技术会很快变质。它很容易给数据增加额外的复杂性,并改变某些数据分类方法的有效性。例如,转换为 OHE 向量的列现在是相互依赖的,这种交互使得在某些类型的分类器中很难有效地表示数据的各个方面。例如,如果您有一个包含 15 个不同类别的列,则需要一个深度为 15 的决策树来处理这个热编码列中的 if-then 模式。如果你感兴趣,可以在这里找到这些问题的一个很好的例子。类似地,由于列是相互依赖的,如果您使用带有 bagging(Bootstrap Aggregating)的分类策略并执行特征采样,您可能会完全错过独热编码列,或者只考虑其部分组件类。

自动编码器

自动编码器是无监督的神经网络,用于将数据嵌入到有效的压缩格式中。它通过利用编码和解码过程将数据编码成更小的格式,然后将该更小的格式解码回原始输入表示来实现这一点。通过获取模型重建(解码)和原始数据之间的损失来训练模型。

A. Dertat 在他的 TDS 文章中提供:应用深度学习——第 3 部分:自动编码器

实际上用代码表示这个网络也很容易做到。我们从两个函数开始: 编码器 模型,和 解码器 模型。这两个“模型”都被打包到一个名为 Network 的类中,该类将包含我们培训和评估的整个系统。最后,我们定义了一个函数 Forward ,PyTorch 使用它作为进入网络的入口,该网络包装了数据的编码和解码。

**import** torch
**import** torch.nn **as** nn
**import** torch.nn.functional **as** F
**import** torch.optim **as** optim**class** **Network**(nn.Module):
   **def** **__init__**(self, input_shape: int):
      **super**().**__init__**()
      **self**.**encode1** = nn.**Linear**(input_shape, 500)
      **self**.**encode2** = nn.**Linear**(500, 250)
      **self**.**encode3** = nn.**Linear**(250, 50)

      **self**.**decode1** = nn.**Linear**(50, 250)
      **self**.**decode2** = nn.**Linear**(250, 500)
      **self**.**decode3** = nn.**Linear**(500, input_shape) **def** **encode**(**self**, x: torch.Tensor):
      x = F.**relu**(**self**.**encode1**(x))
      x = F.**relu**(**self**.**encode2**(x))
      x = F.**relu**(**self**.**encode3**(x))
      **return** x **def decode**(**self**, x: torch.Tensor):
      x = F.**relu**(**self**.**decode1**(x))
      x = F.**relu**(**self**.**decode2**(x))
      x = F.**relu**(**self**.**decode3**(x))
      **return** x **def** **forward**(self, x: torch.Tensor):
      x = **encode**(x)
      x = **decode**(x)
      **return** x **def** **train_model**(data: pd.DataFrame):
   net = **Network**()
   optimizer = optim.**Adagrad**(net.parameters(), lr=1e-3, weight_decay=1e-4)
   losses = [] **for** epoch **in** **range**(250):
     **for** batch **in** **get_batches**(data)
        net.**zero_grad**()

        # Pass batch through 
        output = **net**(batch)

        # Get Loss + Backprop
        loss = **loss_fn**(output, batch).sum() # 
        losses.**append**(loss)
        loss.**backward**()
        optimizer.**step**()
     **return** net, losses

正如我们在上面看到的,我们有一个编码函数,它从输入数据的形状开始,然后随着它向下传播到 50 的形状,减少它的维数。从那里,解码层获得嵌入,然后将其扩展回原始形状。在训练中,我们从解码器获取重构,并获取重构相对于原始输入的损失。

损失函数的问题

现在我们已经介绍了自动编码器的结构和 One Hot 编码过程,我们终于可以谈谈在自动编码器中使用 One Hot 编码的相关问题,以及如何解决这个问题了。当自动编码器将重构与原始输入数据进行比较时,必须对建议的重构与真实值之间的距离进行一些评估。通常,在输出的值被认为彼此不相交的情况下,将使用交叉熵损失或 MSE 损失。但是在我们的 One Hot 编码的情况下,有几个问题使得系统更加复杂:

  1. 一列中出现一意味着其对应的 OHE 列中必须有一个零。 即列不相交
  2. OHE 向量输入的稀疏性会导致系统 选择简单地为大多数列返回 0 的 以减少误差

这些问题结合起来导致上述两个损失(MSE,交叉熵)在重建稀疏 OHE 数据时无效。下面我将介绍三个损失,它们提供了上述一个或两个问题的解决方案,以及在 PyTorch 中实现它们的代码:

余弦嵌入损失

余弦距离是一种经典的向量距离度量,通常在比较 NLP 问题中的单词包表示时使用。距离的计算方法是找出两个向量之间的余弦角,计算公式如下:

作者图片

这种方法被证明在量化稀疏 OHE 嵌入的重构中的误差方面是很好的,因为它能够考虑各个列中二进制值的偏差来评估两个向量的距离。这个 loss 是 PyTorch 中最容易实现的,因为它在torch . nn . cosinembeddingloss中有一个预构建的解决方案

loss_function = torch.nn.**CosineEmbeddingLoss**(reduction='none')# . . . Then during training . . . loss = **loss_function**(reconstructed, input_data).sum()
loss.**backward**()

骰子损失

骰子损失是索伦森骰子 系数【2】的一种实现,在分割任务的计算机视觉领域非常流行。简单地说,它是两个集合之间重叠的度量,并且与两个向量之间的 Jaccard 距离有关。dice 系数对向量中列值的差异高度敏感,并且在图像分割中很流行,因为它利用这种敏感性来有效地区分图像中的像素边缘。骰子损失遵循以下等式:

作者图片

想了解更多关于索伦森骰子系数的信息,你可以看看杜的这篇博文

PyTorch 没有内部实现的骰子系数 。但是可以在 Kaggle 的损失函数库——Keras&py torch[3]中找到一个很好的实现:

class **DiceLoss**(nn.Module):
    def __init__(self, weight=None, size_average=True):
        super(DiceLoss, self).__init__()

    def **forward**(self, inputs, targets, smooth=1):

        *#comment out if your model contains a sigmoid acitvation*
        inputs = F.**sigmoid**(inputs)       

        *#flatten label and prediction tensors*
        inputs = inputs.view(-1)
        targets = targets.view(-1)

        intersection = (inputs * targets).sum()                            
        dice = (2.*intersection + smooth)/
               (inputs.sum() + targets.sum() + smooth)  

        return 1 - dice

不同 OHE 列的个体损失函数

最后,您可以将每个热编码列视为其自己的分类问题,并计算每个分类的损失。这是一个多任务学习问题的用例,其中自动编码器正在解决重建输入向量的各个分量。当您的输入数据中有几个/全部 OHE 列时,这种方法最有效。例如,如果您有一个编码列,前七列是 7 个类别:您可以将其视为多类别分类问题,并将损失视为子问题的交叉熵损失。然后,您可以将子问题的损失组合在一起,并将其作为整个批次的损失向后传递。

作者图片

下面你会看到这个过程的一个例子,有三个 One Hot 编码列,每个列有 50 个类别。

from torch.nn.modules import _Loss
from torch import argmaxclass **CustomLoss**(_Loss):
  **def** **__init__**(self):
    **super**(CustomLoss, self).__init__() def **forward**(self, input, target):
    """ loss function called at runtime """

    # Class 1 - Indices [0:50]
    class_1_loss = F.**nll_loss**(
        F.**log_softmax**(input[:, 0:50], **dim**=1), 
        **argmax**(target[:, 0:50])
    ) # Class 2 - Indices [50:100]
    class_2_loss = F.**nll_loss**(
        F.**log_softmax**(input[:, 50:100], **dim**=1), 
        **argmax**(target[:, 50:100])
    ) # Class 3 - Indices [100:150]
    class_3_loss = F.**nll_loss**(
        F.**log_softmax**(input[:, 100:150], **dim**=1), 
        **argmax**(target[:, 100:150])
    ) **return** class_1_loss **+** class_2_loss **+** class_3_loss

在上面的代码中,您可以看到如何对重构输出的子集进行单个损失,然后在最后合并为一个总和。这里我们使用一个 负对数似然损失 ( nll_loss ),它对于多类分类方案是一个很好的损失函数,并且与 交叉熵损失相关。

谢谢大家!

在本文中,我们浏览了 One Hot Encoding 分类变量的概念以及自动编码器的一般结构和目标。我们讨论了一个热编码向量的缺点,以及尝试在稀疏的一个热编码数据上训练自动编码器模型时的主要问题。最后,我们讨论了解决稀疏热编码问题的 3 个损失函数。尝试训练这些网络没有更好或更坏的损失,在我介绍的功能中,没有办法知道哪一个适合您的用例,直到您尝试它们!

下面我已经包括了一堆深入到我上面讨论的主题的资源,以及一些我提出的损失函数的资源。

来源

  1. D.E. Rumelhart,G.E. Hinton 和 R.J. Williams,“通过错误传播学习内部表征”并行分布式处理。第一卷:基础。麻省理工学院出版社,剑桥,麻省,1986 年。
  2. 特伦森(1948 年)。“在植物社会学中基于物种相似性建立等幅群的方法及其在丹麦公共地植被分析中的应用”。 孔格里格丹斯克5(4):1–34。 骰子,李 R. (1945)。“物种间生态关联数量的度量”。生态学26(3):297–302。
  3. Kaggle 的损失函数库:https://www . ka ggle . com/bigiron sphere/Loss-Function-Library-keras-py torch

提到的其他有用资源

  1. OHE 数据的问题:https://towards Data science . com/one-hot-encoding-is-making-your-tree-based-ensembles-worse-heres-why-d64b 282 b 5769
  2. 装袋背景:https://towards data science . com/ensemble-methods-Bagging-boosting-and-stacking-c 9214 a 10 a 205
  3. 关于骰子系数的一篇大文章:https://medium . com/ai-salon/understanding-Dice-loss-for-crisp-boundary-detection-bb 30 C2 e 5 f 62 b

使用 Matplotlib 构建条形图

原文:https://towardsdatascience.com/building-bar-charts-using-matplotlib-c7cf6db3e728?source=collection_archive---------7-----------------------

图片鸣谢:https://www . can va . com/design/DADvjWtEzNo/snhmmyko 16 hhxicldcexxq/edit?category=tACFakiSKJ8

如何使用条形图展示数据的指南

条形图是一种数据可视化技术,可用于表示数据集中的数值,以显示不同数据点之间的差异。它使用一根棒作为量值的度量。条形越大,数字越高。例如,如果一个人在比较财富,条形图中较大的条形比较小的条形意味着更多的钱。

使用 Jupyter 笔记本构建条形图的分步指南

加载数据集

英超联赛 2019/20 赛季中期最高进球得分数据集是使用 IBM Watson Studio 上的 Jupyter Notebook 加载的。

英超联赛 2019/20 赛季中期前五名射手

导入数据可视化包

为了运行将生成条形图的代码,导入了一个名为 matplotlib 的数据可视化包。下面是一组用于导入条形图的代码。python 编程语言中使用 Matplotlib 作为绘图库。

*## Import data visualization packages*
**import** **matplotlib.pyplot** **as** **plt**
%matplotlib inline

创建条形图

条形图是在安装包后通过运行下面的代码集构建的。

plt.bar("Player", "Stat", data = df_goal, color = "blue")
plt.xlabel("Players")
plt.ylabel("Goal Scored")
plt.title("Highest goal scorers in the Premier league 2019-20 by mid-season")
plt.show()

plt.bar("Player "," Stat ",data = df_goal,color = "blue ")用于表示希望绘制一个条形图,使用 Player 列作为 x 轴,Stat 列作为 y 轴,使用 df_goal 数据集,并且条的颜色为蓝色。plt.xlabel(“球员”)和 plt.ylabel(“进球”)分别用于标注 x 轴和 y 轴。plt.title(“英超联赛 2019-20 赛季中期最高进球者”)用于为图表制作标题。plt.show()用于使用前面的命令生成图形。

上面运行的代码的结果可以在下面看到。

显示英超 2019/20 赛季中期进球最多球员的条形图。

在上面的条形图中可以看到,x 轴刻度无法正常显示。因此,x 轴记号在旋转后会变得可见。下面是一组修改条形图的代码。

plt.bar("Player", "Stat", data = df_goal, color = "blue")
plt.xlabel("Players")
plt.xticks(rotation = 90)
plt.ylabel("Goal Scored")
plt.title("Highest goal scorers in the Premier league 2019-20 by mid-season")
plt.show()

通过添加 plt.xticks(旋转= 90°),条形图可以转换为:

一个改进的条形图显示了英超 2019/20 赛季中期的最高进球得分者。

这是一个更清晰的条形图,x 轴刻度现在垂直且可见。

创建水平条形图

也可以通过将前一组代码中的 plt.bar 更改为 plt.barh 来构建水平条形图。

plt.barh("Player", "Stat", data = df_goal, color = "red") plt.xlabel("Players") 
plt.ylabel("Goal Scored") 
plt.title("Highest goal scorers in the Premier league 2019-20 by mid-season")
plt.show()

显示英超 2019/20 赛季中期最高进球得分者的水平条形图。

条形图和水平条形图都可以修改,看起来更漂亮。

美化条形图

可以通过运行下面的代码集来修改条形图。

df_goal2 = df_goal[['Player', 'Stat']].sort_values(by = 'Stat', ascending = **True**)
ind = df_goal2.set_index("Player", inplace = **True**)
*## A modified bar graph*
bar = df_goal2.plot(kind='bar',figsize=(30, 16), color = "blue", legend = **None**)
bar
plt.yticks(fontsize = 24)
plt.xticks(ind,fontsize = 18)
plt.xlabel("Players", fontsize = 20)
plt.ylabel("Goal scored", fontsize = 20)
plt.title("Higehst goal scorers in the premier league mid-season of 2019/20", fontsize=32)
bar.spines['top'].set_visible(**False**)
bar.spines['right'].set_visible(**False**)
bar.spines['bottom'].set_linewidth(0.5)
bar.spines['left'].set_visible(**True**)
plt.show()

与构建条形图的常规代码相比,增加了一些内容。从上面可以看出,使用新形成的数据集 df_goal2 创建了一个名为 bar 的对象,用代码 bar = df_goal2.plot(kind='bar ',figsize=(30,16),color = "blue ",legend = None )设计了一个条形图。ind 用于存储将由 x 轴表示的索引。

顶部和右侧的书脊被移除,但底部和左侧的书脊仍然可见,使用代码:bar.spines['top']。set_visible( False ),bar.spines['右']。set_visible( False ),bar.spines['bottom']。set_linewidth(0.5)和 bar.spines['left']。set_visible( True )。

这些代码的结果可以在下面看到。

一个漂亮的条形图,显示了英超 2019/20 赛季中期的最高进球得分者。

美化水平条形图

美化条形图的相同过程适用于水平条形图。下面是构建美化版的一组代码。

*## A modified horizontal bar graph*
barh = df_goal2.plot(kind='barh',figsize=(30, 16), color = "red", legend = **None**)
barh
plt.yticks(fontsize = 24)
plt.xticks(ind,fontsize = 18)
plt.xlabel("Goal scored", fontsize = 22)
plt.ylabel("Players", fontsize = 22)
plt.title("Higehst goal scorers in the premier league mid-season of 2019/20", fontsize=32)
barh.spines['top'].set_visible(**False**)
barh.spines['right'].set_visible(**False**)
barh.spines['bottom'].set_linewidth(0.5)
barh.spines['left'].set_visible(**True**)
plt.show()

运行代码后,结果显示为下面的水平条形图:

一个漂亮的水平条形图,显示了英超 2019/20 赛季中期的最高进球得分者。

对美化的条形图中的脊线进行的相同观察也可以对该图中的脊线进行。

条形图在多种数字应用中非常有用,例如显示人们的健康改善如何随着时间的推移而变化,描绘财富如何增加以及其他有趣的概念。

这里可以找到用于这个项目的完整版本的代码

使用 TensorFlow Hub 构建更好的人工智能应用

原文:https://towardsdatascience.com/building-better-ai-apps-with-tf-hub-88716b302265?source=collection_archive---------49-----------------------

来源:tensorflow.org

人工智能最大的好处之一就是你有很多开源内容,我们经常使用它们。我将展示 TensorFlow Hub 如何简化这一过程,并允许您在应用程序中无缝地使用预先训练的卷积或单词嵌入。然后,我们将了解如何使用 TF Hub 模型执行迁移学习,以及如何将其扩展到其他用例。

这篇博文中用于演示的所有代码都可以在这个 GitHub repo-上获得

[## 里希特-达格利/GDG-纳什克-2020

我在谷歌开发者小组 Nashik 2020 的会议。通过创造……为里希特-达格利/GDG-纳西克-2020 的发展做出贡献

github.com](https://github.com/Rishit-dagli/GDG-Nashik-2020)

我也在 GDG(谷歌开发者组织)Nashik 发表了一个关于这个的演讲,在这里找到录音版本-

TF Hub 背后的动机

来源:me.me

我们都使用开源代码,我们试着做一些调整让它变得更好,也许让它为我们工作。我觉得这就是社区的力量,你可以和世界各地的人分享你的作品。

  • 让使用开源代码变得简单

所以这是 TF Hub 关注的主要问题,让开源代码、模型和数据集的使用变得超级简单。

  • 更快地构建应用程序

由于使用了预构建的内容,您可以更快地开发应用程序,因为现在您可以访问其他人已经完成的工作并利用这些工作。

  • 构建更好的应用程序

不是每个人都有机会使用高功率 GPU 或 TPU,并在几天内训练模型,您可以通过使用迁移学习构建开源内容开始,我们将看到您如何利用这一点。比方说,假设你想建立一个模型,将你收到的关于你的产品的所有评论,看看它们是好评论还是坏评论。你可以使用维基百科数据集上训练的模型中的单词嵌入,并在一段时间内做得很好。这是一个非常简单的例子来解释它,我们将在后面看到更多。

什么是 TF Hub

  • 建模是一个重要的部分

建模是任何机器学习应用程序的重要部分,你会想要投入相当多的时间和精力来做好这件事。另外请注意,我并不是说建模是唯一重要的事情,事实上,有很多事情就像建立管道,为模型服务,但我们不会在这篇博客中探讨它们。TF Hub 帮助您更好、更快、更容易地完成模型部分。

  • 一种轻松发现模型的方法

tfhub.dev

你可以在 tfhub.dev 看到一个 GUI 和一个友好版本的 TensorFlow Hub,你可以在这里看到 Hub。您可以根据想要解决的问题类型、需要的模型格式以及发布者来筛选模型。这无疑使模型发现过程变得更加容易。

  • 最先进的模型

你可以在 TF Hub 上找到很多经过良好测试的最先进的模型。TF Hub 上的每个模型都有很好的文档记录,并且经过了全面的测试,所以作为开发人员,您可以充分利用它。TF Hub 上的许多模型甚至有示例 Colab 笔记本来展示它们是如何工作的。

  • 易于使用和集成模型 API

更好的是,您可以轻松地将其与您的模型 API 集成,因此您希望在构建模型时具有灵活性,TF Hub 可以帮助您做到这一点。然后你也会看到 TF Hub 与 Keras 或核心 TensorFlow APIs 的集成有多好,这真的让它变得超级简单。

  • 众多的出版商

这可能不是你想考虑 TF Hub 的第一件事,但了解 TF Hub 的大量发行商也很有好处,一些主要发行商在下图中列出。

TF Hub 上的一些出版商

  • 没有代码依赖性

因此,很多时候,你的代码库变得非常依赖或耦合,这会使实验和迭代过程慢很多,所以 Hub 为你定义了不依赖于代码的工件,这样你就有了一个允许更快迭代和实验的系统。

  • 各种平台

演职员表:桑迪普·古普塔

TF Hub 的另一个优点是,无论您使用高级 Keras APIs 还是低级 API 都没有关系。您还可以在生产级管道或系统中使用它,它与 TensorFlow Extended 也能很好地集成。您还可以将 TF.js 模型用于基于 web 的环境或节点环境。Edge 正在起飞,TF Hub 也在这方面为您提供了帮助,您可以使用 TF Lite 预训练模型直接在您的移动设备和低功耗嵌入式系统中运行您的模型。您还可以找到 Coral Edge TPUs 的模型。它本质上只是 TF Lite,并将其与强大的 edge TPU 相结合。

理解使用 TF Hub 背后的想法

让我们从迁移学习说起,它赋予了 TF Hub 强大的力量。你可能还知道,从头开始制作任何模型都需要良好的算法选择和架构、大量数据、计算,当然还有领域专业知识。那看起来很多。

使用 TF Hub 背后的想法

所以你用 TF Hub 做的事情是,有人把所有需要做模型的东西从零开始,做一个所谓的模块,然后取出模型中可以重复使用的部分。所以在文本的情况下,我们使用单词嵌入,这给了它很大的能力,并且通常需要相当多的计算来训练。就图像而言,它可能是特征。因此,您可以从 TF Hub repo 的一个模块中获得这个可重用的部件。有了迁移学习,这个可重复使用的部件可以用在你不同的模型中,它甚至可以服务于不同的目的,这就是你使用 TF Hub 的基本方式。因此,当我说不同的目的时,它可能是在一千个标签上训练的分类器,但你只是用它来预测或区分 3 个类别。这只是一个例子,但你现在可能明白了。

为什么使用迁移学习?

  • 一般化

迁移学习允许您对数据进行归纳,这在您想要对真实数据运行模型时特别有用。

  • 数据较少

因为你已经预先学习了嵌入、权重或卷积,所以你可以用很少的数据训练高质量的模型。当您无法获取更多数据或者获取更多数据的成本太高时,这非常有用。

  • 训练时间

我们之前已经讨论过,迁移学习需要更少的训练时间。

实践中的 TF Hub

  • 安装 TF 轮毂

我们不会在这里集中安装 TF Hub,它非常简单,在这里找到安装步骤。

  • 装载型号

你可以很容易地用这段代码加载你的模型-

MODULE_HANDLE = '[https://tfhub.dev/google/imagenet/inception_v3/classification/4](https://tfhub.dev/google/imagenet/inception_v3/classification/4)'module = hub.load(MODULE_URL)

当然,您可以为您需要的模型更改MODULE_HANDLE URL。

因此,当您调用model. load方法时,TF Hub 会为您下载它,您会注意到这是一个保存的模型目录,并且您的模型是一个 protobuf 文件,它会为您进行图形定义。

  • 保存的模型 CLI

还有一个很棒的界面叫做保存模型 CLI,我觉得它非常有用。这为您提供了许多关于已保存模型的有用信息,如操作签名和输入输出形状。

!saved_model_cli show --dir [DIR] --all

以下是显示该工具提供的信息的示例输出-

保存的模型 CLI 输出

  • 推理

现在,您可以直接对您加载的模型执行推理。

tf.nn.softmax(module([img]))[0]

但是这是一个糟糕的方法,因为你没有推广你的模型。

对图像进行推理

很多时候使用图像特征向量更好。它们将最终分类图层从网络中移除,并允许您在此基础上训练网络,从而实现更高的泛化能力,进而提高现实数据的性能。

你可以简单地用这种方式来做-

model = tf.keras.Sequential([ hub.KerasLayer(MODULE_HANDLE, input_shape=IMG_SIZE + (3,), output_shape=[FV_size], trainable=True), tf.keras.layers.Dense(NUM_CLASSES, activation='softmax')])

所以你可以看到我现在使用hub.KerasLayer创建我的模型作为 Keras 层,我也设置trainableTrue,因为我想执行迁移学习。

因此,我们可以让我们的模型在它之后添加一个Dense层,这样您就可以让模型添加您自己的层,然后用您拥有的数据对它进行重新训练,当然,您可以在此基础上添加多个层,甚至卷积层。然后你可以像其他 Keras 模型一样编译它。装配和编译过程保持不变,这向我们展示了 TF Hub 的集成水平。

使用基于文本的模型

将文本转换为数字时,我们需要解决的一个主要问题是,您需要以保留文本的含义或语义的方式进行转换。我们经常使用所谓的单词嵌入来做到这一点。如你所见,单词向量顾名思义就是向量,语义相似的单词有相似的向量方向。向量大小的维数也称为嵌入维数。培训的大部分是关于学习这些嵌入。

我会加载嵌入这样的东西-

embedding = "URL"hub_layer = hub.KerasLayer(embedding, input_shape=[], 
                           dtype=tf.string, trainable=True)

加载部分是事情变得有点不一样的地方,所以你可以这样做来指定你正在加载嵌入,然后你就可以在你的神经网络中使用它了,就像我们之前做的一样。所以在实践中没有太大的区别,但是知道如何处理单词嵌入是很好的

另一件要考虑的事情

事实上,这还有另外一个特点。假设我正在使用一个返回 20 维嵌入向量的嵌入层。这意味着如果你传入一个单词,它会返回一个 1 乘 20 的数组。

单字嵌入

假设我传递了两个句子,我想让你们看看它的输出形状 2 乘 20,2 当然是因为我传递了一个有两个条目的列表,但是为什么我有一个 20。每个单词都应该有 20 维的输出向量,那么为什么一个完整的句子有 20 维的向量呢?它会智能地将这些单词嵌入转换成句子嵌入,你不再需要担心保持形状不变之类的问题。

两个句子的嵌入

尝试一些例子!

我强烈建议您亲自尝试这些示例,它们可以在 Colab 中运行。在这篇文章的 GitHub repo 中找到它们-

[## 里希特-达格利/GDG-纳什克-2020

我在谷歌开发者小组 Nashik 2020 的会议。通过创造……为里希特-达格利/GDG-纳西克-2020 的发展做出贡献

github.com](https://github.com/Rishit-dagli/GDG-Nashik-2020)

  1. 神经类型转移

一个神经类型的传递算法理想地需要相当多的计算和时间,我们可以使用 TensorFlow Hub 来轻松地完成这个任务。我们将从定义一些辅助函数开始,将图像转换成张量,反之亦然。

然后你可以将图像转换成张量,创建一个任意的图像风格化模型模型,我们可以马上开始构建图像!

hub_module = hub.load('https://tfhub.dev/google/magenta/arbitrary-image-stylization-v1-256/2')combined_result = hub_module(tf.constant(content_image_tensor[0]), tf.constant(style_image_tensor[1]))[0]tensor_to_image(combined_result)

有了这个,你现在可以创建一个像这样的图像(GitHub repo 中的图像)

神经风格转移的输出

2.文本分类

我们现在来看看如何利用迁移学习来建立一个模型。我们将在 IMDB 数据集上尝试预测评论是正面还是负面的。我们可以使用在 130 GB 谷歌新闻数据上训练的gnews-swivel嵌入。

所以,我们可以像这样简单地加载我们的模型-

embedding = "https://tfhub.dev/google/tf2-preview/gnews-swivel-20dim/1"

hub_layer = hub.KerasLayer(embedding, input_shape=[], dtype=tf.string, trainable=**True**)

我们可以在此基础上建立一个模型-

model = tf.keras.Sequential([
        hub_layer,
        tf.keras.layers.Dense(16, activation='relu'),
        tf.keras.layers.Dense(1, activation='sigmoid')])

然后你可以像训练其他 Keras 模型一样训练你的模型

关于我

大家好,我是里希特·达利

推特

网站

如果你想问我一些问题,报告任何错误,建议改进,给我反馈,你可以发邮件给我

使用 JavaScript 构建盒状图:可视化世界幸福

原文:https://towardsdatascience.com/building-box-plots-using-javascript-visualizing-world-happiness-ab0dd1d370c5?source=collection_archive---------29-----------------------

如何用 JavaScript 构建交互式盒图的指南

数据可视化是数据科学家工具箱中一个重要但有时被低估的工具。它允许我们通过探索性数据分析获得对数据的理解和直觉,这影响预处理、特征工程和正确的机器学习算法选择。它还有助于更好地评估模型,甚至允许您发现数据中模型性能较差的区域。

通过增加交互性使数据可视化更进一步甚至更有优势。通过在你的视觉化图像中加入互动元素,你可以创造一个更吸引人的体验。这反过来使用户“探索”可视化,而不仅仅是阅读它们!

在本教程中,我将讲述如何使用 JavaScript 和图表库构建一个交互式数据可视化,特别是一个盒图作为例子。我将首先简要介绍盒状图的基础知识,然后介绍构建盒状图的步骤,最后在一个有趣的例子中使用这种技术来调查地球上不同地区之间幸福的分布,试图回答这个问题:“为了更幸福,你应该生活在哪里?’。

什么是方框图?

箱线图,也被广泛称为盒须图,是一种用于可视化数据集描述性统计数据的数据可视化技术。虽然这种图表类型在理解单个数据集分布方面不如直方图有用,但这些可视化在允许用户比较不同数据集方面做得很好。

箱线图显示了以下汇总统计数据:中位数、第一和第三四分位数( Q1Q3 )、低位和高位以及异常值和异常值。这些显示如下:

作者图解

如何创建 JavaScript 盒图

要构建交互式数据可视化,有很多选择。如果你想了解 JavaScript 的替代品,你可以看看这里的。在这个例子中,我将使用 JS 图表库,特别是 AnyChart 。我会选择 AnyChart,因为它支持盒须图(以及其他多种图表类型),我认为它的文档API 对初学者和高级用户来说都非常棒,但更适合您需求的替代方案也可以工作,并将遵循类似的步骤。

步骤 1:设置页面

第一步是为盒状图可视化设置一个页面。这包括添加 HTML 元素、加载所需的脚本以及为图表设置 CSS。看起来像是:

<html>
  <head>
    <script src="https://cdn.anychart.com/releases/8.8.0/js/anychart-base.min.js"></script>
    <style type="text/css">
      html, body, #container {
        width: 100%;
        height: 100%;
        margin: 0;
        padding: 0;
      }
     </style>
  </head>
  <body>
    <div id="container"></div>
  </body>
  <script>
    anychart.onDocumentReady(function () {
      // code goes here.
    });
  </script>
</html>

使用图表库时,您需要导入正确的脚本才能使用该库,在某些情况下,不同的图表类型需要不同的模块。例如,要访问 AnyChart 的盒须图,我需要使用基本模块。

一旦排序完毕,我就需要为我的图表元素设置 CSS 属性。在这里,我将箱线图的宽度和高度设置为 100%。您可以根据您自己的用例来更改这一点。CSS 宽度和高度属性接受(父元素的)百分比和各种长度单位(最常见的是像素)。

最后,我有一个带有 JavaScript 函数any chart . ondocumentready()的脚本标记,它只是一个在文档加载时触发的函数。将 JavaScript 图表代码放在这个函数中可以确保代码不会在页面准备好之前触发,这可能会导致不好的结果(请阅读asynchronous JavaScript 以了解更多相关信息)。

步骤 2:加载数据

我将使用来自《世界幸福报告》的数据,这是一项全球调查的结果,该调查试图将每个国家公民的幸福指数量化到 0 到 10 之间。我从 Kaggle 获得了这些数据,这是一个寻找有趣数据集的好地方。不可否认,它们中的大部分都是面向机器学习应用的,但是有一些很好地用于数据可视化的目的。

在准备绘制方框图时,我需要以我们选择的图表库可以接受的格式和形式提供数据。例如,AnyChart JS 接受以下形式的箱线图数据:

{x:"Name", low: value, q1: value, median: value, q3: value, high: value, outliers: array}

其中 x 是标签,q1 和 q3 是第一个和第三个四分位数值,low 和 high 分别是 q1 以下的 1.5 倍四分位数范围和 q3 以上的 1.5 倍四分位数范围,outliers 是包含所有离群值的数组。

我方便地对《世界幸福报告》中的数据进行了预处理,以生成以下数组:

var data = [
  {x:"Western Europe", low: 5.03, q1: 6.36, median: 6.91, q3: 7.34, high: 7.53},
  {x:"North America", low: 7.10, q1: 7.18, median: 7.25, q3: 7.33, high: 7.40},
  {x:"Australia and New Zealand", low: 7.31, q1: 7.32, median: 7.32, q3: 7.33, high: 7.33},
  {x:"Middle East and Northern Africa", low: 3.07, q1: 4.78, median: 5.30, q3: 6.30, high: 7.27},
  {x:"Latin America and Caribbean", low: 4.87, q1: 5.80, median: 6.13, q3: 6.66, high: 7.09, outliers: [4.03]},
  {x:"Southeastern Asia", low: 3.91, q1: 4.88, median: 5.28, q3: 6.01, high: 6.74},
  {x:"Central and Eastern Europe", low: 4.22, q1: 5.15, median: 5.49, q3: 5.81, high: 6.60},
  {x:"Eastern Asia", low: 4.91, q1: 5.30, median: 5.65, q3: 5.90, high: 6.38},
  {x:"Sub-Saharan Africa", low: 2.91, q1: 3.74, median: 4.13, q3: 4.43, high: 5.44, outliers: [5.648]},
  {x:"Southern Asia", low: 4.40, q1: 4.41, median: 4.64, q3: 4.96, high: 5.20, outliers: [3.36]}
]

第三步:画方框图

只用这几行代码,我就可以画出我的方框图:

// create a chart
chart = anychart.box();// create a box series and set the data
series = chart.box(data);// set the container id
chart.container("container");// initiate drawing the chart
chart.draw();

将所有这些放在一起,您将得到以下结果:

<html>
  <head>
    <script src="https://cdn.anychart.com/releases/8.8.0/js/anychart-base.min.js"></script>
    <style type="text/css">
      html, body, #container {
        width: 100%;
        height: 100%;
        margin: 0;
        padding: 0;
      }
    </style>
  </head>
  <body>
    <div id="container"></div>
  </body>
  <script>
    anychart.onDocumentReady(function () {
      var data = [
        {x:"Western Europe", low: 5.03, q1: 6.36, median: 6.91, q3: 7.34, high: 7.53},
        {x:"North America", low: 7.10, q1: 7.18, median: 7.25, q3: 7.33, high: 7.40},
        {x:"Australia and New Zealand", low: 7.31, q1: 7.32, median: 7.32, q3: 7.33, high: 7.33},
        {x:"Middle East and Northern Africa", low: 3.07, q1: 4.78, median: 5.30, q3: 6.30, high: 7.27},
        {x:"Latin America and Caribbean", low: 4.87, q1: 5.80, median: 6.13, q3: 6.66, high: 7.09, outliers: [4.03]},
        {x:"Southeastern Asia", low: 3.91, q1: 4.88, median: 5.28, q3: 6.01, high: 6.74},
        {x:"Central and Eastern Europe", low: 4.22, q1: 5.15, median: 5.49, q3: 5.81, high: 6.60},
        {x:"Eastern Asia", low: 4.91, q1: 5.30, median: 5.65, q3: 5.90, high: 6.38},
        {x:"Sub-Saharan Africa", low: 2.91, q1: 3.74, median: 4.13, q3: 4.43, high: 5.44, outliers: [5.648]},
        {x:"Southern Asia", low: 4.40, q1: 4.41, median: 4.64, q3: 4.96, high: 5.20, outliers: [3.36]}
      ] // create a chart
      chart = anychart.box(); // create a box series and set the data
      series = chart.box(data); // set the container id
      chart.container("container"); // initiate drawing the chart
      chart.draw(); }); </script>
</html>

这导致:

结果代码。

通过这些简单的步骤,我很快就制作出了一个非常实用的交互式方框图,现在我可以轻松地将其嵌入到任何网站或应用程序中!

虽然还不错,但我认为我可以做得更好。继续读下去。

步骤 4:定制盒须图

数据可视化不仅仅是处理一些数据并将其放入图表中。这是讲故事。它是关于为了突出一个洞察力或者使一个可视化更吸引人而做的调整。

所有像样的图表库都会提供许多方法来做到这一点。他们将通过他们的 API 提供这些选项,您通常可以通过查看他们的文档找到这些选项。因为我在这个例子中使用的是 AnyChart JS 图表库,所以我将介绍一些可用于盒状图的定制选项。

自定义图表设计

改变可视化的许多外观和功能方面是可能的。首先,我将添加一个自定义标题,更改各个轴标签,并错开 x 轴标签以防止它们重叠:

// set the chart title
var title = chart.title("Happiness Level by Region");// label axis
chart.xAxis().title("Regions");
chart.yAxis().title("Happiness Level");// stagger the x-axis labels
chart.xAxis().staggerMode(true);

例如,对于框本身,我可以更改它们的默认状态、悬停时的状态以及选中时的状态。我可以对中线、干、须以及异常值进行类似的修改。通常,我会利用这些定制选项来使我的可视化更好地适应我将托管 viz 的布局/主题,或者更好地适应正在显示的数据(例如,在可视化环境数据时使用绿色)。

对于离群值,我甚至可以通过设置标记类型来改变形状。(AnyChart 有各种选项,可以在 API 参考中看到)。

这些更改可以通过以下代码轻松完成:

// configure visual appearance of series
series.normal().fill("#36558F", 0.2);
series.hovered().fill("#36558F", 0.2);
series.selected().fill("#36558F", 0.6);
series.normal().stroke("#36558F", 1);
series.hovered().stroke("#36558F", 2);
series.selected().stroke("#36558F", 4);// configure medians
series.normal().medianStroke("#dd2c00", 1);
series.hovered().medianStroke("#dd2c00", 2);
series.selected().medianStroke("#dd2c00", 2);// configure outliers
series.normal().outlierMarkers({
  fill: "#36558F 0.2",
  stroke: { color: "#36558F", thickness: 1 },
  size: 5,
  type: "star5",
});
series.hovered().outlierMarkers({
  fill: "#36558F 0.2",
  stroke: { color: "#36558F", thickness: 2 },
  size: 5,
  type: "star5",
});
series.selected().outlierMarkers({
  fill: "#36558F 0.6",
  stroke: { color: "#36558F", thickness: 4 },
  size: 7.5,
  type: "star5",
});// configure stems
series.normal().stemStroke("#36558F", 0.5);
series.hovered().stemStroke("#36558F", 1);
series.selected().stemStroke("#36558F", 2);// configure whiskers
series.whiskerWidth(50);
series.normal().whiskerStroke("#36558F", 0.5);
series.hovered().whiskerStroke("#36558F", 1);
series.selected().whiskerStroke("#36558F", 2);

在这里,我分别输入颜色和不透明度以及填充和描边的颜色和宽度。颜色参数可以以多种可接受的格式给出。这里我用了更广泛使用的十六进制代码。

将所有这些整合在一起会导致:

结果代码。

改进方块剧情提示

正如您可能已经注意到的,当悬停在一个框上时,您可以看到用于绘制这些图的所有汇总统计数据…除了异常值。我将解决这个问题,并将异常数据添加到箱线图工具提示中。

// configure tooltip
chart.tooltip().titleFormat("Region: {%x}")
chart.tooltip().format("Low: {%low} \n High: {%high} \n Quantile 1: {%q1} \n Quantile 3: {%q3} \n Median: {%median} \n Outliers: {%outliers}");

如果您将它添加到前面的代码中,您会得到下面的交互式盒须图:

结果代码。

厉害!我们刚刚看到了幸福(某种程度上)!从上面的图中,我可以清楚地看到,撒哈拉以南非洲不是最幸福的地方,而西欧和北美人笑得很开心。最幸福的地方显然是澳大利亚和新西兰!

*我来自撒哈拉以南的非洲地区,对我的轶事经验得出的这些结果不太有信心!

结论

如您所见,制作交互式数据可视化非常容易。它不需要太多的 JavaScript 知识就可以开始使用(取决于您使用的图表库),而且效果非常好!在这里,我创建了一个方框图,但这个过程与其他图表类型非常相似,通过查阅文档,可以很容易地进行更改。

这只是您能做的事情的冰山一角,无论是更有趣的定制还是使用不同的数据源。我希望这个关于盒须图的教程可以成为进一步学习的一个很好的跳板!

用 Sklearn 构建分类模型

原文:https://towardsdatascience.com/building-classification-models-with-sklearn-6a8fd107f0c1?source=collection_archive---------9-----------------------

Sklearn 分类简介

莎伦·麦卡琴在的照片

Scikit-learn 是一个针对 python 的开源机器学习库。它提供了各种回归、分类和聚类算法。在我的上一篇文章Sklearn中,我讨论了几种使用机器学习包进行回归的方法。在这篇文章中,我们将回顾一些建立分类模型的基本方法。该软件包的文档内容丰富,是每位数据科学家的绝佳资源。你可以在这里找到文档。

我们将使用电信客户流失数据集。这里可以找到

首先,让我们导入数据并打印前五行:

import pandas as pd 
df = pd.read_csv("Customer_Churn.csv")
print(df.head())

我们将使用所有的分类和数字数据来预测客户流失。首先,我们需要将分类列转换为神经网络可以处理的数值。例如,对于性别,我们有:

df.gender = pd.Categorical(df.gender)
df['gender_code'] = df.gender.cat.codes

现在,让我们定义输入和输出数组:

import numpy as npfeatures = ['gender_code', 'SeniorCitizen_code', 'PhoneService_code', 'MultipleLines_code', 
                 'InternetService_code', 'Partner_code', 'Dependents_code', 'PaymentMethod_code', 
                 'PaymentMethod_code', 'PaperlessBilling_code','Contract_code', 'StreamingMovies_code',
                 'StreamingTV_code', 'TechSupport_code', 'DeviceProtection_code', 'OnlineBackup_code',
                 'OnlineSecurity_code', 'Dependents_code', 'Partner_code','tenure', 'MonthlyCharges']X = np.array(df[features])
y = np.array(df['Churn_code'])

然后,我们将分割数据用于训练和测试:

from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size = 0.2, random_state = 42)

现在我们所有必要的变量都定义好了。让我们建立一些模型!

逻辑回归

先说逻辑回归。逻辑回归使用逻辑函数来预测二元因变量。

我们导入 LogisticRegression 包,如下所示:

from sklearn.linear_model import LogisticRegression

让我们定义一个逻辑回归对象,拟合我们的模型,并评估性能:

reg_log = LogisticRegression()
reg_log.fit(X_train, y_train)
y_pred = reg_log.predict(X_test)

我们可以使用度量分类报告来可视化预测:

from sklearn import metrics
print(metrics.classification_report(y_test, y_pred))

我们还可以查看“roc_auc_score”和“f1_score”“roc_auc_score”是接收操作特征曲线下的面积。这是衡量二元分类模型区分类别的能力的一种方法。“roc_auc_score”为 0.5 意味着模型无法区分类别。接近 1.0 的值对应于类之间的强分离。“f1_score”是精确度和召回率的调和平均值。与“roc_auc_score”类似,完美的“f1_score”等于 1.0:

print("roc_auc_score: ", roc_auc_score(y_test, y_pred))
print("f1 score: ", f1_score(y_test, y_pred))

随机森林

现在我们来看看随机森林。随机森林是一种基于树的方法,它集成了多个单独的决策树。

我们导入 RandomForestClassifier 包,如下所示:

from sklearn.ensemble import RandomForestClassifier

让我们定义一个随机森林分类对象,符合我们的模型,并评估性能:

reg_rf = RandomForestClassifier()
reg_rf.fit(X_train, y_train)
y_pred = reg_rf.predict(X_test)

让我们看一下指标分类报告:

print(metrics.classification_report(y_test, y_pred))

print("roc_auc_score: ", roc_auc_score(y_test, y_pred))
print("f1 score: ", f1_score(y_test, y_pred))

我们看到随机森林的性能比逻辑回归差。我们还可以打印特征重要性。这使我们能够了解哪些变量对温度预测最为重要:

feature_df = pd.DataFrame({'Importance':reg_rf.feature_importances_, 'Features': features })
print(feature_df)

我想指出的是,通过不传递任何参数,比如 max_depth 和 n_estimators,我选择了默认的随机森林值(n_estimators = 10 和 max_depth = 10)。我们可以通过优化随机森林中的参数来进一步提高性能。这可以手动完成,也可以使用网格搜索技术自动完成。我将把参数优化的问题留给另一篇文章。

支持向量机

我要讨论的下一个方法叫做支持向量回归。这是支持向量机(SVM)的扩展。支持向量机在高维特征空间中构造一组超平面,可用于回归和分类问题。

我们导入 SVC 包,如下所示:

from sklearn.svm import SVC

让我们定义一个支持向量分类对象,适合我们的模型,并评估性能:

reg_svc = SVC()
reg_svc.fit(X_train, y_train)
y_pred = reg_svc.predict(X_test)

我们可以使用度量分类报告来可视化预测:

print(metrics.classification_report(y_test, y_pred))

我们还可以查看 roc_auc_score 和 f1_scores:

print("roc_auc_score: ", roc_auc_score(y_test, y_pred))
print("f1 score: ", f1_score(y_test, y_pred))

我们看到支持向量分类性能比逻辑回归略差,比随机森林略好。与随机森林类似,SVC 采用可用于优化性能的参数。这些包括正则化参数(默认 C = 1.0)、核(默认核= 'rbf ')和核系数(默认伽马= 'scale ')

K-最近邻居

我要讨论的最后一种方法是 k- 最近邻法进行分类。k-最近邻使用欧几里德距离计算,其中预测是 k 个最近邻的平均值。

我们导入 KNeighborsClassifier 包,如下所示:

from sklearn.neighbors import KNeighborsClassifier

让我们定义一个 k- 最近邻分类对象,拟合我们的模型,并评估性能:

reg_knn = KNeighborsClassifier()
reg_knn.fit(X_train, y_train)
y_pred = reg_knn.predict(X_test)

让我们看一下指标分类报告:

print(metrics.classification_report(y_test, y_pred))

我们还可以查看 roc_auc_score 和 f1_scores:

print("roc_auc_score: ", roc_auc_score(y_test, y_pred))
print("f1 score: ", f1_score(y_test, y_pred))

K-最近邻算法也采用超参数,特别是 n_neighbors,可以选择它来最小化误差。

结论

我将在这里停下来,但是您可以随意选择模型特性,看看是否可以改进其中一些模型的性能。

概括地说,我简要介绍了使用 python 机器学习库进行分类。我讲述了如何定义模型对象、使模型适合数据,以及使用逻辑回归、随机森林、支持向量机和最近邻模型来预测输出。

我希望这篇文章是有益的。这篇文章的代码可以在 GitHub 上找到。感谢阅读,机器学习快乐!

动态学习 ColumnTransformer

原文:https://towardsdatascience.com/building-columntransformers-dynamically-1-6354bd08aa54?source=collection_archive---------45-----------------------

使用逻辑条件为不同类型的变换选择特征类型

来源: Unsplash

特征工程可能是机器学习过程中非常耗时的部分,尤其是当您处理许多特征和不同类型的特征时。在我的项目过程中,我开发了一些启发式方法,允许我构造一个相当有效的 Scikit——快速动态地学习 ColumnTransformer。

在我的文章中,我将展示两种技术。首先,我将展示如何用逻辑条件选择特性,而不是在代码中列出每一列。其次,我将解释在训练一个新模型时,我用作“默认”的 transformer 管道。我将在爱荷华州的房价数据集上展示我的技术,你可以在 Kaggle 上找到。

在继续之前,我应该指出,我的帖子假设您以前使用过 Scikit-Learn 和 Pandas,并且熟悉 ColumnTransformerPipeline 、&、预处理类如何促进可再现的特征工程过程。如果您需要复习,请查看这个 Scikit-Learn 示例

让我们从导入所需的包、类、函数和数据开始。

import numpy as np  
import pandas as pd  

import plotly.express as px

from sklearn.preprocessing import OneHotEncoder
from sklearn.impute import SimpleImputer
from sklearn.compose import ColumnTransformer, make_column_selector
from sklearn.pipeline import make_pipeline
from sklearn.ensemble import GradientBoostingRegressor
from sklearn.metrics import r2_score, mean_squared_log_error
import category_encoders as ce

DEP_VAR = 'SalePrice'

train_df = pd.read_csv('/kaggle/input/house-prices-advanced-regression-techniques/train.csv').set_index('Id')

# split the dependent variable from the features
y_train = train_df[DEP_VAR]
train_df.drop(DEP_VAR, axis=1, inplace=True)

test_df =  pd.read_csv('/kaggle/input/house-prices-advanced-regression-techniques/test.csv').set_index('Id')

数据集

Ames 训练数据集具有相对少量的观察值和相当数量的特征(79 个)。这些特征中有 43 个是分类的,36 个是数字的。如果您对数据集上的一些探索性数据分析感兴趣,我推荐您阅读本笔记本。

print(train_df.shape)**(1460, 79)**feature_types =\
    train_df\
    .dtypes\
    .astype(str)\
    .value_counts()\
    .to_frame('count')\
    .rename_axis('datatype')\
    .reset_index()px.bar(feature_types, x='datatype', y='count', color='datatype')\
    .update_layout(showlegend=False)\
    .update_layout(title={'text': 'Ames Dtypes', 'x': .5})

特征的类型

如果您和我一样,那么在代码或配置文件中列出 79 个特性的想法似乎是一项乏味且不必要的任务。如果有一种方法可以根据这些特性在逻辑上将它们联系起来,那会怎么样呢?

允许您动态构造 ColumnTransformer 的关键见解是理解在非文本、非时间序列数据集中有三大类特征:

  1. 数字的
  2. 具有中低基数的分类
  3. 高基数分类

让我们来看看如何动态地选择每种特性类型以及我与之一起使用的默认转换器管道。

数字特征

sklearn.compose 模块附带了一个名为 make_column_selector 的方便的类,它提供了一些有限的功能来动态选择列。您可以列出要包含或排除的 dtypes,或者使用 regex 模式来选择列名。为了选择数字特性,我们将实例化一个函数来选择 np.number 数据类型的列,它将匹配任何整数或浮点列。当我们在训练数据集上调用select_numeric_features时,我们看到它正确地选择了 36 个int64float64列。

select_numeric_features = make_column_selector(dtype_include=np.number)

numeric_features = select_numeric_features(train_df)

print(f'N numeric_features: {len(numeric_features)} \n')
print(', '.join(numeric_features))**N numeric_features: 36 

MSSubClass, LotFrontage, LotArea, OverallQual, OverallCond, YearBuilt, YearRemodAdd, MasVnrArea, BsmtFinSF1, BsmtFinSF2, BsmtUnfSF, TotalBsmtSF, 1stFlrSF, 2ndFlrSF, LowQualFinSF, GrLivArea, BsmtFullBath, BsmtHalfBath, FullBath, HalfBath, BedroomAbvGr, KitchenAbvGr, TotRmsAbvGrd, Fireplaces, GarageYrBlt, GarageCars, GarageArea, WoodDeckSF, OpenPorchSF, EnclosedPorch, 3SsnPorch, ScreenPorch, PoolArea, MiscVal, MoSold, YrSold**

我的默认数字特征转换包括使用简单估算器。我用中间值估算缺失值,并将add_indicator参数设置为True。使用中位数代替估算者的平均缺省值可以防止离群值的影响。使用add_indicator功能调用 MissingIndicator 类,该类为每个具有缺失值的特性创建二进制缺失指示器列。根据我的经验,当数据不是随机丢失时,这些列对模型来说可能是相当重要的。

需要注意一些事情:

  • 当我构建转换器管道时,我更喜欢使用 make_pipeline 函数,而不是 pipeline 类。该函数通过自动使用小写版本的类名,取代了显式命名每个管道步骤的要求,例如,simple imputr 被命名为“simple imputr”。
  • Scikit-Learn 估算器要求用np.nan表示缺失的值——因此,我使用了fillna方法。
  • 如果你要使用线性模型,你要在估算器前插入一个预处理器来居中和缩放。
  • 简单估算器的更复杂的替代品包括需要居中和缩放的 KNNImputer ,或者实验性的迭代估算器
train_df.fillna(np.nan, inplace=True)
test_df.fillna(np.nan, inplace=True)

numeric_pipeline = make_pipeline(SimpleImputer(strategy='median', add_indicator=True))

具有中低基数的分类

接下来,让我们讨论如何选择名义数据并将其转换为数字形式。

一键(OH)编码,为每个唯一值创建一个指示列,是最常用的方法。然而,OH 转换可能不适合具有高基数的特性。具有许多唯一值的 OH 编码功能可能会创建太多方差非常低的列,这可能会占用太多内存或对线性模型的性能产生负面影响。因此,我们可能希望将我们为这种编码选择的特征限制在某个唯一值阈值以下。为了便于说明,我将把我的限制设置为 10 个值。实际上,根据数据集的大小,我们可能会选择更高的阈值。

由于[make_column_selector](https://github.com/scikit-learn/scikit-learn/issues/15873) 不能检测基数,我开发了自己的select_oh_features自定义函数。它由一系列 pandas 方法组成,这些方法执行以下操作:

  • 从熊猫DataFrame中选择objectcategoryd 型
  • 计算这些列的唯一值的数量
  • 使用loc方法中的匿名lambda函数对小于或等于MAX_OH_CARDINALITY的唯一值计数进行子集化
  • 从索引中提取列名,并将它们作为列表返回

当我们对训练数据集调用该函数时,我们看到它选择了 43 个分类特征中的 40 个。

MAX_OH_CARDINALITY = 10

def select_oh_features(df):

    oh_features =\
        df\
        .select_dtypes(['object', 'category'])\
        .apply(lambda col: col.nunique())\
        .loc[lambda x: x <= MAX_OH_CARDINALITY]\
        .index\
        .tolist()

    return oh_features

oh_features = select_oh_features(train_df)

print(f'N oh_features: {len(oh_features)} \n')
print(', '.join(oh_features))**N oh_features: 40 

MSZoning, Street, Alley, LotShape, LandContour, Utilities, LotConfig, LandSlope, Condition1, Condition2, BldgType, HouseStyle, RoofStyle, RoofMatl, MasVnrType, ExterQual, ExterCond, Foundation, BsmtQual, BsmtCond, BsmtExposure, BsmtFinType1, BsmtFinType2, Heating, HeatingQC, CentralAir, Electrical, KitchenQual, Functional, FireplaceQu, GarageType, GarageFinish, GarageQual, GarageCond, PavedDrive, PoolQC, Fence, MiscFeature, SaleType, SaleCondition**

对于具有中低基数的分类特性,我有两个默认转换:SimpleImputerOneHotEncoder

SimpleImputer中,使用“常量”策略将缺失值设置为“missing_value”(我没有将add_indicator参数设置为True,因为这会创建重复的列。)在 OH 编码器中,我喜欢将handle_unknown参数设置为“忽略”,而不是使用默认的“错误”,这样,如果这个转换器在测试数据集中遇到未知值,它就不会抛出错误。相反,如果出现这种情况,它会将所有 OH 列设置为零。因为 Ames 测试数据集包含不在训练数据集中的分类值,所以如果不使用此设置,我们的 ColumnTransformer 将在测试数据集上失败。如果您计划使用线性模型,您将需要设置drop参数,以使特征不完全共线。

oh_pipeline = make_pipeline(SimpleImputer(strategy='constant'), OneHotEncoder(handle_unknown='ignore'))

高基数分类

为了选择具有高基数的特性,我创建了一个类似的函数来选择唯一值计数大于阈值的objectcategory特性。它选择满足这些标准的三个特征。

def select_hc_features(df):

    hc_features =\
        df\
        .select_dtypes(['object', 'category'])\
        .apply(lambda col: col.nunique())\
        .loc[lambda x: x > MAX_OH_CARDINALITY]\
        .index\
        .tolist()

    return hc_features

hc_features = select_hc_features(train_df)

print(f'N hc_features: {len(hc_features)} \n')
print(', '.join(hc_features))**N hc_features: 3 

Neighborhood, Exterior1st, Exterior2nd**

为了用高基数转换我们的特性,我可以采用更基本的方法,使用 Scikit-Learn 的原生 LabelEncoderOrdinalEncoder 预处理器。然而,在许多情况下,这些方法很可能在您的模型中执行次优,除非您处理的是顺序数据。我更喜欢使用类别编码器包,它有十几种智能编码高度基本特征的方法。本帖提供了其中几种方法的概述。其中大多数是监督技术,使用因变量将标称值转换为数值。

TargetEncoder 可能是最容易理解的方法,但我更喜欢使用广义线性混合模型编码器,它“背后有坚实的统计理论”,并且“不需要调整超参数”在不深入 GLMMs 的细节的情况下,该方法的核心是将标称值编码为来自一个热编码线性模型的系数。这个包中的类别编码器方法通过将缺失值和未知值设置为零或因变量的平均值来处理它们。

hc_pipeline = make_pipeline(ce.GLMMEncoder())

把所有的放在一起

最后,让我们把所有的部分放在一起,实例化我们的 ColumnTransformer:

  • transformer参数接受一个由 3 个元素组成的元组列表。每个元组包含转换器/管道的名称、实例化的管道以及我们为该管道创建的选择器函数。
  • 如果您正在处理大量的特性,并且具有多线程能力,我肯定会设置n_jobs参数,这样管道就可以并行运行。将其设置为-1 将使用所有可用的线程。
  • 最后,我想提醒大家注意remainder参数。默认情况下,ColumnTransformer 会删除任何不包含在transformers列表中的列。或者,如果您有不需要转换的功能,您可以将此参数设置为“passthrough ”,并且不删除任何剩余的功能。
column_transformer =\
    ColumnTransformer(transformers=\
                          [('numeric_pipeline',\
                            numeric_pipeline,\
                            select_numeric_features),\
                          ('oh_pipeline',\
                           oh_pipeline,\
                           select_oh_features),\
                          ('hc_pipeline',\
                           hc_pipeline,\
                           select_hc_features)],
                      n_jobs=-1,
                      remainder='drop')

结果

在安装 ColumnTransformer 并转换数据之后,OH 编码将列数从 79 增加到 254。如果我们没有使用GLMMEncoder,我们将会处理 300 多列。

X_train = column_transformer.fit_transform(train_df, y_train)
X_test = column_transformer.transform(test_df)

print(X_train.shape)
print(X_test.shape)**(1460, 254)
(1459, 254)**

让我们看看在没有严格的超参数调整的情况下,工程特性在 GBM 回归器上的表现如何。

model = GradientBoostingRegressor(learning_rate=0.025,\
                                  n_estimators=1000,\
                                  subsample=0.25,\
                                  max_depth=5,\
                                  min_samples_split=50,\
                                  max_features='sqrt')
model.fit(X_train, y_train)

y_train_pred = model.predict(X_train)

由于 R 平方值接近 1,我们的特征几乎可以解释训练集因变量的所有变化。均方根对数误差接近 0.084。

print(f'R-squared: {r2_score(y_train, y_train_pred)}')
print(f'RMSLE: {np.sqrt(mean_squared_log_error(y_train, y_train_pred))}')**R-squared:** **0.9696430970867915** **RMSLE:** **0.08434172203520345**

然而,当我向 Kaggle 提交我的测试预测时,测试 RMSLE 结果是 0.132。训练和测试 RMSLE 之间的差距表明该模型过拟合,并且它将受益于正则化和超参数调整。

submission = pd.DataFrame(dict(Id=test_df.index, 
                               SalePrice=model.predict(X_test)))
submission.to_csv("submission.csv", index=False)

笔记本原图可以在这里找到。请继续关注关于培训&用 Scikit 正则化模型的更多帖子。如果你觉得这篇文章有帮助或者有任何改进的想法,请告诉我。谢谢!

使用 Keras 在 Python 中构建卷积神经网络

原文:https://towardsdatascience.com/building-convolutional-neural-networks-in-python-using-keras-7e4652f6456f?source=collection_archive---------20-----------------------

创建您自己的图像识别系统

照片由来自 PexelsPhilippe Donn 拍摄

计算机视觉

计算机视觉是指计算机如何确定关于图像和视频的知识。最常见的例子是面部识别。卷积神经网络(CNN)主要用于促进图像或视频与所需标签或输出之间的学习。本文将使用 Keras 带您浏览 Python 中的卷积神经网络,并让您直观地了解其内部工作原理,以便您可以开始构建自己的图像识别系统。

这个项目的所有代码都可以在我的 GitHub 上找到。

卷积神经网络演练

数据

首先,我们需要数据供我们的深度学习模型学习。在这个例子中,我将使用来自 Mendeley 的开源天气数据进行分类,我鼓励你通过下载或使用你自己的数据来跟进。我将把天气数据作为一个多类分类问题来处理,标签如下:多云下雨阳光日出。首先,我们将编写一些代码来创建一个图像路径列表,稍后我们将使用它来读取图像并对其进行标记。

让我们逐一分析这段代码的每一部分…

1.)获取所有图像路径集

  • 这段代码负责一件事,为每个图像类创建图像路径列表。我将天气数据集中的所有图像放入文件夹 weather_data 中,然后通过遍历该目录,我们将每个路径附加到列表中,如果每个图像属于其类,则检查其名称是否与其类相关。

2.)随机打乱图像

  • 为了在创建我们的训练和测试分割时没有选择偏差,我们将在聚合这些图像之前随机洗牌。

3.)训练和测试图像集

  • 相对来说,我们将使用每个列表中的前 150 个随机图像作为我们的训练集,每个列表中的其余图像作为我们的测试集。

4.)垃圾收集

  • 图像识别系统的计算成本很低,我们可以通过删除已经重新分配的列表和收集垃圾来清理一些内存分配。

图像预处理

在建立模型之前,我们需要重新格式化图像,以便模型知道预期的尺寸。

5.)静态图像变量

  • 这里我们将 nRows 和 nCols 声明为我们将重新格式化图像的像素高度和宽度。通道是指颜色通道,在这种情况下,我们使用 3 的 RGB,但你可以使用 1 如果你喜欢灰度

6.)培训和测试拆分

  • 我们之前创建了包含图像路径的集合,用于我们的训练和测试分割。现在,我们将使用这些数组来包含读取的图像及其标签。

7/8.)图像读取和数据标记

  • 可能是最重要的部分,让我们来分解一下。有两个 for 循环,一个用于构建训练集,另一个用于测试集。我们正在遍历前面构建的路径集中的每个图像路径。使用 cv2,我们从 (5) 读入图像并根据静态尺寸调整图像大小。接下来,我们有一系列 if 语句来确定每个读取图像的类别。每个图像的名称基于其类别(例如,rain1、rain2、rain 3……),因此我们检查图像路径中是否包含多云、下雨、日出、晴天,并相应地附加 1、2、3 或 4。最后,我想承认有些图像可能无法读取,所以我们想在 for 循环中使用 try/except 来捕获我们无法读取的图像。

9.)垃圾收集

  • 参见 (4)

10.)转换成 NumPy 数组

  • 我们希望将我们的训练和测试集转换为 NumPy 数组,以便我们可以将其输入到我们的卷积神经网络中,并使用 Scikit-learn 和 Keras 等附带的库重新格式化输入和目标数据。

11.)转换为分类

  • 因此,我们可以使用 softmax 激活函数X【0,1】来预测图像类我们将把我们的标签向量(其中L{ 1,2,3,4} 转换为分类集 L ∈)

模型开发

最后,我们将使用 Keras 构建卷积神经网络。

12.)创建模型

  • 在这里,我们创建一个顺序模型,以便在 Conv2D、MaxPooling2D、Flatten 和 Dropout 层完成工作后,我们可以将其直接输入到完全连接的层中进行分类。

Conv2D—从图像中提取关键特征

maxpooli2d—通过下采样进行降维

展平 —展平输入形状,例如(8,16) → 128

辍学——防止模型过拟合

密集 —全连通层,对展平输入进行分类

13.)模型总结

  • 在这里,我们可以了解每次训练迭代优化了多少参数。尝试添加和移除 MaxPooling2D 层,看看这会如何影响训练参数的总数。这显然与训练时间相关。

14.)编译并拟合模型

  • 至此,我们基本上完成了模型。我们需要做的最后一件事是编译并使我们的模型适合训练数据。我们将使用 adam 作为我们的优化器,分类相关性和准确性分别作为我们的损失和度量。

结果

15/16.)可视化结果

  • 我们可以做的最后一件事是帮助可视化我们的模型的结果,为模型的精度和训练期间的损失创建图表。

运行完本文中开发的所有代码后,让我们来看看模型的输出…

在 10 个训练周期之后,我们以大约 88%的训练准确度和大约 93%的验证准确度结束,还不错!

有很多方法可以改进这个模型,你可以通过简单地添加更多的隐藏层和更多的训练时期来开始。

现在你应该对如何创建自己的图像识别系统有了一个很好的想法!同样,如果你想下载这个项目,可以在我的 GitHub 上找到。

从 Jupyter 笔记本电脑构建新冠肺炎交互式仪表板

原文:https://towardsdatascience.com/building-covid-19-analysis-dashboard-using-python-and-voila-ee091f65dcbb?source=collection_archive---------3-----------------------

用你的 jupyter 笔记本创建一个仪表板,无需编写任何前端或后端代码。

随着世界陷入停滞,数千人失去生命和经济自由落体,新冠肺炎新型冠状病毒新型冠状病毒的爆发已经劫持了整个地球。

在封锁期间,我决定查看一些数据存储库,并开始争论和绘制数据。过了一会儿,我决定用我的 jupyter 笔记本创建一个仪表板。我将向您介绍我使用代码片段的步骤:

目录:

  1. 从可靠且不断更新的来源收集数据
  2. 清理和准备数据。
  3. 使用 Plotly 散点图可视化 N 个(使用 ipywidgets )受灾最严重的国家。
  4. 被请求国家的确诊和死亡病例。
  5. 使用叶子在世界地图上绘制所有案例
  6. 将笔记本转换为基于材质主题的仪表盘。

我们开始吧!

这是我频道上的视频教程,如果你想看并跟随我从头开始制作这个:

从可靠且不断更新的来源收集数据

我从约翰·霍普斯金·CSSE 的 2019 年新型冠状病毒新冠肺炎数据库中收集了我的数据。

[## CSSEGISandData/新冠肺炎

这是由约翰·霍普金斯大学运营的 2019 年新型冠状病毒视觉仪表板的数据存储库…

github.com](https://github.com/CSSEGISandData/COVID-19)

这里的数据从官方来源每 24 小时更新一次。我访问了csse_covid_19_time_series目录中的原始文件。

下面是我如何使用 pandas 的read_csv()方法读取文件:

让我们简要地看一下从读取文件返回的数据帧。confirmed_df.head().info()法的结构。

confirmed_dfrecovered_dfdeath_df的结构相同,分别存储每个国家的省/州的确诊、痊愈和死亡病例。国家按字母顺序排列。

我们有从2020 年 1 月 22 日到您访问日期的数据。

从列名中,您可以看到需要对它们进行一些清理,以便于进一步的分析。下一步是准备数据。

数据清理

首先,使用小写的列名总是很容易的。此外,我们需要将Province/State改为简单的state,将Country/Region改为country

让我们按照要求的格式重命名所有的列:

现在,有了正确格式的所有列,我们可以用它们做很多事情。

让我们计算一下确诊、死亡和康复病例的总数。我们可以用country_df来说明:

可视化受灾最严重的国家

对于遭受重创的国家来说,按字母顺序分析数据不是一个好主意。我们需要根据该国的病例数对这些值进行排序。使用country_df,我们有:

sorted_country_df = country_df.sort_values('confirmed', ascending= **False**)

我们可以在数据框中突出显示所需的列,我已经编写了一个函数,使用style属性为我的数据框列添加样式:

在这里,我添加了ipywidget interact()方法,为选择您想要查看的受灾最严重国家的数量提供了一个选项。

我们得到一个文本框来输入我们想要的行数以及高亮显示的列:

Plotly 将数据可视化

那是显示数据,现在使用 Plotly 库绘制我们拥有的数据。我们绘制了相同的受灾最严重的国家,以及一个滑块选项来更改我们希望显示的国家数量:

Plotly 呈现高质量的交互式图表,下面是数据现在的样子:

我们还可以使用 plotly 绘制条形图,以显示使用以下代码片段的所有不同情况:

该图使用柱状图展示了死亡人数方面受影响最严重的国家:

被请求国家的确诊和死亡病例

现在是时候更深入地分析和讨论每个国家日常案例的可视化了。我们将编写一个函数,将国家作为输入,显示每天的确诊和死亡病例。

这将有助于我们直观地看到病例增加的图表和速度。

函数plot_cases_for_country()迭代 confirmed_df 和 death_df 以存储所请求国家的 X 轴和 Y 轴数据,然后使用go.Scatter()函数将其传递到 plotly 的散点轨迹你可以在我的 GitHub 库上找到这个函数的代码。

该功能随后添加了一个交互式文本框,允许您输入您选择的国家,如下所示:

使用树叶在世界地图上进行全球传播

Folium 是一个 python 包,它帮助我们在交互式传单地图上可视化 python 处理的数据。它提供了许多内置的 tilesets 供选择。快速入门示例对入门非常有帮助。

现在创建一个Map()类对象,通过传递起始坐标作为位置参数,你想要选择的瓦片和缩放级别。

遍历confirmed_df行,我们需要每个国家的纬度和经度来放置标记。我们可以使用folium.Circle()类来创建具有所需属性的标记,如radiuscolor和填充颜色。

下面是上面的函数如何渲染地图:

将笔记本转换为独立的仪表板

完成所有分析后,是时候交流我们的发现和视觉效果了。管理层(非技术读者)通常对查看代码不感兴趣。给我们一个可以互动和工作的仪表板,让我们所有人的生活变得更加轻松。

使用 Voilà 可以完美地从您的 jupyter 笔记本分析中创建仪表板。它将 Jupyter 笔记本变成了独立的网络应用。下面是 的几个特点 Voilà:

  • 支持 Jupyter 交互小工具。
  • 不允许任意代码执行。
  • 语言无关的仪表板系统——基于 Jupyter 标准协议和文件格式构建,voilà可与任何 Jupyter 内核(C++、Python、Julia)一起工作。
  • 瞧,这是可扩展的。它包括一个灵活的 模板系统 来产生丰富的应用布局。

确保您已经安装了voila软件包。

  1. 安装命令瞧:

pip install voila或使用康达 foge,conda install voila -c conda-forge

2.在终端中,输入命令:

  • 我用选项--theme=dark使用了黑暗主题,用--template=material使用了材质模板。

我们可以利用模板使仪表板看起来丰富和有吸引力。下面是我使用材料模板制作的仪表板的演示:

Harshit 的数据科学

通过这个渠道,我计划推出几个涵盖整个数据科学领域的系列。以下是你应该订阅频道的原因:

  • 该系列将涵盖每个主题和副主题的所有必需/要求的高质量教程。
  • 解释了为什么我们在 ML 和深度学习中做这些事情的数学和推导。
  • 与谷歌、微软、亚马逊等公司的数据科学家和工程师以及大数据驱动型公司的首席执行官的播客。
  • 项目和说明,以实现迄今为止所学的主题。

你可以在 LinkedInTwitterInstagram 上与我联系(在那里我谈论健康和福祉。)

注意:在这些黑暗的时期,自我隔离为自我提升腾出了一些空间,我们可以利用这些空间来发展新的技能、爱好,并帮助未来的自己。

用 Python 和 Tableau 构建新冠肺炎仪表盘

原文:https://towardsdatascience.com/building-covid-19-dashboard-with-python-and-tableau-296b0016920f?source=collection_archive---------14-----------------------

探索如何使用 Python 和 Tableau 将原始新冠肺炎数据转化为交互式仪表盘。

介绍

自从新冠肺炎第一次成为各大出版商的头条新闻以来,已经过去了几个月。这种致命的病毒从那以后就一直在增长,并对全球健康和经济造成了巨大的破坏。全球都在努力阻止这场疫情,并从它的影响中恢复过来。作为一名技术专家和数据科学爱好者,我也想通过创建一个交互式仪表板来可视化世界上的新冠肺炎情况,从而加入到这些努力中来。

在这篇文章中,我想分享我做这个项目的经验。我将讨论用 Python 预处理数据的步骤,创建 Tableau 仪表板,并分享代码/工作簿,这样你也可以自己创建一个!

我在这个项目中使用的数据集是约翰霍普金斯大学系统科学与工程中心(CSSE)的新冠肺炎数据仓库(来源: Github )。这可能是公开获得的关于新型冠状病毒的最全面的数据集之一。它报告了全球 188 个国家/地区的确诊、死亡和康复病例数。

[## CSSEGISandData/新冠肺炎

这是由约翰·霍普金斯大学运营的 2019 年新型冠状病毒视觉仪表板的数据存储库…

github.com](https://github.com/CSSEGISandData/COVID-19)

数据预处理

注意:本节假设您对 Python 和 Pandas 有一些基本的了解。如果你不熟悉这些,可以看看我之前关于 用熊猫 进行数据操作的文章来入门!

[## 数据对话:熊猫数据操作实用教程

了解如何使用 Kickstarter 项目数据集处理和操作数据。

towardsdatascience.com](/data-talks-a-practical-tutorial-on-data-manipulation-with-pandas-6a4715382a1)

将数据从宽格式转换为长格式

CSSE 新冠肺炎数据集由三个表组成,分别涉及每个国家/地区的每日确诊、死亡和恢复病例。每个表都以 wide(交叉表)格式显示数据,每一天都在一列中。这种格式很难在 Tableau 中使用,所以第一个主要的预处理步骤是将这些列中的数据转换为行(长格式)。

注意,这个操作可以在 Tableau 中用 Pivot 函数来完成。然而,对于大多数预处理任务,Python 通常是我的首选,因为它在处理大型数据集时具有灵活性和速度。

用 Pandas 函数melt可以很简单地将宽格式转换成长格式。

从宽格式转换到长格式

组合表格

为了便于分析,下一步是将确认、死亡和恢复表合并成一个表。然而,加拿大的数据出现了一个问题:confirmed & deaths表格按省/州显示加拿大的数据,而recoveries表格只显示全国的病例总数。在将表组合在一起之前,需要首先解决这个冲突,因为不匹配的值将被忽略。

我采用的方法是将加拿大的confirmeddeaths数据按国家汇总,以匹配recoveries表。

现在可以使用 Pandas 函数merge将这三个表组合起来。

注意:与上一步类似,Tableau 也可以在其数据源部分处理数据连接。

人口数据

我想使用的一个指标是感染率:confirmed / population。但是,CSSE 的新冠肺炎数据集中没有国家/地区的人口,因此我们需要结合其他来源:

[## 各国人口- 2020 年

2020 年按人口统计的世界各国

www.kaggle.com](https://www.kaggle.com/tanuprabhu/population-by-country-2020)

组合不同数据源时的一个常见问题是不匹配的值名,在我们的例子中是国家名。通过比较数据集的国家列,可以简单地识别这些值。

有 13 个不匹配的国家名称。可惜没有快捷的方法替代它们,那就用蛮力吧!

制作 Tableau 仪表板

现在我们的数据已经清理完毕,让我们来构建一个仪表板吧!

Tableau 是我的首选工具,主要是因为它可以制作各种各样的图表,并且高度可定制,同时仍然相对快速和方便。

完整的新冠肺炎仪表板有大量的组件。我将强调更重要或更复杂的图表/特性,而不是讨论它们中的每一个。你也可以在本文末尾找到我的 Tableau 工作簿,看看每个组件是如何制作的。

注意:如果你不熟悉 Tableau,我强烈推荐以下文章:

[## Tableau 教程-基础

容易学,难掌握。

medium.com](https://medium.com/@lucyji/tableau-tutorial-the-basics-1060a856f8b2) [## 使用 Tableau 实现数据可视化

了解如何使用 Tableau 分析和显示数据,并做出更好、更数据驱动的决策。

medium.com](https://medium.com/@parulnith/data-visualisation-with-tableau-150f99a39bba)

过滤最新日期

我们的数据是一个时间序列,每个条目显示在一个地点的特定日期的累积病例。因此,简单地汇总数据是没有意义的。例如,A 天有 2 个案例,b 天有 3 个案例。这两天的总和(5 个案例)不能说明任何有意义的信息。

相反,我们可能会问这样一个问题:今天或过去任何一天的确诊病例数是多少?一种解决方案是创建一个计算字段,返回选定日期范围内的最新日期,然后将该字段用作仪表板中每个图表的筛选器。

筛选出所选日期范围内的最新日期

注意:简单地使用数据集中现有的日期列是不够的,因为无论选择什么日期范围,它总是在整个数据集中找到最近的一天。这是由于 Tableau 中的操作顺序。更多了解这个概念 这里

每日新病例

人们可能有兴趣了解每天新增病例的数量,以了解一个国家的哪个时间段更关键。这可以通过应用差值表格计算来完成。

计算每日新病例

工具提示

这可能是 Tableau 中我最喜欢的特征之一。工具提示允许我们在不占用任何额外空间的情况下向图表添加更多层次的信息!Tableau 中的工具提示可以包含各种各样的东西,从简单的文本、数字到整个图表!

将鼠标悬停在某个国家/地区时,会显示附加信息

只需创建一个要用作工具提示的图表,然后通过标记卡中的工具提示部分将其添加到另一个图表中。

将图纸添加到工具提示

行动

该控制面板的另一个关键特征是,控制面板中的大多数元素都是可交互的,即用户可以选择任何日期或国家,以将相关过滤器应用于整个控制面板。

与仪表板交互

这些行为可以通过活动功能(工作表>活动)进行添加和自定义。在这里,您可以在工作表中添加新操作或编辑现有操作。下例是在源工作表中选择国家/地区时,将国家/地区过滤器应用于目标工作表的操作。

在 Tableau 中创建动作

结论

你可以在这里找到完整的仪表盘。如果您对自己重新创建仪表板感兴趣,您可以在这个 Google Drive 文件夹中找到数据、预处理代码以及 Tableau 工作簿。

如果你有任何问题,请在下面的评论中告诉我!大家注意安全!

在 AWS Lambda 上构建自定义层

原文:https://towardsdatascience.com/building-custom-layers-on-aws-lambda-35d17bd9abbb?source=collection_archive---------3-----------------------

如何为您的无服务器应用程序构建自定义 Python 层。

许多开发人员在 AWS lambda 上导入自定义模块时会遇到问题,您会看到类似“没有名为 pandas 的模块”或“没有名为 numpy 的模块”的错误,大多数情况下,解决这一问题的最简单方法是将 Lambda 函数代码与模块捆绑在一起,并将其部署在 AWS Lambda 上,这最终会使整个项目变得很大,并且没有灵活性。为了解决这个问题,我们在 AWS Lambda 上使用了一种叫做的东西。

王占山Unsplash 上拍照

什么是层?

根据文档,层是包含库、自定义运行时或其他依赖项的 ZIP 存档。使用层,您可以在函数中使用库,而无需将它们包含在部署包中。

层允许你安装你的应用程序运行所需的所有模块,它为你部署你的 lambda 函数提供了灵活性,甚至通过层,你甚至可以制作你自己的定制代码,并作为层本身添加外部功能。它减轻了您管理包的压力,并允许您将更多的精力放在代码上。

层的好处

  • 使您的部署包更小,更容易部署
  • 该层也可以跨其他 lambda 函数使用。
  • 在控制台上快速更改代码。
  • Lambda 层支持版本控制,这允许您添加更多的包,并在需要时使用以前的包版本。

既然我们知道了什么层,也知道了它有多有用,那就让我们来构建一个层吧🚀。

第一步

创建一个新目录,并导航到您计算机上的目录:

israel@israel:~$ mkdir my-lambda-layer && cd my-lambda-layer

第二步

接下来,为需要安装的模块创建一个文件夹结构:

israel@israel:~/my-lambda-layer$ mkdir -p aws-layer/python/lib/python3.7/site-packages

文件夹结构非常重要,因为 Python 希望在那里找到您已经安装的模块。对于本文,我使用的是 python 3.7,如果您想使用不同的 python 版本,请将上面文件夹中的 python3.7 更改为所需的版本。

第三步

让我们安装我们的库。要为您的应用程序只安装一个模块,使用下面的命令,在这个例子中我将使用 numpy。

israel@israel:~/my-lambda-layer$ pip3 install numpy --target aws-layer/python/lib/python3.7/site-packages

要安装多个模块,请在基本目录中创建一个 requirements.txt 文件,并添加模块及其各自的版本:

israel@israel:~/my-lambda-layer$ nano requirements.txt 

添加你的模块,如下图所示:

作者图片

然后用下面的命令安装它们:

israel@israel:~/my-lambda-layer$ pip3 install -r requirements.txt --target aws-layer/python/lib/python3.7/site-packages

您应该有这样一个文件夹树:

作者图片

第四步

接下来,我们导航到 lambda-layer 目录,为将要上传的图层创建一个 zip 文件。

israel@israel:~/my-lambda-layer$ cd aws-layer

现在压缩整个文件夹:

israel@israel:~/my-lambda-layer/aws-layer$ zip -r9 lambda-layer.zip .

压缩包后,它将有一个名字“lambda-layer.zip”

您可以使用 AWS CLI 或 AWS web 控制台将 zip 文件上传到 lambda 层,对于本文,我将使用 AWS CLI

使用 CLI

要使用 AWS CLI 自动创建 lambda 层,您可以使用以下命令:

aws lambda publish-layer-version \
    --layer-name Data-Preprocessing \
    --description "My Python layer" \
    --zip-file fileb://lambda-layer.zip \
    --compatible-runtimes python3.7

您将看到在您的 Web 控制台中创建的 lambda 层。此外,请确保在 AWS CLI 上设置您的凭据和权限来部署该层。

作者图片

测试图层

为了测试 Lambda 层,我简单地创建了一个新的 Lambda 函数并将我的层添加到其中,您可以通过简单地单击 Layers 来完成此操作,然后单击 custom layer 选项并选择您刚刚部署到 Lambda 的层,最后,您应该会看到下图:

作者图片

然后在功能上,我导入了熊猫和 numpy

作者图片

然后我测试了这个函数,得到了如下的响应

作者图片

从图中可以看出,我们的代码能够成功运行,并且我们可以为我们的应用程序导入外部包。

摘要

到目前为止,我们已经看到了层的美丽和它们的强大,你应该在任何你想使用外部模块的地方使用它们,甚至当你有你自己写的定制代码的时候。

感谢阅读😊。

使用 Dash ( < 200 lines of code)

原文:https://towardsdatascience.com/building-dashboards-using-dash-200-lines-of-code-ae0be08d805b?source=collection_archive---------5-----------------------

Image credits: Clipartmax.com

构建仪表板仪表板是以有组织的方式可视化数据的用户界面(ui)。业务仪表板通常包含与特定目标或业务流程相关的关键绩效指标(KPI)的信息。仪表板是一个“快照”报告,它允许我们借助图表以有意义的方式显示给定时刻的数据,以便于参考和快速推断。

有用的仪表板的一些属性包括:

  • 可定制:一个优秀的仪表盘工具必须允许用户根据需要定制。
  • 可访问的:应该可以在各种媒体格式,如网络,移动等。适合在旅途中观看。
  • 可伸缩:应该有添加/更改 KPI 和添加/更改数据源的能力。

如今仪表板有各种形状和大小。许多公司推出现成的仪表板服务,如 SaaS。该软件通常有工作区,用户可以在其中拖放数据列和 KPI。最重要的仪表板工具之一是 Tabealu,它是一个独立的软件,允许用户构建强大的仪表板。然而,如果一个人想要构建他们的仪表板工具,他必须学习很多可视化、数据库管理和脚本的技术。下面是对所涉及的一些技术的简要概述(注意,这并不详尽):

仪表板的组件技术技术

  1. 可视化 D3,React JavaScript
  2. 数据库管理 SQL、AWS、MongoDB
  3. 脚本语言 R-shiny,Python,Java,Cpp

使用 Python,我们有几个选择:

Dash : Dash 是一个强大的开源库,可以帮助使用 Plotly、Flask 和 React 构建交互式和实时的基于 web 的仪表盘。

Jupyter 仪表板: 仪表板布局扩展是 Jupyter 笔记本的附加组件。它允许用户在表格或报告中安排笔记本输出,就像格式一样,并保存这种布局。其他用户需要安装该扩展才能查看此报告。

Pyxley:Pyxlex 是构建仪表板的另一个绝佳选择。它利用 React 和 Flask。然而,对此的支持和文档是有限的。

Bokeh :使用 D3 和 Tornado 的 web dashboard 工具可能需要一些 JavaScript 知识。

几更 : 蝴蝶结Spyre超集

我们用破折号是因为:

  • 易于使用:构建于 Plotly 和 React 之上,因此它易于编码,并且有许多可用的小部件。你只需要知道 Python 不需要学习 React 或者 D3。但是,如果您了解 React,那么 Dash 允许您通过一个包含的工具集将 React 组件打包成 Dash 可用的组件,从而插入 React 的广泛生态系统。
  • 文档 : Dash 有很好的文档记录,在 Stack Overflow 和 Github 上有一个很好的响应社区。

MVC 中的仪表板

大多数用户界面遵循 MVC 框架,MVC 是指模型-视图-控制器。每个相互连接的组件都是为承担开发过程中的特定任务而构建的。

型号:型号是仪表盘的心脏。该模型从数据库中获取数据,对其进行操作,并将其存储在对象中,这些对象稍后可以被视图使用。

控制器:控制器是用户与仪表板交互的方式。它通常从模型中请求数据,并将其呈现给视图。

视图:视图是数据呈现给用户或前端的地方。视图监视仪表板的可视部分。

MVC 框架降低了应用程序的复杂性,使其更易于维护;例如,开发人员可以选择更改 UI,而不需要更改任何支持代码。为了更基本的理解,我们将从 MVC 的角度来看 Dash。

图 1 MVC 图表

Dash 入门

如前所述,Dash 是一个简单的 Python 工具,可以帮助我们快速构建美观且响应迅速的 web 仪表盘。它同时支持 R 和 Python,由 Plotly 维护。它使用 React 作为控制器和视图,使用 Plotly 和 Flask 作为 MVC 设置中的模型。我们构建了一个 flask 应用程序,它在 web 浏览器上创建了一个仪表板,可以调用后端来重新呈现 web 页面的某些部分。

装置

使用pip可以非常容易地安装DashPlotly。建议使用虚拟环境管理器(如Conda)。

pip install dash==0.26.3  # The core dash backend
pip install dash-renderer==0.13.2  # The dash front-end
pip install dash-html-components==0.11.0  # HTML components
pip install dash-core-components==0.28.1  # Supercharged components
pip install dash_table_experiments #for tables
pip install plotly --upgrade # Plotly graphing library

对于数据操作,我们安装了pandas

pip install pandas

请查看仪表板安装指南以获取这些工具的最新版本。

现在我们转到问题陈述。

问题陈述

在之前的帖子中,我们谈到了使用 Scrapy 构建我们自己的数据集,这教会了我们如何使用网络抓取来下载数据。我们从Metacritic.com图书馆下载了有史以来的前 500 张专辑。以下是数据集的快照:

我们看到每张专辑都有流派、艺术家、发行日期、元评分和用户评分。

我们将构建一个包含以下四个组件的仪表板:

  1. 类型的交互式气泡图
  2. 每个流派十年流行度直方图(条形图)
  3. 元得分/用户得分趋势(折线图)
  4. 按元评分/用户评分列出的前 10 位最受欢迎的艺术家列表

用户可以通过下拉菜单与条形图进行交互,根据平均元分数或平均用户分数获得前 10 名艺术家。用户还可以与气泡图进行交互;在悬停时,它会改变条形图,给出该流派每十年出版的专辑数量。

实体模型

在纸上或使用软件(Adobe Illustrator 和 Balsamiq 是专业人士使用的很好的工具)建立一个小的仪表板模型是一个很好的实践。

这里,我们使用 MS PowerPoint 构建了一个简单的静态视图。

图 2 仪表板模型

MVC 框架中的 Dash

我们将把我们的 dash 组件分成三个部分:

  1. 数据操作(模型):执行从文件中读取数据的操作,并操作数据进行显示
  2. 仪表板布局(视图):在网页上直观地呈现数据
  3. 组件(控制器)之间的交互:将用户输入转换为数据操作和重新渲染的命令。

初始化

这里我们导入相关的库。

import dash
import dash_core_components as dcc
import dash_html_components as html
import dash_table_experiments as dt
import pandas as pd
import plotly.graph_objs as go
from dash.dependencies import Input, Output, State, Event
import random

数据操作(模型)

这里我们在一个csv中有数据,所以我们可以使用pandas简单地导入它。然而,如果数据在一个SQL数据库或者某个地方的服务器上,我们需要建立一个数据连接,将数据连接到我们后端的熊猫数据框架,剩下的过程将是相同的。

熊猫的数据操作

我们假设读者有关于熊猫的基本知识,并且能够理解数据操作。基本上,我们为每个仪表板组件创建了 4 个主表:

  1. df_linechart :该表按年份对数据进行分组,给出了专辑数量、平均元评分和平均用户评分。我们还将用户分数乘以 10,以得到与元分数相同的分数。这将用于绘制分数趋势图。
  2. df_table :该表按艺术家对数据进行分组,并给出专辑数量、总元评分和总用户评分。generate_table 函数使用这个表获得按用户得分或元得分排序的前 10 行。这是用来画表的。
  3. df_bubble: 此表按流派对数据进行分组,并给出专辑数量、平均元评分和平均用户评分。专辑的数量成为我们气泡的大小,平均分数成为坐标轴。这是用来画气泡图的。
  4. df2_decade: 此表按流派和十年对数据进行分组,并返回每个十年中每个流派的专辑数量。这是用来画条形图的。
##############################################################
#DATA MANIPULATION (model)
##############################################################df= pd.read_csv("top500_albums_clean.csv")
df['userscore'] = df['userscore'].astype(float)
df['metascore'] = df['metascore'].astype(float)
df['releasedate']=pd.to_datetime(df['releasedate'], format='%b %d, %Y')
df['year']=df["releasedate"].dt.year
df['decade']=(df["year"]//10)*10
#cleaning Genre
df['genre'] = df['genre'].str.strip()
df['genre'] = df['genre'].str.replace("/", ",")
df['genre'] = df['genre'].str.split(",")
#year trend
df_linechart= df.groupby('year')  .agg({'album':'size', 'metascore':'mean', 'userscore':'mean'})  .sort_values(['year'], ascending=[True]).reset_index()
df_linechart.userscore=df_linechart.userscore*10
#table
df_table= df.groupby('artist').agg({'album':'size', 'metascore':'sum', 'userscore':'sum'})
#genrebubble
df2=(df['genre'].apply(lambda  x: pd.Series(x)) .stack().reset_index(level=1, drop=True).to_frame('genre').join(df[['year', 'decade', 'userscore', 'metascore']], how='left') )
df_bubble=  df2.groupby('genre')  .agg({'year':'size', 'metascore':'mean', 'userscore':'mean'})  .sort_values(['year'], ascending=[False]).reset_index().head(15)
df2_decade=df2.groupby(['genre', 'decade']).agg({'year':'size'}) .sort_values(['decade'], ascending=[False]).reset_index()

仪表板布局(视图)

布局决定了仪表板在部署后的外观。Dash 为所有组件提供了 Python 类。组件保存在dash_core_componentsdash_html_components库中。你也可以用JavaScriptReact构建自己的组件。

响应式布局简介

我们为我们的仪表板使用了一个引导布局。简单地说,Bootstrap 通过在网格中包含组件来标准化组件的位置。它将屏幕分成 12 列,我们可以定义任意多的行。

因此,我们的仪表板将看起来像这样:

Row
    Column-12 (title)
Row
    Column-6 (Table of most top 10 popular artist)
    Column-6 (Meta-score/User-score trends- line chart)Row
    Column-6 (Interactive Bubble chart of genres)
    Column-6 (Histogram of decade popularity (bar chart)

这是模型的另一个可视化:

图 3 带有引导网格的实体模型

添加样式

我们可以使用.append.css命令将自定义的CSS添加到我们的仪表板中。

app = dash.Dash()
app.css.append_css({
"external_url": "  https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css"
})

我们还添加了引导所需的JavaScript库。最新版本请参考引导页面:

# Bootstrap Javascript.
app.scripts.append_script({
"external_url": "https://code.jquery.com/jquery-3.2.1.slim.min.js"
})
app.scripts.append_script({
"external_url": "https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js"
})

布局基础

布局骨架可以定义为

html.Div(  
[  
SOME_DASH_OR_HTML_COMPONENT(  
	id,  
	content (data),  
	style (a dictionary with the properties))  
], className (name of the Bootstrap Class)  
)

classname将是引导类名,可以是行或列。

多亏了core components,Dash 使得绘制图形变得非常容易。我们可以使用函数来绘制组件,也可以内联编写代码。

添加表

我们可以使用html.Table标签或者导入Plotly表格。这里我们使用 html 原生表。

def  generate_table(dataframe, max_rows=10):'''Given dataframe, return template generated using Dash components
'''return html.Table(
# Header
[html.Tr([html.Th(col) for col in dataframe.columns])] +
# Body
[html.Tr([
	html.Td(dataframe.iloc[i][col]) for col in dataframe.columns
]) for i in  range(min(len(dataframe), max_rows))],style={'width': '100%', 'display': 'inline-block', 'vertical-align': 'middle'}
)

简单地说,该函数获取一个pandas数据帧,并将其强制转换为一个表。

图表

Dash 使用 Plotly 图形布局。我们在一个dcc.Graph组件中定义一个图形,在一个图形对象(定义为go)中定义这个图形。在这个图形对象中,我们设置数据、样式和布局。每种类型的图表都有不同的相关组成部分。我们在这里讨论三种类型的图形:

添加线图

对于线图,我们必须选择带有go.ScatterScatter类型的图,在模式中,我们定义lines,如下所示。我们还需要为每条线定义xy的数据。

在布局部分,我们可以选择显示图例、标题和其他样式元素。

html.Div(
[ #Line Chart
dcc.Graph(id='line-graph',
figure=go.Figure(
	data = [
	go.Scatter(
		x = df_linechart.year,
		y = df_linechart.userscore,
		mode = 'lines',
		name = 'user score'
		),
	go.Scatter(
		x = df_linechart.year,
		y = df_linechart.metascore,
		mode = 'lines',
		name = 'meta score'
		),
	],
	layout=go.Layout(title="Score trends")
)),], className = "col-md-6"

添加气泡图

类似地,对于气泡图,我们定义了一个go.Scatter,但是使用了mode = markers。我们还可以定义一个文本组件,当鼠标悬停在标记上时,它会给出文本。稍后在我们的动态视图中将使用此文本填充我们的第四个条形图“十年流行度”。

Markers是图表上的点或气泡,我们可以通过传递带有各种选项的字典对象来进一步定制标记:

颜色:用于markerscolors列表,可以是颜色的名称,也可以是色标的数字列表。这里我传递了一个 0 到 100 之间的随机数字列表。

尺寸 : Size指定气泡的尺寸。这里我们把专辑的数量作为大小。所以专辑数量越多的流派泡沫越大。通过尺寸,我们可以通过传递sizemodesizerefsizemin组件来进一步定制尺寸。

html.Div([
dcc.Graph(id='bubble-chart',
	figure=go.Figure(
		data=[
			go.Scatter(
				x=df_bubble.userscore,
				y=df_bubble.metascore,
				mode='markers',
				text=df_bubble.genre,
				marker=dict(
					color= random.sample(range(1,200),15),
					size=df_bubble.year,
					sizemode='area',
					sizeref=2.*max(df_bubble.year)/(40.**2),
					sizemin=4
))],
layout=go.Layout(title="Genre poularity")
))], className = "col-md-6"
),

添加条形图

最后,我们使用一个函数绘制一个条形图。注意,我们可以直接把这个部分粘贴到 html div中,但是因为我们希望这个图形是交互式的,所以我们使用了一个可以在回调中使用的函数。我们用go.Bar

def  bar(results):
gen =results["points"][0]["text"]
figure = go.Figure(
	data=[
	go.Bar(x=df2_decade[df2_decade.genre==gen].decade, 	y=df2_decade[df2_decade.genre==gen].year)
	],
	layout=go.Layout(
	title="Decade popularity of " + gen
))
return figure

在这里,我们已经线框应用程序下一步,我们填充数据和添加控件。整个布局代码:

#generate table
def  generate_table(dataframe, max_rows=10):
'''Given dataframe, return template generated using Dash components
'''
return html.Table(
# Header
	[html.Tr([html.Th(col) for col in dataframe.columns])] +
# Body
	[html.Tr([html.Td(dataframe.iloc[i][col]) for col in dataframe.columns]) for i in  range(min(len(dataframe), max_rows))], style={'width': '100%', 'display': 'inline-block', 'vertical-align': 'middle'}
)#generate bar chart
def  bar(results):
	gen =results["points"][0]["text"]
	figure = go.Figure(
		data=[
			go.Bar(x=df2_decade[df2_decade.genre==gen].decade, y=df2_decade[df2_decade.genre==gen].year)
		],
	layout=go.Layout(
		title="Decade popularity of " + gen
	))
	return figure# Set up Dashboard and create layout
app = dash.Dash()
# Bootstrap CSS.
app.css.append_css({
"external_url": "https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css"
})
# Bootstrap Javascript.
app.scripts.append_script({
"external_url": "https://code.jquery.com/jquery-3.2.1.slim.min.js"
})
app.scripts.append_script({
"external_url": "https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js"
})
#define app layout
app.layout =  html.Div([
	html.Div([
		html.Div([
			html.H1("Music Dashboard", className="text-center", id="heading")
			], className = "col-md-12"
			),
		],className="row"),
	html.Div(
		[ #dropdown and score
		html.Div([
			html.Div(
				[
				dcc.Dropdown(
				options=[
					{'label': 'userscore', 'value': 'userscore'},
					{'label': 'metascore', 'value': 'metascore'},
				],
				id='score-dropdown'
				)
		], className="col-md-12"), html.Div(
		html.Table(id='datatable', className = "table col-md-12")),
		],className="col-md-6"),
	html.Div(
		[ #Line Chart
		dcc.Graph(id='line-graph',
	figure=go.Figure(
		data = [
		go.Scatter(
			x = df_linechart.year,
			y = df_linechart.userscore,
			mode = 'lines',
			name = 'user score'
			),
		go.Scatter(
			x = df_linechart.year,
			y = df_linechart.metascore,
			mode = 'lines',
			name = 'meta score'
			),
		],
		layout=go.Layout(title="Score trends")
		)
		),
		], className = "col-md-6"
	),
	], className="row"),
	html.Div(
		[
		html.Div(
			[
			dcc.Graph(id='bubble-chart',
			figure=go.Figure(
			data=[
				go.Scatter(
				x=df_bubble.userscore,
				y=df_bubble.metascore,
				mode='markers',
				text=df_bubble.genre,
			marker=dict(
				color= random.sample(range(1,200),15),
				size=df_bubble.year,
				sizemode='area',
				sizeref=2.*max(df_bubble.year)/(40.**2),
				sizemin=4
				)
			)
		],
	layout=go.Layout(title="Genre poularity")
	)
	)
	], className = "col-md-6"
	),
	html.Div(
		[
		dcc.Graph(id='bar-chart',
			style={'margin-top': '20'})
		], className = "col-md-6"
		),
		], className="row"),
], className="container-fluid")

不要被代码的大小吓到。代码以这种方式格式化,以便于理解。注意每个 div 组件是如何定义一个类的。

组件之间的交互(控制器)

现在我们已经了解了布局,让我们来看看控制器。在这个控制面板中,我们有两个交互式组件,一个是根据分数类型变化的表格,另一个是根据所选类型填充的条形图,气泡图中的气泡。

我们的控制器框架将看起来像:

@app.callback(
	Output(component_id='selector-id', component_property='figure'),
		[
		Input(component_id='input-selector-id',component_property='value')
		]
	)
def ctrl_func(input_selection)

这里我们有 4 个部分:

回调 : @app.callback是处理输入输出的回调函数。我们的应用程序的输入输出是特定组件的属性。

Input :这将组件使用的id作为输入,以及我们需要捕获的那个组件的属性。这可以是值、hoverDataclickData等等。

输出:输出获取要更改的组件的 id,以及通常会更改的属性,这可以是图形,也可以是子图形。

控制功能 : cltr_function定义了输出的 html 将如何变化。

除此之外,我们还有状态,它允许我们添加除输入输出之外的附加信息。

简而言之,应用回调自动捕捉输入中的任何变化,并根据cltr_function定义更新输出。我们可以在一个回调中有多个输入和多个输出。

在我们的例子中,回调的代码如下所示:

##############################################################
#DATA CONTROL (CONTROLLER)
##############################################################@app.callback(
	Output(component_id='datatable', component_property='children'),
	[Input(component_id='score-dropdown', component_property='value')]
	)def  update_table(input_value):
	return generate_table(df_table.sort_values([input_value], ascending=[False]).reset_index())@app.callback(
	Output(component_id='bar-chart', component_property='figure'),
	[Input(component_id='bubble-chart', component_property='hoverData')]
	)def  update_graph(hoverData):
	return bar(hoverData)

在第一次回调中,我们从下拉列表中给出输入。回调捕获下拉列表的值,并将其传递给生成给定表的函数更新表。然后,它将表格 html 数据传递给 datatable 组件,我们可以在仪表板中看到相关的表格。

在第二次回调中,数据在悬停时从气泡图组件传递到条形函数。这个数据是一个字典,所以我们使用 text 键提取相关的流派细节,然后将数据传递给 bar 函数。条形图功能对给定流派的df_decade数据进行子集划分,并绘制条形图。请注意这里涉及了多少不同的组件,而它只需要 10 行代码!

初始化

需要在代码中添加以下两行来运行应用程序

if  __name__ == '__main__':app.run_server(debug=True)

运行应用程序

该应用程序可以通过以下方式运行:

python app.py

我们完事了。

您将看到如下消息:

服务 Flask app“songsapp”(惰性加载)
环境:生产
警告:不要在生产环境中使用开发服务器。
使用生产 WSGI 服务器。
调试模式:在
上运行
http://127 . 0 . 0 . 1:8050/(按 CTRL+C 退出)
重启 stat
调试器处于活动状态!
调试器引脚:282–940–165

仪表板在链接上可用(可在执行时更改参考您自己的代码执行中的链接):

[http://127.0.0.1:8050/](http://127.0.0.1:8050/)

我们的仪表板看起来像:

图 4 运行中的仪表板

后续步骤—托管

最后,我们的仪表板准备好了。下一步是将代码托管在服务器上,以便可以从任何设备访问它。虽然托管很容易,但涉及的步骤需要对托管有深入的理解。我们没有给读者增加新概念的负担,而是留给她一些主机教程的快速链接。这对经验丰富的开发人员和新手都有用。

一些流行的托管选项有:

Heroku : Dash 有一个关于在 Heroku 上托管应用的简短教程。或者,这里是 Heroku 的教程,用于部署 python 应用程序。

亚马逊关于部署 python 应用的教程。

结论

这就是我们如何在< 200 lines of code. To really understand the real power of dash, do check out the dash gallery 中构建一个简单而又互动的仪表板,以及人们用它完成的令人惊奇的事情。这篇文章的完整代码可以在这个 git repo 中找到。

有用的资源

Dash 教程
Dash 核心组件库
使用 Dash 编写自己的组件
Dash 视频教程

在创业公司建立数据团队

原文:https://towardsdatascience.com/building-data-team-at-startup-5bf51cdb6926?source=collection_archive---------39-----------------------

启动体验

什么是数据团队,公司何时需要它,我如何组建数据团队,以及组建团队的建议

这里有很多关于管理数据团队的文章[1]。约翰·霍普斯金大学甚至有一门关于建立数据科学团队的课程[2]!我就分享一下自己在创业公司搭建数据团队的亲身经历。这篇文章将分享什么是数据团队,初创公司何时以及为什么需要这个团队,我是如何组建数据团队的,最后我将提出组建数据团队的建议。本文旨在为那些已经开始思考如何组建数据团队的新创公司领导者提供见解。

艾里印度尼西亚简介

一家成立于 2015 年的 iry 是印度尼西亚的一家虚拟酒店运营商5。我们投资了可能改变住宿业的技术。不幸的是,由于目前疫情的局势,我们不得不关闭。然而,请随意浏览我们过去写的关于我们技术的文章![7]

什么是数据团队

数据团队是一个拥有功能的团队,从巨大且压倒性的数据源中获取值。每个团队都有自己的功能,根据功能,我脑海中的第一个定义是输入,将输入转换为输出[3]。

资料来源:unsplash.com

大量的数据来源是输入。正如我所提到的,有 3 个特征需要满足:大,压倒性的,和各种各样的(因此我说数据源),这个特征直接从大数据的定义翻译过来[4]。数据量通常很大,因此需要特殊的软件架构来降低存储数据的成本。这里的压倒性意味着数据的速度非常快,应该小心处理,以降低处理的延迟。最后,各种数据格式的各种手段会增加处理数据本身的复杂性。这些因素将需要大量的努力,因此组建一个团队是有意义的。

带来价值就是产出。数据团队需要以描述性、规范性或预测性分析的形式为业务带来价值。这个团队的大部分努力和价值将花费在描述性分析上,无论是仪表板、报告还是 EDA 的形式。规定性分析将需要来自其他部门的更多合作,如营销、产品、设计。当业务和团队技能都成熟时,将进行预测分析。

公司什么时候需要数据团队?

前同事最近问我,如果我们处于创业的早期阶段,我们应该在什么时候组建团队?在我看来,这是一个有趣的问题,因为甚至没有人费心问我这个简单但具有挑战性的问题。我在创业的相对早期阶段直接来到数据团队,那时我们有一个不同的数据团队领导,所以我的 CTO 已经预见到这家公司需要一个数据团队。

1gb 事务规则

我想出了一个1gb 交易规则。有些人需要对何时组建团队,或何时雇佣数据团队负责人做出可量化的调整。有人可能会问,为什么是 1gb?不是很容易存在外置闪存盘里吗?这条规则的美妙之处,或者我应该说是指导原则,不在于精确的边界线本身,而在于其他的考虑因素是什么。关于数据团队的输入,我们可以将这个问题扩展为:我们实现这个目标的速度有多快?以及各种数据来源的假设。

资料来源:unsplash.com

一家公司实现 1gb 交易的速度有多快?现在,对于商业人士来说,这可能很难将其转换成时间估计。您需要假设一个交易将贡献多大的规模。我假设您至少需要 10 列,每一列都是一个事务的 255 长度的字符串/varchar,结果大约是 2.5 千字节(1 千兆字节= 1 百万 kb)。这将把问题转化为:一家公司实现 40 万笔交易的速度有多快?现在商业人士可以投射这种价值。我们可以进一步分为 2 个决策界限:是今年吗?还是不是?如果是在今年,你可能会考虑组建一个数据团队,否则,你可能会征求你的工程经理的意见。

当实现 1gb 事务或 400k 事务时,我还假设数据源会有所不同。您的公司可能有多种产品来实现这一数字,或者收集大量其他数据,例如可以存储在各种类型的数据库中的调查或用户活动数据。

我如何组建数据团队

我会从最后开始。这些是我和我在数据团队中的领导一起定义的一些技术角色(不包括领导变动)。

描述过于简单的角色名称

其他公司的角色会有所不同,比如有商业智能工程师、数据工程师、数据仓库工程师、机器学习工程师、数据运营等。从局外人的角度来看,你可能会问,为什么?以我的经验来看,这种分化现象的发生是因为就业市场和技术环境下工作描述演变的影响。

从简单开始

我从两个非常简单的角色开始:数据科学家软件工程师。“数据科学家”的头衔似乎比“数据分析师”更吸引人才。当我说“人才”不仅仅是一些人,而是一些至少有动力去学习更多“酷”的东西的人。然而,这些担任“数据科学家”角色的人所做的是

然后现实击中了我:处于早期阶段的企业并不真的需要实施复杂的统计和人工智能解决方案,除非这些是你的核心商业价值。然后是下一个区别:“数据分析师”的角色。该角色的目的是:维护数据湖、制作报告、制作描述性仪表板。然而,他们没有将这些角色与数据科学家完全分离,这没什么。

在某个时候,我和一个技术招聘人员很难筛选候选人,因为有太多我们没有预料到的背景变化。我已经检查了我们的工作描述和候选人的要求,它似乎很好,然后如何使这个过滤更有效?我假设“数据分析师”这个名字太普通了,甚至可能是一个同音异义词,对不同的人有多种解释。我决定对其他公司进行基准测试,并推出了“商业智能分析师”。从那以后,我们设法将更多相关的候选人加入这个人才库。

软件工程师呢?为什么没有进化?我决定通过设计第三方解决方案来外包 80%的工程负载。它使我的工程规模保持在平均 2 人。因为这个数字,我很犹豫是否要添加更多的角色来拆分软件工程角色。

组建团队的建议

在合适的时间组建团队

使用 1gb 事务规则,您可以预测组建数据团队的需求。形成得太快可能会导致没有工作要做,并可能导致团队成员的积极性下降。它还可能会扰乱您的技术重点和预算,正如您可能知道的那样,大数据技术并不便宜,需要大量的精力来维持。组建得太晚可能会导致积压太多的工作要做,你必须记住,寻找最佳候选人需要你的资源,尤其是高级和领导职位。

从数据工程开始

数据工程是数据团队的核心。它为其他数据角色提供了基础和架构。如果您选择购买您的数据基础设施,您可能需要一位经验丰富的数据工程师作为您的顾问,因为当今的第三方选择实在太多了!如果您选择使用开源服务构建自己的解决方案,您肯定需要有经验的数据工程师和专门维护该架构的工程团队。

内部招聘作为备选方案

招聘对你的团队来说是一个重要的过程。这将决定你的团队能否成功。但是,以我的经验来看,为数据团队吸引人才是超级难的!软件工程角色中有更多的人,幸运的是,他们的技能可以转移到数据角色中。

结论

以上是我在 Airy 印尼建设数据团队的经验和看法。人们会有不同的经历和观点,我已经列出了我的可以借鉴的。我很荣幸有机会在那里工作,见到了许多杰出的人。我希望这篇文章和我即将发表的关于管理数据团队的文章会有所帮助,尤其是对初创企业的领导者。我将欢迎任何建设性的意见和讨论。谢谢大家!

参考

[1]https://hbr.org/2018/10/managing-a-data-science-team

[2]https://www . coursera . org/lecture/build-data-science-team/managing-the-data-science-team-tblyl

[3]https://en . Wikipedia . org/wiki/Function _(数学)

https://en.wikipedia.org/wiki/Big_data

[5]https://medium . com/@ ariessantoso/bera wal-Dari-airy-rooms-menjadi-airy-Indonesia-cecd 749915 f 7

[6]https://www . the jakartapost . com/news/2020/05/08/hotel-start-up-airy-closes-permanent-due-due-due-to-pandemic . html

https://medium.com/airy-science

使用预训练的模型构建+部署神经风格转移应用程序

原文:https://towardsdatascience.com/building-deploying-a-neural-style-transfer-app-with-pre-trained-models-661bbefc74cd?source=collection_archive---------46-----------------------

有 Flask、Python3、OpenCV、Heroku(还有一堆其他的东西)

样式图像和上传的图像

网友们好!

我觉得我在学习软件开发时面临的一个问题是,许多教程往往太专业(而且不好玩)。这可能是因为软件开发的本质,但我觉得掌握技术概念与构建有趣的迷你项目一样重要,这将继续鼓励和鞭策我们。༼ つ ◕_◕ ༽つ

我认为制作一些简单的图像处理工具并在网上展示给我们的朋友和家人会很酷。因此,今天的教程将是一个图解式的演示,演示如何构建一个 web 应用程序:

  • 允许用户上传图像并选择样式
  • 使用预训练. t7 模型在图像上应用神经风格转移
  • 显示上传的图像和样式化的图像

该项目的目录结构

快速解释一下上面的目录结构——除了 test.py 之外,所有文件都是需要的。test.py 用于我在本地测试并查看我的 neuralStyleProcess.py 脚本是否正常工作。我的项目中有 11 个. t7 模型,但你可以决定想要多少(从ProGamerGov*和 jcjohns 下载)。最后,您可以忽略 pycache 因为它是自动生成的。*

此 web 应用程序的可视化说明

在上图中,我试图只包括最重要的部分,希望能帮助你们理解 web 应用程序是如何工作的(我把我的应用程序命名为 pyFlaskOpenCV,我知道这是个糟糕的命名)。

Let’s start with the app.py script. Think of this as the app’s root — we use this to set up the routes (something like the ‘control’ that tells things how to interact). 这篇文章很好的解释了 Flask。从上图中我们可以看到,render_template()将渲染我们的 upload.html 模板。这意味着我们的 upload.html 页面将显示给我们,正如我们从图中看到的,有一个 POST 方法的表单。使用 action="{{url_for('upload')}} ",我们调用 def upload(),在该函数中,我们允许用户使用以下代码上传图像:

我们呈现 complete.html。

在 complete.html,我们再次使用“url_for('send_processed_image ',parameter1,parameter2)”来调用 send_processed_image 函数,该函数将调用我们的 python OpenCV 图像处理脚本 neuralstyleprocess . py。neuralstyleprocess . py 使用 imwrite 将已处理的图像写入文件,并返回已处理的图像文件名,因此 send_processed_image 函数知道要从目录向 complete.html 发送什么。

关于 neuralStyleProcess.py 脚本,我的实现很大程度上改编自 pyImageSearch 的教程。他的文章全面详细地解释了它是如何工作的。我唯一改变的部分是——我没有显示图像,而是进行了 imwrite,并删除了图像后期处理的缩放部分。

使用 Heroku 在线部署

您不必使用 Heroku,还有其他选择,但因为我们的模型占用相当多的空间(它们的大小可以累加),请记住选择能够为您提供更多存储空间的服务器(例如,Heroku 的免费层对我来说很好,但 PythonAnywhere 的免费层就不行)。

继续我们之前的图表,我们仍然需要为 Heroku 提供三个额外的文件,这样 Heroku 就“知道”如何处理我们的 web 应用程序。这些文件是— Procfile、requirements.txt、Aptfile。

同样,这里有一个图表可以更好地解释事情:

霓虹灯颜色,非常酷

一个 Procfile 是你的应用程序根目录下的一个文本文件,用来明确声明启动你的应用程序应该执行什么命令。我的情况是这样的:

*web: gunicorn app:app*

好吧,请让我咆哮。因此,我花了几个小时调试!!!为什么?因为他们显然很在乎空间。因此,如果您编写了—web:guni corn app:app/web:guni corn app:app/或任何其他配置(好吧,我没有尝试所有,但我知道它的空间(非常)敏感ಥ_ಥ)

叹息,继续前进…

我们使用 Aptfile 来包含我们需要安装在 dyno 上的其他附加软件。dyno 是什么?根据 HerokuHeroku**使用的容器称为dynosDynos是隔离的虚拟化 Linux 容器,旨在根据用户指定的命令执行代码****

下面是我的(OpenCV 需要以下库才能正确运行):

*libsm6libxrender1libfontconfig1libice6*

最后,我做了一个

*$pip freeze > requirements.txt*

来生成我的 requirements.txt。如果你对其中的内容感兴趣:

*click==7.1.1cycler==0.10.0Flask==1.1.1gunicorn==20.0.4imutils==0.5.3itsdangerous==1.1.0Jinja2==2.11.1kiwisolver==1.1.0MarkupSafe==1.1.1matplotlib==3.2.1numpy==1.18.2opencv-python==4.2.0.32pyparsing==2.4.6python-dateutil==2.8.1six==1.14.0Werkzeug==1.0.0*

使用 OpenCV-python 和 NumPy 之类的库最棘手的一点是,要弄清楚哪个版本可以协同工作,哪个版本不可以,这真的很繁琐。(有时 4.2.0.32 版本可以工作,但 4.1.0 可能不行。)这些都是适合我的版本。

后续步骤(一些建议)

  • 对上传的文件进行图像扩展名检查
  • 调整上传图像的大小
  • 将上传/处理的图像保存到数据库,以便数据持久保存
  • 其他图像处理脚本
  • 更好的 html 页面样式
  • 在第一页加载时添加一些指示符(例如加载动画)
  • 在它的基础上建立一些交互式的东西(例如用 javascript 等)
  • 不要选择预先训练好的模型,用生成性对抗网络(GAN)生成 art

好吧,希望这能帮助人们更好地理解事物是如何工作的。我不确定这是否是一种更好的浏览代码的方式,而不是将代码分成块,然后解释每一行——我个人发现大多数教程缺乏图解解释,所以我想我会以一种自己感觉最直观的方式来呈现事情——让我知道你们是怎么想的。

快乐创作!\ (•◡•) /

此处提供的代码:https://github.com/Reine0017/pyFlaskOpenCV

*试试看:【https://apricot-cupcake-90327.herokuapp.com/ *

使用 GCP AutoML Vision Edge、Tensorflow.js 和 GCP 应用程序引擎构建和部署图像分类 Web 应用程序

原文:https://towardsdatascience.com/building-deploying-an-image-classification-web-app-with-automl-vision-tensorflow-js-app-engine-08-3157958a3289?source=collection_archive---------22-----------------------

在本文中,我们将学习如何使用 AutoML 用最少的代码创建一个定制的图像分类 web 应用程序,并将其部署在 Google Cloud App Engine 上。

这将是一个很长的阅读。

对于这篇文章,我将建立一个模型,该模型将对一个人是否戴口罩进行分类。

你可以通过访问这个 链接 在有摄像头的笔记本电脑/PC 上检查我的项目,确保你的脸被很好地照亮,以便更好地预测。

到本文结束时,你将能够仅仅通过改变数据集来建立你的图像分类模型。

照片由米切尔罗Unsplash 拍摄

什么是 AutoML 视觉?

谷歌云汽车视觉

AutoML 允许我们用我们的数据来训练我们的定制模型。它使用 NAS(神经架构搜索)来找到训练我们模型的最佳方法。我们唯一需要做的就是收集数据,提高模型的准确性。它极大地减少了从头开始编写代码来训练模型的工作量。

涉及的步骤

设置&配置一个谷歌云项目

设置自动视觉

创建数据集

训练模型

评估模型

测试模型

将模型导出为 TensorFlow.js 模型

构建网络应用

在应用引擎上托管应用

想跳过这些步骤,直接跳到代码吗?这些都可以在我的 GitHub 存储库中找到,网址是:

[## 信号 h 08 pra shant/屏蔽检测

更新:部署在 GCP 应用引擎。请登录查看🔗面具检测项目🚨仅适用于笔记本电脑/ PC It…

github.com](https://github.com/singh08prashant/MaskDetection)

1.设置和配置谷歌云项目

前往谷歌云控制台,使用你的谷歌账户登录,点击链接创建一个新项目/选择一个现有项目。接下来,你需要为这个项目设置一个帐户。了解如何确认您的项目已启用计费

如果你是学生,你可以在edu.google.com申请价值 50 美元/年的免费学分,获得免费实践培训等,而无需提交任何信用卡信息。

2.设置自动视觉

现在,让我们通过以下步骤来启用云 AutoML API。

您可以通过点击左上角的导航菜单来查看包含谷歌云产品和服务列表的菜单。

导航菜单

导航菜单中选择API&服务 >

导航菜单> API 和服务>库

在搜索栏中输入“Cloud AutoML”。点击 Cloud AutoML API 结果。

在库中搜索云 AutoML API

点击启用启用云 AutoML API。这可能需要几分钟时间。

云 AutoML API 已启用

从工具栏的右上角激活 Google Cloud Shell。

激活云壳

如果出现提示,请单击继续等待外壳被提供和连接被建立。连接后,云壳应该是这样的:

云壳

在 Cloud Shell 中,使用以下命令为您创建环境变量项目 ID用户名,将<USERNAME>替换为您登录控制台时使用的 Google 帐户用户名:

**export PROJECT_ID=$DEVSHELL_PROJECT_ID****export USERNAME=<USERNAME>**

现在运行以下命令,为您的帐户授予 AutoML 管理权限:

**gcloud projects add-iam-policy-binding $PROJECT_ID \ 
    --member="user:$USERNAME" \
    --role="roles/automl.admin"**

接下来,我们将在区域us-central1中创建一个云存储桶,使用以下命令( 或者 来存储我们的图像,您可以使用 UI 通过转到导航菜单 > 存储 > 浏览器 > 创建 来执行相同的操作:

**gsutil mb -p $PROJECT_ID \ 
   -c standard    \
   -l us-central1 \
   gs://$PROJECT_ID-vcm/**

3.创建数据集

下载图片并根据它们的标签存储在单独的文件夹中,例如,我将戴面具的人的图片存储在名为“面具”的目录中,将不带面具的人的图片存储在名为“无面具”的目录中,并压缩这些文件夹以便上传。

我本地硬盘上的数据集

您可以使用此工具从互联网上批量下载图像。

[## Singh 08 prashant/Image-screwing-with python-and-selenium

一个 python 代码库,用提到的搜索词从 Google 浏览器下载图像。…

github.com](https://github.com/singh08prashant/Image-Scrapping-with-python-and-selenium)

现在,让我们前往新浏览器窗口&中的 AutoML UI 点击新建 数据集。

创建新数据集

输入一个数据集 nam e 并选择您的模型的目标。如您所见,我选择了多标签分类来查看标签“屏蔽”&“无屏蔽”的预测百分比。如果您希望您的图像一次属于一个类别,您可以选择单标签分类。点击创建数据集创建数据集。

将图像导入数据集

现在选择从您的计算机上传图像>点击选择文件>选择我们之前在本地磁盘上创建的 zip 文件。

接下来,您需要指定上传文件在云存储中的存储位置。点击浏览 &选择我们之前创建的名为-VCM的 bucket。

点击继续,等待文件导入到你的数据集中。

图像导入完成后,您将能够在数据集中的图像选项卡下看到它们各自的标签。如果您对图像或标签不满意,您可以使用 UI 工具编辑/删除它们。

理想情况下,我们应该为每个标签提供至少 100 张图片&为了更好地表现模型,我们应该为每个标签提供相同数量的图片。但是,如果每个标签至少有 10 张图片,您可以继续训练。数据集以 8:1:1 的比例自动分为训练集、验证集和测试集。

您可以在以下网址了解有关准备培训数据的更多信息:

[## 准备您的训练数据|云自动视觉对象检测

发送反馈一般图像要求支持的文件类型图像类型自动视觉对象检测模型有…

cloud.google.com](https://cloud.google.com/vision/automl/object-detection/docs/prepare?_ga=2.20768254.-754110532.1565768056&_gac=1.125051384.1587747564.CjwKCAjwnIr1BRAWEiwA6GpwNX8BFeLjryfMq9C2hfoL-9pCS1Wq6Z1a0K3bHFBZeQNceqTmBQCoTBoChQsQAvD_BwE)

数据集中导入的图像

4.训练模型

一旦您对数据集的导入和标记感到满意,进入训练选项卡并点击开始训练。

现在将要求您定义您的模型。为您的模型命名,并选择 Edge 以创建可下载的模型。点击继续。

定义您的模型

现在,您需要指定您希望模型是快速的还是准确的。要在这两者之间找到平衡,请选择最佳权衡选项并单击继续。

优化您的模型

然后,您将被要求为训练您的模型设置节点时间预算。你给的训练时间越多,准确率就越高。但是文档也告诉我们,如果模型停止改进,训练就会停止。选择推荐的节点小时预算是一个好的做法。

设定节点时间预算

点击开始训练,你就可以开始训练你的模型了。

当你的模特在训练的时候,你可以关闭标签,也许可以喝杯咖啡放松一下。培训完成后,您将收到一封电子邮件通知。

5.评估您的模型

一旦您收到电子邮件通知,您就可以开始评估您的模型,但在我们评估模型之前,我们应该了解 precisionrecall 的含义。精确度指的是相关结果的百分比。另一方面,召回指的是被你的算法正确分类的总相关结果的百分比。你可以在这里了解更多关于 precision 和 recall

混淆矩阵和其他模型指标

你可以看到许多新的术语,你可以在这里了解更多。

6.测试模型

测试下&使用选项卡,点击上传图像按钮上传图像并生成预测。您一次可以上传 10 张图片。

在线预测

您可能希望Remove Deployment避免任何不必要的账单费用。

祝贺您,您已经成功创建了一个图像分类模型。但是等等,其他用户无法与之互动。

7.将模型导出为 TensorFlow.js 模型

在我们继续之前,我们需要gsutil:一个 Python 应用程序,它允许我们从命令行访问云存储。

如果你已经安装了 PyPI (Python 包索引),你可以运行[pip install gsutil](https://cloud.google.com/storage/docs/gsutil_install#expandable-2-label)或者你可以 安装 gsutil 作为 Google Cloud SDK 的一部分。

测试&使用选项卡下,您可以看到导出和使用您的模型的许多不同选项,我们将使用 TensorFlow.js 模型。

TensorFlow.js 是一个帮助在浏览器中直接使用 ML 的库。AutoML 创建模型只需很少的时间,TensorFlow.js 是直接在浏览器中运行模型的最简单、最有效的方法。

使用你的模型

选择 TensorFlow.js >在相同区域 us-central1指定或创建一个桶,将模型导出为 TensorFlow.js 包,点击导出。导出完成后,在命令提示符或终端中运行以下命令,通过将<bucket-name>替换为将模型导出到& <local-folder>的存储桶的名称,将文件从云存储存储桶复制到本地目录,替换为您希望保存模型的本地目录的路径。

**gsutil cp gs://<cloud-storage-bucket>/model-export/icn/* <local-folder>**for example
gsutil cp gs://mask-detection-pbl-vcm/model-export/icn/* Downloads

一旦模型被下载,你会看到一个model.json文件,它包含张量信息以及权重文件名,.bin文件包含模型权重&一个dict.txt文件,包含标签,在我的例子中是Mask & No Mask

下载的模型

8.编写 Web 应用程序

8.1 创建 index.html 文件

在同一个文件夹中,创建一个index.html文件并复制以下代码:

<html>
<head>
<script src="[https://unpkg.com/@tensorflow/tfjs](https://unpkg.com/@tensorflow/tfjs)"></script>
<script src="[https://unpkg.com/@tensorflow/tfjs-automl](https://unpkg.com/@tensorflow/tfjs-automl)"></script>
</head><body >

<video autoplay playsinline muted id="webcam" width="224" height="224"> 
</video>

<div id="predictions-mask"></div>
<div id="predictions-no-mask"></div>

<button type="button" id="startPredicting" onclick="startPredicting()"> Start Predicting </button>
<button type="button" id="stopPredicting" onclick="stopPredicting()" >Stop Predicting</button>
<script src= "index.js"></script>
</center>
</body>
</html>

tfjs-automltfjs脚本包含运行模型所需的函数。如果您想要离线使用模型,您可以下载这些脚本的副本,并将其包含在您的html文件中。

标签在网页上创建一个视频元素。2 个<div>将用于写入模型的预测。这些按钮将分别用于启动&停止预测。脚本<index.js>是我们实现模型的地方。

8.2 创建 index.js 文件

现在,我们将在同一个文件夹中创建一个<index.js>文件,并复制以下代码:

const webcamElement= document.getElementById('webcam');let net;let isPredicting = false;function startPredicting(){
 isPredicting=true;
 app();
}function stopPredicting(){
 isPredicting=false;
 app();
}async function app(){
 console.log('Loading model..');
 net= await tf.automl.loadImageClassification('model.json');
 console.log('Successfully loaded model');

 const webcam = await tf.data.webcam(webcamElement);
 while(isPredicting){
 const img = await webcam.capture();
 const result = await net.classify(img);

 console.log(result);

 document.getElementById("predictions-mask").innerText=result['0']['label']+": "+Math.round(result['0']['prob']*100)+"%";
 document.getElementById("predictions-no-mask").innerText=result['1']['label']+": "+Math.round(result['1']['prob']*100)+"%";img.dispose();

await tf.nextFrame();

 }

}

您可能会被这段代码弄得不知所措,所以让我来为您解释一下。这里,在异步函数app()中,tf.automl.loadImageClassification()为我们加载模型model.json,并将其存储为net

tf.data.webcam()将设置网络摄像头。webcam.capture()将通过网络摄像头从实时输入中捕捉图像,并将其存储为img

然后,我们使用net.classify(img)将该图像传递给模型,预测结果存储在变量result中。

函数startPredicting() & stopPredicting()作为开关触发图像分类的无限循环。

现在你可能想知道这些行是做什么的:

document.getElementById("predictions-mask").innerText=result['0']['label']+": "+Math.round(result['0']['prob']*100)+"%";
 document.getElementById("predictions-no-mask").innerText=result['1']['label']+": "+Math.round(result['1']['prob']*100)+"%";

我们得到的输出以这种形式存储在result

[0: {label: “Mask”, prob: 0.048721060156822205}, 1: {label: “No Mask”, prob: 0.9479466080665588}]

上面的代码用来在 HTML 页面上把这个结果写成Mask: 5% & No Mask: 95%

最后,img.dispose()释放张量来释放内存& tf.nextFrame()通过等待下一个动画帧触发来提供一些喘息空间。

要运行 Chrome &网络应用下载和启动网络服务器,请选择代码所在的目录。

该应用程序应该如下所示:

Web 应用程序

随意添加一些 CSS 到网页上,让它看起来更漂亮。

9.在应用引擎上托管应用

登录到您的 Google 云控制台,启动云外壳并打开编辑器。

打开编辑器

在编辑器里面创建一个目录,姑且称之为www &在这个目录里面上传你所有与模型和网页相关的文件。

在根目录下(在www之外)创建一个文件app.yaml &将下面的代码粘贴在里面:

runtime: python27
api_version: 1
threadsafe: true

handlers:
- url: /
  static_files: www/index.html
  upload: www/index.html

- url: /(.*)
  static_files: www/\1
  upload: www/(.*)

app.yaml文件是一个配置文件,它告诉 App Engine 如何将 URL 映射到静态文件。在接下来的步骤中,您将添加处理程序,当有人访问您的网站时,这些处理程序将加载www/index.html,所有静态文件将存储在www目录中,并从该目录中调用。

保存文件。您的编辑器应该是这样的:

编辑器的目录结构

现在切换到云 shell 并运行以下命令来部署 web 应用程序:

gcloud app deploy

要在https://PROJECT_ID.REGION_ID.r.appspot.com启动浏览器并查看应用程序,请运行以下命令:

gcloud app browse

结论

这样,我们已经成功地在 Google Cloud 上构建和部署了一个图像分类 web 应用程序。非常感谢耐心阅读。

你可以在LinkedIn&Twitter上和我联系。

参考

[## 谷歌云平台|文档

Google 云平台产品和服务的全面文档、指南和资源。

cloud.google.com](https://cloud.google.com/docs) [## AutoML Vision Edge:加载和运行 TensorFlow.js 模型(第 2 部分)

在 Node.js 服务器上运行 TensorFlow.js 模型

heartbeat.fritz.ai](https://heartbeat.fritz.ai/automl-vision-edge-loading-and-running-a-tensorflow-js-model-part-2-9b4d62a7d5cc)

使用 TensorFlow 隐私建立差分私有机器学习模型

原文:https://towardsdatascience.com/building-differentially-private-machine-learning-models-using-tensorflow-privacy-52068ff6a88e?source=collection_archive---------41-----------------------

活动讲座

刘畅| TMLS2019

来自多伦多机器学习峰会的演讲:【https://torontomachinelearning.com/

关于演讲者

刘畅是一名应用研究科学家,也是格鲁吉亚撞击小组的成员。她带来了她在数学和组合优化方面的深入知识,以帮助格鲁吉亚的投资组合公司。Chang 拥有多伦多大学运筹学应用科学硕士学位,她在该校专攻组合优化。她还拥有滑铁卢大学的数学学士学位。

关于谈话

本次讲座将介绍差分隐私及其使用案例,讨论 TensorFlow 隐私库的新组件,并提供如何应用这些工具的真实场景。近年来,世界变得越来越受数据驱动,个人和组织对其敏感数据的隐私有了更强烈的意识和关注。

已经表明,在不泄露一些信息的情况下,不可能公开关于私有数据库的统计结果。事实上,整个数据库可以从几个查询结果中恢复。

在对敏感数据库的隐私进行研究后,一些大公司,如谷歌、苹果和优步,已经转向差分隐私来帮助保证敏感数据的隐私。主要技术公司的这种关注有助于将差异隐私带出研究实验室,进入软件工程和产品开发领域。

现在,小公司和软件创业公司正在采用差别隐私,并从中发现了巨大的价值。除了隐私保证之外,差异隐私方面的进步还允许企业释放更多的功能和增加数据的效用。

这些能力之一包括通过不同的私有集合模型从现有数据中转移知识的能力,而没有数据隐私问题。随着差分隐私在大型科技公司中获得认可,让普通公众和小型创业公司更容易获得当前最先进的研究的努力正在进行中。

作为对更广泛社区的贡献,Georgian Partners 向 TensorFlow 社区提供了其差分隐私库。总之,我们将在一个用户友好且易于使用的 API 中提供差分私有随机梯度下降,允许用户训练私有逻辑回归。

用 Python 构建带有角度和散景图的动态仪表盘

原文:https://towardsdatascience.com/building-dynamic-dashboards-with-angular-and-bokeh-51668a5367f1?source=collection_archive---------20-----------------------

一个灵活的动态仪表板示例,使用散景图、Angular 和 Python 作为后端。

如果我们考虑工业 4.0 应用,一般来说,对其有必然的要求。

它们应该是:

  • 模块化,
  • 可扩展,
  • 易于集成到现有网络中,并且
  • 可互操作-可以轻松连接到任何其他服务。

动态图表示例

例如,这种应用程序的主要目标之一是独立于工作平台及时查看数据。数据的来源应该与技术无关,可能来自其他物联网设备,如传感器或服务器或其他连接的资源。

我们已经向您展示了…

是这可以使用我们之前在 Medium 上介绍的 web-app 架构来实现。

动态仪表板展示区

为此,我们利用最近提出的架构构建了一个动态仪表板。它由作为框架的 Angular 组成,在过去几年中已经发展成为非常成熟的前端解决方案。它在 Angular Material 中提供了 web 应用程序的标准组件,可以很容易地扩展到任何更复杂的视图或组件。

对于后端,我们一如既往地坚持使用 Python,因为它以非常低的开发开销提供了各种连接,还因为它是一门优秀的语言。此外,它还提供了广泛的数据处理工具,如人工智能/深度学习工具、数学工具以及最后但并非最不重要的绘图工具,如我们将在本次演示中使用的散景

我们也非常乐意用异步编程范式来扩展我们通常使用的 web 服务器技术,这意味着我们使用 aiohttp 作为我们的标准 python web 服务器。

为了连接这两个部分,我们决定广泛使用 websockets ,它通过标准的 tcp/ip 协议保证双向的响应数据流。

需要解决的问题

在此过程中出现的一些典型问题有

  • 图表或数字表示中的动态数据供应,
  • 在 Angular 应用程序中使用独立的散景图表,每个图表都应该是一个组件,图表的内容完全由后端驱动,
  • 例如,在板上排列图表的灵活性。

动态数据供应入门

仪表板必须显示可以随时更新的数据。在某些情况下,我们将需要实时数据显示,或者至少在 tcp/ip 协议处理时间和浏览器显示时间之内。我们从一个稍微不太严格的要求开始。

为图表提供动态数据的最简单方法是使用 ajax 调用散景图表。幸运的是,这已经作为散景图的标准方式实现了。Ajax 数据结构的行为与普通数据结构相同,可以作为一种无缝的方式定期更新数据。

这里我们展示了如何更新标签中的数字。为此,我们必须使用 LabelSet ,因为这是与图表上显示的文本相结合的唯一动态更新的数据结构。我们将下面的代码放在 Python 后端的一个单独的类 ChartProvider 中,这个类有一个类似于下面的函数:

这段代码片段展示了如何每 300 毫秒更新一次,以及如何在适配器中进一步处理数据,适配器是一小段 JavaScript 代码。在 Python 端, AjaxDataSource 的行为很大程度上类似于以下格式的每个静态数据源

为了以 ajax 方式提供连续的数据,我们必须使用我们的 web 服务器支持的 URL。如前所述,我们使用了 aiohttp ,因此我们可以将此路由作为 POST 路由,如下所示:

最后,我们可以包含来自于 ChartProvider 的 chart_item ,就像我们之前在文章中详细描述的一样。chart_item 是一个自包含的 json 格式的图表。这也意味着图表已经包含了数据获取机制 ajax 。在下图中,我们看到了典型的角形材料卡片中包含的图表:

Bokeh 提供了一种简单的 ajax 更新方式。但遗憾的是 Bokeh 还没有一个简单的功能,它只能在 JavaScript 端重绘图表。提醒一下,我们使用全局包含的散景模块的调用,它通过以下方式导入到我们的角度组件中:

角度方面的主要任务是为 embed_item 函数调用提供数据。因为我们使用 websocket 连接,所以我们将 chart_item 从 python 发送到 angular。它也可以由 REST-api 提供。顺便说一下,如果我们需要来自服务器的及时数据,我们的 websocket 连接更可行。另一方面,ajax 调用是通过轮询完成的,这有几个缺点。其中之一是,web 服务器在每次客户端请求数据时都工作,即使数据没有改变。

长话短说。我们可以通过 websocket 提供第一个图表数据或其他事件驱动的数据,但目前图表的更新数据仅限于 ajax 调用。我们希望将来避开 ajax 调用,从后端切换到事件驱动的调用。如果你想想解决办法,为什么不是每次都通过 websocket 发送完整的图表,调用 embed_item 方法,那也不是办法。散景嵌入图表的方式非常昂贵,因此我们永远不会达到我们想要的更新速度。

总结

我们展示了如何在 Angular 应用程序中实现定期更新的散景图,这是我们最终目标的一部分,即创建一个动态仪表板。

对于数据更新,我们使用了 ajax 方法,因为它已经是 Bokeh 中的内置方法,我们不必调整我们的 websocket 连接。

在 python 方面,数据供应或多或少是一项简单的任务,但重要的是我们要确保使用非阻塞调用,这意味着一致地使用异步方法。

我们会尽快让你知道如何解决其他问题。

利用知识库、BERT 和句子聚类构建有效的 FAQ

原文:https://towardsdatascience.com/building-effective-faq-with-knowledge-bases-bert-and-sentence-clustering-b0c15727bbdb?source=collection_archive---------49-----------------------

如何识别和暴露重要的知识

成功的企业几乎总是由高质量的知识和专业技能推动的。现代组织通过对话界面(如机器人和专家系统)公开他们的知识,因此客户、合作伙伴和员工可以立即访问推动成功的知识。作为数据科学家和工程师,我们有责任实现这一目标。我们需要回答一个简单的问题:您如何表示业务知识,使其易于使用?有许多方法和可能的策略来展示知识。在这篇文章中,我想深入探究古老而优秀的FfrequencyAskedQquestions system,并讨论如何用最新的人工智能技术实现它。

在史前时代,网站曾经有一个 FAQ 页面,上面有一个冗长乏味的无用问题列表。只有真正的乐观主义者才会搜索这个列表,为他们面临的问题找到可能的解决办法。那些年已经过去了。今天,现代网站有一个用户用来提问的集成机器人。起初,这些机器人非常糟糕,所以用户不愿意,但现在随着对话界面背后的技术(和人工智能)的改善,用户认为机器人和搜索是提问的主要界面。

知识库

像 Azure QnA Maker 和 Google Dialogflow 知识库这样的知识库是人工智能技术的两个例子,你可以用它们来构建一个机器人,从一系列问答对中回答问题。这些服务可以将用户的问题匹配到列表中最合适的条目,并给出正确的答案。您还可以按层次结构组织问题,并构建一个对话流,引导系统找到正确的 QnA 对。这很好,但核心还是有一个问题和答案的列表。某人需要建立和维护的列表。这样的列表不能太长,因为长列表是不可能维护的,尤其是现代商业是动态的,正确的答案一直在变。显然,如果我们想让客户觉得它有用并得到一个有效的答案,这个列表不能太短。那么,我们如何找到平衡,并保持问题的重要性呢?

NLU 问答

问答是 NLU 网络旨在解决的核心任务之一。在过去的几年里,随着 transformer 和 BERT 类架构的引入,NLU 空间发生了革命性的变化。更多细节请阅读我的关于 BERT 及其实现的文章。今天,有了像拥抱脸/变形金刚 QnA 管道这样的项目,我们可以建立 QnA 系统,在文章中找到一个句子,可以被认为是对一个问题的回答,并有一定的信心。这很好,但有两个基本挑战。
1)类伯特网络具有固定大小(有限)的输入大小,因此它不是为处理非常长的文本(即上下文)而设计的,例如书籍甚至文章。
2)答案永远是原文中的一句话。从我们的日常经验中我们都知道,一个好的答案往往至少是原文中多个地方的句子的集合,甚至是一些原文中不存在的新文本。这样的答案应该是从原文表达的思想中构建出来的。尽管这是一个活跃的研究领域,但是到今天为止,NLU 问答网络还不能做到这一点。欲了解更多详情,请搜索 ELI5 研究。对于第一个挑战,有一些部分的解决方案。在高层次上回答一个问题,从一个大的文本语料库,我们使用搜索索引之前,NLU 网络。换句话说,我们首先使用一些搜索索引来索引文档,然后当用户提交问题时,我们使用搜索引擎来检索一些(短的)候选文本,我们将在这些文本上执行 BERT 网络。最后,我们将返回置信度最高的答案。有很多这样的实现,我想推荐由 train 创建的一个。

FAQ 系统架构

直接的结论是,没有一种方法是完美的,因此您可能需要两种方法来开发 FAQ 应用程序。首先,您将与您的领域专家合作,开发一个问答配对列表,假设这些是“经常”被问到的问题,因此它们涵盖了“大多数”客户需求。答案将是深刻和全面的,因为它们是由人类专家开发的。使用上面提到的知识库技术之一,您将实现系统的第一层。用户提交的任何问题都将首先由知识库处理。希望你的假设是正确的,大多数问题将在这里得到成功处理。显然,列表中会有没有答案的问题。这些将延续到基于 NLU 方法的下一层。您将收集并索引所有可能包含客户问题答案的相关文档、手册和文章。当一个问题到来时,系统会找到候选文章并执行类似 BERT 的问题回答来给出答案。第二层的执行时间将比第一层长得多,并且答案不太准确,但是有可能许多答案足以满足最终用户。希望这样一来,未回答问题的比例会非常小。

记录是关键

正如我们所看到的,这个系统是建立在假设之上的。你想不断验证你的假设。要做到这一点,你必须记录每一个出现的问题。这些日志将为您提供关于您的客户真正在寻找什么的宝贵见解,并且同样重要的是将验证您的假设。您将看到问题是如何回答的,由哪一层回答的,甚至可能从最终用户那里获得一些感悟。

如何找到真正的 FAQ

假设您已经记录了所有内容,现在您需要确定需要进一步关注的问题。这些问题在你的知识库中并不存在,而且出现的频率足够高,所以你需要投入时间,找出能在知识库中找到它们的答案。起初,你可能认为统计日志中文本出现的次数是一件微不足道的事情,但是再看一眼你就会发现生活并没有那么简单。令人惊讶的是,用户仅仅问同一个简单的问题,就能想出这么多不同的答案。要处理大量数据,您需要找到所有这些变化,并将它们聚集成有意义的组。一旦聚类,保存它们是很重要的,这样您以后可以将变体输入知识库,因为现代知识库知道如何将不同的变体分组到单个问题中。不过,你需要解决问题聚类的问题。

句子聚类有两个阶段:

  1. 使用类似 BERT 的语言模型将文本转换成数字向量(即句子嵌入)。
  2. 使用您选择的聚类算法对向量进行聚类。

为了执行句子嵌入,你需要将你的句子插入到一个类似伯特的网络中,并查看 CLS 令牌。幸运的是, SentenceTransformer 项目完成了所有繁重的工作,因此您可以用两行代码完成这个复杂的任务!!!

第二阶段对于没有多少 ML 经验的人来说应该是简单明了的。我正在使用 sklearn.cluster 的 KMeans。

def RunClustering(userQuestions, num_clusters):# extract the text line from the userQuestions object
scorpus = [item[‘QuestionText’] for item in userQuestions]# convert the text into a numeric vector (i.e. sentence embedding) using a BERT-like language modelembedder = SentenceTransformer(‘bert-base-nli-mean-tokens’)
corpus_embeddings = embedder.encode(corpus)# cluster the vectors
clustering_model = KMeans(n_clusters=num_clusters)
clustering_model.fit(corpus_embeddings)
cluster_assignment = clustering_model.labels_# organize the clusters
clustered_sentences = [[] for i in range(num_clusters)]
for sentence_id, cluster_id in enumerate(cluster_assignment):
  clustered_sentences[cluster_id].append(userQuestions[sentence_id])return [{“cluster”: i, “questions”: cluster} for i, cluster in enumerate(clustered_sentences)]

问题聚类的结果

现在,您可以手动分析每个集群,并确定需要解决的问题。可选地,你可以使用像 Faiss 这样的库对每个簇中的向量进行进一步的聚类和相似性搜索。我发现第一级集群已经足够了,但是您的场景可能需要更细粒度的集群。

结论

知识库是支持你的 FAQ bot 的一个很好的工具,但是后端的问答列表不可能覆盖所有的问题,它必须不断地维护。NLU 问答可以填补这一空白,通过句子聚类,您可以识别重要的问题。
我希望这篇文章能帮助你建立下一个成功的 FAQ 系统。

最后说明:为了能够继续发布新的故事,Medium 现在要求作者拥有最低数量的关注者,所以请帮助我继续发布,并按下我名字旁边的“关注”按钮。

利用阿里云构建端到端客户细分解决方案

原文:https://towardsdatascience.com/building-end-to-end-customer-segmentation-solution-with-alibaba-cloud-db326e90c2fb?source=collection_archive---------31-----------------------

莎伦·麦卡琴在 Unsplash 上的照片

想象一下,我们有一个销售各种产品的零售店。为了在您的业务中取得更大的成功,我们必须充分了解我们的客户。尤其是在当今竞争激烈的世界。这样我们就可以回答:

-谁是我们最好的客户?

-谁是我们的潜在客户?

-哪些客户需要成为目标并保留下来?

-我们客户的特点是什么?

了解我们客户的一种方法是进行客户细分。细分是根据共同特征将客户分成几组的过程。我们可以使用许多变量来细分我们的客户。客户人口统计、地理、心理、技术和行为等信息通常被用作区分客户的区分因素。

通过在业务中实现客户细分,我们将能够个性化您的战略,以适应每个细分市场的特点。从而可以最大限度地保留客户,改善客户体验,获得更好的广告效果,并最大限度地降低营销成本。

那么,如何才能做到这种客户细分呢?

我们将应用无监督的机器学习技术对零售数据集进行客户细分。我们将使用新近性、频率和货币(RFM)作为客户交易行为的有用指标。

新近性、频率和货币定义。Bima 提供的图片

我们将利用以下产品来构建此使用案例。

-对象存储服务(OSS)。OSS 是一种加密、安全、经济且易于使用的对象存储服务,使您能够在云中存储、备份和归档大量数据,并保证耐用性。

- MaxCompute (之前称为 ODPS)是一个通用的、完全托管的多租户数据处理平台,用于大规模数据仓库。MaxCompute 支持各种数据导入解决方案和分布式计算模型,使用户能够有效查询海量数据集,降低生产成本,保障数据安全。

- DataWorks 是阿里云推出的大数据平台产品。它提供一站式大数据开发、数据权限管理、离线作业调度等功能。此外,它还提供全方位的服务,包括数据集成、DataStudio、数据地图、数据质量和 DataService Studio。

- 面向 AI 的机器学习平台 (PAI)提供端到端的机器学习服务,包括数据处理、特征工程、模型训练、模型预测和模型评估。人工智能的机器学习平台结合了所有这些服务,使人工智能比以往任何时候都更容易获得。

- Data Lake Analytics 是一种利用无服务器架构的交互式分析服务。DLA 使用 SQL 接口与用户服务客户端交互,这意味着它符合标准的 SQL 语法,并提供各种类似的功能。DLA 允许您从多个数据源或位置(如 OSS 和 Table Store)检索和分析数据,以优化数据处理、分析和可视化,从而提供更好的见解并最终指导更好的决策。

我们将首先准备数据,然后进行模型训练,接着创建一个为模型服务的管道。

端到端流程。图片由 Bima 提供

数据准备

首先,让我们了解一下我们的数据。我们使用来自 UCI 机器学习知识库的在线零售数据(你可以从这个链接下载这些数据)。该数据集由 2010 年和 2011 年的交易数据组成。

数据集概述。图片由 Bima 提供

第一步数据收集

我们将使用 2010 年的数据进行模型训练,并将该文件重命名为 transaction_train.csv。然后,作为需要处理的日常数据示例的数据是 2011 年,我们将其重命名为 transaction_daily.csv。我们需要将该数据集存储到阿里巴巴对象存储服务(OSS)中。

第二步数据摄取

然后,我们使用 MaxCompute 和 Data Works 来编排流程。该步骤旨在使用 Data Works 将 OSS 中的数据导入 MaxCompute。

首先,我们需要创建一个表来存储 MaxCompute 中的数据。这个表应该与我们的数据集模式相匹配。我们可以通过右键单击业务流并选择 Create Table 来实现。然后,我们可以使用 DDL 语句创建模式,并遵循这个过程。

****

现在我们已经准备好桌子了。我们需要创建一个数据集成节点,将我们的数据从 OSS 同步到 MaxCompute。在这个节点中,我们需要设置源和目标,然后映射每个列。

然后,我们可以运行该组件将数据从 OSS 接收到 MaxCompute,并将我们的数据放在适当的位置。

步骤 3 数据清理和转换

我们的数据需要在用于模型训练之前进行清理和转换。首先,我们将清除数据中的无效值。然后,我们通过计算每个客户的近期、频率和货币价值来转换数据。

在 DataWorks 中,我们将使用一个 SQL 节点来执行此任务。我们可以首先创建一个新表来存储结果,然后创建节点并编写一个 DML 查询来完成任务。

这项任务的结果是,我们将获得每个客户的 RFM 值。

从原始数据到 RFM 要素的数据转换。图片由 Bima 提供

我们将在模型训练和模型服务过程中使用这个数据准备步骤。在模型训练中,我们只做一次,直到有了模型。但是,这种数据准备将在模型服务管道中每天批处理运行。

模特培训

我们将使用 K-Means 无监督机器学习算法。K-means 聚类是应用最广泛的聚类算法,它将 n 个对象划分为 k 个簇,以保持每个簇内的高相似性。相似性是基于聚类中对象的平均值来计算的。

该算法随机选择 k 个对象,每个对象最初代表一个聚类的平均值或中心。然后,该算法根据剩余对象与每个聚类中心的距离将它们分配到最近的聚类中,并重新计算每个聚类的平均值。这个过程重复进行,直到标准函数收敛。

K-means 聚类算法假设我们从空间向量中获取对象属性,其目标是确保每个组内的均方误差和最小。

在这一步中,我们将使用 PAI studio 创建一个实验,并使用数据科学研讨会(DSW)进行优化。派工作室和 DSW 是阿里巴巴人工智能机器学习平台的一部分。

在 PAI 工作室中创建一个实验相对简单。PAI Studio 已经有几个功能作为组件,我们可以将它们拖放到实验窗格中。然后我们只需要连接每个组件。下图显示了我们将构建的制作模型的实验。

模型训练步骤。图片由 Bima 提供

第一步数据探索

在这一步,我们的目标是理解数据。我们将通过生成描述性统计数据、创建直方图和创建散点图来研究我们的数据,以检查变量之间的相关性。我们可以在读取数据后创建一个组件来完成这些任务。

数据探索结果。图片由 Bima 提供

因此,我们知道我们的频率和货币数据有偏差。因此,我们需要在创建模型之前进行特征工程。

特征工程

我们将进行日志转换来处理数据中的偏差。在使用它进行建模之前,我们还需要对值进行标准化。因为 K-意味着用距离作为度量,我们需要我们的每个参数都在同一个尺度上。为此,我们需要创建一个特性转换组件和标准化组件。

然后在这个节点下,我们应该将标准化的参数保存到一个表中,这样我们就可以在部署过程中使用它。

特征转换数据之旅。图片由 Bima 提供

步骤 3 模型创建

现在是时候创建我们的模型了。我们将使用 K-Means 来发现客户群。为此,我们需要输入集群的数量作为我们的模型超参数。

为了找到最佳聚类数,我们需要使用 Data Science Workbench 使用不同数量的聚类迭代建模,并通过生成肘图来找到最佳值。最佳聚类数是平方和误差开始变平的地方。

DSW 是一个类似木星笔记本的环境。这里我们需要通过编写 python 脚本来创建一个实验,并生成肘图。

找出最佳聚类的肘形图。图片由 Bima 提供

结果,我们发现我们的最佳集群是五个。然后,我们使用它作为 PAI studio 中 K-Means 组件的超参数,并运行该组件。

这个组件的结果,我们对每个客户都有一个 cluster_index。我们还可以将结果可视化为散点图的形式,散点图已经用 cluster_index 进行了着色。该组件还生成一个稍后将被提供的模型。

分割结果。图片由 Bima 提供

步骤 4 保存聚类结果

这里我们将把原始数据与 k-means 组件的结果结合起来。然后,我们通过创建 Write MaxCompute Table 组件将结果保存到 MaxCompute 表中,作为我们的实验输出。

标记客户群

我们将使用 SQL 组件来计算每个聚类的平均新近度、频率和货币,以了解每个聚类的特征,并表示每个聚类的名称。

每个聚类的平均值。图片由 Bima 提供

细分市场详情。图片由 Bima 提供

现在,我们回到 Data Works,创建一个 PAI 节点来运行我们的实验。然后,我们使用一个 SQL 节点编写一个 DML 来对每个集群进行分段。最后,我们创建另一个数据集成节点,将数据从 MaxCompute 发送回 OSS。

数据工作中的业务流程。图片由 Bima 提供

模型培训总结

这是使用阿里云产品(如 OSS、MaxCompute、DataWorks 和人工智能机器学习平台)创建模型训练需要完成的五个步骤。总之,下图显示了从数据准备到模型训练的模型训练架构。

客户细分模型培训架构。图片由 Bima 提供

模型服务

用户新近性、频率和货币数据可能会随着时间的推移而改变。这意味着我们需要根据用户当前的特征不断给他们贴标签。如果我们手动进行分割,这可能是一项具有挑战性的任务。因此,我们将每天一次批量自动部署和创建细分流程。下图总结了服务模型的步骤。

服务于架构的客户细分模型。图片由 Bima 提供

我们首先将日常数据放入 OSS,然后使用 Data Works 服务将这些数据从 OSS 传输到 Max Compute。之后,我们清理和转换数据到 RFM 形式,和以前一样。

然后我们创建一个新的实验作为 PAI 节点。在这个实验中,我们做了相同的特征工程过程,如日志转换和标准化。但是我们使用在训练过程中保存的标准化参数。

接下来,我们使用之前创建的 RFM 模型的预测组件来预测我们的新数据。然后,我们将分段结果与原始参数结合起来,并将它们保存到最大计算表中。

之后,我们创建一个 SQL 节点,用各自的段名标记我们的客户。然后我们将结果以*的形式发送回 OSS。csv 文件,方法是创建一个数据集成节点。下图显示了 Data Works 和 PAI 实验的整体流程。

DataWorks 和 PAI 中的服务业务流模型。图片由 Bima 提供

最后,数据湖分析(DLA)将用于连接开放源码软件文件和外部可视化工具,如 Tableau。数据湖分析(DLA)是一个无服务器的云原生交互式查询和分析服务。因此,我们可以将细分结果提交给我们的业务团队进行监控和使用。下图显示了在 Tableau 中创建的客户细分仪表板。

客户细分仪表板。图片由 Bima 提供

结论

结果,确定了五个集群。那些是忠诚者,潜在,流失,潜在损失,和损失。因此,我们的业务团队可以设计和执行一个营销策略来参与每个细分市场。

在这个过程中,我们还了解到如何结合各种阿里云产品来制作端到端的机器学习管道,并构建业务用例。

文献学

——https://www.alibabacloud.com/product/maxcompute

**——【https://www.alibabacloud.com/product/ide **

**——【https://www.alibabacloud.com/product/data-lake-analytics **

-https://www.alibabacloud.com/product/oss

30 分钟内建立人脸识别模型

原文:https://towardsdatascience.com/building-face-recognition-model-under-30-minutes-2d1b0ef72fda?source=collection_archive---------6-----------------------

实践教程

微调 VGG-16 以建立在三重损失函数上训练的用于面部识别任务的暹罗网络

萨法尔萨法罗夫Unsplash 上拍摄的照片

介绍

在这篇博文中,我将介绍人脸识别模型的一些实现细节。我还设计了一个基于浏览器的 UI,用于向数据库添加一个新人。解释 web 开发部分超出了本博客的范围。

这篇文章假设读者理解连体网络模型和三重损失函数。如果您喜欢先运行模型,那么您可以从我的库这里克隆它。

中试用工作模型

这篇博文的结构如下:

  • 模型架构
  • 数据集
  • 三胎生成
  • 其他详细信息
  • 结论

模型架构

我们都知道从头开始训练卷积神经网络(CNN)需要大量数据和计算能力。因此,我们转而使用迁移学习,根据我们的要求对基于类似数据训练的模型进行微调。牛津大学的视觉几何小组(VGG)已经建立了三个模型——VGG-16、雷斯内特-50 和塞内特-50,用于人脸识别和人脸分类。我使用了 VGG-16 模型,因为它是一个较小的模型,实时预测可以在我的本地系统上工作,无需 GPU。

注:为了避免混淆 VGG-16 深度学习模型和牛津视觉几何小组(VGG),我将把后者称为牛津小组。

这个实现将 Keras 中的整个模型与 TensorFlow v1.14 作为后端。我计划在 TensorFlow v2.3 中构建相同的模型,所以我在本地系统中创建了一个 virtualenv 并提取了模型权重。这些提取的权重存储在vgg_face_weights.h5中,随后加载到未经训练的 VGG-16(在 TensorFlow v2.3 中)网络上,如本文所示。如果您希望使用 ResNet-50 或 SeNet-50,那么您可以使用 Refik Can Malli 的存储库来获取模型和重量。

VGG-16 模型是在这篇论文中显示的数据集上训练的,他们已经在 2622 张不同的人脸上训练了分类模型。倒数第二层有 4096 个密集单元,我们在其上附加一个 128 个单元的密集层,没有偏差项,并删除包含 2622 个单元的分类/softmax 层。128 密层之前的所有层都被冻结(trainable = False),只需要训练新加入的密层。

加载 VGG-16 预训练的重量,然后定制模型

现在为了训练这个网络,我们使用一个三重损失函数。三重损失函数采用从上述网络生成的三个 128 维特征。让这三个被称为锚,积极和消极的地方

  • 主持人:一个人的形象,将用于比较。
  • 正面:与主播同一个人的形象。
  • 负面:与主播不同的人的形象。

三重损失函数— 论文

三重态损失试图减少锚和正对之间的距离,增加锚和负对之间的距离。还有另一个参数alpha = 0.2,它增加了一个余量,从而使训练更加困难,并给出更好的收敛性。参数128D密集单元和损失函数参数alpha是根据本论文中的分析选择的。

三重损失函数的实现

总结到现在吧!!VGG-16 网络为我们提供了锚、正和负图像的 128D 特征,这些特征然后被馈送到损失函数。

现在对于训练,一种选择是在每个锚、正面和负面图像上调用相同的模型三次,然后将值赋予损失函数。然而,一个接一个地运行它们并不是一个好主意。因此,我将它们包装在一个扩展了tf.keras.Model的 Siamese Network 类中,并将并行化留给了 TensorFlow。此外,还有一件事添加到模型中, L2 正则化应用于 128D 密集层的输出。

暹罗网络类

我已经在 SiameseNetwork 类中添加了一个函数get_features,这只是一个优化,在测试过程中会很有用。

太好了,我们建立了一个模型!!现在让我们来看看用于训练的数据集。

资料组

由 2622 个不同的名人图像组成的 VGGFace 数据集用于训练上面使用的 VGG-16 模型。后来,牛津小组还发布了由 8631 个名人图像组成的 VGGFace2 用于训练,测试中有 500 个,每个都很独特。由于训练集是 39GB,所以我只下载了测试集,是 2BG,训练了最后一个密集层。

虽然使用一个测试集进行训练听起来可能有违直觉,但这是关于他们训练的模型的测试集。至于我,我已经把它作为一个训练集,并在我的家人和朋友身上测试了我的模型。

预处理通常取决于底层模型。因此,为了训练和测试,输入图像必须经过由牛津小组实施的 VGG-16 模型定义的相同预处理。输入到模型中的图像首先通过这篇论文中描述的人脸检测器,然后发送到这里给出的preprocess_input函数。在我的实现中,我使用了 dlib 库提供的正面人脸检测器,然后将图像发送到preprocess_input函数。

注意:这里定义的预处理 _ 输入函数与 ImageNet 上训练的 VGG-16 使用的不同。因此,在我的存储库中,预处理的代码取自 pip installed VGGFace 库。

现在,我将展示数据集的目录结构,因为它成为在训练期间优化内存的一种方式。让我们先来看看下载的数据集目录结构。在下面的目录结构中,每个目录(n000001,n000009 等。)被分配给一个名人的所有图像。

.
└── vggface2_test
    └── test
        ├── n000001
        │   ├── 0001_01.jpg
        │   ├── 0002_01.jpg
        │   ├── 0003_01.jpg ...
        ├── n000009
        │   ├── 0001_01.jpg
        │   ├── 0002_01.jpg
        │   ├── 0003_01.jpg ...
(so on and so forth)

如上所述,我们使用 dlib 的正面人脸检测器来检测包含人脸的图像,并将它们存储在一个名为 dataset 的不同文件夹中。下面是人脸检测图像的目录树。本笔记本也有同样的实现。

.
└── dataset
    └── list.txt
    └── images
        ├── n000001
        │   ├── 0001_01.jpg
        │   ├── 0002_01.jpg
        │   ├── 0003_01.jpg ...
        ├── n000009
        │   ├── 0001_01.jpg
        │   ├── 0002_01.jpg
        │   ├── 0003_01.jpg ...
(so on and so forth)

vggface_test 和 dataset 的目录结构几乎是相似的。但是,数据集目录可能包含更少的图像,因为一些面部可能没有被 dlib 的检测器检测到。此外,在数据集目录中有一个文件list.txt,它包含每个图像的如下数据directory-name/image-name。这个 list.txt 用于训练时的内存优化。

三胞胎世代

为了训练,模型需要三个图像——锚、正面和负面图像。我脑海中的第一个想法是生成所有可能的三胞胎对。这似乎给出了很多数据,但研究文献表明这是低效的。所以我用了一个随机数发生器来选择锚,积极和消极的图像对。我使用了一个在训练循环中产生数据的数据生成器。如果你不熟悉数据生成器,请参考这个博客。

有趣的事实:我花了比模型训练更多的时间来编写 DataGenerator 类。

三元组数据发生器

__getitem__是最重要的功能。然而,为了理解这一点,让我们也检查一下构造函数和其他方法。

  • init: 构造函数采用前面小节中定义的数据集目录的路径。构造函数使用list.txt来创建一个字典。这个字典将目录名作为它的键,将目录中的一系列图像作为它的值。正是在这里,在混洗步骤中,list.txt 成为我们了解数据集概况的一种简单方式,从而避免了加载图像进行混洗。
  • getitem: 我们从上面的字典键中获取人名。对于第一批,前 32 个(批量大小)人物图像用作锚,同一个人的不同图像用作阳性。从任何其他目录中选择一个负面图像用于训练。对于所有的三胞胎,锚,积极和消极的形象是随机选择的。接下来的 32 人将成为下一批的主播。
  • 策展 _ 数据集:创建在__init__中解释的字典
  • on_epoch_end: 在每个 epoch 结束时,人的顺序被打乱,因此在下一个 epoch 中,前 32 个图像与前一个 epoch 中看到的图像不同。
  • get_image: 获取图像功能在将图像调整到(224 x 224)大小后使用preprocess_input
  • len: 这将返回定义一个时期的批次数量。

搞定了。!!

培训和测试

我已经用 tqdm 使用了一个定制的训练循环(你仍然可以感觉到 Keras ),并训练了 50 个时期的模型。在 colab 上,每个历元的训练时间是 24 秒,所以是的,训练相当快。

对于测试,您可以将家人、朋友和您自己的图像保存在一个目录中,还可以存储从每个人的密集图层中生成的 128D 特征。您可以使用get_features()函数,该函数在此处的 SiameseNetwork 类中定义。此外,为了节省您的时间,我制作了一个笔记本 Real-time-prediction.ipynb,,它加载模型检查点,并提供收集图像以进行动态测试并在网络摄像头视频上预测它们的说明。

杂项详细信息

提高 Colab 的训练速度

在 DataGenerator 中,并不是所有的图像都加载到内存中,而是加载它们的索引进行操作。如果您有自己的 GPU,那么这一小节中的细节可能不太相关。

我最初认为从 colab 到 drive 的读写操作应该很快,但结果是它们变得比我的甚至没有 GPU 的本地系统慢。为了解决这个问题,我将数据集压缩到dataset.7z并上传到我的硬盘上。然后将 zip 文件从我的 Google drive 复制到 colab 的空间,在那里提取,然后用于训练。使用 colab 的空间大大提高了训练过程的速度。

但是,我的 tensorboard 摘要和模型检查点存储在驱动器中,因为它们每个时期都被访问一次,不会显著降低性能。

基于用户界面的工具

我想学习一些网络技术,比如 HTML、CSS 和 Javascript。最好的学习方法是做一个小项目。因此,我试图开发一个基于 UI 的工具来收集测试和预测数据。运行相同程序的步骤在我的中有解释。

结论

在这篇博客中,我们讨论了关于微调现有网络和在其上构建连体网络的关键细节。当前模型的结果比预期好得多,但是我们也可以通过手动创建好的三胞胎来改进它们。也可以下载整个训练数据集来训练模型。文献表明,手动选择一组硬三元组将显著减少训练时间并提高模型的收敛速度。

你可以参考我的知识库来尝试基于浏览器的工具以及查看培训用的笔记本。该工具还可以检测多人!!

参考

O.M. Parkhi,A. Vedaldi,A. Zisserman,深度人脸识别,英国机器视觉大会,2015。

Q. Cao,L. Shen,W. Xie,O. M. Parkhi,A. Zisserman, VGGFace2:跨姿态和年龄的人脸识别数据集 自动人脸和手势识别国际会议,2018。

F.Schroff,D. Kalenichenko,J. Philbin, FaceNet:用于人脸识别和聚类的统一嵌入, CVPR,2015 年。

G.Koch,R. Zemel,R. Salakhutdinov,用于一次性图像识别的连体神经网络ICML 深度学习研讨会。第二卷。2015.

[## rcmalli/keras-vggface

使用 Keras Functional Framework v2+模型的 Oxford VGGFace 实现是从原来的 caffe 网络转换而来的…

github.com](https://github.com/rcmalli/keras-vggface) [## 使用 Keras 的数据生成器的详细示例

python keras 2 fit _ generator Afshine Amidi 和 Shervine Amidi 的大型数据集多重处理您是否曾经不得不…

stanford.edu](https://stanford.edu/~shervine/blog/keras-how-to-generate-data-on-the-fly) [## 在 Google Colab 上加速你的图像训练

获得一个因素 20 加速训练猫对狗分类器免费!

medium.com](https://medium.com/datadriveninvestor/speed-up-your-image-training-on-google-colab-dc95ea1491cf) [## 用 PyTorch - neptune.ai 中的连体网络实现基于内容的图像检索

图像检索是寻找与给定查询相关的图像的任务。对于基于内容的图像检索,我们指的是…

海王星. ai](https://neptune.ai/blog/content-based-image-retrieval-with-siamese-networks)

培养习惯并进入数据科学领域

原文:https://towardsdatascience.com/building-habits-and-breaking-into-data-science-2c0872400e3e?source=collection_archive---------33-----------------------

苹果 | 谷歌 | SPOTIFY | 其他

拉塞尔·波拉里在 TDS 播客

编者按:迈向数据科学播客的“攀登数据科学阶梯”系列由 Jeremie Harris 主持。Jeremie 帮助运营一家名为sharpes minds的数据科学导师初创公司。可以听下面的播客:

我们大多数人都想改变自己的身份。我们通常对自己有一个理想化的版本,我们渴望成为——一个更健康、更聪明、更健康、更有名、更富有、更集中的人,或者其他人。

但是如果不改变你在日常生活中所做的事情,你就无法从根本上改变你的身份。不经常锻炼,你不会变得更健康。不定期学习是不会变聪明的。

要改变自己,首先要改变习惯。但是你是怎么做到的呢?

最近,像《原子习惯》和《深度工作》这样的书集中在用一般术语回答这个问题,它们绝对值得一读。但是,在数据科学、分析、机器学习和创业的背景下,习惯的形成伴随着一系列独特的挑战,其本身就值得关注。这也是为什么我想和今天的嘉宾拉塞尔·波拉里坐在一起。

拉塞尔现在可能是世界上最大的收入分享导师市场的首席技术官(也是我每天工作的公司!)但他曾经——不久前——是一名几乎没有任何编程能力的物理学博士生,是研究生院忧郁的典型案例。为了取得今天的成就,他不得不学习很多东西,在他寻求优化这一过程的过程中,他将很多注意力放在了在技术、数据科学和创业背景下的习惯养成和自我完善上。以下是我最大的收获:

  • 不是每个人都适合读研,中途放弃读研计划也不会让你失败。知道什么时候放弃很重要,这样你就不会为了一个你可能并不感兴趣的研究项目而牺牲掉接触现实世界问题的宝贵时间。
  • 优秀的数据科学家像工程师一样思考,而不是物理学家。物理学家希望所有事情都从第一原理出发进行推理(如果他们像我一样,他们甚至有从头开始编写 ML 模型的冲动!).工程师乐于使用他们还不太了解的现成工具,这样他们就可以从现实世界的反馈中学习并快速迭代。当你需要的时候,你总能学到你需要的理论。
  • 在数据科学中构建项目的价值很大一部分是对你需要学习的东西施加约束。这个空间如此广阔,如此开放,以至于很容易在兔子洞里迷路,所以限制你正在构建的范围——以及你需要专注的技能范围——是避免旋转轮子的一个好方法。
  • 为了投身于工业,Russell 必须学习的两件最重要的事情是:1)版本控制,以及 2)学习如何编码以便其他人能够理解他的工作(例如,不使用thing_1x_12作为变量名)。有趣的是,这两者都反映了由于大多数人的研究生院环境的孤独性质而产生的盲点:当你独自工作时,很容易注意不到你的代码有多糟糕,因为你知道你所有有趣的变量名意味着什么。
  • 养成习惯的一个很好的方法是采取创业的心态,从小处着手。让你每天编码或申请工作的最基本的可行习惯是什么?答案可以(而且经常应该!)声音小得让人觉得有点可笑。“我每天会花 5 分钟申请工作”一开始看起来似乎是一件毫无意义的事情,但是好习惯会随着时间的推移而复合。

你可以点击这里在 Twitter 上关注 Russell,你也可以点击这里在 Twitter 上关注我

订阅《走向数据科学》的月刊,直接在你的邮箱✨中接收我们最好的文章、视频和播客

利用 Google Cloud AutoML 进行建筑物图像检测

原文:https://towardsdatascience.com/building-image-detection-with-google-cloud-automl-8b9cf2b8074b?source=collection_archive---------22-----------------------

零编码知识如何搭建图像检测 app

就在最近,谷歌推出了谷歌自动标记语言(https://cloud.google.com/vision/overview/docs#automl-vision),这使我们能够不用任何代码就能建立图像分类甚至物体检测。该功能不仅仅限于培训,还包括部署,无论是云计算还是边缘计算,如 android、ios 或 web 浏览器。

在这篇文章中,我将演示如何用 AutoML 视觉建立图像分类。今天我们要建立一个零食分类器,一个机器学习系统,能够对图片中的零食进行分类。为了简单起见,我们把可能的零食限定为五个选项(以后我们将此称为标签,分别是:好时光PiattosCheetosQtela 和 richeseNabati。我选择这些小吃并没有什么特别的原因,这完全是我在超市过道上随意浏览的结果。

我的零食并排放着,等着被吃掉。图片作者。

入门

在我们开始之前,需要准备三样东西,

  • 快餐
  • 捕捉小吃图像的设备
  • Google Cloud 帐户(如果您没有帐户,您有资格获得长达 1 年的 300 美元免费积分)

图像采集

为了在 Google Cloud AutoML 中构建图像分类器,我们需要为每个标签准备至少十张图像。由于我有点精力充沛(我想这是因为我的拿铁咖啡里的糖),我为每个标签收集了总共 30 张图片。为了有好的图像分类器,建议你捕捉不同角度的物体。

在这种情况下,我用手机拍了照片,然后把它们传输到我的笔记本电脑上。在您的笔记本电脑中有图像后,我们可以继续下一步。

图像标记

对于这一部分,直到本文结束,我们将进入谷歌云控制台。

进入云愿景的切入点

首先登录您的谷歌云控制台,然后转到视觉部分。你可以通过点击汉堡 图标(在谷歌云平台旁边的左上方)→人工智能→视觉,或者更简单的方法是使用控制台中的搜索栏。

创建新数据集框

我们需要做的第一件事是创建数据集。当我们创建数据集时,有三个选项可供选择:

  • 单一标签分类 每张图片只有一个标签,即山/湖/海滩。
  • 多标签分类 每幅图像都有多个标签,例如,我们希望从一幅画中预测画家、流派和情感。
  • 物体检测 识别图像中的每一个物体,例如,我们想从一张图像中找到每一辆卡车/公共汽车/小汽车。

在我们的例子中,每个图像都有一个标签(Cheetos/qtela/等。),所以我们就用单标签分类。创建新的数据集后,下一步将是导入数据。

导入数据集页面

在导入数据集页面中,我们需要做两件事。首先,我们需要上传图片,然后设置谷歌云存储桶,我们将保存我们的文件。

添加可能的标签

图像上传需要一段时间才能完成,尤其是当我们上传大量高质量的图像时。当上传过程完成时,我们可以在图像选项卡中看到所有图像。一开始,我们没有标记任何数据,所以我们可以看到在未标记的部分有 150 个图像。在我们将图像分配给标签之前,我们需要先创建标签,为此点击添加新标签

将每个图像分配给标签

添加标签后,我们可以将图像分配给标签。选择属于一个标签的多个图像,然后点击分配标签,选择正确的标签,然后瞧~。

重复这个过程,直到我们完成标记所有的图像。我知道这是一个缓慢、乏味和痛苦的过程,尤其是当你有大量图像的时候。请耐心等待,因为拥有大量正确标记的图像是拥有良好图像分类器的关键。

完成指定的所有图像

唷!在这一阶段,我们已经完成了所有 150 张图片的标签,下一步可能是最有趣的部分,那就是“鼓声效果”

…构建我们的图像分类器

模特培训

在我们进行模型训练之前,一个至关重要的部分是将我们的数据集分成三个部分:

  • 我们将用来训练模型的训练集
    数据。
  • 用于超参数调整和寻找最佳模型的验证集
    数据。
  • 测试最终数据,看看我们的模型在现实世界中的表现有多好。

当我们标记我们的图像时,Cloud Vision 已经自动将图像分配给训练/验证/测试。虽然有一个自动的特性很好,但是到目前为止,我们还没有办法将一个图像从训练转移到验证或者从验证转移到测试集。

我们可以看到,平均来说,我们在训练中有大约 24 幅图像,在验证中有大约 3 幅,在测试中有大约 3 幅。虽然这个数字很低,但对于我们的演示来说已经足够了。

配置模型名称,如果我们没有训练任何模型,我们将不会在图像的左侧看到“snack_single_label”模型。

好了,我们终于建立了真正的机器学习模型。对于学习步骤,我们需要做两件事。首先,我们需要设置我们的模型名称(在这种情况下,我使用 snack_single_label ),然后决定如何部署模型,选项有:

  • 云托管
  • 该模型将部署在谷歌云内,我们可以使用 gcloud UI,gcloud CLI 或 REST API 访问该模型
  • 边缘
  • 我们将能够下载模型并在 edge 设备中运行,比如安卓、ios 甚至浏览器

在这个演示中,我们将在 google cloud 内部部署,所以我们选择第一个选项

AutoML 的独特之处在于,谷歌会自动训练不同的模型架构和参数,并为我们提供性能最佳的模型。我们唯一需要配置的是我们能够负担的节点小时数。最小值是八个节点小时(使用八个核心机器一个小时)。

如果我们使用一个新的谷歌云账户,我们将有免费的 40 个节点小时来训练我们的 ML 模型。因为我们还有一个空闲层,所以我们选择最小节点小时数。

我们填写完节点工时预算后,点击开始培训,等待培训结束。我们可以在节点小时预算文本框下找到培训何时结束。

模型评估

在我们部署(或服务)我们的机器学习模型之前,最后一步是找出我们的模型在真实世界场景中的表现有多好。

我们可以通过进入评估选项卡来了解我们的模型有多好。有一些有用的指标,例如:

  • 精度
    在所有的正面预测结果中,有多少百分比实际上是正面的?
  • 回想一下
    在所有实际的阳性病例中,我们能预测多少百分比是阳性的?
    我尽量保持这种精确和召回尽可能短,更详细的解释你可以看看这里的谷歌机器学习课程(https://developers . Google . com/machine-learning/crash-course/classification/precision-and-recall)。
  • 混淆矩阵
    精确度和召回率给出的是百分比,混淆矩阵给出的是更精细的结果。行显示图像的真实标签,而列显示预测结果。让我们举一个例子,第 3 行第 2 列表示有多少奇多图像被模型识别为 qtela。

如果我们看一下指标,我们有 100%的准确率和 100%的召回率,这是非常好的,但是因为我们的测试集中只有非常小的数据集,所以很难相信这些数字

模型服务

当我们决定我们的模型足够好的时候,我们终于可以部署模型了(耶!),为此,让我们转到测试&使用选项卡。

模型服务页面

在尝试我们的模型之前,我们需要首先部署它。请注意,因为我们没有在培训后不勾选将模型部署到 1 个节点的复选框,所以我们需要通过单击部署模型来手动部署它

单击“部署模型”后的“部署模型”框

为了进行部署,我们需要设置将要使用的节点数量。Cloud vision 已经告诉我们每个节点每秒大约可以处理多少个请求,所以只需要估计进入这个 API 的流量有多大。

例如,在此图中,我们可以看到,对于每个节点,它可以每秒处理 3.2 个请求,因此,如果我们的生产流量约为每秒 10 个请求,可以肯定地说,我们至少需要 4 个节点来处理流量。

部署模型需要几分钟时间,完成后,您将看到下面的页面。

部署过程完成后使用测试页

在所有这些数据准备、训练和部署之后,我们最终能够测试我们的图像分类器。模型有多种使用方式,我们可以使用 REST API,也可以使用 google vision python 库,还可以在这个页面上传一张图片。

让我们上传一张图片,把 REST API 和 python 客户端留待另一天:d .我要在我们的训练/验证/测试集之外拍一张新的图片,希望模型会返回正确的预测。

耶模型正确地将图像分类为奇多

我上传了我拿着一个奇多的图片,幸运的是,这个模型正确的归类为奇多(唷!~)

结论

虽然 Google Vision AutoML 被证明易于训练和部署,但在使用它之前,您需要考虑一些限制。

  • 昂贵的
    所有这些无代码功能都需要一些成本,云托管培训的最低价格为每小时25.2美元(最低为每小时八核,每核3.15美元)。
    对于部署,我们至少需要在 1 个节点中部署,即每小时1.25美元,这意味着每月900美元。
  • 完全黑箱
    虽然我们可以方便地让 AutoML 决定什么是最好的模型,但是我们无法控制模型的构建和部署。最重要的是,我们不能下载模型并在本地理解模型。
  • 云部署没有自动扩展
    到目前为止,没有在云部署中启用自动扩展的选项。对于那些希望在面向客户的生产环境中实现这一点的人来说,这可能是一个倒胃口的事情

这就是我们今天旅程的结束。对于下一个主题,我将写关于 Google AutoML 视觉中的对象检测,以及我们如何使用 tensorflow 2.0 在本地重新创建这个零食分类器。我希望你喜欢这篇文章,如果你有任何意见或想法,请随时给我留言

使用 Excel 构建交互式仪表板

原文:https://towardsdatascience.com/building-interactive-dashboards-using-excel-3b5402da5d22?source=collection_archive---------24-----------------------

使用数据透视表在 Microsoft Excel 上构建交互式仪表板的分步介绍

活动发起人Unsplash 上的照片

曾经想免费构建一个仪表板,但找不到合适的工具?如果是,那么这篇文章无疑是写给你的。流行的仪表盘软件/工具如 Tableau、Power BI 和 Infogram 为交互式仪表盘提供了优秀的平台。但是它们中的大多数需要高级/付费会员资格来构建和与他人共享仪表板。

在这种情况下,有几个备选方案,Microsoft Excel 是可能的备选方案之一(与其他工具相比,它确实有一些缺点,我将在本文末尾讨论)。

在本文中,我将一步一步地详细介绍如何使用 Excel 构建交互式仪表板。由于有很多关于新冠肺炎及其趋势的讨论,我决定参照给定的数据建立一个跟踪全球案例的仪表板。

:

  • 这篇文章假设你对使用 Excel 创建数据透视表和图表有很好的了解。
  • 正在使用 Microsoft Excel 版。因此,这里显示的一些功能可能与旧版本不兼容。

开始之前,这里有一个最终仪表板的预览:

所以我们开始吧!

简单地说,我们需要分别设计单独的图形/视觉效果,然后在一个新的工作表中安排一切。

第一步:

如果您现在猜对了,有 3 个不同的数据集(病例、死亡和康复)需要关联。这是必需的,因为仪表板中的过滤器会影响所有 3 个数据集的图表/表格。对此有不同的方法,我将讨论其中一种方法(通过创建数据模型)。

首先,通过从 cases 表中复制 Country/Region 列来创建所有国家的列表,并删除所有重复项。(数据->删除重复)。我们现在将该列表与所有 3 个表相关联,因此将使用该列表在仪表板中应用过滤器。

接下来,将所有 3 个数据集格式化为表格(Ctrl+t ),然后转到第一个表格并选择 Power Pivot 菜单->添加到数据模型。对第二个、第三个表和国家列表重复同样的操作。现在,将 countries 列表中的 country/region 列拖动并连接到所有 3 个表的 country/region 列。一旦一切都完成了,我们将得到以下内容:

现在,我们已经使用 power pivot 视图成功地连接了所有 3 个表。

第二步:

要使用我们创建的数据模型插入数据透视表,请单击空白单元格,然后插入->数据透视表->使用此工作簿的数据模型。

使用上面的过程,创建数据透视表(如果你对这 5 个步骤不熟悉,请参考一些数据透视表教程):

  1. 确诊病例总数
  2. 死亡总数
  3. 回收总额
  4. 国家/地区的病例数
  5. 病例数的时间序列

第三步:

从创建的数据透视表中插入国家/地区案例数量和时间序列的图表。Excel 只有几个图表选项可以直接插入数据透视表,因此地图不能直接插入。因此,通过引用数据透视表的各个单元格创建另一个表,然后使用新表插入地图(我们引用数据透视表的值,因为只有这样,仪表板中应用的任何更改/过滤才会影响图表)。

查看公式栏以供参考

第四步:

为最终仪表板添加新工作表,并粘贴所有创建的视觉效果。对于病例、死亡和康复的总数,请引用相应数据透视表中单元格的值。

第五步:

现在我们将插入一个切片器,使用国家/地区来过滤值。为此,插入->切片器->数据模型,然后选择数据模型。然后从我们创建的 countries 表中选择 country/region 列。

现在我们的切片机准备好了。

第六步:

是时候做些最后的润色了:

  1. 首先,安排所有的视觉和切片在一个适当的格式。
  2. 添加颜色并为您的仪表板添加标题
  3. 检查文本的对齐方式和字体大小。
  4. 隐藏所有其他工作表,只保留仪表板
  5. 最后,转到视图菜单,删除公式栏、标题和网格线。

仪表板准备好了!

结论

我们已经讨论了如何使用 Excel 构建交互式仪表板。这是一种简单的方法,可以很容易地应用于任何其他数据。正如我们所看到的,Excel 仪表板简单且易于构建,具有许多功能,最重要的是,它可以免费构建并与他人共享。但是正如我前面提到的,与 Tableau、Power BI 和 Infogram 等其他高级工具相比,使用 Excel 作为仪表板有很多缺点。一些缺点是:

  1. 与 excel 相比,高级工具为仪表板提供了更好的 UI/UX。
  2. 与其他工具不同,Excel 仪表板不能直接链接到 web,并且您每次都需要共享 excel 文件。当其他人使用不同版本的 Excel 时,这有时可能会导致问题。
  3. 高级工具提供了更好的交互性。
  4. 如果文件变大,Excel 有时会开始崩溃/变慢。

还有许多其他因素使高级工具更适合仪表板,因此 Excel 不是完全的替代品。但是,如果您正在为个人项目或任何其他小规模用例寻找一些经济高效的解决方案,Excel 无疑是一个不错的选择。因此,知道如何使用 Excel 构建有用的仪表板是一项值得掌握的技能,我希望本文为此提供了一个很好的介绍。

请随意评论任何关于这方面的帮助,我很乐意帮助你。

在这里找到最后使用的 Excel 文档。(下载文件,并取消隐藏所有的工作表,看看工作)

用 Python 构建交互式地图——初学者指南

原文:https://towardsdatascience.com/building-interactive-maps-in-python-the-beginners-guide-5711dd66257e?source=collection_archive---------16-----------------------

使用气候数据的简单实践

欢迎来到用 Python 构建交互式地图的初学者指南

在这篇文章中,我将向你展示如何使用历史气候数据创建交互式气候地图,在这里你可以可视化、检查和探索这些数据。数据可视化在表示数据方面起着重要的作用。创建可视化有助于以更容易理解的形式呈现您的分析。尤其是在处理大型数据集时,很容易迷失方向,这时我们就可以看到数据可视化的威力了。在本练习中,我们将使用 Kaggle 的气候数据。我们将制作两张交互式气候地图。第一个将显示每个国家的气候变化,第二个将显示温度随时间的变化。让我们开始吧,我们有很多事情要做!

Kaggle 是世界上最大的数据科学社区,拥有强大的工具和资源来帮助您实现数据科学目标。

目录:

  • Plotly
  • 理解数据
  • 数据清理
  • 数据预处理
  • 数据可视化

Plotly

Plotly 是一个 Python 图形库,可以制作交互式的、出版物质量的图形。如何制作折线图、散点图、面积图、条形图、误差线、箱线图、直方图、热图、支线图、多轴图、极坐标图和气泡图的示例。它也是一个开源库。

了解更多关于 Plotly: Plotly 图形库

理解数据

伯克利地球表面温度研究结合了来自 16 个现有档案的 16 亿份温度报告。它被很好地打包,并允许分割成有趣的子集(例如按国家)。他们发布源数据和他们应用的转换代码。

数据集可在以下链接找到:气候数据

数据文件夹包括以下数据集:

  • 按国家分列的全球平均陆地温度
  • 各州全球平均陆地温度
  • 主要城市的全球陆地温度
  • 按城市划分的全球土地温度
  • 全球陆地和海洋与陆地温度

我们将使用“按国家划分的全球平均陆地温度”数据集,该数据更符合我们的目标,因为我们将构建交互式气候地图,按国家过滤数据将使我们的生活更加轻松。

图书馆

我们需要三个主要的库来开始。当我们谈到可视化时,我会要求您导入更多的子库,也称为库组件。现在,我们将导入以下库:

import numpy as np
import pandas as pd
import plotly as py

如果您没有这些库,也不用担心。安装它们非常容易,如下图所示:

pip install numpy pandas plotly

读出数据

df = pd.read_csv("data/GlobalLandTemperaturesByCountry.csv")print(df.head())

print(df.tail())

尾巴

# Checking the null values in each column
df.isnull().sum()

无效的

数据清理

数据科学更多的是理解数据,数据清洗是这个过程中非常重要的一部分。什么让数据更有价值,取决于我们能从中获得多少。做好数据准备会让你的数据分析结果更加准确。

先说清洁流程。首先,让我们从删除“AverageTemperatureUncertainty”列开始,因为我们不需要它。

df = df.drop("AverageTemperatureUncertainty", axis=1)

然后,让我们重新命名列名,以便看起来更好。正如你在上面看到的,我们使用了一种叫做重命名的方法。重命名列名是不是很容易?

df = df.rename(columns={'dt':'Date'})
df = df.rename(columns={'AverageTemperature':'AvTemp'})

最后,对于数据清理,让我们删除具有空值的行,这样它们就不会影响我们的分析。如前所述,我们在 AverageTemperature 列中有大约 32000 行的值为空。我们总共有大约 577000 行,所以删除它们没什么大不了的。但是在某些情况下,有一些其他的方法来处理空值。

df = df.dropna()

现在,让我们看看我们的数据框架。我将使用 head 方法打印前 10 行。

df.head(10)

结果

数据预处理

这一步也被称为数据操作,我们过滤数据,以便我们可以专注于特定的分析。尤其是在处理大数据集时,数据预处理/过滤是必须的。例如,我们的历史气候数据显示了从 1744 年到 2013 年所有 12 个月的温度,所以这实际上是一个非常大的范围。使用数据过滤技术,我们将集中在一个更小的范围内,如 2000 年至 2002 年。

比较运算符

  • <
  • <=
  • =

  • ==
  • !=

我们将使用这些运算符将特定值与列中的值进行比较。结果将是一系列的布尔值:真和假。如果比较正确,则为真,如果比较不正确,则为假。

分组依据

在这一步中,我们按照国家名称和日期列对数据帧进行分组。此外,按日期从最近到最早对值进行排序。

df_countries = df.groupby( ['Country','Date']).sum().reset_index().sort_values('Date', ascending=False
)

结果

通过数据范围屏蔽

start_date = '2000-01-01' 
end_date = '2002-01-01' mask = (df_countries['Date'] > start_date) & (df_countries['Date'] <= end_date) df_countries = df_countries.loc[mask] df_countries.head(10)

结果

正如你在上面看到的,数据框看起来很棒。按日期排序并按国家名称过滤。通过查看这个数据图表,我们可以找到每个国家每个月的平均温度。有趣的部分来了,这就是数据可视化。你准备好了吗?

数据可视化

Plotly 的组件

在我们开始之前,如前所述,有几个子库需要导入以享受数据可视化。这些子库也称为组件。

#Plotly Componentsimport plotly.express as px
import plotly.graph_objs as go
from plotly.subplots import make_subplots
from plotly.offline import download_plotlyjs, init_notebook_mode, plot, iplot 

气候变化图

很好,现在通过运行下面的代码,你将会看到奇迹的发生。

#Creating the visualization
fig = go.Figure(data=go.Choropleth( locations = df_countries['Country'], locationmode = 'country names', z = df_countries['AvTemp'], colorscale = 'Reds', marker_line_color = 'black', marker_line_width = 0.5, ))fig.update_layout( title_text = 'Climate Change', title_x = 0.5, geo=dict( showframe = False, showcoastlines = False, projection_type = 'equirectangular' ) ) fig.show()

气候变化互动地图

按时间线划分的气候变化

# Manipulating the original dataframe 
df_countrydate = df_countries.groupby(['Date','Country']). sum().reset_index() #Creating the visualization 
fig = px.choropleth(df_countrydate, locations="Country", locationmode = "country names", color="AvTemp", hover_name="Country", animation_frame="Date" ) fig.update_layout( title_text = 'Average Temperature Change', title_x = 0.5, geo=dict( showframe = False, showcoastlines = False, )) fig.show()

结果

两张图是一样的,在第一张图中你可以看到平均温度的变化。在第二张图中,我将鼠标悬停在一些国家的上方,这将显示每个国家的更多详细信息。

互动地图 1

互动地图 2

感谢你阅读这篇文章,我希望你喜欢并且今天学到了一些新的东西。如果您在执行代码时有任何问题,请随时通过我的博客联系我。我非常乐意帮忙。你可以找到更多我发表的与 Python 和机器学习相关的帖子。保持安全和快乐的编码!

我是贝希克·居文,我喜欢分享关于创造力、编程、动力和生活的故事。

跟随 我的博客 走向数据科学 以待启发。

相关职位

[## 面向金融的 Python 完全初学者指南

使用亚马逊股票数据的简单实践

towardsdatascience.com](/python-for-finance-the-complete-beginners-guide-764276d74cef) [## 用 Python 构建语音识别器

使用谷歌云语音 API 将您的音频文件转换为文本

towardsdatascience.com](/building-a-speech-recognizer-in-python-2dad733949b4)

构建 Jarvis,一个有态度的生成型聊天机器人

原文:https://towardsdatascience.com/building-jarvis-the-generative-chatbot-with-an-attitude-a833f709f46c?source=collection_archive---------13-----------------------

我使用了一个在亚马逊 SageMaker、亚马逊 Polly、谷歌语音 API 和一个音频路由工具上训练的神经机器翻译模型,来构建 Jarvis——一个可以在视频会议电话中与你交谈的聊天机器人。

我工作的公司 Carsales.com 正在举办一场黑客马拉松活动。这是一年一度的活动,每个人(技术或非技术)都聚集在一起,组成一个团队,建造任何东西——任何东西。嗯,最好你能建立一些有商业目的的东西,但这真的取决于你。这个聊天机器人的想法实际上来自 carsales.com 公司的首席信息官杰森·布莱克曼。

Carsales 黑客马拉松

鉴于我们的下一个黑客马拉松是一个在线活动,感谢新冠肺炎,如果我们能举办一个 Zoom 网络研讨会,任何 carsales.com 的员工都可以加入进来,与一个我们可以称为贾维斯的人工智能机器人聊天,他将永远与你聊天,这不是很酷吗?

头脑风暴

经过反复思考,我想出了一个高层次的范围。Jarvis 需要有一个视觉形象,就像网上研讨会的参与者一样。他需要能够倾听你说的话,并用声音回应上下文。

我希望他的回复尽可能有创意,并且能够即时回复。大多数聊天机器人系统是基于检索的,这意味着它们有数百或数千个准备好的句子对(源和目标),这些句子对形成了它们的知识库。当机器人听到一个句子时,它会试图从知识库中找到最相似的源句子,并简单地返回配对的目标句子。基于检索的机器人,如亚马逊 Alexa 和谷歌 Home,更容易构建,并且可以很好地完成特定的任务,如预订餐厅或开灯或关灯,而对话范围有限。然而,出于娱乐目的,如闲聊,他们的回答与生成性回答相比缺乏创造性。

出于这个原因,我想为 Jarvis 创建一个基于生成的系统。我充分意识到我很可能不会取得好结果。但是,我很想知道现在的生成式聊天机器人技术已经走了多远,能做什么。

建筑

好吧,我知道我想要什么。现在是时候认真思考我到底该如何建造这个机器人了。

我们知道,第一个需要的组件是路由音频和视频的机制。我们的机器人需要能够听到 Zoom 上的对话,所以我们需要一种方法将音频从 Zoom 发送到我们的机器人。然后,这个音频需要被传递到语音识别模块,这将为我们提供文本形式的对话。然后,我们需要将这些文本传递到我们的生成式人工智能模型中,以获得回复,通过使用文本到语音技术,回复将被转换为语音。当播放音频回复时,我们需要一个动画化身,除了坐立不安之外,还可以与音频播放同步移动他的嘴唇。化身动画和音频回放需要被发送回缩放,以便所有与会者听到和看到。哇!这确实是一个相当复杂的系统。

贾维斯的建筑图

总而言之,我们需要以下组件:

  • 音频/视频路由
  • 语音识别
  • 生成人工智能模型
  • 文本到语音
  • 动画化身
  • 控制器

音频/视频路由

我喜欢别人为我做艰苦的工作。 Loopback 是一款音频工具,允许你将音频从任何应用程序重定向到虚拟麦克风。我只需要两条音频路径。第一个是将音频从 Zoom 应用程序发送到一个虚拟麦克风,我的机器人可以从这个麦克风中监听。

音频路由 1 图

第二个路由是将聊天机器人的音频输出路由到另一个虚拟麦克风,Zoom 应用程序和我们的头像工具都可以听到。很明显,Zoom 需要监听这段音频。然而,为什么我们的头像工具需要这个音频呢?嘴唇同步,这样我们的化身就可以根据音频播放移动他的嘴唇。在这篇博客的后面部分,你会看到更多的细节。

音频路由 2 图

语音识别

该模块负责通过虚拟麦克风处理来自 Zoom 的音频,并将其转换为文本。有几个离线和在线语音识别框架可供选择。我最终使用的是谷歌语音 API。这是一个在线 API,具有令人敬畏的 python 接口,提供了极高的准确性,更重要的是,允许您以块的形式传输和识别音频,从而大大减少了处理时间。我想强调的是,延迟(机器人响应查询所需的时间)对于聊天机器人来说非常重要。一个反应缓慢的机器人可能看起来非常机器人化和不切实际。

大多数情况下,谷歌语音 API 会在一个句子被完全听到后不到一秒钟内返回一个响应。

生成人工智能模型

这是我花了大部分时间的部分。在花了一两天时间了解生成聊天机器人技术的最新发展后,我发现神经机器翻译模型最近似乎非常流行。

其概念是向编码器-解码器 LSTM 模型提供来自输入句子的单词嵌入,并且能够生成上下文输出句子。这种技术通常用于语言翻译。然而,考虑到这项工作只是简单地将一个句子映射到另一个句子,它也可以用于生成对一个句子的回复(理论上)。

神经机器翻译模型体系结构

通俗地说,就是把一个输入的句子分解成单词。然后,每个单词被映射到一个整数 id,该整数 id 随后被传递到嵌入层。在训练期间,嵌入层学习将 id 列表转换成嵌入向量列表,其大小为“x”维。这个向量是以这样一种方式构建的,即具有相似含义的单词产生相似的向量,这将提供更深层次的信息,而不仅仅是单个整数值。这些向量然后被传递到 LSTM 编码器层,该层将它们转换成思维向量(有些人称之为潜在向量),其中包含关于整个输入句子的信息。请注意,有一个流行的误解,认为有许多 LSTM 层或块,而事实上只有一个。上图中的许多块显示了同一个 LSTM 块在一个接一个的逐字处理后被一次性调用。

模型右手边的解码器负责把这个思维向量变成输出句子。句子的特殊开头单词作为初始输入与思想向量一起被传递到 LSTM 层,以生成第一个单词,该第一个单词作为输入被转发到相同的 LSTM 层,以生成下一个单词,等等。

解码器输出 Softmax

稍微深入到技术领域,LSTM 解码器单元的输出实际上是一个传递到 softmax(分类)层的数字,该层返回我们词汇表中每个可能单词的概率。具有最高概率的字(在上面的情况中,它是“I”)是被挑选出来作为输出字的一个,并且也作为输入被传递到 LSTM 解码器层以生成下一个字。

网上有一些关于如何构建这种模型架构的例子。然而,如果别人已经为你做了艰苦的工作,为什么还要建立一个呢?介绍:亚马逊 SageMaker !Amazon SageMaker 是一个工具和管道的集合,可以加速构建 ML 模型,并带有大量令人惊叹的内置算法,如图像分类,对象检测,神经风格转移和 seq2seq,它是神经机器翻译的一个近似变体,但具有额外的注意力机制。

亚马逊 SageMaker

亚马逊 SageMaker seq2seq 模型是高度可定制的。您可以选择使用多少 LSTM 单位、隐藏单元的数量、嵌入向量维数、LSTM 层数等。,这给了我足够的灵活性来试验不同的参数以获得更好的结果。

获取训练集

训练集的选择对于你的聊天机器人成功地做出有上下文和有意义的回复是至关重要的。训练集需要是双方之间的对话交流的集合。具体来说,我们需要为每个条目构建一对句子,一个源句子和一个目标句子。比如‘来源:你好吗?’目标:我很好。来源:你住在哪里?目标:我住在澳大利亚。如果没有大量的人工清理工作,这种类型的训练集是很难获得的。人们使用的最流行的数据集是康奈尔电影对话语料库数据集,它并不出色(稍后你会明白为什么),但却是目前你能得到的最好的数据集。

康奈尔电影对话语料库

这个数据集由取自一部电影对白的 22 万行对话组成。每行包括一个或多个人的对话。下面的三行示例向您展示了一些很好的例子,在这些例子中,对话以一个人的问题(粗体)开始,然后是另一个人的回答。

你懂法语?当然……我妈妈来自加拿大

你要去哪里?如果你一定要知道的话,我们正试图参加一个由朋友组成的小型学习小组。

有多少人去这里?几千。大多数都是邪恶的

然而,还有很多糟糕的例子,因为你需要前一段对话的上下文或视觉参考才能理解后面的句子。例如,请参见以下内容:

最坏的是什么?你得到了那个女孩。

不开玩笑。他是个罪犯。

这是肺癌问题。她最喜欢的叔叔

没有一种简单的方法可以在不投入体力劳动的情况下去除坏的线条,而我并不真的准备这么做。即使我这样做了,我得到的数据集也会少得多;不足以训练我的人工智能模型。因此,我决定不顾一切地继续下去,看看我能走多远。

我从每个对话行和每两个连续的句子中生成了多对源句子和目标句子,不管是谁说的。

最糟糕的是什么?你破产了?“下一步做什么,”会变成两对对话线。

最糟糕的是什么?你破产了?

你破产了?下一步怎么办?

这样,我设法扩大了我的数据集。我完全意识到,我可能会错误地将同一个人所说的源句子和目标句子配对。然而,有一半时间,如果目标句子在别人的回复中,它仍然是有意义的,如下例所示。

最糟糕的是什么?你破产了?接下来做什么?

使用 nltk 库,可以在两行代码中完成对句子的分词/拆分。

tokenizer = nltk.data.load('tokenizers/punkt/english.pickle')
sentences = tokenizer.tokenize(text)

我还修剪了一个超过 20 个单词的句子,因为我的模型只能读取最多 20 个单词的输入和 20 个单词的输出。除此之外,更长的句子意味着更大的上下文和更高的变化,这对于人工智能模型来说更难学习,因为我们的训练集规模不是那么大。

用上面的方法,我设法得到了 441k 对对话行。

预处理训练集

下一步是进一步预处理这个训练集,这包括几个步骤。

第一步是从字符串对中删除和替换不需要的字符串,比如所有的 xml 和 html 标签以及多个点和破折号。
接下来就是扩展缩略词,例如,你会被扩展为你会我是我是等等。,这增加了我的人工智能模型准确性。这样做的原因是,在后面的步骤中,通过对分隔符(如空格、换行符或制表符)进行拆分,句子将被转换为单词列表。所有独特的单词将构成我们的词汇。像“I'm”这样的缩写将被认为是词汇表中的新单词,这将不必要地增加我们的词汇量,并由于碎片化而降低我们的训练集的有效性;因此让我们的人工智能更难学习。

我使用了一个非常方便的 python 框架,叫做“缩写”,它用一行代码扩展了一个句子中的缩写。

text = contractions.fix(text)

标点符号是下一个要解决的问题。我把标点符号从句子中分离出来(例如,“你好吗?”变成了“你好吗?”)这样做的原因类似于收缩;确保标点符号本身会被视为一个单词,而不会与前一个单词合并为一个新单词,例如“you?在上面的例子中。

通过以上所有步骤,我获得了大约 441k 对训练集和 56k 个词汇。

作为最后一步,我添加了一个词汇表剪枝,这样我就可以控制词汇表中支持的最大单词数。删除不常用的单词很容易做到。较小的词汇量和较大的训练集更有利。这很有意义——想象一下,通过给孩子 100 个例句来教他们 5 个新单词,而不是用同样的 100 个例句来教 100 个新单词,这有多难。在更多的句子中更频繁地使用更少的单词当然会帮助你学得更好。

训练数据准备管道

从我的许多实验中,我发现 15k 单词的词汇量给了我最好的结果,产生了 346k 训练集,等于 23 的比率,而不是最初的 441k/56k =7.9。

为 Seq2Seq 模型定型

由于 Amazon SageMaker 已经提供了一个关于如何训练 Seq2Seq 模型的示例 Jupyter 笔记本,因此启动培训非常容易。

我只需要定制我的训练文件所在的 S3 桶,并添加我的数据预处理代码。接下来,我将向您展示在 SageMaker 中训练 seq2seq 模型是多么容易,方法是向您展示这里和那里的代码片段。如果你想看完整的源代码,可以看看我的 jupyter 笔记本。

首先,你需要让 SageMaker 知道你想要使用哪个内置算法。每个算法都被封装起来,可以从一个特定的 URL 获得。

from sagemaker.amazon.amazon_estimator import get_image_uri
container = get_image_uri(region_name, 'seq2seq')

接下来,您需要构建培训工作描述,它提供了一些关于培训工作的重要信息。首先,您需要设置训练集的位置以及存储最终模型的位置。

"InputDataConfig": [
    {
        "ChannelName": "train",
        "DataSource": {
            "S3DataSource": {
                "S3DataType": "S3Prefix",
                "S3Uri": "s3://{}/{}/train/".format(bucket, prefix),
                "S3DataDistributionType": "FullyReplicated"
            }
        },
    },
....
"OutputDataConfig": {
    "S3OutputPath": "s3://{}/{}/".format(bucket, prefix)
},

在这之后,您需要选择您想要在什么机器或实例上运行这个培训。Seq2seq 是一款相当重的机型,需要一台 GPU 机。我推荐的是 ml.p3.8xlarge,有四个 NVIDIA V100 GPUs。

"ResourceConfig": {
    "InstanceCount": 1,
    "InstanceType": "ml.p3.8xlarge",
    "VolumeSizeInGB": 50
}

最后,您需要设置超参数。这是我花了 30%时间的地方,几乎是第二次尝试数据准备策略。我在不同的设置下构建了各种模型,并比较了它们的性能,以得出最佳配置。

记住,我选择把我的句子限制在 20 个单词以内。下面的前两行是原因。我的 LSTM 模型只被训练识别最多 20 个单词的输入和最多 20 个单词的输出。

"HyperParameters": {
    "max_seq_len_source": "20",
    "max_seq_len_target": "20",
    "optimized_metric": "bleu",
    "bleu_sample_size": "1000",
    "batch_size": "512",
    "checkpoint_frequency_num_batches": "1000",
    "rnn_num_hidden": "2048",
    "num_layers_encoder": "1",
    "num_layers_decoder": "1",
    "num_embed_source": "512",
    "num_embed_target": "512",
    "max_num_batches": "40100",
    "checkpoint_threshold": "3"
},

通常,批量越大(如果你的 GPU RAM 可以处理的话)越好,因为你可以一次训练更多的数据来加快训练过程。512 似乎是 p3.8xlarge 的最大尺寸。有些人可能会认为不同的批量大小会产生稍微不同的精度,但是我的目标不是在这里获得诺贝尔奖,所以小的精度差异对我来说并不重要。

我为每个编码器和解码器使用一层,每个编码器和解码器有 2048 个隐藏单元,字嵌入大小为 512。然后在每批 1,000 个样本中进行检查点和评估,这相当于每批 1,000×512 个样本(512k 对样本)。因此,训练将达到总训练集的 1.5 倍(总共 346k),直到它针对该模型执行评估。在每个检查点,最好的评估模型被保留,然后最终保存到我们的输出 S3 桶中。SageMaker 也有提前终止的能力。例如,如果模型在连续三个检查点(' checkpoint_threshold ')后没有改善,则训练将停止。

此外,“最大数量批次”是一个安全网,无论如何都可以停止训练。在你的模型永远保持改进的情况下(这是一件好事),这将保护你,使培训费用不会打破你的银行(这不是一件好事)

训练模型

只需要两行代码就可以开始训练。然后你只需要耐心等待几个小时,这取决于你使用的实例类型。我用 p3.8xlarge 实例花了一个半小时。

sagemaker_client = boto3.Session().client(service_name='sagemaker')
sagemaker_client.create_training_job(**create_training_params)

正如您在下面看到的,验证指标在第八个检查点找到了性能最好的模型。

Bleu 评估指标

模型评估

我之前有意跳过了对“optimized_metric”的讨论,因为它值得有自己的部分来适当地解释它。评估一个生成式人工智能模型并不是一项简单的任务。通常,问题和答案之间没有一对一的关系。比如一个问题,十个不同的答案都一样好,这就导致了映射的范围非常广。对于语言翻译任务,映射范围要窄得多。然而,对于聊天机器人来说,范围显著增加,特别是当你使用电影对话作为训练集时。你要去哪里?'可以是以下任何一种:

  • 去地狱
  • 为什么这么问?
  • 我不会告诉你的。
  • 什么?
  • 你能再说一遍吗?

亚马逊 SageMaker 中的 seq2seq 算法有两个流行的评测指标可供选择:困惑度和 bleu。performance 通过在我们的训练集中使用概率分布模型逐字随机抽取样本来评估生成的句子,这在这篇文章中有很好的解释。

Bleu 通过对单词 n-gram 匹配进行评分来针对目标句子评估生成的句子,并且如果生成的句子比目标句子短,则惩罚生成的句子。这篇文章解释了它是如何工作的,并建议不要使用它,有一个很好的理由。相反,我发现它比困惑更有效,因为生成的句子的质量与人工检查时 bleu 的分数密切相关。

测试模型

当训练完成时,您可以创建一个用于推断的端点。从那里,用几行代码就可以生成一个带推理的文本。

response = runtime.invoke_endpoint(EndpointName=endpoint_name, 
                                   ContentType='application/json', 
                                   Body=json.dumps(payload))

response = response["Body"].read().decode("utf-8")
response = json.loads(response)
for i, pred in enumerate(response['predictions']):
    print(f"Human: {sentences[i]}\nJarvis: {pred['target']}\n")

这些结果不会让我赢得奖牌(大约 60%的回答与上下文无关,20%还过得去)。然而,让我兴奋的是,另外的 20%出奇的好。它回答了正确的上下文和语法。因此,它向我展示了它可以在一定程度上学习英语,甚至可以像我们一样骂人。这也很好地提醒了你的孩子可以从电影中学到什么。

我最喜欢的一个例子是当机器人被问到,谁是你最好的朋友他回答,‘我的妻子’。我已经检查过了,这些句子中的大多数甚至不在训练集中,所以人工智能模型确实学到了一点创造力,而不仅仅是记住了训练集。

Jarvis 聊天日志(很好的例子)

这里有一些不好的。

Jarvis 聊天日志(不好的例子)

通过试验几个不同的超参数,我发现:

  • 增加更多的编码器和解码器层会使情况变得更糟。有趣的是,生成的句子结构更加复杂。然而,问题答案的相关性很差。
  • 减小单词嵌入大小也降低了生成质量。
  • 将词汇量减少到 15,000 个单词提高了质量,而进一步减少会降低质量。
  • 扩展单词缩写和分隔标点符号无疑提高了回答的质量。
  • 虽然这可能更多的是一个预处理步骤,而不是一个超参数设置。值得一提的是,添加字节对编码(正如 SageMaker notebook 所建议的)会降低响应的质量。

语音生成和动画化身

我需要的下一个模块是文本到语音和动画化身。我用了一个很棒的 Amazon Polly 来生成文本到语音。同样,它是一个在线 API。然而,它具有超强的灯光响应时间(大多数情况下不到 300 毫秒)和听起来自然的高质量语音。

亚马逊 Polly 文本到语音

鉴于我之前作为特效和动作捕捉软件工程师的工作,我非常想自己制作动画化身。实际上,我在另一个项目中构建了一个简单的虚拟化身系统:构建一个为我蹒跚学步的孩子播放视频的机器人。谢天谢地,我的大部分意识到,如果我继续这个旅程,而不是使用一个很棒的 3D 化身软件, Loom.ai ,这将花费我多长时间,它带有音频对口型功能!你只需要向 Loom.ai 应用程序发送一个音频剪辑,它就会制作一个 3D 化身的动画,与提供的音频进行口型同步。多棒啊。该应用程序还带有一个假的摄像机驱动程序,它可以传输渲染输出。我只需要在缩放应用程序设置中选择假摄像机,这样我就可以在缩放会议中包含动画。

贾维斯动画化身

结果

随着所有模块的完成,我需要做的就是建立一个控制器系统,将一切结合起来。第一次测试运行得相当好,除了贾维斯偷走了所有的对话。他不客气地插话,并回答视频会议中任何人说的每一句话,惹恼了每个人。嗯,有一件事我忘了训练贾维斯——礼貌和社交技巧!

与贾维斯进行视频会议聊天

与其建立另一个社交技能模块,我不知道如何开始,一个简单的解决办法是教贾维斯通过语音命令开始和停止说话。当他听到“贾维斯,别说话”时,他停止了反应,直到他听到“贾维斯,开始说话”。有了这个新的重要技能,每个人都开始更喜欢贾维斯了。

这种生成模型在最初的几次交流中很有趣,因为他的一些回答让我们惊讶于他的创造力和他的粗鲁。然而,这种新鲜感很快就消失了,因为对话持续的时间越长,我们听到的断章取义的回应就越多,直到最终我们意识到我们根本没有在进行一场有意义的对话。出于这个原因,我最终使用了由 AIML 语言模型驱动的基于模式的聊天机器人技术,它令人惊讶地提供了比普通的基于检索的模型更好的创造力,并且可以从过去的信息中回忆上下文!这篇文章清楚地解释了它是什么,也许它会成为我改天讲述的故事。

在我的超级棒的黑客马拉松团队成员的帮助下,我们甚至扩展了 Jarvis 的能力,使其成为按需会议助理,可以帮助记录笔记、分配给相关个人的行动要点、通过电子邮件发送给与会者以及安排后续会议。

结论

有时候机器人生成的文本质量很好。然而,仅仅从两次对话中(即使是好的对话),你就能清楚地看出有些事情不正常。

  • 它没有过去的背景。每个回答都是从所提问题的上下文中产生的。它没有考虑之前的对话,而大多数人可以很容易地做到这一点。显然,目前的技术还不足以建立一个可以考虑过去对话历史的模型。至少我还没有听说过。
  • 一半以上的时间里,它给出的都是不着边际的回应。虽然可能还需要更好的模型架构,但是我最大的怀疑是训练集。电影对话具有广泛的主题,这使得人工智能模型很难学习,特别是当源句子和目标句子有时没有意义时(例如,没有事先的上下文,需要视觉参考或配对不正确)。像餐馆预订对话这样的训练集可能工作得更好,因为餐馆预订的范围非常有限。然而,对话交流可能会不那么有趣。

鉴于以上所述,我相信一个完全实用的、可生成的聊天机器人还需要几年或几十年的时间。我现在完全可以理解为什么我们要花很多年去学习一门语言。

未来生成聊天机器人

然而,这是一个非常有趣的项目,我从中学到了很多。

这个项目的代码可以从 github 获得。

从结构化来源构建知识图

原文:https://towardsdatascience.com/building-knowledge-graphs-from-structured-sources-346c56c9d40e?source=collection_archive---------19-----------------------

KGs 洞察

关于集成异构数据所采用的映射方法的说明

构图——原图来自 Pixabay

K 知识图(KGs)被标记为有向多重图,以与特定领域或组织相关的实体和关系的形式编码信息。kg 是捕获和组织大量结构化和多关系数据的有效工具,这些数据可以使用查询机制进行研究。考虑到这些特性,KGs 正在成为不同研究领域和工业应用中的 Web 和遗留信息系统的主干。关于 KG 特性的更多细节,包括 RDF、本体和 SPARQL,可以在下面的文章中找到:

[## 知识图表一览

利用语义图视角,将人类知识融入智能系统

towardsdatascience.com](/knowledge-graphs-at-a-glance-c9119130a9f0)

将数据发布到 KGs 是一个复杂的过程,因为它需要提取和整合来自不同来源的信息。整合这些来源的目标是协调它们的数据,并导致对整体信息的一致看法。异构数据源包括从非结构化数据(如纯文本)到结构化数据(包括表格式,如 CSV 和关系数据库)以及树结构格式(如 JSONs 和 XML)。本文的贡献是通过基于映射的方法将结构化数据源中的数据发布到 KGs 中。

结构化来源的整合

结构化数据源在数据生态系统中扮演着重要的角色,因为组织和网络中的许多有价值(且可重用)的信息都是结构化数据。

不像其他来源,比如纯文本,结构化信息可以通过一个语义集成过程映射到 kg。语义网(SW)社区应用这一过程的共同策略是采用参考本体作为全局模式。然后,构建映射来描述目标数据源的全局模式和局部模式之间的关系。从数据集成的角度来看,这种方法被归类为全局视图(GAV)。

从这个角度来看,数据集成性能基于作为全局模式的本体的一致性和表达能力。为了阐明真实场景中的映射过程,下一节将介绍公共采购领域中的一个运行示例。

公共采购示例:数据源和参考本体

公共采购是指公共机构和行政部门从公司购买货物或服务的过程。在这一过程中,公共当局宣布招标,公司带着具体的标书参加招标,公共当局将授予其中一份标书。

假设我们有一个目标数据源 ds ,包括一组属性 ds{a,a,a,…} 。在公共采购中,目标数据源是表示特定公共合同的 JSON 文件。JSON (JavaScript Object Notation)是一种独立于语言的数据交换格式。它使用人类可读的文本来存储和传输以属性-值对和任何可序列化的数据类型(包括数组)为特征的数据对象。

{
   "contract_id":"Z4ADEA9DE4",
   "contract_object":"MANUTENZIONE ORDINARIA MEZZI DI TRASPORTO",
   "proposing_struct":{
      "business_id":"80004990927",
      "business_name":"Ministero dell'Interno"
   },
   "participants":[
      {
         "business_id":"08106710158",
         "business_name":"CAR WASH CARALIS"
      }
   ]
}

该 JSON 描述了以下数据:

  • contract_id 包括所需服务的标识符(“Z4ADEA9DE4”)。
  • 合同对象包括服务的描述(“MANUTENZIONE ORDINARIA MEZZI DI TRASPORTO”)。
  • business_idbusiness_name ,嵌套在 proposing_struct 字段包括提出招标的公共机构的标识符(“80004990927”)和名称(“Ministero dell'Interno”)。
  • business_idbusiness_name ,嵌套在参与者字段中,包括标识符(“Z4ADEA9DE4”)和名称(“洗车卡利斯”)。

映射过程需要参考本体作为全局模式。捷克 OpenData.cz 倡议发布了公共采购中采用的最常见的本体之一,可在 GitHub 上获得:https://github.com/opendatacz/public-contracts-ontology

与我们的运行示例相关的映射过程中涉及的本体公理可以用 Turtle 格式表示如下:

@prefix dcterms: <[http://purl.org/dc/terms/](http://purl.org/dc/terms/)> .
[@prefix](http://twitter.com/prefix) gr: <[http://purl.org/goodrelations/v1#](http://purl.org/goodrelations/v1#)> .
[@prefix](http://twitter.com/prefix) owl: <[http://www.w3.org/2002/07/owl#](http://www.w3.org/2002/07/owl#)> .
[@prefix](http://twitter.com/prefix) pc: <[http://purl.org/procurement/public-contracts#](http://purl.org/procurement/public-contracts#)> .
[@prefix](http://twitter.com/prefix) rdf: <[http://www.w3.org/1999/02/22-rdf-syntax-ns#](http://www.w3.org/1999/02/22-rdf-syntax-ns#)> .
[@prefix](http://twitter.com/prefix) rdfs: <[http://www.w3.org/2000/01/rdf-schema#](http://www.w3.org/2000/01/rdf-schema#)> .####### 
####### Classes
#######pc:Contract a owl:Class .
gr:Offering a owl:Class .
pc:Tender a owl:Class ;
    rdfs:subClassOf gr:Offering .####### 
####### Relations (or Object Properties)
#######pc:contractingAuthority a owl:FunctionalProperty, owl:ObjectProperty ;
    rdfs:domain pc:Contract ;
    rdfs:range gr:BusinessEntity .pc:tender a owl:ObjectProperty ;
    rdfs:domain pc:Contract ;
    rdfs:range pc:Tender .pc:awardedTender a owl:FunctionalProperty, owl:ObjectProperty ;
    rdfs:subPropertyOf pc:tender .pc:bidder a owl:ObjectProperty ;
    rdfs:domain pc:Tender ;
    rdfs:range gr:BusinessEntity .####### 
####### Datatype properties
#######dcterms:identifier a owl:DatatypeProperty ;
    rdfs:domain pc:Contract ;
    rdfs:domain gr:BusinessEntity ;
    rdfs:range rdfs:Literal .rdfs:label a owl:DatatypeProperty ;
    rdfs:domain pc:Contract ;
    rdfs:domain gr:BusinessEntity ;
    rdfs:range rdfs:Literal .rdfs:description a owl:DatatypeProperty ;
    rdfs:domain pc:Contract ;
    rdfs:range rdfs:Literal .

映射过程的详细信息

整个映射过程包括两个主要步骤。第一步是在目标数据源的本地模式和参考本体之间创建一个映射。第二步是将源数据具体化为 KG 语句或虚拟化对源的访问,定义遗留信息的基于图形的视图。物化的语句可以直接发布到 KG 中,而基于图形和虚拟化的访问允许我们检索和浏览目标数据源的数据,就像它是 KG 一样。

映射步骤最广泛采用的方法是基于所谓的定制映射。这些方法利用用声明性语言编写的可定制文档来执行地图生成步骤。声明性语言利用 SW 形式来描述局部模式和全局模式之间的关系。

研究团体采用的最突出的语言是 R2RML,它表示用 RDF 编写的关系数据库到 kg 之间的定制映射。这种语言的扩展,称为 RML,是一种更通用的映射语言,其适用性扩展到其他类型的表,如 CSV 文件和树结构模式。其他类型的语言,如 TARQL 和 JARQL,分别采用 SPARQL 语法为特定格式(如 CSV 和 JSON 文件)创建映射。描述 dsO 之间映射的 JARQL 示例如下:

[@prefix](http://twitter.com/prefix) dcterms: <[http://purl.org/dc/terms/](http://purl.org/dc/terms/)> .
[@prefix](http://twitter.com/prefix) pc: <[http://purl.org/procurement/public-contracts#](http://purl.org/procurement/public-contracts#)> .
[@prefix](http://twitter.com/prefix) gr: <[http://purl.org/goodrelations/v1#](http://purl.org/goodrelations/v1#)> .
[@prefix](http://twitter.com/prefix) rdf: <[http://www.w3.org/1999/02/22-rdf-syntax-ns#](http://www.w3.org/1999/02/22-rdf-syntax-ns#)> .CONSTRUCT {
    ?BusinessEntity0 dcterms:identifier ?proposing_struct__business_id ;
        rdf:type gr:BusinessEntity. ?BusinessEntity1 dcterms:identifier ?participants__business_id ;
        rdf:type gr:BusinessEntity . ?Tender0 pc:bidder ?BusinessEntity1 . ?Contract0 dcterms:identifier ?contract_id ;
        rdf:type pc:Contract ;
        pc:contractingAuthority ?BusinessEntity0 ;
        pc:tender ?Tender0 .
}WHERE {
    ?root a jarql:Root.
    OPTIONAL { ?root jarql:contract_id ?contract_id . }
    OPTIONAL { ?root jarql:proposing_struct ?proposing_struct . }
    OPTIONAL { ?proposing_struct jarql:proposing_struct__business_id
    ?proposing_struct__business_id . }
    OPTIONAL { ?root jarql:participants ?participants . }
    OPTIONAL { ?participants jarql:participants__business_id
    ?participants__business_id . }
    BIND (URI(CONCAT('[http://purl.org/procurement/public-contracts/contract/'](http://purl.org/procurement/public-contracts/contract/'),
    ?contract_id)) as ?Contract0)
    BIND (URI(CONCAT('[http://purl.org/goodrelations/v1/businessentity/'](http://purl.org/goodrelations/v1/businessentity/'),
    ?proposing_struct__business_id)) as ?BusinessEntity0)
    BIND (URI(CONCAT('[http://purl.org/goodrelations/v1/businessentity/'](http://purl.org/goodrelations/v1/businessentity/'),
    ?participants__business_id)) as ?BusinessEntity1)
    BIND (URI(CONCAT('[http://purl.org/procurement/public-contracts/tender/'](http://purl.org/procurement/public-contracts/tender/')
    ?contract_id + + '_' participants__business_id)) as ?Tender0)
}

这个 JARQL 文件包括 3 个主要部分。第一个包含在 CONSTRUCT 部分,而其他的包含在 WHERE 部分。

CONSTRUCT 部分描述了对语义类型进行编码的图形模式,例如

?BusinessEntity0 dcterms:identifier ?proposing_struct__business_id .

语义关系,如

?Contract0 pc:contractingAuthority ?BusinessEntity0 .

WHERE 部分的第一部分包括可选的操作符,描述了如何解析 JSON 文件以提取创建 KG 语句所需的数据。例如:模式

?proposing_struct jarql:proposing_struct__business_id ?proposing_struct__business_id .

表明变量。proposing_struct__business_id 必须替换为 JSON 的 proposing_struct__business_id 属性。

WHERE 部分的第二段包括不同的绑定操作符,声明如何为从 JSON 中提取的数据生成实体 URIs。这条线

BIND (URI(CONCAT(‘[http://purl.org/procurement/public-contracts/contract/'](http://purl.org/procurement/public-contracts/contract/'), ?contract\_id)) as ?Contract0) .

指示合同的 URIs 是结合 http://purl.org/procurement/public-contracts/contract/和从绑定到?合同 id 变量。

语言驱动的引擎旨在按照使用声明性语言编写的映射文档的指令,物化或虚拟化 KG 语句。这些引擎执行两个不同的任务:第一个任务是将目标数据源字段链接到由引用本体定义的类或属性。一旦创建了这个链接,这些引擎就会具体化或虚拟化 URIs 和 KG 语句,检索数据源中包含的遗留信息。JARQL 文件描述了属性集 ds{a,a,a,…} 和由 o 表示的全局模式之间的链接,允许具体化以下语句:

[@prefix](http://twitter.com/prefix) dcterms: <[http://purl.org/dc/terms/](http://purl.org/dc/terms/)> .
[@prefix](http://twitter.com/prefix) pc: <[http://purl.org/procurement/public-contracts#](http://purl.org/procurement/public-contracts#)> .
[@prefix](http://twitter.com/prefix) gr: <[http://purl.org/goodrelations/v1#](http://purl.org/goodrelations/v1#)> .
[@prefix](http://twitter.com/prefix) rdf: <[http://www.w3.org/1999/02/22-rdf-syntax-ns#](http://www.w3.org/1999/02/22-rdf-syntax-ns#)> .
[@prefix](http://twitter.com/prefix) contract: <[http://purl.org/procurement/public-contracts/contract/](http://purl.org/procurement/public-contracts/contract/)> .
[@prefix](http://twitter.com/prefix) be: <[http://purl.org/goodrelations/v1/businessentity/](http://purl.org/goodrelations/v1/businessentity/)> .
[@prefix](http://twitter.com/prefix) tender: <[http://purl.org/procurement/public-contracts/tender/](http://purl.org/procurement/public-contracts/tender/)> .# ?BusinessEntity0 dcterms:identifier ?proposing_struct__business_id ;
#      rdf:type gr:BusinessEntity .be:08106710158 dcterms:identifier 08106710158 ;
    rdf:type gr:BusinessEntity .# ?BusinessEntity1 dcterms:identifier ?participants__business_id ;
#        rdf:type gr:BusinessEntity .be:08106710158 dcterms:identifier 08106710158 ;
    rdf:type gr:BusinessEntity .# ?Tender0 pc:bidder ?BusinessEntity1 .tender:Z4ADEA9DE4-80004990927 pc:bidder be:08106710158 .# ?Contract0 dcterms:identifier ?contract_id ;
#     rdf:type pc:Contract ;
#     pc:contractingAuthority ?BusinessEntity0 ;
#     pc:tender ?Tender0 .contract:Z4ADEA9DE4 dcterms:identifier Z4ADEA9DE4 ;
    rdf:type pc:Contract ;
    pc:contractingAuthority 80004990927 ;
    pc:tender tender:Z4ADEA9DE4-80004990927 .

为了阐明 JARQL 文件支持的转换过程,本例将包含在其 CONSTRUCT 部分的图形模式作为注释进行报告。使用 JARQL 作为声明性语言的主要原因之一是它能够创建 URIs,组合位于 JSON 树结构不同层次的数据。在运行的示例中,投标人的 URI 是结合位于 JSON 根级别的合同 id(“z 4 ade a9 de 4”)和位于 JSON 嵌套结构中的业务实体 id(“80004990927”)构建的。

下一步是什么

在下一篇文章中,我将向您展示一种基于语义模型的特殊方法,用于捕获具有基于图的结构的数据源的语义。

[## 构建知识图的语义模型

用基于图的结构捕获数据源的语义

towardsdatascience.com](/semantic-models-for-constructing-knowledge-graphs-38c0a1df316a)

如果你喜欢我的文章,你可以支持我使用这个链接成为中等会员。

在 R 中构建克里金模型

原文:https://towardsdatascience.com/building-kriging-models-in-r-b94d7c9750d8?source=collection_archive---------17-----------------------

利用空间统计算法进行空间预测

西蒙·米加吉在 Unsplash 上的照片

介绍

在当今世界,我们可以访问大量地理标记数据。我们能够以各种方式利用这些信息,创造新的信息,从而做出更好的决策,而不是将它们存放在数据库或文本文件中。

空间统计模型的优势之一是能够通过计算邻近点和感兴趣点之间的相关性来分析空间中的任何点。有许多空间模型可以使用,但我最喜欢的是克里金法。虽然地理加权回归(GWR)或反距离加权(IDW)等其他方法可能是有用的探索工具,但根据我的经验,克里金法往往会随着数据的增加而扩展得更好,并且在许多情况下,可以做出更准确的预测。

在本文中,我将展示如何使用 R 中的克里金法进行预测,并使用芝加哥市警察局的数据(在此处找到数据)提供提示&技巧。目标是对芝加哥市任一给定点的犯罪频率进行插值。这种空间统计技术允许我们利用现有的数据,构建一个模型来解释该区域的空间结构,并基于该空间结构对空间中任何其他纬度/经度点进行预测。

数据准备

在我们开始之前,我想讨论一下数据集,这样我们就能理解预测的是什么。数据可以在这里找到,我将 csv 文件下载到我的本地驱动器上。该文件有 22 个变量和超过 700 万行,但对于本教程,我们将只利用坐标点(纬度经度)、犯罪的主要类型和犯罪的描述

Nikola Johnny Mirkovic 在 Unsplash 拍摄的照片

【Tips&Tricks】重要的是克里格只有特定类别类型的值。这意味着我们一次只能为一种犯罪类型插入点,否则插入的点没有任何意义。因此,我们将重点关注的特定犯罪是对车辆的刑事损坏,我们将使用克里金法来插值芝加哥市此类犯罪的频率。

数据准备的 r 代码:

library(data.table)# fast read/write function
library(dplyr)df_1 <- reported_crimes %>% filter(PrimaryType=='CRIMINAL DAMAGE') %>% filter(Description=='TO VEHICLE')df_2 <- df_1 %>% 
  group_by(Latitude, Longitude) %>% 
  count() %>% 
  ungroup() %>% 
  inner_join(df_1, by=c('Latitude', 'Longitude')) %>% 
  distinct(Latitude, Longitude, .keep_all = TRUE) %>%
  select(Latitude, Longitude, n) %>%
  mutate(location_id = group_indices(., Latitude, Longitude))

一旦我们过滤了数据,在每个唯一的位置(纬度经度)计算犯罪的频率(变量名为 n ,并为每个唯一的位置添加一个索引( location_id) ,数据将类似于图 1。

图 1:前 10 行准备好的数据(图片由作者提供)

克里金法背后的思想是使用一组有限的数据点来预测给定区域中的其他邻近点。这种方法允许该领域的科学家只对少量数据点进行采样,并对其余数据点进行插值,从而节省了时间和金钱。因此,为了模拟这种方法,我们可以随机选取 10,000 个点作为训练集来构建模型(如图 2(a)所示)。

用于创建训练集的 R 代码:

random_generator <- sample(1:nrow(df_2), round(nrow(df_2)*.3), replace=F)train_df <- df_2 %>%
  filter(!location_id %in% random_generator) %>% 
  slice(1:10000)

然后,我们可以在感兴趣区域周围的点网格上使用该模型,如图 2(b)所示。点网格是通过构建一个多边形并在该多边形内创建超过 76,000 个纬度/经度点自动生成的(为简洁起见,本文不包含该代码)。

图 2:训练与网格(图片由作者提供)

构建变异函数

准备好数据后,第一步是构建变异函数并对其拟合曲线函数,该函数可用于对点格网的值进行插值。幸运的是,有了 R 中的 gstat 包,这可以使用变差函数轻松完成。

建立变差函数模型的 R 代码:

coordinates(train_df) <- c("Longitude", "Latitude")
proj4string(train_df) <- CRS("+proj=longlat +datum=WGS84")lzn.vgm <- variogram(log(n) ~ Latitude + Longitude, train_df, width=0.1)lzn.fit = fit.variogram(lzn.vgm, vgm(c("Gau", "Sph", "Mat", "Exp")), fit.kappa = TRUE)

【T21 技巧】log 函数用于确保所有插值保持正值。exp 函数将用于归一化本文“克里格”部分的结果。

可以绘制变差函数和曲线函数来了解我们区域的空间结构,如图 3 所示。直观的说,我们可以看到曲线函数(Matern)可以比较好的描述点之间的自相关性。我们可以看到,在 1.1 km 左右,点对不再具有空间相关性。

图 3:变异函数模型(图片由作者提供)

让我们克里格

现在,我们可以使用 gstat 包中的克里格函数使用这些信息来克里格点格网。如前所述,点的网格已经生成,为了简洁起见,本文不再赘述。但是,数据中未包含在训练集中的任何其他点都可以用于插值点和测试精度。

克里金法的 r 代码:

coordinates(grid) <- c("Longitude", "Latitude")
proj4string(grid) <- CRS("+proj=longlat +datum=WGS84")lzn.kriged <-krige(log(n) ~ Latitude + Longitude, train_df, grid, model = lzn.fit, maxdist=10, nmax=50)# Retrieve interpolated values
predicted <- lzn.kriged@data$var1.pred %>%
  as.data.frame() %>%
  rename(krige_pred = 1) %>% 
  mutate(krige= exp(krige))variance <- lzn.kriged@data$var1.var %>%
  as.data.frame() %>%
  mutate(variance = exp(variance))

图 4(a)显示了用于构建模型的点,并放大到芝加哥住宅区以便更好地查看数据点。一旦应用了代码并对网格点进行了克里格处理,我们就可以看到图 4(b)中所示的预测。显示的是模型计算出对车辆的犯罪损害的最高频率的热点,以及该犯罪发生频率可能较低的区域。克里金法填补了我们没有的数据空白。虽然在图 4(a)中我们可能有许多数据点可供选择,但并没有涵盖该区域的每个点。对于所有这些我们没有的点,克里金法开始发挥作用并填充空洞,从而增加区域的覆盖范围,如图 4(b)所示。

图 4:芝加哥住宅区的预测点(图片由作者提供)

结论

Esri 雄辩地陈述,

空间分析使您能够解决复杂的定位问题,并更好地了解您的世界中正在发生的位置和事件。它不仅仅是绘制地图,还能让你研究地方的特征以及它们之间的关系。空间分析为您的决策提供了新的视角。

在空间上理解我们的数据是进行预测的重要一步。这也是我喜欢克里金法的原因之一。当我们构建变差函数时(如图 3 所示),很清楚点之间的自相关何时停止,这告诉我们在什么距离上数据不再相关。然后利用变差函数对新数据进行插值。

虽然视觉效果总是令人兴奋,但在现实世界中实施之前,测试模型的准确性是至关重要的。虽然本文中没有讨论,但是我们使用训练/测试分割方法对数据(14%训练和 86%测试)进行了测试,以测量模型的准确性,RMSE 为 1.81,MAE 为 0.80。

有了空间统计,我们可以凭直觉判断数据的空间相关性,并通过数学方法精确计算相关性的终点。我希望这篇文章能帮助您集思广益,找出其他哪些数据集可能是空间相关的,以及克里金法如何帮助您更好地理解它!

使用线性代数构建线性回归(最小二乘法)

原文:https://towardsdatascience.com/building-linear-regression-least-squares-with-linear-algebra-2adf071dd5dd?source=collection_archive---------7-----------------------

使用 excel 或 numpy 解决线性回归问题的全线性代数方法。

迪米特里·卡拉斯泰列夫在 Unsplash 上的照片

有了 python 和 R 中大量复杂的包供我们使用,我们不太可能在每次必须拟合大量数据点时都要经历算法背后的数学运算。但是,有时学习数学并手动从头开始求解算法是很有用的,这样我们就能够直观地了解它是如何在后台完成的。在我为 ISB-CBA 的课程工作中,有一堂统计学课涉及使用线性代数在 excel 上用矩阵乘法求解多元线性回归的截距、系数和 R 平方值。在此之前,我一直使用 python 中的 statmodel OLS 或 R 上的 lm() 命令来获取截距和系数,看一眼 R 平方值就知道它有多好。

自从我的课程结束后,我早就忘记了如何使用 excel 解决这个问题,所以我想重温一下这些概念,并写下这篇文章,以便对其他人也有用。

我在我的 K aggle 笔记本上使用 numpy 完成了这篇文章。如果你觉得这篇文章有用,请查看我的笔记本并投赞成票!

使用矩阵乘法求解线性回归的数学基础

让我们从一个简单的线性回归开始。我们想通过一组数据点找到最佳拟合线:(x1,y1),(x2,y2),……(xn,yn)。但是最适合是什么意思呢?

如果我们能找到一条穿过所有可能数据点的直线的斜率和截距,那么这条直线就是最佳拟合直线。但是在大多数情况下,这样的线是不存在的!所以我们决心找到一条线,使得当从数据点到回归线平行于 y 轴画一条连接线时,测量每个数据点的误差,所有这些误差的总和应该是最小的。简单,嗯?

在图中,错误分别用红、蓝、绿、黄和紫线表示。为了将此公式化为矩阵求解问题,考虑下面给出的线性方程,其中β0 是截距,β是斜率。

为了简化这种表示法,我们将把β0 加到β向量上。这是通过在 X 矩阵中添加一个额外的 1 列,并在 Beta 向量中添加一个额外的变量来实现的。因此,矩阵形式将为:

那么最小二乘矩阵问题就是:

让我们考虑我们的初始方程:

两边乘以 X _ 转置矩阵:

其中:

那是一大堆方程式。但是当我们用下面一个简单的案例来解决它时,它就足够简单了。

一个玩具简单线性回归问题的求解

为简单起见,我们将从一个简单的线性回归问题开始,它有 4 个数据点(1,1),(2,3),(3,3)和(4,5)。X = [1,2,3,4],y = [1,3,3,5]。当我们转换成如上所述的矩阵形式时,我们得到:

下面是实现这个简单解决方案的 numpy 代码:

多元线性回归

多元线性回归的求解也非常类似于简单线性回归,我们遵循 6 个步骤:

  1. 为 X 矩阵中的截距添加一个从全 1 开始的新列
  2. 取 X 矩阵的转置
  3. 乘以 X 转置矩阵和 X 矩阵
  4. 求这个矩阵的逆矩阵
  5. 将 X 转置与 y 矩阵相乘
  6. 将两个矩阵相乘,找出截距和系数

为了解决多元线性回归问题,我从 kaggle 获取了一个数据集,其中包含英国二手车销售价格。

我在 excel 中手工计算了所有的计算。我已经从大众数据集中取出了前 300 行,并且只取出了其中的数字变量。回归得出的 r 平方得分为 0.77。

[## 大众 _ 回归

数据年份,里程,税,mpg,发动机尺寸,价格 2019,13904,145,49.6,225000 2019,4562,145,49.6,226883…

docs.google.com](https://docs.google.com/spreadsheets/d/10zoOfiQLryz0Y7T1Zbd1grvOSdDGEgEEyECZ5sVyKJ8/edit#gid=212170771)

我敦促你下载 excel 工作簿,并按照计算(谷歌表的新数学字体格式不好。可以下载在 MS excel 上查看,可读性更好)。在“解释”表中,我用矩阵乘以 X_Transpose 和 X。这包含了我们计算模型参数(如 R 平方值)所需的所有信息。

请参考 kaggle 笔记本的第三节这里:https://www . ka ggle . com/gireeshs/DIY-build-linear-regression-with-linear-algebra # Part-3:-Multiple-linear-regression在这里我用矩阵乘法解决了这个问题。

参考资料:

  1. https://www.youtube.com/watch?v=Lx6CfgKVIuE
  2. 完整的商业统计手册
  3. 我的课程工作为 ISB CBA

图像:

http://onlinestatbook.com/2/regression/intro.html

为什么 Alteryx 最适合构建线性回归模型?

原文:https://towardsdatascience.com/building-linear-regression-models-with-alteryx-6a43df39e254?source=collection_archive---------37-----------------------

多亏了 Alteryx,数据准备、争论和建模变得高效且无需代码!

改变一切— 改变

在商业分析领域,Alteryx 不仅是一个领导者,也是一个无代码的终结者。当涉及到数据准备、转换、争论或构建模型时,Alteryx 在每个领域都大放异彩。

谈到自动化和分析自动化,Alteryx 已经成为市场的领跑者。一两个月前我偶然遇到了 Alteryx,从那时起,Alteryx 就是我的首选数据准备和分析工具。Alteryx 最好的特性之一是它在构建预测分析模型时的效率。除此之外,它还提供了无代码建模。

用 Alteryx 建立线性回归模型

问题陈述:

一家公司正准备在未来几个月内寄出今年的目录。该公司的邮件列表中有 250 个新客户,他们希望将目录发送给他们。您的经理被要求确定向这些客户发送目录可以为公司带来多少利润。你被要求预测这 250 个新客户的预期利润。除非预期利润贡献超过 10,000 美元,否则管理层不想将目录发送给这些新客户。

  1. 输入数据

有两个数据集:

客户-现有客户数据集。

邮寄名单-新的 250 个客户,他们想发送目录。

您可以在下面找到数据集和项目链接。

在 Alteryx 中输入文件

2。数据争论

一旦数据集在空间中被提出,就可以使用“选择”工具并查看数据集的数据类型和列。大多数情况下,当我们需要更改数据集中列的数据类型时,这很方便。

使用选择工具查看所有列

3。线性回归

可以从 Alteryx 提供的“预测”工具中带入“线性回归”工具,输入“目标”和“预测”变量,做一个线性回归方程。

在左边,可以给出目标变量和预测变量。

4。浏览结果

工作流运行后,您可以通过点击“浏览”工具(即工作流最右侧的工具)来查看输出。

运行工作流后,您可以看到结果

通过检查结果和篡改预测变量,可以建立有效的线性回归模型。有效的线性回归模型的 p 值必须小于 0.05。在上面的例子中,“客户细分”和“Avg_Num_Products_Purchases”的 p 值远小于 0.005。所以线性方程是有效的。

5。得分

一旦建立了一个强线性方程,就引入了“得分”工具,并将其与用于预测平均销售额的数据集相连接。“评分”工具输出“是”和“否”两列,即客户在看到发送的目录后购买的可能性。

6。结果

看到分数后,使用线性回归模型的“平均销售额”必须乘以“Score_Yes”来计算新客户的“预期销售额”。

之后,使用利润率 50%计算利润,每个目录的成本为 6.50 美元,因此适用于所有 250 个客户。如果总利润大于 10,000 美元,目录将发送给新客户。

预测目录需求的最终 Alteryx 模型

使用 Alteryx 构建无代码模型变得更加容易,您可以尝试构建自己的线性回归模型。奥特莱斯的奇迹还有很多。但是现在,享受“线性回归”模型吧。

项目和数据集链接:https://github . com/essharmavi/Predictive-Analytics-for-Business-nano degree/tree/master/Predicting % 20 catalog % 20 demand

用 DEEL-LIP 构建 Lipschitz 约束网络

原文:https://towardsdatascience.com/building-lipschitz-networks-with-deel-lip-68452d7bc2bc?source=collection_archive---------24-----------------------

光谱标准化变得简单

什么是李普希茨函数?

以 Rudolf Lipschitz 命名的函数称为 k-Lipschitz,当它的一阶导数以某个常数 k 为界时,这个常数 k 的最小值称为函数的 Lipschitz 常数。这种函数有一些有趣的特性,其中包括:

这意味着,如果 x1 和 x2 彼此接近,那么,它们的预测 f(x1)和 f(x2)也将接近。由于神经网络近似函数,我们将集中于 1-Lipschitz 网络。

为什么网络的李普希兹常数很重要

这些类型的网络在现代机器学习中有许多用途:它们已经被证明对敌对攻击具有鲁棒性[1]。

来源:《解释和利用对立的例子》,古德菲勒等人,ICLR,2015 年。

对抗性攻击是找到一个 x2 (“长臂猿”图像)接近我们最初的 x1 (“熊猫”图像)的行为,这样他们的预测 f(x1)f(x2) 就不同了。主要问题是,构建这种对立例子所增加的噪声非常小。控制网络的 Lipschitz 常数对对抗的鲁棒性有很大的影响:Lipschitz 常数越低,为了建立对抗的例子就需要越多的噪声。请随意查看这篇关于对抗性鲁棒性的温和介绍来了解更多关于这个主题的信息。

但这并不是 Lipschitz 网络的唯一用途:它们也用于 Wasserstein 距离估计。Wasserstein 度量允许测量两个分布之间的距离。计算这个度量需要优化 1-Lipschitz 网络。这在 Wasserstein-GAN [2]中被特别使用。

不同级别的 Lipschitz 约束

前面的等式告诉我们,我们函数的梯度最多等于 1。但是强制实施这个约束并不一定意味着梯度将在某个点有效地达到 1。

根据用例,我们可能需要不同级别的 Lipschitz 约束:

  • “软”1-Lipschitz 约束:我们强制梯度函数接近 1 并且平均等于 1。在这种情况下,在输入域的某些特定点处,梯度可以大于 1。在训练期间使用这样的约束并不保证最终的网络将是 1-Lipschitz,但是允许正则化训练。
  • “硬”1-Lipschitz 约束:在输入域的每个点上强制梯度小于或等于 1。在某些点上,梯度可以小于 1。这在处理对抗性鲁棒性时使用。
  • “梯度几乎处处等于 1”:强制梯度几乎处处等于 1。这种特殊类型的网络不适合传统的 ML。然而,当使用 Wasserstein 距离估计时,这是必要的[3]。

我们将关注最后两个层次的约束。

我们如何在神经网络上应用这些约束

众所周知,计算神经网络的 Lipschitz 常数是一个 NP 难问题。然而,已经提出了几种方法来在训练期间实施这种“硬”约束。这些方法中的大多数依赖于约束每层的 Lipschitz 常数,然后可以使用下面的组成属性:

对于线状图层(如密集图层),有多种方法可以实现该属性,如权重裁剪或归一化。

我们现在将探索现有的方法,以此为密集层。回想一下稠密层的定义,它是由非线性激活函数组成的线性算子:

我们将首先关注线性核 k。我们稍后将看到如何包括激活函数,以及如何将这些方法扩展到其他类型的层。

重量剪裁

这是在每个线性图层级别强制执行 Lipschitz 约束的简单方法。例如,具有 m 个输入和 n 个输出的密集层是 1-Lipschitz,如果所有权重在以下间隔内被限幅:

这足以保证该层的 Lipschitz 常数低于 1。然而,在实践中,训练数据点的梯度通常远低于 1。这是因为在某一点上使渐变等于 1 的唯一方法是将所有权重设置为它们的剪裁值,这是非常严格的。换句话说,这意味着在这种约束下训练的网络具有小于 1 的 Lipschitz 常数,但是许多 1-Lipschitz 网络不满足这种约束。

那么我们能找到一个没有这些缺点的约束吗?

光谱归一化

该方法通过使用频谱归一化解决了权重削波的限制。数学上,这种类型的归一化依赖于奇异值分解:

由于中心矩阵是对角的,并且由于 UV 不影响 K 的梯度,因此我们可以说 K 的梯度由下式限定:

如果我们将所有权重除以最大 sigma 值,则梯度以 1 为界:

这种标准化提供了非常强的保证:

  • 梯度不能大于 1。
  • 输入域中存在一组点,使得梯度等于 1。

此外,使用幂迭代法【4】可以快速计算最高奇异值。

它还提供了一种表达“梯度几乎处处等于 1”约束的正式方式:

在实践中,也可以通过使用bj rck 正交归一化算法[5]来获得该约束

激活功能呢?

为了获得 1-Lipschitz 神经网络,所使用的激活函数也必须是 1-Lipschitz。这些大部分已经是 1-Lipschitz: ReLU ELU,sigmoid,tanh,logSigmoid…有些需要适当参数化,比如 leakyRelu,PReLU…最后有些根本不是 1-Lipschitz。

其他层呢?

在层上应用合成属性要求每个层都遵守 1-Lipschitz 约束。我们展示了密集图层的标准化示例,但是这适用于任何图层类型吗?

卷积层:有人可能认为归一化卷积层的内核(通过裁剪或光谱归一化)就足够了,但是有一个问题:卷积层使用填充、步长、膨胀...所有这些操作都会影响输出的范数,从而改变层的 Lipschitz 常数。为了捕捉这种现象,可以通过计算这些参数来计算校正系数[6]。

汇集层:这些层可以被视为卷积的特例,因此我们也可以应用一个校正因子。

批量规范:当它执行重缩放时,该层不受约束。此外,1-lipschitz 约束下的学习减少了对这些层的需求。使用它只对校正每层后引起的偏差有用,这也可以通过设置每层的 use_bias 参数来完成。

Dropout: 允许一种正则化,切换到层输出的零部分。然而,在推断中,应用了一个比例因子来补偿丢失因子,这破坏了 Lipschitz 属性。

用 DEEL-LIP 简化 k-Lipschitz 神经网络

DEEL-LIP 是一个基于 Tensorflow 构建的库,它扩展了常见的 Keras 元素,如层、初始化器或激活,允许用户轻松构建 1-Lipschitz 网络。提供的层使用光谱归一化和/或 Bjö rck 正交归一化。需要时,计算并应用适当的校正系数。

怎么用?

首先,可以用 pip 安装库

下面的代码演示了如何用 DEEL-LIP 构建和编译一个神经网络。它非常类似于标准的 Keras 代码。

使用 DEEL-LIP 和 Keras 构建 NN 的示例

DEEL-LIP 的开发旨在方便 Keras 用户使用:

  • DEEL-LIP 遵循与 Tensorflow/Keras 相同的封装结构。
  • 所有元素(层、激活、初始化器……)都与标准的 Keras 元素兼容。
  • 当一个层覆盖一个标准的 Keras 元素时,它使用相同的参数实现相同的接口。唯一的区别在于控制层的 Lipschitz 常数的参数。

处理 Lipschitz 图层时有什么重要的?

由于网络的 Lipschitz 常数是每一层常数的乘积,所以每一层都必须遵守 Lipschitz 约束。增加一个不满足 Lipschitz 约束的单层,就足以打破整个网络的 Lipschitz 约束。

为了方便起见,从 DEEL-LIP 导入的任何图层都可以安全使用:如果可以导入,就可以使用。对于层的参数也是如此:层上的错误设置(即,打破 Lipschitz 约束)将引起错误。

可用层的完整列表可在文档中找到。

反向传播/推断期间的开销是多少?

在反向传播期间,权重的归一化增加了每次迭代的开销。然而,在反向传播期间,谱归一化算法被直接添加到图中。这被称为可微分约束,它产生更有效的优化步骤。

在推理期间,完全没有增加开销:只有在反向传播期间,规范化才是强制的。DEEL-LIP 提供了一个导出功能,可用于从深唇模型创建标准的 Keras 模型。然后,每个密集层或 Conv2D 层被转换为标准 Keras 层。通过导出模型,可以使用使用 DEEL-LIP 训练的网络进行推理,甚至不需要安装 DEEL-LIP。

结论

Lipschitz 约束网络是具有有界导数的神经网络。它们有许多应用,从对抗鲁棒性到 Wasserstein 距离估计。有各种方式来实施这样的约束。光谱归一化和 Bjö rck 正交归一化是最有效的方法,DEEL-LIP 库提供了一种训练和使用这些约束图层的简单方法。

感谢阅读!

参考

[1]穆斯塔法·西塞、皮奥特尔·博扬诺夫斯基、爱德华·格雷夫、扬恩·多芬、尼古拉·乌苏尼尔。帕塞瓦尔网络:提高对抗例子的鲁棒性。 arXiv:1704.08847 【统计。
[2]马丁·阿约夫斯基,苏史密斯·钦塔拉,莱昂·博图。瓦瑟斯坦·甘。 arXiv:1701.07875 【统计。ML]
【3】塞德里克·维拉尼。最佳运输:旧与新。数学科学小组。施普林格柏林海德堡,2008。
【4】维基百科:幂迭代法
【5】杰姆·阿尼尔,詹姆斯·卢卡斯,罗杰·格罗斯。整理李普希兹函数逼近。arXiv:1811.05381T17【cs。[LG]
[6]马蒂厄·塞鲁里埃、弗兰克·马马莱特、阿尔贝托·冈萨雷斯-桑斯、蒂博·布瓦辛、让-米歇尔·卢贝斯、尤斯塔西奥·德尔巴里奥。利用铰链正则化的最佳传输实现分类的稳健性。arXiv:2006.06520T22【cs。LG]

构建机器学习管道

原文:https://towardsdatascience.com/building-machine-learning-pipelines-7bbd613054cc?source=collection_archive---------19-----------------------

管道的 A-Z

ML 管道自动化工作流程。但是,这意味着什么呢?关键是,它们帮助开发从一个估算器/转换器到下一个的顺序数据流,直到它到达最终的预测算法。

它确保训练集、测试集和验证集之间没有数据泄漏。流水线还使程序更自动化,可以用作功能代码。

我们将根据步骤的复杂程度介绍 3 种不同类型的管道:

  1. 简单管道
  2. 带自定义功能的流水线 -顺序应用
  3. 带定制选择器和功能的流水线 -并行应用

作者图片:ML 管道可重用性

让我们根据应用程序的复杂性来看看不同类型的管道:

简单管道

我们将使用网格搜索来调整超参数并生成输出。对于参数网格值,写入“管道步骤”的名称,后跟 2 '_ ',然后是要优化的超参数。就这么简单!

作者图片:简单管道

为管道转换设置自定义函数

如果我们希望在某些列上进行一些自定义转换,而有一个您想调用的函数来代替呢?

作者图片:管道中的自定义函数

column transformer是 scikit-learn 中的一个类,它允许有选择地应用数据准备转换。它将接受要执行转换的列名,以及转换器本身。Param " remainder "指定如何处理未提及的其余列。ColumnTransformer 按顺序应用每个转换

在管道中设置自定义选择器和转换器

让我们更进一步说,我们甚至不想提前选择列。相反,我们需要管道来为我们做选择。现在,我们引入 feature union——它连接了并行发生的多个转换的结果!

作者图片:复杂管道

由于 pipeline 希望类作为具有 fit、transform 方法的选择器,我们将扩展基类 BaseEstimator &TransformerMixin。

我们本可以在所有这些应用网格搜索、随机搜索和交叉验证!你现在是管道专家了…祝贺你!

完整的代码可以在这里找到。

建立原发性胆汁性肝硬化患者的医学预后模型

原文:https://towardsdatascience.com/building-medical-prognostic-models-for-patients-with-primary-biliary-cirrhosis-71bd4ceb7db1?source=collection_archive---------60-----------------------

如何将机器学习技术应用于医学数据集并建立评估它们的指标。

国家癌症研究所Unsplash 上拍摄的照片

摘要

医学预测是人工智能直接应用的新兴领域。这篇文章讨论了人工智能在医学预测方面的想法,并提供了构建预测模型的详细教程。预后是一个医学术语,用于预测疾病可能或预期的发展,包括体征和症状是否会随着时间的推移而改善或恶化或保持稳定。

在这篇文章中,我将解释我为原发性胆汁性肝硬化(PBC)患者建立风险模型的工作。我们将使用在这里维护的 PBC 数据集。

预测模型如何工作?

预后模型基于一个基本原理,即在给定患者档案的情况下,患者在给定时间后的生存概率。预测模型可以大致分为两类:

  1. 建立在患者群体水平上
  2. 在单个患者层面构建

在这里,我们将在单个患者的水平上工作,并将使用两种不同的方法,然后对它们进行比较。下面提到了这两种方法:

  1. 使用 Cox 比例风险模型的线性比例方法。
  2. 使用随机存活森林模型的基于树的方法。

马库斯·斯皮斯克Unsplash 上拍摄

建立 Cox 比例风险模型

导入必要的库和包

import sklearn
import os
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from lifelines import CoxPHFitter
from lifelines.utils import concordance_index as cindex
from sklearn.model_selection import train_test_split

我们使用的是生命线库,这是一个开源库,用于导入生存模型中使用的模型和度量。

加载数据集并执行数据预处理

df = pd.read_csv('pbc.csv')
df.head()
df = df.drop('id', axis=1)
df = df[df.status != 1]
df.loc[:, 'status'] = df.status / 2.0
df.loc[:, 'time'] = df.time / 365.0
df.loc[:, 'trt'] = df.trt - 1
df.loc[:, 'sex'] = df.sex.map({'f':0.0, 'm':1.0})
df = df.dropna(axis=0)

让我们看看我们的数据

时间以天为单位,因此我们将其转换为年,并删除“id”列,因为它不是必需的。我们转换和缩放“状态”和“trt”列以及编码“性别”列的标签。

np.random.seed(0)
df_dev, df_test = train_test_split(df, test_size = 0.2)
df_train, df_val = train_test_split(df_dev, test_size = 0.25)

在继续建模之前,让我们归一化连续协变量,以确保它们在相同的尺度上。为了防止数据泄漏,我们应该使用从训练数据计算的统计数据来标准化测试数据。

continuous_columns = ['age', 'bili', 'chol', 'albumin', 'copper', 'alk.phos', 'ast', 'trig', 'platelet', 'protime']
mean = df_train.loc[:, continuous_columns].mean()
std = df_train.loc[:, continuous_columns].std()
df_train.loc[:, continuous_columns] = (df_train.loc[:, continuous_columns] - mean) / std
df_val.loc[:, continuous_columns] = (df_val.loc[:, continuous_columns] - mean) / std
df_test.loc[:, continuous_columns] = (df_test.loc[:, continuous_columns] - mean) / std

Cox 比例风险模型基于以下数学方程:

𝜆(𝑡,𝑥)=𝜆₀(𝑡)exp(𝛉ᵀXᵢ)

𝜆₀项是一个基线风险,包含了随时间变化的风险,等式中的第二项包含了因个人协变量/特征引起的风险。拟合模型后,我们可以使用个人相关风险项(即 exp(𝛉ᵀXᵢ)对个人进行排名

让我们将分类列转换为编码,以便在线性模型中使用它们。我们有两列,“水肿”和“分期”,它们在本质上是分类的,因此我们对它们进行转换。

columns=['edema','stage']
one_hot_train=pd.get_dummies(df_train, columns=columns, drop_first=True)
one_hot_test=pd.get_dummies(df_test, columns=columns, drop_first=True)
one_hot_val=pd.get_dummies(df_val, columns=columns, drop_first=True)

现在,经过这些转换和预处理步骤后,我们的数据看起来像这样:

训练我们的 Cox 比例风险模型

现在,我们已经准备好让我们的数据符合 Cox 比例风险模型,我们将从从生命线库构造一个 CoxPHFitter 对象开始。

cph = CoxPHFitter()
cph.fit(one_hot_train, duration_col = 'time', event_col = 'status', step_size=0.1)cph.print_summary()

经过训练的 CoxPH 模型看起来像:

让我们开始分析上表,该表描述了我们的训练模型,提供了根据训练数据优化的所有系数。

在这里,我们可以看到具有负系数的特征是有益的,即它们有助于降低风险系数/输出值,而具有正系数的特征有助于增加风险系数/输出值。从上表我们可以推断出【TRT】【腹水】【蜘蛛】****【白蛋白】【alk . phos】具有负系数,因此,这些特征的高值将成比例地降低危险系数。

我们可以比较治疗变量的预测存活曲线

  • y 轴是存活率
  • x 轴是时间

治疗变量的预测生存曲线

我们可以清楚地看到,“TRT”= 0 的生存曲线比“TRT”= 1 的生存曲线低。这是因为“trt”变量或治疗特征是一个负协变量,即它在拟合模型中具有负系数,因此变量的较高值将产生较低的风险评分。

接下来,我们比较“水肿 1.0”变量的预测存活曲线。

水肿的预测生存曲线 _1.0 变量

这里,可以清楚地注意到两条曲线被一个大的值分开,并且“水肿 _ 1.0”= 1 的曲线比“水肿 _ 1.0”= 0 的曲线低。这是因为“水肿 _1”变量是一个正协变量,即它在拟合模型中有一个正系数,因此该变量的值越高,风险得分越高。两条曲线之间的较大差距是由于对应于“水肿 _1.0”的系数值相对较高。

危险比率

两个病人之间的风险比是一个病人比另一个病人更危险的可能性。它可以用数学公式表示为:

𝜆₁(𝑡)/𝜆₂(𝑡)=𝑒xp(𝜃(𝑋₁−𝑋₂)ᵀ

哪里,𝜆₁(𝑡)=𝜆₀(𝑡)𝑒xp(𝜃𝑋₁ᵀ)

还有,𝜆₂(𝑡)=𝜆₀(𝑡)𝑒xp(𝜃𝑋₂ᵀ)

现在,我们将使用数据集中的任意 2 名患者来计算风险比。

i = 1
patient_1 = one_hot_train.iloc[i, :].drop(['time', 'status']).values
j = 5
patient_5 = one_hot_train.iloc[j, :].drop(['time', 'status']).values
cox_params=cph.params_.values
hr = np.exp(np.dot(cox_params,(patient_1-patient_5)))
print(hr)

运行上面这段代码后,我们得到病人 1 和病人 5 的风险比**15.029017732492221** 。这意味着与患者 5 相比,患者 1 的风险更高,因为两者之间的风险比大于 1。类似地,我们可以计算任意两个患者之间的风险比,并比较他们的风险评分。

衡量我们模型的性能

衡量一个经过训练的模型的性能是机器学习管道中不可或缺的一部分,这里我们将使用 Harrell 的 C 指数作为我们模型性能的衡量标准。生存环境中的 c 指数或和谐指数是这样的概率,给定一对随机选择的个体,较早死亡的个体具有较高的风险评分。在接下来的几行代码中,我们将编写自己的 C-index 版本。但是在进入代码之前,让我介绍一个重要的概念,这是理解预测模型所必需的,即审查。审查是指在研究过程中没有发生死亡事件,这可能是由以下两个原因造成的:

  1. 患者在目睹事件/死亡之前选择退出研究
  2. 研究在患者目睹事件/死亡之前结束

因此,如果患者的个人资料被审查,则在数据集的“事件”列中以值“0”表示。如果它未经审查,则用值“1”表示。

哈勒尔 C 指数的数学公式为:

哈勒尔 C 指数=

在哪里,

PermissiblePairs =满足以下任一条件的患者对总数:对于(patient_i,patient_j)对,

  1. 两个病人都有未经审查的数据
  2. 如果任何一个病人的数据被审查,时间[审查]>时间[未审查]

ConcordantPairs =满足以下两个条件的患者对总数:对于(patient_i,patient_j)对,

  1. 允许患者配对
  2. 如果 risk score[病人 _ I]> risk score[病人 _j],那么,

time[patient_i]

risks=满足以下条件的患者对总数:对于(patient _ I,patient_j)对,

  1. 允许患者配对
  2. risk score[病人 I]= risk score[病人 j]
event=one_hot_train['status'].values
y_true=one_hot_train['time'].values
scores=scores.valuesn = len(y_true)
assert (len(scores) == n and len(event) == n)
concordant = 0.0
permissible = 0.0
ties = 0.0
result = 0.0

for i in range(n):
    for j in range(i+1, n):
       if event[i]==1 or event[j]==1:
                pass
          if event[i]==1 and event[j]==1:
              permissible+=1

              if scores[i]==scores[j]:
                   ties+=1

              elif (y_true[i]>y_true[j] and scores[i]<scores[j]):
                        concordant+=1
              elif (y_true[i]<y_true[j] and scores[i]>scores[j]):
                        concordant+=1 elif (event[i]!=event[j]):

                    if event[j] == 0:
                        censored = j
                        uncensored = i

                    if event[i] == 0:
                        censored = i
                        uncensored = j

                    if y_true[censored]>=y_true[uncensored]:
                        permissible+=1

                        if scores[i]==scores[j]:
                            ties+=1

                        if scores[censored]<scores[uncensored]:
                            concordant+=1

c-index = (concordant+ties*0.5)/permissibleprint(c-index)

在上面这段代码中,我们在计算了可允许的、一致的和风险关系对的数量之后,计算了训练数据的 Harrell C 指数。我们训练数据对应的 Harrell 的 C 指数是0.82639116202946

类似地,我们可以计算 Harrell 的 C 指数来验证和测试数据。

**Val: 0.8544776119402985
Test: 0.8478543563068921**

我们得到了一个可观的 C 指数,它告诉我们,我们的模型表现得相当好,但我们可以通过使用一种基于树的方法来提高我们的预测模型的性能,我将在下一节中讨论这种方法。

建立随机生存森林模型

随机生存森林模型是一种基于树的机器学习方法,它构建了许多估计器/树,并在投票的基础上做出决策。这种基于树的方法优于线性模型,如我们已经讨论过的 Cox 比例风险模型。Cox 比例风险模型假设线性风险函数,这使得不可能表示非线性风险函数,而非线性风险函数在所讨论的数据集中非常普遍。因此,让我们从头开始构建我们的模型。

在人群中,不同的患者可能有不同的风险函数。因此,为了优化我们的预测,随机生存森林模型首先通过遍历树来预测该患者的风险函数,然后使用该风险函数计算风险得分。

导入必要的库

我们将使用 R 中预先安装的库来构建我们的随机生存森林模型。您可以在这里找到这个库,并将其下载到您的本地机器上,以便在您的项目中使用。为了从 Python 中调用 R 函数,我们将使用“r2py”包。在构建实际模型之前,让我们开始一步一步地导入它们。

%load_ext rpy2.ipython
%R require(ggplot2)from rpy2.robjects.packages import importr
base = importr('base')
utils = importr('utils')
import rpy2.robjects.packages as rpackages forest = rpackages.importr('randomForestSRC', lib_loc='R')from rpy2 import robjects as ro
R = ro.rfrom rpy2.robjects import pandas2ri
pandas2ri.activate()

在基于树的模型中,不需要对变量进行编码,因为树可以很好地处理原始分类数据。所以我们马上开始训练我们的模型。

培训和评估模型

model = forest.rfsrc(ro.Formula('Surv(time, status) ~ .'), data=df_train, ntree=300, nodedepth=5, seed=-1)

在拟合我们的数据之后,让我们看看我们的训练模型及其参数。

print(model)

模型描述

现在让我们通过计算 Harrell 的 C 指数来评估我们的随机生存森林模型的性能。本文前面提到的代码也可以用来根据 RSF 模型预测的分数计算哈勒尔的 C 指数,这些分数是用下面提到的代码计算的。

result = R.predict(model, newdata=df_train)
scores = np.array(result.rx('predicted')[0])

对应于训练数据的 Harrell 的 C 指数为 0.8769230769230769 ,这是一个很好的分数,我们的预测模型的性能得到了改善。让我们看看它是如何处理验证和测试数据的。

result = R.predict(model, newdata=df_val)
scores_val = np.array(result.rx('predicted')[0])
result = R.predict(model, newdata=df_test)
scores_test = np.array(result.rx('predicted')[0])

为了计算测试和验证数据的 C 指数,使用“scores_val”和 scores_test”来计算 Harrell 的 C 指数。运行上述步骤后,我们得到以下分数/c 指数:

**Survival Forest Validation Score: 0.8769230769230769
Survival Forest Testing Score: 0.8621586475942783**

因此,我们的模型在验证和测试数据上都表现很好,并且优于 Cox 比例风险模型。

深入挖掘

让我们更深入地研究一下我们刚刚训练和评估的 RSF 模型,以便我们可以了解我们的模型在其风险得分预测中优先考虑的功能。为此,我们将使用随机生存模型自带的内置函数,称为“可变重要性”或 VIMP。

VIMPS = np.array(forest.vimp(model).rx('importance')[0])y = np.arange(len(VIMPS))
plt.barh(y, np.abs(VIMPS))
plt.yticks(y, df_train.drop(['time', 'status'], axis=1).columns)
plt.title("Variable Importance (absolute value)")
plt.show()

VIMP 条形图

看到上面的图表,我们可以推断出“毕丽”变量在预测风险评分中最重要,其次是“水肿”和“保护时间”。如果我们将上表中的值与 Cox 模型中的系数进行比较,我们会注意到“水肿”在两个模型中都是一个重要的特征,但是“毕丽”似乎只在随机存活森林中重要。

结论

在这篇文章中,我们目睹了医疗预后模型的概述,然后详细说明了建立风险模型的两种机器学习方法。最后,我们使用可视化插图和图形评估了这两个模型,并使用 Harrell 的 C 指数比较了它们的性能。

在这篇文章的结尾,我将分享我在这个研究领域的想法,首先引用一位知名人士吴恩达的名言

很难想出一个 AI 不会改造的主要行业。这包括医疗保健、教育、交通、零售、通信和农业。人工智能在所有这些行业都有令人惊讶的清晰路径。

人工智能在医学和医疗保健领域的范围是巨大的。在预后模型的帮助下,卫生从业者可以通过分析随时间计算的风险得分来规划患者的未来治疗,从而大大改善患者的健康。该领域的研究正在以非常快的速度发展,如果我们计划在现实生活中实施这些方法,我们将很快见证领先的医院和医疗诊所有效地使用这些模型。

使用 Keras 构建模型

原文:https://towardsdatascience.com/building-models-with-keras-bbe6dcfdad31?source=collection_archive---------8-----------------------

神经网络 API 简介

照片由拍摄,另一个摄影爱好者像素

Keras 是用于在 python 中构建神经网络的高级 API。API 支持顺序神经网络、递归神经网络和卷积神经网络。由于其模块化、用户友好性和可扩展性,它还允许简单快速的原型制作。在本帖中,我们将通过使用 Keras 为回归和分类任务构建序列神经网络的过程。Keras 的文档可以在这里找到。

我们开始吧!

回归

数据准备

我们将用于回归的数据是加州房价数据集。数据可以在这里找到。

首先,让我们导入数据并打印前五行:

import pandas as pd 
df = pd.read_csv("housing.csv")
print(df.head())

现在,让我们定义我们的输入和目标变量。我们将使用经度、纬度、住房中位数年龄、总房间数、总卧室数、人口数、家庭数和收入中位数来预测房屋中位数价值。

import numpy as np
X = np.array(df[['longitude', 'latitude', 'housing_median_age', 'total_rooms', 'total_bedrooms', 'population', 'households', 'median_income']])
y = np.array(df['median_house_value'])

然后,我们将分割数据用于训练和测试:

from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size = 0.2, random_state = 42)

现在我们所有必要的变量都定义好了。让我们建立一些模型!

定义模型

顺序模型是层的线性堆叠。

**from** keras.models **import** Sequential
model = Sequential()

如果出现以下错误:

尝试在此次导入和所有后续导入中从 tensorflow 导入 Keras:

**from** tensorflow.keras.models **import** Sequential
model = Sequential()

添加图层

我们可以使用。add()方法来添加层。我们将添加需要单独导入的密集层:

**from** keras.layers **import** Dense

模型应该知道第一层中预期的输入形状。因此,您需要传递关于模型中特征数量的信息。因为我们有 8 个特征,我们需要传入一个输入形状(8,)。我们将添加一个包含 8 个节点的密集层:

model.add(Dense(8, input_shape = (8,)))

让我们添加一个额外的隐藏层。对于我们的隐藏层,我们将使用 relu 函数:

model.add(Dense(8, activation = 'relu'))

最后,让我们添加我们的输出层。对于回归问题,我们通常将输出层中的激活函数定义为线性。此外,输出图层有一个用于回归问题的节点:

model.add(Dense(1, activation = 'linear'))

编译

我们要做的下一件事是配置学习过程。这是使用 compile 方法完成的。在编译方法中,我们必须传递以下参数:

  1. 损失函数(Loss Function ):这是一个函数,用于评估您的算法对数据集的建模效果。
  2. 优化器:这是一种找到最小化你的损失函数的权重的方法。
  3. 度量:对于回归,我们通常将度量定义为损失函数。这允许我们在模型被训练时跟踪损失。

我们将对优化器使用均方根传播算子,对损失函数使用均方误差,对指标使用均方误差:

model.compile(optimizer='rmsprop', loss='mse', metrics =['mse'])

我们可以查看模型摘要来分析我们的神经网络架构:

print(model.summary())

拟合

对于模型训练,我们将使用。fit()方法:

model.fit(X_train, y_train)

我们应该得到以下输出:

我们可以传递一个历元数的值(数据的迭代次数)来尝试提高准确性:

model.fit(X_train, y_train, epochs = 10)

您可以调整纪元的数量,以尽量减少误差:

model.fit(X_train, y_train, epochs = 50)

您可以对隐藏层的数量进行同样的操作。让我们尝试添加两个额外的隐藏层:

model.add(Dense(8, input_shape = (8,)))
model.add(Dense(8, activation = 'relu'))
model.add(Dense(8, activation = 'relu'))
model.add(Dense(8, activation = 'relu'))
model.add(Dense(1, activation = 'linear'))
model.compile(optimizer='rmsprop', loss='mse', metrics =['mse'])
model.fit(X_train, y_train, epochs = 50)

我们看到最后五个时期具有较低的均方误差。我们还可以尝试添加更多节点。让我们试试 16 个节点,而不是 8 个:

model.add(Dense(16, input_shape = (8,)))
model.add(Dense(16, activation = 'relu'))
model.add(Dense(16, activation = 'relu'))
model.add(Dense(16, activation = 'relu'))
model.add(Dense(1, activation = 'linear'))
model.compile(optimizer='rmsprop', loss='mse', metrics =['mse'])
model.fit(X_train, y_train, epochs = 50)

最后,让我们尝试使用不同的优化器。让我们试试“亚当”优化器:

model.add(Dense(16, input_shape = (8,)))
model.add(Dense(16, activation = 'relu'))
model.add(Dense(16, activation = 'relu'))
model.add(Dense(16, activation = 'relu'))
model.add(Dense(1, activation = 'linear'))
model.compile(optimizer='adam', loss='mse', metrics =['mse'])
model.fit(X_train, y_train, epochs = 50)

最终模型的性能大致相同。最后,让我们尝试大量的 epochs,比如 500 个,并传递一个更大的 batch_size。让我们传递 100 的 batch_size:

model.add(Dense(16, input_shape = (8,)))
model.add(Dense(16, activation = 'relu'))
model.add(Dense(16, activation = 'relu'))
model.add(Dense(16, activation = 'relu'))
model.add(Dense(1, activation = 'linear'))
model.compile(optimizer='adam', loss='mse', metrics =['mse'])
model.fit(X_train, y_train, epochs = 500, batch_size=100)

结果我们看到了一些改进。虽然我们在最小化均方误差,但我们可以显示不同的误差指标,如平均绝对百分比误差:

model.add(Dense(16, input_shape = (8,)))
model.add(Dense(16, activation = 'relu'))
model.add(Dense(16, activation = 'relu'))
model.add(Dense(16, activation = 'relu'))
model.add(Dense(1, activation = 'linear'))
model.compile(optimizer='adam', loss='mse', metrics =['mape'])
model.fit(X_train, y_train, epochs = 500, batch_size=100)

我们看到最后一个时期的平均绝对百分比误差为 28%。

预测

为了生成预测,我们执行以下操作:

y_pred = model.predict(X_test)

我们可以使用 matplotlib 可视化我们的预测:

import matplotlib.pyplot as pltplt.clf()
fig = plt.figure()
fig.suptitle('Scatter plot of Actual versus Predicted')
plt.scatter(x=y_pred, y=y_test, marker='.')
plt.xlabel('Predicted')
plt.ylabel('Actual ')
plt.show()

预测值和实际值之间的关系越像直线,我们的模型就越准确。就优化我们的模型而言,我们还可以尝试更多的东西,但我会把这些留给你去做。

分类

现在让我们来完成构建分类模型的相同过程。工作流程中有许多相似之处,但有一些小差异!

数据准备

我们将用于分类的数据是电信客户流失数据集。这里可以找到

首先,让我们导入数据并打印前五行:

import pandas as pd 
df = pd.read_csv("Customer_Churn.csv")
print(df.head())

为简单起见,我们将使用所有的分类和数字数据来预测客户流失。首先,我们需要将分类列转换为神经网络可以处理的数值。例如,对于性别,我们有:

df.gender = pd.Categorical(df.gender)
df['gender_code'] = df.gender.cat.codes

现在让我们定义输入和输出数组:

import numpy as npfeatures = ['gender_code', 'SeniorCitizen_code', 'PhoneService_code', 'MultipleLines_code', 
                 'InternetService_code', 'Partner_code', 'Dependents_code', 'PaymentMethod_code', 
                 'PaymentMethod_code', 'PaperlessBilling_code','Contract_code', 'StreamingMovies_code',
                 'StreamingTV_code', 'TechSupport_code', 'DeviceProtection_code', 'OnlineBackup_code',
                 'OnlineSecurity_code', 'Dependents_code', 'Partner_code','tenure', 'MonthlyCharges']X = np.array(df[features])
y = np.array(df['Churn_code'])

让我们也标准化输入:

from sklearn.preprocessing import StandardScaler
sc = StandardScaler()
X = sc.fit_transform(X)

然后,我们将分割数据用于训练和测试:

from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size = 0.2, random_state = 42)

现在我们所有必要的变量都定义好了。让我们建立一些模型!

定义模型&添加图层

让我们从一个 8 节点输入图层开始,其输入形状对应于要素的数量:

model = Sequential()
model.add(Dense(8, input_shape = (len(features),)))

让我们添加一个隐藏层:

model.add(Dense(8, activation='relu'))

接下来,让我们添加我们的输出层。对于二元分类,我们使用 1 个节点作为输出和 sigmoid 激活函数:

model.add(Dense(1, activation='sigmoid')) 

编译

现在让我们编译我们的模型。我们将使用“Adam”传播算子、损失的二进制交叉熵和度量的“准确性”。Keras 文档建议我们将指标设置为值“准确性”:

model.compile(optimizer='adam',            loss='binary_crossentropy', metrics=['accuracy'])

让我们打印我们模型的摘要:

print(model.summary())

试衣

对于模型训练,我们将使用。fit()方法:

model.fit(X_train, y_train)

我们应该得到以下输出:

与回归问题类似,您可以随意调整节点数、层数、时期数、batch_size 和优化器类型。

预测

在预测步骤中,我们希望将输出(数组)转换为浮点数,然后使用列表理解对值进行舍入:

y_pred = [round(float(x)) for x in model.predict(X_test)]

我们可以使用度量分类报告来可视化预测:

from sklearn import metrics
print(metrics.classification_report(y_test, y_pred))

我们还可以查看 roc_auc_score 和 f1_scores:

在这两种情况下,等于 1.0 的值都是完美的。通过调整模型参数,可以显著提高性能。我鼓励你尝试增加神经元(节点)、时期、层的数量,并设计额外的功能。

结论

在这篇文章中,我们介绍了使用 Keras 神经网络 API 构建回归和分类模型的过程。我们回顾了定义模型对象、添加层、用编译方法配置模型、训练模型、进行预测和评估模型性能的过程。我鼓励您尝试回归和分类的神经网络架构。一旦你觉得舒服了,就试着把你的知识应用到其他数据集和预测问题上。我希望这篇文章有趣。这篇文章的代码将会在 GitHub 上发布。感谢阅读,机器学习快乐!

构建我的第一个机器学习模型| NBA 预测算法

原文:https://towardsdatascience.com/building-my-first-machine-learning-model-nba-prediction-algorithm-dee5c5bc4cc1?source=collection_archive---------5-----------------------

融合体育分析和机器学习的世界

马库斯·斯皮斯克在 Unsplash 上的照片

编辑:自从写了这篇文章,我们已经在https://infinity sports . ai推出了订阅服务,可以访问我们新的 NBA 预测模型的 API 和输出。我们现在能够预测赢家、差价和总点数。我非常喜欢在这个项目上工作,请随时与任何问题联系!

当我几年前开始编程的时候,我从来没有想过要用我新学到的技能做什么。我玩着制作视频游戏,制作基本的网站,iOS 应用程序,但在某种程度上,我没有专攻某一门手艺,分散了自己的精力。大约在这个时候,多伦多猛龙队进入了历史性的季后赛,我刚刚开始玩 bet365,碰巧在整个季后赛期间专门押注于他们,获得了丰厚的利润。

鉴于我在商业和计算机科学方面的背景,我想更多地了解定量分析师和数据科学家的角色。鉴于我的 python 背景,我开始探索数据科学的世界,并从学习 Scikit-learn 包的基础开始。本文的其余部分将概述我是如何从对数据科学和机器学习几乎一无所知,到构建我的第一个 NBA 预测模型,准确率约为 72%(稍后将详细介绍,但结果并不像看起来那么好)。

目录

  1. 游戏计划
  2. 数据采集
  3. 数据探索&处理
  4. 选择合适的型号
  5. 测试和结果

游戏计划

在过去的 10-20 年里,NBA 以及其他许多体育项目已经见证了统计学的应用呈指数级增长。我通过阅读 开始搜索最相关的 NBA 统计数据,NBA 统计数据实际上转化为胜利 作者 Chinmay Vayda 。他的研究发现,NBA 获胜的最佳预测因素是球队的进攻得分、防守得分、篮板差、3.5%以及其他数据,你可以通过上面的链接了解更多信息。

UnsplashNeONBRAND 拍摄的照片

我计划使用更近的数据,通过利用 NBA 的月度统计数据,并使用这些数据作为在那个进行的比赛的预测。因为这是我第一次尝试建立一个模型,所以我想保持数据简单,并保持数学复杂性最小。

下一步是弄清楚如何获取这些数据。我需要一个过去 10 年比赛结果的数据源,以及一个球队在任何给定月份的统计数据。

数据采集

对于这个项目的数据获取部分,我使用了 Selenium ,这是一个方便 web 抓取的 python 包,用于从各种网站获取我需要的数据。我还决定将我的搜索限制在从 2008-2009 赛季至今的数据上。

过去的结果

我过去比赛结果的数据来源是 Basketball-Reference.com 的比赛结果,可以追溯到 1946 年。

Basketball-Reference.com上的数据源截图

用 Selenium,我把这些表格一张张刮下来,转换成 CSV 文件,以备后用。

过去的月度统计

我还需要知道每个月每个球队的统计数据,我的数据来源是 NBA 官方统计页面。在调整了一些过滤器之后,我最终得到了下表。

NBA 高级统计数据上的数据源截图

我又一次使用 Selenium,把这些月度统计表刮下来,保存为 CSV 文件。我的下一步是将这些文件合并成一个数据集。

数据处理和探索

数据处理

我们现在必须以一种可以进行比较和分析的格式处理我们的 2 组 CSV 文件。我在下面嵌入了每个 CSV 文件的前 5 行的例子,正如它们被废弃一样。

原始形式的结果和统计数据的前 5 行。

我们注意到的第一件事是,两个表都没有标题,这需要通过引用原始表来手动添加。其次,我们的数据集中有多个空列,需要删除它们。最后,在我们的结果数据集中,我们需要删除时间列框分数文本出席人数,因为它们对我们没有用。

按照这些步骤,我们的数据集现在看起来像这样:

结果和统计后处理的前 5 行。

下面的代码概述了我如何着手合并这两个 CSV 文件,以及添加一个新列来表示团队 1 是赢还是输,这将成为我们的预测变量。我贴上代码是因为合并的细节不容易解释,但是如果你感兴趣,你可以看看下面。

执行上述代码后,我们的数据集现在看起来像这样:

我们现在有了我们的数据集,它比较了每个队的统计数据以及哪个队赢得了这场遭遇战。在这个过程的最后,我们的数据集包含超过 13,000 场独特的比赛!这个数据集现在非常接近于为机器学习分析做好准备。

由于我只需要团队名称和分数来合并数据集,并找出哪个团队赢了,所以我也将删除这些列,留下一个数据集,现在可以对其进行探索和建模以进行机器学习。

数据探索

我们当前的数据集如下所示:

我现在有兴趣知道我们数据集中的各种统计数据是如何相互关联的。我的一个朋友向我介绍了一个名为 Speedml 的伟大的 python 包,它简化了探索性分析的过程,并生成了非常好看的图表来共享您的数据。

当您有数字要素时,查看数据集中的每个要素如何与其他要素相关总是很有趣的。下图说明了我们的特征相关性,它有一些我们可以得出的有趣见解。

Speedml 生成特征关联图

首先,我们可以看出,因为左上和右下象限具有最相关的特征,所以团队的统计数据与其自身有很强的相关性。如果一个团队的节奏很快,它可能也会有很高的效率。

我们还看到,“Team1Win”功能与任何特定功能都没有很强的相关性,尽管有一些功能可能比其他功能具有更强的相关性。这让我创建了一个特性重要性图,这是 Speedml 中包含的另一个易于使用的实现。

团队统计特征重要性

虽然我们确实看到了一些更重要的特性,但我们没有明确的理由在这一点上消除任何特性,但它允许进行一些有趣的观察。

Team2NETRTG vs Team1Win 的带状图

从上面的带状图中可以看出 Team2NETRTG 如此重要的原因;当团队 2 的净评分较高时,团队 1 赢得的机会往往会少于,反之,当团队 2 的净评分较低时,团队 1 获胜的机会往往会更高。

客队胜率

最后,我觉得有趣的最后一个情节是队 1 胜的分布。我们获取数据的方式是,团队 1 总是客队。这种分布显示,客场球队输掉了大约 59%的时间,这说明了我们所知道的主场优势。

选择正确的模型

我选择使用 Scikit-learn,因为它易于实现各种算法,并且我有使用 python 的经验。使用 Scikit-learn 提供的流程图,我确定了几个我想尝试的模型,并查看哪一个在我的数据集上表现得最准确。我的尝试开始如下:

在所有尝试的模型中,准确率最高的是支持向量机 SVC 分类器,对历史数据的准确率为 72.52%!现在是时候在现实世界中测试这个模型了,在新冠肺炎事件导致 NBA 停摆之前,我尝试预测了 67 场比赛的结果。

测试和结果

在我的前 28 场比赛预测中,我只是简单地说出谁会赢,没有考虑我得到的差距。在我预测的剩余时间里,我实现了一种计分方法,根据哪支球队更有可能获胜,给出了我的算法创建的一个分布。我的算法准确性结果如下:

我的算法的一个主要弱点是预测混乱的能力。在运行我的模型的第一天,7 场比赛中有 5 场是失败者赢了比赛。我的算法通常与在线体育博彩网站发布的内容一致,这是一个好迹象。在网上阅读,我们可以看到 NBA 通常有32.1%的爆冷率,而我的样本量有 40.2%的爆冷率。

我还想尝试一种财务策略,并开始简单地在预测的赢家身上下注。但是在更新了我的模型之后,我实现了一个货币线,并与 bet365 提供的货币线进行了比较。我比较了我和 bet365 的赔率,选择了两者中更有利的一个。下面我们可以看到盲目和选择性策略的结果。

我们可以立即看出选择性是一个更好的策略,它包括选择更有利的机会。

我一直等到文章的结尾来解决这个问题,但是这个模型中的一个主要缺陷,我花了几个月才意识到,就是它使用的数据是有偏见的。简而言之,一个球队的月度统计数据直接受到他们当月比赛表现的影响,所以考虑到我是用一个月的统计数据来预测球队的表现,统计数据已经包括了他们在那些比赛中的表现。这种认识让我开始建立我的新 NBA 预测模型。

未来的步骤

我选择现在撰写和发布这篇文章的原因是,我在 2020 年 1 月/2 月建立了这个模型,我一直在致力于一个更新的、更全面的模型,该模型将使用人工神经网络来预测不是赢或输,而是每次遭遇的实际分数。这种新模式将基于一个团队中的球员,而不是整个团队。我将在接下来的几周写下这段旅程,并希望分享一些好消息!

如果你喜欢这篇文章或者想讨论提到的任何信息,你可以通过 LinkedinTwitter 或者通过电子邮件联系我。

从头开始构建我的神经网络

原文:https://towardsdatascience.com/building-my-neural-network-from-scratch-cbb97321cb8f?source=collection_archive---------15-----------------------

理解 Python 中深度学习的具体细节

动机

尽管已经有很多令人惊叹的深度学习框架可供使用,但我相信从头开始构建自己的神经网络有助于更好地理解神经网络的内部工作方式。

当我在 Coursera 上斯坦福机器学习课程时,有一个作业指导你完成神经网络反向传播的基本实现。所以我决定根据这个任务建立我的模型。但是有几件事我想修改/改进:

  • 基于用 Octave 编写的原始赋值派生一个 Python 实现。
  • 不是建立一个简单的 3 层神经网络,而是让我们自己决定神经网络架构成为可能。
  • 对算法实现矢量化。

事不宜迟,我们开始吧!

创建神经网络类

基本神经网络由以下部分组成:

  • 输入层
  • 输出层
  • 一个或多个隐藏层
  • 各层之间的权重
  • 激活功能

请注意:

  • 输入层中的节点数与输入的要素数相同,输出的类数给出了输出层中的节点数。
  • 除了输出层之外的每一层都增加了一个偏置单元。
  • 有几种类型的激活功能。这里我为我的神经网络的每一层使用一个 sigmoid 函数。

所以让我们用 python 创建一个神经网络类:

模型表示

给定 m 个训练例子 (X,Y)我们把它们写成矩阵:

其中, X 的每一行是 n 个特征的一个训练示例, Y 的每一行表示每个示例在 K 类中的哪一个。

我们将一个层神经网络的每一层 l 表示为如下:**

每个矩阵的第 j 行表示该层中第 j 个示例的值。这里 Sl 表示第 l 层的节点数。

每层之间的权重也用矩阵表示:

****

前馈计算

我们使用前馈计算来计算给定输入和权重的预测输出。

给定输入 X,我们首先计算第一个隐藏层的值。

首先,向输入层添加一个偏置项。然后将输入层乘以权重矩阵得到

应用激活函数以获得第一层的矩阵 A:

现在,我们只需重复此过程来计算每个图层的值,包括输出图层。

价值函数

正则化神经网络的成本函数由下式给出:

这里 a 是来自输出层的值, λ 表示正则化参数。

我们的目标是找到一个好的设置权重参数来最小化代价函数。我们可以用几个优化算法比如梯度下降来最小化函数。为了应用该算法,我们必须计算成本函数的梯度。

反向传播

我们实现反向传播算法来计算成本函数的梯度。

我写了一篇关于反向传播的实现及其工作原理的文章。如果你对反向传播的细节感兴趣,可以去看看。

这是代码:

学习参数

在我们实现了成本函数和梯度计算之后,我们使用 SciPy 的最小化函数来学习使成本函数最小化的良好参数集。

训练神经网络

让我们把所有东西放在一起训练我们的神经网络:

预言;预测;预告

现在,我们可以使用经过训练的神经网络来预测测试数据集的标签。为此,只需执行前馈计算来计算测试数据 X 的输出 Y

这就是了!我们已经完成了整个神经网络的构建!

感谢你和我一起踏上这个从零开始构建神经网络的奇妙旅程。我确实从整个过程中获益良多,我希望你也有同样的感受!

完整的代码可以在我的 GitHub 库中找到。欢迎给我留言!

参考

使用决斗深度 Q-学习构建用于毁灭的攻击性 AI 代理。

原文:https://towardsdatascience.com/building-offensive-ai-agents-for-doom-using-dueling-deep-q-learning-ab2a3ff7355f?source=collection_archive---------32-----------------------

Pytorch 中的实现。

简介

在过去的几篇文章中,我们已经讨论了 VizDoom 游戏环境中实现了 深度 Q 学习 (DQN)和双重深度 Q 学习 (DDQN),并评估了它们的性能。深度 Q-learning 是一种高度灵活且响应迅速的在线学习方法,它利用场景内的快速更新来估计环境中的状态-动作(Q)值,以便最大化回报。Double Deep Q-Learning 通过将负责动作选择和 TD-target 计算的网络解耦,以最小化 Q 值高估,在训练过程的早期,当代理尚未完全探索大多数可能的状态时,这个问题尤其明显。

我们之前的双 DQN 特工在维兹杜姆环境中的表现,训练了 500 集。

本质上,使用单个状态-动作值来判断情况需要探索和学习每个单个状态的动作的效果,从而导致模型的泛化能力的固有障碍。此外,并非所有国家都同样与环境相关。

来自我们之前在 Pong 中训练的代理的预处理帧。

回想一下我们早期的实现中的 Pong 环境。在我们的代理人击球后,向左或向右移动的价值可以忽略不计,因为球必须首先移动到对手,然后返回给球员。结果,在这一点上计算用于训练的状态-动作值可能破坏我们的代理的收敛。理想情况下,我们希望能够识别每个行为的价值,而无需了解每个状态的特定效果,以便鼓励我们的代理专注于选择与环境相关的行为。

决斗深度 Q 学习(以下简称 DuelDQN)通过将 DQN 网络输出分成两个流来解决这些缺点:价值流和优势(或行动)流。通过这样做,我们部分地分离了整个国家行为评估过程。在他们的开创性论文中。al 展示了 DuelDQN 如何影响 Atari 游戏 Enduro 中代理人表现的可视化,演示了代理人如何学习专注于不同的目标。请注意,价值流已经学会关注道路的方向,而优势流已经学会关注代理人面前的直接障碍。实质上,我们通过这种方法获得了一定程度的短期和中期远见。

为了计算状态-动作的 Q 值,我们然后利用优势函数来告诉我们动作的相对重要性。平均优势的减法,计算一个状态中所有可能的动作,用于找到我们感兴趣的动作的相对优势。

根据 DuelDQN 架构的状态动作的 Q 值。

直观地说,为了获得对环境更可靠的评估,我们已经部分地分离了动作和状态值估计过程。

实现

我们将在与上一篇文章保卫防线相同的多目标条件下,在相同的 VizDoomgym 场景中实现我们的方法。环境的一些特征包括:

  • 一个 3 的动作空间:开火,左转,右转。不允许扫射。
  • 向玩家发射火球的棕色怪物,命中率为 100%。
  • 试图以之字形靠近来咬玩家的粉红色怪物。
  • 重生的怪物可以承受更多伤害。
  • 杀死一个怪物+1 点。
  • -死了得 1 分。

“防线方案”的初始状态

我们的 Google 协作实现是使用 Pytorch 用 Python 编写的,可以在 GradientCrescent Github 上找到。我们的方法基于泰伯优秀强化学习课程中详述的方法。由于我们的 DuelDQN 实现类似于我们之前的香草 DQN 实现,所以整个高级工作流是共享的,这里不再重复。

让我们从导入所有必需的包开始,包括 OpenAI 和 Vizdoomgym 环境。我们还将安装火炬视觉所需的 AV 包,我们将使用它进行可视化。请注意,安装完成后,必须重新启动运行时,才能执行笔记本的其余部分。

#Visualization cobe for running within Colab
!sudo apt-get update
!sudo apt-get install build-essential zlib1g-dev libsdl2-dev libjpeg-dev nasm tar libbz2-dev libgtk2.0-dev cmake git libfluidsynth-dev libgme-dev libopenal-dev timidity libwildmidi-dev unzip# Boost libraries
!sudo apt-get install libboost-all-dev# Lua binding dependencies
!apt-get install liblua5.1-dev
!sudo apt-get install cmake libboost-all-dev libgtk2.0-dev libsdl2-dev python-numpy git
!git clone [https://github.com/shakenes/vizdoomgym.git](https://github.com/shakenes/vizdoomgym.git)
!python3 -m pip install -e vizdoomgym/!pip install av

接下来,我们初始化我们的环境场景,检查观察空间和动作空间,并可视化我们的环境。

import gym
import vizdoomgymenv = gym.make('VizdoomDefendLine-v0')
n_outputs = env.action_space.n
print(n_outputs)observation = env.reset()import matplotlib.pyplot as pltfor i in range(22):

  if i > 20:
    print(observation.shape)
    plt.imshow(observation)
    plt.show()observation, _, _, _ = env.step(1)

接下来,我们将定义预处理包装器。这些类继承自 OpenAI gym 基类,覆盖了它们的方法和变量,以便隐式地提供所有必要的预处理。我们将开始定义一个包装器来重复许多帧的每个动作,并执行元素方式的最大值以增加任何动作的强度。您会注意到一些三级参数,如 fire_firstno _ ops——这些是特定于环境的,在 Vizdoomgym 中对我们没有影响。

class RepeatActionAndMaxFrame(gym.Wrapper):
  #input: environment, repeat
  #init frame buffer as an array of zeros in shape 2 x the obs space
    def __init__(self, env=None, repeat=4, clip_reward=False, no_ops=0,
                 fire_first=False):
        super(RepeatActionAndMaxFrame, self).__init__(env)
        self.repeat = repeat
        self.shape = env.observation_space.low.shape
        self.frame_buffer = np.zeros_like((2, self.shape))
        self.clip_reward = clip_reward
        self.no_ops = no_ops
        self.fire_first = fire_first
  def step(self, action):
        t_reward = 0.0
        done = False
        for i in range(self.repeat):
            obs, reward, done, info = self.env.step(action)
            if self.clip_reward:
                reward = np.clip(np.array([reward]), -1, 1)[0]
            t_reward += reward
            idx = i % 2
            self.frame_buffer[idx] = obs
            if done:
                break
        max_frame = np.maximum(self.frame_buffer[0], self.frame_buffer[1])
        return max_frame, t_reward, done, info
  def reset(self):
        obs = self.env.reset()
        no_ops = np.random.randint(self.no_ops)+1 if self.no_ops > 0    else 0
        for _ in range(no_ops):
            _, _, done, _ = self.env.step(0)
            if done:
                self.env.reset()

        if self.fire_first:
            assert self.env.unwrapped.get_action_meanings()[1] == 'FIRE'
            obs, _, _, _ = self.env.step(1)
        self.frame_buffer = np.zeros_like((2,self.shape))
        self.frame_buffer[0] = obs
    return obs

接下来,我们为我们的观察定义预处理函数。我们将使我们的环境对称,将它转换到标准化的盒子空间,将通道整数交换到张量的前面,并将其从原始(320,480)分辨率调整到(84,84)区域。我们也将我们的环境灰度化,并通过除以一个常数来归一化整个图像。

class PreprocessFrame(gym.ObservationWrapper):
  #set shape by swapping channels axis
 #set observation space to new shape using gym.spaces.Box (0 to 1.0)
    def __init__(self, shape, env=None):
        super(PreprocessFrame, self).__init__(env)
        self.shape = (shape[2], shape[0], shape[1])
        self.observation_space = gym.spaces.Box(low=0.0, high=1.0,
                                    shape=self.shape, dtype=np.float32)
   def observation(self, obs):
        new_frame = cv2.cvtColor(obs, cv2.COLOR_RGB2GRAY)
        resized_screen = cv2.resize(new_frame, self.shape[1:],
                                    interpolation=cv2.INTER_AREA)
        new_obs = np.array(resized_screen, dtype=np.uint8).reshape(self.shape)
        new_obs = new_obs / 255.0
   return new_obs

接下来,我们创建一个包装器来处理帧堆叠。这里的目标是通过将几个帧堆叠在一起作为单个批次,帮助从堆叠帧中捕捉运动和方向。这样,我们可以捕捉环境中元素的位置、平移、速度和加速度。通过堆叠,我们的输入采用(4,84,84,1)的形状。

class StackFrames(gym.ObservationWrapper):
  #init the new obs space (gym.spaces.Box) low & high bounds as repeat of n_steps. These should have been defined for vizdooom

  #Create a return a stack of observations
    def __init__(self, env, repeat):
        super(StackFrames, self).__init__(env)
        self.observation_space = gym.spaces.Box( env.observation_space.low.repeat(repeat, axis=0),
                              env.observation_space.high.repeat(repeat, axis=0),
                            dtype=np.float32)
        self.stack = collections.deque(maxlen=repeat)
    def reset(self):
        self.stack.clear()
        observation = self.env.reset()
        for _ in range(self.stack.maxlen):
            self.stack.append(observation)
        return  np.array(self.stack).reshape(self.observation_space.low.shape)
    def observation(self, observation):
        self.stack.append(observation)
    return np.array(self.stack).reshape(self.observation_space.low.shape)

最后,在返回最终环境供使用之前,我们将所有的包装器绑定到一个单独的 make_env() 方法中。

def make_env(env_name, shape=(84,84,1), repeat=4, clip_rewards=False,
             no_ops=0, fire_first=False):
    env = gym.make(env_name)
    env = PreprocessFrame(shape, env)
    env = RepeatActionAndMaxFrame(env, repeat, clip_rewards, no_ops, fire_first)

    env = StackFrames(env, repeat)
    return env

接下来,让我们定义我们的模型,一个深度 Q 网络,具有决斗架构的两个输出。这基本上是一个三层卷积网络,它采用预处理的输入观测值,将生成的展平输出馈送到一个全连接层,然后将输出分成价值流(单节点输出)和优势流(节点输出对应于环境中的动作数量)。

请注意,这里没有激活层,因为激活层的存在会导致二进制输出分布。我们的损失是我们当前状态-动作的估计 Q 值和我们预测的状态-动作值的平方差。然后,我们附上 RMSProp 优化器,以尽量减少我们在培训期间的损失。

import os
import torch as T
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
import numpy as npclass DeepQNetwork(nn.Module):
    def __init__(self, lr, n_actions, name, input_dims, chkpt_dir):
        super(DeepQNetwork, self).__init__()
        self.checkpoint_dir = chkpt_dir
        self.checkpoint_file = os.path.join(self.checkpoint_dir, name) self.conv1 = nn.Conv2d(input_dims[0], 32, 8, stride=4)
        self.conv2 = nn.Conv2d(32, 64, 4, stride=2)
        self.conv3 = nn.Conv2d(64, 64, 3, stride=1) fc_input_dims = self.calculate_conv_output_dims(input_dims) self.fc1 = nn.Linear(fc_input_dims,1024)
        self.fc2 = nn.Linear(1024, 512)
        #Here we split the linear layer into the State and Advantage streams
        self.V = nn.Linear(512, 1)
        self.A = nn.Linear(512, n_actions) self.optimizer = optim.RMSprop(self.parameters(), lr=lr) self.loss = nn.MSELoss()
        self.device = T.device('cuda:0' if T.cuda.is_available() else 'cpu')
        self.to(self.device) def calculate_conv_output_dims(self, input_dims):
        state = T.zeros(1, *input_dims)
        dims = self.conv1(state)
        dims = self.conv2(dims)
        dims = self.conv3(dims)
        return int(np.prod(dims.size())) def forward(self, state):
        conv1 = F.relu(self.conv1(state))
        conv2 = F.relu(self.conv2(conv1))
        conv3 = F.relu(self.conv3(conv2))
        # conv3 shape is BS x n_filters x H x W
        conv_state = conv3.view(conv3.size()[0], -1)
        # conv_state shape is BS x (n_filters * H * W)
        flat1 = F.relu(self.fc1(conv_state))
        flat2 = F.relu(self.fc2(flat1)) V = self.V(flat2)
        A = self.A(flat2) return V, A def save_checkpoint(self):
        print('... saving checkpoint ...')
        T.save(self.state_dict(), self.checkpoint_file) def load_checkpoint(self):
        print('... loading checkpoint ...')
        self.load_state_dict(T.load(self.checkpoint_file))

回想一下,用于决斗深度 Q 学习的更新函数需要以下内容:

  • 当前状态 s
  • 当前动作
  • 当前动作后的奖励 r
  • 下一个状态s’
  • 下一个动作a’

为了以有意义的数量提供这些参数,我们需要按照一组参数评估我们当前的策略,并将所有变量存储在一个缓冲区中,我们将在训练期间从该缓冲区中提取迷你批次中的数据。因此,我们需要一个重放内存缓冲区来存储和提取观察值。

import numpy as np
class ReplayBuffer(object):
    def __init__(self, max_size, input_shape, n_actions):
        self.mem_size = max_size
        self.mem_cntr = 0
        self.state_memory = np.zeros((self.mem_size, *input_shape),
                                     dtype=np.float32)
        self.new_state_memory = np.zeros((self.mem_size, *input_shape),
                                         dtype=np.float32)
        self.action_memory = np.zeros(self.mem_size, dtype=np.int64)
        self.reward_memory = np.zeros(self.mem_size, dtype=np.float32)
        self.terminal_memory = np.zeros(self.mem_size, dtype=np.bool)
#Identify index and store  the the current SARSA into batch memory def store_transition(self, state, action, reward, state_, done):
        index = self.mem_cntr % self.mem_size
        self.state_memory[index] = state
        self.new_state_memory[index] = state_
        self.action_memory[index] = action
        self.reward_memory[index] = reward
        self.terminal_memory[index] = done
        self.mem_cntr += 1 def sample_buffer(self, batch_size):
        max_mem = min(self.mem_cntr, self.mem_size)
        batch = np.random.choice(max_mem, batch_size, replace=False)

        states = self.state_memory[batch]
        actions = self.action_memory[batch]
        rewards = self.reward_memory[batch]
        states_ = self.new_state_memory[batch]
        terminal = self.terminal_memory[batch]
     return states, actions, rewards, states_, terminal

接下来,我们将定义我们的代理,它不同于我们普通的 DQN 实现。我们的代理正在使用一个勘探率递减的ε贪婪策略,以便随着时间的推移最大化开发。为了学会预测使我们的累积奖励最大化的状态和优势值,我们的代理将使用通过抽样存储的记忆获得的贴现的未来奖励。

您会注意到,作为代理的一部分,我们初始化了 DQN 的两个副本,并使用方法将原始网络的权重参数复制到目标网络中。虽然我们的常规方法利用这种设置来生成固定的 TD-目标,但在我们的 DuelDQN 方法中,双流的存在给该过程增加了一层复杂性:

  • 从重放存储器中检索状态、动作、奖励和下一状态(sar)。
  • 评估网络用于生成当前状态的优势( A_s )和状态( V_s )值。
  • 目标网络还用于创建下一个状态的优势( A_s_ )和状态( V_s_ )值。
  • 预测的 Q 值是通过将当前状态的优势和状态值相加,并减去用于归一化的当前状态优势值的平均值而生成的。
  • 通过将下一个状态的优势和状态值相加,并减去下一个状态优势值的平均值以进行归一化,来计算目标 Q 值当前状态。
  • 然后,通过将折扣后的目标 Q 值与当前状态奖励相结合,构建 TD 目标。
  • 通过将 TD 目标与预测的 Q 值进行比较来计算损失函数,然后将其用于训练网络。
import numpy as np
import torch as T
#from deep_q_network import DeepQNetwork
#from replay_memory import ReplayBufferclass DuelDQNAgent(object):
    def __init__(self, gamma, epsilon, lr, n_actions, input_dims,
                 mem_size, batch_size, eps_min=0.01, eps_dec=5e-7,
                 replace=1000, algo=None, env_name=None, chkpt_dir='tmp/dqn'):
        self.gamma = gamma
        self.epsilon = epsilon
        self.lr = lr
        self.n_actions = n_actions
        self.input_dims = input_dims
        self.batch_size = batch_size
        self.eps_min = eps_min
        self.eps_dec = eps_dec
        self.replace_target_cnt = replace
        self.algo = algo
        self.env_name = env_name
        self.chkpt_dir = chkpt_dir
        self.action_space = [i for i in range(n_actions)]
        self.learn_step_counter = 0 self.memory = ReplayBuffer(mem_size, input_dims, n_actions) self.q_eval = DeepQNetwork(self.lr, self.n_actions,
                                    input_dims=self.input_dims,
                                    name=self.env_name+'_'+self.algo+'_q_eval',
                                    chkpt_dir=self.chkpt_dir) self.q_next = DeepQNetwork(self.lr, self.n_actions,
                                    input_dims=self.input_dims,
                                    name=self.env_name+'_'+self.algo+'_q_next',
                                    chkpt_dir=self.chkpt_dir)#Epsilon greedy action selection
    def choose_action(self, observation):
        if np.random.random() > self.epsilon:
          # Add dimension to observation to match input_dims x batch_size by placing in list, then converting to tensor
            state = T.tensor([observation],dtype=T.float).to(self.q_eval.device)
            #As our forward function now has both state and advantage, fetch latter for actio selection
            _, advantage = self.q_eval.forward(state)
            action = T.argmax(advantage).item()
        else:
            action = np.random.choice(self.action_space) return action def store_transition(self, state, action, reward, state_, done):
        self.memory.store_transition(state, action, reward, state_, done) def sample_memory(self):
        state, action, reward, new_state, done = \
                                    self.memory.sample_buffer(self.batch_size) states = T.tensor(state).to(self.q_eval.device)
        rewards = T.tensor(reward).to(self.q_eval.device)
        dones = T.tensor(done).to(self.q_eval.device)
        actions = T.tensor(action).to(self.q_eval.device)
        states_ = T.tensor(new_state).to(self.q_eval.device) return states, actions, rewards, states_, dones def replace_target_network(self):
        if self.learn_step_counter % self.replace_target_cnt == 0:
            self.q_next.load_state_dict(self.q_eval.state_dict()) def decrement_epsilon(self):
        self.epsilon = self.epsilon - self.eps_dec \
                           if self.epsilon > self.eps_min else self.eps_min def save_models(self):
        self.q_eval.save_checkpoint()
        self.q_next.save_checkpoint() def load_models(self):
        self.q_eval.load_checkpoint()
        self.q_next.load_checkpoint()

    def learn(self):
        if self.memory.mem_cntr < self.batch_size:
            return self.q_eval.optimizer.zero_grad() #Replace target network if appropriate
        self.replace_target_network() states, actions, rewards, states_, dones = self.sample_memory()
        #Fetch states and advantage actions for current state using eval network
        #Also fetch the same for next state using target network
        V_s, A_s = self.q_eval.forward(states)
        V_s_, A_s_ = self.q_next.forward(states_) #Indices for matrix multiplication
        indices = np.arange(self.batch_size) #Calculate current state Q-values and next state max Q-value by aggregation, subtracting constant advantage mean

        q_pred = T.add(V_s,
                        (A_s - A_s.mean(dim=1, keepdim=True)))[indices, actions]

        q_next = T.add(V_s_,
                        (A_s_ - A_s_.mean(dim=1, keepdim=True))).max(dim=1)[0] q_next[dones] = 0.0
        #Build your target using the current state reward and q_next
        q_target = rewards + self.gamma*q_next loss = self.q_eval.loss(q_target, q_pred).to(self.q_eval.device)
        loss.backward()
        self.q_eval.optimizer.step()
        self.learn_step_counter += 1 self.decrement_epsilon()

定义了所有支持代码后,让我们运行主训练循环。我们已经在最初的总结中定义了大部分,但是让我们为后代回忆一下。

  • 对于训练集的每一步,在使用ε-贪婪策略选择下一个动作之前,我们将输入图像堆栈输入到我们的网络中,以生成可用动作的概率分布
  • 然后,我们将它输入到网络中,获取下一个状态和相应奖励的信息,并将其存储到我们的缓冲区中。我们更新我们的堆栈,并通过一些预定义的步骤重复这一过程。
  • 在一集的结尾,我们将下一个状态输入到我们的网络中,以便获得下一个动作。我们还通过对当前奖励进行贴现来计算下一个奖励。
  • 我们通过上面提到的 Q 学习更新函数生成我们的目标 y 值,并训练我们的网络。
  • 通过最小化训练损失,我们更新网络权重参数,以便为下一个策略输出改进的状态-动作值。
  • 我们通过跟踪模型的平均得分(在 100 个训练步骤中测量)来评估模型。
env = make_env('VizdoomDefendLine-v0')
best_score = -np.inf
load_checkpoint = False
n_games = 2000
agent = DuelDQNAgent(gamma=0.99, epsilon=1.0, lr=0.0001,input_dims=(env.observation_space.shape),n_actions=env.action_space.n, mem_size=5000, eps_min=0.1,batch_size=32, replace=1000, eps_dec=1e-5,chkpt_dir='/content/', algo='DuelDQNAgent',env_name='vizdoogym')if load_checkpoint:
  agent.load_models()fname = agent.algo + '_' + agent.env_name + '_lr' + str(agent.lr) +'_'+ str(n_games) + 'games'
figure_file = 'plots/' + fname + '.png'n_steps = 0
scores, eps_history, steps_array = [], [], []for i in range(n_games):
  done = False
  observation = env.reset() score = 0
  while not done:
    action = agent.choose_action(observation)
    observation_, reward, done, info = env.step(action)
    score += reward if not load_checkpoint:
      agent.store_transition(observation, action,reward, observation_, int(done))
      agent.learn()
    observation = observation_
    n_steps += 1scores.append(score)
steps_array.append(n_steps)avg_score = np.mean(scores[-100:])if avg_score > best_score:
    best_score = avg_score

    print('Checkpoint saved at episode ', i)
    agent.save_models()print('Episode: ', i,'Score: ', score,' Average score: %.2f' % avg_score, 'Best average: %.2f' % best_score,'Epsilon: %.2f' % agent.epsilon, 'Steps:', n_steps)eps_history.append(agent.epsilon)
  if load_checkpoint and n_steps >= 18000:
    break

我们绘制了 500 集、1000 集和 2000 集的代理商平均分和我们的 epsilon 值。

500 集后我们经纪人的奖励分配。

1000 集后我们经纪人的奖励分配。

2000 集后我们经纪人的报酬分配。

查看结果并将其与我们的普通 DQN 实施和双 DQN 实施进行比较,您会注意到在 500、1000 和 2000 集的分布中有显著提高的改善率。此外,具有更受约束的报酬振荡,表明当比较任一实现时改进了收敛。

我们可以想象我们的代理人在 500 和 1000 集以下的表现。

500 集的代理性能。

在 500 集时,代理已经采用了先前在较高训练时间为 DQN 和 DDQN 识别的相同策略,归因于在局部最小值的收敛。仍然会采取一些攻击性的行动,但是主要的策略仍然依赖于怪物之间的友好射击。

at 1000 集呢?

1000 集的代理性能。

我们的经纪人已经成功地打破了本地化的最低限度,并发现了一个更具进攻性的角色为导向的替代战略。这是我们的 DQN 和 DDQN 模型都无法做到的,即使是在 2000 集的情况下——证明了 DuelDQN 的双流方法在识别和优先考虑与环境相关的行动方面的效用。

这就结束了双深度 Q 学习的实现。在我们的下一篇文章中,我们将通过把我们所学的所有知识结合到一个方法中来完成我们的 Q 学习方法系列,并在一个更动态的结局中使用它。

我们希望你喜欢这篇文章,并希望你查看 GradientCrescent 上的许多其他文章,涵盖人工智能的应用和理论方面。为了保持对 GradientCrescent 的最新更新,请考虑关注该出版物并关注我们的 Github 资源库

来源

萨顿等人。al,“强化学习”

塔博尔,“运动中的强化学习”

西蒙尼尼,“深度 Q 学习的改进*

用 Python 构建人口模型

原文:https://towardsdatascience.com/building-population-models-in-python-57f9e174d27d?source=collection_archive---------16-----------------------

概念模型和动画视觉效果,用于在没有大量数据的情况下讲述强大的数据科学故事。

做“数据科学”的一大挑战是收集足够的数据。许多模型,尤其是机器视觉或自然语言处理等复杂问题的模型,需要大型数据集,尽管我们可能生活在大数据时代,但在家中利用大型高质量数据集并不总是容易的。特别是对于数据科学的学生或该领域的新手来说,缺乏整洁的数据似乎会成为学习的障碍——我们还能从 iris 数据集或泰坦尼克号数据集获得多少里程?

如果你是一名数据科学家或正在接受培训的数据科学家,正在寻找一些兼职项目,我想建议有另一种方法来练习高度相关的技能,这种方法不需要大量的网络搜集或对精选数据的高级访问。如果说有什么不同的话,那就是许多数据科学教学(特别是在越来越普遍的训练营)对高精度预测的关注——这种预测需要基于大量数据训练的模型——不利于建立模型的基本用途之一:创建概念框架以更好地理解事物如何或为什么以它们的方式工作。考虑到这一点,我认为不需要收集数据就可以建立有用的模型。

在新冠肺炎疫情的世界里,这种模型无论是作为具体预测的基础,还是作为公共交流的工具,都显示出了极大的价值。也许你已经读过《华盛顿邮报》的那篇文章,这篇文章基本上像病毒一样传播开来,并迅速成为他们有史以来最受欢迎的在线文章。或者看了 3Blue1Brown 的优秀视频和他的各种疫情场景的模型。在这两种情况下,以及许多其他情况下,非常有用的说明性视觉效果已经建立在不需要先前数据的模型之上。当然,这些模型的目标略有不同;他们并不试图预测确切的病例数或诸如此类的事情,相反,他们的目标是建立一种直觉,了解某些特征可能如何影响结果。但是,这仍然是有价值的,对于一种没有历史数据的新疾病,这可能是你能做的最好的了。

值得强调的是,从技术的角度来看,华盛顿邮报中强调的模型和视觉效果并不特别难构建。大多数初级数据科学家对 Python 和常用数学和绘图库有基本的掌握,他们应该有技术技能来构建这样的模型,并创建动画或交互式视觉效果。考虑到这一点,这里有几个开发人口模型的小项目,并为任何有兴趣在这方面创造自己的视觉效果的人提供相应的视觉效果。

疾病在人群中传播的简单模拟

任何类型的建模规则一:做好你的研究,不要太相信结果!

这确实适用于任何项目,但是如果你的模型不是建立在经验观察的基础上,它的重要性就会被放大。如今,人们通常根据预测模型的表现来判断它们,而不是根据它们对为什么做出某些预测的说法来判断——深度神经网络甚至以的不透明而闻名——但我们将讨论的各种模型没有验证集。相反,它们是一系列已经被操作化的假设,所以你可以看到它们是如何发挥作用的。你能做出什么样的假设或简化,并且仍然能看到有用的结果?这在很大程度上取决于您正在建模的内容,而判断什么样的模型有意义的唯一方法是首先积累一些领域知识。

这类模型通常也不会给你准确的预测,也不应该这样对待。重要的是要明白,我们的目标不一定是预测未来,而是感受某些事情是如何影响潜在结果的。在我描述建立一个流行病模型之前,让我声明我不是流行病学家,也不是我提到的其他新冠肺炎模型的创造者。我们创建的任何模型都不应被理解为对疾病将如何传播的准确预测。相反,它们可以说明现实世界中可能存在的联系和影响,或者某些影响的大小。有一句话,通常被认为是乔治·博克斯说的,所有的模型都是错的,但有些是有用的。我们的目标是完全属于错误但有用的类别。

项目 1:使用 Lotka-Volterra 的简单人口模型

洛特卡-沃尔泰拉方程是一组简单的微分方程,也称为捕食者-猎物方程,你可能在高中生物课上遇到过。名副其实,他们模拟了捕食者和被捕食者相互作用的种群动态,其中每个种群的增长或下降部分取决于当前的种群规模。一个地区的大量野兔会让以野兔为食的狐狸数量增长,但随着狐狸数量的增长,捕食可能会开始减少野兔的数量,进而给狐狸的数量带来压力,等等。

出于我们的目的,Lotka-Volterra 方程是一个很好的起点,因为它们易于实现。只有两个方程,一个描述被捕食者种群的变化率,另一个描述捕食者种群的变化率。被捕食种群的公式为:

其中 x 是当前的猎物种群,y 是当前的捕食者种群,α是猎物的生长常数,β是代表捕食率的常数。猎物数量的相应等式是:

这两个方程合在一起形成了周期曲线,在捕食者数量少的时期,被捕食者数量迅速增长,但随着捕食者数量的增长,被捕食者数量又会下降。如果解微分方程的想法让你望而生畏,请认识到,你可以通过对每个变化率进行瞬时估计,向前增加一点时间,然后计算新的变化率,而无需任何复杂的数学运算,这就是我在这里所做的:

一个简单的人口模型

虽然这不是一个没有吸引力的图表,但我们希望真正利用视觉的力量,我们可以通过两种方式做到这一点。一种方法是将图形制成动画,就像实时描绘这些曲线一样。最普遍的 Python 图形库 Matplotlib 实际上有一个专门用于创建动画的模块。该模块只有两种方法。一种方法是通过收集一系列 Matplotlib artist 对象来创建动画(比如 Axes 或 Line2d 对象,它们代表您正在绘制的事物的位置或绘制方式)。另一个应用您提供的函数来重复更新图形。该模块非常强大,但是可能有点敏感,因此有必要详细地浏览一个示例。

对于这种情况,我将使用。FuncAnimation()函数,它将重复调用一个函数来更新图形的信息。原则上,您可以使用这个函数来不断更新您的底层模型,如果您想要放弃一个没有设定结尾的动画,并看着它无限期地继续播放,这将是有意义的。如果您对动画有一个固定的结尾,我通常发现更简单的方法是运行模型,无论您需要创建多长时间的数据,然后使用动画功能来引用这些数据的增加部分。需要记住的另一件事是 Matplotlib 会自己显示动画,它没有办法将它们保存为视频文件或 gif 文件。你需要一个外部包来完成这个任务。我用 ImageMagick 制作我的 gif。实现 Lotke-Volterra 方程动画的完整代码块如下:

以下是这段代码创建的内容:

Matplotlib 动画

模型是确定性的;对于四个参数的任何给定值,以及两个总体的任何给定值,两个总体的大小只会以一种方式出现。要考虑的问题是,当参数改变时,这条路径将如何运行。当捕食率增加或减少时,图表会有怎样的变化?什么因素会改变周期的长短?

为了真正探索模型中参数的相互作用,我们可以转向第二个技巧,即增加交互性。如果能够为每个参数设置新值,并让可视化自动重新计算,而不需要更改我们的代码,这将是很有帮助的。为此,我们可以考虑用 Dash 之类的工具构建一个仪表板(Matplotlib 也有图形交互工具,但 Dash 是一个常用工具,因此使用起来会更简单)。让我们建立一个简单的仪表板,它有四个滑块来控制参数的值。

在 Dash 中设置仪表板的代码分为两部分。在第一部分中,我们定义了仪表板的布局,使用 Dash 的 html 组件模块来控制仪表板的 html 布局。在第二种情况下,一系列回调函数将您拥有的任何输入(如本例中的滑块)连接到图形,并定义图形如何对输入做出反应。

这个示例 dash 非常简单,毕竟没有样式,只有仪表板的核心组件,但是能够实时处理参数和查看结果,这提供了一个有启发性的视觉效果,让您可以真正了解等式是如何工作的。这个方程有一个有趣的特征,表面上并不明显,那就是降低α,即猎物物种的增长率,实际上并不会降低猎物物种达到的峰值,因为这些峰值是由捕食者和猎物物种之间的相互作用决定的,较低的增长率最终也会降低捕食者物种的数量:

降低 alpha 值会改变图形的周期,但不会改变被捕食物种的峰值

当然,有一个重要的问题是,这个模型是否真的描述了野生动物的数量是如何随着时间变化的。为了回答这个问题,我们需要阅读生物学,但是我要重申的是,这个模型不需要准确地预测实际的动物数量才是有用的。

项目 2:流行病学建模

从静态的两方程模型到华盛顿邮报或 3Blue1Brown 视频中强调的那种更动态的半随机模型,需要更多的编码,但比你想象的要少。他们强调的那种先生流行病学模型有更多的活动部分,大量的个体被建模出来,一定量的随机机会发挥作用,但是,计算机做了大部分的繁重工作。真正的工作是考虑如何建立模型,而不是实际建模。

这种模型是面向对象编程的主要用例。毕竟,该模型实际上只是代表了一群具有特定属性的个体(他们是否被感染,他们的位置,他们被感染了多长时间,等等)。)和某些相互作用的方式(从一个地方移动到另一个地方,传播疾病)。因此,我们可以通过创建一个表示单个案例的对象类并让它们进行交互来模拟模型的工作。

对于这个模型,我们的类需要一些属性。当然,我们需要知道个人是否生病,因此一个属性是“状态”,我将它设置为 0 表示未感染但易感染,1 表示生病,2 表示已移除。每个人还需要一个位置,以及一些关于他们如何移动的信息,所以我也给了他们一个“速度”。我还想象每个人都有一套“界限”,标明他们活动的大致区域。你可以用这个来建模,比如说,生活在不同城市的人(就像 3Blue1Brown 所做的那样)或者不同的社区,他们之间有一些联系,但不是完全重叠。

最后,你需要描述个人可能改变和互动的方式。在这个简单的版本中,我包含了一个设置个人运动方向的方法,一个沿着运动方向改变他们位置的方法和一个覆盖可能传播的方法。在这个模型中,我假设离一个有传染性的人足够近(我们的模型对社会互动的粗略代表)会带来一定的患病概率。在这个例子中,半径和概率是全局变量,所以我为每个人都设置了相同的值,但是您可以很容易地更改代码,使它们成为个体本身的属性,并为不同的个体设置不同的值(也许有些个体天生就不容易受到影响)。在这个简单的示例中,您可以像这样设置该类:

现在您已经设置了这个类,实际上运行这个模型是非常简单的。生成一堆对象并将它们随机放置在整个网格中,其中一些应该用设置为 1 的“状态”进行实例化,表示已感染。在每一轮,更新他们的位置,看看是否有任何传播发生,然后跟踪有多少人被感染。冲洗并重复,记录每个回合中每个类别有多少人,然后根据数据生成您的绘图,这次使用 ArtistAnimation()函数,该函数接受 Matplotlib artist 对象的列表:

这就产生了这个情节,很像《华盛顿邮报》文章中的情节:

我们的简单流行病学模型的图形结果,蓝色表示易感,红色表示受感染,灰色表示已移除

创建动画散点图遵循基本相同的步骤,除了他们的状态之外,我们还需要跟踪每个人的位置。为了使视觉更容易理解,我还减少了创建的个体数量并减慢了它们的速度(也减少了感染半径和感染概率,因为当你减慢个体速度时,它们在慢慢经过时会在彼此附近转更多的圈,这实质上提高了你的感染率)。

它将会创造出类似这样的东西,这取决于机遇:

这些图与 Grant Sanderson 在 3Blue1Brown 制作的图有两个主要区别。一个是样式的质量——我用尽可能少的样式展示了这些示例,以尽可能清晰地展示代码的机制,而 3Blue1Brown 动画经过了仔细而美丽的渲染,以最大限度地清晰最终的动画。格兰特制作的动画可能超出了我的能力——他实际上编写了自己的动画库来制作他的视频——但是使用 Matplotlib 的工具来制作一些优秀的动画是可能的。

第二个大的区别实际上与模型或代码无关,而是所提问题的信息量。不过,我要再次强调,任何有好奇心并愿意做一些研究的人都可以开始问一些有趣的问题。你如何让这个模型更好地符合我们对这种疾病的了解?这些模型中个体行为的变化会如何影响结果?寻找答案的障碍比你想象的要低。

用 MyAnimeList 和 Sklearn 构建预测模型(第 1 部分)

原文:https://towardsdatascience.com/building-predictive-models-with-myanimelist-and-sklearn-54edc6c9fff3?source=collection_archive---------49-----------------------

本文使用 MyAnimeList 数据库中的许多特性变量来预测用户评分

MyAnimeList is one of the largest online data repositories for anime on the internet with listings ranging from TV series to Manga comics and data dating back to ~1905 (for anyone interested, this is 活動写真/Katsudou Shashin). Luckily this data is all available on Kaggleand comes split into different components/data-frames: user ratings and anime listing information. Given my mutual love for anime and all things data, I thought it would be cool to combine data and to build some predictive models and basic recommendation systems in Python. I will discuss the intricacies of such in more detail below. The first section of my work with the MyAnimeList will focus on some machine learning techniques used to predict user rating scores using a variety of features that were computed for analysis using Sklearn. The second section will follow this post and will hone in on some simple techniques used to recommend anime content, based on both user rating correlations and feature variables.

数据准备

在执行任何分析之前,安装必要的软件包:

import numpy as np
import pandas as pd
import sklearn
import matplotlib.pyplot as plt
import seaborn as sb
from sklearn.preprocessing import LabelEncoder
from sklearn.model_selection import train_test_split
from sklearn.ensemble import RandomForestClassifier, RandomForestRegressor
from sklearn.model_selection import GridSearchCV
from sklearn.metrics import accuracy_score
from sklearn.neural_network import MLPRegressor, MLPClassifier
from sklearn import metrics
from matplotlib import rcParams
import joblib
from sklearn.tree import export_graphviz
import pydot
from IPython.display import Image

从本地导入两个数据框:

local_1 = '/Users/hopkif05/Desktop/rating.csv'
ratings = pd.read_csv(local_1)local_2 = '/Users/hopkif05/Desktop/AnimeList.csv'
anime_list = pd.read_csv(local_2)

并将它们连接在一个唯一的标识符上,在这种情况下是它们的 anime_id :

anime = pd.merge(ratings, anime_list, on=’anime_id’)
anime.head()

您可以从上面的数据框中看到,我们现在在数据库中有每个用户的评级,这些评级已经与该内容相关的所有元数据合并在一起。这里有大约 800 万个用户评级,有 33 行,每一行代表我们模型的一个潜在特征变量。为了后续的分析,我已经选择了 user_id、anime_id、rating_x、title_english、type、source、scored_by、score、favorites、members、popularitystudio 作为模型中包含的变量;并将它们存储在一个新的数据帧中:

anime_df = anime[[‘user_id’,’anime_id’,’rating_x’,’title_english’,’type’,’source’,’scored_by’,’score’,’favorites’,’members’,’popularity’,’studio’]].copy()
anime_df.head(100)

您可能会注意到,一些变量是分类变量,并且目前没有正确的格式来包含在任何后续的建模中。为了准备这些特性,我们可以对数据中的某些列进行一次性编码。我将使用我们的源特性作为例子:

enconder = LabelEncoder()
source_labels = enconder.fit_transform(anime_df[‘source’])
source_mappings = {index: label for index, label in 
 enumerate(enconder.classes_)}
source_mappings>>>>>> {0: '4-koma manga',
 1: 'Book',
 2: 'Card game',
 3: 'Digital manga',
 4: 'Game',
 5: 'Light novel',
 6: 'Manga',
 7: 'Music',
 8: 'Novel',
 9: 'Original',
 10: 'Other',
 11: 'Picture book',
 12: 'Radio',
 13: 'Unknown',
 14: 'Visual novel',
 15: 'Web manga'}

正如您从我们的变量中看到的,每个源类型都被分配了一个数字变量,可用作我们预测模型的特征变量。对您希望在建模中使用的变量重复此操作,并将它们合并回您的原始数据框:

anime_df[‘source_label’] = source_labels
anime_df[‘type_label’] = type_labels
anime_df[‘title_label’] = title_labels

数据建模

现在我们已经准备好了我们的特征变量,我们准备开始数据建模。在此之前,重要的是在我们的数据框架中可视化变量之间的关系。因为我们希望预测用户评级,所以我们希望了解我们的变量与 rating_x: 的相关程度

plt.figure(figsize=(12,10))
cor = anime_df.corr()
sb.heatmap(cor, annot=True, cmap=plt.cm.Dark2)
plt.show()

查看我们数据中总体分数的分布也很有用:

sb.distplot(anime_df[‘score’], color=”orchid”)

由于我们的结果变量( rating_x )与计算出的任何特征变量几乎没有相关性,因此我创建了一个二元结果测量,它使用平均用户评级分数作为高于或低于平均分数的阈值:

anime_df.rating_x.mean()
anime_df[‘rating_bracket’] = np.where(anime_df[‘rating_x’] >= 6.14, ‘1’, ‘0’)

现在,我们希望将这些数据分成训练、测试和验证部分,相应地将结果度量和特征变量分开。由于我们当前的框架中有大约 800 万行数据,运行模型可能会很耗时;为此,我从 anime_df 数据帧中随机抽取了 250k 的样本:

## Take a random sampleanime_sample = anime_df.sample(n=250000, random_state=1)features = anime_sample[[‘favorites’,’members’, ‘popularity’,’scored_by’,’source_label’, ‘type_label’,’title_label’]].copy()
labels = anime_sample[‘rating_bracket’]## Train — testX_train, X_test, y_train, y_test = train_test_split(features, labels, test_size=0.4, random_state=42)## Validation

X_val, X_test, y_val, y_test = train_test_split(X_test, y_test, test_size=0.5, random_state=42)

随机森林模型

我们要训练的第一个模型是随机森林;你可以在我之前写的 的 博客中读到更多关于训练一个随机森林模型的内容。在运行我们的随机森林模型之前,我们将首先优化两个输入参数,它们是 n_estimatorsmax_depth ,它们分别代表决策树的数量和每棵树的深度:

def print_results(results):
 print(‘BEST PARAMS: {}\n’.format(results.best_params_))means = results.cv_results_[‘mean_test_score’]
 stds = results.cv_results_[‘std_test_score’]
 for mean, std, params in zip(means, stds, results.cv_results_[‘params’]):
 print(‘{} (+/-{}) for {}’.format(round(mean, 3), round(std * 2, 3), params))rf = RandomForestClassifier()
parameters = {
 ‘n_estimators’: [50,100],
 ‘max_depth’: [10,20,None]
}rf_cv = GridSearchCV(rf, parameters, cv=5)
rf_cv.fit(X_train, y_train.values.ravel())print_results(rf_cv)

我们可以将最佳参数设置存储到本地机器,以便在评估结束时用于模型验证:

joblib.dump(rf_cv.best_estimator_, ‘/.../.../.../AnimeRecs/RF_model.pkl’)

现在,我们可以使用优化的参数运行随机森林,并打印模型的准确性:

rf_model = RandomForestClassifier(n_estimators=100, max_depth=20)
rf_model.fit(X_train, y_train)
rf_predicted_values = rf_model.predict(X_test)
score = accuracy_score(y_test,rf_predicted_values)
print(score)>>>> Accuracy: 0.695905

您可以看到,我们的随机森林模型达到了大约 70%的准确率,这表明我们可以使用我们为此分析创建的特征变量以相当高的准确率预测用户评分。在得出这个结论之前,评估哪些变量与模型成功相关是很重要的。我们可以这样看待输入变量的相对重要性:

for name, importance in zip(features.columns, rf_model.feature_importances_):
… print(name, “=”, importance)favorites = 0.2778530464552122
members = 0.19784680037872093
popularity = 0.1759396781482536
scored_by = 0.15560656547263593
source_label = 0.0509643924057434
type_label = 0.04176822827070301
title_label = 0.10002128886873099

以上变量按降序排列,最重要的特性在顶部。您可以看到,我们编码的变量对我们的模型产生的重要性很小,并且在这种情况下可能具有最小的预测能力。就对我们的模型的相对重要性而言,最相关的特征变量是动画内容有多少个收藏夹,这完全是直观的。

我们还可以在随机森林模型中可视化一个单独的决策树,以查看数据是如何传递的:

export_graphviz(tree,
 feature_names=features.columns,
 out_file=’rf_anime_tree.dot’,
 filled=True,
 rounded=True)

前馈多层感知器

我们要训练的第二个模型是多层感知器( MLP ),这是一类前馈神经网络,旨在模拟大脑处理和存储信息的神经生理过程。MLP 通常用于监督学习问题,它们在一组输入-输出对上进行训练,并学习对它们之间的相关性进行建模。想了解更多关于 MLP 的信息,请阅读我之前的一篇文章。

我们将为我们的 MLP 模型优化的超参数是 hidden_layer_sizes ,这是第 I 个隐藏层中的节点数,以及激活,这是隐藏层的激活函数。对于激活功能,我们将确定逻辑激活和 relu 激活之间哪个功能更好:

逻辑:使用 sigmoid 函数(如逻辑回归),返回 f(x) = 1 / (1 + exp(-x))

Relu :整流后的线性单位函数,返回 f(x) = max(0,x)。如果 value 值为正,该函数输出输入值,否则传递一个零

mlp = MLPClassifier()
parameters = {
 ‘hidden_layer_sizes’: [(10,), (50,)],
 ‘activation’: [‘relu’, ‘logistic’]
}
mlp_cv = GridSearchCV(mlp, parameters, cv=5)
mlp_cv.fit(X_train, y_train.values.ravel())
print_results(mlp_cv)

我们可以将最佳参数设置存储到本地机器,以便在评估结束时用于模型验证:

joblib.dump(rf_cv.best_estimator_, ‘/.../.../.../AnimeRecs/MLP_model.pkl’)

现在,我们可以使用优化的参数运行我们的 MLP 模型,并打印我们模型的精确度:

rf_model = RandomForestClassifier(n_estimators=100, max_depth=20)
rf_model.fit(X_train, y_train)
rf_predicted_values = rf_model.predict(X_test)
score = accuracy_score(y_test,rf_predicted_values)
print(score)>>>> Accuracy: 0.6758805

如您所见,该模型达到了大约 68%的准确率,低于我们的随机森林模型。由于 MLP 模型循环遍历数据的方式,在不同的时期/遍数遍历整个训练数据集之后评估模型的性能非常重要。为了评估这一点,我们可以设想模型随时间的验证损失:

loss_values = mlp_model.loss_curve_
mlp_model.score
plt.plot(loss_values)
plt.ylim((0.61,0.640))
plt.axvline(10,0,0.7)
plt.show()

从上图可以看出,10 个时期后,验证损失开始增加,这表明模型过度拟合。减少通过我们的训练数据的完整次数可能是有价值的,从而减少训练我们的模型的时间。然而,值得考虑的是,这可能会降低模型的整体准确性。

模型验证

如前所述,我们对数据进行了分割,这样我们就有了一个验证集,用于在评估的最后阶段对模型进行比较。这些数据对于所使用的两个模型中的任何一个都是完全看不到的,因此可以用作我们训练的模型的性能的强有力的衡量标准。此外,我们还存储了为此目的使用各种超参数的最佳估计值。

以下代码将遍历您存储的最佳估计值:

models = {}
for mdl in [‘MLP’, ‘RF’]:
 models[mdl] = joblib.load(‘/Users/hopkif05/Desktop/AnimeRecs/{}_model.pkl’.format(mdl))
models

下面的代码将创建一个函数来评估和比较所使用的两个模型的准确性。正如所见, model.predict() 函数存在于开始和结束时间函数之间,这意味着我们可以计算每个模型的延迟值,以评估它们计算预测需要多长时间:

def evaluate_model(name, model, features, labels):
    start = time()
    pred = model.predict(features)
    end = time()
    accuracy = round(accuracy_score(labels, pred), 3)
    print('{} -- Accuracy: {} / Latency: {}ms'.format(name,
                                                                                   accuracy,
                                                                                   round((end - start)*1000, 1)))

我们现在可以遍历我们的模型来确定它们的准确性:

for name, mdl in models.items():
    evaluate_model(name, mdl, X_val, y_val)MLP -- Accuracy: 0.683 / Latency: 675.7ms
RF -- Accuracy: 0.713 / Latency: 668.9ms

如上所述,随机森林模型在看不见的验证数据上表现最好。有趣的是,它还具有更短的延迟,这表明 MLP 模型已经被训练了太长时间;如前所述,这会对训练神经网络产生负面影响。

因此,可以总结为,我们的随机森林模型可以使用各种特征变量来预测 MyAnimeList 上的用户评级。考虑到随机森林模型在分类问题上表现良好,如我们的动漫数据中使用的二元结果测量,这一结果并不十分令人惊讶。给定我们为我们的随机森林输出计算的特征重要性,可以进一步确定与我们的随机森林模型的准确性最相关的变量是:收藏夹 (0.28)、成员 (0.20)、流行度 (0.18)和 (0.16)评分。这些发现得到了上面打印的相关矩阵的支持,考虑到这些指标的性质,这些发现并不令人惊讶;因此,与我们的一次性编码变量相比,预计它们会产生相对较高的预测能力。

构建生产就绪的机器学习模型

原文:https://towardsdatascience.com/building-production-ready-machine-learning-models-adc68754bcf4?source=collection_archive---------36-----------------------

了解具有本机 MLOps 的全新横向扩展 RDBMS,它可以轻松开发、管理、部署和治理 ML 模型。

资料来源:Peshkova/Adobe

作者:蒙特·兹韦本和本·爱泼斯坦

随着今天开发的机器学习包的质量,测试和创建模型再容易不过了。数据科学家可以简单地导入他们最喜欢的库,并立即访问数十种前沿算法。但是,创建生产就绪的机器学习模型需要的不仅仅是有效的算法。这需要实验、数据探索和坚实的组织。

数据科学家需要一个环境来自由探索数据,绘制不同的趋势和相关性,而不受数据集大小的限制。借助 Splice Machine 的 MLManager 2.0 平台,构建、测试、实验和将机器学习模型无缝部署到生产中再容易不过了。

以下是 ML Manager 最新版本中提供的数据科学功能的总结。

Jupyter 笔记本

作为 ML Manager 2.0 的一部分,我们已经将 Jupyter 笔记本电脑直接构建到具有 BeakerX 支持的拼接机器平台中。Jupyter 笔记本是数据科学家工作和共享数据以及代码的最受欢迎的工具。它们易于使用,并允许通过将数据科学过程的各个部分分成不同的单元来模块化工作流。

用 Jupyter 处理熊猫数据帧——来源:拼接机

懂得多种语言的

在 ML Manager 2.0 中,我们不仅仅提供对通用 Jupyter 笔记本的访问。有了 ML Manager 2.0,数据科学家拥有了定制的笔记本,并增加了 BeakerX 的功能。我们已经与开源 BeakerX 项目合作,并为 Splice Machine 定制了它。这种定制的一个最重要的特性是多语言编程。这种编程范式允许数据科学家在同一个笔记本中用多种不同的语言编写代码。每个单元格都可以用“魔法”来定义,告诉 Jupyter 如何解释代码。

查看拼接机器上 BeakerX 内置的可用魔法命令—来源:拼接机器

数据科学家可以灵活地使用%%定义整个单元格的语言,或者使用%定义单行的语言。这允许你在同一个笔记本上写 SQL 代码,比如 Python、Scala、Java 或 R 代码。

在同一个 Jupyter 笔记本上运行不同语言的多个单元——来源:拼接机

在分离的多语言编程之上,我们还允许交叉内核变量共享。这意味着您可以用一种语言定义一个变量,并使用底层的 BeakerX 变量与另一种语言共享它。这使得数据科学家能够前所未有地访问他们的 SQL 数据。例如:

在 SQL 和 Python 之间直接共享变量—来源:Splice Machine

BeakerX 还有许多其他的优秀特性,这些特性直接内置在 Splice Machine 中。

形象化

Jupyter 笔记本电脑还配有出色的内置可视化工具,例如用于 2D 和 3D 绘图的 matplotlib 和 plotly:

在 Python 单元中查看 plotly 3D 图形-来源:Splice Machine

数据科学家还可以访问交互式 Pandas 表,您可以在其中对结果进行过滤和排序,并直接向表中添加可视化效果:

将结果直接过滤和分类到 Pandas 表中——来源:拼接机

原生 Spark 数据源— PySpliceContext

数据科学中的一个严重问题是过度拟合。过度拟合可能由许多不同的因素造成。这些问题包括创建的模型对训练数据过于精细,在数据集中进行有偏见的分割,甚至为问题选择错误的算法。然而,所有这些问题都可以克服。

数据科学团队面临的另一个挑战是数据规模问题。最难克服的偏差形式是倾斜数据集。许多数据科学团队遇到了在数据子集上构建模型,然后在整个数据集上训练模型的问题。这有可能给模型带来巨大的偏差。

因此,根据全部可用数据训练模型至关重要,而有了拼接机,这不再是一个问题。因为我们的 Jupyter 笔记本电脑与我们的数据库部署在同一位置,所以数据科学家可以直接使用横向扩展功能,即时访问他们的全部数据集。更重要的是,数据工程师和数据科学家使用与应用程序开发人员不同的编程模式。他们操纵火花和熊猫数据帧。现在 Splice 也提供了这种模式和完整的 CRUD API。使用我们的原生 Spark 数据源,您可以查询整个表,无论大小如何,并立即将其作为 Spark 数据帧使用。在几毫秒内,只需一次 api 调用,您就可以将表中的所有数据移动到 Spark Dataframe 中,并在屏幕上显示出来。更强大的是能够将数据帧插入到 Splice 中,并具有完全的 ACID 事务性。这种级别的数据访问和操作使得模型构建和数据探索比以往任何时候都更容易。

想象一下,能够以事务方式更新实时特征库,供多个模型实时使用。例如,这些特征存储可以有数万列跟踪客户行为模式的数据,这些数据可以立即转化为数据帧中的特征向量,以供模型管道测试。想象一下能够实时更新这个特征库。然后,模型会即时测试特征。呼叫中心呼叫前几秒钟发生了什么交易?放弃前添加到订单中的最后一个行项目是什么?一个网站的最后一次点击是什么?有了实时特性库和原生 Spark 数据源,ML 变得更有价值。

使用拼接原生数据源将完整的表摄取到 Spark 数据帧中—来源:拼接机器

建模简易性

在拼接机器平台上建模也同样简单。使用 Spark、Scikit-learn、TensorFlow、H2O 和 PyTorch,您可以在您最喜欢的库中工作。对于 Scikit-learn,我们可以利用 skdist 在 Spark 上同时训练许多 scikit-learn 模型(想想交叉验证模型)。未来的工作将包括 TensorflowOnSpark。

MLOps

所有这些伟大的特性都带来了很多复杂性。现在,您可以完全访问您的数据和建模工作,您如何围绕这一点建立一个团队呢?你如何组织你的工作?你如何维护治理?你如何部署你的模型?这些问题以及其他许多问题让术语 MLOps 进入了人们的视野。MLOps 是为生产就绪型团队实施数据科学工作流的流程。数据科学不同于其他工程工作,因为它是一个实验过程,需要不同的结构和组织方法。

第一遍

使用典型的数据科学家电子表格 —来源:拼接机

如果你曾经见过这样的电子表格,你就知道它的创建有多恐怖。这是一个典型的数据科学运行手册电子表格。不同的实验、运行、参数、数据集、指标以及数据科学实验所需的一切都存储在一个 excel 电子表格中。

这是任何严肃的数据科学团队都无法使用的。创建它需要很长时间,每个新项目都需要重新设计,而且很容易出现用户错误,因为不同的版本作为附件在人们的收件箱里飞来飞去。作为数据科学家,您可能会看着这张工作表,并问“如果我想调整另一个参数,或者尝试随机森林,那将如何适应这张电子表格?”你的恐惧是正确的,这些问题的答案是“你不能”和“它不会”这就是 MLManager 的用武之地

MLManager(和 MLFlow)

干净灵活的 MLFlow 用户界面可以跟踪您的实验,无论多么复杂——来源:Splice Machine

Splice Machine 的 MLManager 采用了流行的开源项目 MLFlow,并添加了我们认为“完成 ML 生命周期”的功能。使用 MLFlow,您可以轻松、动态地跟踪与您的模型尝试相关的一切一切:参数、度量、训练时间、运行名称、模型类型、图像等工件(想想 AUC 图表),甚至是序列化模型。使用 Splice Machine 的 MLManager,所有这些指标、参数和工件都直接存储在数据库中,无需任何外部存储机制。

您可以轻松地构建大量模型,跟踪您需要的一切,并在 MLFlow UI 中对它们进行数字或视觉比较。例如,上面我们可以看到在一个实验中创建的许多不同运行的指标,下面我们深入研究其中的三个运行,看看我们的随机森林中的树的数量如何与我们的 F1:

用于比较运行和指标的直观用户界面—来源:拼接机

数据科学家甚至可以将他们用来构建模型的笔记本发布到 Github gists,以便共享代码、笔记和片段,供同行审查和透明。给定一个 MLFlow run_id,您可以将模型追溯到它的起源。对于生产就绪的数据科学团队来说,这对于保持对部署哪些模型以及何时部署的控制至关重要。

我们简单明了的 API 使这变得非常容易和可重用,无论您采用什么样的建模过程,只需几个函数调用就可以为您提供完全的可追溯性。我们提供的三个关键功能是:

  1. log _ feature _ transformations 记录特征向量中的每个特征为达到其最终状态而经历的所有单独转换(即一次性编码、标准化、标准化等)
  2. log _ Pipeline _ stages——记录 Spark 管道的所有阶段(如过采样、编码、字符串索引、向量组装、建模等)
  3. log _ model _ params 记录模型的所有参数和超参数
  4. log _ metrics 记录由我们的内置评估器类确定的模型的所有指标

一旦对模型进行了比较和测试,并且您的团队已经准备好进行部署,下一个主要障碍就要出现了。模型部署是数据科学世界中讨论最多的话题,因为它很复杂,不可概括,并且难以监督。像 AzureML 和 Sagemaker 这样的流行部署机制很难构建到您的管道中,并且很难维护治理。

谁部署了模型?谁可以给模特打电话?在过去的 24 小时里谁给模特打过电话?模特表现如何?如何将新模型集成到应用程序中?如果出于安全考虑,我们不想部署到云中,该怎么办?

这些问题很重要,而且没有直接的答案。借助 Splice Machine 的 MLManager,我们通过在 DB 模型部署中使用来消除复杂性。我们的平台允许您将模型直接部署到数据库中,因此每次在表中插入新行时,您的模型都会立即自动运行,预测会被存储,并跟踪到正在使用的模型。最重要的是,它的部署和利用速度都非常快。部署时间不到 10 秒(相比之下,Sagemaker/Azure ML 部署需要将近 30 分钟)。所有型号的触发器都完全符合 ACID 标准。

在不到 6 秒的时间内将生产模型直接部署到数据库表中— 来源:拼接机

将行插入表中,并立即看到与每一行插入链接的预测,以实现全面的 ACID AI 治理——来源:拼接机

在 DB 模型部署中,安全性和治理对于您的 DBA 来说也很容易学习,因为您的模型预测就在一个表中,就像数据库中的任何其他表一样。想要撤销对模型的访问权限吗?撤销对表的访问。想要收集模型的统计数据吗?只是一些 SQL 查询。想在应用程序中用一个模型替换另一个模型吗?更改查询中的表名。没有学习曲线,因为没有新技术在使用。此外,由于这只是持久数据,您也可以围绕该模型设计微服务

这是怎么做到的?我们的 MLManager 确定模型管道的类型和结构,创建特定于数据集和模型的表和触发器,并将它们部署到您选择的表中。触发器是动态生成的,当新记录插入到所选的表中时,它会立即运行。触发器调用一个生成的存储过程,该存储过程反序列化模型并将其应用于新记录,并将预测写入预测表。数据科学家和数据工程师不必学习 RDBMS 触发器和存储过程,因为它们是自动生成的。例如,下面是为模型部署生成的触发器和函数:

为直接在数据库的表中运行模型而创建的触发器—来源:拼接机器

然而,如果您的团队仍然希望部署到 Sagemaker 或 AzureML,MLManager 也支持这一点,包括部署 UI 和 API 调用。

将模型部署到 AWS Sagemaker 或 AzureML 的一行代码——来源:Splice Machine

用于将模型部署到 AWS Sagemaker 或 AzureML 而无需代码的 job tracker UI——来源:Splice Machine

如今,人工智能在商业成功方面变得越来越谨慎。没有它,你的公司将会被更敏捷、市场准备更充分的竞争对手甩在后面。由于糟糕的模型开发环境和部署时间的严重风险和影响,注入人工智能已经成为成功的最大障碍。您不能冒险将一个糟糕的模型投入到正在做出关键任务决策的生产中。然而,有了 Splice Machine,治理被完全集成,实验比以往任何时候都更容易,幕后管道也成为过去。

例如,考虑一家保险公司。当监管机构开始质疑你在承销中使用的模式时,Splice Machine ML Manager 如何帮助你遵守规定?您可以通过时间旅行回到您训练模型时存在的数据库的“虚拟快照”,并显示进入模型的要素以及当时这些要素上的数据的统计分布。Splice 使数据科学家能够在培训时将我们的 MVCC 事务引擎的快照隔离时间戳作为元数据记录在 MLFlow 中。通过时间旅行,您可以在几分钟内证明您的合规性。

借助我们的单点治理架构构建您可以信赖的模型,并访问完整、公正的数据,轻松、快速地部署模型,并在您的任务关键型应用程序中利用它们,没有延迟,并且与所有其他表具有相同的 ACID 合规性。无缝集成您的模型,无需学习任何新的架构或平台,您的模型只是一个(超级)表格。使用与监视任何其他表相同的技术来监视您的模型,当新模型准备好替换它时,可以立即重新部署。不要再为您想要现代化的每个应用程序重复创建轮子。

为了亲身体验 Splice Machine 和 ML Manager 2.0,观看演示视频,该视频强调了 Splice Machine 的强大功能,以及它如何帮助您构建生产就绪的 ML 模型。

在 Ubuntu 20.04 LTS Focal Fossa 上从源代码构建 python

原文:https://towardsdatascience.com/building-python-from-source-on-ubuntu-20-04-2ed29eec152b?source=collection_archive---------7-----------------------

了解如何在基于 Linux 的操作系统上从源代码构建您最喜欢的语言。

在你最喜欢的操作系统上构建你最喜欢的语言

除了面临依赖问题的用户之外,这个博客是为任何想尝试在任何基于 Linux 的操作系统上从源代码安装 python 的人准备的

如果你已经升级了你的操作系统,并且想知道为什么一些在 Ubuntu 18.04 上很容易安装的库不能在 20.04 上安装。当我的一个开发环境需要一个库,而我试图使用内置 python 来安装这个库时,我遇到了同样的问题。不幸的是,我面临着依赖问题。

这背后的原因是 Ubuntu 20.04 带有内置的 python 版本 3.8.2,而 Ubuntu 18.04 带有 Python 3.6.x

您可能会尝试安装不同的依赖于 Python 3.6 的库。别担心,这很容易..2..3

让我们从一个特定的 python 版本开始,我将在这篇博客中使用 Python 3.6.9,因为我是在以前的系统上工作的。

重要的事情先来

安装构建库所需的基本依赖项

sudo apt-get update
sudo apt-get install -y build-essential checkinstall 

sudo apt-get install libreadline-gplv2-dev libncursesw5-dev libssl-dev libsqlite3-dev tk-dev libgdbm-dev libc6-dev libbz2-dev

让我们建造

从改变目录开始,从 python 下载源代码。

我将从 /usr/src 安装它,但是你可以使用你选择的任何位置

cd /usr/src
sudo wget https://www.python.org/ftp/python/3.6.9/Python-3.6.9.tgz

正在提取下载的包

sudo tar xzf Python-3.6.9.tgz

编译源

cd Python-3.6.9
sudo ./configure --enable-optimizations

在这里,我们添加了一个新标志--enable-optimizations
,它设置了默认的 make targets 来启用 Profile Guided Optimization (PGO ),并可用于在某些平台上自动启用链接时间优化(LTO)。

关于--enable-optimizations的更多详情,请参见 此处的

我们都在等待的一步

构建 python

sudo make altinstall

当我们可以使用install时,为什么要使用altinstall

原因:在 Unix 和 Mac 系统上,如果你打算安装多个版本的 python,你必须注意你的主要 Python 可执行文件不会被不同版本的安装覆盖
altinstall构建特定版本的 python 而不覆盖你的默认版本,这样你就可以使用多个版本的 python。

注:根据设备配置,此步骤可能需要一些时间

最终输出将是这样的

python 版本将用后缀python3.6构建

验证安装

python3.6 --version

注意我用了python3.6来代替。如果你尝试运行python3 --version,它会显示内置的 python 版本,从 Ubuntu 20.04 开始是 3.8

注意区别

用一个命令构建一切

我为此创建了一个 shell 脚本,您可以从这里下载脚本文件。

[## novas ush/build-python-from-source

用于从源代码构建 python 的 Shell 脚本...

github.com](https://github.com/novasush/build-python-from-source)

升级 pip(可选)

为 pip 安装依赖关系

sudo apt-get install -y python3-distutils python3-testresources

下载 get-pip.py 并运行

cd ~/
wget [https://bootstrap.pypa.io/get-pip.py](https://bootstrap.pypa.io/get-pip.py)
sudo python3.6 get-pip.py

就像python3.6这样使用前缀pip3.6进行安装

pip3.6 安装包

奖金

如果你对每次使用python3.6pip3.6感到厌烦,就使用下面提到的任何一种配置。

使用别名

alias py36=python3.6
alias pip36=pip3.6

python3.6 和 pip3.6 的别名

别名命令的限制:
要么在每次运行 python 或 pip 之前运行该命令,要么在~/.bashrc的底部添加这些行,但它将仅在用户级别运行。
解决这个问题的最佳方案是使用update-alternatives

使用更新替代方案

检查 python 路径,将其添加到更新替代配置中

which python3.6

我的路径是/usr/local/bin/python 3.6

添加更新替代配置的路径

sudo update-alternatives --install /usr/bin/python python /usr/local/bin/python3.6 0

这是所有的乡亲

用 Python 框架构建推荐系统引擎

原文:https://towardsdatascience.com/building-recommender-systems-engines-with-a-python-framework-5b4d29925c28?source=collection_archive---------18-----------------------

理解如何使用和开发案例推荐器的实用指南

马丁·范·登·霍维尔在 Unsplash 上的照片

科学界已经提出了许多推荐系统的框架,包括不同的编程语言,如 Java、C#、Python 等。然而,它们中的大多数缺乏包含聚类和集成方法的集成环境,这些方法能够提高推荐准确度。一方面,聚类可以支持开发者预处理他们的数据,以优化或扩展推荐算法。另一方面,集成方法是以个性化方式组合不同类型数据的有效工具。

在本文中,我描述了一个名为案例推荐器的 python 框架,它包含各种基于内容的协作推荐算法,以及用于组合多种算法和数据源的集成方法。此外,它还提供了一套流行的评价方法和指标,用于评分预测和项目推荐。

语境化

传统上,推荐系统使用过滤技术和机器学习信息从用户简档的表示中产生适合用户兴趣的推荐。然而,其他技术,如神经网络、贝叶斯网络和关联规则,也在过滤过程中使用。目前使用最多的过滤类型是:基于内容的(CBF),负责基于元素的内容过滤来选择信息,例如,由于包含不想要的单词而被过滤为垃圾的电子邮件消息;协同过滤(CF),基于人与人之间的关系以及他们对要过滤的信息的主观看法。基于发件人和收件人之间的关系选择电子邮件就是一个例子。此外,还有一种结合了基于内容和协同过滤方法的混合方法。

案例推荐器

案例推荐器目前正在重写,以支持使用已知 Python 科学库的优化计算。除了使用元数据和外部信息,我还开发和改进了允许开发人员操作文件、预测和评估模型、计算用户或项目之间的相似性(不相似)的类。该框架现在在 Python 3 中实现,它解决了推荐系统中的两个常见场景:评级预测和项目推荐,在几个推荐策略中使用显式、隐式或两种类型的反馈。要在 Mac OS X / Linux/ Windows 上安装 Case Recommender,以下两个命令中的一个可能对开发者和用户有用:

easy_install CaseRecommender

或者:

pip install CaseRecommender

在设计我们的框架时,重要的特性是支持大规模的推荐计算,容易为不同类型的过滤和场景创建和扩展算法。另一个特性是支持稀疏和大型数据集,尽可能减少存储数据和中间结果的开销。开发人员可以选择使用一种可用的推荐算法,或者使用一种可用的集成技术组合多个推荐,或者使用 BaseRatingPredictionBaseItemRecommendation 类开发自己的算法。到当前版本为止,框架中可用的算法如下表所示:

输入数据

该框架允许开发者处理不同的数据集,而不必开发他们自己的程序来执行推荐器功能。算法的输入要求数据采用简单的文本格式:

用户标识项目标识反馈

其中 user_id 和 item_id 是分别表示用户和项目 id 的整数,而 feedback 是表示用户有多喜欢某个项目或二元交互的数字。值之间的分隔符可以是空格、制表符或逗号。如果超过三列,则忽略所有附加列。例如,下面是来自 ML100k 数据集的数据示例:

推荐系统的数据集

在学习期间,我还为遥感建立了一个高质量的以主题为中心的公共数据源库。它们是从栈溢出、文章、推荐网站和学术实验中收集整理的。存储库中提供的大多数数据集都是免费的,有开放的源链接,然而,有些不是,你需要请求许可才能使用或引用作者的工作。访问此链接获取这些数据。

[## 推荐系统数据集

这是一个用于推荐系统(RS)的以主题为中心的高质量公共数据源的存储库。他们是…

github.com](https://github.com/caserec/Datasets-for-Recommender-Systems)

使用

对于使用折叠交叉验证划分数据集:

from caserec.utils.split_database import SplitDatabaseSplitDatabase(input_file=dataset, dir_folds=dir_path, n_splits=10).k_fold_cross_validation()

运行项目推荐算法(例如:ItemKNN)

from caserec.recommenders.item_recommendation.itemknn import ItemKNNItemKNN(train_file, test_file).compute()

运行评级预测算法(例如:ItemKNN)

from caserec.recommenders.rating_prediction.itemknn import ItemKNNItemKNN(train_file, test_file).compute()

评估排名(预测@N、回忆@N、NDCG @N、地图@ N 和地图总计)

from caserec.evaluation.item_recommendation import ItemRecommendationEvaluation
 ItemRecommendationEvaluation().evaluate_with_files(predictions_file, test_file)

评估排名(梅和 RMSE)

from caserec.evaluation.rating_prediction import RatingPredictionEvaluationRatingPredictionEvaluation().evaluate_with_files(predictions_file, test_file)

在折叠交叉验证方法中运行 ItemKNN

from caserec.recommenders.item_recommendation.itemknn import ItemKNN
from caserec.utils.cross_validation import CrossValidation## Cross Validation
recommender = ItemKNN(as_binary=True)
CrossValidation(input_file=db, recommender=recommender, dir_folds=folds_path, header=1, k_folds=5).compute()

更多例子可以在这个链接中找到。

结论

案例推荐的目标是集成和促进不同领域的新推荐技术的实验和开发。我们的框架包含一个推荐引擎,它包含基于内容的、协作的和混合的过滤方法,用于评级预测和项目推荐场景。此外,该框架包含集成和聚类算法、验证和评估指标,以提高和衡量推荐的质量。

未来的版本计划将包括更多的功能(并行处理技术和新算法)和一个评估工具,该工具有几个图和图形,以帮助开发人员更好地了解他们的推荐算法的行为。

有用的链接

[## 案例推荐/案例推荐者

案例推荐器是许多流行的推荐算法的 Python 实现,包括隐式推荐算法和…

github.com](https://github.com/caserec/CaseRecommender) [## MyMediaLite 推荐系统库

MyMediaLite 是公共语言运行时(CLR,通常称为。网)。它解决了…

www.mymedialite.net](http://www.mymedialite.net/) [## 案例推荐|第 12 届 ACM 推荐系统会议录

本文提出了一个改进的基于 Python 的开源推荐框架&案例推荐器

dl.acm.org](https://dl.acm.org/doi/10.1145/3240323.3241611)

在 Google Cloud 上用 Dockers 构建无服务器 Python 数据 API

原文:https://towardsdatascience.com/building-serverless-python-data-apis-with-dockers-on-google-cloud-24d4f15cf81?source=collection_archive---------33-----------------------

在本文中,我将向您展示一种简单的方法,可以在几分钟内构建几个数据 API 来利用来自 BigQuery 数据集的数据。这些 API 将通过 dockers 部署,dockers 使用一个名为 Cloud Run 的 GCP 无服务器服务。

体系结构

体系结构

背后的想法是使用无服务器组件。首先,让我们了解这些服务及其在架构中的用途。

  • 云运行:云运行是一个完全托管的计算平台,可以自动扩展你的无状态容器[ 云运行文档 ]。它将处理所有的 API 请求,因为它是完全托管的,我们不需要担心伸缩性。为了实现第一个目标,必须部署 docker。
  • 云壳:云壳为您提供直接从浏览器【云壳 Doc】命令行访问您的云资源。总是需要一个开发环境,在这种情况下,是云壳。python 代码将在这里开发。
  • 云构建:云构建是一种在谷歌云平台基础设施上执行构建的服务。云构建根据您的规范执行构建,并生成 Docker 容器[ Doc ]之类的工件。当我们准备好构建 docker 时,我们将调用 Cloud Build 来完成这项工作,默认情况下,这个 docker 将发布在 GCP 容器注册中心。
  • 容器注册:在 Google 云平台上提供安全、私有的 Docker 图像存储[ Doc ]。在这个地方将托管我们的 docker,准备由云构建调用。
  • BigQuery: BigQuery 是 Google 完全托管的、Pb 级、低成本的分析数据仓库。BigQuery 是 NoOps——不需要管理基础设施,也不需要数据库管理员。BigQuery 是我们的数据仓库,所以 API 需要的所有数据都在这里。

获取数据

对于这个项目,我们将使用一个名为 covid19_ecdcBigQuery 公共数据集,其中包含按国家和日期分列的确诊病例和死亡数据。

资料组

创建数据集并查看

让我们创建一个 BigQuery 数据集和一个视图或一个物化视图来获取项目中的数据。

创建视图和数据集

数据

做一个简单的探索,我们可以确定模式和数据。对于这个项目,我们的 API 将提取

  • 所有的数据
  • 特定日期来自所有国家的案例
  • 特定国家报告的所有病例

使用的表格

样板工程

用 python 构建 REST API 并不难。为了简单起见,我发布了一个包含所有文件的 GitHub 项目。

[## antoniocachuan/data API-big query

用 python 构建一个无服务器数据 API,从 big query-antoniocachuan/data API-big query 获取数据

github.com](https://github.com/antoniocachuan/dataapi-bigquery)

让我们了解一下文件

项目文件

App.py

主文件。Flask base 项目,这包括其他库,用 flask_sqlalchemyflask_marshmallow 制作一个更简单的 API。

app.py

需要修改的代码行包括您的 GCP ID 项目、数据集和您的 GCP 凭据。了解更多关于如何获得 GCP 证书的信息。

在项目中更新此变量

Dockerfile

一个Dockerfile是一个文本文档,它包含用户可以在命令行上调用的所有命令,以组合一个图像。

这个项目使用一个瘦 python 映像,安装所有需要的库,最后用 gunicorn 启动一个 HTTP 服务器来处理 API 请求。

Dockerfile 文件

Requirements.txt

项目所需的所有 python 库的列表。

设计 API 请求

对象关系映射

我们使用 SQLAlchemy 作为 ORM,将 Python 类转换成 BigQuery 表。Marshmallow 用于使对象序列化和反序列化更容易。

要使这段代码工作,定义类名等于 BigQuery 表是很重要的(默认情况下,内部的 Covid 将更改为 covid 以在 BigQuery 上搜索该表)。如果您有其他表名,请在第 23 行使用__table__='YOUR_TABLE_NAME'

让我们为你定义所有你想被调用的列。

请求

为了实现定义的三个数据需求,我们编写了 3 个函数。

首先,我们的数据 API 返回 BigQuery 表中的所有数据。

获取所有国家数据

函数get_day()接收像2020-04-04这样的参数,并返回该特定日期来自所有国家的数据。

特定一天所有国家的数据

函数country_detail()PE一样接收国家地理编码,并从特定国家返回数据。

使用云构建构建 docker 映像

每次我们需要更新我们的项目,你需要建立一个新版本的 docker 图像。存在许多替代方案,这里我们使用 Cloud Build 工具来构建 docker 映像并将其发布到容器注册表。别忘了定义你的 DOCKER_IMAGE_NAME

gcloud builds submit — tag gcr.io/YOUR_PROJECT_ID/DOCKER_IMAGE_NAME

我在 Cloud Shell 上运行它,并记住与您的 Dockerfile 位于同一层,并且您的 project_id 必须被解决(您需要看到黄色的字母)

构建 Docker 映像

如果一切顺利,你可以看到你的 Docker 图片发布。

集装箱登记处

使用云运行部署 docker

最后一步是在 Web 上部署 Docker 映像!Cloud Run 会负责一切,决定一个 SERVICENAME ,更新对你 Docker 镜像的引用。

gcloud run deploy SERVICENAME --image gcr.io/YOUR_PROJECT_ID/DOCKER_IMAGE_NAME --platform managed --region us-central1 --allow-unauthenticated

最后,您获得一个公共 URL 来开始获取数据!

数据 API 工作

是时候通过浏览器获取发出请求的数据了。

所有国家的数据

https://datahackservice-xx-uc.a.run.app/countries

特定日期的数据

https://datahackservice-xx-uc.a.run.app/day/2020-05-01

特定国家的数据

https://datahackservice-xx-uc.a.run.app/country/PE

结论和未来工作

本文向您展示了使用 Python 和无服务器产品(如 Cloud Run 和 BigQuery)开发数据 API 是多么容易。

有了这个 API,我们就能构建出色的数据可视化,数据 API 强大的一个很好的例子来自《世界银行评论》Sébastien Pierre 的文章这里

特别感谢 Martin OmanderSagar Chand 提供的优秀知识库和文章帮助我开发了这篇文章。

PS 如果你有什么问题,或者有什么有趣的数据想法,可以在 TwitterLinkedIn 上找我。另外,如果你正在考虑参加谷歌云认证,我写了一篇技术文章描述我的经验和建议。

[## 我如何完成谷歌云认证挑战?

在按照谷歌推荐的 12 周准备后,我通过了云工程师助理考试,在这里…

towardsdatascience.com](/how-i-could-achieve-the-google-cloud-certification-challenge-6f07a2993197)

使用自动旋转构建最先进的机器学习模型

原文:https://towardsdatascience.com/building-state-of-art-machine-learning-models-with-autogluon-9f19f61048fb?source=collection_archive---------27-----------------------

照片由凯尔·布鲁姆Unsplash 拍摄

自动登录和自动

AutoGluon 是由 AWS 构建的开源 AutoML 框架,它支持简单易用且易于扩展的 AutoML。它使您能够在没有专业知识的情况下,通过利用最先进的深度学习技术来实现最先进的预测准确性。这也是一种快速的方式来原型化您可以从您的数据集获得什么,以及获得您的机器学习的初始基线。AutoGluon 目前支持处理表格数据、文本预测、图像分类和对象检测。

AutoML 框架的存在是为了降低机器学习的入门门槛。他们负责繁重的任务,如数据预处理、特征工程、算法选择和超参数调整。这意味着,给定一个数据集和一个机器学习问题,继续用不同的超参数组合训练不同的模型,直到找到模型和超参数的最佳组合——也称为 CASH(组合算法/超参数调整)。现有的 AutoML 框架包括 SageMaker Autopilot、Auto-WEKA 和 Auto-sklearn。

AutoGluon 不同于其他(传统)AutoML 框架,它比 CASH(组合算法/超参数调优)做得更多。

集成机器学习和堆叠

在深入研究旋翼飞行器之前,重温一下系综机器学习和堆叠是很有用的。集成学习是一种并行训练许多(有目的地)弱模型来解决同一问题的机器技术。集成由一组单独训练的分类器组成,例如神经网络或决策树,当对新实例进行分类时,它们的预测被组合。这种机器学习技术背后的基本思想是许多模型比少数模型好,不同学习的模型可以提高准确性,即使它们在孤立状态下表现较差。

在大多数情况下,选择单个基本算法来构建多个模型,然后对其结果进行汇总。这也被称为 同质 集成学习方法,就像随机森林算法是最常见和最流行的同质集成学习技术之一,其中多棵树被训练来预测同一问题,然后在它们之间进行多数投票。同类方法的其他例子包括装袋、旋转森林、随机子空间等。

相比之下, 异构 方法涉及使用不同的机器学习基础算法,如决策树、人工神经网络等,来创建用于集成学习的模型。 堆叠 是一种常见的异构集成学习技术。

这个表这里列出了同构和异构机器学习的例子。

AutoGluon 使用了一个多层堆栈集合,我们接下来将研究它是如何工作的。

自动旋转是如何工作的

AutoGluon 在监督机器学习域中运行。这意味着您需要有用于训练的带标签的输入数据。AutoGluon 负责预处理、特征工程,并根据您试图解决的机器学习问题生成模型。

AutoML 的主要部分依赖于超参数调整来生成和选择最佳模型。超参数调整涉及为提供最佳模型的机器学习算法找到超参数的最佳组合。最佳参数集的搜索策略基于随机搜索、网格搜索或贝叶斯优化(SageMaker 使用的方法)。

然而,超参数调谐存在局限性。超参数调优效率低、耗时,而且由于不是所有调优的模型最终都会被使用,因此会造成大量开销浪费。最后,还存在过度拟合验证(保持)数据的风险,因为每次运行调优过程时,都会检查验证数据集,最终会过度拟合验证数据集。

AutoGluon 与其他 AutoML 框架的一个关键区别是,AutoGluon 使用(几乎)每一个经过训练的模型来生成最终预测(而不是在超参数调整后选择最佳候选模型)。

记忆和状态

自动增长是内存感知的,它确保训练的模型不会超过它可用的内存资源。

AutoGluon 是状态感知的,它预计模型在训练期间会失败或超时,并优雅地跳过失败的模型,继续下一个模型。只要你生成了一个成功的模型,自动旋转就可以开始了。

AutoGluon 依赖于多层堆栈集成等策略。它自动进行 k-fold bagging 和 out-of-fold 预测生成,以确保没有过度拟合。具体来说,它利用了现代深度学习技术,也不需要任何数据预处理。

行动中的自动旋转

自动增长也支持文本和图像,但是对于这篇文章,我们主要关注自动增长表格。分类和回归的监督机器学习问题的自动增长表格工作。您可以预先指定问题的类型,或者 AutoGluon 会根据您的数据集自动选择一个问题。

数据集

对于数据集,我们使用来自 Kaggle 的流行、开源的 Titanic 数据集。该数据集包含训练数据,其中包括皇家邮轮泰坦尼克号上 851 名乘客的标记数据,以及他们是否在灾难中幸存。该数据集还包括一个测试集,其中有 418 名乘客,但没有标签,即他们是否幸存。

图片来自维基百科

挑战在于根据包括的特征如姓名、年龄、性别、社会经济阶层等来预测乘客是否幸存。

设置

AutoGluon 安装非常简单,只需几行代码

培养

为了开始训练,我们首先从 AutoGluon 导入 TabularPrediction,然后加载数据。AutoGluon 目前可以对已经作为 pandas DataFrames 加载到 Python 中的数据表进行操作,或者对那些存储在 CSV 格式或 Parquet 格式的文件中的数据表进行操作。

一旦您加载了数据,您就可以立即开始训练,您所需要做的就是指向训练数据并指定您想要预测的列的名称。

训练从.fit()法开始

您可以选择指定希望培训运行的时间,AutoGluon 将在该时间内自动结束所有培训作业。

eval_metric参数允许您指定 AutoGluon 将用于验证模型的评估指标。默认是accuracy

设置auto_stack = True允许 AutoGluon 管理它将自动创建的堆栈数量。您可以通过stack_ensemble_levels参数选择指定您想要的堆叠数量。

presets参数允许您选择想要生成的模型类型,例如,如果延迟和时间不是一个约束条件的话presets='best_quality通常会生成更精确的模型。另一方面,如果您知道延迟将是一个约束,那么您可以设置presets=[‘good_quality_faster_inference_only_refit’, ‘optimize_for_deployment’ 来生成针对部署和推断更优化的模型。

训练开始后,AutoGluon 将在训练的不同阶段开始记录消息。

培训日志(图片由作者提供)

随着培训的进行,AutoGluon 还将记录其生成的各种模型的评估分数。

模型分数(图片由作者提供)

这里需要注意的是,除非您明确需要指定一个验证数据集,否则您应该直接将所有训练数据发送到 AutoGluon。这允许 AutoGluon 以有效的方式自动选择数据的随机训练/验证分割。

训练完成后,您可以开始使用.predict()功能进行推理。

在上面的例子中,我们只使用默认设置来训练 Titanic 数据集。

我们取得的成绩是最先进的。准确率为 ~78 ,在 Kaggle 的比赛中名列前 8%-10% 。

如果您想使用一个或一些模型,该怎么办

默认情况下,auto glon 会自动选择最佳的多层堆栈集合来运行您的预测,但是,您也可以通过生成一个带有单行代码的排行榜来获得 auto glon 生成的所有模型及其特定性能指标的列表:predictor.leaderboard(extra_info=True, silent=True)

排行榜(作者图片)

除了性能指标,排行榜还显示了每个模型/堆栈的训练和推理时间,以及顺序、祖先、后代等。以及其他信息。

使用此排行榜,您只需指定索引号,即可选择想要运行的特定组合。

例如,在上面的代码中,我们使用了系综的堆栈 19。

超参数调优呢

与您使用其他机器学习框架的经历相反,您可能不需要使用 AutoGluon 进行任何超参数调整。在大多数情况下,通过设置auto_stack = True或手动指定 stack_levels 和num_bagging_folds可以获得最佳精度

然而,AutoGluon 支持通过hp_tune = True参数进行超参数调整。当您启用超参数调整时,AutoGluon 将仅使用您指定了超参数设置的基本算法创建模型。它将跳过其余部分。

在上面的示例代码中,AutoGluon 将训练神经网络和各种基于树的模型,并在指定的搜索空间内调整每个模型的超参数。

结束语

虽然 AutoGluon 可以直接构建最先进的机器学习模型,但我发现它和我的新基线方法一样有用。

虽然 auto glon 会进行数据预处理和特征工程(而且做得非常好),但是您会发现,如果在使用 auto glon 进行训练之前对数据进行预处理和特征工程,您可以获得更好的性能。总的来说,更好的数据几乎总是导致更好的模型。

附加阅读

构建 Tableau 服务器影响分析报告:为什么和如何

原文:https://towardsdatascience.com/building-tableau-server-impact-analysis-reports-why-and-how-191be0ce5015?source=collection_archive---------27-----------------------

TABLEAU REST API: TABLEAU-API-LIB 教程

一个关注通过构建交互式可视化工具来跟踪数据谱系,从而提高团队生产力的系列

后台数据库的变化会波及到你的场景环境(图片由张凯夫Unsplash 上拍摄)

任何 Tableau 环境的支柱都是您输入的数据。俗话说:垃圾进,垃圾出。而您的许多 Tableau 资产(工作簿、数据源等。)代表您的数据生态系统的前端,当后端出现故障时,一切都完了。您的视觉效果是最终用户看到的和与之交互的内容,但这只是冰山一角。

对于许多使用 Tableau 的团队来说,每天都以掷骰子开始。他们将工作簿连接到数据库表,设置提取刷新计划,并希望明天一切都像今天一样顺利。但是希望不是一个好的策略。

在管理 Tableau 服务器环境的上下文中,一个好的策略是确切地知道数据管道中的变化将如何影响 Tableau 工作簿和数据源。在许多组织中,这种意识是存在的——但它往往是非正式的部落知识。

更好的策略是构建交互式 Tableau 工作簿,当您的数据团队对表和模式进行更改时,可以参考这些工作簿,这样任何人都可以准确地跟踪哪些内容和团队将受到影响。换句话说,我们对抗后端更改的最佳武器是构建一些交互式的东西,让我们只需点击一个按钮,就可以可视化数据库更改的影响。

本文是关于如何在 Tableau 中构建影响分析报告的系列文章的第一篇。在这里,我们将概述我们可以获得什么样的信息,概述我们如何利用相关数据,并为一系列教程做好准备,这些教程将介绍如何使用 Tableau 生态系统中的可用工具,将 Tableau 环境中的元数据转化为有价值的资产,从而提高所有 Tableau 相关工作流和团队的工作效率。

在接下来的几个星期里,这些教程将会被发布,链接也会被添加到这里。我们可以把这篇文章看作是引言和目录。

我们为什么要构建自己的影响分析报告?

在众多 Tableau 环境中担任顾问多年后,我还没有看到一个通用的解决方案可以全面解决每个环境的独特需求。

这并不是说不存在一刀切的解决方案。它们确实存在。然而,很难找到一个对千篇一律的解决方案充满热情的客户。每个组织的数据管理都是独一无二的,围绕其数据生态系统演变的复杂性通常需要复杂的解决方案来充分满足其目标。祝你好运从盒子里拿出那种解决方案。

话虽如此,在标准的 Tableau 服务器部署中,有一些现成的产品可供您使用,但需要付费。其中一个工具是数据管理插件 (DMAO)。这个产品(特别是它的元数据方面)值得了解。

我鼓励你尝试一下 DMAO,Tableau 在开发新工具方面一直做得很好。然而,这些工具被设计成普遍适用于任何环境,因此它们可能不会触及您的团队的具体痛处。

让我们更仔细地看看 DMAO 提供了什么,因为这将有助于将我们的对话引向底层的元数据 API ,以及它在构建定制的影响分析报告时对我们的宝贵价值。

一个新的 Tableau 产品:数据管理插件

DMAO 是 Tableau 产品系列中的许可产品。它包括 Tableau 目录和 Tableau 准备指挥。

用 Tableau 自己的话说,他们是这样定义产品组合的:

数据管理插件是一个特性和功能的集合,可帮助客户在其 Tableau 服务器或 Tableau 在线环境中管理 Tableau 内容和数据资产。

您可以使用 Tableau Prep Conductor 利用 Tableau Server 或 Tableau Online 的计划和跟踪功能来自动更新流输出。

Tableau Catalog 包含在数据管理附加组件中,使您可以在数据管理空间中使用各种附加功能。您可以使用 Tableau Catalog 来发现数据、管理数据资产、传达数据质量、执行影响分析以及跟踪 Tableau 内容中使用的数据的沿袭。

好奇那看起来像什么吗?Tableau Prep Conductor 超出了本文的范围,因为它旨在为 Tableau 用户提供一个熟悉的界面来管理他们的数据管道,所以让我们把重点放在 Tableau 目录上。

数据沿袭:跟踪数据的来源和在环境中的使用位置。

在上图中,我们可以看到 Tableau 目录为我们探索各种资产之间的关联提供了一个前端界面。底层元数据知道如果您使用工作簿 A,它将引用数据源 B。同样,数据源 B 接入数据库 C 并使用表 X、Y 和 z。想知道表中存在的列吗?没问题。想知道有多少其他工作簿使用这些表?没问题。

这些元数据对你来说是非常有用的。最棒的是,即使你不拥有 DMAO 的产品,你也可以通过元数据 API 使用底层元数据。这意味着你可以自己获取数据,并随意使用。自由!

如果你在 2019.3 或更高版本上,元数据 API 是标准的,包括电池。如果您想深入研究,请查看这篇关于使用元数据 API 的教程。

元数据:你在 Tableau 世界的新朋友

虽然它确实有一个未来的环,元数据不仅仅是一个时髦的词。在 Tableau 的上下文中,元数据提供了关于你的内容、你的用户、如何访问或使用内容的信息,以及几乎所有介于两者之间的信息。

作为元数据的一个例子,考虑一下这个:Tableau 最近举办了一个社区挑战赛,邀请参与者使用元数据扫描 Tableau 服务器上的工作簿,识别命名不当的计算,并向工作簿的所有者发送电子邮件,要求他们重命名标记的计算。

在 Tableau Server 版本 2019.3 中引入元数据 API 之前,以直接的方式解决这一挑战是不可能的。能够查看工作簿的内部并查询其计算字段的详细信息是元数据 API 赋予我们所有人的一个愿望。

这仅仅是皮毛。我们从 Metdata API 中获得的另一个强大功能是查看我们的工作簿和数据源,以查看为我们的视觉效果提供支持的数据库资产。

我甚至数不清有多少次听到一位分析师抱怨被要求编制一份 Tableau 中使用的所有数据库表的列表。当你思考这个问题时,从管理的角度来看,这似乎是一个很好的问题:我们的 Tableau 仪表板中使用了什么?我们如何确保人们使用的是正确的 T4 数据来源?我们如何确保在不破坏仪表板的情况下修改数据库中的表是安全的?如果我们从数据库中删除旧表,会破坏多少仪表板?

在过去,这些问题会在一些负担过重的分析师任务清单的积压中痛苦地死去。今天不行。今天,您只需几个问题和一个仪表板,就能得到所有这些问题的答案。

介绍我们影响分析节目的演员阵容

舞台已经布置好了,让我们给你介绍一下演员。这部剧里有三位明星你会想熟悉的:

  1. 元数据 API
  2. REST API
  3. Tableau 服务器仓库(工作组 PostgreSQL 数据库)

这些是我们将在接下来的教程中使用的工具,用于从我们的 Tableau 环境中收集数据并构建影响分析仪表板。我们从我们的 Tableau 环境中提取数据,将数据反馈到 Tableau 视觉效果中,并使用这些视觉效果来更好地管理我们的 Tableau 环境。我怀疑“元”这个词再合适不过了。

在我们需要识别内容之间关系的任何场景中,元数据 API 都将是主角。虽然这些信息中的一些也可以从 REST API 中提取,但元数据 API 受益于 GraphQL,这是一种查询语言,它让我们能够以 REST API 实现不支持的方式动态定义查询。元数据 API 允许我们准确地定义我们想要拉取的数据,并使搜索 Tableau 环境的每个角落成为可能。

我不是说给 REST API 蒙上阴影;它有自己的角色。事实上,REST API 是我们进入元数据 API 的网关,也是我们所有查询都要通过的载体。REST API 允许我们以自动化的方式获取数据并发布、更新或删除内容。使用 REST API,我们将能够将从元数据 API 提取的数据发布为 Tableau 数据源文件或摘录。在构建我们的影响分析时,这是非常必要的一步——我们需要将数据源插入到我们的仪表板中。

这个故事中的第三个火枪手是 Tableau Server 的内部 PostgreSQL 数据库。这包含了丰富的信息,虽然我们需要的 90%将来自元数据 API 和 REST API,但是存储库数据库将为我们的影响分析添加有价值的上下文。例如,我们可以添加一些信息来描述特定内容的访问频率、用户对特定内容采取的操作、哪些内容是以交互方式而不是通过电子邮件订阅来访问的,等等。当需要真正定制您的影响分析时,PostgreSQL 数据库将是您的得力助手。

既然我提到了火枪手…第四个火枪手(达达尼昂)应该是超级 API。这更像是客串,更像是一种最佳实践,而不是必须的。我们不会将 CSV 文件作为影响分析仪表板的数据源发布,而是使用 Hyper API 来创建。超级文件。这些是高性能的提取,API 支持将数据附加到。超级文件。这为跟踪我们元数据中的历史趋势打开了大门,而不需要担心将我们的数据存储在一个巨大的 CSV 中,或者担心将数据存储在数据库中。

影响分析路线图和里程碑

我们将在此发布更新。

里程碑 1 (ETA 是 4/24/2020):使用元数据 API 为我们的工作簿(视图和仪表板)、数据源和流收集数据。对于这三类内容中的每一类,我们将确定所有相关的数据库资产。

里程碑 2 (ETA 是 4/29/2020):在里程碑 1 的基础上,使用来自 Tableau 服务器存储库的补充数据,提供与每个工作簿(视图和仪表板)、数据源和流程相关联的交互数量。

里程碑 3 (ETA 为 5/6/2020):使用 hyper API 将里程碑 1 和 2 的组合输出转换为. Hyper 提取,并将内容发布到 Tableau 服务器(或 Tableau Online)。

里程碑 4 (ETA 是 5/13/2020):使用里程碑 3 中发布的数据源构建我们的第一个影响分析仪表板。

里程碑 5 (TBD)

包装它

本文到此结束,并为我们探索构建定制的视觉效果,为我们的团队提供易于使用的影响分析仪表板打下基础!请继续关注我们在上面列出的里程碑上的复选框。

希望使用 Python 来开发 Tableau APIs 吗?

如果你想开始使用 tableau-api-lib ,我用来与 tableau 的 api 交互的 Python 库,这里有几个相关的教程。

Tableau 服务器上线:入门

tap 上的 Tableau 服务器:使用个人访问令牌进行认证

Tableau Server on tap:向元数据 API 发送 GraphQL 查询

如何将 Tableau 服务器元数据加载到熊猫数据帧中

构建放射学性别差异的道琼斯指数

原文:https://towardsdatascience.com/building-the-dow-jones-index-for-gender-disparities-in-radiology-b9b14df758fa?source=collection_archive---------35-----------------------

改编自https://bit.ly/2SUhBed

Twitter 上的数据流提供了丰富的信息来源,可以帮助我们实时了解趋势和观点。

人们可以很容易地以自动化的方式利用 Twitter 数据,并创建大型的结构化数据集。这种能力直觉上很强大——我们能从无休止的患者/医生聊天中学到什么?—我花了相当多的时间思考最高产量的用例。

我的结论是,Twitter 数据的一个特别有意义的应用是理解放射学中的性别差异,以及它们是如何随时间变化的。这是有意义的,因为:

  • 有一场强大的运动专注于纠正放射学中的性别差异,这个领域在吸引和培养女性人才方面历来表现不佳。这种论述是围绕一系列离散的、确定的标签组织的,比如#RadXX 和# WomenInRadiology。
  • 这个讨论代表了围绕放射学的更大的 Twitter 讨论的一个子集,这是一个每天有成千上万条推文的强大对话。因此,我们可以考虑女性赋权放射活动与整个放射活动的某种比例。

利用这一推理,我决定建立 RadX 指数(RXI),这是一个类似道琼斯的定量指数,为我们在纠正放射学性别差异方面的表现提供了一个动态的实时标志。你可以在这里查看:【https://bit.ly/320uK9D

简单的 1.0 版本是这样工作的:

  1. 包括#RadXX 在内的所有推文都放在一个数据库中,带有时间戳和其他关键信息,如用户名和推文 URL。
  2. 包括#Radiology 在内的所有推文都放在一个类似的数据库中。
  3. 每 24 小时,每个数据库(#RadXX 和#Radiology)中的推文数量被量化,并取一个比率来形成 RXI。
  4. 当天的 RXI 以经典的股票代码方式自动绘制,提供了一个公开的图形索引,显示我们的表现以及随时间的变化。

包含#radiology 的推文组织在数据库中进行分析

我们正在有效地创建以下内容的自动近似:

这种方法简单且不完美,但它是一个开始。有许多方法可以使它变得更复杂,例如:

  • 对女性和男性放射科医生反应的情感分析。放射科对女性是什么态度?
  • 量化男性和女性放射科医生的推文数量。有足够数量的女性放射科医生参加讨论吗?
  • 围绕#RadXX 推文的参与度分析(例如,浏览量、点赞数、RTs)。参与度越高,RXI 越高。
  • RadXX 和#Radiology 不一定是 RXI 比率的最佳标签。例如,将#RadXX、# WomenInRadiology 和#HeForShe 结合使用可能比单独使用#RadXX 更好。

可以整合这些指标,以创建一个更复杂的主 RXI 得分,该得分优于 1.0 迭代(在一天内构建,以开始生成数据和讨论)。

你怎么想呢?我很想让这个指数开源,并共同努力来改善它。我的方法是粗略的第一步,但你可能比我聪明…所以,你能想到更好的 RXI 实现吗?大家讨论一下!

如果你想就这个项目或医疗保健/技术领域的任何其他事情进行交流,请联系 Twitter LinkedIn。如果你喜欢这篇文章,请分享…讨论将使这个想法变得更好!

使用 AutoAI 构建 Kaggle 项目的“Hello World”

原文:https://towardsdatascience.com/building-the-hello-world-of-kaggle-projects-using-autoai-ea1faf6fa567?source=collection_archive---------51-----------------------

从这个博客开始你的 Kaggle 之旅,并使用 Watson AutoAI 充分利用它

埃里克·克鲁尔在 Unsplash 上的照片

总是有人问我,开始研究数据科学的最佳方式是什么?嗯,我对此的回答总是一样的,做大量的数据科学课程来熟悉概念,然后通过参加 Kaggle 竞赛来继续构建项目。在这篇博客中,我将展示我是如何使用 AutoAI 在 30 分钟内完成泰坦尼克号生存 Kaggle 比赛,从而开始我的 Kaggle 之旅的。

什么是 Kaggle 比赛?

Justin Chan 关于数据驱动型投资者的照片

Kaggle 为数据科学爱好者提供了一个分析比赛的平台,公司和研究人员发布数据,让爱好者竞争,以产生预测和描述数据的最佳模型。目前有超过 13,000 个数据集,Kaggle 提供了一个名副其实的数据金矿。

为什么选择 IBM Watson AutoAI?

Watson Studio 中的 AutoAI 可以自动完成通常需要数据科学家几天或几周时间才能完成的任务。您所需要做的就是提交您的数据,剩下的就交给工具来决定最适合您项目的管道。

格雷格·菲拉在 IBM 沃森上的照片

让卡格灵开始吧!

泰坦尼克号项目被称为 Kaggle 项目的“hello world ”,因为初学者可以在尝试复杂项目之前获得 Kaggle 实践经验。

准备:

  1. 设置 AutoAI: —创建一个 IBM Cloud 账户。— 创建一个 Watson Studio 实例。—创建项目—在项目中添加 AutoAI。
  2. Kaggle 上创建账户
  3. 参加泰坦尼克号生存竞赛

步骤 1 —数据收集:

我们必须从竞赛页面下载数据集。zip 文件将包含 3 个文件 train.csv 文件是我们将用于训练模型的文件,测试是我们将用于批量评分我们的模型并将其用于提交目的的文件,而 gender_ submission 文件显示了 Kaggle 的提交文件应该是什么样子(它是我们将填写的模板)。

第 2 步—数据准备:

数据非常干净,但是在训练数据集和测试数据集中需要注意空值。首先,让我们用 Age 和 Fare 列中值的平均值替换它们中的空值。我使用 Excel 公式“平均值”来查找和替换这些值。我让客舱栏为空。在测试模型时,这一点正在处理中(待续…)

步骤 3 —使用 AutoAI 建立模型:

虽然这一步看起来很难,但却是最简单的,因为我们使用的是 AutoAI。只需在 Watson Studio 中创建一个 AutoAI 项目,为您的实验命名,并上传您的 train.csv 文件。选择“幸存”作为您的预测变量,运行实验,并等待至少 15 分钟。

AutoAI 采用数据集和目标变量来设计管道(这些是不同的模型),使用各种 HPO(超参数优化)参数和增强的特征工程来为每个管道获得最佳模型。

您可能已经知道,有不同的方法来评估和选择最佳模型,如准确度、F1 分数、精确度等。您也可以根据自己的需要对其进行编辑,但是我们将针对这种情况选择准确性(您也可以尝试其他评估者)。在下图中,您可以看到每个管道在不同评估者面前的表现。这就是选择和推荐部署最佳管道(领导者管道)的方式。

现在,要部署模型,请单击第一个管道(带有星号),另存为,然后单击模型。这将允许您将您的模型部署为 Watson 机器学习模型。

回到您的项目,单击 WML 模型并将其部署为 web 服务,部署完成后,您可以测试它并获取我们将在下一步中使用的评分链接。

步骤 4 —对模型进行批量评分

现在我们有了模型,让我们创建一个 python 脚本,根据 test.csv 对 AutoAI 模型进行批量评分,以提交我们的结果。

下面是对测试文件中的所有记录运行批处理分数测试的代码。

代码基本上是将每个记录逐个存储在变量中,这样就可以将它作为有效载荷传递给模型进行评分。由于一些客舱值为空,我们将其替换为空。一旦我们从模型中获得 JSON 结果,我们解析它以获得记录的预测值,并将其存储在数组中,该数组将被写回到 Results.csv 表中。

Results.csv 文件

最后一步——向 Kaggle 提交结果

转到竞赛页面,点击提交结果以提交您的结果文件。等到你出现在领导板上,你的分数(我的分数是 77%)和排名会显示在屏幕上。分数决定你的排名。你可以不断改进你的模型,并不断提交多次到达顶端。

作者在 Kaggle 上的排行榜

瞧💃!!您已经提交了您的第一个 Kaggle 项目!!

我的经历

AutoAI 确实使 kaggling 变得更简单,一个担心是缺乏对参数的控制,并且需要对某些事情进行微调。这可以通过将模型保存为笔记本并用 python 编码来轻松完成。在我看来,Watson AutoAI 是初学者开始 Kaggle 之旅的最佳工具。

快乐的旅行💃!

参考

  1. https://www.kaggle.com/c/titanic#
  2. https://dataplatform.cloud.ibm.com/
  3. https://kaggle.com/
  4. https://medium . com/IBM-Watson/introducing-autoai-for-Watson-studio-241675 FB 0454

构建香农熵公式

原文:https://towardsdatascience.com/building-the-shannon-entropy-formula-ca67bdb74cdc?source=collection_archive---------23-----------------------

每个人理解香农熵的直观方法

凯利·西克玛在 Unsplash 上的照片

目的

本文的写作是试图理解可以使用香农熵公式的决策树算法的结果。本文旨在通过首先用一个例子说明熵,然后一步一步地建立公式,来展示公式背后的直观推理。

熵是什么?

熵是不确定性的度量,由 Claude E. Shannon 引入信息论领域。在这种情况下,可以区分两个相关的量:熵和自熵,前者处理一组事件,后者与单个事件相关。

信息和熵

要记住的一个基本概念是信息和熵是直接相关的

自熵度量的是事件中包含的不确定性或信息。

当考虑自熵时,高度不确定的事件具有高熵并提供大量信息,而某个事件具有低熵并提供少量信息。

举个极端的例子,一个总会发生的事件有零自熵,而一个永远不会发生的事件有无限自熵。

另一方面,熵测量所有事件对系统贡献的平均自熵。

为了说明这两种熵类型,假设您有一个容器,其中只能放置四个球,可以是绿色或红色,如图 1 所示。这导致了五种配置,其中排序并不重要。

图 1:熵和自熵与提取绿色球的概率相关

考虑到容器配置 1,整个系统的熵为零,因为提取球的事件不存在不确定性,因为球总是红色的。提取红球的自熵为零,对于绿球是无穷大。

在容器配置 2 中,整个系统的熵很低,因为人们很可能从容器中抽取一个红球。在这种情况下,提取绿色球的自熵高,而提取红色球的自熵低。

最后一个重要的情况是容器配置 3,因为它提供了最大熵,因为这两个事件的可能性相等。这两个事件的自熵中等,相等。

容器配置 4 和 5 分别与容器配置 2 和 1 相同,只是颠倒了绿色和红色。

上述事件的自熵可以通过图 2 中的图表来总结。

图 2:与提取绿色球的概率相关的自熵图

同样,两个事件系统的熵可以用图 3 中的图来概括。

图 3:与提取绿色球的概率相关的熵图

测量信息

由于熵和信息是直接相关的,测量一个也会导致测量另一个的方法。

一种更直观的表示信息的方式是使用二进制数字,即比特。例如,整数值可以表示为 0 和 1 的位模式。

像从容器中取出一个球这样的事件或者像“客队赢了比赛”这样的消息也可以用表 1 中的某些位模式来表示。

表 1:不同事件的潜在位模式表示

测量自熵

比特模式可以与取决于比特数的概率相关联。例如,任何单个位模式的概率为 1/2,而任何两个位的位模式的概率为 1/4,如此类推,如图 4 所示。

图 4:与位数相关的位模式概率

通常,某个位模式的概率为:

相反,通过在两面都应用以 2 为底的对数:

如图 4 所示,对于[0,1]的整个概率范围,可以获得信息比特的数量,这接近于图 2 中的直觉。

图 5:自熵公式的图表

测量熵

当测量一个系统的熵时,每个事件的平均自熵贡献被考虑在内。

举例来说,考虑包含 10 位信息的事件(或消息)。如果该消息以 0.2 的概率出现,那么它对整个系统的平均贡献是 10 比特* 0.2 = 2 比特因此,考虑到自熵的公式,每个事件的平均贡献是:

包含在 n 个事件及其相关概率的系统中的熵是所有这些平均贡献的总和:

回到第一个示例,考虑两个事件的系统,公式变为:

因为所有概率的总和等于 1,所以上述可以替换为:

在图 5 中画出两个事件系统的熵公式,我们可以再次证实图 3 中的直觉。

图 6:两个事件的熵图

表 2 给出了对两个事件的系统应用自熵和熵公式时得到的 of 值。事件 1 可以与“抽取一个红球”相关联,而事件 2 可以与“抽取一个绿球”相关联,如在所示的例子中。

表 2:双事件系统的自熵和熵值

结论

我希望这能让我们对熵公式是如何建立的有所了解。

回顾本文的目的,决策树使用 Shannon 熵公式来挑选一个特征,该特征将数据集递归地分割成具有高度一致性(低熵值)的子集。更均匀地分割数据集会使决策树深度更小,从而更易于使用。

如果你有意见或建议,我将很高兴收到你的来信!

图 1 中的例子基本上是 Luis Serrano 在文章“香农熵、信息增益和从桶中捡球”中的简化版本

使用决斗式双深度 Q 学习构建终极 AI 智能体

原文:https://towardsdatascience.com/building-the-ultimate-ai-agent-for-doom-using-dueling-double-deep-q-learning-ea2d5b8cdd9f?source=collection_archive---------43-----------------------

Pytorch 中强化学习的实现。

简介

在过去的几篇文章中,我们已经讨论了为 VizDoom 环境实现了各种价值学习架构,并检验了它们在最大化回报方面的表现。简而言之,这些包括:

总的来说,vanilla Deep Q-learning 是一种高度灵活和响应性的在线强化学习方法,它利用快速的场景内更新来估计环境中的状态-动作(Q)值,以便最大化回报。Q-learning 可以被认为是一种策略外的 TD 方法,其中该算法旨在选择独立于当前遵循的策略的最高值的状态-动作对,并且已经与 OpenAI Atari 健身房环境的许多原始突破相关联。

相比之下,双重深度 Q 学习改进通过使用双重网络设置将动作选择与 Q 值目标计算分离,解决了在 DQN 观察到的状态-动作值高估的问题,这是之前在训练中观察到的普遍问题。类似地,due Deep Q-learning 通过将单个输出状态-动作流分成单独的价值和优势流,提高了模型在训练期间的概括能力,允许代理学习专注于单独的目标以提高性能。

在我们的 DQN 系列文章的最后一篇中,我们将把迄今为止我们所学的所有内容结合到一个单一的复合方法中——决斗双深度 Q 学习(DuelDDQN)。通过结合我们以前模型的所有优势,我们将致力于进一步提高我们的代理在 VizDoom 环境中的收敛性。

实施

我们将在与上一篇文章保卫防线相同的多目标条件下,在相同的 VizDoomgym 场景中实现我们的方法。环境的一些特征包括:

  • 一个 3 的动作空间:开火,左转,右转。不允许扫射。
  • 向玩家发射火球的棕色怪物,命中率为 100%。
  • 试图以之字形靠近来咬玩家的粉红色怪物。
  • 重生的怪物可以承受更多伤害。
  • 杀死一个怪物+1 点。
  • -死了得 1 分。

“防线方案”的初始状态

回想一下,在我们最初的 DQN 实现中,我们已经利用了两个并发网络——一个用于行动选择的评估网络,以及一个定期更新的目标网络,以确保生成的 TD 目标是固定的。我们可以利用这个现有的设置来构建我们的 DuelDDQN 架构,而无需初始化更多的网络。

请注意,由于两个网络定期更新彼此的权重,因此这两个模型仍然是部分耦合的,但重要的是,动作选择和 Q 值评估是由在特定时间步长不共享同一组 a 权重的独立网络完成的。

我们的 Google 协作实现是利用 Pytorch 用 Python 编写的,可以在 GradientCrescent Github 上找到。我们的方法基于泰伯优秀强化学习课程中详述的方法。由于我们的 DDQN 实现类似于我们之前的普通 DQN 实现,所以整个高级工作流是共享的,这里不再重复。

让我们从导入所有必需的包开始,包括 OpenAI 和 Vizdoomgym 环境。我们还将安装火炬视觉所需的 AV 包,我们将使用它进行可视化。请注意,安装完成后必须重新启动运行时。

#Visualization cobe for running within Colab
!sudo apt-get update
!sudo apt-get install build-essential zlib1g-dev libsdl2-dev libjpeg-dev nasm tar libbz2-dev libgtk2.0-dev cmake git libfluidsynth-dev libgme-dev libopenal-dev timidity libwildmidi-dev unzip# Boost libraries!sudo apt-get install libboost-all-dev# Lua binding dependencies
!apt-get install liblua5.1-dev
!sudo apt-get install cmake libboost-all-dev libgtk2.0-dev libsdl2-dev python-numpy git
!git clone [https://github.com/shakenes/vizdoomgym.git](https://github.com/shakenes/vizdoomgym.git)
!python3 -m pip install -e vizdoomgym/!pip install av

接下来,我们初始化我们的环境场景,检查观察空间和动作空间,并可视化我们的环境。

import gym
import vizdoomgymenv = gym.make('VizdoomDefendLine-v0')
n_outputs = env.action_space.n
print(n_outputs)observation = env.reset()
import matplotlib.pyplot as pltfor i in range(22):

  if i > 20:
    print(observation.shape)
    plt.imshow(observation)
    plt.show()
observation, _, _, _ = env.step(1)

接下来,我们将定义预处理包装器。这些类继承自 OpenAI gym 基类,覆盖了它们的方法和变量,以便隐式地提供所有必要的预处理。我们将开始定义一个包装器来重复许多帧的每个动作,并执行元素方式的最大值以增加任何动作的强度。您会注意到一些三级参数,如 fire_firstno _ ops——这些是特定于环境的,在 Vizdoomgym 中对我们没有影响。

class RepeatActionAndMaxFrame(gym.Wrapper):
  #input: environment, repeat
  #init frame buffer as an array of zeros in shape 2 x the obs space
    def __init__(self, env=None, repeat=4, clip_reward=False, no_ops=0,
                 fire_first=False):
        super(RepeatActionAndMaxFrame, self).__init__(env)
        self.repeat = repeat
        self.shape = env.observation_space.low.shape
        self.frame_buffer = np.zeros_like((2, self.shape))
        self.clip_reward = clip_reward
        self.no_ops = no_ops
        self.fire_first = fire_first
  def step(self, action):
        t_reward = 0.0
        done = False
        for i in range(self.repeat):
            obs, reward, done, info = self.env.step(action)
            if self.clip_reward:
                reward = np.clip(np.array([reward]), -1, 1)[0]
            t_reward += reward
            idx = i % 2
            self.frame_buffer[idx] = obs
            if done:
                break
        max_frame = np.maximum(self.frame_buffer[0], self.frame_buffer[1])
        return max_frame, t_reward, done, info
  def reset(self):
        obs = self.env.reset()
        no_ops = np.random.randint(self.no_ops)+1 if self.no_ops > 0    else 0
        for _ in range(no_ops):
            _, _, done, _ = self.env.step(0)
            if done:
                self.env.reset()

        if self.fire_first:
            assert self.env.unwrapped.get_action_meanings()[1] == 'FIRE'
            obs, _, _, _ = self.env.step(1)
        self.frame_buffer = np.zeros_like((2,self.shape))
        self.frame_buffer[0] = obs
    return obs

接下来,我们为我们的观察定义预处理函数。我们将使我们的环境对称,将它转换到标准化的盒子空间,将通道整数交换到张量的前面,并将其从原始(320,480)分辨率调整到(84,84)区域。我们也将我们的环境灰度化,并通过除以一个常数来归一化整个图像。

class PreprocessFrame(gym.ObservationWrapper):
  #set shape by swapping channels axis
 #set observation space to new shape using gym.spaces.Box (0 to 1.0)
    def __init__(self, shape, env=None):
        super(PreprocessFrame, self).__init__(env)
        self.shape = (shape[2], shape[0], shape[1])
        self.observation_space = gym.spaces.Box(low=0.0, high=1.0,
                                    shape=self.shape, dtype=np.float32)
   def observation(self, obs):
        new_frame = cv2.cvtColor(obs, cv2.COLOR_RGB2GRAY)
        resized_screen = cv2.resize(new_frame, self.shape[1:],
                                    interpolation=cv2.INTER_AREA)
        new_obs = np.array(resized_screen, dtype=np.uint8).reshape(self.shape)
        new_obs = new_obs / 255.0
   return new_obs

接下来,我们创建一个包装器来处理帧堆叠。这里的目标是通过将几个帧堆叠在一起作为单个批次,帮助从堆叠帧中捕捉运动和方向。这样,我们可以捕捉环境中元素的位置、平移、速度和加速度。通过堆叠,我们的输入采用(4,84,84,1)的形状。

class StackFrames(gym.ObservationWrapper):
  #init the new obs space (gym.spaces.Box) low & high bounds as repeat of n_steps. These should have been defined for vizdooom

  #Create a return a stack of observations
    def __init__(self, env, repeat):
        super(StackFrames, self).__init__(env)
        self.observation_space = gym.spaces.Box( env.observation_space.low.repeat(repeat, axis=0),
                              env.observation_space.high.repeat(repeat, axis=0),
                            dtype=np.float32)
        self.stack = collections.deque(maxlen=repeat)
    def reset(self):
        self.stack.clear()
        observation = self.env.reset()
        for _ in range(self.stack.maxlen):
            self.stack.append(observation)
        return  np.array(self.stack).reshape(self.observation_space.low.shape)
    def observation(self, observation):
        self.stack.append(observation)
    return np.array(self.stack).reshape(self.observation_space.low.shape)

最后,在返回最终环境供使用之前,我们将所有的包装器绑定到一个单独的 make_env() 方法中。

def make_env(env_name, shape=(84,84,1), repeat=4, clip_rewards=False,
             no_ops=0, fire_first=False):
    env = gym.make(env_name)
    env = PreprocessFrame(shape, env)
    env = RepeatActionAndMaxFrame(env, repeat, clip_rewards, no_ops, fire_first)

    env = StackFrames(env, repeat)
    return env

接下来,让我们定义模型中的决斗部分,一个深度 Q 网络,具有两个输出流。这基本上是一个三层卷积网络,它采用预处理的输入观测值,将生成的展平输出馈送到一个全连接层,然后将输出分成价值流(单节点输出)和优势流(节点输出对应于环境中的动作数量)。

请注意,这里没有激活层,因为激活层的存在会导致二进制输出分布。我们的损失是我们当前状态-动作的估计 Q 值和我们预测的状态-动作值的平方差。然后,我们附上 RMSProp 优化器,以尽量减少我们在培训期间的损失。

import os
import torch as T
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
import numpy as np
class DeepQNetwork(nn.Module):
    def __init__(self, lr, n_actions, name, input_dims, chkpt_dir):
        super(DeepQNetwork, self).__init__()
        self.checkpoint_dir = chkpt_dir
        self.checkpoint_file = os.path.join(self.checkpoint_dir, name)
        self.conv1 = nn.Conv2d(input_dims[0], 32, 8, stride=4)
        self.conv2 = nn.Conv2d(32, 64, 4, stride=2)
        self.conv3 = nn.Conv2d(64, 64, 3, stride=1)
        fc_input_dims = self.calculate_conv_output_dims(input_dims)
        self.fc1 = nn.Linear(fc_input_dims,1024)
        self.fc2 = nn.Linear(1024, 512)
        #Here we split the linear layer into the State and Advantage streams
        self.V = nn.Linear(512, 1)
        self.A = nn.Linear(512, n_actions)
        self.optimizer = optim.RMSprop(self.parameters(), lr=lr)
        self.loss = nn.MSELoss()
        self.device = T.device('cuda:0' if T.cuda.is_available() else 'cpu')
        self.to(self.device)
    def calculate_conv_output_dims(self, input_dims):
        state = T.zeros(1, *input_dims)
        dims = self.conv1(state)
        dims = self.conv2(dims)
        dims = self.conv3(dims)
        return int(np.prod(dims.size()))
    def forward(self, state):
        conv1 = F.relu(self.conv1(state))
        conv2 = F.relu(self.conv2(conv1))
        conv3 = F.relu(self.conv3(conv2))
        # conv3 shape is BS x n_filters x H x W
        conv_state = conv3.view(conv3.size()[0], -1)
        # conv_state shape is BS x (n_filters * H * W)
        flat1 = F.relu(self.fc1(conv_state))
        flat2 = F.relu(self.fc2(flat1))
        V = self.V(flat2)
        A = self.A(flat2)
        return V, A
     def save_checkpoint(self):
        print('... saving checkpoint ...')
        T.save(self.state_dict(), self.checkpoint_file)
     def load_checkpoint(self):
        print('... loading checkpoint ...')
        self.load_state_dict(T.load(self.checkpoint_file))

接下来,我们将定义我们的代理,它遵循我们之前的 DDQN 实现DuelDQN 实现。我们的代理正在使用一个勘探率递减的ε贪婪策略,以便随着时间的推移最大化开发。为了学会预测使我们的累积奖励最大化的状态-行动-值,我们的代理人将使用通过抽样存储的记忆获得的贴现的未来奖励。

您会注意到,作为代理的一部分,我们初始化了 DQN 的两个副本,并使用方法将原始网络的权重参数复制到目标网络中。虽然我们的常规方法利用这种设置来生成固定的 TD 目标,但我们的 DuelDDQN 方法将扩展到这一点:

  • 从重放存储器中检索状态、动作、奖励和下一状态(sar)。
  • 评估网络用于生成当前状态的优势( A_s )和状态( V_s )值。它还用于为下一个状态生成相应的值。
  • 目标网络用于创建下一个状态的优势( A_s_ )和状态( V_s_ )值。
  • 当前状态的预测 Q 值( q_pred )是通过对当前状态的优势和状态值求和,并使用评估网络的输出流减去用于归一化的当前状态优势值的平均值而生成的。使用评估网络的输出流,下一状态 re 的评估 Q 值( q_eval )也以类似的方式创建。使用 argmax() 函数获得下一个状态的最大可能性动作。请注意, q_eval 在这里的作用只是关注于动作选择,与明确地参与目标计算无关。
  • 使用目标网络的输出流,以类似的方式为下一状态( q_next )创建目标 Q 值。 q_next 的作用是专注于目标计算,不参与动作选择。
  • 通过评估网络识别的 max_actions 将当前状态中的回报与从下一状态的目标网络导出的 Q 值相结合,计算当前状态的 TD-target。
  • 通过将 TD 目标与当前状态 Q 值进行比较来计算损失函数,然后将其用于训练网络。
import numpy as np
import torch as T
#from deep_q_network import DeepQNetwork
#from replay_memory import ReplayBuffer#Combining the value stream splitting of a DuelDQN with the update approach of a DDQN
class DuelDDQNAgent(object):
    def __init__(self, gamma, epsilon, lr, n_actions, input_dims,
                 mem_size, batch_size, eps_min=0.01, eps_dec=5e-7,
                 replace=1000, algo=None, env_name=None, chkpt_dir='tmp/dqn'):
        self.gamma = gamma
        self.epsilon = epsilon
        self.lr = lr
        self.n_actions = n_actions
        self.input_dims = input_dims
        self.batch_size = batch_size
        self.eps_min = eps_min
        self.eps_dec = eps_dec
        self.replace_target_cnt = replace
        self.algo = algo
        self.env_name = env_name
        self.chkpt_dir = chkpt_dir
        self.action_space = [i for i in range(n_actions)]
        self.learn_step_counter = 0self.memory = ReplayBuffer(mem_size, input_dims, n_actions)self.q_eval = DeepQNetwork(self.lr, self.n_actions,
                                    input_dims=self.input_dims,
                                    name=self.env_name+'_'+self.algo+'_q_eval',
                                    chkpt_dir=self.chkpt_dir)self.q_next = DeepQNetwork(self.lr, self.n_actions,
                                    input_dims=self.input_dims,
                                    name=self.env_name+'_'+self.algo+'_q_next',
                                    chkpt_dir=self.chkpt_dir)#Epsilon greedy action selection
    def choose_action(self, observation):
        if np.random.random() > self.epsilon:
          # Add dimension to observation to match input_dims x batch_size by placing in list, then converting to tensor
            state = T.tensor([observation],dtype=T.float).to(self.q_eval.device)
            #As our forward function now has both state and advantage, fetch latter for actio selection
            _, advantage = self.q_eval.forward(state)
            action = T.argmax(advantage).item()
        else:
            action = np.random.choice(self.action_space)return actiondef store_transition(self, state, action, reward, state_, done):
        self.memory.store_transition(state, action, reward, state_, done)def sample_memory(self):
        state, action, reward, new_state, done = \
                                self.memory.sample_buffer(self.batch_size)states = T.tensor(state).to(self.q_eval.device)
        rewards = T.tensor(reward).to(self.q_eval.device)
        dones = T.tensor(done).to(self.q_eval.device)
        actions = T.tensor(action).to(self.q_eval.device)
        states_ = T.tensor(new_state).to(self.q_eval.device)return states, actions, rewards, states_, donesdef replace_target_network(self):
        if self.learn_step_counter % self.replace_target_cnt == 0:
            self.q_next.load_state_dict(self.q_eval.state_dict())def decrement_epsilon(self):
        self.epsilon = self.epsilon - self.eps_dec \
                           if self.epsilon > self.eps_min else self.eps_mindef save_models(self):
        self.q_eval.save_checkpoint()
        self.q_next.save_checkpoint()def load_models(self):
        self.q_eval.load_checkpoint()
        self.q_next.load_checkpoint()
    #Make sure you understand this line by line
    #For DDQN main difference is here - Consult the lecture to gain a stronger understanding of why things differe here
    def learn(self):#First check if memory is even big enough
        if self.memory.mem_cntr < self.batch_size:
            returnself.q_eval.optimizer.zero_grad()#Replace target network if appropriate
        self.replace_target_network()states, actions, rewards, states_, dones = self.sample_memory()
        #Fetch states and advantage actions for current state using eval network
        #Also fetch the same for next state using target network
        V_s, A_s = self.q_eval.forward(states)
        V_s_, A_s_ = self.q_next.forward(states_)

        #Eval network calculation of next state V and A
        V_s_eval, A_s_eval = self.q_eval.forward(states_)#Indices for matrix multiplication
        indices = np.arange(self.batch_size)#Calculate current state Q-values and next state max Q-value by aggregation, subtracting constant advantage mean
        #Along first dimension, which is action dimension, keeping original matrix dimensions

        #recall [indices,actions] is used to maintain array shape of (batch_size) instead of (batch_size,actions)
        #Essentilly by adding a a batch index to our vector array we ensure that calculated Q_pred is not tabular, but applicable for a batch update
        q_pred = T.add(V_s,
                        (A_s - A_s.mean(dim=1, keepdim=True)))[indices, actions]
        #For q_next, fetch max along the action dimension. 0th element, because max returns a tuple,
        #of which 0th position are values and 1st position the indices.
        q_next = T.add(V_s_,
                        (A_s_ - A_s_.mean(dim=1, keepdim=True)))

        #QEval q-values for DDQN
        q_eval = T.add(V_s_eval, (A_s_eval - A_s_eval.mean(dim=1,keepdim=True)))
        max_actions = T.argmax(q_eval, dim=1)
        q_next[dones] = 0.0
        #Build your target using the current state reward and q_next, DDQN setup
        q_target = rewards + self.gamma*q_next[indices, max_actions]loss = self.q_eval.loss(q_target, q_pred).to(self.q_eval.device)
        loss.backward()
        self.q_eval.optimizer.step()
        self.learn_step_counter += 1self.decrement_epsilon()

定义了所有支持代码后,让我们运行主训练循环。我们已经在最初的总结中定义了大部分,但是让我们为后代回忆一下。

  • 对于训练集的每一步,在使用ε-贪婪策略选择下一个动作之前,我们将输入图像堆栈输入到我们的网络中,以生成可用动作的概率分布。
  • 然后,我们将它输入到网络中,获取下一个状态和相应奖励的信息,并将其存储到我们的缓冲区中。我们更新我们的堆栈,并通过一些预定义的步骤重复这一过程。
  • 在一集的结尾,我们将下一个状态输入到我们的网络中,以便获得下一个动作。我们还通过对当前奖励进行贴现来计算下一个奖励。
  • 我们使用去耦网络通过上述 Q 学习更新函数生成我们的目标 y 值,并训练我们的 actor 网络。
  • 通过最小化训练损失,我们更新网络权重参数,以便为下一个策略输出改进的状态-动作值。
  • 我们通过跟踪模型的平均得分(在 100 个训练步骤中测量)来评估模型。
# Main threadenv = make_env('DefendTheLine-v0')
best_score = -np.inf
load_checkpoint = False
n_games = 20000agent = DuelDDQNAgent(gamma=0.99, epsilon=1.0, lr=0.001,input_dims=(env.observation_space.shape),n_actions=env.action_space.n, mem_size=5000, eps_min=0.15,batch_size=32, replace=1000, eps_dec=1e-5,chkpt_dir='/content/', algo='DuelDDQNAgent',env_name='vizdoogym')if load_checkpoint:
  agent.load_models()fname = agent.algo + '_' + agent.env_name + '_lr' + str(agent.lr) +'_'+ str(n_games) + 'games'
figure_file = 'plots/' + fname + '.png'n_steps = 0
scores, eps_history, steps_array = [], [], []for i in range(n_games):
  done = False
  observation = env.reset()score = 0
  while not done:
    action = agent.choose_action(observation)
    observation_, reward, done, info = env.step(action)
    score += rewardif not load_checkpoint:
      agent.store_transition(observation, action,reward, observation_, int(done))
      agent.learn()
    observation = observation_
    n_steps += 1scores.append(score)
  steps_array.append(n_steps)avg_score = np.mean(scores[-100:])if avg_score > best_score:
    best_score = avg_score

    print('Checkpoint saved at episode ', i)
    agent.save_models()print('Episode: ', i,'Score: ', score,' Average score: %.2f' % avg_score, 'Best average: %.2f' % best_score,'Epsilon: %.2f' % agent.epsilon, 'Steps:', n_steps)eps_history.append(agent.epsilon)
  if load_checkpoint and n_steps >= 18000:
    break#x = [i+1 for i in range(len(scores))]

我们绘制了 500 集和 1000 集代理人的平均得分和我们的 epsilon 值。可以观察到,DuelDDQN 模型的收敛性明显优于基线 DQN 和 DDQN 模型的收敛性,甚至稍微改善了 DuelDQN 模型的性能。

500 集后我们经纪人的奖励分配。

1000 集后我们经纪人的奖励分配。

我们可以想象我们的代理人在 500 集以下的表现。

500 集的代理性能。

回想一下,我们以前的代理遭受了陷入局部最小值的特殊问题,依赖于怪物之间的友好射击作为主要的得分策略,而不是代理的主动性。我们的 DuelDDQN 代理没有什么不同,比以前的模型更快地达到这个最小值。解决这个问题要么需要改变环境(用扫射代替转弯可能是一种选择),加速学习以避免模式崩溃(如通过动量),要么通过奖励工程——例如,根据生存的持续时间或怪物从代理人的武器中受到的原始伤害增加相应的奖励。

那么,通过我们的系列课程,我们学到了什么?

  • 像 DQN 这样的价值学习架构为 RL 代理开发提供了一个很好的基线。
  • 这种架构的性能可以通过对学习过程的设计进行小的修改来提高。
  • 鼓励模型探索,特别是在高水平的收敛上,是学习过程的一把双刃剑。
  • 就实现目标而言,一个模型的表现只能和它的回报函数的设计一样好,这必须通过观察仔细分析或推断。

这就结束了决斗双深度 Q 学习的实现,以及我们关于 DQN 的系列。在我们的下一篇文章中,我们将暂时停止强化学习,尝试一些特定的生成图像处理技术。

我们希望你喜欢这篇文章,并希望你查看 GradientCrescent 上的许多其他文章,涵盖人工智能的应用和理论方面。为了保持对 GradientCrescent 的最新更新,请考虑关注该出版物并关注我们的 Github 资源库

来源

萨顿等人。al,“强化学习”

塔博尔,“运动中的强化学习”

西蒙尼尼,“深度 Q 学习的改进*

建立对机器学习恶意软件检测器的信任

原文:https://towardsdatascience.com/building-trust-in-machine-learning-malware-detectors-d01f3b8592fc?source=collection_archive---------43-----------------------

我们能相信 ML 系统做出的决定吗?

图片:来源

每一次我们创造一个更强大的技术,我们就创造了改变世界的下一个层次。在 AI 中,我们不会给它们自己学习的机器编程。对人工智能最大的误解之一是,它是一个超级智能的存在,或者我们称之为广义人工智能,它知道一切,比我们所有人都聪明,这完全是一种误解。尽管取得了这些进展,但仍有许多挑战需要更多的科学突破。

"当言行一致时,信任就产生了."—克里斯·巴特勒

介绍

新通信技术的广泛传播不可避免地促使人们不断改进保护各种数字系统中信息的手段和方法,这些数字系统在现代公共生活的几乎所有领域中运行,从银行、制造业到国防和政府部门。这些系统的特点是各种各样的网络威胁。事实上,计算机网络和互联网已经成为恶意软件作者传播和分发恶意软件的主要工具。网络安全专家普遍认为,由人工智能和人工智能支持的反恶意软件工具和系统将成为现代恶意软件攻击的解决方案。然而,创建一个完美的恶意软件检测系统,检测所有类型的恶意软件,零误报似乎是不可能的。

我们知道,网络安全分析师总是更喜欢可解释和可理解的解决方案,如基于规则或基于签名的检测。这就是为什么本文的重点是回顾恶意软件检测的当前技术水平,指出它们的局限性,并讨论可能的解决方案,以增加对人工智能系统的信任。

目录

  1. 恶意软件检测的机器学习综述
  2. 限制由 ML 支持的恶意软件检测成功的挑战
  3. 克服挑战的解决方案

恶意软件检测的机器学习综述

机器学习是一种数据分析工具,用于在没有明确指示的情况下有效执行特定任务。近年来,ML 能力已经被用于设计用于恶意软件检测的静态和动态分析技术。在这种情况下,我将使用 ML 方法设置恶意软件检测的一些技术状态。

  • Hassen 等人提出了一种新的恶意软件分类技术,他们使用静态分析将恶意软件实例分类到新的已知恶意软件家族中。通过从反汇编的恶意二进制文件中提取特征,并使用随机森林算法使用提取的特征对恶意软件进行分类(Hassen et al .,2017)。使用 10,260 个恶意软件实例的数据集,他们报告的准确率高达 99.21%。
  • Naeem 等人提出了一种静态分析技术来检测物联网恶意软件(Naeem 等人,2018)。所提出的技术将恶意软件文件转换为灰度图像,并从恶意软件图像中提取一组视觉特征来训练 SVM 分类器,该分类器可以使用视觉特征来区分恶意软件家族。使用来自 25 个恶意软件家族的 9342 个样本的数据集,他们报告了 97.4%的准确率。
  • 已经提出了几项工作来使用静态分析技术检测 Android 恶意软件应用。Sahin 等人提出了一种 Android 恶意软件检测模型,使用应用程序权限来检测恶意应用程序(OSahn 等人,2018)。他们使用应用程序所需的权限和加权距离函数以及 KNN 和朴素贝叶斯分类器来检测恶意应用程序。他们报告的准确率高达 93.27%。
  • Yeo 等人通过监测网络流量中的恶意行为,提出了一种新的恶意软件检测方法(Yeo 等人,2018)。他们设计了 35 个特征来描述恶意软件实例的恶意流量。他们测试了几种机器学习算法,包括 CNN、MLP、SVM 和随机森林。当使用 CNN 或随机森林时,该方法达到了 85%以上的准确率。

这些技术试图提高恶意软件检测系统的质量和性能,以建立一个健壮的系统。

限制由 ML 支持的恶意软件检测成功的挑战

虽然使用机器学习进行恶意软件检测在静态和动态分析中都显示出有希望的结果,但是存在限制基于 ML 的恶意软件检测器成功的重大挑战。

1.培训费用

第一个挑战是在生产环境中培训和更新恶意软件的成本。不幸的是,恶意软件检测不同于 ML 技术已经成功应用的其他领域,例如计算机视觉、自然语言处理和电子商务。恶意软件实例在短时间内进化并改变它们的行为;反恶意软件供应商的一些研究报告称,新的恶意软件实例可以在发布后不到 24 小时内改变他们的行为。这意味着我们需要经常重新培训我们的恶意软件检测器,以便能够检测新的和变异的恶意软件实例。因此,恶意软件检测的 ML 模型的适应性是一个至关重要的要求,而不仅仅是一个辅助功能。
与计算机视觉、自然语言处理和其他利用 ML 的领域不同,恶意软件实例会继续发展和变化。这通常需要在生产中重新训练 ML 模型,这是一项昂贵而复杂的任务。因此,当使用 ML 进行恶意软件检测时,我们需要换一种方式思考。

图片来源

2.了解恶意软件检测器

网络安全分析师总是倾向于可解释和可理解的解决方案。因为他们需要调整和优化这些解决方案,以减轻和控制误报和漏报的影响。理解和解释 ML 模型对他们来说是一个新的挑战。任何恶意软件检测器都会产生误报。我们知道恶意软件分析师不会接受那些黑盒恶意软件检测器。这就是为什么我们需要专注于对 ML 模型的解释,以使恶意软件分析师可以接受,他们不需要成为机器学习专家。

3.敌对恶意软件

众所周知,机器学习模型对对手精心制作的输入缺乏鲁棒性。你可以在这里查看我之前关于对抗性攻击的文章。事实上,利用 ML 的恶意软件检测系统可以使用敌对的恶意软件样本来绕过。Kolosnjaji 等人表明,通过使用智能规避攻击,他们可以击败(Raff 等人,2017 年)中提出的深度学习检测系统。

克服这些挑战的解决方案

为了克服我们之前讨论的挑战,我将提出一些新的解决方案。

  • 受微服务架构启发的新方法。这意味着构建多个小型、廉价、集中的 ML 模型来检测恶意软件实例。每个模型被构建来检测特定恶意软件实例(例如,WannaCry )或单个恶意软件家族(一组相似的恶意软件实例)的行为。此外,每个模型或检测器都是使用相似的特征构建的,例如具有相同的计算成本,或者对于特定的执行环境是唯一的。
    使用微型聚焦检测机降低了生产中再培训和部署的成本。这是因为可以训练和添加新恶意软件的检测器,而不需要重新训练现有的检测器。此外,当恶意软件检测器由于恶意软件进化行为而变得过时时,过时的检测器被移除并被新的检测器取代。
  • 使用进化计算,我们可以使用一组 IF-Then 规则来描述恶意软件检测器的决策。唯一需要的信息是恶意软件检测器用来做出决定的输入特征。
    IF-Then 规则有助于解释触发恶意软件检测器做出特定决定(如恶意或良性)的行为。网络安全和恶意软件分析师对使用“如果-那么”规则感到满意。这些规则将有助于理解恶意软件检测器做出的决定,解释检测的范围,并识别可能导致假阳性或假阴性的潜在过度概括或过度拟合。
  • 培训和更新恶意软件检测器是对抗恶意软件的最有效的解决方案。正如我之前提到的,我们无法创建一个完美的恶意软件检测系统。我也相信我们不能设计完美的恶意软件。为此,使用混合机器学习模型(当使用两种或更多种不同的 ML 算法来构建模型时)将有效对抗敌对的恶意软件。这种方法将为恶意软件检测器提供深度防御。

结论

“人们担心计算机会变得太聪明并接管世界,但真正的问题是它们太笨了,它们已经接管了世界。”佩德罗·多明戈斯。

总之,我的文章旨在清理 ML 恶意软件检测器的视野。因为我相信透明度是建立信任的第一把钥匙。通过这篇文章,我们知道目前机器学习模型缺乏鲁棒性。我们知道他们有能力发现隐藏的关系,以解决复杂的问题,如网络安全问题。当然,这方面还需要进一步的研究。而也许有一天我们可以相信 AI 在安全领域做出的决定。就目前而言,我认为在我们能够回答这个问题之前还有很多工作要做。

请关注我,阅读我关于 AI 在安全领域的文章,因为这只是一个开始。✌ ✌ 👌

快乐阅读,快乐学习,快乐编码。👏

参考资料:

[1] Hassen,m .,Carvalho,M. M .,和 Chan,P. K. 使用基于静态分析的特征进行恶意软件分类
2017 年。

[2] Naeem,h .、Guo,b .和 Naeem,M. R. 一种用于物联网基础设施的轻量级
恶意软件静态可视化分析。
2018。

[3]d . OSahn、o . e . Kural、s . akley lek 和 e .基里奇Android 恶意软件基于许可的静态分析的新结果。 (2018)。

[4]萨阿德,s;Briguglio,w .和 Elmiligi,H. 恶意软件检测中机器学习的奇特案例。 2019。

[5] Yeo,m .,Koo,y .,Yoon,y .,Hwang,t .,Ryu,j .,Song,j .,和 Park,C. 使用卷积神经网络进行基于流的恶意软件检测。 2018 年。

[6] Raff,e .,Barker,j .,Sylvester,j .,Brandon,r .,Catanzaro,b .,Nicholas,C. K. 通过吃掉整个 exe 文件来检测恶意软件。 2017。

[7] Kolosnjaji,b .、Demontis,a .、Biggio,b .、Maiorca,d .、Giacinto,g .、埃克特,c .和花小蕾,F. 敌对的恶意软件二进制文件:规避深度学习以检测可执行文件中的恶意软件。 2018

用石灰建立信任:解释机器学习预测

原文:https://towardsdatascience.com/building-trust-with-lime-explaining-machine-learning-predictions-55ba1fc0cb27?source=collection_archive---------64-----------------------

Joshua Hoehne 在 Unsplash 上拍摄的照片

解释机器学习预测,用石灰建立信任

作者:杨紫琼 — 6 分钟阅读

不用说:机器学习是强大的。

在最基本的层面上,机器学习算法可以用来对事物进行分类。给定一组可爱的动物图片,分类器可以将图片分成“狗”和“不是狗”两类。给定关于顾客餐馆偏好的数据,分类器可以预测用户接下来去哪家餐馆。

梯度下降优化器对神经网络训练的影响

Daryl ChangApurva Pathak — 23 分钟读取

欢迎来到我们深度学习实验系列的另一部分,在这里我们进行实验来评估关于训练神经网络的常见假设。我们的目标是更好地理解影响模型训练和评估的不同设计选择。为了做到这一点,我们提出了关于每个设计选择的问题,然后运行实验来回答它们。

Sean Lim 在 Unsplash 上拍摄的照片

实用 Cython —音乐检索:短时傅立叶变换

通过 Stefano Bosisio — 10 分钟阅读

我非常喜欢 Cython,因为它吸取了两个主要编程领域的精华:C 和 Python。这两种语言可以以一种简单明了的方式结合在一起,以便为您提供计算效率更高的 API 或脚本。此外,用 Cython 和 C 编写代码有助于您理解常见 python 包(如sklearn)下的内容,让数据科学家更进一步,这与简单的import torch和预定义算法用法相去甚远。

作者形象

互换:soft max-加权平均池

肖恩·贾恩和布莱克·埃利亚斯——7 分钟阅读

我们提出了一种卷积神经网络的池化方法,作为最大池化或平均池化的替代方法。我们的方法 softmax-加权平均池(SWAP)应用平均池,但是通过每个窗口的 soft max 对输入重新加权。虽然向前传递的值与最大池化的值几乎相同,但 SWAP 的向后传递具有这样的属性,即窗口中的所有元素都接收渐变更新,而不仅仅是最大值。

Fatos Bytyqi 在 Unsplash 上拍摄的照片

零到 Jupyter 与 Docker 在亚马逊网络服务上

约书亚·库克 — 11 分钟阅读

AWS 是占主导地位的云服务提供商。我们不赞同他们的统治地位是使用他们服务的理由的想法。相反,我们提出了一个 AWS 解决方案,它最容易被大多数人采用。我们相信,这种方法将推广到其他基于云的产品,如 DigitalOcean 或 Google Cloud Platform,前提是读者可以通过安全外壳(ssh)访问这些系统,并且它们运行的是 Linux 版本。

GitHub 动作介绍

作者:德博拉·梅斯基塔(Déborah Mesquita)

如果您是 DevOps 和 CI/CD 世界的新手,GitHub 操作可能会有点混乱,所以在本文中,我们将探索一些特性,看看我们可以使用该工具做些什么。

从 CI/CD 的角度来看,GitHub 动作和工作流的主要目标是在每次有变化的时候测试我们的软件。这样我们可以发现错误,并在错误出现时立即纠正。

基于预期目标的场馆调整 RAPM 建设:起源、过程和结果(上)

原文:https://towardsdatascience.com/building-venue-adjusted-rapm-for-expected-goals-the-origin-the-process-and-the-results-part-1-1c12f68df0cc?source=collection_archive---------44-----------------------

起源:是什么激励我建造这一切?

Evolving-Hockey.com报道,在 2019 年至 2020 年常规赛中,明尼苏达州野生滑冰运动员在 NHL 的 goals above replacement (GAR)中领先。换句话说,他们的计算通过调整赛程强度和背靠背等背景因素来隔离团队表现,并将团队守门员的表现与他们的运动员分开,从而得出结论:如果你用一名替补级别的球员(一名容易被替换的球员,低于他们球队图表上的第 13 名前锋、第 7 名防守队员或第 2 名守门员)替换明尼苏达野外的每一名运动员,那么他们的净胜球差将比你对 NHL 中的其他球队做同样的事情时下降更多。更简洁地说:通过这种模式,除了守门之外,明尼苏达野生队是 2019-20 常规赛中 NHL 最好的球队。

如果你不熟悉替代以上的目标,我会列出一些快速的基础:这是一个由 EvolvingWild 制作的非常有效的模型,允许我们通过将玩家的个人贡献与各种外部因素(如竞争、队友和使用)隔离开来,然后将他们的贡献与典型的替代级别玩家的贡献进行比较,并确定他们增加了多少价值,来改善我们的玩家评估。该模型是在单个玩家级别上从头开始构建的,团队价值仅仅是聚合的玩家价值,但统计数据也可以用于团队级别。自 2007-2008 年以来,常规赛进球超过替补的球队赢得了 60.5%的季后赛系列赛,而常规赛排名更高的球队仅赢得了 55.9%的系列赛,这表明该模型具有可靠的预测能力,尽管它本质上是描述性的。我不会进一步深入细节,但这是一个很好的模型,可以说是比目前存在的任何其他曲棍球统计数据更好的“全面”反映球员价值的快照。您可以从这个由模型创建者撰写的 3 部分系列文章中了解更多详细信息:

[## 超越替代的胜利:历史、哲学和目标(第一部分)

胜于替补(WAR)是一个由 sabermetric 社区在过去的几年中创建和发展的棒球度量标准…

hockey-graphs.com](https://hockey-graphs.com/2019/01/16/wins-above-replacement-history-philosophy-and-objectives-part-1/)

现在,回到野外。考虑到他们的运动员在净胜球上排名第 19,在得分率上排名第 21,这似乎有点令人惊讶。如果我们假设模型是有效的和准确的——我很乐意这样做——那么模型输出和真实排名结果之间的差异只能用两个因素来解释:

  1. 荒野有着联盟中最艰难的赛程,每隔一个晚上与联盟中的顶级球队背靠背比赛。
  2. 荒野的守门员太糟糕了,他们把一个真正的斯坦利杯竞争者降到了第 21 位。

从模型本身来看,多半是后者。他们的守门员以 6.8 个进球 排在 替补之后,排名倒数第一。这意味着,如果你把一个典型的第三阵容守门员放在网上,他们的净胜球将会 提高 大约 7 个球。这比第二差的守门员差得多,第二差的守门员属于底特律红翼队,贡献了 1.2 个进球。这些数字不太容易用储蓄百分比来调和;明尼苏达州守门员的综合扑救百分比为 90.25%,而底特律的守门员的综合扑救百分比为 89.36%。那么,《进化中的曲棍球》是如何计算出底特律有明显更好的守门员的呢?

答案在于调整拍摄质量。Evolving Hockey 根据各种因素(例如报告的离网距离(到目前为止最重要的因素)、报告的离网角度和比赛强度)计算射门得分或失球得分的概率,以计算每次射门得分的概率,并根据射门得分的概率为该射门分配“预期得分”值。(举个例子,一个有 35%机会成为进球的危险射门,其预期进球值为 0.35。)他们的计算发现,与底特律的守门员面对的平均射门相比,明尼苏达的守门员面对的平均射门明显不太可能成为进球——以至于当你调整这些射门的质量时,底特律的守门员看起来要好得多,尽管他们的扑救百分比较低。直觉上,这是有道理的。我们知道,决定射门成为进球概率的最大因素是离网的距离。我们还知道,底特律守门员面对的平均射门距离为 32.2 英尺,而明尼苏达守门员面对的平均射门距离为 36.69 英尺。所以,你有它:野生动物只是需要更好的守门员,他们将是联盟中最好的球队。对吗?

不完全是。虽然明尼苏达州的守门员面对的射门平均来说比底特律的守门员面对的更容易,但我刚刚列出的统计数据可能没有描述实际发生的事情,这也是事实。如果你仔细再读一遍前面的段落,你会发现我很小心地指出这是每次射门的距离。我做出这种区分是因为我有充分的理由相信 Xcel 能源中心(明尼苏达州的主场)的记分员报告说,那些射门的距离比实际距离稍微远一些,并且这些记分员对那些没有入网的射门也是如此。

:在本文的剩余部分,我将使用术语“射门”不仅指球门上的射门,也指没有入网的射门。这些射门被分析界称为 Fenwicks(以分析师 Matt Fenwick 命名),我用它们来代替射门,因为我相信迫使射门偏出和射门入网是守门员和溜冰者分别拥有的技能,我希望表扬那些在这些技能上表现出熟练程度的人,惩罚那些没有的人。

现在,回到野外:指责明尼苏达州的记分员工作不力似乎有些苛刻,但有太多的证据表明不能置之不理。在过去 13 个赛季中的每一个赛季,明尼苏达主场比赛在冰场两端的平均报道击球距离都比客场比赛的平均报道距离远得多。以下是每一个赛季的数字,显示在联赛平均水平旁边,让你对分布有个概念:

图片由 TopDownHockey

在客场,明尼苏达的比赛风格在比联盟平均水平更接近外线和比联盟平均水平更接近球网之间来回摇摆,但从来没有巨大的差异。在这段时间里,明尼苏达客场比赛报道的平均投篮距离是 34.69 英尺,而所有 NHL 比赛报道的平均投篮距离是…34.69 英尺。相比之下,明尼苏达的主场比赛报道的平均投篮距离是 37.27 英尺。

图片由 TopDownHockey

这不仅仅是一次性事件;在超过 500 场主场比赛、超过 500 场客场比赛和超过 15,000 场 NHL 比赛的样本量中,这种现象年复一年地存在。

对错误的计分假设的一个有效的反驳是,野生动物只是在家里玩的风格不同于在路上。也许他们在主场会加强控制,减少冒险,因为他们觉得自己能够控制比赛的进程,但在客场他们会采用对手更典型的风格。这是有可能的,但是这种趋势也不太可能持续十三季。当你考虑到野外有四个总经理和六个不同的主教练负责这个样本时,这种可能性就更小了,他们每个人都可能部署不同的策略。在这些赛季的每一个赛季中,无论是谁执掌球队,他们客场比赛的平均射门距离从未在任何一个方向上偏离联盟平均水平超过 1.5 英尺,而他们主场比赛的平均射门距离一直超过联盟平均水平 1.5 英尺以上。因此,虽然我们不能 100%确定明尼苏达州的记分员跟踪投篮数据是错误的,但这显然比任何其他结论更合理,需要的假设也明显更少。考虑到这一点,我将继续假设明尼苏达州的记分员确实表现出一种错误地跟踪比实际距离更远的投篮的模式,我将把这种行为模式称为**“记分员偏见”****

现在,我们已经为记分员偏见奠定了基础,并同意在假设它确实存在的情况下继续工作,下一个问题是,这究竟如何影响曲棍球在明尼苏达野生动物替代排名之上的目标。这个问题的答案是防御。在运动员层面,这一指标的防守部分主要是通过预期目标来量化的,正如我们已经提到的,该统计的最大组成部分是报告的允许击球距离。如果明尼苏达州的记分员报告射门距离球网比实际距离远,那么他们的运动员允许的射门的预期进球值将低于这些射门成为进球的“真实”概率,因此看起来这些运动员在抑制预期进球方面比实际情况做得更好。

这是另一种形象化的方式:想象一个对手在投币口击球。如果该射门被正确记录,预期目标模型可以计算出它有 25%的得分概率,并且该射门将被分配相应的预期目标值 0.25。但是,如果明尼苏达州的记分员将此记录为从圆圈外射门,预期目标模型可能会使用报告的距离来计算它只有 10%的得分机会,并且该射门只会被分配 0.1 的预期目标值。这将会不准确地吹捧明尼苏达州的滑冰运动员的防守表现,暗示他们允许一个比他们实际做的更不危险的射门。而且,如果明尼苏达队的守门员在那次射门中允许进球,他们将被错误地记为比预期低 0.9 球,而不是比预期低 0.75 球。这听起来不多,但在一个赛季的过程中,或者在许多赛季的过程中,这加起来是一个非常重要的数字:

图片由jfreshchoke提供。数据来自自然属性绝招

正如你所看到的,明尼苏达一直被糟糕的守门员表现所困扰。但是这个问题由于记分员的偏见而变得更加复杂,当他们在客场比赛时,这种偏见并不存在。

进攻呢?上表中明尼苏达州的主客场比赛显示了两个队的射门,这意味着明尼苏达州的记分员也报告说,主场比赛中的射门距离球网比实际距离要远。因此,从逻辑上来说,明尼苏达投篮的预期进球值也会不准确地低,因此他们制造危险投篮的进攻能力应该被高估,就像他们压制危险投篮的防守能力被高估一样,对吗?从上面同样的视觉化,看进球 得分 ,而不是扑出,上面的主客场预期支持了这一点:

图片由jfreshchoke提供。数据来自自然属性绝招

正如你所看到的,明尼苏达的滑手在家里看起来像精英射手,但在路上略低于平均水平。从逻辑上讲,人们可能会怀疑这些问题本质上是相互平衡的。他们确实这么做了。双方的预期目标是平等的。但与防守不同,替换目标以上使用实际目标而不是预期目标来衡量进攻贡献。由于 NHL 审查每一个进球,以确保它进入网内,记分员的偏见将不会在实际进球中发挥作用。从理论上讲,如果野生动物表现出一般的进球能力,不管他们是通过糟糕的射门质量和出色的射门还是相反的方式做到这一点;他们高于替补的进攻目标将是一般球队的目标。

然而,明尼苏达队本赛季实际上并不是一支普通的进攻球队。他们明显高于平均水平。这可能会让你感到惊讶(这让我感到惊讶),但明尼苏达实际上在每小时 5 对 5 进球方面排名第五,在每小时强力进攻进球方面排名第十。这与他们在替补得分上的排名是一致的:在力量进攻部分排名第五,在力量进攻部分排名第九。鉴于这些信息,我可以很舒服地说,这种模式给了他们的进攻一个公平的震动,而防守是这种特定模式的唯一问题。

在我们继续之前,让我们记住我们从哪里开始:发展曲棍球的目标高于替代是一个非常有效的评估指标,它表明在 2019-2020 NHL 常规赛中,明尼苏达野生队拥有联盟中最好的滑冰运动员和联盟中最差的守门员。但是我们有理由非常有信心地相信,明尼苏达州主场馆的记分员一直报告说,射门距离球网比实际距离稍远,我们知道,如果他们这样做,那么高于替换的进球将高估明尼苏达州滑冰运动员的防守性能,代价是守门员,而他们的进攻没有受到惩罚。

虽然我只关注 XCel 能源中心的记分员,但他们并不是唯一表现出记分员偏见的人。他们甚至不是最糟糕的罪犯。历史上,迄今为止最糟糕的犯规者是麦迪逊广场花园的记分员,他们与明尼苏达的记分员相反,报告说射门比他们更靠近球网,不知何故甚至不如明尼苏达的记分员准确。此外,虽然麦迪逊广场花园在过去两个赛季中实际上已经改善了他们的问题,但明尼苏达仍然没有他们主客场投篮距离之间的最大差异。

图片由 TopDownHockey

正如你所看到的,费城飞人队主场在富国银行中心的平均击球距离的绝对值实际上比在野外要大。达拉斯、阿纳海姆和芝加哥也很近。会不会是大部分冰场都有某种程度的记分员偏见?下面的图像表明情况可能是这样的:

图片由 TopDownHockey

正如你所看到的,球队在主场比赛时比在客场比赛时平均投篮距离的差异要大得多。在客场,每支球队的平均投篮距离在 32.5 英尺和 35.5 英尺之间,而在主场,球队的平均投篮距离在 30.5 英尺和 37.5 英尺之间。另外,主场距离的方差是 2.06 英尺,而客场距离的方差只有 0.31 英尺!****

因此,公平地说,明尼苏达州的 Xcel 能源中心可能不是唯一一个遭受记分员偏见的溜冰场。我认为这是有道理的,从逻辑上讲,人为错误在这里发挥作用,大多数记分员做的事情与其他记分员略有不同。在一个完美的世界中,我们会让记分员前往不同的竞技场,以便随机处理这个问题,但这个世界并不完美,这样做会产生大量的旅行费用,所以我们必须利用现有的数据。

我还应该指出,我不是唯一认识到这一点的人,我也不是第一个。2007 年,我十岁的时候,艾伦·赖德发布了“关于‘镜头质量’的产品召回通知”,他在通知中介绍了这个问题并声明:

我们显然有一个 RTSS 得分手偏见的问题。这个问题在麦迪逊广场花园看起来很残酷,但在其他地方显然也很重要。NHL 需要认真审视这一过程的一致性。

许多其他曲棍球分析家认为这是一个问题,并提供了额外的证据来支持他们的立场。很明显,记分员偏见在 NHL 是一个真实的现象。

现在,我们已经确定 XCel 能源中心存在记分员偏见,解释了它如何导致我们不准确地评估明尼苏达州野生的滑冰运动员和守门员,并确定 XCel 能源中心不是唯一存在记分员偏见的竞技场,它只是逻辑上遵循许多其他滑冰运动员和守门员可能会由于记分员偏见而被高估或低估。例如,据报道,过去两年在本田中心举行的阿纳海姆鸭队比赛中,平均射门距离球网 2.56 英尺,比这个样本中阿纳海姆鸭队客场比赛的平均射门距离近 2.56 英尺。如果这个数字实际上是射门距离被不准确报告的结果,那么从逻辑上来说,阿纳海姆的防守表现将被低估,他们的守门员将被高估。达拉斯也是如此,费城反过来也是如此。这里的底线是记分员的偏见导致我们对运动员和守门员的一些不准确的评估,特别是在进球高于替补的情况下。

我们如何解决这个问题?嗯,不要。模型不是我做的,我也无权决定如何修复我认为是问题的东西。这甚至不是由我来决定这是一个问题还是需要解决。我在这里的目的只是简单地提供一个估计,即记分员偏差的影响可能在多大程度上歪曲了滑冰运动员和守门员在冰上的实际表现,并允许我们在分析不断发展的曲棍球目标高于替换和其他高级曲棍球统计数据时记住这些估计的影响,这些统计数据使用预期目标,并不考虑记分员偏差的影响。**

为了做到这一点,我要做三件事:

  1. 建立一个预期目标模型,它在测试中表现得足够好,以至于我可以放心地将它作为一个描述性模型来评估已经发生的投篮质量。
  2. 确定记分员偏差的调整,当我用我计算的调整后的射门距离替换我获得的报告射门距离时,该调整不会损害我的预期目标模型的性能。
  3. 建立一个规则的调整正负(RAPM)模型,允许我提供一个球员对预期目标的孤立进攻,防守和净影响的点估计。我会这样做,因为 goals above replacement 使用球员对预期目标的孤立影响来对抗来自发展中的曲棍球的 RAPM 模型,作为评估滑冰运动员防守的主要组成部分之一,他们使用的大多数其他组成部分与这一措施密切相关。因此,RAPM 将提供一个坚实的估计,什么样的运动员的贡献将是上述目标的防守组成部分替换。(此外,RAPM 本身也被许多粉丝和分析师用作评估运动员的工具,对球员进攻对预期目标的影响的分数估计在那里比在替代目标中更有分量。)

此过程的最终目标是使用报告的击球距离提供一名选手的单独进攻、防守和净影响的单点估计,使用调整后的击球距离提供一名选手的单独进攻、防守和净影响的单点估计,然后并排显示它们,以便根据这些模型之一提供对记分员偏差的调整如何影响选手或球队的单独影响的估计。我还希望对守门员做同样的事情,使用报告的射门距离提供守门员孤立表现的一分估计,使用调整的射门距离提供守门员孤立表现的一分估计。我的假设是,像明尼苏达州的亚历克斯·斯塔洛克和德万·杜布尼克这样的守门员,在调整了记分员的偏见后,看起来会明显更好。

在第 2 部分中,我将回顾这个过程。敬请关注。

基于预期目标的场馆调整 RAPM 建设:起源、过程和结果(下)

原文:https://towardsdatascience.com/building-venue-adjusted-rapm-for-expected-goals-the-origin-the-process-and-the-results-part-2-9245ae51e23d?source=collection_archive---------44-----------------------

过程:我是如何建立这一切的?

照片由克莱门特 HUnsplash 上拍摄

在本文的第一部分,我为几个关键概念奠定了基础:

  1. 不断发展的曲棍球的高效目标高于替代模型得出结论,明尼苏达州荒野在 2019-2020 年常规赛中拥有 NHL 最好的滑冰选手和最差的守门员。
  2. Xcel Energy Center 是明尼苏达野生动物队主场比赛的场地,该中心的记分员表现出一种错误的报告模式,即投篮距离球网比实际距离远,我称之为“记分员偏差”。
  3. 如果射门(定义为射门和失球)被报告为离球网比拍摄时更远,这将导致上述目标高于替代模型以守门员为代价高估明尼苏达州运动员的防守性能,而不会影响运动员的进攻性能。
  4. Xcel 能源中心的记分员并不是唯一表现出记分员偏见的人。他们甚至不是最差的。

我最后说,我的目的不是“解决”这个问题,而是提供一个估计,记分员的偏见在多大程度上歪曲了滑冰运动员和守门员在容易发生这种情况的冰场上的行为。然后我概述了我打算做的三件事:

  1. 建立一个预期目标模型,它在测试中表现得足够好,以至于我可以放心地将它作为一个描述性模型来评估已经发生的投篮质量。
  2. 确定记分员偏差的调整,当我用我计算的调整后的射门距离替换我获得的报告射门距离时,该调整不会损害我的预期目标模型的性能。
  3. 建立一个正则化的调整正负(RAPM)模型,允许我提供一个滑冰运动员对预期目标的孤立进攻,防守和净影响的点估计。这将允许我比较一个运动员在调整记分员偏差前后的单独影响,并确定记分员偏差对他们未调整结果的影响。

如果您还没有阅读本文的第一部分,我建议您在继续阅读之前先阅读一下。它会让你更好地理解手头的主要概念,并解释为什么我能在我所做的假设下舒服地工作。话虽如此,如果你已经很好地理解了替代目标、预期目标、RAPM 和记分员偏差等概念,并且你真的只是对我最近在 Twitter 上发布的一些图表和统计数据背后的方法论感兴趣,那么请随意继续阅读。

我以最少的编码经验开始了这个过程。我学过一点 Python,包括一门大学入门课程,还完成了 EDX 的免费在线“统计和 R”课程。大概就是这样。虽然我在 Python 方面有更多的经验,但我选择了使用 R,因为在我有限的时间里,我觉得使用 R 更舒服,更重要的是因为 Evolving Hockey scraper 是用 R 编写的。虽然我在这个 Scraper 的主题上,但我不能不感谢 EvolvingWild 创建了这个 Scraper 并向公众提供它。

我用这个刮刀从 2018-2019 和 2019-2020 NHL 赛季的每一场记录比赛中提取数据。我想使用不止一年的样本量,因为我正在研究的所有指标都被认为在多年样本中更可靠,但我选择不使用三年或更长时间的样本量,因为NHL 在 2018-2019 赛季之前缩小了最大守门员垫尺寸。这一根本性的变化对任何投篮成为进球的概率都产生了重大影响,我过去曾注意到,2018-2019 年之前建立的预期进球模型表现出严重低估投篮质量的模式;我不想我的孩子遭受同样的问题。

一旦我确定了我想要的样本并为它收集了数据,下一步就是构建预期的目标模型。我首先对预期目标模型的历史和理论做了一些研究,发现它们通常是用两种方法之一构建的:

  1. 逻辑回归:一种统计建模技术,使用其他预测变量来预测一个二元变量的概率。在这种情况下,二元变量是投篮是否成为进球,预测变量是距离、投篮角度、比赛实力状态、投篮是否是篮板等。
  2. 梯度推进:一种用于回归和分类问题的机器学习技术。

这里的两个关键区别是,基于梯度推进的模型比基于逻辑回归的模型产生的结果稍好,但是逻辑回归更容易编码和实现,并且仍然提供良好的结果。因为我的目标是构建一个可靠的模型,我可以轻松地使用它,而不一定是构建最好的模型,所以我选择了逻辑回归。

在确定我可以使用逻辑回归建立一个可接受的模型之后,下一个问题是我如何确定一个模型可接受的。你如何让一个预期目标模型经得起推敲?我过去“测试”公共模型的一种方法是在团队级别检查预期目标份额(xGF%)和实际目标份额(GF%)之间的相关性。大多数公共模型的 R 在 0.4 到 0.46 之间,因此 R 为 0.4 的预期目标模型将通过该测试。但是这种测试也有问题——一些球队的射门和守门能力比其他球队更好——我从来没有见过一个可信的分析师这样测试一个模型。我想确保我用来测试我的模型的任何方法都是已经被其他分析师使用过的可靠方法,所以我着手确定测试我的模型的最佳过程。

我的研究发现,测试预期目标模型最常见的方法是测量曲线下面积(AUC);通过接收机工作特性(ROC)测试获得的值。如果你不熟悉这些东西,你和几周前的我没什么不同,所以让我来给你解释一下:在第二次世界大战中,雷达接收器操作员是一个分析雷达并确定雷达上的光点是友军船只、敌军船只还是随机碎片/噪音的人。他们进行这些区分的熟练程度被称为他们的“接收器操作特征”接收器工作曲线在 x 轴上绘出了信号检测器的假阳性率,在 y 轴上绘出了信号检测器的真阳性率,曲线穿过并示出了给定假阳性率的对应真阳性率。

无用或完全随机的模型的 AUC 为 0.5,而“一般”模型通常被认为是 AUC 在 0.7 到 0.8 之间的模型。据我所知,他的是每个公众期望的目标模型的范围,大多数更受欢迎的模型在该范围的上端。(作为参考,发展曲棍球的预期目标模型的平均力量击球的 AUC 为 0.7822,强力击球的 AUC 为 0.7183,短手击球的 AUC 为 0.7975。)

请记住,我在这里的目标不是创建现有的最佳预期目标模型,而是创建一个足够可靠的模型,我可以用它作为过去事件的描述模型。我建立的第一个模型使用距离作为唯一的变量,我仍然获得了 0.7045 的 AUC,这在技术上意味着它符合公平模型的条件。为了给你一个形象的描述,我的 ROC 曲线是这样的:

图片由 TopDownHockey

在这一点上,我很兴奋我已经看到了一些结果,但我也意识到我还没有做那么多,我可能应该尽我的努力在前进之前对这个模型的做一些改进。

接下来的几天,我修补了一些我认为可能会提高模型性能的变量。这里的过程只是我构建不同的虚拟变量(值为 0 或 1 的分类变量,表示该变量是否存在),并在添加它们之前和之后测试我的模型的 AUC,以查看它们是否有任何改进。以下是我在最终模型中使用的变量:

  • 射击距离。(连续)
  • 拍摄角度。(连续)
  • 拍摄和之前事件之间的时间差。(连续)
  • 射击类型。(分类)
  • 射手实力。(分类)
  • 游戏实力状态。(分类)
  • 之前的事件是否是拍摄团队的外卖。(假人)
  • 之前的事件是否是对手的赠品。(假人)
  • 一个投篮是否跟随对方球队的一个被阻挡的投篮。(假人)
  • 一枪到底是不是冲。(假人)
  • 一次击球是否是前一次击球零秒后发生的反弹。(假人)
  • 一次击球是否是前一次击球后一秒钟发生的反弹。(假人)
  • 一次击球是否是前一次击球后两秒钟发生的反弹。(假人)
  • 一次投篮是否是在前一次投篮后三秒钟发生的反弹。(假人)

为什么我在选择篮板球的分类上如此小心翼翼?因为我发现篮板对进球几率的影响有很大的不同,这取决于它们发生的时间。以下是我对每个系数的估计:

图片由 TopDownHockey

我完全可以猜到零秒的篮板最有可能成为进球。这很有道理。我想不到的是,一秒钟的篮板比两三秒钟的篮板更不容易得分。这是为什么呢?我不能肯定地说,但我的理论是:零秒篮板是快速的,没有给守门员时间回到位置的 bang-bang 比赛,而一秒篮板通常是“强迫”射门的结果,守门员只有足够的时间回到位置并准备做出扑救。相比之下,2 秒和 3 秒的篮板更多的是在第一次射门后传球或偏转的结果,这使得守门员离开了位置,造成了更多的“混乱”

这只是我的理论,不管它是不是真的,最终都无关紧要;重要的是,包含这些变量为模型增添了独特的风格,并提高了性能。最后,我得到了 0.7707 的 AUC,仅次于预期目标模型的 AUC。这是我改进的模型中的大鹏鸟的图像:

图片由托普顿曲棍球

如果你回头看第一个只使用距离的 ROC 曲线,你会发现有很大的改善。我对自己在这一点上所做的感到非常自豪,我很高兴能够继续这一过程的下一步,即进行场地调整,以解决记分员的偏见。

创建场地调整是这个过程中最简单的部分,因为已经有很多关于场地调整的优秀作品可供公众使用。在我发现的场地调整中,我发现最可行的两个是迈克尔·舒克斯(Michael Schuckers)(在这篇文章的第 9 页)和肯·克日维奇(Ken Krzywicki)(在这篇文章的第 2 页)。

我实现了两种调整,并使用调整后的距离数据再次测试了模型。我发现 Schuckers 的调整稍微损害了模型性能,而 Krzywicki 的稍微改善了模型性能,将 AUC 从 0.7707 增加到 0.7708,所以我选择了 Krzywicki 的。这是一个非常小的改进,但这正是我所需要的。以下是 Krzywicki 调整背后的方法论:

调整后的距离=(报告距离)-(Rink 平均报告距离)

有时候最简单的方法也管用。

现在,我已经建立了一个预期目标模型,并实施了一个可接受的场地调整,我对 2018-2019 和 2019-2020 赛季的每一个畅通的镜头都有两套预期目标值:一套在场地调整之前,一套在场地调整之后。下一步是建立一个正则化的调整正负模型(RAPM)模型,使用根据报告的击球距离计算的预期目标值来隔离每个运动员的进攻、防守和净影响,然后根据调整的击球距离的预期目标值进行同样的操作。

这是这个过程中最难的部分。请记住,在这个项目之前,我的编码经验非常有限,虽然我通过搜集数据、创建预期目标模型和应用场地调整学到了很多东西,但我仍然远远不是编程专家。不过,在讨论我的经验之前,我将向您概述一下什么是 RAPM 以及我为什么在这里使用它。

我之前说过,一旦我建立了一个 RAPM 模型,我将“对一个球员的单独进攻、防守和对预期目标的净影响进行点估计。”但是你如何得到它呢?曲棍球是一项流动性很强的运动,以至于很难对一名球员的单独冲击做出哪怕是一点点的估计。当你说 RAPM 是在没有使用任何单个结果的情况下计算出的,而仅仅是冰上结果时,这听起来就更加困难了。

说实话,这不是一个简单的概念。我花了一段时间来理解它,我已经尝试解释了很多次,几乎每次都失败了。这一次,为了解释 RAPM 是如何工作的,我将从我建立这些模型的经验中吸取教训,并通过尽可能少的数据点来解释这个概念。

让我们假设影响冰上进攻结果的唯一背景因素是康纳·麦克戴维是否在场外的冰上比赛。其他一切——所有其他队友和对手,转变开始于哪个区域,比分是多少——这些都不重要。这听起来是不是很傻?是的,确实是这样,而且完全是愚蠢的,但是我保证这将有助于理解这一点。

这个傻乎乎的虚构世界与现实世界有一个共同点:康纳·麦克戴维(Connor McDavid)极其擅长进攻,并对他的团队产生进攻的速度产生强烈影响,这种速度是以每小时的预期进球数(xGF/60)来衡量的。我们有十个数据点可以让我们准确地确定麦克戴维的影响:没有康纳·麦克戴维的五个班次,球队的进攻率为 2.5 xGF/60;有康纳·麦克戴维的五个班次,球队的进攻率为 3.0 xGF/60。如果我们创建一个名为“Connor McDavid”的虚拟变量,当 McDavid 在冰上时,该变量的值为 1,当他不在冰上时,该变量的值为 0,我们的数据如下所示:

图片由 TopDownHockey

如果我们使用 xGF/60 作为我们的目标变量对该数据进行非常简单的回归,我们的斜率截距形式的趋势线将是 xGF/60 = 2.5 + 0.5*(Connor McDavid)。这意味着,对于没有康纳·麦克达维的典型转变,我们预计球队的冰上进攻预期进球率为 2.5 xGF/60,康纳·麦克达维的存在将该值增加了 0.5,因此我们预计有康纳·麦克达维的转变将看到进攻以 3.00 xGF/60 的比率产生。这意味着康纳·麦克戴维的孤立影响使他的球队的进攻率增加了 0.5 xGF/60。

现在,让我们介绍另一名球员,他也可以影响他们球队的冰上进攻率。记住,除了麦克戴维之外,没有什么比 T4 更重要的了,但是这个球员也很重要。但是我们不知道它们到底有多重要。

幸运的是,有了我们现有的数据,我们可以很容易地计算出它们有多重要。假设这个玩家在没有 Connor McDavid 的情况下跳出来进行转换,他们的冰上 xGF/60 是 2.8。我们知道,除了一名球员的控制之外,绝对没有其他外部因素影响这个比率,我们知道正常的比率是 2.5 xGF/60,所以我们可以得出结论,这名球员对他们球队的进攻率的孤立影响是增加了 0.3 xGF/60。我们不需要观察这种转变,我们不需要计算这个球员有多少进球或助攻——我们确切地知道他们的影响是什么。

现在,如果这个玩家跳出来和 Connor MC David 换班,他们以 2.9 xGF/60 的比率产生进攻,会怎么样?除了 McDavid 之外,我们不知道其他任何事情,但是 McDavid 在这种转变中在那里,所以我们从他们的冰上率中减去 3.0 xGF/60 的比率,发现他们对他们球队进攻率的单独影响是减少了0.1 xGF/60。同样,我们不需要观察这种转变来知道这个玩家的影响是什么。这是自上而下曲棍球分析的精髓:使用冰上结果来隔离球员的影响。****

现在,回到现实世界。一切又变得重要了。不仅是康纳·麦克达维,还有其他每一个队友,每一个对手,是在进攻区还是防守区开始转移,比分是多少,等等。哦,我们不仅要隔离球员的进攻影响,还要隔离他们的防守影响。这一切都让事情变得更加复杂,给我们的最终价值增加了许多不确定性,这就是为什么我尽可能使用“点估计”这个术语来衡量一个玩家的影响。

虽然事情现在变得更加复杂,但 McDavid 的例子为我们提供了一个如何隔离每个人的影响的例子:除了表示游戏强度、分数、转换开始的区域和其他一些变量的虚拟变量之外,还创建表示溜冰者是否在冰上的虚拟变量,因为我们知道这些事情很重要,并且我们希望对它们进行说明。此外,对于每个滑冰运动员,我们必须创建一个虚拟变量来表示他们是否在冰上,而是一个表示他们是否在冰上进行进攻,一个表示他们是否在冰上进行防守,因为我们想隔离他们的进攻防守影响,并为每个影响找到单独的点估计。

为了举例说明这一点,我将通过拉 2018-2019 赛季第二场比赛第三阶段发生的一次转变来“分块”我的真实数据集。关于这种转变,你需要知道以下几点:

  • 华盛顿首都队主场迎战波士顿布鲁因斯队。
  • 华盛顿首都队领先三个或更多的球。
  • 这一转变持续了 24 秒。
  • 这种转变始于波士顿进攻区的对抗。
  • 布鲁因斯拍摄了一个未被阻挡的镜头,该镜头的值为 0.1 xGF。
  • 首都队在这一次转移中拍摄了一个未被阻挡的镜头,其值为 0.02 xGF。
  • 波士顿的五名选手是布兰登·卡罗、约阿金·诺德斯特龙、克里斯·瓦格纳、诺埃尔·阿恰里和兹德诺·查拉。
  • 华盛顿的滑冰选手有安德烈·布拉科夫斯基、布鲁克斯·奥尔皮克、钱德勒·斯蒂芬森、拉斯·埃勒和麦迪逊·鲍威。

这是 RAPM 准备格式转变的局部视图:

图片由托普顿曲棍球

正如您所看到的,顶行从主队的角度将这种转变显示为“进攻”,而从客队的角度将这种转变显示为“防守”,而底行从主队的角度将这种转变显示为“进攻”。上面的图像只显示了一些防守的选手,但这里是进攻的选手。

图片由 TopDownHockey

请记住,整个顶行是从华盛顿首都的角度来看的。在顶行中,xGF/60 是首都队进攻的比率,标记为“进攻”的选手是那些为首都队效力的选手,标记为“防守”的选手是那些为布鲁因斯队效力的选手。相比之下,底部一行是从波士顿布鲁因斯的角度来看的,这意味着 xGF/60 是布鲁因斯产生进攻的速率,标记为“进攻”的选手是那些为布鲁因斯效力的选手,标记为“防守”的选手是那些为首都效力的选手。首都也领先至少 3 个球,所以他们被标记为“上升 3”,而布鲁因斯被标记为“下降 3”

要锁定两个特定的球员,只需看看安德烈·布拉科夫斯基和布兰登·卡洛。从首都的角度来看——第一排——布兰登·卡罗在防守,安德烈·布拉科夫斯基在进攻。从布鲁因斯(最下面一排)的角度来看,布兰登·卡罗在进攻,安德烈·布拉科夫斯基在防守。

这个例子是一个班次对应两行。我使用的样本大小的整个数据集包含 544,502 个移位,总共 1,089,004 行。这听起来很多,但这里的关键概念仍然存在:从每个团队的角度来看,每个班次包含两行,从虚拟变量返回的系数将确定某个变量(如 Connor McDavid 进攻)的存在对团队的预期进球率的影响。

这种使用回归分析曲棍球运动员的概念是基于他们在冰上发生的一切事情,这是大多数现代公共曲棍球分析的核心。我喜欢这样想,RAPM 不是通过专注于进球和助攻等个人统计数据,或者更“高级”的个人统计数据,如封锁传球和区域出口,来从头到尾分析曲棍球,而是从自上而下——因此有了用户名。如果一名球员阻挡了很多传球,并因此阻止了对手积累危险的得分机会,这将显示在他们的防守 RAPM 统计中。如果一名球员在防守区表现一般,努力阻挡传球,但他们是一名出色的前锋,不断阻止对手在转换中建立进攻,这也会在他们的防守 RAPM 统计数据中显示出来。如果他们做好了其中的一些事情,但他们也做了一些其他的事情,这些事情阻止了他们产生强大的影响,谁会真正在乎呢?这是我的哲学。

我的解释清楚地表明,我是 RAPM 的超级粉丝,但我没有在这里使用它,因为我喜欢它。我使用它是因为球员对预期目标的 RAPM 影响是替代目标以上防守部分最重要的指标之一,替代目标以上的其他部分与 RAPM 密切相关,因此提供场地调整前后球员 RAPM 的点估计将显示场地调整对替代目标以上某些部分的影响程度。(RAPM 本身也是一个非常受欢迎的指标,我认为它有助于了解场馆调整可能如何影响 RAPM 本身。)

既然我已经解释了自上而下曲棍球分析的概念,RAPM,以及为什么我选择使用它,是时候解释我是如何实现它的了。如上所示,我的数据框包含过去两个赛季中每次换班的两行:一行是主队“进攻”,一行是客场队“进攻”。我的目标是运行一个回归,通过获得系数估计值来隔离每个外部变量对 xGF/60 结冰速率的影响。

不幸的是,不像前面的例子中虚构的 Connor McDavid 数据,我不能只运行一个标准的多元线性回归。这有三个原因:

  1. xGF/60 表示某事物发生的速率,不考虑结冰时间。两次移动的速率可能相等,都是 4.0 xGF/60,但是如果其中一次发生在两分钟内,而另一次发生在两秒内,我们就不能简单地将它们组合起来,就好像它们是同一件事一样。两分钟内发生的那次更有意义。
  2. 数据中存在大量多重共线性。这是一种奇特的说法,表明这些数据中的许多变量彼此高度相关,这是一种奇特的说法,表明像肖恩·莫纳汉和约翰尼·高德罗这样的球员几乎所有的时间都在一起打球。具有多重共线性的数据集上的传统线性回归导致极不稳定的系数估计。
  3. 这种不稳定性也会存在于在非常小的样本中发布极端结果的玩家身上。如果你运行一个简单的多元线性回归,“最好的”球员可能会成为某个打了 13 分钟,冰上命中率达到 72%的人,或者类似的荒谬的人。

对于第一个问题,有一个非常简单的解决方法:使用轮班持续时间作为权重,将其转换为加权多元线性回归。砰,简单,搞定。

还有一个解决方案可以同时解决第二个和第三个问题,但是有点不太简单:正则化(RAPM 的 R)。正则化是一种数学技术,它使系数估计值偏向总体均值零。为了简化这个概念,并把它变成曲棍球术语,我们本质上是说,直到我们有足够的数据来准确地估计一个球员到底是什么,我们将假设他们大致是平均水平。这不仅适用于自己玩过很小样本的选手,也适用于玩过大样本,但是没有某个队友的小样本的选手。

我使用的正则化版本被称为岭正则化或吉洪诺夫正则化,我将这些系数偏向零或“收缩”它们的程度由我通过对我的原始数据集进行交叉验证测试获得的λ值决定。运行的最终回归被认为是“加权岭回归我可以更详细地解释这在数学上到底意味着什么,但是现在,这个模型仍处于测试阶段,我宁愿坚持从曲棍球的角度用尽可能少的数学来解释这个过程。

这整个过程与 Evolving Wild 用来建造他们的 RAPM 模型的过程极其相似。我非常感谢他们的赞扬;如果没有它,我不确定我能不能造出自己的模型。我有意密切跟踪他们的过程,因为正如我在第一篇文章中所述,他们的 RAPM 模型的防守部分在他们的目标替换模型中起着很大的作用,而大多数其他部分与 RAPM 密切相关,因此根据 RAPM 在场地调整前后对球员防守影响的点估计将使我了解场地调整如何影响替换后的防守目标。

虽然我的过程在设计上非常相似,但它在两个方面有所不同:我使用了不同的预期目标模型来构建它,并且我同时使用了两年时间。一次使用两年时间给了我更大的样本量,这意味着我通过交叉验证获得的 Lambda 值比我只对一年的数据进行交叉验证获得的值要小。因此,我的系数在那里“缩小”或偏向零,比进化的 Wild 的系数小度,我在光谱的两端得到更大的值。

虽然这是本文的第二部分,我不应该在第三部分之前讨论结果,但我仍然觉得有必要提供一个结果作为较小的 Lambda 值所做的示例,所以我将与 Mark Stone 一起讨论。在过去的两年里,根据我们的两个模型,他在平均实力下对预期目标的净影响是所有球员中最高的。但是他们的模型对他的净影响的系数估计是+0.46 xG/60;在场地调整之前,我的模型的系数估计值为+0.701 xG/60。

直观地说,我更喜欢同时使用多个年份的,因为的λ值较低,因此系数估计值较大。换句话说,我认为像 Mark Stone 这样的精英玩家的真实孤立影响可能比他们更接近我的系数估计,我更喜欢使用一种方法来使这种类型的结果成为可能。但这是个人喜好的问题,两种方式都有各自的优缺点。

一旦我使用通过报告的击球距离计算的预期目标值完成了这个过程,我就使用通过调整的击球距离计算的预期目标值重复了相同的精确过程。

结果非常有趣。它们将在第 3 部分中讨论。敬请关注。

基于预期目标的场馆调整 RAPM 建设:起源、过程和结果(下)

原文:https://towardsdatascience.com/building-venue-adjusted-rapm-for-expected-goals-the-origin-the-process-and-the-results-part-3-87db02cc2cfd?source=collection_archive---------38-----------------------

结果:记分员偏差有多大?

在为本文的第一部分和第二部分写了超过 7000 个单词之后,我很兴奋终于可以分享结果了。如果你一直在阅读,我相信你也是,我想感谢你花时间阅读我的作品。我希望你喜欢它。

如果你没有跟着读,没关系。我强烈建议你在阅读本文之前先阅读第一和第二部分;它们并不短,但它们会让你很好地理解为什么这对我很重要,为什么你也应该关心它。但是如果你对阅读不感兴趣,你只想看到结果,我不会责怪你,也不会阻止你阅读。但是,我将为您快速回顾一下我在第一部分和第二部分中介绍的内容:

  1. 明尼苏达州的记分员错误地报告说,射门距离球网比实际距离远,这导致他们的滑冰运动员的防守性能被高估,他们的守门员被高效的模型低估,如发展曲棍球的目标高于替换。明尼苏达州的记分员也不是唯一表现出这种行为模式的人,我称之为“记分员偏见”
  2. 我的目的不是“修复”这个问题,而是提供一个对它所导致的不准确性的估计。为此,我构建了一个预期目标模型,对击球距离进行了场地调整,从而提高了我的预期目标模型的性能,并构建了一个 RAPM 模型,它提供了一个溜冰者孤立影响的点估计。

对于结果,我将从团队层面开始分析,然后转移到守门员,最后是个人选手。

在团队层面,我会从防守开始。以下是每支球队在过去两个赛季的防守表现,以每小时预期进球数(xGA/60)衡量,地点调整前后:

图片由托普顿曲棍球

既然明尼苏达从本文开始就一直走在前列,那我们就重点关注他们吧。正如你所看到的,他们的防守在这次调整中受到了最大的惩罚。在调整之前,他们的 2.52 的 xGA/60 是联盟中最好的,第二名哥伦布以 2.66 的 xGA/60 舒适地坐在他们的后面。调整后,明尼苏达以 2.75 的成绩跌至 xGA/60 的第 6 位。他们的防守仍然很好,但是他们不再是最好的防守球队了。

在光谱的另一端,芝加哥的进步程度与明尼苏达的衰退程度差不多。他们是调整前防守最差的球队,调整后防守第四差的球队。阿纳海姆和纽约流浪者队也看到了重大的改善,而蒙特利尔和费城看到他们的防守人数下降到类似的程度。其他六个团队的 xGA/60 比率变化至少为 0.1,其中三个团队有所改善,三个团队有所下降。

我发现这里非常有趣的是这个图案的对称性。光谱两端各有一个团队,他们看到的极端变化彼此相同,两端各有两个团队,他们稍微不太极端的变化实际上相同,光谱两端各有三个团队,他们非极端但仍然显著的变化实际上相同。让我们看看同样的模式是否在进攻中持续,这是通过每小时的预期目标(xGF/60)和同一两年样本的数据来衡量的:

图片由 TopDownHockey

团队进攻没有团队防守那么对称,但模式是相似的:明尼苏达和蒙特利尔等球队的记分员报告说,投篮距离球网比实际距离远,他们的进攻有所加强,而芝加哥和阿纳海姆等球队的进攻有所下降。

让我感到惊讶的一件事是,明尼苏达的进攻比他们的防守下降了更大的程度。这意味着他们的预期目标差异有了显著的改善。他们是一个异数,还是这发生在许多不同的团队?让我们通过查看加减值来找出答案,这是通过在同一两年样本中每小时的预期目标差异(xG+/-/60)来衡量的:

图片由托普顿曲棍球

事实证明,明尼苏达是唯一一支预期净胜球率显著上升的球队。蒙特利尔的变化足以令人惊讶,但其他人都在误差范围内。明尼苏达为什么进步了?我不完全确定,但我有一个理论:明尼苏达在主场是一支很好的控球球队,有 51.93%的芬威克投篮命中率。他们拍摄的允许的拍摄值通过调整增加了,但是因为有更多的主场拍摄,这给了他们一个整体的提升。这一理论也适用于蒙特利尔和波士顿,这两支球队的预期净胜球率分别排名第二和第三。

他们主场芬威克的命中率分别是 54.74%和 53.71%,经过我的调整,他们在主场的支持和反对投篮变得稍微更有价值。这只是一个理论,我需要做更多的研究来确定为什么会这样。这也没什么大不了的,因为没有一支球队会看到他们的预期目标差异率变化太大。

为了更好地了解这些调整是如何影响我做了重大调整的球队在联盟范围内的排名,请查看以下可视化内容:

图片由 TopDownHockey

图片由托普顿曲棍球

正如你所看到的,一个好的团队通常仍然是一个好的团队,一个坏的团队通常仍然是一个坏的团队。但是,对于记分员有严重偏见的球队来说,他们擅长不同事情的程度会有很大的不同。

现在是时候转向守门员了。对于可视化,我选择使用超出预期的扑救目标(GSAx)来代替德尔塔芬威克扑救百分比(dfSV%)或另一个每射门/每分钟指标,因为我想提供一个守门员的综合指标受到了多大影响的想法。如果我使用一个每次射门的指标,一个无关紧要的守门员在 Xcel 能源中心或麦迪逊广场花园玩了一个小样本大小的游戏,大部分时间都在玩,可能会看到一个疯狂膨胀的“调整影响”。

在我的第一次可视化中,我任意设定了最少 75 场游戏。我这样做是因为我知道我需要为设定某种最小值,因为包括每个守门员将产生大约 100 个数据点——太多了,无法显示——我计算出每个球队自 2019-2020 赛季以来至少打了 150 场比赛,所以守门员需要至少打了他球队一半的比赛才能达到最低阈值。以下是达到我的最低门槛的守门员,按照场地调整对他们的 GSAx 的影响进行排名:

图片由 TopDownHockey

Dubnyk、Price 和 Gibson 都受到此次调整的巨大影响。杜布尼克从联盟中最差的守门员变成了……一个比少数人更好的非常差的守门员。凯里·普莱斯从一个低于平均水平的守门员变成了十佳守门员。约翰·吉布森从最好的国家之一发展到略高于平均水平。克劳福德、伦奎斯特、雷纳和毕晓普也受到重创。莱纳尤其有趣,因为尽管他在过去两个赛季为三支不同的球队效力,但他上场时间最多的两支球队的主场记分员历史上报告说,射门距离球网比实际距离更近,所以他因场地调整而受到重罚是有道理的。

创建了最后一个可视化后,我仔细检查了一下,发现卡特·哈特不见了。我可以发誓他是费城过去两年的首发,但结果却是他打了整整 74 场比赛。尽管他的样本很小,但他是受我的调整影响最大的球员之一,所以我讨厌我的想象没有包括他。Alex Stalock 也出现在本文第一部分的特色图片中,他也受到了我的调整的严重影响。我没有办法忘记这些人的存在,所以我为守门员创造了两个可视化效果来展示那些没有达到我最低出场次数的守门员:一个是受到场地调整最积极影响的十个守门员,一个是受到最消极影响的十个守门员。他们在这里:

图片由 TopDownHockey

图片由托普顿曲棍球

这里的要点很简单:像乔尔杰夫和谢斯特金比赛的麦迪逊广场花园这样的场地导致他们的守门员被没有考虑场地的指标高估,而像卡特哈特和布莱恩埃利奥特比赛的富国银行中心这样的场地导致他们的守门员被没有考虑场地的指标低估。

既然每个人都喜欢最好和最差球员的排名,我觉得我应该按照调整后的 GSAx 列出前 15 名和后 15 名的守门员,没有最低的比赛门槛。以下是前 15 名:

图片由 TopDownHockey

这是倒数 15 名:

图片由 TopDownHockey

正如你所看到的,这一调整对像 Devan Dubnyk 和 Alex Stalock 这样的守门员产生了非常重大的影响,他们在 Xcel Energy Center 打主场比赛,记分员报告说射门距离球网比实际距离远。这也对像 Ben Bishop 和 Anton Khudobin 这样的守门员产生了重大影响,他们在美国航空中心打主场比赛,记分员报告说,射门距离球网比实际距离近。但这种调整并没有显著到颠倒世界,告诉我们明尼苏达的守门员是好的,达拉斯的守门员是坏的。Bishop 和 Khudobin 仍然在 GSAx 排名前 15 位,而 Dubnyk 仍然在排名后 15 位,Stalock 只是勉强错过了晋级。

为了更好地了解这些调整如何影响守门员相对于他们的同行,请查看以下可视化:

图片由 TopDownHockey

图片由托普顿曲棍球

最后,在我进入滑冰者之前:我特此授予德万·杜布尼克“圣何塞鲨鱼队最差守门员”的称号。

请记住,我使用加权岭回归来计算一个称为正则化调整正负(RAPM)的指标,这是一个玩家的孤立影响的点估计。我这样做是因为进化曲棍球的目标高于替换使用一个球员的孤立(通过 RAPM)对预期目标的影响作为滑冰运动员防守的主要组成部分之一,因为大多数其他组成部分与 RAPM 密切相关。

在这里,我要做的第一件事是将我的 RAPM 在场地调整前的结果与所有玩了至少 200 分钟的冰上曲棍球 RAPM 模型的结果进行比较:

图片由 TopDownHockey

如您所见,两个模型的结果非常相似。这是我一直以来的目标:建立一个类似他们的模型,但是是我自己的,这样我就可以在场地调整前后比较我自己的模型。如果我只是建立了一个有场地调整的模型,并与他们的进行比较,我不会知道哪些差异是由场地调整引起的,哪些差异是由模型差异引起的。

正如我对团队所做的那样,我将从他们的防守开始我对运动员的分析。在我公布这些数字之前,我应该指出,这些数据大致遵循正态分布,原始 RAPM xGA/60 的标准偏差为 0.117,而调整后的 RAPM xGA/60 的标准偏差为 0.115。现在我已经澄清了这一点,以下是防守受到这一调整最消极和最积极影响的运动员:

图片由 TopDownHockey

图片由托普顿曲棍球

(记住,负数是好的,减少玩家 RAPM xGA/60 的调整意味着调整奉承他们。)

这与我们的预期大致相符:来自明尼苏达、费城和蒙特利尔的选手在调整后防守看起来稍差,而来自阿纳海姆、芝加哥和纽约流浪者的选手看起来稍好。但是他们受影响有多大?记住,这里的标准差大致是 0.116,所以没有球员的防守影响改变了哪怕一个标准差,只有大约十几个人看到他们的防守影响在任一方向上改变了半个标准差。虽然像阿纳海姆和明尼苏达这样的球队在进行场地调整后看起来有很大的不同,但他们的个人选手都没有看到巨大的变化。

进攻呢?进攻的标准偏差略大于防守:原始 RAPM xGF/60 的标准偏差为 0.122,调整后的 RAPM xGF/60 的标准偏差为 0.121。以下是进攻冲击最大的球员:

图片由 TopDownHockey

图片由 TopDownHockey

还记得在我实施场地调整后,明尼苏达看到他们的进攻比他们看到的防守下降有了更大程度的提高吗?嗯,这只能说明他们的选手看到了迄今为止所有队伍中进攻方面最大的进步。这也是有道理的,唯一一个看到他们的进攻或防守孤立影响改变至少一个标准差的球员是明尼苏达野生动物队的贾里德·司布真,这种改变是他进攻影响的增加。

加号/减号呢?既然记分员的偏见是双向的,那么大多数球员在场地调整前后不应该有相似的净影响吗?我们将深入探讨这一点,但在此之前,我们需要确定标准差,以便了解任何调整的幅度。原始预期目标差异率的标准偏差为 0.170,调整后预期目标差异率的标准偏差为 0.169。这比进攻或防守的规模要大得多,我们应该记住这一点。

现在我们已经确定了这一点,让我们来看看那些净影响受场地调整影响最大的玩家:

图片由 TopDownHockey

图片由 TopDownHockey

如你所见,大多数进攻调整基本上被防守调整抵消了;Jared Spurgeon 的调整是唯一一个甚至在净影响标准偏差的三分之一以内的调整。因此,公平地说,记分员偏见在球员对预期净胜球的净影响中没有发挥很大作用,如果我们只看这个指标,记分员偏见就不是问题。

以下是另一个形象化的例子,让您了解在场地调整前后,玩家相对于其他玩家的影响力会发生怎样的变化:

图片由托普顿曲棍球

上面的可视化是为了强调场地调整可以对球员的进攻影响做出重大改变,但他们的防守影响通常会在相反的方向上看到类似幅度的变化,因此他们的净影响将非常相似。在 Jonas Brodin 的例子中,你是使用原始的还是调整后的指标并不重要:他的净影响非常好。

当我们关注净影响之外的东西时,问题就出现了。正如我上面提到的,某些指标,如发展曲棍球的目标高于替代使用预期目标的防守,但实际目标的进攻。如果我们看看上面乔纳斯·布洛丁图表中间的两列,很明显,在我们调整了记分员的偏见后,他的防守明显变弱了。同样,这对于预期的净胜球差来说不是一个大问题,因为他的进攻被记分员的偏见所拖累,其程度与他的防守被支撑的程度大致相同,但如果我们使用一个不容易出现记分员偏见的不同指标(如进球),我们将高估他的防守影响,而不惩罚他的进攻。

当我们分析一个运动员时,这也不是什么大问题。如上所述,只有一名选手看到他们的进攻或防守影响改变了一个标准差,只有少数其他选手接近。但是,当我们分析一个全队的运动员时,平均每个运动员的防守都比实际情况好大约半个标准差,所有这些不准确性加起来会导致我们得出一些相当不准确的结论。请记住,无论记分员的偏见导致我们高估一支球队的防守,它导致我们随后低估他们的守门员(反之亦然)。

现在我们已经建立了所有这些,我们从这里去哪里?这不是我能决定的。就我个人而言,我仍然计划使用不断发展的曲棍球目标高于替代和 RAPM 模型来评估 NHL 滑冰运动员和守门员,以及其他没有纳入场地调整的模型,因为尽管记分员有偏见,它们仍然非常有效。我还计划交叉引用我自己的模型的结果,以确定记分员偏差是否影响其他模型的输出,以及影响的程度。但重要的是,现在我已经研究了这个问题,并做了一些工作来确定它可能会导致我对某些球员的评估有多不准确,我处于一个更好的位置来细微地处理这些数字,并且比以前更少错误地看待曲棍球。希望你看了我的研究也有同感。

如果你想与我进一步讨论这些细节,或者你只是想看到一些你最喜欢的球队或球员的可视化效果,请随时在 Twitter 上联系我,地址是 TopDownHockey

使用 Streamlit 为 NLP 项目构建 Web 应用程序

原文:https://towardsdatascience.com/building-web-applications-with-streamlit-for-nlp-projects-cdc1cf0b38db?source=collection_archive---------25-----------------------

实践教程

在解决 NLP 任务(如情感分析、NER 和文本摘要)时,Streamlit 基础知识的代码演示和解释。

图片来自 Unsplash

数据科学家面临的最常见的任务之一是以一种用户交互的格式呈现他们的模型/项目。如果模型不能在某种类型的应用程序中呈现,那么它对任何外部用户都没有多大用处。这介绍了 web/app 开发的广阔领域,然后引出了更多的语言和工具,如 HTML、CSS、ReactJS、Dash、Flask 等,它们可以帮助您创建一个前端界面,供用户与您的模型进行交互并从中获得结果。这个广阔的领域起初可能会令人生畏,并且与传统的数据科学技能集格格不入。

Streamlit 是一个简单的 Python API,它提供了一种方式来缩短这个学习曲线,并消除数据科学家了解这些工具的需要。一般的全栈库应该是大规模项目的首选,但是如果您的数据科学项目需要一个快速前端,Streamlit 不仅仅是为了这个目的。在本文中,我想演示 Streamlit 的基础知识,同时构建一个 web 应用程序,允许用户使用模型解决常见的 NLP 任务,如情感分析、命名实体识别和文本摘要。

注意:虽然我们可以为这些任务中的每一项构建定制模型,但我还是使用了 Python 库中预先训练好的模型,因为本文更侧重于构建一个 web 应用程序来通过 Streamlit 显示您的 ML/NLP 项目。

目录

  1. Web 应用程序设置
  2. 数据
  3. 情感分析
  4. 命名实体识别(NER)
  5. 文本摘要
  6. 整个代码和结论

1.Web 应用程序设置

在构建任何模型之前,我们需要一个应用程序外观的模板。让我们先导入 streamlit(为了安装使用 pip3 为 Python3 安装 streamlit)。

导入 streamlit

为了解释我们的应用程序是什么,让我们添加一个标题和一个小标题,询问用户希望从我们的三个选项中选择哪一个 NLP 服务。 Streamlit.title()Streamlit.header()Streamlit.subheader() 是按降序排列的不同标题级别。对于纯非标题文本数据,您可以使用 Streamlit.text()

用于创建应用程序标题的代码

Streamlit 的航向代码结果

现在我们有了应用程序的主要问题,我们可以构建一个下拉菜单,让用户从我们的模型可以完成的三个任务中进行选择。这可以使用 Streamlit.selectbox() 调用来完成。

用于创建带有选项的下拉菜单的代码

NLP 服务使用的下拉菜单

从三个选项中选择的服务作为字符串存储在选项变量中。需要输入的下一条信息是用户想要输入的文本文本,以便我们的模型处理/执行所选择的任务。为此我们可以使用 Streamlit.text_input() 调用,它类似于 HTML 中的 textarea 标签。

用于输入文本进行分析的代码

供用户输入模型的文本区域

然后,文本输入被存储在文本变量中。现在我们有了包含所有需要处理的信息的选项和文本变量,我们需要创建一个区域来显示结果。

现在我们的模板设置,我们有一些东西看起来像下面的。

用 Streamlit 制作的整体模板

只用了大约 10 行 Python 代码,就创建了一个非常简单而有效的模板。现在我们有了前端,我们可以处理/向我们的模型提供这些信息,并将结果返回给用户。

2.数据

对于 NLP 任务,我们将使用以下一篇名为“ 微软推出智能云中心以提升人工智能&云技术的学生的文本数据。文章/正文是通过中的文章找到链接的。我们将使用下面的文本块作为所有三个 NLP 任务的输入文本。

In an attempt to build an AI-ready workforce, Microsoft announced Intelligent Cloud Hub which has been launched to empower the next generation of students with AI-ready skills. Envisioned as a three-year collaborative program, Intelligent Cloud Hub will support around 100 institutions with AI infrastructure, course content and curriculum, developer support, development tools and give students access to cloud and AI services. As part of the program, the Redmond giant which wants to expand its reach and is planning to build a strong developer ecosystem in India with the program will set up the core AI infrastructure and IoT Hub for the selected campuses. The company will provide AI development tools and Azure AI services such as Microsoft Cognitive Services, Bot Services and Azure Machine Learning.According to Manish Prakash, Country General Manager-PS, Health and Education, Microsoft India, said, "With AI being the defining technology of our time, it is transforming lives and industry and the jobs of tomorrow will require a different skillset. This will require more collaborations and training and working with AI. That’s why it has become more critical than ever for educational institutions to integrate new cloud and AI technologies. The program is an attempt to ramp up the institutional set-up and build capabilities among the educators to educate the workforce of tomorrow." The program aims to build up the cognitive skills and in-depth understanding of developing intelligent cloud connected solutions for applications across industry. Earlier in April this year, the company announced Microsoft Professional Program In AI as a learning track open to the public. The program was developed to provide job ready skills to programmers who wanted to hone their skills in AI and data science with a series of online courses which featured hands-on labs and expert instructors as well. This program also included developer-focused AI school that provided a bunch of assets to help build AI skills.

3.情感分析

对于情感分析,我们将使用 textblob 库来生成输入文本的极性和主观性。(使用 pip3 为 Python3 安装 textblob)

为了标记数据,我们使用 NLTK 库将文本分割成句子,这样我们就可以将较大的文本分割成较小的部分进行可视化分析。使用 textblob 库,我们得到了每句话的情感来创建贯穿文本的情感的可视情节。:对于图书馆,情绪用-1 到 1 表示,1 表示最积极,-1 表示最消极。

策划每句话的情绪

使用 Streamlit.line_chart() 中的另一个简洁的 Streamlit 特性,我们可以用线图绘制每个句子的情感,以便用户直观地分析第二部分中显示的文本数据。

文本中每句话的情感得分图

如果我们想要对文本有一个总体的看法,我们可以对整个数据字符串使用 TextBlob API 调用。

整篇文章的整体情绪

使用 st.write() 调用,我们可以显示从 textblob 返回的情感。返回的两个特征是极性主观性。极性在[-1,1]之间,主观性在[0,1]之间,0.0 为客观,1.0 为主观。

输入文本的总体情感

4.命名实体识别(NER)

对于 NER,我们将在空间中使用另一个流行的 NLP 库。

Spacy 有一个很好的特性,它提供了类型的实体以及它所识别的实体。这方面的例子包括:人、组织、日期、金钱等等。为了提取实体及其标签,我们可以遍历找到的实体的整个列表,并将它们加入到一个字典中。

使用空间提取实体和实体类型

为了让我们的用户获得每种类型实体的直观表示,我们可以创建一个助手函数,列出每种类型的所有实体。

提取每种类型的实体的辅助函数

使用这个函数,我们可以提取每种类型的实体,并将结果写出来让用户清楚地看到。

将函数应用于编写每种类型的实体

文本数据中每种类型的实体

5.文本摘要

对于文本摘要,我们将使用 gensim 库,它有一个简单的摘要调用

Gensim 进口

使用 summary 调用,我们可以轻松地生成输入文本的摘要。注意:总结电话需要不止一句话来配合 gensim。

使用 gensim 的文本摘要

输入的摘要文本

6.整个代码和结论

完整代码

** [## RamVegiraju/NLPStreamlit

一个 Streamlit 应用程序,可以执行 NLP 任务,如情感分析、命名实体识别(NER)和文本…

github.com](https://github.com/RamVegiraju/NLPStreamlit)

Streamlit 允许您无缝集成流行的数据科学/ML 库,以在简单而简洁的 web 应用程序/仪表板上显示您的项目。虽然更常见的全栈技能组合将更好地服务于大规模项目,但使用 Streamlit 所需的短时间和简单性大大有助于数据科学家和 ML 工程师展示他们的项目。

我希望这篇文章对那些试图在 ML/NLP 项目中使用 Streamlit 的人有用。如果你对谈论 ML &数据科学感兴趣,请随时在评论中留下任何反馈或通过 Linkedln 与我联系。感谢您的阅读!**

打造您在数据科学领域的品牌

原文:https://towardsdatascience.com/building-your-brand-in-data-science-840ba5baaff9?source=collection_archive---------36-----------------------

苹果 | 谷歌 | SPOTIFY | 其他

肯·吉在 TDS 播客

背景图片由 Markus Spiske 提供

编者按:迈向数据科学播客的“攀登数据科学阶梯”系列由 Jeremie Harris 主持。Jeremie 帮助运营一家名为sharpes minds的数据科学导师初创公司。可以听下面的播客:

众所周知,数据科学是一个品牌非常重要的领域。

事实上,如果说我从帮助求职者在sharpes minds获得工作的 A/B 测试方法中学到了什么,那就是写博客、在社交媒体上保持良好形象、做开源贡献、播客和在聚会上演讲是获得雇主注意的最佳方式之一。

品牌很重要。如果有一个人对数据科学中的品牌价值以及如何建立品牌有深刻的理解,那就是数据科学家兼 YouTuber Ken Jee。Ken 不仅拥有数据科学家和体育分析师的经验,曾在 DraftKings 和 GE 工作过,而且他还创办了许多公司——他的 YouTube 频道拥有超过 60 000 名订户,是他今天的主要项目之一。

在今天的节目中,我和 Ken 讨论了数据科学中的品牌建设策略,以及为那些希望获得第一份数据相关职位的人提供的求职技巧。以下是我最喜欢的一些外卖食品:

  • 许多公司积极寻找不仅将数据科学作为一种工具,而且作为看待世界的一种方式的候选人。这是因为一名优秀的数据科学家需要能够解决数据科学问题,还需要能够发现这些问题,这意味着要学会通过数据的镜头来思考和看待世界。
  • 例如,我们讨论了将求职视为数据科学项目的想法:用 NLP 对职位描述进行聚类,以确定可能值得申请的角色,并且重要的是,测量您的申请转化率。最后一个技巧是关键:转化率是你知道你需要投入多少工作才能达到每周面试的目标数量的唯一方法。
  • 认识到求职中你能控制的方面和你不能控制的方面是很重要的——因为不这样做可能会导致你因为不可避免的失败而责怪自己。例如,面试官的低血糖可能会导致他们过于严厉,招聘经理在你现场的早上起床就心情不好,可能无法与你以及他前一天面试的人沟通。
  • 研究你申请的公司。你应该带着对公司业务的清晰想法去参加面试,以及对他们最大的问题可能是什么的一些有根据的猜测。如果您准备好了一些关于工业或经济趋势如何影响他们的业务的问题,将会得到加分!(提示:COVID 是许多公司的一个重要因素)

你也可以点击这里在 Twitter 上关注肯,点击这里查看的 YouTube 频道,了解他的工作。另外,点击这里查看肯最新的 66 天数据计划

你可以在推特上关注我这里

使用 Python 构建您自己的新冠肺炎流行病简单模型

原文:https://towardsdatascience.com/building-your-own-covid-19-epidemic-simple-model-using-python-e39788fbda55?source=collection_archive---------12-----------------------

理解基本的动力学和概念,同时编写一个简单的计算流行病模型

如果你生活在这个世界上,很有可能你还没有听说过这种新型冠状病毒新冠肺炎和它在全世界引起的疫情。事实上,这是一个很好的机会,当你在家中自我隔离,远离公众,像许多人一样,有很多额外的时间来处理。所以,如果你熟悉编程,为什么不试试这个:在家里建立自己的流行病模型。这有助于提高你的编程能力,并帮助你理解流行病的概念和基本动态。

照片由融合医学动画Unsplash 上拍摄

但首先请注意,这只是一篇展示如何使用 Python 为新冠肺炎疫情准备一个基本模型的文章。另一方面,这意味着:它不是一个你可以用来预测未来的模型。这是而不是你的 DIY 模型,它让你能够简单地忽略官方机构推荐你做的事情。它也不是在社交媒体上向其他人证明我们注定要失败的可视化工具!如果你同意这些免责声明,现在让我们开始编码。

我们在此假设的主要概念是,模拟一个样本社会中有限数量的个体每天相互作用,以及每次相互作用带来的污染机会。为了简单起见,我随机生成了这个组,邻近度代表每个人与其最近的 邻居 的互动量。注意,一个人的邻居可能是他/她的家人、同事、同学或他/她每天接触的任何人。我假设这种相互作用对于样本中的大多数人来说是相同的,但是为了使事情更有趣,让我们假设一部分群体,我取了 10%,我们称之为 运输者 ,在模型中每天随机移动;这意味着他们的邻居每天都在变化。我添加了这些组来代表那些店主、出租车司机、警察等等。他们每天都和新的人打交道。我用圆圈标记了运输者和他们的号码来跟踪他们。
最后,为了完成我们的零日初始化,我们更需要的是零号患者!我把它涂成红色。

初始化后,模拟每天都是这样进行的:

  • 运输者随机移动。
  • 人们与他们的邻居互动。
  • 在互动过程中,感染会随机传播,并有预先确定的机会从一个病人传染给一个健康人。
  • 一定比例的患者,新冠肺炎(+),随机住院,假设他们的症状变得最糟糕。
  • 随机选择的住院病人中有一部分死亡。
  • 那些已经忍受了疾病期,比如说 14 天,症状轻微的患者将被治愈,并对疾病产生免疫力。
  • 还有明天。。。我们再次重复以上所有内容。这种情况一直持续到不可能再有新的感染为止。

对所有编程语言来说,编写上面提到的算法是一件容易的事情,但是对于可视化来说,Python 使用matplotlib提供了很多。

让我们看看,如果我们用 1000 个人的随机数据集运行这个模拟,并且在每次交互中感染机会为 25%,会发生什么:

让我们看看模拟过程中的统计数据是什么样的:

由于模拟需要一些时间,我让 python 在日志中报告我的社会在模拟期间到底发生了什么:

-------------------------------
Day: 0
- - - - - - - - - - - - - - - - 
Healthy         999
Covid-19(+)       1
Hospitalized      0
Cured             0
Dead              0
Name: 0, dtype: int64
-------------------------------
Day: 1
- - - - - - - - - - - - - - - - 
Healthy         987.0
Covid-19(+)      13.0
Hospitalized      0.0
Cured             0.0
Dead              0.0
Name: 1, dtype: float64
-------------------------------
Day: 2
- - - - - - - - - - - - - - - - 
Healthy         977.0
Covid-19(+)      23.0
Hospitalized      0.0
Cured             0.0
Dead              0.0
Name: 2, dtype: float64
-------------------------------
.
. . 
. . . 
. . . . 
-------------------------------
Day: 53
- - - - - - - - - - - - - - - - 
Healthy           1.0
Covid-19(+)       0.0
Hospitalized      0.0
Cured           951.0
Dead             48.0
Name: 53, dtype: float64

我让 python 自动完成所有的工作,制作所有的图表、列表和视频,并以我选择的格式保存。但是请注意,如果您为数量选择了较大的数字,并且以错误的方式更改了一些假设,则可能需要一些模拟运行时间。

例如,模拟 10,000 个人花费了我大约 24 小时,直到第 34 天,您可能会看到下面的模拟结果:

既然你已经开发了自己的样本模型,你可能会发现搜索由专业流行病学家开发的更科学的模型很有趣,或者你可能会在这里看到一个来自华盛顿邮报的。

下面,你可以找到我的 python 代码,在上面的模型中,你可以很容易地改变参数,看看在不同的假设下会发生什么。

# -*- coding: utf-8 -*-
"""
Created on Sat Feb 29 21:57:53 2020[@author](http://twitter.com/author): Sadegh Maghsoudi
"""
# Functions --------------------------------------------------
def point(xlimit,ylimit):
    import random
    x = random.uniform(0,xlimit)
    y = random.uniform(0,ylimit)
    return x,ydef Generate(GrupSize,xlimit,ylimit):
    import pandas as pd
    import math
    df = pd.DataFrame(columns='X,Y,Covid-19,Day'.split(','))

    for i in range(GrupSize):
        df.loc[i,'X'], df.loc[i,'Y'] = point(xlimit,ylimit)
        df.loc[i,'Covid-19'] = False

    samplesize = math.floor(GrupSize/100)
    MoversList = df.sample(n = samplesize).index.values.tolist()

    StatofDay = pd.DataFrame(columns='Healthy,Covid-19(+),Hospitalized,Cured,Dead'.split(','))
    return df , StatofDay, MoversList

def plt1color(df):
    cols=[]
    for l in df.index:
        if df.loc[l,'Covid-19']==True: #Infected
            cols.append('red')
        elif df.loc[l,'Covid-19']==666: #Dead
            cols.append('black')
        elif df.loc[l,'Covid-19']==115: #Hospitalized
            cols.append('yellow')
        elif df.loc[l,'Covid-19']==7: #Cured
            cols.append('green')
        else:
            cols.append('blue') #Healthy
    return colsdef plt2color(Stat):
    cols=[]
    for i in Stat.columns:
        if i=='Covid-19(+)': #Infected
            cols.append('red')
        elif i=='Dead': #Dead
            cols.append('black')
        elif i=='Hospitalized': #Hospitalized
            cols.append('yellow')
        elif i=='Cured': #Cured
            cols.append('green')
        else:
            cols.append('blue') #Healthy
    return colsdef Plot():
    import matplotlib.pyplot as plt
    global df, fig, Stat, Day, Moverslist
    cols=plt1color(df)
    ld = ['Healthy','Covid-19(+)','Hospitalized','Cured','Death Toll']
    axs[0].cla()
    axs[0].scatter(df['X'],df['Y'],s=1,c=cols)
    for i in MoversList:
        axs[0].scatter(df.loc[i,'X'],df.loc[i,'Y'],s=6,facecolors='none', edgecolors='black')
        axs[0].text(df.loc[i,'X']+0.02, df.loc[i,'Y']+0.02, str(i), fontsize=5)
    cols=plt2color(Stat)
    sDay = str(Day)
    title = 'Day' + sDay
    axs[0].set_title(title,loc='left')
    axs[0].set_yticklabels([])
    axs[0].set_xticklabels([])
    axs[0].tick_params(
#    axis='both',       # changes apply to the x-axis
    which='both',      # both major and minor ticks are affected
    bottom=False,      # ticks along the bottom edge are off
    top=False,         # ticks along the top edge are off
    right=False,      # ticks along the right edge are off
    left=False,         # ticks along the left edge are off
    labelbottom=False) # labels along the bottom edge are off
    axs[1].cla()
    axs[1].plot(Stat.Healthy,label=ld[0],color=cols[0])
    axs[1].plot(Stat['Covid-19(+)'],label=ld[1],color=cols[1])
    axs[1].plot(Stat.Hospitalized,label=ld[2],color=cols[2])
    axs[1].plot(Stat.Cured,label=ld[3],color=cols[3])
    axs[1].plot(Stat.Dead,label=ld[4],color=cols[4])
#    axs[1].set_prop_cycle(color=cols)
    axs[1].legend(bbox_to_anchor=(0, 1), loc='upper left', borderaxespad=0.)
    plt.xlabel('Days')
    plt.show()
    if Day<10 : sDay = '0' + sDay
    title = 'Day' + sDay + '.png'
    plt.savefig(title)returndef Png_to_gif():
    from PIL import Image
    import glob

    # Create frames
    frames = []
    imgs = glob.glob("*.png")
    for i in imgs:
        new_frame = Image.open(i)
        frames.append(new_frame)

    # Save into GIF
    frames[0].save('png_to_gif.gif', format='GIF',
          append_images=frames[1:],
          save_all=True,
          duration=500, loop=0)def infect(Person):
    import random
    global df,Day
    if random.random()>0.25 and Day>3 : return
    if df.loc[Person,'Covid-19']==False:
        df.loc[Person,'Covid-19'], df.loc[Person,'Day'] = True, Day

def Move(xlimit,ylimit):
    """ 
    Move Movers Randomly
    """
    import random
    global df, MoversList
    for i in MoversList:
        if (df.loc[i,'Covid-19']==115) or (df.loc[i,'Covid-19']==666) : MoversList.remove(i)
        df.loc[i,'X'], df.loc[i,'Y'] = (df.loc[i,'X']+random.uniform(1,xlimit/3))%xlimit, (df.loc[i,'Y']+random.uniform(1,ylimit/3))%ylimitdef check(i,j):
    import math
    global df, YesterdayPatients, Distlimit
    Dist = math.sqrt((df.loc[i,'X']-df.loc[j,'X'])**2+(df.loc[i,'Y']-df.loc[j,'Y'])**2)
    flag = ((YesterdayPatients[i]==True) ^ (YesterdayPatients[j]==True)) and Dist<Distlimit
    return flag

def interact():
    global Day, df
    for i in range(len(df)):
        for j in range(i):
            if check(i,j):
                if (df.loc[i,'Covid-19']==False) :
                    infect(i)
                else:
                    infect(j)def kill():
    import math
    global df

    samplesize = math.floor(len(df[df['Covid-19']==True])*.005+len(df[df['Covid-19']==115])*.005)
    if samplesize>len(df[df['Covid-19']==True]): return
    df.loc[df[df['Covid-19']==True].sample(n = samplesize).index.values.tolist(),'Covid-19']=666
    returndef hospitilize():
    import math
    global df
    samplesize = math.floor(len(df[df['Covid-19']==True])*0.03)
    if samplesize>len(df[df['Covid-19']==True]): return
    df.loc[df[df['Covid-19']==True].sample(n = samplesize).index.values.tolist(),'Covid-19']=115
    returndef cure():
    global df, Day
    df.loc[(df['Day']<Day-10) & (df['Covid-19']==True) ,'Covid-19'] = 7
    df.loc[(df['Day']<Day-21) & (df['Covid-19']==115) ,'Covid-19'] = 7
    return

def Tomorrow(): # To Be checked and Resolved!!!
    global df, Day
    Day +=1
    kill()
    hospitilize()
    cure()
    Move(xlimit,ylimit)
    interact()def Count(Day):
    global df, Stat

    List = list(df['Covid-19'])

    Stat.loc[Day,'Healthy'] = List.count(False)
    Stat.loc[Day,'Covid-19(+)'] = List.count(True)    
    Stat.loc[Day,'Hospitalized'] = List.count(115)    
    Stat.loc[Day,'Cured'] = List.count(7)
    Stat.loc[Day,'Dead'] = List.count(666)

    return

def write_log(*args):
    global log_file
    line = ' '.join([str(a) for a in args])
    log_file.write(line+'\n')
    print(line)

# Main -------------------------------------------------------------------
import matplotlib.pyplot as plt
import random
log_file = open("Log.txt","w+")# -------------------------------------------
#   I N P U T   V A R I A B L E S   H E R E  |
# -------------------------------------------
#                                            |
n = 1000    #                                |
xlimit,ylimit=30,30   #                      |
Distlimit = 1.5 #                            |
#                                            |
# -------------------------------------------write_log(31*'-')
write_log("Here's the Input Data:")
write_log(8*'- - ') 
write_log('Numper of Sample:',n)
write_log('X & Y limites: ',xlimit,', ',ylimit)
write_log('Distance required for Contamination:', Distlimit)# Day = 0,  Generating Model...
Day = 0df, Stat, MoversList = Generate(n,xlimit,ylimit)
infect(random.randrange(n))
fig, axs = plt.subplots(2)
fig.suptitle('Covid-19 Epidemic Sample Model', fontsize=16)
Plot()
Count(Day)
write_log(31*'-')
write_log('Day:',Day)
write_log(8*'- - ')    
write_log(Stat.loc[Day])# Day=1
YesterdayPatients = list(df['Covid-19'])
Tomorrow()
Plot()
Count(Day)
write_log(31*'-')
write_log('Day:',Day)
write_log(8*'- - ')    
write_log(Stat.loc[Day])

#Main Loop  .  .  .countsames = 0
while Stat.loc[Day, 'Healthy']>0 and Day<100:
    log_file = open("Log.txt","a+")
    if (list(Stat.loc[Day])==list(Stat.loc[Day-1])): 
        countsames +=1
        if countsames>2 : break
    else :
        countsames = 0

    YesterdayPatients = list(df['Covid-19'])
    Tomorrow()
    Plot()
    Count(Day)
    write_log(31*'-')
    write_log('Day:',Day)
    write_log(8*'- - ')    
    write_log(Stat.loc[Day])
    log_file.close()Png_to_gif()
Stat.to_excel('Stat.xlsx')
Stat.plot(title='Statistical Data Vs. Days Passed')
plt.savefig('Stat')

编者注: 迈向数据科学 是一份以数据科学和机器学习研究为主的中型刊物。我们不是健康专家或流行病学家,本文的观点不应被解释为专业建议。想了解更多关于疫情冠状病毒的信息,可以点击 这里

构建自己的物体探测器——py torch vs tensor flow 以及如何开始?

原文:https://towardsdatascience.com/building-your-own-object-detector-pytorch-vs-tensorflow-and-how-to-even-get-started-1d314691d4ae?source=collection_archive---------2-----------------------

-详细的“操作”方法-

UnsplashGreg Rakozy 拍摄的照片

在经历了实现用于物体检测的人工智能系统的一些困难和头痛之后,我想分享一些我所获得的关于如何开始的知识。第一步总是最难的,这就是为什么这种非常实用的方法旨在让人们轻松进入 TensorFlow 和 PyTorch 的对象检测框架的世界。

由于我已经是一名经验丰富的数据科学家,并且在过去几年中开发了一些高效的机器学习软件组件,我认为上网应该是一件相当容易的工作,找到一个预先训练的 CNN,并在一个看不见的数据集上进一步训练它,使它能够检测一些以前未知的物体。直到大约一年前,我大部分时间都在做基于树的模型,大量的 scikit-learn 以及 xgboost,当然还有大量的熊猫。和许多人工智能任务一样,我的第一个方法是经典的 a:“不要这么快!”有一些你必须知道的垫脚石,但是似乎没有多少文章和博客提到。在这个主题上花了很多时间并阅读了大量 TensorFlow 源代码后,我知道了如下问题的答案:

"为什么我从官方 GitHub repos 得到的示例代码不能工作?"

所以我想为什么不与他人分享我使用这些工具的经验,并描述我是如何着手解决我所面临的问题的。

简单地说,我的目标是开发自己的对象检测器——一种可以分类和检测图像中对象的神经网络,不仅仅是现成的对象检测算法,而是特别擅长从我自己的数据集中检测图像——这意味着我必须进行迁移学习。

首先,我上网更新了一些关于这些算法在数学上如何工作的知识(我真的可以推荐吴恩达在 coursera 上关于卷积神经网络的 deeplearning.ai 课程。它有整整一周的时间专门用于对象检测,让您了解关键概念并很好地描述每件事情背后的数学。因此,有了这个理论,我看了一些我可以用于我的任务的开源数据集,偶然发现了 experiencor 在 https://github.com/experiencor/raccoon_datasetGitHub上的以下浣熊数据集,然后我决定使用它。它已经将注释和图像整齐地存储在各自的文件夹中,并且只需要检测一个类(浣熊),因此这是一个很好的起点。

完成后,我看了看两个广为人知的深度学习框架,它们让你使用预训练的网络进行迁移学习(进一步训练这些网络以使它们适应你的特定数据集), TensorFlow 的对象检测 API 以及 PyTorch 的 torchvision 包。有一件事我可以马上说:我在 PyTorch 中只用了半天就完成了我的工作,而在 TensorFlow 中我却花了几天时间才完成了我想要的一切!事实上,tf object detection API 是基于 TensorFlow 1.x 构建的,而我们现在已经有了 2.x 版本,这给我带来了很多兼容性问题,如果不仔细查看源代码,几乎不可能完成任何工作,因为他们的报告中的一些示例是基于 2.x 的,而核心培训代码仍然基于 1.x。

我在 Google Colab 上编写代码,因为他们为你提供了整洁的笔记本体验,让你可以访问他们云上的免费 CPU 和 GPU,以及以 google drive 形式提供的免费存储,可以很容易地安装到你的 Colab 空间。

获取数据

获取数据相当容易,因为我只需导航到一个不同的文件夹(在我安装的 google drive 存储中)并运行

! git clone [https://github.com/experiencor/raccoon_dataset.git](https://github.com/experiencor/raccoon_dataset.git)

检查数据很容易,就像这样:

from PIL import Imageimage = Image.open("<your path>/raccoon_dataset/images/raccoon-1.jpg")
image

来自 https://github.com/experiencor/raccoon_dataset的 raccoon-1.jpg

当谈到这个图像的标签时,我们必须查看 annotations 文件夹。在对象检测的情况下,每个对象的一个类(在我们的例子中只是浣熊的一个类)和每个对象的 4 个代表边界框的坐标组成了标签。要快速浏览,只需将浣熊-1.xml 文件下载到您的笔记本电脑上,用编辑器打开它(我用的是 Atom)。然后,您会看到以下内容:

<annotation verified="yes">
 <folder>images</folder>
 <filename>raccoon-1.jpg</filename>
 <path>/Users/datitran/Desktop/raccoon/images/raccoon-1.jpg</path>
 <source>
  <database>Unknown</database>
 </source>
 <size>
  <width>650</width>
  <height>417</height>
  <depth>3</depth>
 </size>
 <segmented>0</segmented>
 <object>
  <name>raccoon</name>
  <pose>Unspecified</pose>
  <truncated>0</truncated>
  <difficult>0</difficult>
  <bndbox>
   <xmin>81</xmin>
   <ymin>88</ymin>
   <xmax>522</xmax>
   <ymax>408</ymax>
  </bndbox>
 </object>
</annotation>

它告诉你这个注释文件对应于 raccoon-1.jpg 文件,对象的类是“浣熊”,边界框的边由 xmin,xmax,ymin 和 ymax 表示。一个非常自然的反应是首先通过简单地在图像中画出方框来检查这是否正确。

很容易做到:

from PIL import ImageDrawxmin = 81
ymin = 88
xmax = 522
ymax = 408draw = ImageDraw.Draw(image)
draw.rectangle([(xmin, ymin), (xmax, ymax)], outline =”red”)
image

带有来自浣熊-1.xml 的绑定框的 raccoon-1.jpg

因此,我们不必仔细检查文件夹中的每个文件并收集每个图像上的正确标签,因为已经有一个包含所有这些信息的 csv 文件。快速浏览一下可以这样做:

import pandas as pdlabels = pd.read_csv("<your path>/raccoon_dataset/data/raccoon_labels.csv")labels.head()

这是 TensorFlow 和 PyTorch 库的起点,也是两者开始不同的地方。

A 部分:张量流

装置

从 TensorFlow 对象检测开始,基本上应该是这样工作的:你克隆他们的 repo 并根据他们的安装指南安装 API。在实践中,你必须用 Google Colab 做的事情在某些方面更容易,在某些方面更难:

  1. 安装 TensorFlow 和所有其他提到的库(在 Google Colab 中,选择一个 tf 版本的神奇命令就是你所需要的,其他的都是预装的)
%tensorflow_version 1.x

2.COCO API 安装—已经预装,可以通过

import pycocotools

3.克隆他们的回购协议

! git clone [https://github.com/tensorflow/models.git](https://github.com/tensorflow/models.git)

4.导航到模型/研究文件夹

import os 
os.chdir("<your path>/models/research")

5.安装 protobufs

! protoc object_detection/protos/*.proto --python_out=.

6.现在,要测试您的安装,只需运行:

%%bashexport PYTHONPATH=$PYTHONPATH:`pwd`:`pwd`/slim
python object_detection/builders/model_builder_test.py

7.超级重要:您在 Colab 中执行的每个 bash 命令都像是启动一个新的终端会话,因此非常重要的是,每次您使用 CLI 进行 tf 的对象检测时,您都必须包括

export “PYTHONPATH=$PYTHONPATH:`pwd`:`pwd`/slim

在你的牢房顶上!

因为与 API 的每一次交互都是以命令行方式进行的(为了训练一个您称之为类似的模型):

%% bash 
export “PYTHONPATH=$PYTHONPATH:`pwd`:`pwd`/slimpython”python object_detection/model_main.py \
    --pipeline_config_path=${PIPELINE_CONFIG_PATH} \
    --model_dir=${MODEL_DIR} \
    --num_train_steps=${NUM_TRAIN_STEPS} \
    --sample_1_of_n_eval_examples=$SAMPLE_1_OF_N_EVAL_EXAMPLES \
    --alsologtostderr

我想深入了解这个 CLI(另外,笔记本环境并不是真正为命令行执行而构建的),我在 Colab 上的安装中添加了一个关键的东西:

8.启用 python 内核来查找 model_main.py 文件中的所有导入(在上面的 CLI 中调用)。

import syssys.path.append("<your path>/models/research/object_detection")sys.path.append("<your path>/models/research/slim/")

9.确保您安装了 numpy==1.17,否则评估脚本将无法工作。

瞧,准备好开始了!

选择一个预先训练好的网络

安装了必要的软件并下载了训练数据后,我们终于可以考虑人工智能了。我们可以使用哪种卷积神经网络架构?它是在哪个数据集上训练的?在 TensorFlow 的对象检测 API 中,我们可以从他们的检测模型 zoo (顺便说一下,喜欢这个名称:)中的各种模型中进行选择,这些模型是在不同的行业和研究标准图像数据集上训练的。也许最著名的是所谓的 COCO 数据集(上下文中的公共对象),它有许多不同的图像,带有标签类、边界框,甚至可以作为注释使用的遮罩。该数据集被用于许多比赛,并被引用为“旨在推动物体检测技术的发展”。很酷的东西,也是我一直在寻找的。我选了一个叫“更快的 R-CNN”的网络。我选择它是因为它在 TensorFlow 模型动物园和火炬视觉套装中都有,这样我可以更容易地比较这两者。更快的 R-CNN 是一个区域提议网络(因此称为 R ),它使用“锚盒”技术来定位物体并预测它们。简而言之,它是两个网络的组合,一个检测图像中很可能包含对象的区域,另一个预测边界框以及这些区域中的类别概率。网上有很多更详细的解释。我个人比较喜欢这个高昊的。

配置模型

1.下载模型文件

一旦您选择了模型,您首先必须下载它,这意味着您下载网络架构和预先训练的权重。我选择的是“更快 _ rcnn _ inception _ v2 _ coco _ 2018 _ 01 _ 28”的模式。这是通过从 models/research 文件夹运行以下命令来完成的:

%%bashwget [http://download.tensorflow.org/models/object_detection/faster_rcnn_inception_v2_coco_2018_01_28.tar.gz](http://download.tensorflow.org/models/object_detection/faster_rcnn_inception_v2_coco_2018_01_28.tar.gz)tar -xvf faster_rcnn_inception_v2_coco_2018_01_28.tar.gz

现在您可以找到一个包含所有模型文件的文件夹:

import os 
os.listdir("faster_rcnn_inception_v2_coco_2018_01_28")

看起来像这样:

['model.ckpt.index',  'checkpoint',  'pipeline.config',  'model.ckpt.data-00000-of-00001',  'model.ckpt.meta',  'saved_model',  'frozen_inference_graph.pb']

在那里,您可以找到检查点信息,让您使用预训练的权重重建模型,从而为迁移学习做好准备。您还有一个 saved_model 文件夹,其中包含下载模型的导出版本以及一个 freezed _ inference 图——正如其名称所建议的,您可以使用一个 tf 图进行推理。最后但同样重要的是,您可以找到一个 pipeline.config 文件的示例。在此文件中,您可以准确指定您希望如何训练您的网络,使用什么样的训练数据,您有多少个对象类,等等。在这里,我们可以根据我们的具体需求定制预训练模型。

2.准备数据

在 TensorFlow 的对象检测中,估算器 API 用于处理训练和评估(以及一些类似检查点等的东西。)过程。这个 API 需要您首先创建的 tf.record 格式的数据。

幸运的是,浣熊数据集的创建者已经完成了这项工作。否则我们也可以自己改造它:

!python generate_tfrecord.py --csv_input=data/train_labels.csv  --output_path=data/train.record

我们有一个 train.record 和一个 test.record 数据集,它们将用于在训练期间向 tf.estimator 对象成批提供数据。

3.调整 pipeline.config 文件

在 pipeline.config 文件中,我们必须调整 5 个区域。微调检查点、训练配置、有效配置、类数量和第二阶段后处理。其他一切都可以保持原样。fine_tune_checkpoint 是下载的检查点文件中包含的预训练权重的路径。我们在这里所做的就是设置它等于

fine_tune_checkpoint: "<your path >/faster_rcnn_inception_v2_coco_2018_01_28/model.ckpt"

如果您发现没有名为 model.ckpt 的文件,请不要担心,相信我,它确实是这样工作的;)可以重建模型并将权重插入到正确的层中。num_classes 字段被设置为 1(仅 1 个类:浣熊)。

在 train_config 部分,我们可以调整所有关于训练阶段的东西。我保留了所有的默认设置,只修改了必要的部分,比如 train_input_reader,在这里,您必须设置 train.record 训练数据集的输入路径,以及包含标签信息的 pbtxt 文件的 label_map_path。我首先将 label_map 文件更改为

item {
  id: 1
  name: 'raccoon'
}

然后我将 train_input_reader 设置为

train_input_reader: {
  tf_record_input_reader {
   input_path: "<your path>/raccoon_dataset/data/train.record"
  }
  label_map_path: "<your path>/training/raccoon_label_map.pbtxt"
}

接下来,我更改了有效配置部分。这个名字再次解释了这一切。我只是将 eval_input_reader 设置为我的 label_map 文件,并将 test.record 测试数据设置为

eval_input_reader: {
  tf_record_input_reader {
   input_path: "<your path>/raccoon_dataset/data/test.record"
  }
label_map_path: "<your path>/training/raccoon_label_map.pbtxt"
  shuffle: false
  num_readers: 1
}

最后,我调整了第二阶段后处理。在这里,您可以定义模型如何处理输出。通常,检测模型输出类别概率和边界框坐标的多个可能组合(例如,每个锚一组),其不等于图像中对象的数量。作为用户,我们必须使用非最大值抑制等技术来获得最佳匹配的边界框(上面提到的 Andrew Ngs Coursera 课程对此做了很好的介绍!)到图像中的每个对象。我在这里挑选了以下内容:

second_stage_post_processing {
      batch_non_max_suppression {
        score_threshold: 0.5
        iou_threshold: 0.6
        max_detections_per_class: 5
        max_total_detections: 5
      }
      score_converter: SOFTMAX
    }

这意味着我将只允许每个图像 5 个检测(理想情况下,如果图像中存在不同的浣熊,5 个不同的检测)。IoU(并集上的交集)阈值以及分数阈值被定制为每个对象只有一个边界框。

训练模型

一旦 pipeline.config 文件准备就绪,我们就可以开始培训了。TensorFlow 建议使用一个命令行作业调用带有各种输入参数的 model_main.py 文件来完成这项工作。然而,当我想深入了解一下时,我跳进了这个文件并自己运行了这些命令。对于模型的训练和评估,对象检测 API 利用 tf 的估计器 API 。它很好地照顾了我们的训练和评估,并且集成了自动检查点和模型导出。它还允许从给定的检查点进行迁移学习(这正是我们正在做的),并在训练期间编写自动摘要,我们可以用 tensorboard 可视化这些摘要,以监控表现。

评估人员将需要培训和评估规范,这些规范包含并定义刚才提到的所有内容,并负责为我们批量处理数据。tf 的对象检测具有预定义的函数来从我们的 pipline.config 文件中实例化这些对象:

在开始之前,我们需要导入所有必需的库(就像在 model_main.py 文件中一样):

from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from absl import flags
import tensorflow as tf
from object_detection import model_hparams
from object_detection import model_lib

现在,我们可以从指定模型目录开始:

model_dir = "<your path>/faster_rcnn_inception_v2_coco_2018_01_28/model_dir"

API 将把它的日志文件以及检查点和导出的模型写到这个目录中。然后,在指定运行配置、pipeline.config 文件的路径以及要训练的步骤数(一个步骤是对单批数据的一次运行)之后:

config = tf.estimator.RunConfig(model_dir=model_dir)
pipeline_config_path= "<your path>/training/pipeline.config"
num_train_steps=10000

我们可以实例化所有对象,并最终使用适当的预构建函数创建训练和评估规格。

train_and_eval_dict = model_lib.create_estimator_and_inputs(
                        run_config=config,
                        hparams=model_hparams.create_hparams(None),
                        pipeline_config_path = pipeline_config_path,
                        train_steps =num_train_steps,
                        sample_1_of_n_eval_examples = 1)estimator = train_and_eval_dict['estimator']
train_input_fn = train_and_eval_dict['train_input_fn']
eval_input_fns=train_and_eval_dict['eval_input_fns']
eval_on_train_input_fn=train_and_eval_dict['eval_on_train_input_fn']predict_input_fn = train_and_eval_dict['predict_input_fn']
train_steps = train_and_eval_dict['train_steps']

然后使用 Estimator、train_spec 和 eval_spec 进行训练:

tf.estimator.train_and_evaluate(estimator,train_spec,eval_specs[0])

现在发生的一些事情值得一提。首先,使用 pipeline.config 文件中的适当优化器设置来训练模型。每隔config . save _ check points _ secs模型将检查点(一组权重)写入 model_dir。然后每个 eval_specs[0]。我们暂停训练,在测试集上运行评估。为了做到这一点,我们首先需要有一个可用的检查点。eval_spec 配置为使用 COCO challenge 的评估指标(使用 pycocotools 库)。每个config . save _ summary _ stepsestimator API 都会写出事件文件,以便在 tensorboard 中可视化。

我运行了 10.000 步的模型,并在测试集上获得了 0.65 的最终 mAP。当对肉眼进行预测时,这已经输出了一些相当不错的边界框。但是为了看到这一点,我们首先需要弄清楚如何用这个训练过的模型进行推理。

用模型做预测

这可能是整个对象检测流程中最难解决的部分。在 TensorFlow 的对象检测报告中,有一些关于如何在预建模型上进行推理的示例,但是,代码依赖于 TensorFlow 1.x 版。在 TensorFlow 1.x 代码中使用保存的模型或冻结的推理图(相对于 tf 2.x)要复杂得多,因为您必须直接使用 tf 图和会话。

1.导出模型

无论哪种方式,我们首先必须以正确的方式导出模型。我花了一些实验来找出为什么已经在 eval_specs 中的导出器并在 TF . estimator . train _ and _ evaluate 末尾导出一个保存的模型不工作。本质上,这是由于在这个自动导出器中使用的服务功能。它是针对使用 tf.example 类型数据(例如,测试.记录数据)而定制的,然而,我们正在尝试输入表示图像的 3D 张量。这就是为什么我们必须运行一个指定输入类型的导出作业。

包含以下进口:

import tensorflow as tf
from google.protobuf import text_format
from object_detection import exporter
from object_detection.protos import pipeline_pb2
slim = tf.contrib.slim
flags = tf.app.flags

我们必须提供一个输出目录、我们的 pipeline.config 文件、输入类型以及一个我们希望从中导出模型的检查点(只使用最新的一个)

pipeline_config_path = "<your path>/training/pipeline.config"
config_override = ""
input_shape = False
trained_checkpoint_prefix ="<your path>/faster_rcnn_inception_v2_coco_2018_01_28/model_dir/model.ckpt-10000"
output_directory = "<your path>faster_rcnn_inception_v2_coco_2018_01_28/output"
input_type = "image_tensor"
write_inference_graph = False

现在我们可以经营出口商了

tf.reset_default_graph()
pipeline_config = pipeline_pb2.TrainEvalPipelineConfig()
with tf.gfile.GFile(pipeline_config_path, 'r') as f:
   text_format.Merge(f.read(), pipeline_config)
text_format.Merge(config_override, pipeline_config)
if input_shape:
   input_shape = [
        int(dim) if dim != '-1' else None
        for dim in input_shape.split(',')
   ]else:
    input_shape = Noneexporter.export_inference_graph(
   input_type, pipeline_config, trained_checkpoint_prefix,
   output_directory, input_shape=input_shape, 
   write_inference_graph=write_inference_graph)

2.模型的加载和推理

对于 TensorFlow 2.x,我们只需重新加载运行时并选择 2.x 作为版本

%tensorflow_version 2.x

而且基本上只是跟随他们的例子笔记本。然后,您可以用两行代码加载模型:

model = tf.saved_model.load("<your path>/faster_rcnn_inception_v2_coco_2018_01_28/output/saved_model")
model = model.signatures['serving_default']

我们现在要做的就是从测试集中加载一个图像(例如 raccoon-14.jpg)

from PIL import Image, ImageDraw
import numpy as np
import pandas  as pd
filename = "raccoon-14.jpg"img = Image.open("<your path>/raccoon_dataset/images/"+filename)test = pd.read_csv("<your path>/raccoon_dataset/data/test_labels.csv")true_labels = test[test["filename"] == filename][["xmin", "ymin", "xmax","ymax"]]

并运行推理(如 tf 推理脚本所建议的)

image = np.asarray(img)
# The input needs to be a tensor, convert it using `tf.convert_to_tensor`.
input_tensor = tf.convert_to_tensor(image)# The model expects a batch of images, so add an axis with `tf.newaxis`.
input_tensor = input_tensor[tf.newaxis,...]# Run inference
output_dict = model(input_tensor)# All outputs are batches tensors.
# Convert to numpy arrays, and take index [0] to remove the batchdimension.# We're only interested in the first num_detections.
num_detections = int(output_dict.pop('num_detections'))
output_dict = {key:value[0, :num_detections].numpy()
                 for key,value in output_dict.items()}
output_dict['num_detections'] = num_detections# detection_classes should be ints.
output_dict['detection_classes'] = output_dict['detection_classes'].astype(np.int64)

3.绘制边界框

现在,我们可以用红色绘制已学习的边界框,用绿色绘制真实标签:

selected_boxes = output_dict["detection_boxes"]
selected_scores = output_dict["detection_scores"]
draw = ImageDraw.Draw(img)#draw predictions:
for i in range(len(selected_scores)):
  draw.rectangle([(selected_boxes[i][1]*img.width,                     selected_boxes[i][0]*img.height), (selected_boxes[i][3]*img.width,  selected_boxes[i][2]*img.height)],  outline ="red",  width = 3)
  draw.text((selected_boxes[i][1]*img.width, selected_boxes[i][0]*img.height), text = str(selected_scores[i]))# draw groundtruth:
for i in range(len(true_labels["xmin"].values)):
  draw.rectangle([(true_labels["xmin"].values[i], true_labels["ymin"].values[i]), (true_labels["xmax"].values[i], true_labels["ymax"].values[i])],  outline ="green",  width = 3)img

测试数据集中的 raccoon-14.jpg

我们可以看到一只很酷的小浣熊在做家务。考虑到我们只训练了 10.000 步的网络,包围盒和概率也相当不错。

注意:如果你想在 TF 1 . x 版本中这样做,你可以按照这里的脚本来做。

B 部分:PyTorch

PyTorch 有一个名为 torchvision 的包,包括模型架构、数据集和其他对计算机视觉有用的功能。Torchvision 也有一个关于对象检测的子包,我们将在本节中使用。下面的很多设置和代码都是根据 torchvision 的目标检测教程建模的。

装置

  1. 我们需要确保安装了 numpy 版。
  2. 检查 pycocotools 安装
import pycocotools

4.转到您创建的 PyTorch 对象检测目录

import os
os.chdir("<your path>/pytorch object detection")

3.克隆 PyTorch vision repo 并复制一些 python 文件

%%bashgit clone [https://github.com/pytorch/vision.git](https://github.com/pytorch/vision.git)
cd vision
git checkout v0.3.0cp references/detection/utils.py ../
cp references/detection/transforms.py ../
cp references/detection/coco_eval.py ../
cp references/detection/engine.py ../
cp references/detection/coco_utils.py ../

4.现在我们可以做所有必要的进口

import numpy as np
import torch
import torch.utils.data
from PIL import Image
import pandas as pdfrom torchvision.models.detection.faster_rcnn import FastRCNNPredictorfrom engine import train_one_epoch, evaluate
import utils
import transforms as T

配置模型

1.准备数据

与必须为我们的训练数据创建新类型的数据文件(与 TensorFlow 中的 training.record 文件一样)相反,我们只需编写一个数据集 python 类,稍后模型将使用该类来解析图像和相应的注释。该类需要从 torch.utils.data.Dataset 继承并实现 getitemlen 方法。由于我们将使用与前面相同的浣熊数据集,并且 repo 中的 data 文件夹已经包含一个包含所有已解析注释的浣熊 _labels.csv 文件,所以我们所要做的就是编写一个小的助手函数:

def parse_one_annot(path_to_data_file, filename):
   data = pd.read_csv(path_to_data_file)
   boxes_array = data[data["filename"] == filename][["xmin", "ymin",        
   "xmax", "ymax"]].values

   return boxes_array

然后我们可以用它来编写我们的 RaccoonDataset 类:

class RaccoonDataset(torch.utils.data.Dataset):
   def __init__(self, root, data_file, transforms=None):
       self.root = root
       self.transforms = transforms
       self.imgs = sorted(os.listdir(os.path.join(root, "images")))
       self.path_to_data_file = data_filedef __getitem__(self, idx):
      # load images and bounding boxes
      img_path = os.path.join(self.root, "images", self.imgs[idx])
      img = Image.open(img_path).convert("RGB")
      box_list = parse_one_annot(self.path_to_data_file, 
      self.imgs[idx])
      boxes = torch.as_tensor(box_list, dtype=torch.float32)num_objs = len(box_list)
      # there is only one class
      labels = torch.ones((num_objs,), dtype=torch.int64)
      image_id = torch.tensor([idx])
      area = (boxes[:, 3] - boxes[:, 1]) * (boxes[:, 2] - boxes[:,
      0])
      # suppose all instances are not crowd
      iscrowd = torch.zeros((num_objs,), dtype=torch.int64)
      target = {}
      target["boxes"] = boxes
      target["labels"] = labels
      target["image_id"] = image_id
      target["area"] = area
      target["iscrowd"] = iscrowdif self.transforms is not None:
         img, target = self.transforms(img, target)
      return img, targetdef __len__(self):
         return len(self.imgs)

我们可以运行一个快速检查,看看是否一切都正确实现了。实例化一个 RaccoonDataset 对象并运行 getitem

dataset = RaccoonDataset(root= "<your path>/raccoon_dataset",data_file= "<your path>/raccoon_dataset/data/raccoon_labels.csv")dataset.__getitem__(0)

输出应该是这样的

(<PIL.Image.Image image mode=RGB size=650x417 at 0x7FE20F358908>,  {'area': tensor([141120.]),
 'boxes': tensor([[ 81.,  88., 522., 408.]]),   
 'image_id': tensor([0]),   
 'iscrowd': tensor([0]),   
 'labels': tensor([1])})

2.下载并调整模型

由于我们之前在 TensorFlow 中使用了一个更快的 R-CNN,我们在这里也将使用一个。而且火炬接力也没有给我们太多的选择。在他们的网站上,他们只有两个预先训练好的(在 coco 上)用于物体检测的模型。一个更快的 R-CNN ResNet-50 FPN 和一个面罩 R-CNN ResNet-50 FPN

为了下载预训练的更快的 r-cnn,我们可以编写一个小的 get_model 函数来调整模型的最后一层,以输出许多适合我们的浣熊数据集的类:

def get_model(num_classes):
   # load an object detection model pre-trained on COCO
   model = torchvision.models.detection.
           fasterrcnn_resnet50_fpn(pretrained=True)# get the number of input features for the classifier
   in_features = model.roi_heads.box_predictor.cls_score.in_features
   # replace the pre-trained head with a new on
   model.roi_heads.box_predictor = FastRCNNPredictor(in_features,/
   num_classes)

   return model

我们还可以添加一个小小的 transformer 函数,通过做一些基本的数据扩充(图像的水平翻转)来增强我们的训练集

def get_transform(train):
   transforms = []
   # converts the image, a PIL image, into a PyTorch Tensor
   transforms.append(T.ToTensor())
   if train:
      # during training, randomly flip the training images
      # and ground-truth for data augmentation
      transforms.append(T.RandomHorizontalFlip(0.5))
   return T.Compose(transforms)

现在,我们可以实例化我们的训练和测试数据类,并将它们分配给 data_loaders,data _ loaders 控制在训练和测试期间如何加载图像(批量大小等)。

# use our dataset and defined transformationsdataset = RaccoonDataset(root= "<your path>/raccoon_dataset",
          data_file= "<your 
          path>/raccoon_dataset/data/raccoon_labels.csv",
          transforms = get_transform(train=True))dataset_test = RaccoonDataset(root= "<your path>/raccoon_dataset"
               data_file= "<your
               path>/raccoon_dataset/data/raccoon_labels.csv",
               transforms = get_transform(train=False))# split the dataset in train and test settorch.manual_seed(1)
indices = torch.randperm(len(dataset)).tolist()
dataset = torch.utils.data.Subset(dataset, indices[:-40])
dataset_test = torch.utils.data.Subset(dataset_test, indices[-40:])# define training and validation data loaders
data_loader = torch.utils.data.DataLoader(
              dataset, batch_size=2, shuffle=True, num_workers=4,
              collate_fn=utils.collate_fn)data_loader_test = torch.utils.data.DataLoader(
         dataset_test, batch_size=1, shuffle=False, num_workers=4,
         collate_fn=utils.collate_fn)print("We have: {} examples, {} are training and {} testing".format(len(indices), len(dataset), len(dataset_test)))

训练模型

首先,我们可以使用以下命令来检查 GPU 设置是否正确:

torch.cuda.is_available()

如果你在 Google Colab 上运行,只需将运行时间切换到 GPU 即可。

现在我们可以使用我们的 get_model 函数来建立模型。此外,我们还必须指定一个优化器和一个学习率调度器,以便随着时间的推移调整学习率。

device = torch.device('cuda') if torch.cuda.is_available() else torch.device('cpu')# our dataset has two classes only - raccoon and not racoon
num_classes = 2
# get the model using our helper function
model = get_model(num_classes)
# move model to the right device
model.to(device)# construct an optimizer
params = [p for p in model.parameters() if p.requires_grad]
optimizer = torch.optim.SGD(params, lr=0.005,
                            momentum=0.9, weight_decay=0.0005)# and a learning rate scheduler which decreases the learning rate by # 10x every 3 epochslr_scheduler = torch.optim.lr_scheduler.StepLR(optimizer,
                                               step_size=3,
                                               gamma=0.1)

为了进行训练,我们现在必须编写自己的 for 循环,遍历我们希望训练的历元数,然后调用 PyTorch 的 train_one_epoch 函数,调整学习速率,最后每个历元评估一次。

# let's train it for 10 epochsnum_epochs = 10
for epoch in range(num_epochs):
   # train for one epoch, printing every 10 iterations
   train_one_epoch(model, optimizer, data_loader, device, epoch,
                   print_freq=10)# update the learning rate
   lr_scheduler.step()
   # evaluate on the test dataset
   evaluate(model, data_loader_test, device=device)

为了保存训练好的模型(没有自动检查点),我们可以在完成训练后运行以下命令:

os.mkdir("<your path>/pytorch object detection/raccoon/")torch.save(model.state_dict(), "<your path>/pytorch object detection/raccoon/model")

10 个时代后,我得到了 0.66 的地图。

用模型做预测

我们首先必须加载模型,其工作方式是用前面的 get_model 函数实例化同一个“空”模型,然后将保存的权重加载到网络架构中。

loaded_model = get_model(num_classes = 2)loaded_model.load_state_dict(torch.load(“<your path>/pytorch object detection/raccoon/model”))

现在,我们可以使用 dataset_test 类来获取测试图像及其基本事实,并通过预测器运行它们。

idx = 0
img, _ = dataset_test[idx]
label_boxes = np.array(dataset_test[idx][1]["boxes"])#put the model in evaluation mode
loaded_model.eval()with torch.no_grad():
   prediction = loaded_model([img])image = Image.fromarray(img.mul(255).permute(1, 2,0).byte().numpy())
draw = ImageDraw.Draw(image)# draw groundtruthfor elem in range(len(label_boxes)):
   draw.rectangle([(label_boxes[elem][0], label_boxes[elem][1]),
   (label_boxes[elem][2], label_boxes[elem][3])], 
   outline ="green", width =3)for element in range(len(prediction[0]["boxes"])):
   boxes = prediction[0]["boxes"][element].cpu().numpy()
   score = np.round(prediction[0]["scores"][element].cpu().numpy(),
                    decimals= 4)
   if score > 0.8:
      draw.rectangle([(boxes[0], boxes[1]), (boxes[2], boxes[3])], 
      outline ="red", width =3)
      draw.text((boxes[0], boxes[1]), text = str(score))image

测试集中的 raccoon-45.jpg

简短比较

总而言之,可以肯定地说,对于习惯于命令式编码(代码在编写时执行)并一直使用 scikit-learn 类型 ML 框架的人来说,PyTorch 很可能更容易让他们开始使用(一旦 TensorFlow 将对象检测 API 升级到 2.x 版,这也可能会改变)。在本文中,我只关注“易用性”这一方面,而不关注模型的性能速度、可变性或可移植性。然而,当试图将这样的模型部署到生产性软件环境中时,需要许多这样的特性。在这里,TensorFlow 中使用的带有 config.py 文件的高级 API 从一开始就在框架中融入了更多的灵活性,其中较低级别的 torchvision 更加开放,这意味着更容易看到“幕后”(您不必研究大量 TensorFlow 源代码),但是在其教程中只显示标准。任何不符合标准的内容都必须从头开始编写,而 TensorFlow 可能会在配置文件中有一个部分来解决这个问题。此外,TensorFlow 模型动物园有更多预先训练好的模型可供人们使用。总的来说,我的经验类似于数据科学社区中一个非常典型的观点,即 PyTorche 的对象检测框架可能更适合于研究目的和“理解实现的每个细节”,而 TensorFlow 的 API 是构建生产模型的更好选择。

我希望你们能从中获得一些有价值的信息。现在是时候亲自尝试一下了。快乐编码:)

参考资料:

张量流物体检测:

[## 张量流/模型

创建精确的机器学习模型,能够在单个图像中定位和识别多个对象…

github.com](https://github.com/tensorflow/models/tree/master/research/object_detection)

火炬传递:

[## TorchVision 对象检测微调教程— PyTorch 教程 1.5.0 文档

提示为了充分利用本教程,我们建议使用 Colab 版本。这将允许您试验…

pytorch.org](https://pytorch.org/tutorials/intermediate/torchvision_tutorial.html)

卷积神经网络(coursera):

[## 卷积神经网络| Coursera

本课程将教你如何建立卷积神经网络,并将其应用于图像数据。感谢深…

www.coursera.org](https://www.coursera.org/learn/convolutional-neural-networks?specialization=deep-learning)

posted @ 2024-10-14 11:50  绝不原创的飞龙  阅读(307)  评论(0)    收藏  举报