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

原文:TowardsDataScience Blog

协议:CC BY-NC-SA 4.0

在维基百科上使用 BeautifulSoup

原文:https://towardsdatascience.com/using-beautifulsoup-on-wikipedia-dd0c620d5861?source=collection_archive---------50-----------------------

使用 BeautifulSoup 将表格(的一部分)变成字典

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

国家癌症研究所Unsplash 上拍摄的照片

如果你不熟悉的话,网页抓取是从网页上获取数据并以列表、字典或表格的形式保存的过程。在 Python 中,包美汤是人们用来进行网页抓取的。从本质上说,web 抓取就是查看给定网站上的 html 代码,识别代码中您要查找的内容,并告诉您的代码从这些部分获取数据。这里有一个来自英国烘焙大赛第一页的例子。

在本例中,我们将关注第一个表,它包含面包师的全名、年龄、职业和家乡。下面是表格本身的截图

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

截图来自维基百科页面

现在,我们将查看该页面上的 html 代码,您可以右键单击该页面,然后单击“Inspect”,对我来说,这是倒数第三个,您的结果可能会明显不同。现在,您的屏幕应该看起来像这样:

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

从这里开始,你可以滚动 html 来阅读代码,找到你要找的东西,但是有一个更简单的方法。如果你看一下 inspect 页面屏幕的左上角,你会看到在一个正方形内有一个看起来像鼠标指针的图标。如果您单击该指针,然后将鼠标悬停在页面上感兴趣的区域上,将会显示该区域在代码中的确切位置:

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

如果你看一下 inspect 侧,行上高亮显示的文本对应于页面上当前高亮显示的文本。从那里,您可以查看该表的其余代码,并对事物的位置有所了解。现在我们已经为从页面抓取数据做好了准备。

假设我想创建一个字典,其中的键是参赛者的名字,值是一个包含参赛者年龄和家乡的列表。查看页面 inspection,我们可以看到该数据在页面上的位置:

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

现在进入代码,首先我要导入必要的包、请求和 BeautifulSoup,然后向页面发出请求,并创建我将用来从中提取数据的 Soup。

在这里,我可以创建我的字典,我将它命名为“参赛者姓名年龄城镇”:

遍历代码时,我们会稍微违反直觉,从最后开始。在第 6-8 行,我们看到一个“for”语句,意思是整个字典是一个字典理解(这只是一个列表理解,但作为字典而不是列表)。现在,让我们看看第 7 行的第一部分。

soup.find("table", class_="wikitable")

在 BeautifulSoup 中,说“find”而不是“find_all”只会返回您告诉代码要查找的任何内容的第一个实例。因此,虽然页面有多个表,但这将只返回第一个表,这很好,因为这是我们想要使用的表。现在转到附加在末尾的 find_all 语句,我们看到我要求它查找所有标记为“tr”的实例,这是 table row 的缩写。第 8 行基本上确保了每当我请求代码查找某个东西时,我不是在请求一个 None 对象,因为那会返回一个错误。

回到第 1 行,我们看到:

contestant_name_age_town = {item.td.text:

这是分配钥匙,正如我所说的,是参赛者的名字。所以,我说过,对于每一行,查看第一个标记为“td”的内容,并从中提取文本。有趣的是,字典中的值是:

[int(item.td.find_next_siblings(limit = 3)[0].text), item.td.find_next_siblings(limit = 3)[2].a.get('href')]

您可能注意到的第一件事是,这两个项目都以“item . TD . find _ next _ siblings(limit = 3)”开头。还记得 item.td 是怎么给我们选手名字的吗?接下来的三个条目为我们提供了该行的所有其他信息,或者 item.td 的接下来三个兄弟条目。当您执行“find_next_siblings”时,该函数将返回一个兄弟条目列表。因此,下一项是索引。列表中的第一项是年龄,所以这是第一个兄弟(特别是文本,它被转换成整数)。最后一项是家乡信息,这就是为什么它的索引是 2。从那里我得到了家乡页面的 url 片段。下面是字典理解的打印结果:

{'Annetha Mills': [30, '/wiki/Essex'],
 'David Chambers': [31, '/wiki/Milton_Keynes'],
 'Edward "Edd" Kimber': [24, '/wiki/Bradford'],
 'Jasminder Randhawa': [45, '/wiki/Birmingham'],
 'Jonathan Shepherd': [25, '/wiki/St_Albans'],
 'Lea Harris': [51, '/wiki/Midlothian'],
 'Louise Brimelow': [44, '/wiki/Manchester'],
 'Mark Whithers': [48, '/wiki/Wales'],
 'Miranda Gore Browne': [37, '/wiki/Midhurst'],
 'Ruth Clemens': [31, '/wiki/Poynton,_Cheshire']}

现在,为什么我取了 url 片段而不仅仅是家乡名呢?因为在我的下一篇文章中,我将向您展示如何创建一个从多个页面中抓取的脚本,以便我们可以提取像家乡的人口和面积这样的数据。

利用伯特对抗工作诈骗

原文:https://towardsdatascience.com/using-bert-to-battle-job-scams-d98e6a0ca1e1?source=collection_archive---------53-----------------------

伯特模型有许多实际应用。在这里,我们用它来解决一个就业骗局。

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

来源

BERT 是一种现代语言表示方法,由谷歌研究人员在 2018 年开发,并在 2019 年的出版物 中概述了 BERT:用于语言理解的深度双向转换器 的预训练。该方法对于诸如问题回答、语言推理和常识推理的任务是有用的。正如论文所概述的,当在几个基准自然语言理解(NLU)数据集上训练时,BERT 优于先前的自然语言处理(NLP)方法。其中包括通用语言理解评测( GLUE )数据集、多体裁自然语言推理( MultiNLI )数据集、斯坦福问答数据集( SQUAD )等等。

BERT 的独特之处在于它能够将双向上下文转化为单词表示。这到底是什么意思?通常,语言表示是上下文无关模型或单向/从左到右语言模型的结果。BERT 是预训练上下文表征的第一个扩展,包括来自两个方向的上下文。为了理解这一点,让我们考虑三种类型的文本表示:

1.上下文无关

一个上下文无关的模型,比如 word2vec 和 GloVe,会在句子“他是一个棒球投手”和“他渴了,所以他要了一罐水”中给单词“pitcher”相同的表示。虽然我们知道,基于上下文,单词“pitcher”有不同的含义,但上下文无关的模型无法提供有区别的表示。

2。 单向上下文

一个单向的上下文模型,比如 OpenAI GPT,会从左到右用前面的单词来表示每个单词。例如,“他曾是棒球投手”中的“pitcher”用“他曾是棒球手”来表示与上下文无关的模型不同,单向模型为单词表示提供了一些上下文。尽管如此,单向模型是有限的,因为单词只能用前面的文本来表示。这种局限性激发了对双向语言模型的需求,这种模型能够完全捕捉单词的上下文含义。

3。 双向语境

虽然单向模型提供了一些上下文,但有时为了完全理解一个单词的意思,两个方向都需要上下文。例如,考虑句子“他知道水罐在桌子上。”像奥博奈·GPT 这样的模特会用“他知道”来代表“投手”这个词伯特是双向的,他会恰当地用“他知道”和“水在桌子上”来表示“水罐”通过用前后文本表示单词,BERT 能够准确地表示单词在文本中的含义。你可以在谷歌人工智能博客这里阅读更多关于伯特捕捉双向语境的能力。

根据我们的理解,这提出了如何在实践中使用的问题。最相关的应用是谷歌的搜索引擎,它使用 BERT 来增强搜索结果。在这篇谷歌博客文章中,作者概述了实施 BERT 后对搜索结果的一些关键改进。例如,他们使用查询“你能在药店为某人买药吗?”在 BERT 之前,结果建议如何得到一个处方的资源。在伯特之后,结果正确地代表了情境:为另一个人拿处方药,而不是一般的处方。结果显示了相关内容的页面。

现在让我们考虑 BERT 的一个有趣的应用。近几十年来,企业已经采用了各种基于云的招聘广告解决方案,如应用程序跟踪系统(ATS)。虽然这使企业能够更有效地招聘员工,但它也让骗子有机会制作和发布欺诈性内容。具体来说,骗子们已经变得善于为蓝领和秘书工作制作令人信服的广告。此外,通过像 Workable 这样的应用程序跟踪系统,骗子可以毫不费力地收集成千上万份使用欺诈性帖子作为诱饵的简历。

雇佣诈骗有两个目的。首先是收集联系信息,如电子邮件、邮政编码、电话号码和地址。这些信息可以卖给营销人员和陌生来电者。雇佣诈骗的更恶意的目的是身份盗窃。这些虚假帖子通常会将用户从 ATS 引导到第三方网站,然后用户会在那里经历一系列虚假的面试活动。最终,用户被要求提供高度敏感的信息,如社会安全号码和银行账户信息,这些信息可用于洗钱。

最近,爱琴海大学发布了 就业骗局爱琴海数据集 ,试图揭露就业骗局问题。该数据包含约 18,000 条包含真实生活和欺诈性招聘广告的记录。识别虚假职位的任务自然属于二进制分类。具体来说,我们将使用 BERT 模型对虚假职位进行分类,通过使用双向上下文来表示职位发布中的单词,这些单词被标记为“欺诈”或“真实”。在这个用例中,在“欺诈性”帖子中找到的单词的上下文含义应该与在“真实”帖子中找到的单词的上下文含义不同。与单向和上下文无关的模型相比,捕获这些双向上下文差异应该导致分类性能的提高。在开始之前,感谢马頔·舒尔加,他的帖子“伯特来拯救”启发了我在这里的工作。

现在让我们开始吧!

1.导入包

首先,让我们导入一些必要的包:

import pandas as pd
import numpy as np
import torch.nn as nn
from pytorch_pretrained_bert import BertTokenizer, BertModel
import torch
from keras.preprocessing.sequence import pad_sequences
from sklearn.metrics import classification_report

2.数据探索

接下来,让我们将数据读入一个数据框,并打印前五行。我们还可以将最大显示列数设置为“无”:

pd.set_option('display.max_columns', None)
df = pd.read_csv("fake_job_postings.csv")
print(df.head())

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

为简单起见,让我们看看“描述”和“欺诈”栏:

df = df[['description', 'fraudulent']]
print(df.head())

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

我们分类模型的目标在“欺诈性”一栏中为了了解“欺诈”值的分布和种类,我们可以使用集合模块中的“计数器”:

from collections import Counter
print(Counter(df['fraudulent'].values))

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

“0”值对应于正常的工作发布,“1”值对应于欺诈性发布。我们看到数据略有不平衡,这意味着正常的职位发布(17,000)多于欺诈性发布(866)。

在继续之前,让我们删除“NaN”值:

df.dropna(inplace = True)

接下来,我们希望平衡我们的数据集,使“欺诈”和“非欺诈”类型的数量相等。我们还应该随机改变目标:

df_fraudulent= df[df['fraudulent'] == 1]
df_normal = df[df['fraudulent'] == 0]
df_normal = df_normal.sample(n=len(df_fraudulent))
df = df_normal.append(df_fraudulent)
df = df.sample(frac=1, random_state = 24).reset_index(drop=True)

再次验证我们得到了想要的结果:

print(Counter(df['fraudulent'].values))

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

接下来,我们想要格式化数据,以便它可以用作我们的 BERT 模型的输入。我们将数据分为训练集和测试集:

train_data = df.head(866)
test_data = df.tail(866)

我们生成一个包含“描述”和“欺诈”关键字的字典列表:

train_data = [{'description': description, 'fraudulent': fraudulent } for description in list(train_data['description']) for fraudulent in list(train_data['fraudulent'])]test_data = [{'description': description, 'fraudulent': fraudulent } for description in list(test_data['description']) for fraudulent in list(test_data['fraudulent'])]

从字典列表中生成元组列表:

train_texts, train_labels = list(zip(*map(lambda d: (d['description'], d['fraudulent']), train_data)))
test_texts, test_labels = list(zip(*map(lambda d: (d['description'], d['fraudulent']), test_data)))

生成令牌和令牌 id:

tokenizer = BertTokenizer.from_pretrained('bert-base-uncased', do_lower_case=True)train_tokens = list(map(lambda t: ['[CLS]'] + tokenizer.tokenize(t)[:511], train_texts))test_tokens = list(map(lambda t: ['[CLS]'] + tokenizer.tokenize(t)[:511], test_texts))train_tokens_ids = list(map(tokenizer.convert_tokens_to_ids, train_tokens))test_tokens_ids = list(map(tokenizer.convert_tokens_to_ids, test_tokens))train_tokens_ids = pad_sequences(train_tokens_ids, maxlen=512, truncating="post", padding="post", dtype="int")test_tokens_ids = pad_sequences(test_tokens_ids, maxlen=512, truncating="post", padding="post", dtype="int")

请注意,我们将输入字符串截断为 512 个字符,因为这是 BERT 可以处理的最大令牌数。

最后,为我们的测试和训练集生成一个基于“欺诈”值的布尔数组:

train_y = np.array(train_labels) == 1
test_y = np.array(test_labels) == 1

4.模型构建

我们创建了我们的 BERT 分类器,它包含一个“初始化”方法和一个返回令牌概率的“转发”方法:

class BertBinaryClassifier(nn.Module):
    def __init__(self, dropout=0.1):
        super(BertBinaryClassifier, self).__init__()
        self.bert = BertModel.from_pretrained('bert-base-uncased')
        self.dropout = nn.Dropout(dropout)
        self.linear = nn.Linear(768, 1)
        self.sigmoid = nn.Sigmoid()

    def forward(self, tokens, masks=None):
        _, pooled_output = self.bert(tokens, attention_mask=masks, output_all_encoded_layers=False)
        dropout_output = self.dropout(pooled_output)
        linear_output = self.linear(dropout_output)
        proba = self.sigmoid(linear_output)
        return proba

接下来,我们生成训练和测试掩码:

train_masks = [[float(i > 0) for i in ii] for ii in train_tokens_ids]
test_masks = [[float(i > 0) for i in ii] for ii in test_tokens_ids]
train_masks_tensor = torch.tensor(train_masks)
test_masks_tensor = torch.tensor(test_masks)

生成用于训练和测试的令牌张量:

train_tokens_tensor = torch.tensor(train_tokens_ids)
train_y_tensor = torch.tensor(train_y.reshape(-1, 1)).float()
test_tokens_tensor = torch.tensor(test_tokens_ids)
test_y_tensor = torch.tensor(test_y.reshape(-1, 1)).float()

最后,准备我们的数据加载器:

BATCH_SIZE = 1
train_dataset =  torch.utils.data.TensorDataset(train_tokens_tensor, train_masks_tensor, train_y_tensor)
train_sampler =  torch.utils.data.RandomSampler(train_dataset)
train_dataloader =  torch.utils.data.DataLoader(train_dataset, sampler=train_sampler, batch_size=BATCH_SIZE)test_dataset =  torch.utils.data.TensorDataset(test_tokens_tensor, test_masks_tensor, test_y_tensor)
test_sampler =  torch.utils.data.SequentialSampler(test_dataset)
test_dataloader =  torch.utils.data.DataLoader(test_dataset, sampler=test_sampler, batch_size=BATCH_SIZE)

5.微调

我们使用 Adam 优化器来最小化二进制交叉熵损失,并且我们使用 1 个时期的批量大小 1 来训练:

BATCH_SIZE = 1
EPOCHS = 1bert_clf = BertBinaryClassifier()
optimizer = torch.optim.Adam(bert_clf.parameters(), lr=3e-6)for epoch_num in range(EPOCHS):
    bert_clf.train()
    train_loss = 0
    for step_num, batch_data in enumerate(train_dataloader):
        token_ids, masks, labels = tuple(t for t in batch_data)
        probas = bert_clf(token_ids, masks)
        loss_func = nn.BCELoss()
        batch_loss = loss_func(probas, labels)
        train_loss += batch_loss.item()
        bert_clf.zero_grad()
        batch_loss.backward()
        optimizer.step()
        print('Epoch: ', epoch_num + 1)
        print("\r" + "{0}/{1} loss: {2} ".format(step_num, len(train_data) / BATCH_SIZE, train_loss / (step_num + 1)))

我们评估我们的模型:

bert_clf.eval()
bert_predicted = []
all_logits = []
with torch.no_grad():
    for step_num, batch_data in enumerate(test_dataloader):token_ids, masks, labels = tuple(t for t in batch_data)logits = bert_clf(token_ids, masks)
        loss_func = nn.BCELoss()
        loss = loss_func(logits, labels)
        numpy_logits = logits.cpu().detach().numpy()

        bert_predicted += list(numpy_logits[:, 0] > 0.5)
        all_logits += list(numpy_logits[:, 0])

print(classification_report(test_y, bert_predicted))

这将显示一个包含精确度、召回率和 f1 分数指标的混淆矩阵,所有这些指标都描述了模型的性能。显然,这篇文章只是解决伯特雇佣骗局的初级读本。通过额外的微调,我们可以进一步提高准确性,并发现更多的欺诈性列表。BERT 模型相对于上下文无关和单向上下文模型的优势是显而易见的。双向语言处理使得 BERT 更擅长使用上下文来确定一个列表的合法性,从而保护求职者免受不良行为者的剥削。虽然这只是将机器学习用于解决现实世界问题的一种方式,但它是 BERT 如何有用的一个很好的例子。如果对 BERT 的其他应用感兴趣,可以阅读 假新闻分类用 BERT俄罗斯巨魔推文:分类用 BERT 。如果你对伯特方法有兴趣,我鼓励你阅读 伯特拯救 *。*这篇文章的代码可以在 GitHub 上找到。感谢您的阅读!

使用 BigQuery、Firebase Analytics 来吸引、吸引和评估您的应用用户

原文:https://towardsdatascience.com/using-bigquery-firebase-analytics-to-engage-attract-and-assess-your-app-users-da02980daffa?source=collection_archive---------30-----------------------

处理点击流数据时的有用提示和重要转换,以及强大的查询示例。

什么是 Firebase Analytics?

你开发了一个令人惊叹的应用程序,但是你如何知道用户是否以正确的方式使用它,或者他们对应用程序的哪些部分更感兴趣?Firebase Analytics 是记录用户分析的最好的免费库之一。无论你的应用是用于 Web、iOS、Android,甚至是 Flutter, Firebase Analytics 都可以与之集成。一旦集成了分析库,您需要记录您感兴趣的所有事件以及特定于该事件的任何参数,如 app_purchase (购买金额)、 audio_play (音频 id)等。默认情况下,Firebase 向您显示许多不同的图表来解释您正在记录的事件。如果您想进行更多的定制分析,您可以在 firebase 仪表板中将您的 firebase analytics 链接到 BigQuery。BigQuery 将包含所有的原始数据事件,您可以处理它们来进行更复杂的分析。本文详细说明了如何……

这篇文章是给你的,如果…

您刚刚创建并启动了您的移动应用程序(或网站),现在您有兴趣知道如何查询 Firebase 在我们说话时每秒钟为您处理的庞大数据集。或者您只是好奇,想知道对点击流事件数据的一些有用查询,这些查询可以为您提供一些可操作的见解。无论如何,荣誉和欢迎教程。今天,我们将讨论使用 Google BigQuery 的技巧、诀窍和更多内容。我们开始吧!

云控制台中的 BigQuery Web UI

一旦将 firebase analytics 链接到 BigQuery,您的控制台应该是这样的。我将在这个屏幕上提到几个我日常使用的项目:

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

  • 查询编辑器:可以把它想象成我们写查询的黑板。
  • 保存查询按钮:保存重要查询以供将来参考的按钮。
  • 保存的查询:这是我们将要使用 save query 按钮保存的所有查询的主页。
  • 查询历史:它存储您过去运行过的所有查询(当您想要重用查询时很有用)。

既然我们已经理解了基本的布局,让我们直接进入一些有用的查询。

日期时间格式

Firebase 擅长跟踪你的应用程序或网站的每次点击的大量事件数据。例如,关于应用首次打开的时间、应用最后更新的时间、应用内购买的时间等信息。这意味着与这些事件相关联的时间和日期对于提供洞察力非常有用。默认情况下,Firebase 将事件的日期和时间记录为列 event_timestamp 中的一个整数

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

这个整数基本上是客户端记录事件的时间(以微秒计,UTC)。使用FORMAT_DATE & FORMAT_TIME将这个整数转换成两个独立的(人类可读的)日期和时间列可能很有吸引力:

SELECT 
FORMAT_DATE('%d-%m-%Y', PARSE_DATE('%Y%m%d', event_date)) AS date, 
FORMAT_TIME('%T', TIME(TIMESTAMP_MICROS(event_timestamp))) AS time
FROM analytics_xxxxxxxxx.events_*

这给出了以下简洁的输出:

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

如果您的最终目标是向您的同事或其他利益相关者展示这两个专栏,这种格式应该很适合您。

但是,您可能会使用事件日志文件中的日期和时间列对数据集进行大量转换。在那种情况下,FORMAT_DATE将不会是最有效的转换。这是因为它返回的输出是类型为 string 的,而 BigQuery 支持的大多数日期和时间函数(比如添加两个日期,将日期截断到特定的粒度)要求日期是 date 或 DATETIME 格式。

在这种情况下,我喜欢使用TIMESTAMP_MICROS函数从 event_timestamp 中提取日期和时间,然后使用CAST AS DATETIME将这个时间戳转换为 DATETIME 对象:

SELECT
  CAST(TIMESTAMP_MICROS(event_timestamp) AS DATETIME) As UTC_Time 
FROM analytics_xxxxxxxxx.events_*

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

注意:默认情况下,存储的时间是 UTC 时区

与存储在 event_timestamp 列中的整数相比,这种格式更加清晰。同时,我们设法保留了 DATETIME 格式,这样任何日期或时间转换仍然是可能的。我们在下面的例子中演示了这种格式的一种用法。

将 UTC 时间转换为本地时间

UTC 是 Firebase 用来存储与事件相关的日期和时间的默认时区。事件数据库中还记录了另一个名为*‘time _ zone _ offset _ seconds’*的功能,该功能以秒为单位存储与 GMT 的偏差(根据国家的不同,可能为正值或负值)。因此,将这个偏移量加到任何给定的 UTC 时间上,就会得到正确的本地时间。

以下是使用 CTE(通用表表达式)计算本地时间的查询。它首先检查偏移量是否为空,在这种情况下,我们将 UTC 时间设置为本地时间。在非空偏移的情况下,我们首先将偏移秒转换为小时,并使用DATETIME_ADD将其添加到 UTC 日期:

**WITH CTE AS (**
SELECT *,
 CAST(TIMESTAMP_MICROS(event_timestamp) AS DATETIME) As UTC_Time,
 device.time_zone_offset_seconds AS offset_seconds,
FROM analytics_xxxxxxxxx.events_*
**)****SELECT UTC_Time, offset_seconds,
CASE
WHEN offset_seconds is NULL then UTC_Time
ELSE DATETIME_ADD(
UTC_Time, INTERVAL CAST(offset_seconds/3600 AS INT64) HOUR)
END As local_time
FROM CTE**

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

老实说,我对我的数据库中的空偏移量感到很困扰(天哪,有很多!).因此,我决定通过检查 Firebase 存储的 geo.country 列中的国家来手工编码偏移时间,从而解决其中的一些问题。

因此,将上面的查询更新为:

**WITH CTE AS (**
SELECT *,
  CAST(TIMESTAMP_MICROS(event_timestamp) AS DATETIME) As UTC_Time,
  device.time_zone_offset_seconds AS offset_seconds,
FROM analytics_xxxxxxxxx.events_*
**)**

**SELECT *, 
CASE
  WHEN offset_seconds IS NULL AND geo.country IN ('India', 'Pakistan') THEN DATETIME_ADD(UTC_Time, INTERVAL CAST(5.5 AS INT64) HOUR) 
  WHEN offset_seconds IS NULL AND geo.country = 'United States' THEN DATETIME_SUB(UTC_Time, INTERVAL CAST(5 AS INT64) HOUR) 
  WHEN offset_seconds IS NULL AND geo.country = 'Australia' THEN DATETIME_ADD(UTC_Time, INTERVAL CAST(10 AS INT64) HOUR) 
  WHEN offset_seconds IS NULL AND geo.country = 'Canada' THEN DATETIME_SUB(UTC_Time, INTERVAL CAST(5 AS INT64) HOUR) 
  WHEN offset_seconds IS NULL AND geo.country = 'China' THEN DATETIME_ADD(UTC_Time, INTERVAL CAST(8 AS INT64) HOUR) 
  WHEN offset_seconds IS NULL AND geo.country IN ('France', 'Spain', 'Germany', 'Sweden', 'Italy', 'Sweden', 'United Kingdom', 'Ireland') THEN DATETIME_ADD(UTC_Time, INTERVAL CAST(1 AS INT64) HOUR) 
  WHEN offset_seconds IS NOT NULL THEN DATETIME_ADD(UTC_Time, INTERVAL CAST(IFNULL(offset_seconds,0)/3600 AS INT64) HOUR) 
  ELSE UTC_time
END As local_time
FROM CTE**

唷,这看起来好多了!我们已经设法修复了一些主流国家的时间,这些国家在应用程序上注册了大量的流量。

附注:我使用的时差并不准确,例如,加拿大作为一个国家,在西部和东部观察不同的时区,但为了本教程的目的,我采取了更简单的方法。

对本地时间进行转换,以提取星期几、工作日名称和时间

一旦我们计算出了本地时间,我们就可以通过引入进一步的转换来更进一步。包含上述所有代码的最终查询如下所示:

WITH CTE AS (
SELECT *,
  CAST(TIMESTAMP_MICROS(event_timestamp) AS DATETIME) As UTC_Time,
  device.time_zone_offset_seconds AS offset_seconds,
FROM analytics_xxxxxxxxx.events_*
),**CTE2 AS (**
SELECT *, 
CASE
  WHEN offset_seconds IS NULL AND geo.country IN ('India', 'Pakistan') THEN DATETIME_ADD(UTC_Time, INTERVAL CAST(5.5 AS INT64) HOUR) 
  WHEN offset_seconds IS NULL AND geo.country = 'United States' THEN DATETIME_SUB(UTC_Time, INTERVAL CAST(5 AS INT64) HOUR) 
  WHEN offset_seconds IS NULL AND geo.country = 'Australia' THEN DATETIME_ADD(UTC_Time, INTERVAL CAST(10 AS INT64) HOUR) 
  WHEN offset_seconds IS NULL AND geo.country = 'Canada' THEN DATETIME_SUB(UTC_Time, INTERVAL CAST(5 AS INT64) HOUR) 
  WHEN offset_seconds IS NULL AND geo.country = 'China' THEN DATETIME_ADD(UTC_Time, INTERVAL CAST(8 AS INT64) HOUR) 
  WHEN offset_seconds IS NULL AND geo.country IN ('France', 'Spain', 'Germany', 'Sweden', 'Italy', 'Sweden', 'United Kingdom', 'Ireland') THEN DATETIME_ADD(UTC_Time, INTERVAL CAST(1 AS INT64) HOUR) 
  WHEN offset_seconds IS NOT NULL THEN DATETIME_ADD(UTC_Time, INTERVAL CAST(IFNULL(offset_seconds,0)/3600 AS INT64) HOUR) 
  ELSE UTC_time
END As local_time
FROM CTE
**)****SELECT 
DATE(local_time) as date, -- returns a DATE
EXTRACT(DAYOFWEEK FROM DATE(local_time)) as day_of_Week, 
FORMAT_DATE('%a', DATE(local_time)) as weekday_name,
TIME(local_time) as time, -- returns a TIME
CASE
  WHEN TIME(local_time) BETWEEN '06:00:00' AND '11:00:00' THEN 'Morning'
  WHEN TIME(local_time) BETWEEN '11:00:00' AND '16:00:00' THEN 'Afternoon'
  WHEN TIME(local_time) BETWEEN '16:00:00' AND '19:00:00' THEN 'Evening'
  WHEN TIME(local_time) BETWEEN '19:00:00' AND '23:59:00' THEN 'Night'
  ELSE 'LateNight'
END AS time_of_day
from CTE2**

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

注意:day_of_Week是星期日=1 的数字;星期一=2,以此类推。

这些本地时间的转换对于评估你的应用用户何时最活跃,或者一周中的哪一天向他们发送应用内购买营销电子邮件是有意义的,是非常强大的。

为推荐系统准备数据

推荐系统基于用户的显式(例如:评级)和隐式(例如:花费的时间)偏好、其他用户的偏好以及用户和项目属性来建议用户感兴趣的项目。在最基本的层面上,为您的应用程序构建任何此类建议所需的数据框需要三列:user_id、item_id 和 rating。让我们看看如何从数据库中查询它。

我已经做了几个星期的播客数据集了(感谢我的朋友,他一直在做一个神奇的播客应用程序,叫做Podurama——可以在 Android 和 iOS 上使用);我记录了一个名为 logReview 的自定义事件。每当有人试图对应用程序上的播客进行评级并记录四条主要信息(以及其他信息)时,就会触发该事件:

  • 收藏 Id :播客 Id
  • guid :剧集 id
  • 评级:1 到 5 之间的整数
  • 交互类型 : 0-删除;1-创建;2-编辑

对于单个用户,该事件的行看起来是这样的:

SELECT user_pseudo_id, event_name, event_params
FROM analytics_xxxxxxxxx.events_*
WHERE event_name LIKE '%logReview%'

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

我知道这看起来很吓人,但请耐心听我解释发生了什么。这些事件参数(比如 ratingguid )并没有一个唯一的行,而是被分组到某种具有唯一键值对的 JSON 对象中。我可以解释得更详细,但没有什么能比得上托德·克佩尔曼的精彩解释。

底线是我们可以使用下面的查询中的UNNEST只提取我们感兴趣的事件参数。再次向托德大声欢呼,感谢他为我节省了这么多时间,并在这个帖子中解释得如此漂亮。

SELECT user_pseudo_id,
(SELECT value.int_value
FROM UNNEST(event_params) WHERE key = 'rating'
) AS rating
FROM analytics_xxxxxxxxx.events_*
WHERE event_name LIKE '%logReview%'

我们在这里所做的是告诉 BigQuery 让unnestevent_params 分开到单独的行中,并且只从那些key = rating 所在的行中选取int_value

输出如下所示:

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

这看起来很棒,除了我们仍然不知道这个评级是哪个播客的插曲。因此,我们将继续提取剧集 id 和播客 id。

SELECT user_pseudo_id,
(SELECT value.int_value
FROM UNNEST(event_params) WHERE key = 'rating'
) AS rating,
**(SELECT value.string_value
FROM UNNEST(event_params) WHERE key = 'guid'
) AS episode_id,
(SELECT value.int_value
FROM UNNEST(event_params) WHERE key = 'collectionId'
) AS podcast_id**FROM analytics_xxxxxxxxx.events_*
WHERE event_name LIKE '%logReview%'

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

这看起来几乎完美!为什么几乎你问?这是因为我们没有考虑这个评级是否被更改过(记得我们为每个日志审查事件存储了交互类型)。

从逻辑上讲,我们希望保留用户对播客剧集的最新评价。为了实现这一点,我们将使用LAST_VALUE窗口函数、PARTITIONED BY 用户 id剧集 idORDERED BY 时间戳:

**WITH CTE AS (**
SELECT user_pseudo_id,
(
SELECT value.int_value
FROM UNNEST(event_params) WHERE key = 'rating'
) AS rating,
(
SELECT value.string_value
FROM UNNEST(event_params) WHERE key = 'guid'
) AS episode_id,
(
SELECT value.int_value
FROM UNNEST(event_params) WHERE key = 'collectionId'
) AS podcast_id,
(
SELECT value.int_value
FROM UNNEST(event_params) WHERE key = 'interactionType'
) AS interaction_type,
event_timestamp, timeFROM analytics_xxxxxxxxx.events_*
WHERE event_name LIKE '%logReview%'
)**SELECT DISTINCT user_pseudo_id, episode_id, podcast_id,
LAST_VALUE(rating) OVER (PARTITION BY user_pseudo_id, episode_id ORDER BY event_timestamp ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING)  AS latest_rating 
FROM CTE**

LAST_VALUE...调用本质上是对数据进行分区,首先是按用户 id,在每个用户 id 内,按播客剧集。然后,在这些用户剧集片段(或分区)的每一个中,结果行按照事件时间戳排序。最后,选择对应于分区中最后一行的额定值。

瞧啊。我们做到了。:)现在我们有了开始我们的推荐系统建模所需的数据框架。您可以单击保存结果按钮将数据框导出到本地计算机上。

用于版本更新的定制电子邮件

作为一名应用程序开发人员,每隔几周就会推出一个新版本的应用程序,这是很自然的事情。理想情况下,新版本将有更多的特性和功能,并解决了以前版本的一些令人讨厌的错误。因此,确保大多数早期用户已经过渡到这个最新版本是很重要的,如果他们还没有,通过电子邮件给他们一些提示。

我们将检测所有在我们的应用程序上活跃但尚未切换到最新版本的用户。我们认为那些在过去 10 天内使用过该应用程序的用户是活跃的。

我们将使用FIRST_VALUE来检测用户第一次使用我们的应用程序时使用了哪个版本的应用程序。同样,我们将使用我们的好朋友LAST_VALUE来检测与该应用程序最近交互相关的应用程序版本,并提取该应用程序上次使用的日期。我们把这些都储存在 CTE 里。

WITH CTE AS (
SELECT 
user_id,
FIRST_VALUE(app_info.version) OVER (user_event_window) AS initial_version,
LAST_VALUE(app_info.version) OVER (user_event_window) AS latest_version,
LAST_VALUE(event_timestamp) OVER (user_event_window) AS app_last_used
FROM analytics_xxxxxxxxx.events_*
WHERE device.operating_system <> 'WEB' AND user_id IS NOT NULL
WINDOW user_event_window AS (PARTITION BY user_id ORDER BY event_timestamp ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING)
)

由于我们对 web 应用程序用户不感兴趣,我们指定了WHERE条件来检查设备的操作系统。

我们还使用了一个WINDOW调用user_event_window来指定事件窗口,这使得代码使用起来更加简洁(通过简化所有冗余的PARTITION BY调用)。

现在我们有了应用程序最后一次使用的日期(存储为app_last_used),我们可以使用DATE_DIFF从今天的日期中减去它。

DATE_DIFF(CURRENT_DATE(), DATE(TIMESTAMP_MICROS(app_last_used)), DAY) AS gap

如果结果小于或等于 10,这意味着该用户是一个活跃用户,因此是我们的应用程序更新电子邮件的目标。

现在是检索用户 id 的最终代码,这些用户应该根据他们上次使用应用程序的时间收到应用程序版本更新的电子邮件。

WITH CTE AS (
SELECT 
user_id,
FIRST_VALUE(app_info.version) OVER (user_event_window) AS initial_version,
LAST_VALUE(app_info.version) OVER (user_event_window) AS latest_version,
LAST_VALUE(event_timestamp) OVER (user_event_window) AS app_last_used
FROM `podcastapp-767c2.analytics_193436959.events_*`
WHERE device.operating_system <> 'WEB' AND user_id IS NOT NULL
WINDOW user_event_window AS (PARTITION BY user_id ORDER BY event_timestamp ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING)
)**SELECT user_id, initial_version, latest_version,
DATE(TIMESTAMP_MICROS(app_last_used)) AS last_used, 
DATE_DIFF(CURRENT_DATE(), DATE(TIMESTAMP_MICROS(app_last_used)), DAY) AS gap 
FROM CTE
WHERE latest_version NOT IN ('1.1.3') -- update the version here in future
GROUP BY user_id, initial_version, latest_version, app_last_used
HAVING gap <= 10**

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

我希望这篇文章为您提供了一些在 BigQuery 中使用事件数据的有用技巧。查看这篇文章,了解如何利用流程挖掘从事件数据中获取更多关于工作时间损失率和冷启动建议的信息。将来,我会提供一些食谱和其他有趣的查询,您可以尝试用于 Firebase 事件分析。

直到下次:)

[## 从点击流数据中评估应用用户行为的流程挖掘

对 pm4py python 库的深入介绍

towardsdatascience.com](/process-mining-to-assess-app-user-behavior-from-clickstream-data-8e53a71428a4) [## 如何对自己的数据集使用 BigQuery API?

使用 Flask 和 Bigquery APIs 根据用户查询参数从 Bigquery 数据集中提取数据。

towardsdatascience.com](/how-to-use-bigquery-api-with-your-own-dataset-c901972cebd) [## 如何使用 AutoKeras 用一行代码建立图像分类模型?

甚至不需要知道 Conv2d、Maxpool 或批处理规范化层是做什么的!

medium.com](https://medium.com/analytics-vidhya/how-to-use-autokeras-to-build-image-classification-models-using-one-line-of-code-c35b0c36e66e)

用脑电波控制音乐播放列表

原文:https://towardsdatascience.com/using-brainwaves-to-control-a-music-playlist-909668d82546?source=collection_archive---------34-----------------------

仅仅通过集中注意力来改变歌曲。

从小到大,我一直在想,是否有一天,只要想到你最喜欢的歌曲,它就会为你播放。我当时并没有意识到,但是像这样的技术如果存在的话,它的影响是巨大的。也就是说,如果我们能想到一首歌并播放出来,瘫痪的人能不能用同样的技术来思考,比如说,移动轮椅并转动轮子?

但现在,我将展示我如何使用 Muse 耳机和物联网技术来改变播放列表中的歌曲。这是我的妹妹在尝试这个系统,她让我们知道她正专注于通过举起拇指来切换歌曲。

如果她给出了一个口头信号,这可能会干扰她的注意力,因为这些神经离大脑更近,需要更多的肌肉运动。然而,拇指的轻微移动不会改变她的脑电波状态太多,这意味着这是一个很好的信号,让我们知道她什么时候开始集中精力改变歌曲。

背景资料

很好地理解脑电波是如何工作的,以及它们是如何被读取的,对于理解这个项目中的所有组件是如何组合在一起的非常重要。该项目还使用了物联网技术,包括一个连接到物联网集线器的扬声器和一个连接到集线器的 Muse 头带。

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

解释脑电波类型的图表。

我先从神经科学说起。如果你想更详细地了解脑电波,你可以阅读我的另一篇关于解读脑电波的文章,但我将在这里提供一个简短的总结。脑波分为五种主要类型——δ波、θ波、α波、β波和γ波。δ波是最慢的,出现在睡眠期间,γ波是最快的,出现在高度集中、注意力高度集中的情况下。有了 Muse 头带,我能够非常清楚地阅读β和α的状态,以及更难归纳和阅读的θ状态。这项工作做得非常好,因为这个项目最需要测量β波来检查浓度水平。

以下是该项目的物联网方面的更多细节。正如我之前所说,我需要将两个设备都连接到一个物联网集线器,以便更好地处理。Muse 头带每秒钟都会产生大量需要分析和解释的数据,如果在同一个脚本中完成,不会太准确。

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

物联网组件的信息流。

物联网集线器是两台物联网设备连接的中央服务器,用于发送和接收信息。在这种情况下,它确实非常有用,因为它可以减少客户端的大量繁重处理工作,还可以保持您的解决方案井井有条,因为无论连接了多少设备,信息都是在一个点上处理的。在这个特定的项目中,信息流可以在旁边的图表中看到。

如何测量浓度?

为了达到我们想要的目标,我们需要测量浓度。正如我之前提到的,β波的出现是这部分的完美目标。β波是在专注和学习的状态下出现的,比如你在做作业或者解题的时候。为了首先测量β波,电极需要靠近大脑的额叶或顶叶。

Muse 头带在 AF7 和 AF8 位置提供这些传感器。AF7 位置测量大脑额叶的左侧,AF8 位置测量大脑额叶的右侧。现在,数据从这些位置开始流动,需要建立一个阈值,以确定什么是集中状态,什么不是。

当你处于放松状态时,你的大脑很可能会产生α波而不是β波。这意味着从 Muse 头带传入的β波的频率将小于零,或者是负数。当你开始集中精力做某事时,比如解决一个数学问题,β波的频率会增加,显示大于零的数字。这意味着零很可能是我们在寻找的阈值,来决定你是否在集中注意力。

然而,为了使用零作为阈值,还需要考虑其他因素,例如,如果头带在数字中产生突然的尖峰,因为读取脑电波是一个非常敏感的过程,该怎么办?这也可以通过确保数字逐渐增加或在几秒钟内保持正数来解决,这意味着你肯定处于集中状态。

缪斯发带的代码可以在下面找到。我使用了一个名为 Muselsl 的 Python 库来处理这些头带。

[## alisyakainth/脑波播放列表/musedata.py

github.com](https://github.com/alisyakainth/brainwave-playlist/blob/master/musedata.py)

连接到扬声器

这个项目的第二个主要部分是将物联网中枢连接到扬声器,当达到专注状态时,扬声器负责切换到播放列表中的下一首歌曲。这是通过首先确保所有的处理都在物联网中枢中完成,以便它可以随时向扬声器发送信号来改变歌曲。一旦达到测试波的阈值,物联网中心就会向扬声器发送一首新歌,并立即播放。

为此,我使用了一个名为 winsound 的 Python 模块来控制设备的声音输出,比如开始和停止音轨。对于所有进出物联网集线器的连接,我使用了一个名为 Socket 的模块,它建立了一个 TCP 连接。我还为 Muse 头带使用了一个名为 BlueMuse 的特殊蓝牙流媒体服务,因为它可以与头带建立更强的连接,并且可以直接从这个流中访问数据。

现在,只要集中精神去改变一首歌,你就能改变它!正如一开始提到的,这项技术可以有更多的实际用途,比如帮助那些处于锁定状态的人交流或者以更好的方式控制假肢。我很兴奋看到这项技术的未来发展和它所拥有的东西!

物联网集线器和扬声器的代码可以在下面找到。

[## alisyakainth/脑波播放列表/iothub.py

github.com](https://github.com/alisyakainth/brainwave-playlist/blob/master/iothub.py) [## alisyakainth/脑波播放列表/扬声器. py

github.com](https://github.com/alisyakainth/brainwave-playlist/blob/master/speaker.py)

使用浏览器 Cookies 和 Voyager API 通过 Python 抓取 LinkedIn

原文:https://towardsdatascience.com/using-browser-cookies-and-voyager-api-to-scrape-linkedin-via-python-25e4ae98d2a8?source=collection_archive---------18-----------------------

在这篇文章中,我们将使用 requests 的 Sessions 方法、浏览器 cookies 和 LinkedIn 的 voyager API 从 LinkedIn 收集数据。

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

格雷格·布拉在 Unsplash 上的照片

抓取 LinkedIn 每天都变得越来越困难,尤其是如果你使用像 UiPath,Selenium 等开放软件的话。LinkedIn 软屏蔽你,并在登录时添加一个 reCAPTCHA,这变得很难绕过。LinkedIn 的 voyager API 可能是从 LinkedIn 提取信息的最佳方式。

LinkedIn 的数据可以用于许多目的,从线索生成开始,搜索工作,如果你是一名招聘人员,你可以为你的组织找到合适的候选人,如果你想为你的公司找到投资者,底线是你可以使用 LinkedIn 获得数据以满足你的需求。最重要的是你必须知道你在寻找什么。

我们开始吧

我们将使用 LinkedIn 的 voyager API 来抓取一家公司的 about 页面。
见/找航海家 API 的完整文档阅读 此处

首先,最重要的部分是获取浏览器 cookies,我们将在该过程的后期阶段使用它们。在深入研究不同类型的 Linkedin Cookies 之前,您将需要来自您的 LinkedIn 登录会话的两个主要 cookie,让我们看看如何访问我们的 cookie,只需遵循这些简单的步骤。点击谷歌浏览器中的选项(右上角有 3 个垂直点)。
2。在这之后,点击更多工具,然后是开发人员工具(你也可以通过使用键盘组合-ctrl+shift+I 来达到这里)。

现在,一旦您获得了开发人员工具的访问权限,您将看到名为 Elements、console 等选项卡。导航到 Application 选项卡,从这里复制两个 cookies 的值,分别命名为 li_at 和 JSESSIONID。

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

记下这两个 cookies 的值,并将其存储在某个我们稍后会用到的地方。我已经模糊了值,以防止我的帐户可能被滥用。

现在,我们已经完成了最初的部分,让我们进入有趣的部分——你猜对了——编码。

下面是完整的代码,我们一步步来看。

variables headers 是一个包含用户代理的字典,你也可以通过运行简单的 Google 搜索“我的用户代理”来使用自己的代理,你会得到类似这样的结果,只需粘贴你的结果并继续前进。

现在,LinkedIn 给每个公司页面分配一个唯一的 id,你可以很容易地找到它。一旦你有了那个,就用代码中的一来代替那个数字。

https://www . LinkedIn . com/voyager/API/entities/companies/{公司 id}

现在,我们将使用最初从浏览器中提取的 cookies 创建一个会话。

s.cookies['li_at'] = "your li_at cookie" s.cookies['JSESSIONID'] = "your JSESSIONID"

此外,我们需要 csrf-token 身份验证,并在我们的头中设置它。为此,我们需要修改 JSESSIONID 键,您的 JSESSIONID 将如下所示:

"ajax:7454514581611861831542146"

将需要从 JSESSIONID 中剥离",然后将其添加到标题中:

s.headers["csrf-token"] = s.cookies["JSESSIONID"].strip('"')

最后一部分非常简单,向 voyager API 发送 get 请求,并将响应转换为字典。

在这之后,你会看到关于该公司的所有信息。

脚注

有许多工具(我个人使用的一个工具是 PhantomBuster )可以做完全相同的事情,但是在规模上,它们本质上做的是在两个请求之间放置一个等待时间以及一些其他技巧。

这个脚本只是为了提取公司的详细信息,如果你试图在一个循环中运行它来一次获得成千上万 Linkedin 公司页面的详细信息,Linkedin 会阻止你,还可能永久禁止你的帐户,这可能非常非常难以恢复。

这只是我们用 voyager API 构建的一个工作流,你可以用它做很多很酷的事情,从获取一个人的个人资料开始,回复对话,发送连接请求或回复它们,用高级过滤器进行工作搜索,喜欢或评论帖子。简而言之,你可以做任何你在 LinkedIn 上做的事情,但是有它的 API,没有 UI。如果你还想看另一篇关于其他事情的文章,请在下面评论。

用 CAPM 评价上市投资公司的绩效

原文:https://towardsdatascience.com/using-capm-to-evaluate-the-performance-of-listed-investment-companies-with-r-4b3301cce76b?source=collection_archive---------30-----------------------

投资者选择投资 LIC 证券的主要原因是追求高于市场基准的回报,即阿尔法回报。我们利用资本资产定价模型分析了 118 家 ASX 上市投资公司过去 5 年的业绩。

投资回报应该补偿投资者投入资本的时间价值,以及部分或全部投资可能损失的风险。资本资产定价模型(CAPM)在金融领域被广泛使用,作为一种手段,在给定持有特定资产而非持有“无风险”资产(如主权政府债券)的相关风险水平的情况下,确定投资者预期从投资中获得的补偿水平。

风险类型

特殊(非系统)风险——指特定资产特有的风险。可以通过分散投资和保持结构良好的投资组合来降低特殊风险(Investopedia 2019a)。

系统性风险——指无法通过投资组合多样化来降低的市场范围风险。虽然投资者可以建立一个投资组合来限制他们的系统风险敞口,但 CAPM 理论表明,这将伴随着他们预期获得的回报的权衡(Investopedia 2019b)。

资本资产定价模型与计算金融资产的预期收益

因为有可能以消除特殊风险的方式分散投资,CAPM 假设投资回报不能补偿投资者持有这种类型的风险。因此,投资者必须分散投资,以避免承担无法获得补偿的风险*。

*[如何实现这种多样化超出了本文的范围,但是,在以后的文章中,我将讨论现代投资组合理论,该理论能够开发旨在通过资产多样化消除特殊风险的资产组合,并根据个人风险承受能力偏好获得适当的系统风险敞口水平。]

CAPM 方法描述了一项资产的预期回报与其系统风险之间的关系。为了计算给定风险的资产的预期回报,通过 CAPM,使用以下等式:

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

通过操纵这个等式,我们可以利用金融资产数据和回归分析来首先评估 CAPM 理论在实践中是否成立,其次评估特定金融资产的表现。

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

这种方法源自 Jensen (1968),在该方法中,系统地考察了美国共同基金的表现,以确定是否有任何基金能够“跑赢市场”(Brooks 2008,第 67-81 页)。

利用 CAPM 评估目前在澳大利亚证券交易所上市的投资公司的业绩

*上市投资公司(lic)——*LIC 类似于管理型共同基金,但是,投资者可以像在证券交易所买卖普通股一样买卖 LIC 股票。因此,历史定价数据在网上很容易获得,可以很容易地进行分析。一般来说,持有 LIC 证券的目的是获得 LIC 经理人的技能和专业知识,他们利用积极的投资策略来超越既定的基准。对于持有 LIC 证券的投资者,每年收取 1.0-1.5%的管理费。当回报率高于规定的基准时,低收入国家收取绩效费也并不少见,通常为基准回报率的 10-20%(first links,2016)。

*被动交易所交易基金(ETF)——*与主动管理的低收入国家不同,被动管理的 ETF 的目标是复制既定基准的表现,如市场指数(ASXETFS.com 2019)。投资者持有被动 ETF 的预期费用通常只是主动管理型低收入国家的一小部分。例如,我们的分析使用 Vanguard Australia n Shares Index ETF(VAS ),该指数试图在考虑费用之前跟踪标准普尔/ASX 300 指数的回报,截至 2020 年 5 月,费用为每年 0.1%(Vanguard 2020)。

为什么投资者更喜欢 LIC 而不是被动型 ETF? —使用上面介绍的 CAPM 术语,投资者选择 LIC 投资而不是被动 ETF 的主要原因是相信 LIC 经理采用的主动投资策略将产生“alpha”回报( alpha > 0),同时将风险敞口降至最低。

实例分析——评估过去 5 年里低收入国家的绩效

以下章节将利用 Jensen (1968)的 CAPM 方法,通过获得每种证券的 alphabeta 参数的估计值,来检验澳大利亚低收入国家的历史表现。此外,我们将使用我们的发现来评估构建投资组合的潜力,以复制使用被动指数跟踪交易所交易基金(ETF),即先锋澳大利亚股票指数 ETF (VAS)和澳大利亚联邦政府债券的 LIC 资产的回报。

我们分析的首要任务是获得所需的数据。我们决定使用过去 5 年的月度回报数据,因为这是计算 CAPM 参数*的常见时间框架和频率,但是,分析和代码可以很容易地进行调整,以适应替代的时间框架和频率。在我们的分析中,我们使用了从 2015 年 6 月 1 日到 2020 年 3 月 1 日的退货数据。

*[这是目前在雅虎(Au)上用于 beta 计算的时间框架和频率】

数据收集

LIC 列表—https://www.asxlics.com获得当前在 ASX 交易的 118 个低收入国家的列表,作为数据帧导入 R 并清理。

# IMPORT AND TIDY LIC LIST #################################

LICs <- read.csv("https://www.asxlics.com/uploads/csv/20200401-lics.csv", header = TRUE)
n <- nrow(LICs)
LICs <- LICs[c(2:n), 1:3]
lic.colnames <- c("Code", "Company", "Market Cap")
names(LICs) <- lic.colnames
ticker <- as.character(LICs[,1])
row.names(LICs) <- ticker

无风险利率数据— 主权政府债券作为“无风险”资产在金融领域被广泛使用(Investopedia 2020b)。在我们的分析中,我们将使用 5 年到期的澳大利亚联邦政府债券的收益率数据*。这一数据可以从澳大利亚储备银行的网页上获得。

*[选择 5 年时间框架只是因为它与为数据收集选择的 5 年时间框架非常匹配。]

*[*在将这些数据导入 R 之前,我们很快在 Excel 中将日期格式化为 YYYY-MM-DD。]

# IMPORT AND TIDY RISK-FREE RATE DATA ######################

Rf <- import("f2.1-data.csv") ## need to have manually formatted dates to YYYY-MM-DD in Excel
n <- nrow(Rf)
Rf <- Rf[c(12:n), c(1, 4)]
Rf <- Rf[!apply(Rf == "", 1, all),]
Rf$V1 <- as.Date(Rf$V1)
Rf$V4 <- as.numeric(Rf$V4)
Rf$V4 <- ((1+(Rf$V4/100))^(1/12)-1)
Rf <- xts(Rf$V4, order.by = Rf$V1)
names(Rf) <- c("Rf")

基准指数数据— 我们选择使用被动管理的先锋澳大利亚股票指数 ETF (VAS) 的定价数据作为市场回报的代理。该基金寻求跟踪标准普尔/ASX 300 指数的回报,因此,如果我们明确使用该市场指数的直接价格数据,或者在较小的程度上使用其他指数,如 All Ordinaries 或标准普尔/ASX 200 指数,我们预计会获得可比的结果。使用 Vanguard ETF 数据的原因是,它将使我们能够根据我们的初始 CAPM 建模轻松地生成复制投资组合。增值服务的历史价格数据从雅虎财经获得。我们使用“Adj. Close”价格数据,因为该数据系列针对股息分配进行了调整,并使我们的分析能够考虑投资者有权获得的股息现金流以及资本收益回报。

# IMPORT AND TIDY BENCHMARK DATA ###########################

Rb <- read.csv("https://query1.finance.yahoo.com/v7/finance/download/VAS.AX?period1=1430265600&period2=1588118400&interval=1mo&events=history")
n <- nrow(Rb)
Rb <- Rb[c(1:n-1), c(1,6)]
Rb$Date <- as.Date(Rb[, 1])
Rb <- xts(Rb$`Adj.Close`, order.by = Rb$Date)
names(Rb) <- c("Rb")
Rb$Rb <- Return.calculate(Rb$Rb, method = "log")

LIC 数据— 低收入国家清单随后被用于从雅虎财经获得每一个低收入国家的历史定价数据。再次使用“可调接近”价格数据。使用 R,将数据与无风险数据和基准数据一起编译到一个 xts 对象中,然后进行裁剪。

# IMPORT AND TIDY LIC DATA #################################

url_f <- "https://query1.finance.yahoo.com/v7/finance/download/"
url_e <- ".AX?period1=1430265600&period2=1588118400&interval=1mo&events=history"
n <- nrow(LICs)
data <- merge(Rf, Rb)
k <- nrow(data)
for(i in 1:n){
  url_temp_ch <- as.character(LICs[i,1])
  url_temp <- paste(url_f, url_temp_ch, url_e, sep = "")
  Ra_temp <- data.frame(rep(NA, k))
  try(Ra_temp <- read.csv(url_temp, na.strings = c("null")), silent = T)
  n_temp <- nrow(Ra_temp)
  try(Ra_temp <- Ra_temp[c(1:n_temp-1), c(1,6)], silent = T)

  if(is.na(Ra_temp[1, 1]) != TRUE){
    Ra_temp$Date <- as.Date(Ra_temp[, 1])
    Ra_temp <- xts(Ra_temp$`Adj.Close`, order.by = Ra_temp$Date)
    header <- as.character(LICs[i,1])
    names(Ra_temp) <- header
    Ra_temp[, 1] <- Return.calculate(Ra_temp[, 1], method = "log")
    data <- merge(data, Ra_temp)
    rm(Ra_temp)
  }
  else if(is.na(Ra_temp[1, 1]) == TRUE){
    data_temp <- data
    data_temp$Rf <- rep(data_temp[1, 2], k)
    data_temp <- data_temp$Rf
    header <- as.character(LICs[i,1])
    names(data_temp) <- header
    data <- merge(data, data_temp)
    rm(data_temp)
  }
}
n <- nrow(data)
data <- data[complete.cases(data[1:n, c(1, 2)]),]
LIC.list <- names(data)
names(data) <- LIC.list
n <- ncol(data)
LIC.list <- LIC.list[3:n]

生成 CAPM 变量— 如上所述,CAPM 回归分析要求我们计算每个 LIC 的“超额收益”和“市场风险溢价”。如果我们希望导出并保存数据,这些数据将被计算并添加到一个名为“capm.data”的新数据框架中。

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

# GENERATE CAPM VARIABLES ##################################

n <- ncol(data)
capm.data <- as.xts(merge(data$Rf, data$Rb-data$Rf))
names(capm.data) <- c("Rf", "mrp")
for(i in 3:n){
  Ra.Er_temp <- as.xts(data[, i]-data$Rf)
  header <- as.character(names(data))
  header <- paste(header, ".Er", sep = "")
  names(Ra.Er_temp) <- header[i]
  capm.data <- merge(capm.data, Ra.Er_temp)
}
n <- ncol(capm.data)
LICs$Code <- LIC.list

计算 CAPM 参数α和β

编辑完数据后,我们现在准备计算 CAPM 参数αβ。为此,我们使用一系列线性回归,将我们的低收入国家的“超额收益”作为因变量,将“市场风险溢价”作为解释变量。值得注意的是,并非我们所有的低收入国家都有整个 5 年期的数据,因此,由于可用数据点较少,一些参数的估计会不太精确*。下面的代码计算参数 alphabeta ,并使用 t-test 函数来生成 p 值,这样我们就可以对参数估计的精确度有所了解。

*[同样,人们可能更喜欢调整时间框架和频率来缓解这一问题。】

# LOAD t-TEST FUNCTION #####################################

ttest <- function(reg, coefnum, val){
  co <- coef(summary(reg))
  tstat <- (co[coefnum,1]-val)/co[coefnum,2]
  2 * pt(abs(tstat), reg$df.residual, lower.tail = FALSE)
}

# CALCULATE CAPM PARAMETERS ################################

n <- ncol(capm.data)
capm.para <- data.frame()
for(i in 3:n){
  try(
    capm <- lm(capm.data[, i] ~ capm.data$mrp)
  , silent = T)
  para.temp <- data.frame(rep(0, 4))
  try(para.temp <- capm$coefficients, silent = T)
  para.temp <- as.data.frame(para.temp)
  para.temp <- as.data.frame(transpose(para.temp))
  try(para.temp[1, 3] <- ttest(capm, 1, 0), silent = T)
  try(para.temp[1, 4] <- ttest(capm, 2, 1), silent = T)
  names(para.temp) <- c("alpha", "beta", "alpha(0) ~ Pr(>|t|)", "beta(1) ~ Pr(>|t|)")
  row.names(para.temp) <- as.character(LICs[i-2,1])
  capm.para[i-2, 1:4] <- para.temp[1, 1:4]
  try(rm(capm), silent = T)
  rm(para.temp)
}
row.names(LICs) <- LICs$Code
LICs <- merge(LICs, capm.para, by.x = 1, by.y = 0, all.x = TRUE, all.y = TRUE)

对于 beta ,我们简单地测试了我们的估计值与 1 无关的假设,即 LIC 证券倾向于与市场整体一致*移动。

*[该测试和随后的 p 值与我们的特定分析路线不太相关,但是,可以修改 t 测试来测试是否与任何特定值无关,因此,如果我们选择将我们的分析深入到个别低收入国家,以及它们在历史上如何对更广泛的市场波动做出反应,这可能是有用的。]

对于 alpha ,我们的假设是我们的估计值从零开始是无关紧要的,这表明我们还没有发现 LIC 经理人的表现优于市场,或者根据我们收集的数据,有证据表明 CAPM 不成立。因此,我们只需检查我们是否获得了任何 lic 的正的且具有统计显著性的 alpha 值。

# CHECK FOR POSITIVE AND SIGNIFICANT ALPHA RETURNS #########

LICs$alpha.rtns <- ifelse(LICs$`alpha(0) ~ Pr(>|t|)`<= 0.05 & LICs$alpha > 0.0, "TRUE", "FALSE")

我们的结果显示,在此期间,没有 LIC 取得了统计上显著的(在 95%的置信水平下)和正的 alpha 回报。事实上,只有一个 LIC 的估计 alpha 值在统计上不同于零,在这种情况下, alpha 值为-0.0051,这表明相对于系统风险敞口,这个 LIC 的表现低于市场。如果我们将“显著性”截止值扩大到 90%的置信水平,我们只发现一个额外的具有显著 alpha 值的低收入国家,同样估计为负值,表明表现不佳。因此,我们可以得出结论,我们的分析无法找到证据表明,相对于风险敞口,积极管理的地方政府投资公司为投资者带来的回报明显不同于更广泛市场的回报。

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

排序结果表的标题

构建复制产品组合

由于我们现在已经看到,低收入国家并没有通过提供具有统计意义的更高回报来超越市场,我们能够将无风险资产和市场跟踪 ETF 相结合,以更低的风险敞口复制 LIC 的回报。从我们上面关于 alpha 回报的发现中,我们知道我们的复制投资组合和 LIC 证券投资组合的预期回报在统计上不会彼此不同*。因此,如果我们能够证明,与相对 LIC 相比,我们的复制投资组合表现出较小的回报差异,我们就可以确信,这些投资组合为投资者提供了可比的回报,同时降低了风险。

*[在这一点上,我们还需要提醒自己,我们没有考虑到地方投资公司可能收取的任何管理费或绩效费。]

计算 lic 及其相对复制组合的标准差

我们首先需要使用每个 LIC 之前产生的数据序列来计算 LIC 超额收益的标准差。

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

# CALCULATE SD(x) FOR LICs #################################

k <- ncol(data)-2
sd.temp <- as.numeric(vector())
er.list <- names(capm.data)
n <- nrow(er.list)
er.list <- er.list[3:(k+2)]
for(i in 1:k){
  sd.temp[i] <- STDEV(capm.data[, er.list[i]])
}
sd.temp <- as.data.frame(as.numeric(sd.temp))
row.names(sd.temp) <- LIC.list
names(sd.temp) <- c("SD(ER_at)")
LICs <- merge(LICs, sd.temp, by.x = 1, by.y = 0, all.x = TRUE, all.y = TRUE)

接下来,我们需要计算复制投资组合的超额回报,首先计算每个时期的历史回报,然后减去无风险利率。由此,可以计算复制投资组合超额收益的标准差。

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

# CALCULATE SD(x) FOR REP. PORT. ###########################

k <- nrow(data)
j <- nrow(LICs)
sd.temp <- as.numeric(vector())
for(i in 1:j){
  beta.temp <- as.data.frame(rep(LICs[i, 5], k))
  rep.port.temp <- beta.temp
  Rf.temp <- as.numeric(data$Rf)
  rep.port.temp <- add_column(rep.port.temp, Rf.temp, .after = 100)
  rtn.temp <- as.data.frame(data[, 2])
  rep.port.temp <- add_column(rep.port.temp, rtn.temp, .after = 100)
  names(rep.port.temp) <- c("Beta", "Rf", "Rtn")
  port.temp <- (1-rep.port.temp$Beta)*rep.port.temp$Rf+rep.port.temp$Beta*rep.port.temp$Rtn
  rep.port.temp <- add_column(rep.port.temp, port.temp, .after = 100)
  names(rep.port.temp) <- c("Beta", "Rf", "Rtn", "Port. Rtn")
  rep.port.temp$`Port. Rtn` <- as.numeric(unlist(rep.port.temp$`Port. Rtn`))
  rep.port.temp$Rtn <- as.numeric(unlist(rep.port.temp$Rtn))
  rep.port.temp$Exc.Port.Rtn <- as.numeric(unlist(rep.port.temp$`Port. Rtn`-rep.port.temp$Rf))
  sd.temp[i] <- STDEV(rep.port.temp[, 5])
}
LICs$"SD(ER_pt)" <- sd.temp

最后,我们检查我们的复制投资组合的回报率的标准差是否低于 LIC 证券的回报率,这表明风险暴露较少。

# COMPARE SD(x) PERFORMANCE ################################

LICs$'Lower Rep. Port. Risk?' <- ifelse(LICs$`SD(ER_pt)` <= LICs$`SD(ER_at)`, "TRUE", "FALSE")

结果表明,对于我们分析中的每个 LIC,我们都能够产生一个复制投资组合,将被动指数跟踪 ETF 和无风险资产结合起来,这将产生可比的回报率,同时使投资者面临更小的风险。

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

结果表主管(包括复制组合风险分析)

结论

我们通过这一分析表明,在我们的样本期(2015 年 6 月 1 日至 2020 年 3 月 1 日)内,我们未能拒绝我们的假设,即 LIC 经理人无法持续提供高于持有被动指数跟踪 ETF 和无风险资产的复制投资组合所提供的回报。此外,持有 LIC 证券的投资者被收取更高的管理费,在某些情况下,还要收取绩效费,他们持有这些证券是因为他们相信,他们的 LIC 经理的专业技能将会以最低的风险敞口带来最高的回报。然而,我们已经看到,通过持有复制投资组合,投资者也将能够减少他们的风险敞口。

需要注意的是,与所有金融资产一样,过去的表现并不总是未来表现的可靠指标。上述分析使用了 5 年的月度价格数据,使用不同的时间框架和周期可能会产生不同的结果。我发布这篇文章的目的不是提供财务建议,而是分享方法和编码,让其他人根据自己的喜好复制。

感谢您一直读到文章结尾!很想听听大家对以上的任何评价。欢迎随时留言,或者通过 LinkedIn 联系我。

r 包装清单

# Packages #############################pacman::p_load(pacman, expss, jtools, NCmisc, PerformanceAnalytics, purrr, quantmod, rio, stats, tibble, tidyquant, utils, xts, zoo)

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

参考

ASXETFS.com(2019 年)——https://www.asxetfs.com【访问日期:2020 年 5 月 8 日】

ASXLICS.com(2019 年)——https://www.asxlics.com【访问日期:2020 年 5 月 8 日】

布鲁克斯,C . 2008,《金融计量经济学导论》,第二版,剑桥大学出版社

Firstlinks (2016 年)——https://www . first links . com . au/understanding-lic-fee-structures【2020 年 8 月 5 日访问】

投资媒体(2019 年 a)——https://www.investopedia.com/terms/u/unsystematicrisk.asp【2020 年 5 月 8 日访问】

investopedia(2019 年 b)—https://www.investopedia.com/terms/s/systematicrisk.asp【2020 年 5 月 8 日访问】

investopedia(2020 a)—https://www.investopedia.com/terms/c/capm.asp【2020 年 8 月 5 日访问】

investopedia(2020 年 b)—https://www.investopedia.com/terms/r/riskfreeasset.asp【2020 年 5 月 8 日访问】

澳洲储备银行(2020)——【https://www.rba.gov.au/statistics/tables/#interest-rates 【2020 年 08 月 05 日获取】

先锋(2020)——https://www . vanguard investments . com . au/ret/ret/investments/product . html #/fund detail/ETF/portId = 8205/?概述【访问日期:2020 年 8 月 5 日】

雅虎财经(2020 年)——https://au.finance.yahoo.com【2020 年 5 月 8 日采访】

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

图片:pixabay.com

使用类进行机器学习

原文:https://towardsdatascience.com/using-classes-for-machine-learning-2ed6c0713305?source=collection_archive---------5-----------------------

使用面向对象编程来构建模型

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

Clem Onojeghuo 在像素上拍摄的照片

类提供了一种结合数据和功能的有用方式。类的模块化支持有效的故障排除、代码重用和问题解决。例如,如果您的代码中断,您将能够指向一个特定的类或类方法,而不必筛选太多其他代码。由于这些因素,模型开发自然地适合于面向对象的编程实践。

在本帖中,我们将讨论如何使用面向对象编程来读取数据、分割数据以进行训练、拟合模型以及进行预测。我们将使用天气数据,这些数据可以在这里找到。

在我们开始之前,让我们进口熊猫:

import pandas as pd 

现在,让我们定义一个名为 Model 的类:

class Model:
    def __init__(self, datafile = "weatherHistory.csv"):
        self.df = pd.read_csv(datafile)

该类将有一个“init”函数,也称为构造函数,它允许我们在创建该类的新实例时初始化数据。我们可以将一个新变量“model_instance”定义为一个对象(模型类的一个实例):

if __name__ == '__main__':
    model_instance = Model()

我们应该能够通过对象“model_instance”访问数据框让我们调用数据框并打印前五行数据:

if __name__ == '__main__':
    model_instance= Model()
    print(model_instance.df.head())

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

看起来不错。

接下来我们可以在初始化函数中定义一个线性回归对象。不要将它与“model_instance”混淆,后者是我们的自定义类“model”的一个实例:

class Model:
    def __init__(self, datafile = "weatherHistory.csv"):
        self.df = pd.read_csv(datafile)
        self.linear_reg = LinearRegression()

同样,值得注意的是 LinearRegression 是一个独立于我们的自定义“模型”类的类,并且在代码行中:

self.linear_reg = LinearRegression()

我们正在定义 LinearRegression 类的一个实例。

现在,让我们确保可以访问我们的线性回归对象:

if __name__ == '__main__':
    model_instance = Model()
    print(model_instance.linear_reg)

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

接下来我们要做的是定义一个方法,让我们分割数据用于训练和测试。该函数将采用一个“test_size”参数,让我们指定训练和测试的规模。

首先,让我们从“sklearn”中导入“train_test_split”方法,并导入“NumPy”:

from sklearn.model_selection import train_test_split
import numpy as np

我们将建立一个线性回归模型来预测温度。为简单起见,让我们用“湿度”和“压力(毫巴)”作为输入,用“温度”作为输出。我们将分割方法定义如下:

def split(self, test_size):
        X = np.array(self.df[['Humidity', 'Pressure (millibars)']])
        y = np.array(self.df['Temperature (C)'])
        self.X_train, self.X_test, self.y_train, self.y_test = train_test_split(X, y, test_size = test_size, random_state = 42)

接下来,让我们打印“X_train”和“y_train”以供检查:

if __name__ == '__main__':
    model_instance = Model()
    model_instance.split(0.2)
    print(model_instance.X_train)
    print(model_instance.y_train)

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

我们现在将为线性回归模型定义一个“拟合”函数:

def fit(self):
        self.model = self.linear_reg.fit(self.X_train, self.y_train)

我们还将定义一个“预测”函数:

def predict(self):
        result = self.linear_reg.predict(self.X_test)
        return result

现在,让我们打印我们的测试预测,其中测试规模是数据的 20%:

if __name__ == '__main__':    
    model_instance = Model()   
    model_instance.split(0.2)    
    model_instance.fit()
    print(model_instance.predict())

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

我们还可以打印模型性能:

if __name__ == '__main__':
    model_instance = Model()
    model_instance.split(0.2)
    model_instance.fit()    
    print("Accuracy: ",     model_instance.model.score(model_instance.X_test, model_instance.y_test))

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

我们还可以向我们的 predict 方法传递一个“input_value”参数,这将允许我们做出样本外的预测。如果“无”通过,则将对测试输入进行预测。否则,将对“输入值”进行预测:

def predict(self, input_value):
    if input_value == None:
        result = self.linear_reg.predict(self.X_test)
    else: 
        result = self.linear_reg.predict(np.array([input_value]))
    return result

让我们用一些样本外的测试输入来预测:

if __name__ == '__main__':
    model_instance = Model()
    model_instance.split(0.2)
    model_instance.fit()    
    print(model_instance.predict([.9, 1000]))

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

我们还可以将随机森林回归模型对象定义为模型字段,并运行我们的脚本:

class Model:
    def __init__(self, datafile = "weatherHistory.csv"):
        self.df = pd.read_csv(datafile)
        self.linear_reg = LinearRegression()
        self.random_forest = RandomForestRegressor()
    def split(self, test_size):
        X = np.array(self.df[['Humidity', 'Pressure (millibars)']])
        y = np.array(self.df['Temperature (C)'])
        self.X_train, self.X_test, self.y_train, self.y_test = train_test_split(X, y, test_size = test_size, random_state = 42)

    def fit(self):
        self.model = self.random_forest.fit(self.X_train, self.y_train)

    def predict(self, input_value):
        if input_value == None:
            result = self.random_forest.fit(self.X_test)
        else: 
            result = self.random_forest.fit(np.array([input_values]))
        return result if __name__ == '__main__':
    model_instance = Model()
    model_instance.split(0.2)
    model_instance.fit()    
    print("Accuracy: ", model_instance.model.score(model_instance.X_test, model_instance.y_test))

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

您可以轻松地修改代码来构建支持向量回归模型、“xgboost”模型等等。我们可以通过向构造函数传递一个参数来进一步一般化我们的类,当指定时,该构造函数从可能的模型列表中选择。

逻辑可能看起来像这样:

class Model:
    def __init__(self, datafile = "weatherHistory.csv", model_type = None):
       self.df = pd.read_csv(datafile) if model_type == 'rf':
            self.user_defined_model = RandomForestRegressor() 
       else:
            self.user_defined_model = LinearRegression()

并且修改了“拟合”和“预测”方法:

 def fit(self):
        self.model = self.user_defined_model.fit(self.X_train, self.y_train)

    def predict(self, input_value):
        if input_value == None:
            result = self.user_defined_model.fit(self.X_test)
        else: 
            result =     self.user_defined_model.fit(np.array([input_values]))
        return result

我们执行如下:

if __name__ == '__main__':
    model_instance = Model(model_type = 'rf')
    model_instance.split(0.2)
    model_instance.fit()    
    print("Accuracy: ", model_instance.model.score(model_instance.X_test, model_instance.y_test))

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

如果我们传递“None ”,我们会得到:

if __name__ == '__main__':
    model_instance = Model(model_type = None)
    model_instance.split(0.2)
    model_instance.fit()    
    print("Accuracy: ", model_instance.model.score(model_instance.X_test, model_instance.y_test))

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

我将在这里停下来,但我鼓励您添加额外的模型对象。您可以尝试的一些有趣的例子是支持向量机、“xgboost”回归和“lightgbm”回归模型。添加帮助器方法也很有用,这些方法可以为任何数字列生成汇总统计信息,如平均值和标准偏差。您还可以定义通过计算统计数据(如相关性)来帮助您选择要素的方法。

概括地说,在这篇文章中,我讨论了如何在面向对象编程框架中构建机器学习模型。这个框架对于故障诊断、问题解决、字段收集、方法收集等等非常有用。我希望您能在自己的数据科学项目中找到 OOP 的用处。这篇文章的代码可以在 GitHub 上找到。感谢阅读,机器学习快乐!

使用聚类改进分类—一个用例

原文:https://towardsdatascience.com/using-clustering-to-improve-classification-a-use-case-afb92106088e?source=collection_archive---------48-----------------------

提升朴素贝叶斯文本分类

在今天的博客中,我们将给出一篇发表在印度期刊《国际学术研究通知》上的早期文章的直觉。标题中给出了故事的情节。体裁是文本分类。主角是朴素贝叶斯和 k 均值。

这篇文章有两个目的

  • 激励你尝试自己的直觉(开始可能有点疯狂,比如使用聚类进行分类,实际上我被我的一位教授骂了一顿)并转化为正式的方法
  • 将作为将直觉转化为成熟的研究文章的蓝图。最重要的部分是你如何系统地建立你的工作方法。

它可以分为以下任务

任务 1:确定朴素贝叶斯需要改进

我们选取了 3 个公开可用的数据集。挑选了另外 3 种最大似然算法,并做了实证研究。

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

图 1:朴素贝叶斯与其他分类器(来源:作者)

这足以说明与其他算法相比,朴素贝叶斯的性能确实不佳。

提示:为了再现性,给出尽可能多的程序细节。挑选足够流行的算法进行比较。

还值得努力吗?我举两个论据,朴素贝叶斯是最容易理解的方法之一,其次,它相当快。

为了快速理解朴素贝叶斯,您可以使用参考文献 3

任务 2:发展直觉

这没有固定的公式。我们的想法是朴素贝叶斯假设特征或属性相互独立。这是它非常快的原因之一,同时这也是一个限制的弱点。对于文本分类,单词是我们的特征。具体来说,使用由来已久的 tf-idf 技术将文本转换成数字。更多的细节你可以查阅参考文献 5。

T 也就是说,如果它想吃披萨,就给他吃披萨,而不是全餐自助餐。

我们如何找到独立的或者相对独立的单词?

嵌入:第一步是拥有单词的特征表示。我们的观点是,如果两个单词在文档中出现的频率相似,那么它们就是相似的。我们来举个例子。

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

图 2:单词的嵌入(来源:作者)

如果我们看上面的表,并假设勾号表示文档中存在该单词,而叉号表示文档中不存在该单词,那么我们可以得出结论

  • 单词 1 和单词 2 不相似
  • 单词 2 和单词 3 相似

现在,如果我们把这种直觉松散地延伸一下,得出不是很相似的词相对独立的结论,可能不是很不正确。

如何轻松找到一组相对独立的单词?

字数和文档数量都很庞大。我们怎么可能在这么大的矩阵中找到它呢?另一个直觉,如果我们能根据相似度对特征进行分组,我们能不能不得到相对独立的词(特征)组。怎么做?这难道不像是一个集群问题吗?**答对了!**我们简单的对单词嵌入使用 k-means

  • 形成 k 个聚类,每个聚类将有一组相似的单词
  • 从每个聚类中选出一个有代表性的单词(最接近平均值)

这是论文的关键所在。有一些细微的差别,我会推荐你读这篇文章。我们首先使用卡方来删除一些特征(改天会有更多),让我们假设它有助于剔除无关紧要的单词。这种特征选择方法,我们称之为 FSCHICLUST。

我们可能有兴趣知道,通过这种方法减少了多少单词?

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

图 FSCHICLUST 实现的特征减少百分比(来源:作者)

我们可以从图 3 中看到,现在使用的单词数量显著减少。你一定很怀疑,就这么几句话,真的有用吗?我们现在回答这个问题。

任务 3:建立你的方案作品

这是一个棘手的部分,我们需要通过打破它来真正地计划好它。

  • 首先,它比普通的朴素贝叶斯好吗?
  • 其次,如果是(第一个问题的答案),那么性能是否可以与其他分类器相比?
  • 第三,如果我们用朴素贝叶斯应用任何标准的特征选择算法,我们的方法工作得更好吗?

我们必须证明这些词集是否提供了更好的分类准确性,这次我们增加了数据集的数量。

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

图 4:使用 FSCHICLUST 的朴素贝叶斯和特征选择(来源:作者)

与其他分类器相比,它做得好吗?分类器在相同的数据集上进行比较。

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

图 5:改进的朴素贝叶斯与其他分类器(来源:作者)

第三,与其他特征选择算法相比,它做得好吗

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

图 6:使用其他特征选择算法提出方法(来源:作者)

因此,我们看到我们所有的问题都得到了很好的满足,所以它可能也会满足审查者。

结论:

在本文中,我们讨论了如何使用 k-means 在文本分类中改进朴素贝叶斯。

  • 我们首先确定朴素贝叶斯做得不好。
  • 然后,我们开发了一个基于聚类的特征选择方案,只选择几个词
  • 论证这个方案的工程,如前所述,这必须系统地进行,可能是最重要的一点。

参考文献:

[1] Dey Sarkar S,Goswami S,Agarwal A,Aktar J .使用朴素贝叶斯进行文本分类的新特征选择技术。国际学术研究通知。2014;2014.(【https://www.hindawi.com/journals/isrn/2014/717092/】T2

[2] Sarkar、Subhajit Dey 和 Saptarsi Goswami。“基于过滤器的文本分类特征选择方法的实证研究。”国际计算机应用杂志 81.6 (2013)。

[3]https://towards data science . com/all-about-naive-Bayes-8e 13 cef 044 cf

https://archive.ics.uci.edu/ml/datasets.php

[5]https://towards data science . com/natural-language-processing-feature-engineering-using-TF-IDF-E8 b 9d 00 e 7 e 76

使用 ColumnTransformer 组合数据处理步骤

原文:https://towardsdatascience.com/using-columntransformer-to-combine-data-processing-steps-af383f7d5260?source=collection_archive---------8-----------------------

在不同列需要不同技术的情况下,创建内聚管道来处理数据

这个 scikit-learn 工具非常方便,但也有自己的一些怪癖。今天,我们将使用它来转换华盛顿州渡轮埃德蒙兹-金斯顿航线的渡轮等待时间数据。(谢谢 WSF 提供的数据!).完全公开:我们今天只使用数据集的一小部分。

更全面的披露——来自 scikit-learn 的警告:“警告:[**compose.ColumnTransformer**](https://scikit-learn.org/stable/modules/generated/sklearn.compose.ColumnTransformer.html#sklearn.compose.ColumnTransformer)类是实验性的,API 可能会改变。”

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

华盛顿州渡口。布莱恩·汉森在 Unsplash 上的照片

一般概念和用途

当您创建一个不同列需要不同转换的数据管道时,ColumnTransformers 就派上了用场。也许你有分类和数字特征的组合。也许您希望使用不同的插补策略来填充不同数字列中的 nan。您可以分别转换每一列,然后将它们缝合在一起,或者您可以使用 ColumnTransformer 来完成这项工作。

这里有一个基本的例子。在这种情况下,我们的输入要素是工作日(0–6 周一至周日)、小时(0–23)以及最高、平均和最低日温度。我想标准规模的温度功能和一个热编码的日期功能。

假设我已经加载了输入和目标数据帧(X_train,y_train ):

from sklearn.compose import ColumnTransformer
from sklearn.preprocessing import StandardScaler, OneHotEncoder
from sklearn.impute import SimpleImputer
from sklearn.linear_model import LinearRegression
from sklearn.pipeline import Pipeline# define column transformer and set n_jobs to use all cores
col_transformer = ColumnTransformer(
                    transformers=[
                        ('ss', StandardScaler(), ['max_temp', 
                                                  'avg_temp', 
                                                  'min_temp']),
                        ('ohe', OneHotEncoder(), ['weekday', 
                                                  'hour'])
                    ],
                    remainder='drop',
                    n_jobs=-1
                    )

然后我们就可以开始转型了!

X_train_transformed = col_transformer.fit_transform(X_train)

我们得到:

<465x30 sparse matrix of type '<class 'numpy.float64'>'
	with 2325 stored elements in Compressed Sparse Row format>

更有可能的是,您将把 ColumnTransformer 添加为管道中的一个步骤:

lr = LinearRegression()pipe = Pipeline([
            ("preprocessing", col_transformer),
            ("lr", lr)
       ])pipe.fit(X_train, y_train)

现在你的烟斗可以做预测了!或者用于交叉验证,而不会跨切片泄漏信息。

注意,我们需要以转换器期望的格式来指示列。如果转换器需要一个 2D 数组,那么传递一个字符串列的列表(即使它只有一列——例如[‘col1'])。如果转换器需要一个 1D 数组,只需传递字符串列名,例如'col1'

但是事情并不总是这么简单——可能您的数据集具有空值,并且需要在同一列上进行多次转换,您想要一个自定义的转换器,或者您想要更深入地挖掘功能的重要性,可能并不是所有 OneHotEncoder 类别实际上都保证会出现在所有数据片中。

技巧 1:对任何需要多次转换的列使用管道

我第一次使用 ColumnTransformer 时,我认为它会按顺序执行转换,我可以从在任何列上简单地输入 nan 开始,然后 StandardScale®一个重叠的列子集,然后 OneHotEncode 另一个重叠的列子集,等等。我错了。如果您想要在同一列上进行多次转换,您需要一个管道。这意味着每组得到相同处理的列都有一个管道,例如:

# define transformers
si_0 = SimpleImputer(strategy='constant', fill_value=0)
ss = StandardScaler()
ohe = OneHotEncoder()# define column groups with same processing
cat_vars = ['weekday', 'hour']
num_vars = ['max_temp', 'avg_temp', 'min_temp']# set up pipelines for each column group
categorical_pipe = Pipeline([('si_0', si_0), ('ohe', ohe)])
numeric_pipe = Pipeline([('si_0', si_0), ('ss', ss)])# set up columnTransformer
col_transformer = ColumnTransformer(
                    transformers=[
                        ('nums', numeric_pipe, num_vars),
                        ('cats', categorical_pipe, cat_vars)
                    ],
                    remainder='drop',
                    n_jobs=-1
                    )

技巧 2:跟踪你的列名

来自 scikit-learn 文档:“转换后的特征矩阵中的列顺序遵循在transformers列表中指定列的顺序。除非在passthrough关键字中指定,否则原始特征矩阵中未指定的列将从结果转换后的特征矩阵中删除。用passthrough指定的那些列被添加到变压器输出的右边。”

对于上面的例子,预处理的数组列是:

[‘max_temp’, ‘avg_temp’, ‘min_temp, ‘weekday_0’, ‘weekday_1’, ‘weekday_2’, ‘weekday_3’, ‘weekday_4’, ‘weekday_5’, ‘weekday_6’, ‘hour_0’, ‘hour_1’, ‘hour_2’, ‘hour_3’, ‘hour_4’, ‘hour_5’, ‘hour_6’, ‘hour_7’, ‘hour_8’, ‘hour_9’, ‘hour_10’, ‘hour_11’, ‘hour_12’, ‘hour_13’, ‘hour_14’, ‘hour_15’, ‘hour_16’, ‘hour_17’, ‘hour_18’, ‘hour_19’, ‘hour_20’, ‘hour_21’, ‘hour_22’, ‘hour_23’] 

这是非常繁琐的手工操作。对于提供功能名称的转换,您可以像这样访问它们:

col_transformer.named_transformers_['ohe'].get_feature_names()

这里,“ohe”是第一个示例中我的转换器的名称。不幸的是,不能创建更多特性/列的转换器通常没有这个方法,ColumnTransformer 依赖于其内部转换器的这个属性。如果你只使用有这个方法的变压器,那么你可以调用col_transformer.get_feature_names()来轻松地得到它们。我还没有这个机会,但我们可能会在某个时候。或者这个列跟踪功能可能会被添加到未来的 ColumnTransformer 版本中。

注意:如果你使用管道(就像技巧 1 中一样),你需要更深入一点,使用管道属性named_steps。在这种情况下:

col_transformer.named_transformers_['cats'].named_steps['ohe']\
     .get_feature_names()

技巧 3:随意创造你自己的变形金刚

ColumnTransformer 可以与任何转换器一起工作,所以您可以随意创建自己的转换器。我们今天不打算太深入地研究定制转换器,但是在使用定制转换器和 ColumnTransformer 时,我想指出一点。

对于我们的 ferry 项目,我们可以用一个定制的转换器提取日期特性:

from sklearn.base import TransformerMixin, BaseEstimatorclass DateTransformer(TransformerMixin, BaseEstimator):
    """Extracts features from datetime column

    Returns:
      hour: hour
      day: Between 1 and the number of days in the month
      month: Between 1 and 12 inclusive.
      year: four-digit year
      weekday: day of the week as an integer. Mon=0 and Sun=6
   """def fit(self, x, y=None):
        return selfdef transform(self, x, y=None):
        result = pd.DataFrame(x, columns=['date_hour'])
        result['hour'] = [dt.hour for dt in result['date_hour']]
        result['day'] = [dt.day for dt in result['date_hour']]
        result['month'] = [dt.month for dt in result['date_hour']]
        result['year'] = [dt.year for dt in result['date_hour']]
        result['weekday'] = [dt.weekday() for dt in 
                             result['date_hour']]
        return result[['hour', 'day', 'month', 'year', 'weekday']]

def get_feature_names(self):
        return ['hour','day', 'month', 'year', 'weekday']

注意,ColumnTransformer 以 numpy 数组的形式“发送”列。为了从字符串转换这些时间戳,我将它们转换为 pandas 数据帧(可能不是最优雅的解决方案)。

注意,ColumnTransformer 将所有指定的列一起“发送”到我们的转换器。这意味着您需要设计您的转换器来同时获取和转换多个列,或者确保在 ColumnTransformer 的单独一行中发送每个列。由于我们的自定义转换器仅设计用于处理单个列,因此我们需要像这样定制 ColumnTransformer(假设我们希望在有两个 datetime 列的情况下重用它,我们希望扩展这两个列):

transformers=[(‘dates1’, DateTransformer, [‘start_date’])ct = ColumnTransformer(
          transformers=[
              (‘dates1’, DateTransformer, [‘start_date’]),
              (‘dates2’, DateTransformer, [‘end_date’])
          ])

提示 4:对“罕见的”分类特征或标志要积极主动

这里的关键是您的模型期望在训练集、测试集和生产输入中有相同数量的特征。

如果我们有任何罕见的分类特征最终没有出现在这些组中,默认的 OneHotEncoding 设置将为不同的输入集产生不同数量的列。

类似地,如果有任何要估算的 nan,SimpleImputer 只创建一个“flag”列。如果一个或多个输入集碰巧没有任何 nan,那么在预处理阶段之后,列的数量将再次不同。

这可能会引发一些不同的错误,包括:

ValueError: Found unknown categories [0] in column 0 during transform

ValueError: The features [0] have missing values in transform but have no missing values in fit.

对于 OneHotEncoding 问题,可以在初始化 ohe 时列出类别。如果您有两个分类特征,第一个具有类别“一”和“二”,第二个具有“三月”、“四月”,您可以这样表示:OneHotEncoder(categories=[['one', 'two'], ['March', 'April]])

对于 simple imputr,您不能使用一个标志,删除带有 NaN 的列(如果 NaN 很少),调整您的训练测试分割(并确保您的生产输入考虑到这个差异),或者通过添加一个标志列来创建您自己的基于 simple imputr 的转换器,而不管 NaN 是否存在。

今天,这个数据准备步骤感觉有点不令人满意,因为我们还没有从我们的数据集得出任何结论或有趣的事实。但是你们都知道,这是我们预测轮渡等待时间(或者任何你想预测/分类/等等的事情)的必不可少的一步。

和往常一样,查看 GitHub repo 获取完整代码。编码快乐!

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

照片由帕特里克·罗宾逊Unsplash 拍摄

利用计算机视觉寻找最佳猫咪照片

原文:https://towardsdatascience.com/using-computer-vision-to-find-the-best-cat-photo-from-a-video-fd11c43596b8?source=collection_archive---------58-----------------------

与 YOLO、哈尔·卡斯卡特和 CPBD 一起

作者:基拉·巴拉德,安德鲁·傅,什拉万·纳格斯瓦兰,艾米莉·谢

这篇博客文章介绍了作者在哈佛应用计算科学研究所与 Austin Pets Alive 合作的 顶点 课程项目中所做的工作!领养孩子。本文所表达的观点仅代表作者的观点,并不代表他们的合作组织。

TL;博士

我们使用 ML 和计算机视觉技术来优化收容所猫的照片,以提高它们的收养率。见此视频此海报

介绍

鉴于美国动物收容所的拥挤问题,每年有数百万收容所的动物被实施安乐死。奥斯汀宠物活着!总部位于德克萨斯州的关注动物福利的组织 APA 试图通过倡导不杀动物来解决这个问题。作为他们努力的一部分,他们已经与一家技术公司 Adoptimize 合作,该公司提供软件来自动选择给定动物视频的最佳照片。由此产生的高质量照片转化为增加的收养率,从而降低安乐死率。然而,尽管 Adoptimize 产生了重大影响,但他们的技术只对狗有效,并不能推广到其他动物。

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

照片来自https://adoptimize.co/

考虑到收养率较低,收容所的猫比它们的犬类同伴更需要这项技术。作为我们在哈佛的顶点项目的一部分,我们与 APA 和 Adoptimize 合作,为猫开创了一个类似的过程,总体目标是提高猫的收养率,以减少它们安乐死的机会。因此,这个项目的目标是产生一个模型,当给定一个猫的视频时,它能选择最佳的帧。我们最终设计了一个逻辑回归模型,对通过各种计算机视觉技术提取的特征进行预测。

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

trieu88【coraline 1】CC BY-ND 2.0 下获得授权

数据和标签

我们的数据由我们的合作伙伴提供的 79 个收容所猫的视频组成。一眼看去,这些视频平均持续时间为 23 秒,最短 2 秒,最长 55 秒。它们大多以每秒 60 帧的速度播放,分辨率一般为 200 万像素。

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

图 1:直方图显示,我们数据集中的大部分视频大约为 20 秒或更短,而所有视频都不到一分钟。

为了更好地理解我们的数据集,这里有一个示例视频:

## sample _ 猫 _ 视频. mp4

虽然筛选起来很有趣,但这些数据在规模和质量方面带来了一些挑战。例如,许多视频是在弱光环境下拍摄的。有时猫在狗窝里,笼子的栅栏挡住了任何清晰的视线。在行为方面,有些猫特别胆小,躲着镜头。数据的另一个挑战是视频质量,因为许多视频是在移动设备上拍摄的,而不是在更高分辨率的摄像机上拍摄的。最后,数据集的大小有限,因为我们只有 79 个视频可以处理。

最重要的是,一个主要的障碍是我们的数据没有按照图像质量来标注。由于受监督的机器学习方法将最好地支持我们获得最佳照片的目标,我们需要找到一种以系统方式标记帧质量(因变量)的方法。

为了实现这一点,我们编写了一个简单的脚本,在我们的数据集中随机采样快照,通过一个准系统 UI 提供给我们,并记录我们的注释。对于质量本身的衡量,我们使用了通常在心理测量调查表格中使用的李克特量表,从 1 到 5 指定一个值,1 表示质量最低的照片,5 表示质量最高。在第一轮“实践”评分之后,为了就哪种类型的图像对应于每个李克特值达成普遍一致,我们坐下来,在我们的视频中共标记了 471 帧。

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

图 2:我们的标签界面截图。

在检查生成的数据集时,我们发现它略有偏差,因为高质量帧的比例很低(只有 12%的帧得分为 5)。因此,我们决定通过从互联网上获取额外的 20 个剪辑并对它们重复标记过程来扩展这个数据集,使我们的总数据集超过 500 个。唯一的挑战是互联网上没有猫的视频——任何地方都没有。

特征工程

物体检测

我们怀疑我们缺乏足够的数据用于深度学习方法,所以我们在合作伙伴的领域知识的指导下,将我们的努力集中在特征工程上。我们的第一个任务是检测一帧中是否有猫。我们研究了两种不同的对象检测算法:哈尔级联分类器[ 1 ](一种相对传统的方法)以及 YOLO [ 2 ](更接近最先进的技术)。

Haar 级联分类器使用 Haar-like 特征,该特征是相邻矩形区域中像素强度的和与差。Haar-like 特征用于训练决策树桩的 AdaBoost 集合。一系列这样的集成以增加的复杂度和降低的假阳性率来构造,从而尽可能快地拒绝负样本。我们能够使用在 OpenCV 中的猫脸上训练的开箱即用的实现。

YOLO 采用卷积神经网络(CNN)的形式,是一种单程对象检测算法,为对象提出边界框,并同时对它们进行分类。图像被分成网格,每个单元负责产生固定数量的边界框(包括边界框包含对象的置信度)以及类的概率分布,假设存在对象。在这个训练设置猫数据集 [ 3 ]的帮助下,我们使用了在 ImageNet 上预先训练的 YOLO 网络,并对其进行了微调,以检测猫的头部、耳朵、眼睛和鼻子。

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

图 3:由 Richardpics2 拍摄的照片 cat-5 上通过我们微调的 YOLO 模型提取的特征。 CC PDM 1.0

我们决定使用 YOLO 网络进行对象检测,而不是 Haar 级联分类器,因为它具有更高的召回率和一次检测多个特征的能力。我们将检测到的鼻子、眼睛和耳朵的置信度分数保存为一组预测值。

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

图 4:鼻子和眼睛的定量对象检测特征测量相对于每一帧的我们的标记类的曲线图都显示了有希望的相关性。

衍生特征

使用边界框,我们能够计算一组额外的特征,包括:

  • 从头部中心到框架中心的距离
  • 头部相对于框架的大小
  • 两只眼睛大小的比例
  • 两只耳朵大小的比例

我们认为,头部与图像中点的距离将反映出猫的居中程度,而头部的相对大小将作为猫在图像中所占百分比的一个不错的代表。最后,通过眼睛和耳朵的比例,我们旨在捕捉猫的脸可能偏离相机的角度。

锐利

由于图像清晰度往往是照片质量的一个因素,我们研究了两种不同的清晰度指标:应用拉普拉斯滤波器后像素强度的方差,以及模糊检测的累积概率(CPBD) [ 4 ]。在将这两种方法应用于我们的数据集时,我们只测量了猫头部边界框内区域的锐度。

我们从拉普拉斯方差开始,但是发现这个度量不能准确地表示图像的清晰度。然而,CPBD 指标与我们的数据集表现出了稍好的相关性,并且更容易处理,因为它是在 0-1 范围内标准化的。因此,我们最终选择这种方法作为我们的图像清晰度的措施。

EDA 预测器

在对我们的特征进行进一步的探索性数据分析后,我们发现产生最有希望的相关性的预测因子是由 YOLO 产生的检测到的眼睛、鼻子和耳朵的置信度值。这是有意义的,因为网络的训练数据大多包含正面朝向的猫的图像,这些图像具有安静的特征(警觉的眼睛,没有咆哮的鼻子等)——这些特征在一张好的猫收养照片中是可取的。

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

图 5:针对耳朵的定量对象检测特征测量和针对每一帧的我们的标记类别的锐度的图都显示了有希望的相关性。

有趣的是,我们也注意到清晰度和图像质量之间的关系并不那么明显。经过进一步思考,这可能是因为“好”的猫照片不一定是最清晰的——一张适度清晰、但带有温暖模糊的照片可能比一张猫脸转向一侧的非常清晰的照片更有吸引力。

此外,我们在要素中发现了某种程度的多重共线性,这需要在建模中解决。

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

图 6:我们的预测因子的相关矩阵显示了共线性。

造型

基线模型

对于我们的基线,我们用来对我们开发的模型进行测试,我们只是从输入视频中返回任何检测到猫头的随机帧。

初始模型

我们最初的模型基于特征的主观函数,其中我们对子集应用任意权重——在对我们的特征进行更深入的研究之前——我们直觉地认为最相关的是:锐度和头部大小。然后,该模型根据这两个特征的相对排名对每一帧进行评分,并返回得分最高的图像。

然而,这个最初的模型主要是作为一个粗略的概念证明。一旦我们有了足够数量的带注释的数据,我们在构建最终模型时就转向一种更有纪律的数据驱动的方法。

最终模型

我们最终使用了一个逻辑回归分类器来预测给定每个特征的图像质量标签。我们应用 L1 正则化来解决我们在要素中发现的一些共线性。为了进一步调整,我们检查了系数,发现头部的大小和它与中心的距离证明是没有意义的,所以我们删除了这些特征。

最终的模型通过回归运行每一帧,然后选择具有最大可能性的图像作为好或优秀(在 Likert 标度上分别为 4 级或 5 级)。

乍一看,这个模型表现得相当不错。如果我们将先前的示例视频传递给它,我们将返回以下帧:

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

我们的逻辑回归模型选择的“最佳”框架。

考虑到原始视频很少提供猫完全可见的镜头(因为狗窝栅栏经常与猫重叠),以及经常转向天花板或地面的抖动相机,这被证明是一个特别好的结果(当然,我们通过测试定量评估性能,在下一节中进一步描述)。

其他模式探索

虽然逻辑回归最终产生了最好的结果,但我们也用其他模型进行了实验。简单地说,我们尝试了线性回归,但是发现它产生了无法解释的结果,并且没有胜过逻辑回归。

接下来,我们尝试从头开始训练 CNN。然而,根据我们调整参数的方式,准确性分数仅在 30–40%之间。这很可能是由于我们的数据集的大小,它对于模型中的参数数量来说太小了。这可能是由于给定剪辑中的许多采样帧在逐个像素的基础上是相似的,并且被类似地评级,从而导致网络学习适应特定的视频。

作为应用深度学习的最后一次尝试,我们还研究了迁移学习,使用了在 ImageNet 上预先训练的 InceptionV3 网络。这产生了同样令人失望的结果。

最终结果

考虑到这个问题固有的主观性质——因为没有量化标准来认为一张照片比另一张照片“更好”——我们创建了一个受 A/B 测试启发的框架来评估我们的模型相对于基线的性能。该界面以随机顺序向用户呈现两张照片:一张由开发的模型生成,另一张由基线生成。不知道哪个是哪个的用户必须选择他们认为更好的图像。

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

图 7:我们的基本盲 A/B 测试框架的界面。

利用这一点,我们根据基线评估了初始和最终模型的性能,并发现了有希望的结果。

初始模型在 64%的时间里被选择在基线之上,而我们的最终模型在 75%的时间里被选择在基线之上,证明了显著的改进(注意,不同的测试集被用于评估两个模型)。如果我们认为照片质量与基线不可区分是成功的,那么我们最终模型的性能指标是甚至更好: 96%的时候,用户要么喜欢我们最终模型的框架,要么无法将其与基线区分开来。

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

图 8:逻辑回归模型性能的最终结果。

总结与未来工作

通过我们改进的逻辑回归模型,我们的项目证明是成功的,至少在 96%的视频上表现得和基线一样好。我们最终交付给合作伙伴的是一个 web 应用原型,它允许用户上传一只猫的视频,并随后显示我们的模型认为是最好的帧。

在我们项目的范围之外,如果有更多的时间,我们还想探索许多项目。首先,web 应用原型需要相当长的时间来运行,主要是由于逐帧的 YOLO 对象检测。对于较长的视频来说尤其如此,因此并行化这个过程会很有帮助。此外,测量我们的项目一旦被我们的合作伙伴采用后的下游影响将是令人满意的——也就是说,在使用我们的模型返回的照片的情况下,确定猫的收养率是否确实提高了。

最后,作为数据科学家,我们永远不会完全完成;我们总是有能力完善和改进我们的模型——无论这意味着找到可扩展的方法来收集更多数据,还是进行进一步的实验来调整参数。

就这样,我们留给你一张猫的照片。祝你在网上找到另一个。

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

personaltrainertoronto《Kitty》由 2.0 的 CC 授权

致谢

我们要感谢我们在奥斯汀宠物生活和收养的合作伙伴,以及我们的课程导师克里斯·坦纳,感谢他们在整个项目中的指导。最后,我们要感谢哈佛大学应用计算科学研究院给予我们的大力支持。

链接

github:https://github . com/iacs-capstone-2020-adoptimize/final _ project

参考文献

[1] P. Viola 和 M. Jones,“使用简单特征的增强级联的快速对象检测”,2001 年 IEEE 计算机学会关于计算机视觉和模式识别会议的会议录。CVPR 2001 年,第 1 卷,第 I-I 页,2001 年 12 月

[2] J. Redmon,S. Divvala,R. Girshick 和 a .法尔哈迪,“你只看一次:统一的实时对象检测”, 2016 年 IEEE 计算机视觉和模式识别会议(CVPR) ,内华达州拉斯韦加斯,2016 年,第 779–788 页

[3] W. Zhang,J. Sun,和 X. Tang,“猫头检测-如何有效地利用形状和纹理特征”,欧洲计算机视觉会议第 802–816 页,Springer,2008 年。

[4] N. D. Narvekar 和 L. J. Karam,“一种基于模糊检测累积概率的无参考图像模糊度量(CPBD),”IEEE 图像处理汇刊*,*,第 20 卷,第 9 期,第 2678-2683 页,2011 年 9 月,doi:10.1109/tip . 2011 . 26661 . 2020106

利用余弦相似度构建电影推荐系统

原文:https://towardsdatascience.com/using-cosine-similarity-to-build-a-movie-recommendation-system-ae7f20842599?source=collection_archive---------3-----------------------

使用余弦相似性构建基于 Python 的电影推荐系统的分步指南

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

图片由 Jade87 来自 Pixabay

你有没有想象过,在你已经喜欢的电影的基础上,你在高中学习的一个简单的公式会在推荐你一部电影的过程中发挥作用?

好了,现在我们使用余弦相似度(归一化向量的点积)来构建一个电影推荐系统

什么是推荐系统?

推荐系统是一类重要的机器学习算法,为用户提供“相关”建议。Youtube、亚马逊、网飞,都在推荐系统上发挥作用,系统根据你过去的活动(基于内容的过滤)或根据与你相似的其他用户的活动和偏好(协同过滤)向你推荐下一个视频或产品。同样,脸书也使用推荐系统来推荐你线下可能认识的脸书用户。

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

照片由格伦·凯莉Unsplash 上拍摄

推荐系统的工作基于内容或访问内容的用户之间的相似性。

有几种方法可以衡量两个项目之间的相似性。推荐系统使用这个相似矩阵向用户推荐下一个最相似的产品。

在本文中,我们将构建一个机器学习算法,根据用户喜欢的电影推荐电影。这个机器学习模型将基于余弦相似度

获取数据集

构建电影推荐系统的第一步是获取适当的数据。您可以从网上下载电影数据集,或者从下面的链接下载,该链接包含一个 22MB 的 CSV 文件,标题为“ movie_dataset.csv ”:

[## mahnoorjaved 98/电影推荐系统

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

github.com](https://github.com/MahnoorJaved98/Movie-Recommendation-System/blob/main/movie_dataset.csv)

现在让我们来探索数据集吧!

我们的 CSV 文件总共包含了 4802 部电影24 个栏目:索引、预算、流派、主页、id、关键词、原创 _ 语言、原创 _ 标题、概述、人气、制作 _ 公司、制作 _ 国家、发行 _ 日期、收入、运行时间、口语、状态、标语、标题、vote_average、vote_count、演员、剧组和导演(唉!).

在所有这些不同的特性中,我们感兴趣的是找出相似之处,以便提出下一个建议,这些特性如下:

关键词剧组流派 & 导演

喜欢恐怖电影的用户很可能会喜欢另一部恐怖电影。一些用户可能喜欢在电影的演员阵容中看到他们最喜欢的演员。其他人可能喜欢某个人导演的电影。结合所有这些方面,我们入围的 4 个特征足以训练我们的推荐算法。

开始编码

现在,让我们从编码开始。首先,让我们导入我们需要的库,以及电影数据集的 CSV 文件。

import pandas as pd
import numpy as np
from sklearn.feature_extraction.text import CountVectorizer
from sklearn.metrics.pairwise import cosine_similaritydf = pd.read_csv(r"...\movie_dataset.csv")

我们将导入两个重要的库用于数据分析和操作;熊猫numpy 。我们还将导入 Scikit-learn 的 **CountVectorizer,**用于将一组文本文档转换成一个术语/标记计数的向量。

最后,我们将从 sklearn 中导入余弦 _ 相似度作为我们相似度矩阵的度量(这将在后面详细讨论)。

我们将 CSV 文件读入数据帧 df ,然后可以在 Python IDE 的变量浏览器中访问它。

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

CSV 加载到数据帧中(图片由作者提供)

功能列表

我们将列出我们将使用的功能。如上所述,考虑到我们手头的问题,我们将只使用与我们最相关的特性。因此,我们选择的功能将是关键词演员流派 & 导演

此外,我们将做一些数据预处理,并用空格/空字符串替换任何具有 NaN 值的行,这样在运行代码时就不会产生错误。这个预处理已经在 for 循环中完成了。

features = ['keywords', 'cast', 'genres', 'director']for feature in features:
    df[feature] = df[feature].fillna('')

将相关特征组合成单个特征

接下来,我们将定义一个名为 combined_features 的函数。该函数将把我们所有有用的特征(关键字、演员、流派&导演)从它们各自的行中组合起来,并返回一个包含所有组合特征的行。

def combined_features(row):
    return row['keywords']+" "+row['cast']+" "+row['genres']+" "+row['director']df["combined_features"] = df.apply(combined_features, axis =1)

我们将添加一个新列, combined_features 到我们现有的 dataframe (df)中,并将上述函数应用于每一行(轴= 1)。现在,数据帧的末尾将有一个额外的列,由多行组合特征组成。

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

我们的数据框架中的组合特征栏(图片由作者提供)

提取特征

接下来,我们将从数据中提取特征。

sklearn.feature_extraction 模块可用于从由文本和图像等格式组成的数据集中提取机器学习算法支持的格式的要素。我们将使用 CountVectorizer 的 fit . transform来计算文本的数量,并将转换后的矩阵 count_matrix 打印成一个数组,以便更好地理解。

cv = CountVectorizer()
count_matrix = cv.fit_transform(df["combined_features"])
print("Count Matrix:", count_matrix.toarray())

使用余弦相似度

我们将使用来自 Sklearn 的余弦相似度作为度量来计算两部电影之间的相似度。

余弦相似性是一种用于衡量两个项目相似程度的度量。在数学上,它测量的是在多维空间中投影的两个向量之间的角度余弦。输出值范围从0–1

0 表示没有相似性,其中 as 1 表示两个项目 100%相似。

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

余弦相似度(图片由作者提供)

python 余弦相似度或余弦内核将相似度计算为输入样本 X 和 y 的归一化点积。我们将使用 sk learnCosine _ Similarity来查找计数矩阵中两个向量的 cos θ 。

cosine_sim = cosine_similarity(count_matrix)

cosine_sim 矩阵是一个 numpy 数组,用于计算每部电影之间的余弦相似度。下图可以看到,电影 0 与电影 0 的余弦相似度为 1;它们 100%相似(理应如此)。

类似地,电影 0 和电影 1 之间的余弦相似度是 0.105409(电影 1 和电影 0 之间的分数相同——顺序无关紧要)。

电影 0 和 4 比电影 0 和 3 更相似(相似性分数为 0.23094)(分数= 0.0377426)。

带有 1 的对角线表示情况是什么,每部电影“x”都与自己 100%相似!

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

余弦相似矩阵(图片由作者提供)

用户喜欢的内容

下一步是在 movie_user_likes 变量中输入用户喜欢的电影。

由于我们正在建立一个基于内容的过滤系统,我们需要知道用户的喜欢,以便预测类似的项目。

movie_user_likes = "Dead Poets Society"def get_index_from_title(title):
    return df[df.title == title]["index"].values[0]movie_index = get_index_from_title(movie_user_likes)

假设我喜欢电影《死亡诗社》。接下来,我将构建一个函数来从这部电影的名称中获取索引。该索引将保存在 movie_index 变量中。

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

用户喜欢的电影的电影索引变量(作者图片)

生成相似电影矩阵

接下来,我们将生成一个类似电影的列表。我们将使用我们给出的电影的电影 _ 索引作为输入电影 _ 用户 _ 喜欢。enumerate()方法将向可迭代列表余弦 _sim 添加一个计数器,并以列表相似 _ 电影的形式返回它,其中包含每个索引的相似性得分。

similar_movies = list(enumerate(cosine_sim[movie_index]))

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

相似电影列表(作者图片)

按降序对相似电影列表进行排序

下一步是对列表中的电影进行排序相似 _ 电影。我们使用了参数 reverse=True,因为我们希望列表按降序排列,最相似的条目在顶部。

sorted_similar_movies = sorted(similar_movies, key=lambda x:x[1], reverse=True)

sorted_similar_movies 将是按照与输入电影 movie_user_likes 的相似性分数降序排序的所有电影的列表。

从下图可以看出,相似度得分为 0.9999999999999999993 的最相似的一个在最上面,其索引号为 2453(电影是我们作为输入给出的’死亡诗社’,有道理吧?).

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

相似分数排序的相似电影列表(图片由作者提供)

印刷相似的电影

现在,项目的最后一部分来了,打印电影的名字,类似于我们通过 movie_user_likes 变量给系统输入的名字。

sorted_similar_movies 列表所示,电影按其索引号排序。打印索引号对我们来说没有用,所以我们将定义一个简单的函数,将索引号转换成电影标题,就像在 dataframe 中一样。

索引号→电影名称

接下来我们将在 for 循环中调用这个函数来打印来自 sorted_similar_movies 的第一个“x”个电影。

在我们的例子中,我们将打印 4802 部电影中最相似的 15 部电影。

def get_title_from_index(index):
    return df[df.index == index]["title"].values[0]i=0
for movie in sorted_similar_movies:
    print(get_title_from_index(movie[0]))
    i=i+1
    if i>15:
        break

运行整个代码

现在应用程序来了。使用上述步骤编写您自己的推荐系统,并通过将您喜欢的电影交给 movie_user_likes 来运行代码。

我给了“死亡诗社”,它给我打印了以下类似的电影:

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

IPython 控制台(图片由作者提供)

可以看出,最相似的显然是电影本身。算法定义“无事生非”为下一部最相似的电影!(将它添加到我的“观察列表”😄)

本文到此为止!本文提供了一种实践方法,通过在任何 python IDE 上编写代码,从头开始构建推荐系统。

现在,一旦建立了算法,是时候拿些爆米花,看你的系统推荐的电影了!!😁

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

爆米花时间!(照片由 Unsplash 上的乔治亚·瓦吉姆拍摄)

如何在 SQL 中使用 cte

原文:https://towardsdatascience.com/using-ctes-to-improve-sql-queries-dfcb04b7edf0?source=collection_archive---------6-----------------------

启动和运行 cte 的介绍

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

照片来自 PexelsTim Gouw

如果您曾经使用过 SQL,您就会知道当您创建一个对以后使用有帮助的编写良好的查询时,它是多么重要。通常,当您查询某个东西时,您只使用它一次,但是有时您需要引用旧的查询——这就是**通用表表达式(cte)**的用武之地!

一个 CTE 允许你定义一个临时命名的结果集,在一个语句的执行范围内临时可用,比如[*SELECT*](https://www.sqlservertutorial.net/sql-server-basics/sql-server-select/)[*INSERT*](https://www.sqlservertutorial.net/sql-server-basics/sql-server-insert/)[*UPDATE*](https://www.sqlservertutorial.net/sql-server-basics/sql-server-update/)[*DELETE*](https://www.sqlservertutorial.net/sql-server-basics/sql-server-delete/)或者[*MERGE*](https://www.sqlservertutorial.net/sql-server-basics/sql-server-merge/)

对于本教程,我将假设您具有初级—中级 SQL 经验。如果你没有,这里有一些很好的资源可以帮助你开始。

入门指南

我将从 Datacamp 的 SQL 课程中使用一个电影数据库。(如果你是一名拥有***【edu】***电子邮件的学生,并且想要获得三个月的免费 Datacamp 访问— GitHub 学生开发者包)。

电影数据库由以下表格(+列)组成:

  • 电影——(id、片名、上映年份、国家/地区、时长、语言、认证、总票房、&预算)
  • ——(身份证,姓名,出生日期,&死亡日期)
  • 评论——(电影 id,用户数量,评论数量,imdb 评分,投票数量,& facebook 点赞)
  • 角色——(id,film_id,person_id,角色)

我们可以看到表格和列。可以用几把钥匙把桌子连接起来。下面是每个表的快照。

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

影片表( 【数据营】 )

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

人表(data camp)

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

评论表(data camp)

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

角色表(data camp)

CTEs 简介

如前所述,cte 在语句的执行范围内暂时可用。

这是 CTE 的基本布局。

**WITH** CTE_NAME(column_1, column_2, column_3)
**AS**
    (--***normal SQl query***
       SELECT *
       FROM table)

在上面的代码中,CTE 必须以带有的开始。这告诉您的查询这是一个 CTE 语句。接下来, CTE_NAME 是你想给它取的名字。你可以给它起任何名字,但是由于这通常是用于以后的参考,所以它应该是有意义的。column_1、column_2、& column_3 是您希望为列设置别名的名称。最后,作为,是 SQL 语句的开始,我将其标记为# 普通 SQL 查询作为注释参考。

让我们看看它的实际效果吧!

我们的目标是编写几个可以构建到 CTE 中的查询。我们试图找到以下内容,

2014 年 IMDB 收视率最高的电影,演员的名字以“Matt”开头

我们可以立即看到查询可以分解成几个部分。

  1. Names = 'Matt ’
  2. 电影和 IMDB 评级
  3. 2014 年
  4. 第三高的 IMDB

对于这个例子,我们将创建两个 cte。

第一个查询 Matt 这个名字

我们将首先编写一个没有 CTE 语法的查询来调用整个数据库中名字中包含“Matt”的所有人。我们还想加入 roles 表,以找出该演员所在的确切的 film_id

SELECT p.name, r.film_id
FROM people AS p
JOIN roles AS r
ON p.id = r.person_id
WHERE p.name LIKE 'Matt%'

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

所有“马特”名字的输出和他们出演的电影

现在我们有了一个调用所有 Matt 的查询,我们可以在以后构建 CTE 时使用它。

第二个问题—2014 年电影和 IMDB 评级

我们希望构建下一个查询来查找 2014 年的每部电影,并加入评论表来查找它们的 IMDB 评级。

SELECT f.id, f.title, f.release_year, r.imdb_score
FROM films AS f
JOIN reviews AS r
ON f.id = r.film_id
WHERE f.release_year = 2014

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

查询输出

现在,我们已经从两个查询中获得了所有必要的信息,可以找到答案,找出由名为“Matt”的演员主演的电影在 IMDB 中的评分最高

建造 CTE

让我们结合前面的两个查询来创建这个 CTE。当我们想在同一个语句中添加第二个 CTE 时,我们添加一个逗号并添加第二个 AS 语句,如下所示。

**WITH**
    **MATT_cte**(actor, ***film_id***)
        **AS**(
            SELECT p.name, r.film_id
            FROM people AS p
            JOIN roles AS r
                ON p.id = r.person_id
            WHERE p.name LIKE 'Matt%')**,****IMDB_cte**(***film_id***, movie, release_year, IMDB_Score)
        **AS**(
            SELECT f.id, f.title, f.release_year, r.imdb_score
            FROM films AS f
            JOIN reviews AS r
                ON f.id = r.film_id
            WHERE f.release_year = 2014)

我们的 CTE 已经构建并保存了别名为 MATT_cteIMDB_cte 的两个查询。这些别名将在我们运行最终代码时作为参考。

需要注意的是,如果您计划连接稍后创建的两个 cte,您将需要在两个 cte 中都有一个外键。在这种情况下,我们的外键在两个表中都是 film_id

使用 cte

我们都准备使用我们的 cte 并得到我们的答案!让我们加入我们新的临时结果,并找出它是哪部电影。提醒,我们正试图得到以下。CTE 将需要在同一个查询中被引用,所以我们在它下面添加了最后一个查询。

2014 年 IMDB 收视率最高的电影,演员的名字以“Matt”开头

WITH
    MATT_cte(actor, film_id)
        AS(
            SELECT p.name, r.film_id
            FROM people AS p
            JOIN roles AS r
                ON p.id = r.person_id
            WHERE p.name LIKE 'Matt%'),IMDB_cte(film_id, movie, release_year, IMDB_Score)
        AS(
            SELECT f.id, f.title, f.release_year, r.imdb_score
            FROM films AS f
            JOIN reviews AS r
                ON f.id = r.film_id
            WHERE f.release_year = 2014)**SELECT *
FROM MATT_cte AS m
JOIN IMDB_cte AS i
    ON m.film_id = i.film_id
ORDER BY i.IMDB_Score DESC**

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

查询输出

我们已经成功地找到了所有包含“马特”的名字,电影名称,IMDB 评分和上映年份。

我们最终的答案是,星际是 2014 年 IMDB 中收视率最高的电影,它有一个名字以“马特”开头的演员。

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

桌面壁纸由刺客大师

最终结果

我希望您看到 cte 为您的 SQL 查询带来的价值。如果您计划一次又一次地调用同一个查询,最好使用它。例如,如果我们只想要 IMDB 评分高于 8 的电影,我们可以创建一个临时结果来确保 CTE 始终可用。

SQL 中使用的另一个有用的方法是子查询。我计划分享一个关于如何使用子查询的教程,所以一定要跟着我学习更多的课程!

LinkedinGithub 上与我联系

在 Salesforce 组织中使用自定义 JavaScript 模块作为静态资源

原文:https://towardsdatascience.com/using-custom-javascript-modules-as-static-resources-in-salesforce-orgs-4b6df1477dc4?source=collection_archive---------13-----------------------

上传一次 JavaScript 模块,并在任何 Lightning Web 组件中使用它

本教程将引导您将自定义 JavaScript 模块与 Salesforce Lightning Web 组件集成,并在 Salesforce 组织中使用它们。

Salesforce Developer 101:术语和基本概念

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

某机构截图(作者:Lynn Zheng)

什么是 Salesforce 组织?

Salesforce 组织是一个面向客户的平台,包含 Salesforce 数据。

什么是闪电网页组件(LWC)?

lightning web 组件本质上是一个 Web 组件,它使用 Salesforce 品牌的样式并封装 Salesforce 数据。

开发环境和工具包

您将需要什么:

  • 一个 Trailhead 账户,允许你创建一个 Trailhead 游乐场/测试组织
  • Salesforce 开发人员体验(SFDX)命令行客户端
  • Visual Studio 代码

如果您尚未设置完整的 Salesforce 开发人员体验(SFDX)环境,请转到 Trailhead,注册并完成快速入门:Lightning Web Components Trail。这将建立我们在本教程中需要的整个工具包,应该不会超过 20 分钟。

如果你是 Visualforce 或 Aura 开发人员,已经建立了 SFDX 但不熟悉 LWC,我建议你完成快速入门的最后一个模块: LWC 小径来创建一个 HelloWorld LWC。

如果您已经熟悉 SFDX 和 LWC,请继续创建一个简单的 HelloWorld LWC。LWC 不需要超过<div>Hello World</div>的任何东西。

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

自定义 HelloWorld LWC 位于左侧面板的顶部

无论您来自何方,您最终都会看到一个 org 页面,其中包含一个带有文件**helloWorld.html、helloWorld.js、**和 helloWorld.js-meta.xml 的定制 LWC。

使用lightning-button标签向自定义 LWC 添加一个按钮。它的label属性包含它显示的文本。它的onclick属性应该指向一个名为clickHandler的事件处理程序。

helloWorld.html中,为按钮添加 HTML:

<lightning-button label=”Say Hello in the JS Console” onclick={clickHandler}></lightning-button>

helloWorld.js 中,实现clickHandler方法:

clickHandler() {
    console.log('hello from helloWorld.js');
}

打开默认组织页面(在 Visual Studio 代码中,Command + Shift + P,SFDX: Open Default Org),打开 web 控制台,点击按钮,我们应该会看到控制台日志。

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

如果组织没有自动刷新,点击页面右上角的齿轮图标,进入页面设置,刷新当前页面,保存。

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

创建静态资源 JavaScript 模块

force-app/main/default/static resources 下创建两个文件,helloModule.js、hello module . resource-meta . XML .

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

要使用自定义 JavaScript 文件作为静态资源,我们需要将它包装在一个立即调用的函数表达式(IIFE)中。

helloModule.js 中的生活:

(function() {
    function sayHello() {
        console.log('hello from helloModule.js');
    }
    // this makes the sayHello function available in the window     namespace
    // so we can call window.sayHello from any LWC JS file
    window.sayHello = sayHello;
})();

hello module . resource-meta . XML中,我们指定了资源内容类型和缓存控制属性:

<?xml version="1.0" encoding="UTF-8"?>
<StaticResource ae na" href="http://soap.sforce.com/2006/04/metadata" rel="noopener ugc nofollow" target="_blank">http://soap.sforce.com/2006/04/metadata" fqn="helloWorld">
  <cacheControl>Private</cacheControl>
  <contentType>application/javascript</contentType>
</StaticResource>

force-app/main/default/static resources部署到 org。在组织页面上,转到设置并搜索**静态资源。**应该有一个名为 helloModule 的资源,类型 application/javascript,私有缓存控制。

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

在 LWC 中使用静态资源

helloWorld.js 中,添加静态资源和加载静态资源的方法的导入:

import SAY_HELLO from '[@salesforce/resourceUrl](http://twitter.com/salesforce/resourceUrl)/helloModule';
import { loadScript } from 'lightning/platformResourceLoader';

然后实现一个在页面上呈现 LWC 时运行的回调:

renderedCallback() {
    loadScript(this, SAY_HELLO)
    .then(() => console.log('Loaded sayHello'))
    .catch(error => console.log(error));
}

将此 LWC 部署到组织。刷新 org 页面,我们应该会看到控制台日志,表明已经成功加载了 helloModule 静态资源。

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

既然资源已经成功加载,我们就能够在 LWC 中使用函数sayHello。在 helloWorld.js 中,更新按钮点击处理程序调用window.sayHello:

clickHandler() {
    console.log('hello from helloWorld.js');
    window.sayHello();
}

部署到 org,刷新,点击按钮,我们应该看到来自 helloModule 的日志打印到控制台。

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

奖励:创建一个静态资源 JavaScript 模块,调用第三方 API

由于 Salesforce org 的默认安全设置,如果我们的 JavaScript 函数需要调用非 Salesforce 的第三方 API,我们需要将这些站点列为 CSP(内容共享策略)可信站点。我们将以 JSONPlaceholder 为例。转到设置并搜索 CSP 可信站点。添加新的可信站点,如下所示:(注意,Salesforce 不接受以正斜杠结尾的 URL,即https://mysite.com有效,但https://mysite.com/无效。)

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

CSP 可能需要两到五分钟才能生效。同时,向 helloModule.js 添加一个函数(仍在 IFFE 中),并将其公开给 window 名称空间:

function retrieveData(dataId) {
    fetch('[https://jsonplaceholder.typicode.com/todos/'](https://jsonplaceholder.typicode.com/todos/') + dataId)
    .then(response => response.json())
    .then(json => console.log(json));
}
window.retrieveData = retrieveData;

更新 helloWorld.js 中的clickHandler以使用带参数的window.retrieveData:

clickHandler() {
    console.log('hello from helloWorld.js');
    window.sayHello();
    for (let i = 1; i < 4; i++) {
        window.retrieveData(i);
    }
}

部署、刷新,如果没有 CSP 错误(这意味着 CSP 尚未生效),我们应该能够在控制台日志中看到一些从第三方 API 获取的 JSON 数据。

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

完整的文件如下:

helloWorld.html:

<template>
  <lightning-card title="HelloWorld" icon-name="custom:custom14">
    <div class="slds-m-around_medium">
      <p>Hello, {greeting}!</p>
      <lightning-input label="Name" value={greeting} onchange={changeHandler}></lightning-input>
      <lightning-button label="Say Hello in the JS Console" onclick={clickHandler}></lightning-button>
    </div>
  </lightning-card>
</template>

helloWorld.js:

import { LightningElement } from 'lwc';
import SAY_HELLO from '[@salesforce/resourceUrl](http://twitter.com/salesforce/resourceUrl)/helloModule';
import { loadScript } from 'lightning/platformResourceLoader';export default class HelloWorld extends LightningElement {
    greeting = 'World';
    changeHandler(event) {
        this.greeting = event.target.value;
    }
    clickHandler() {
        console.log('hello from helloWorld.js');
        window.sayHello();
        for (let i = 1; i < 4; i++) {
            window.retrieveData(i);
        }
    }
    renderedCallback() {
        loadScript(this, SAY_HELLO)
        .then(() => console.log('Loaded sayHello'))
        .catch(error => console.log(error));
    }
}

helloModule.js:

(function() {
    function sayHello() {
        console.log('hello from helloModule.js');
    }
    function retrieveData(dataId) {
        fetch('[https://jsonplaceholder.typicode.com/todos/'](https://jsonplaceholder.typicode.com/todos/') + dataId)
        .then(response => response.json())
        .then(json => console.log(json));
    }
    // this makes the sayHello function available in the window namespace
    // so we can call window.sayHello from any LWC JS file
    window.sayHello = sayHello;
    window.retrieveData = retrieveData;
})();

更多资源,请查看 TrailheadSalesforce 面向开发者的官方文档

在家工作时使用计算机视觉和机器学习来监控活动

原文:https://towardsdatascience.com/using-cv-and-ml-to-monitor-activity-while-working-from-home-f59e5302fe67?source=collection_archive---------23-----------------------

介绍在嵌入式系统上构建基于视觉的健康监测软件

世界上大多数人在这种封锁期间呆在家里面临的最大挑战之一是身体活动的突然限制,尤其是当被限制在一个小空间内时。为了帮助解决这个问题,我想看看我是否可以使用计算机视觉来帮助激励自己更加积极,这将是一件有趣的事情。

在这篇文章中,我将分享我的方法,以及一个完整的示例源代码,它展示了帮助监控和改善许多呆在家里的人的生活质量的潜力。这个想法是拥有一个低成本、支持 GPU 的基于视觉的系统(99 美元 NVIDIA Jetson Nano ),该系统可以在 edge 上执行大多数计算机视觉处理(例如,人脸检测、情感分类、人物检测和姿势估计)一体化易于安装的软件包。然后,该系统可以处理从本地收集到远程云的数据,以便进行后处理,并重新分发到为一个或多个健康状况提供仪表板的服务器。

为了解决隐私问题,我讨论了通过仅上传经过后处理的数据来保护用户隐私的方法,这些数据包含分类的情感数据、面部特征和身体姿势数据,而不是实际的相机馈送。虽然您还可以做更多的事情来隐藏数据,但是这个例子将为您指出正确的方向。

这个概念验证是一个简单的应用程序,它根据我的身体姿势和情绪显示我的活动仪表板。整个系统运行在 99 美元的 NVIDIA Jetson Nano 开发板和大约 40 美元的网络摄像头上。这种设置有助于人们在跟踪活动的同时专注于工作。

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

开启隐私模式的最终结果,因此仅显示已处理的数据。

让我们来看看如何构建和我一样的系统。

设置网络摄像机服务器

我采取的第一个设置是建立一个网络摄像头服务器,这样我就可以根据需要向系统添加尽可能多的摄像头,并且还可以以分布式和可扩展的方式处理这些帧。

在 Linux 中,我们可以通过一个名为“ Motion ”的程序轻松设置自动网络摄像头流。该计划允许您创建一个智能安全摄像机,检测运动,更重要的是作为服务器传输运动 JPEG (MJPEG)。这个特性非常有用,它允许我们异步运行这些应用程序,而不会使任何特定机器上的每个相机流陷入困境。

要在 NVIDIA Jetson Nano 上安装 motion,只需使用以下命令行。

sudo apt-get install motion

然后,我们编辑运动配置文件来设置分辨率和帧率。默认情况下,motion 应用程序被配置为检测运动和捕捉帧,这不是我们在用例中想要的。

nano /etc/motion/motion.conf

这里,您需要将摄像机馈送(帧速率)和流馈送( stream_maxrate )的分辨率和帧速率更改为 640 x 32015 fps。

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

如果你有一个更强大的机器,如 Jetson Xavier ,你可以设置更高的分辨率和帧速率,以便更好地跟踪远距离物体。

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

然后,我们关闭自动快照和视频捕捉功能(即,将输出 _ 图片设置为关闭)。这一点很重要,否则你的硬盘会很快被新捕获的内容填满。

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

配置 Motion 后,您需要允许它在启动时作为服务运行。

nano /etc/defaultmotion

您将这一行编辑为

start_motion_daemon=yes

现在你需要编辑位于**/etc/Motion/Motion . conf**的主配置文件,并将 Motion 守护进程设置为 on。重启电脑后,Motion 现在应该会自动运行。

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

以下命令控制 Motion 服务:

  • 启动 Motion 服务:
sudo service motion start
  • 停止运动服务:
sudo service motion stop
  • 重新启动 Motion 服务:
sudo service motion restart

要预览订阅源,只需打开浏览器并转到以下链接:

[http://localhost:8080](http://localhost:8080)

此时,您将能够看到摄像机馈送的预览。现在是我们真正使用它的时候了。

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

用于姿态估计的机器学习工具

该系统的主要目标之一是更好地了解一个人在家的活动。为了做到这一点,我们将创建一个姿势估计程序,跟踪一个人的姿势。更重要的是,姿势估计允许你通过省略你的相机的馈送来创建一个隐私层,并且只显示处理过的数据。

首先,您需要安装 Tensorflow 和 OpenPose 库。GitHub 成员 karaage0703 在为 NVIDIA Jetson Nano 整合安装脚本方面做了一项令人惊叹的工作。在这里你可以按照 GitHub 的说明来设置工具,下面是 Github 库的链接。

github clone [https://github.com/karaage0703/jetson-nano-tools](https://github.com/karaage0703/jetson-nano-tools)

特别是,您想要运行**‘install-tensorflow . sh’‘install-Pose-Estimation . sh’**脚本来在您的机器上安装 tensor flow 和 Pose Estimation 库。

$ cd ~/jetson-nano-tools
$ ./install-tensorflow.sh
$ ./install-pose-estimation.sh

这个过程可能需要 30 分钟,所以在执行完命令后休息一下,做做伸展运动。一旦您有了合适的工具,让我们看看我写的 Python 脚本,它允许您捕获、处理和可视化数据。

姿态估计

我创建了一个 Python 脚本,它使用了刚刚安装的工具集,这是骨架覆盖在摄像机镜头上的结果。

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

在 NVIDIA Jetson Nano 上运行的 OpenPose 库的输出示例

你可以在这里查看我写的脚本:

git clone [https://github.com/raymondlo84/nvidia-jetson-ai-monitor](https://github.com/raymondlo84/nvidia-jetson-ai-monitor)

**‘run _ web cam _ IP . py’**脚本有两个基于 CMU 感知计算实验室 OpenPose 项目的关键功能。

OpenPose 代表了第一个在单幅图像上联合检测人体、手、面部和脚关键点(总共 135 个关键点)的实时多人系统

这个库是一个非常强大的工具,它可以通过 GPU 加速检测完整的人体骨骼。有了这些,现在想象你如何跟踪人体姿势,为你提供适当的反馈。例如,您可以创建一个颈部角度检测器,并在工作期间帮助固定您的身体姿势。

from tf_pose.estimator import TfPoseEstimator
from tf_pose.networks import get_graph_path, model_wh

脚本的主循环对 IP 摄像机捕获的每一帧进行推断。然后,这两行将执行推理并在框架上绘制结果

humans = e.inference(image, resize_to_default=(w > 0 and h > 0), upsample_size=args.resize_out_ratio)image = TfPoseEstimator.draw_humans(image, humans, imgcopy=False)

NVIDIA Jetson 的 GPU 可以在 320 x 160 分辨率下,使用 mobilenet_thin 模型,以大约 7–8 fps 的速度执行姿态估计。对于一台满负荷使用不超过 10W 功率的机器来说,这是非常令人印象深刻的。随着身体姿态的估计,现在可以预测一个人有多活跃。这类似于一个 FitBit,但我们使用的不是加速度计或陀螺仪,而是来自摄像机的视频信号。

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

在 NVIDIA Jetson Nano 上运行 OpenPose

人脸检测和情感分析

此时,我们计划要做的一切都已经实现了,但是为什么不利用捕获的视频做更多的事情呢?让我们看看如何从面部反应中检测情绪,以便用 GPU 构建一个快乐的仪表。要做到这一点,我们需要添加代码来决定一个人的面部和情绪。令人惊讶的是,这两个系统都在 GPU 上利用 Tensorflow,因此我们不需要分配大量的内存,只需要最少的开销。同样,还有额外的优化,如智能地选择基于身体姿态的人脸检测的边界框,以获得额外的速度。

目前,处理速度约为每秒 7 帧,面部检测和情感分析的分辨率为 320x160 。然而,使用分布式设置,我可以通过将工作卸载到 GPU 集群设置中的另一个节点上,轻松地将速率提高一倍或两倍。即使每秒更新 3 次,系统每天也将有大约 259,200 个样本。这不是一个微不足道的数据量。

为了执行情感分析,这次我将包括 Keras 库和人脸识别包。

import face_recognition
import keras
from keras.models import load_model
from keras.preprocessing.image import img_to_array

然后,这条线从图像中提取检测到的面部的位置。

face_locations = face_recognition.face_locations(small_image, model=’cnn’)

参数 mode=‘cnn’ 使我们能够使用 CUDA GPU 加速代码,并在不同视角或遮挡情况下提供更好的准确性。

一旦它从图像中检测到人脸,人脸图像就会根据我们预先训练的模型(_ mini _ xception . 106–0.65 . HD F5)运行预测函数,并将人脸分为七类:愤怒、厌恶、害怕、快乐、悲伤、惊讶或中性。

emotion_dict = [“Angry”, “Disgust”, “Scared”, “Happy”, “Sad”, “Surprised”, “Neutral”]model = load_model(“emotion_detector_models/_mini_XCEPTION.106–0.65.hdf5”, compile=False)model_result = model.predict(face_image)

基本上,实际工作大约是 5 行代码,我还提供了额外的示例代码(web cam _ face _ detect . py)来单独运行这一部分进行您自己的测试。

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

在 GPU 上运行姿势估计、人脸检测和情感分析。

CPU/GPU 性能

从我的实验中得到的一个重要收获是,这些 CV+ML 算法可以与 GPU 设置很好地堆叠在一起。默认情况下,Tensorflow 的核心占用了大量内存,因此无论如何最好地利用资源是明智的。现在,随着 CPU 完全从处理中释放出来,我们有超过 300% 的 CPU 处理资源可用于许多其他任务,例如对已处理数据进行记账。

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

请注意,即使同时运行所有算法,我们的 CPU 使用率也非常低。

最后的想法

这里我们有一个低成本的监控系统,可以监控身体姿势,进行面部检测,并返回基本的情绪分析。这三个特性代表了一个全新的基于视觉的健康仪表板的基础,全部基于 GPU。最棒的是,这可以用一台 99 美元的机器来建造。想象一下可能性!

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

启用隐私模式

通过姿势估计、面部检测和情绪分析,我可以提取运动参数,如每天的平均运动,并跟踪我自己的情绪数据,而无需将我的图像与云共享。这个数据并不完美,但这种反馈可以被证明对改善长时间坐在电脑前的姿势非常有用(例如,颈部问题)。此外,通过一些小的修改,这可以很快变成一个睡眠监视器,并有其他潜在的健身用途可以扩展。

关于我

目前,我住在硅谷,并在英特尔担任 OpenVINO Edge 人工智能软件布道者。此前,我是 Meta 的联合创始人兼首席技术官,在那里我发布了两套增强现实开发工具包。作为 Meta 公司研发部门的负责人,我曾与数百名出色的工程师一起工作,创造出最先进的技术和 CV 算法,如 SLAM、3D 手部跟踪和 AR/XR 的新 UX。在我的博士研究期间,我还发表和开发了关于 HDR 视频处理的实时 GPGPU 应用程序,并用 3D 深度感应相机创建了增强现实原型。我始终相信,技术的可及性和简单性是创造颠覆性变革所必需的。现在,我看到人工智能边缘计算的快速增长可以以非常低廉的成本带来许多智能应用。而这篇帖子只是这种不断增长的趋势的开始。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值