如何在一天内构建和部署一个机器学习 web 应用程序
构建 durian 分类器的一步一步的端到端指南(包括代码)
因此,我决定构建一个 web 应用程序来对榴莲进行分类,因为嘿,为什么不呢?点击查看。
网络应用演示 (GIF 由作者创建)
对于我所有的国际读者来说,如果你不知道榴莲是什么,它是一种水果(在新加坡我们称它为水果之王),它有着奶油般的质地,刺鼻的气味(见下图)和尖尖的外表。这种说的刺鼻的气味,让人要么讨厌,要么绝对喜欢(我属于后一类,明显是)。如果你觉得它闻起来不错,那么它的味道可能会更好。
榴莲长什么样(照片由 Jim Teo 在 Unsplash 上拍摄)
问题陈述
是的,这个项目的动力源于我对榴莲的热爱。你一定想知道,我们到底在分类什么?
你看,榴莲有很多种,它们的味道、质地和颜色都不一样。对于这个项目,我们将对四种不同类型的榴莲进行分类,即:
- 猫山王
- 金凤(金凤凰)
- D24
- 红对虾
这些榴莲的差异可以总结在下表中:
来源:https://www . you . co/SG/blog/types-of-durians-how-to-pick-the-best-durian/
那里有更多种类的榴莲(在这个链接中找到更多)但是我认为这些榴莲的细微差别可能已经证明我们的模型很难学习。
数据收集
每个项目都从数据收集开始。由于我们将部署的模型将用于个人和教育目的,我们将从 Google Images 获取图像。如果您将图像用于其他目的,请检查版权。
我们将使用这个 API 来获取我们的图像。只需按照回购上的说明安装软件包。在说明的步骤 3 中,我们将针对我们的特定用例运行该命令(替换到 chromedriver 的路径):
python3 bing_scraper.py --url 'https://www.bing.com/images/search?q=mao+shan+wang' --limit 100 --download --chromedriver <path_to_chromedriver>
在这里,我们将下载的图片数量限制为 100 张,因为没有多少特定的“茅山王”图片。我们重复上述步骤三次以上,用其他品种的榴莲代替搜索。请注意,由于我们正在 API 中修改搜索 URL,查询中的空格将被替换为“+”(即mao+shan+wang
、red+prawn+durian
等)。
当然,您可以对任何想要分类的图像执行此步骤。
数据清理
在我们的使用案例中,由于公开可用的榴莲图像不多,许多下载的图像可能与正确的榴莲品种不对应(例如,在搜索“猫山王”时,您可能会找到一种普通的“未标记”榴莲)。因此,我需要手动检查所有下载的图像,以确保颜色和背景是正确的。毕竟,拥有高质量(即标签正确)的数据胜过数量,对吗?
这一步确实需要一些领域知识,可能会稍微费时。(但当然,数据清理是机器学习管道中的一个基本步骤,反映了数据科学家和人工智能工程师的现实。)
清理完数据后,我们剩下的是 55 D24、39 金凤、 59 毛山王和 68 红对虾的图像。
训练我们的榴莲分类器
我选择了使用 TensorFlow 框架,我相信大多数从业者都已经习惯了(当然,可以随意使用 Pytorch )。由于我们只有很少的图像,我们无疑必须使用预训练的模型,并在我们的数据集上对其进行微调。
首先,确保你有下面的文件夹结构,这是稍后flow_from_directory
所需要的。
train
|-- d24
|-- golden-phoenix
|-- mao-shan-wang
|-- red-prawn
valid
|-- d24
|-- golden-phoenix
|-- mao-shan-wang
|-- red-prawn
让我们开始构建我们的分类器!
# Import relevant libraries we will be using
import numpy as npfrom tensorflow.keras.initializers import glorot_uniform
from tensorflow.keras.regularizers import l2
from tensorflow.keras.preprocessing.image import ImageDataGenerator
from tensorflow.keras.applications import Xception
from tensorflow.keras.layers import (
Flatten,
Dense,
AveragePooling2D,
Dropout
)
from tensorflow.keras.optimizers import SGD
from tensorflow.keras.preprocessing import image
from tensorflow.keras import Model
from tensorflow.keras.preprocessing.image import img_to_array
from tensorflow.keras.callbacks import (
EarlyStopping,
ModelCheckpoint,
LearningRateScheduler
)
如上所述,我们将使用的基本模型是异常。让我们实例化这一点,并添加一些密集层。我们将使用较小的批量 8,因为我们有许多图像。我们还需要警惕过度适应我们的小数据集。
SHAPE = 224
BATCH_SIZE = 8model = Xception(
input_shape=(SHAPE, SHAPE, 3),
include_top=False,
weights='imagenet'
)x = model.output
x = AveragePooling2D(pool_size=(2, 2))(x)
x = Dense(32, activation='relu')(x)
x = Dropout(0.1)(x)
x = Flatten()(x)
x = Dense(4, activation='softmax',
kernel_regularizer=l2(.0005))(x)model = Model(inputs=model.inputs, outputs=x)opt = SGD(lr=0.0001, momentum=.9)
model.compile(loss='categorical_crossentropy',
optimizer=opt,
metrics=['accuracy'])
之后,让我们使用 TensorFlow 的ImageDataGenerator
和flow_from_directory
创建我们的图像生成器对象。由于我们没有足够的训练图像,图像增强比以往任何时候都更加重要。
train_datagen = ImageDataGenerator(
rescale=1./255,
rotation_range=15,
width_shift_range=0.1,
height_shift_range=0.1,
horizontal_flip=True
)valid_datagen = ImageDataGenerator(
rescale=1./255,
rotation_range=0,
width_shift_range=0.0,
height_shift_range=0.0,
horizontal_flip=False
)train_generator = train_datagen.flow_from_directory(
'train/',
target_size=(SHAPE, SHAPE),
shuffle=True,
batch_size=BATCH_SIZE,
class_mode='categorical',
)valid_generator = valid_datagen.flow_from_directory(
'valid/',
target_size=(SHAPE, SHAPE),
shuffle=True,
batch_size=BATCH_SIZE,
class_mode='categorical',
)>>> Found 178 images belonging to 4 classes.
>>> Found 42 images belonging to 4 classes.
在我们的模型之前,让我们定义一些回调函数。
earlystop = EarlyStopping(monitor='val_loss',
patience=4,
verbose=1)checkpoint = ModelCheckpoint(
"model-weights/xception_checkpoint.h5",
monitor="val_loss",
mode="min",
save_best_only=True,
verbose=1
)
最后,让我们开始训练我们的模型!
history = model.fit_generator(
train_generator,
epochs=30,
callbacks=[earlystop, checkpoint],
validation_data=valid_generator
)# Save our model for inference
model.save("model-weights/xception.h5")
不幸的是,由于我们拥有的图像数量有限,我们的模型在验证集上没有达到很好的准确性。然而,由于模型微调不是本文的主要焦点,我们不会过多地讨论这个问题。
选择我们的 Web 框架
对于这个项目,我选择了使用streamlit因为它可以实现机器学习应用程序的超级快速可视化,而且方便,它也是用 Python 编写的。在我们构建好之后,我们剩下要做的就是部署它。
首先,导入我们需要的库,并指定模型权重的路径。此外,由于我们使用了flow_from_directory
,TensorFlow 按字母顺序分配类号。照此,D24 将是 0 类,以此类推。
import numpy as npfrom PIL import Image
from tensorflow.keras.models import load_model
from tensorflow.keras.preprocessing.image import img_to_array
from tensorflow.keras.preprocessing import image
import streamlit as stPATH = "model-weights/"
WEIGHTS = "xception.h5"
CLASS_DICT = {
0: 'D24',
1: 'JIN FENG',
2: 'MAO SHAN WANG',
3: 'RED PRAWN'
}
接下来,我们创建一个函数,将上传的图像转换成模型可以使用的格式。我们特别使用来自PIL
的Image
类,因为上传的图像是BytesIO
格式的。
def load_img(input_image, shape):
img = Image.open(input_image).convert('RGB')
img = img.resize((shape, shape))
img = image.img_to_array(img)
return np.reshape(img, [1, shape, shape, 3])/255
Streamlit 的工作方式是,给定用户指定的参数的每一个变化,脚本从上到下重新运行(因此其交互式 UI)。因此,它以st.cache
的形式提供了一个cache
装饰器来缓存加载的对象。缓存通常用于数据加载步骤或任何需要长时间计算/处理的步骤。请记住,我们使用allow_output_mutation=True
参数,因为默认情况下这是False
,如果输出对象以任何方式发生变化,应用程序将被重新加载。
在我们的例子中,模型对象在每次预测时都会发生变异。因此,我们将这个allow_output_mutation
参数设置为True
。我们想要缓存我们的模型的原因是因为我们不想在每次用户选择不同的图像时加载它(即模型加载只进行一次)。
[@st](http://twitter.com/st).cache(allow_output_mutation=True)
def load_own_model(weights):
return load_model(weights)
最后,我们只需要一些代码添加到 UI 中:
if __name__ == "__main__":
result = st.empty()
uploaded_img = st.file_uploader(label='upload your image:')
if uploaded_img:
st.image(uploaded_img, caption="your sexy durian pic",
width=350)
result.info("please wait for your results")
model = load_own_model(PATH + WEIGHTS)
pred_img = load_img(uploaded_img, 224)
pred = CLASS_DICT[np.argmax(model.predict(pred_img))] result.success("The breed of durian is " + pred)
这就对了。我们的 web 应用程序是用 Python 创建的,代码不多。您可以通过在命令行中输入以下命令来确保它(假设它将被称为app.py
)能够在本地运行:
streamlit run app.py
将我们的模型部署到 Heroku
就我个人而言,部署不是我最喜欢的部分,但是,嘿,如果它不在网络上,那它是什么呢?所以让我们开始吧。
有许多方法可以为 web 应用程序提供服务,也有许多云服务提供商来托管它。在这种情况下,我选择与 Heroku 合作主要是因为我以前没有尝试过。
**什么是 Heroku?**Heroku是一个云平台即服务(PaaS),支持多种编程语言,允许开发人员完全在云端构建、运行和操作应用。这篇文章解释的很清楚。
为了部署一个应用程序,我们总是需要某种版本控制来确保我们的应用程序运行在本地机器之外的另一个服务器上。为此,许多人使用 Docker 容器,指定所需的可运行的应用程序和包。
使用 Heroku 进行部署类似于同时使用 Docker 容器和 web 托管服务。但是,它使用 Git 作为部署应用程序的主要手段。我们不需要将所有需要的文件打包到 Docker 容器中,而是必须创建一个 git 存储库来进行版本控制,然后我们可以使用熟悉的git push
,但是要使用heroku
遥控器。
Heroku 然后使用相同的容器技术,但是以一个 dyno 的形式。每个应用程序都放在一个 dyno(或容器)中,每个应用程序消耗“dyno-hours”(更多信息请点击)。每个 Heroku 帐户都有一些可用的免费小时数,消耗的小时数取决于应用程序的活动/流量。首先,如果你没有预见到应用程序的大流量,你应该对免费层非常满意。
同样值得注意的是,当 Heroku 收到应用程序源代码时,它会启动应用程序的构建(如在requirements.txt
中检索依赖项,创建必要的资产等),然后组装成 slug。
术语:slug 是您的源代码、获取的依赖项、语言运行时和编译/生成的构建系统输出的捆绑包——准备执行。
要在 Heroku 上部署,我们需要以下文件:
(1) setup.sh —创建必要的目录并将一些信息(如端口号)写入一个.toml
文件
mkdir -p ~/.streamlit/echo "\
[server]\n\
headless = true\n\
port = $PORT\n\
enableCORS = false\n\
\n\
" > ~/.streamlit/config.toml
(2) Procfile —类似于 Dockerfile,包含我们想要执行的指令。我们将首先在setup.sh
中执行一些 bash 命令,然后执行streamlit run app.py
命令。
web: sh setup.sh && streamlit run app.py
(3) requirements.txt —包含我们的应用程序所需的所有包依赖关系。请注意,这些是我正在使用的版本。您可以通过终端中的conda list
或者使用pip freeze > requirements.txt
来获得您的环境当前使用的软件包的详细列表,从而找到您正在使用的软件包版本。
numpy==1.18.1
spacy==2.2.4
pandas==1.0.1
Pillow==7.1.2
streamlit==0.61.0
tensorflow-cpu==2.2.0
我们的文件夹目录应该如下所示:
app.py
Procfile
README.md
requirements.txt
setup.sh
model-weights
|-- xception.h5
如果您以前没有创建过 Github 存储库,以下是一些简单的步骤:
- 创建新的存储库<repo_name></repo_name>
2.复制红色框中的 URL
3.从终端运行以下命令:
# Clone the repository into our local machine
git clone <repo URL in step 2># Enter the directory we just cloned
cd <repo_name>
4.将之前创建的文件复制到该文件夹中,并在终端中运行以下命令:
# Add all the files we just copied over to be committed
git add .# Commit the files, along with a commit message
git commit -m "deploy app"# Push to master branch on our github repo
git push origin master
我们快到了!这是最后的步骤。
(1)创建一个 Heroku 账户并验证
(2)在这里安装 Heroku CLI
(3)通过终端登录您的 Heroku 账户。将会打开一个浏览器窗口进行身份验证。
heroku login
(4)创建一个 Heroku app
heroku create <project-name>
完成此步骤后,您将能够在终端中看到指向您的项目的链接。
(5)将我们的 git 回购推送到 Heroku remote。从 github repo 的同一个目录中,运行以下命令:
git push heroku master
我们完了!构建完成后,您应该能够在上面的链接中看到您的应用程序已部署!
结束语
希望这对每个试图从头到尾创建自己的迷你 ML 项目的人有用!请随时留下任何意见:)
支持我! —如果你喜欢我的内容并且没有订阅 Medium,请考虑支持我并通过我在这里的推荐链接订阅 ( 注意:你的一部分会员费将作为推荐费分摊给我)。
如何在本地机器上构建和部署 AWS 应用程序
动手教程,云计算系列
在本地机器上构建和部署基于 AWS 的云应用程序
图片作者。由 Pixabay 通过 Canva 生成的原始图像
在我以前的文章中,我谈到了使用 Chalice 和 SAM 在 AWS 上构建和部署无服务器应用程序。这些是快速有趣的项目,利用了无服务器计算的能力,让我们在几分钟内就可以在 AWS 上部署无服务器应用程序。
但是由于没有 AWS 帐户,许多人不能完全利用这样的教程。设置 AWS 帐户和配置开发环境可能非常耗时,有时还会导致不必要的费用(如果配置不当)。
在本文中,我将带您完成构建和部署无服务器应用程序所需的步骤,而无需创建和设置实际的 AWS 帐户。
这一次,我们将使用 Amazon API Gateway、AWS Lambda 和 Amazon DynamoDB 创建一个样本宠物商店应用程序。这个应用程序将有 API 来添加一个新的宠物和获取可用宠物的列表。
先决条件
在本教程中,我们将使用 AWS SAM。您可以按照之前的文章中的指导来安装和配置 SAM。
如何创建项目
运行sam-init
命令创建一个新项目。这将在当前目录下创建一个pet-store
文件夹。
sam init -r java11 -d maven --app-template pet-store -n pet-store
关于传递的参数的更多细节,请参考之前的文章。
让我们更改pom.xml
,将模块名称更新为PetStore
,并使用Java 11
而不是Java 8
。
现在让我们创建一个Pet
类来包含宠物的属性。我们将从简单的属性开始,如name
、age
和category
。
因为我们将使用 Amazon DynamoDB 作为我们的数据存储,所以让我们在pom.xml
中添加相应的 SDK 依赖项。
这将引入 AWS SDK for DynamoDB 和 Apache HTTP Client 的依赖项,这些依赖项将用于创建同步 DynamoDB 客户端。
如何读写项目
我们需要创建一个数据访问类来与 Amazon DynamoDB 交互,并运行我们的读/写查询。创建一个PetStoreClient
类并添加对DynamoDbClient
的依赖。
我们现在将在PetStoreClient
类中创建两个函数来从 DynamoDB 中读取和写入项目。
写一个项目
在 DynamoDB 中添加一个条目是一个PUT
请求。我们将创建一个PutItemRequest
,并指定要添加的表名和项目属性。
然后我们将使用DynamoDbClient
把这个项目放到 DynamoDB 中。
阅读项目
读取 DynamoDB 中的项目列表是一个SCAN
请求。我们将创建一个ScanRequest
并指定要扫描的表名。
然后我们将使用DynamoDbClient
来扫描 DynamoDB 中的表,并返回一个条目列表。
**注意:**扫描请求会遍历表中的所有项目,因此不建议在真实情况下使用。
如何解决依赖关系
我们已经在我们的PetStoreClient
类中添加了DynamoDbClient
作为依赖项。作为一般的最佳实践,代码中的所有此类依赖都应该使用依赖注入(DI)来解决。
说起 DI,春天是我们脑海里第一个蹦出来的名字。然而,Spring 生态系统是巨大的,我们最终会引入很多它的框架,即使我们只想使用 DI 部分。喷射也在运行时完成,使得 Lambda 的冷启动时间更长。
Guice 是另一个很好的依赖注入框架,比 Spring 轻得多。但是就像 Spring 一样,它在运行时进行注入,因此也不适合 DI。
然后是 Dagger,一个纯 DI 框架,在编译时注入依赖项!!它的小尺寸和编译时注入使它成为在 Lambdas 中实现 DI 的完美选择。
我将更深入地研究 DI 的细节,并在另一篇文章中介绍 Dagger 的使用。然而在本文中,我们将使用静态工厂方法的永恒风格来提供依赖关系。
让我们创建一个类DependencyModule
,并在其中声明我们所有的依赖项。
在这个类中,我们正在创建一个新的DynamoDbClient
实例,并将其注入到我们的PetStoreClient
中。我们还创建了一个ObjectMapper
的实例来帮助我们处理 JSON 对象的序列化和反序列化。
如何更新 Lambda 和 API 端点
接下来,我们需要更新 Lambda 函数的入口点,并为添加和检索宠物添加特定的端点。
将下面的代码片段添加到template.yaml
文件的Resources
部分。
这更新了我们的函数来使用来自App
类的handleRequest
方法,并且还添加了两个 API 端点来添加和检索宠物。
更新Outputs
部分以反映新的函数名。
如何整合客户端
既然我们已经准备好了与 DynamoDB 交互的代码,并且对依赖项进行了排序,那么我们需要在 Lambda 处理程序中进行一些更改来调用这些代码。
更新App.java
中的代码,以调用PetStoreClient
中的函数,并根据 API 请求执行动作。
由于我们使用静态工厂进行依赖注入,我们将不能有效地测试我们的代码。我将在另一篇文章中介绍云应用程序的单元测试。现在,我们需要删除单元测试来构建项目。
如何构建项目
从pet-store
文件夹中,运行sam build
命令。
作者图片
这将编译您的源代码并构建您在应用程序中拥有的任何依赖项。然后,它将所有文件移动到.aws-sam/build
文件夹中,这样它们就可以打包和部署了。
如何进行本地测试(第 1 部分)
在之前的文章中,我们讨论了 SAM CLI 如何提供sam local
命令来本地运行您的应用程序。这在内部使用 Docker 来模拟 Lambda 的执行环境。如果没有安装 Docker,可以从这里获取。
这对 Daily News API 来说很好,因为它从互联网上获取数据,并且不依赖于任何其他 AWS 组件。然而,在当前的项目中,我们依赖 Amazon DynamoDB 作为我们的数据存储,并且需要访问它以便我们能够成功地运行我们的应用程序。
本质上,我们需要一种方法在本地机器上模拟 AWS 提供的服务,这样我们就可以在本地测试它们,而不需要使用实际的 AWS 帐户。
如何在本地运行 AWS
LocalStack 就是为了解决这个问题而产生的。用它自己的话说:
LocalStack 为开发云应用程序提供了一个易于使用的测试/模拟框架。它在您的本地机器上构建了一个测试环境,提供与真正的 AWS 云环境相同的功能和 API。
简而言之,LocalStack 将 AWS cloud 的所有功能都集成到一个 docker 容器中,该容器在我们的机器上本地运行。这使开发人员能够构建和测试他们的云应用程序,而不必在实际的 AWS 云帐户上部署它们。
对一个开发者来说意味着什么?
- 无需提供 AWS 帐户。
- 不需要设置开发环境和考虑安全性和其他配置。
- 不需要在开发期间产生不必要的 AWS 成本。
- 完全模拟实际 AWS 环境的透明本地环境。
如何设置本地堆栈
LocalStack 非常容易设置和开始使用。我们将使用 Docker 获取 LocalStack 的最新映像,并启动一个运行 Amazon DynamoDB 模拟版本的容器。
在pet-store
文件夹中创建一个docker-compose.yaml
文件,并添加以下内容。
让我们来看看我们正在使用的一些配置:
- 服务——因为我们只依赖 Amazon DynamoDB,所以我们将只启用这个特定的服务
- DEFAULT_REGION —我们将使用 us-west-1 作为我们的 AWS 区域
- LAMBDA _ EXECUTOR——将它设置为 local 意味着我们所有的 LAMBDA 函数都将在本地机器上的一个临时目录中运行
- DATA_DIR —为 Amazon DynamoDB 等服务保存持久数据的位置
**注意:**所有的 LocalStack 服务都是通过端口 4566 上的 edge 服务公开的。这是我们需要使用的唯一端口。
现在,我们可以使用docker-compose
在它自己的容器中启动我们本地版本的 Amazon DynamoDB。
作者图片
如何创建表格
既然我们已经运行了 Amazon DynamoDB 的本地设置,我们应该能够为我们的应用程序创建一个表。
我们在代码中使用了pet-store
作为表名,所以让我们继续创建它。我们将使用 AWS CLI 访问运行在本地机器上的 Amazon DynamoDB,并创建所需的表。
运行下面的命令创建一个名为pet-store
的表,并将属性id
作为主键。
aws --endpoint-url "[http://localhost:4566](http://localhost:4566)" dynamodb create-table \
--table-name pet-store \
--attribute-definitions AttributeName=id,AttributeType=S \
--key-schema AttributeName=id,KeyType=HASH \
--billing-mode PAY_PER_REQUEST
注意,我们使用了endpoint-url
参数来指定我们指向本地运行的 AWS 实例,而不是实际的实例。
如何进行本地测试(第 2 部分)
对 DynamoDbClient 代码进行以下更改,使其指向本地运行的 Amazon DynamoDB 实例。
接下来,使用sam build
构建项目,并运行以下命令在本地启动 API。
sam local start-api
这在内部创建了一个本地服务器,并公开了一个复制 REST API 的本地端点。
作者图片
现在,让我们通过添加一只新宠物来测试我们的应用程序。运行下面的命令,通过调用我们之前指定的/pets
端点来添加一个新的宠物。
curl --location --request PUT '[http://127.0.0.1:3000/pets'](http://127.0.0.1:3000/pet') \
--header 'Content-Type: application/json' \
--data-raw '{
"name": "Rocket",
"age": 2,
"category": "Dog"
}'
这创建了一个新的宠物记录,将其添加到我们的本地 Amazon DynamoDB,并在响应中返回生成的 UUID。
我们店里再添一只宠物吧。
curl --location --request PUT '[http://127.0.0.1:3000/pets'](http://127.0.0.1:3000/pet') \
--header 'Content-Type: application/json' \
--data-raw '{
"name": "Candle",
"age": 1,
"category": "Pig"
}'
现在,让我们调用我们的/pets
API 来获取数据存储中可用的宠物列表。我们应该会得到一个包含Rocket
和Candle
的宠物列表。
作者图片
结论
恭喜你!!您刚刚在本地机器上构建并部署了一个完全使用 AWS DynamoDB 的无服务器应用程序。
现在您可以继续对您的App.java
文件进行任何修改。重新运行sam deploy
来重新部署您的更改,运行sam local start-api
来启动本地服务器并测试更改。
一旦您为部署做好准备,您只需要删除端点覆盖,就万事大吉了。在理想情况下,这将由环境变量控制,并且绝对不需要修改代码就可以投入生产。
本教程的完整源代码可以在这里找到。
如何使用 Python、Google Sheets 和 Vue.js 构建和部署您的仪表板
实践教程
部署仪表板的实用指南,无需服务器。
Stephen Dawson 在 Unsplash 上拍摄的照片
数据的可视化让我们能够快速洞察。但是,部署数据可视化解决方案可能会很困难,因为大多数时间都需要购买许可证或服务器。
在本文中,我们将看到如何使用免费工具构建一个仪表板来呈现一些虚假销售,您可以下载数据这里以便遵循教程。
首先,我们将使用 Python 来填充一个 Google sheet。然后,我们将使用 Vue.js 创建一个使用 apache Echart 和 Vuetify 的仪表板。最后,我们将使用 Github action 在 GitHub 页面上部署这个仪表板。
要求
要遵循本指南,您需要具备以下条件:
- 一个谷歌账户
- gcloud 命令行工具
- Python 3.7 或更高版本
- Github 账户
设置 Google cloud 帐户
我将使用 google cloud 命令行工具来设置 google cloud 帐户。如果你不熟悉 google cloud SDK,你可以在这里找到适合你平台的快速入门。
让我们来配置我们的谷歌云项目:
-
使用
gcloud create project <project_id>
创建新项目 -
将新创建的项目设置为其他命令的默认项目:
gcloud config set project <project_id>
-
启用 google sheet API 和 Drive API:
gcloud services enable sheets.googleapis.com
-
为 python 脚本创建一个服务帐户:
gcloud iam service-accounts <name_of_service_account>
-
从服务帐户:
gcloud iam service-accounts keys create <local_path_to_download.json> --iam-account <name_of_service_account>@<project_id>.iam.gserviceaccount.com
创建并下载凭证 -
为 Vue.js 应用程序创建 API 密钥:
- 导航到云控制台中的API&服务→凭证面板
- 选择创建凭证,然后从下拉菜单中选择 API key 。
- API 密钥创建对话框显示您新创建的密钥,保存它以备后用。
- 然后点击“编辑”,在“限制 API”部分选中“限制关键字”选项,选择“Google 工作表 API ”,然后点击“保存”。
接下来,我们将创建一个脚本,使用 Python 将仪表板数据推送到 Google sheets。
将数据框架推送到 Google sheets
Python 脚本将需要一些依赖关系,以便将数据推送到 Google。用:pip install pandas pygsheets
安装它们,然后创建一个目录和一个 Python 文件,并复制下面的脚本。
将数据框上传到新的谷歌工作表来源:作者
在这个脚本中,我们首先用前面创建的凭证文件初始化 Google sheets 客户机。然后,我们创建一个全新的谷歌表,我们与我们的谷歌帐户共享它,这样我们就可以在谷歌驱动可视化它。我们还公开了我们的工作表,以便 Vue 应用程序可以访问它。接下来,我们创建 3 个工作表,并为每个工作表上传一个熊猫数据框。最后,我们打印工作表 id,保存它,因为我们将在下一部分中需要它来构建 Vue.app。
构建仪表板
为了简单起见,我们将使用 Vue.js 构建仪表板,不使用任何构建工具。首先,在根目录下创建一个dist
文件夹,在里面创建一个index.html文件,内容如下。
仪表板代码来源:作者
我们来破个码,Vue app 是从 161 行到 245 行定义的。首先,有一个数据部分,其中我们声明了包含来自 Google sheet 的数据的对象,以及一个包含与 Google sheet 数据匹配的指标的数组。
接下来,我们有一个允许我们通过 Google sheets API 恢复数据的函数。这个方法使用从第 80 行到第 160 行定义的类。这个类是在 mounted 部分初始化的,它在参数中接受工作表的 id 和您的 API 键,,所以请确保用您自己的替换这些值。
然后在计算部分,我们声明我们的图表变量。如你所见,它们都遵循相同的模式。我们首先引用数据所在的工作表,然后为每个图表声明列、行和不同的设置。
最后,我们使用 HTML 格式的数据可视化库电子图表,从第 26 行到第 45 行,根据我们声明的变量来呈现我们的图表。现在,如果你打开你的index.html文件,你应该会看到这样一个仪表板:
index.html 仪表板来源:作者
在 GitHub 页面上部署
我们现在将使用 Github 操作在 GitHub 页面上部署我们的仪表板。为此,您必须在您的 GitHub 帐户上创建一个新目录。然后,您将需要创建一个 GitHub 个人访问令牌以便能够使用 GitHub 操作,点击 此处 查看如何生成一个。
您需要选择您想要授予这个令牌的作用域,确保选择repo
复选框以便能够在 GitHub 页面上部署。
现在,您需要在 GitHub 存储库中创建一个秘密,以便部署操作可以使用您创建的令牌。查看官方文档此处了解如何继续。命名秘密访问令牌。
一旦你配置好了,你就可以在你的项目的根文件夹中创建一个目录树.github/worflows
,它将包含下面的main.yml
文件。
GitHub 工作流来源:作者
现在您已经定义了您的工作流,在您的 GitHub 远程 repo 上推送您的项目。如果一切正常,你可以在以下网址看到仪表盘:
https:/<your-github-username>. github . io/<repo-name>/。
最后,转到您在谷歌云控制台上的凭证点击编辑您的 API 密钥,并在应用程序限制部分:
- 选中选项:HTTP 推荐人(网站)
- 点击添加一个项目
- 在输入表单中输入你的 Github 页面的 URL:
https:/<your-Github-username>. Github . io/<repo-name>/。
缺点和限制
API 密钥在这里被用作调用 Google APIs 的认证方法。这个方法只允许你读取公开的工作表,所以不要使用本教程来部署敏感数据。
对于这种用法,您必须通过本指南 中的 所示的 oauth2 认证协议。
结论
在本文中,我们看到了如何部署一个由 Google sheet 支持的简单仪表板。该仪表板读取引用工作表中的数据。如果您想更新数据,您只需更新 Google sheet 工作簿,单击刷新按钮,它就会反映在仪表板上。最后,由于 GitHub action,仪表板的部署是自动化的。
如果您有兴趣通过电子邮件发送您的仪表板或报告,请查看这篇文章。
[## 使用 Python、Vue.js 和 Gmail 自动化您的报告流程
一个具体的一步一步的例子,自动化您的报告,而不需要服务器。
medium.com](https://medium.com/swlh/automate-your-reporting-with-python-and-vue-js-15ef130fff8)
如何用 Python 构建和发布命令行应用程序
戴维·克洛德在 Unsplash 上的照片
关于如何用 Python 构建和发布命令行应用程序的全面指南。
命令行应用程序基本上是您在终端上运行的程序,并且您很可能已经尝试或考虑过构建一个。
构建命令行应用程序是一回事,将它发布到像 PyPI 这样的开放公共代码库是另一回事,不应该被视为一项困难的任务或过程。
我以前写过一篇文章,其中我深入解释了用 Python 构建命令行应用程序的各种方式和方法。
在本文中,我将解释如何用 Python 构建一个简单的 CLI 并将其发布到 PyPI。
入门指南
最近我一直在做一些关于开源漏洞的研究,我想有一个命令行工具,我可以用它来搜索和查找终端上的漏洞。开源漏洞通常发布在公共数据库中,可以在类似 CVE 、 NVD 、白源漏洞等网站上找到。
在本文中,我们将构建一个简单的刮刀来搜索和查找 CVE 网站上的漏洞,将其包装成一个简单的命令行应用程序,并将其发布到 PyPI。
酷吧?
要开始,您需要设置您的开发环境并安装所需的模块。我建议建立一个虚拟环境,因为它使事情变得更容易,并有助于避免模块版本的冲突。
要创建一个虚拟环境,你可以使用 python3 命令python -m venv <path/name>
或者使用pip install virtualenvwrapper
安装virtualenvwrapper
并使用mkvirtualenv -p /path/topython <path/name>
创建一个虚拟环境
设置并激活 virtualenv 后,您可以创建项目文件夹并安装所需的模块:
mkvirtualenv -p /usr/bin/python cvecli-env
mkdir cvecli && cd cvecli
mkdir cver && touch setup.py && touch README.md && touch cver/__init__.py && touch .gitignore
pip install requests beautifulsoup4 lxml twine click
pip freeze > requirements.txt
一旦一切运行成功,您可以在任何代码编辑器中打开该项目,您应该会看到如下所示的结构:
编写我们的网页抓取器
为了能够在 CVE 网站上搜索和查找漏洞,我们需要一个网页抓取器来帮助抓取漏洞细节。我们将根据要求制造铲运机和 Beautifulsoup,铲运机能够:
- 搜索漏洞
- 使用漏洞的 CVE 名称获取漏洞详细信息
现在在cver
文件夹中,创建一个名为cve_scraper
的文件,基本设置如下:
import requests
from bs4 import BeautifulSoupSEARCH_URL = "[https://cve.mitre.org/cgi-bin/cvekey.cgi?keyword=](https://cve.mitre.org/cgi-bin/cvekey.cgi?keyword=python)"CVE_URL = "[https://cve.mitre.org/cgi-bin/cvename.cgi?name=](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-8492)"def get_html(url):
request = requests.get(url)
if request.status_code == 200:
return request.content
else:
raise Exception("Bad request")def search(s):
passdef lookup_cve(name):
pass
搜索漏洞
在 CVE 网站上搜索漏洞,URL 格式如下:[https://cve.mitre.org/cgi-bin/cvekey.cgi?keyword](https://cve.mitre.org/cgi-bin/cvekey.cgi?keyword=python)=<keyword>
。这样,您应该能够用关键字,
例如,我们可以使用 URL 来获取所有与 python 相关的漏洞:
为了提取数据,让我们打开开发人员控制台,检查呈现上述数据的 DOM 元素。您可以右键单击页面的任何部分,然后单击inspect element
或按 Ctrl + F12。
如果您看一下上图中的 DOM 结构,您会注意到结果显示在一个表格中,每个结果都是表格下的一行。数据可以很容易地提取如下:
def search(s): url = f"{SEARCH_URL}{s}"
results=[]
html = get_html(url)
soup = BeautifulSoup(html, "lxml")
result_rows = soup.select("#TableWithRules table tr") for row in result_rows:
_row = {}
name = row.select_one("td a")
description = row.select_one("td:nth-child(2)") if all([name, description]): _row["name"] = name.text
_row["url"] = name.get("href")
_row["description"] = description.text results.append(_row)
return results
在上面的代码中,我们:
- 使用请求向 SEARCH_URL 发送请求并获取 DOM 内容
- 将 dom 内容转换成漂亮的组对象,我们可以使用 CSS 选择器和其他类似 XPATH 的方法来选择 DOM 元素。
- 选择
#TableWithRules
表格下的所有tr
,选择该行的第一列作为名称,第二列作为描述,然后提取文本。
查找漏洞详细信息
要查找漏洞详细信息,您需要提供漏洞的 CVE ID,并将其传递到以下 URL:https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-ID
打开您的开发人员控制台,让我们检查一下 DOM 结构。
查看上面的结构有点复杂,因为表行没有用类名或 id 标识。我们需要遍历每一行,检查它是否是一个副标题,如果是,那么我们把下一个元素作为子内容。每个字幕在th
中呈现,而内容在td
中呈现
def lookup_cve(name):
url = f"{CVE_URL}{name}"
html = get_html(url)
soup = BeautifulSoup(html, "lxml")
result_rows = soup.select("#GeneratedTable table tr")subtitle = ""
description = ""raw_results = {}for row in result_rows:
head = row.select_one("th")
if head:
subtitle = head.text
else:
body = row.select_one("td")
description = body.text.strip().strip("\n")
raw_results[subtitle.lower()] = description
return raw_results
Tada!我们已经成功地创建了我们的 CVE 网络刮刀。你现在可以用两个函数(search 和 lookup_sve)来搜索漏洞,并使用漏洞的 CVE ID 来获取漏洞的详细信息。
构建我们的命令行应用程序
下一步是使用 Click 库构建我们的命令行应用程序。
Click 是一个 Python 包,用于以可组合的方式用尽可能少的代码创建漂亮的命令行界面。这是创建 CLI 的最佳 python 包之一,并且易于入门
使用 Click,您可以构建任何类型的简单或企业级 CLI,如 Heroku CLI。
在我们的 CLI 中,我们将实现两个命令:
- 搜索漏洞
- 查找漏洞
在cver
文件夹中创建一个名为__main__.py
的文件,并输入以下基本代码:
import sys
import click[@click](http://twitter.com/click).group()
[@click](http://twitter.com/click).version_option("1.0.0")
def main():
"""A CVE Search and Lookup CLI"""
print("Hye")
pass[@main](http://twitter.com/main).command()
[@click](http://twitter.com/click).argument('keyword', required=False)
def search(**kwargs):
"""Search through CVE Database for vulnerabilities"""
click.echo(kwargs)
pass[@main](http://twitter.com/main).command()
[@click](http://twitter.com/click).argument('name', required=False)
def look_up(**kwargs):
"""Get vulnerability details using its CVE-ID on CVE Database"""
click.echo(kwargs)
passif __name__ == '__main__':
args = sys.argv
if "--help" in args or len(args) == 1:
print("CVE")
main()
搜索漏洞
为了实现这一点,我们将从 web scraper 导入搜索功能,并从命令行向其传递关键字参数,以搜索与该关键字匹配的漏洞:
from scraper import search as cve_search, lookup_cve[@main](http://twitter.com/main).command()
[@click](http://twitter.com/click).argument('keyword', required=False)
def search(**kwargs):
"""Search through CVE Database for vulnerabilities"""
results = cve_search(kwargs.get("keyword"))
for res in results:
click.echo(f'{res["name"]} - {res["url"]} \n{res["description"]}')
要运行此命令:
python cver/__main__.py search python
查找漏洞
同样的事情在这里,我们将使用来自 web scraper 的lookup_cve
,并从look_up
命令传递给它 name 参数。
[@main](http://twitter.com/main).command()
[@click](http://twitter.com/click).argument('name', required=False)
def look_up(**kwargs):
"""Get vulnerability details using its CVE-ID on CVE Database"""
details = lookup_cve(kwargs.get("name"))
click.echo(f'CVE-ID \n\n{details["cve-id"]}\n')
click.echo(f'Description \n\n{details["description"]}\n')
click.echo(f'References \n\n{details["references"]}\n')
click.echo(f'Assigning CNA \n\n{details["assigning cna"]}\n')
click.echo(f'Date Entry \n\n{details["date entry created"]}')
要运行此命令:
python cver/__main__.py look-up CVE-2013-4238
Tada!我们已经成功构建了 CVE 查找命令行工具。
向 PyPI 发布我们的命令行应用程序
现在我们已经成功地构建了命令行应用程序,一切都运行良好,我们可以将它发布到 PyPI 供公众使用和安装。
PyPI 是 python 包的软件仓库,它保存了我们使用pip
命令工具安装的大多数 Python 包。要在 PyPI 上发布一个包,你需要创建一个账户,这样你就可以去网站创建一个新账户,如果你已经有了一个,那么你就可以开始了。
配置我们的包
一旦你完成了这些,下一件事就是使用setup.py
配置我们的 Python 包。为了将您的包上传到 PyPI,您需要提供一些关于包的基本信息。该信息通常在 setup.py 文件中提供。
因此,打开项目基本目录中的setup.py
,并将它放在文件的开头:
from setuptools import setup, find_packages
from io import open
from os import pathimport pathlib
# The directory containing this file
HERE = pathlib.Path(__file__).parent# The text of the README file
README = (HERE / "README.md").read_text()# automatically captured required modules for install_requires in requirements.txt and as well as configure dependency links
with open(path.join(HERE, 'requirements.txt'), encoding='utf-8') as f:
all_reqs = f.read().split('\n')install_requires = [x.strip() for x in all_reqs if ('git+' not in x) and (
not x.startswith('#')) and (not x.startswith('-'))]
dependency_links = [x.strip().replace('git+', '') for x in all_reqs \
if 'git+' not in x]
在上面的例子中,我们将README.md
文件的内容转换成字符串供以后使用。我们还从requirements.txt
中捕获了所有需要的模块,并生成了它们的依赖链接。
您的 requirements.txt 文件应该如下所示:
click
requests
beautifulsoup4
lxml
twine
现在让我们看看我们的设置配置:
setup (
name = 'cver',
description = 'A simple commandline app for searching and looking up opensource vulnerabilities',
version = '1.0.0',
packages = find_packages(), # list of all packages
install_requires = install_requires,
python_requires='>=2.7', # any python greater than 2.7
entry_points='''
[console_scripts]
cver=cver.__main__:main
''',
author="Oyetoke Toby",
keyword="cve, vuln, vulnerabilities, security, nvd",
long_description=README,
long_description_content_type="text/markdown",
license='MIT',
url='[https://github.com/CITGuru/cver'](https://github.com/CITGuru/cver/'),
download_url='[https://github.com/CITGuru/cver/archive/1.0.0.tar.gz'](https://github.com/CITGuru/cver/archive/1.0.0.tar.gz'),
dependency_links=dependency_links,
[author_email='oyetoketoby80@gmail.com](mailto:author_email='oyetoketoby80@gmail.com)',
classifiers=[
"License :: OSI Approved :: MIT License",
"Programming Language :: Python :: 2.7",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.7",
]
)
在上面的代码中,我们添加了一些选项,这里我们将只介绍设置中可用的一些选项。设置工具文档很好地研究了所有细节。
- name:将出现在 PyPI 上的包的名称
- 版本:您的软件包的当前版本
- 包:包含源代码的包和子包。我们正在使用设置中的
find_packages
模块来帮助我们自动找到我们的子包。 - install_requires:这是用来列出你的包拥有的任何依赖项或第三方库。在
cver
中,我们使用了 requests、beautifulsoup4 和 click。它们必须包含在 install_requires 安装程序中。我们不需要手动放置它,因为我们已经阅读了requirements.txt
来获取它们。 - entry_points:这用于创建调用包内函数的脚本。在我们的设置中,我们创建了一个新的脚本
cver
,它在 cver/main 中调用 main()。py 文件。我们的主入口是__main__.py
,它调用main()
函数来启动 click。
在向 PyPI 或公众发布您的包之前,您应该添加一些文档。如何记录包取决于您的项目。这可能是一个简单的README.md
文件或Readme.rst
文件,完全取决于你。
这里有一个典型的好听的README.md
:
# CVERA simple commandline app for searching and looking up opensource vulnerabilities# Installation## Using Pip```bash
$ pip install cver
```## Manual```bash
$ git clone [https://github.com/citguru/cevr](https://github.com/citguru/cevr)
$ cd cver
$ python setup.py install
```# Usage```bash
$ cver
```## Search`search <keyword>````bash
$ cver search python
Lookup`search ````bash
$ cver look-up CVE-2020-2121
另外,创建一个.gitignore
文件:
就是这样。
发布到 PyPI
一旦一切都成功完成,这个包就可以公开发布了,并发布在 PyPI 上。确保你已经创建了一个我前面提到的帐户,你还需要在 PyPI 测试服务器上创建一个测试帐户,以便在发布到实时服务器之前测试包。
我们将使用一个叫做 Twine 的工具来上传你的 python 包到 PyPI。它应该在前面的步骤中安装,但是如果你没有安装,你可以只做pip install twine
。
在本地和测试服务器上构建和测试包
PyPI 上发布的 Python 包不是以普通源代码的形式发布的,而是被打包成发布包。Python wheels 和源代码档案是分发 Python 包时最常见的格式。
Python wheels 本质上是一个包含您的代码的 zip 存档,并且包括任何可以使用的扩展。Source Archives 由您的源代码和任何支持文件组成,打包到一个 tar 文件中。
为了在本地测试我们的包,我们只需要运行:
python setup.py install
那么我们现在可以把它当作:
cver search python
为了在 PyPI 测试服务器上测试我们的包,我们需要为本地测试生成一个构建。创建一个构建将同时生成 python wheel 和源档案。
要创建构件:
python setup.py sdist bdist_wheel
这将在dist
目录中生成两个文件:
cvecli/
│
└── dist/
├── cver-1.0.0-py3-none-any.whl
└── cver-1.0.0.tar.gz
然后使用 twine,我们现在可以上传到服务器:
twine upload --repository-url https://test.pypi.org/legacy/ dist/*
这将要求您输入用户名和密码,因此请确保输入正确。
如果上传成功,没有任何问题,这意味着我们可以在服务器上发布。你可以在这里查看。
要从 TestPyPI 安装,运行以下命令:
pip install -i [https://test.pypi.org/simple/](https://test.pypi.org/simple/) cver==1.0.0
从这里你可以尝试所有的命令,看看在我们发布到服务器之前是否一切顺利。
测试完所有命令并准备好发布到 live server 后:
twine upload dist/*
出现提示时,输入您的用户名和密码。就是这样!
您现在可以使用以下方式安装它:
pip install cver
恭喜你!你的包发布在 PyPI 上,你可以在这里查看!
结论
在本文中,我解释了如何用 Python 构建和发布下一个命令行应用程序的逐步过程。
如何使用 BigQuery ML 利用网站数据构建受众群
理解大数据
收集受众见解和建立客户细分的技术指南
一个常见的营销分析挑战是理解消费者行为并开发客户属性或原型。随着组织更好地解决这个问题,他们可以激活营销策略,将更多的客户知识融入到他们的活动中。使用一种叫做聚类的技术,使用 BigQuery ML 构建客户档案比以往任何时候都容易。在这篇文章中,你将学习如何创建细分市场,以及如何利用这些受众进行营销活动。
为什么聚类算法如此重要?
聚类算法可以将相似的用户行为分组在一起,以建立用于营销的细分。随着我们进入个性化时代,聚类算法可以帮助公司通过基于网站行为的广告向现有客户或潜在客户发送专门的信息。
聚类算法是如何工作的?
在本教程中,我将提供对聚类算法的简单理解,然而,大部分内容将涵盖过程和实现,而不是幕后发生的事情。一般来说,聚类属于无监督机器学习的范畴。我们正在运行一个算法,具体来说,在这个过程中,我们将使用 k-means,在不给算法一个目标变量来训练的情况下,找出数据如何逻辑地分组在一起。例如,假设我们想根据年龄和估计收入这两个特征对您的受众进行分类。聚类是自动为您完成这一任务的过程。我们面临的唯一输入是我们的数据中存在多少个聚类。在下面的例子中,三个集群“感觉”正确。这个例子可能看起来很简单,但是您可以看到这个问题是如何变得无法手工处理更多的特性的。
作者图片
聚类的数量通常更难确定,并且通常是执行分析的人的主要挑战。我们将花时间给出一个示例工作流和流程来帮助应对这一挑战。现在,考虑将操作集群化,以自动将我们的数据分组在一起。
数据
我们将使用公开可用的 Google Analytics 360 样本数据集,它托管在 BigQuery 上,包含来自 Google 商品商店的 12 个月(2016 年 8 月至 2017 年 8 月)模糊的 Google Analytics 360 数据,这是一家销售 Google 品牌商品的真实电子商务商店。我们还将构建合成的 CRM 数据来展示线下+线上数据的力量,这将提供一个更加全面的用户行为视图。
来自谷歌商品商店的截图
这是来自谷歌分析 360 的一些原始数据的样本
处理数据
为聚类构建特征(我们关心的个人属性)完全取决于我们试图解决的问题。在花太多时间处理数据集之前,您应该首先确定业务挑战。为了做到这一点,请咨询您的业务利益相关者,以确定您想要解决的问题。例如,您可能会假设人口统计和地理数据、SKU 或产品类别、重复购买者或首次购买者以及当前客户价值之间的关系。你会注意到这包括了分类特征和连续特征的混合。通常,如果您使用 scikit-learn、statsmodels 或其他软件包,这意味着需要时间来规范化和在数据中创建一个热编码。BigQuery ML 的一个直接优势是这个需求不存在!您可以以原始格式传递要素,而无需预处理。当然,花时间做探索性的数据分析并理解您的数据集,但是享受使用 BigQuery ML 节省的时间。
本教程的一些问题和注意事项:
在本教程中,我们将做一些假设。例如,我们将简化方法,并假设当 pagetype = "EVENT "时发生购买。您的 Google Analytics 360 设置可能会有所不同,因此您可能需要进行相应的调整。我们还使用 fullVisitorID 作为我们的 cookie 标签。虽然这是正确的,但我们建议您将 clientID 设置为自定义维度,其中 clientID 只是 fullVisitorID 的哈希版本。当你想在以后的道路上激活观众时,这是一个要求。我们还假设您有可以映射到 fullVisitorID 的离线数据。我们将创建 3 个 BigQuery 视图。第一个视图是聚合 GA360 数据,第二个视图是聚合 CRM 数据,最后是连接在一起,最终用于建模。这样,我们就可以创建我们的第一个视图了(注意,SQL 很长,所以我会在这篇博客中将其缩短,但是完整的代码可以在这个 github repo 中找到)
*# We start with GA360 data, and will eventually build synthetic CRM as an example.*
*# This block is the first step, which is just working with GA360
# I am cutting this code short for formatting. See github for full code.*ga360_only_view = 'GA360_View'
shared_dataset_ref = client.dataset(DATA_SET_ID)
ga360_view_ref = shared_dataset_ref.table(ga360_only_view)
ga360_view = bigquery.Table(ga360_view_ref)ga360_query = '''
SELECT
fullVisitorID,
ABS(farm_fingerprint(fullVisitorID)) AS Hashed_fullVisitorID,
MAX(device.operatingSystem) AS OS,
SUM (CASE
WHEN REGEXP_EXTRACT (v2ProductCategory,
r'^(?:(?:.*?)Home/)(.*?)/')
= 'Apparel' THEN 1 ELSE 0 END) AS Apparel,...
FROM
`bigquery-public-data.google_analytics_sample.ga_sessions_*`,
UNNEST(hits) AS hits,
UNNEST(hits.product) AS hits_product
WHERE
_TABLE_SUFFIX BETWEEN '20160801'
AND '20160831'
AND geoNetwork.country = 'United States'
AND type = 'EVENT'
GROUP BY
1,
2
'''ga360_view.view_query = ga360_query.format(PROJECT_ID)
ga360_view = client.create_table(ga360_view) *# API request*
视图以这种格式创建数据
现在我们有了利用网站行为的基线数据集,我们希望将它与我们可能也了解的用户的离线数据结合起来。为了最好地展示这一点,我们将简单地生成合成数据。如果您有更好的数据源,请随意用您自己的流程替换这一部分。如果没有,请遵循以下步骤:
*# Create synthetic CRM data in SQL*CRM_only_view = 'CRM_View'
shared_dataset_ref = client.dataset(DATA_SET_ID)
CRM_view_ref = shared_dataset_ref.table(CRM_only_view)
CRM_view = bigquery.Table(CRM_view_ref)*# Query below works by hashing the fullVisitorID, which creates a random distribution.*
*# We use modulo to artificially split gender and hhi distribution.*CRM_query = '''
SELECT
fullVisitorID,
IF
(MOD(Hashed_fullVisitorID,2) = 0,
"M",
"F") AS gender,
CASE
WHEN MOD(Hashed_fullVisitorID,10) = 0 THEN 55000
WHEN MOD(Hashed_fullVisitorID,10) < 3 THEN 65000
WHEN MOD(Hashed_fullVisitorID,10) < 7 THEN 75000
WHEN MOD(Hashed_fullVisitorID,10) < 9 THEN 85000
WHEN MOD(Hashed_fullVisitorID,10) = 9 THEN 95000
ELSE
Hashed_fullVisitorID
END
AS hhi
FROM (
SELECT
fullVisitorID,
ABS(farm_fingerprint(fullVisitorID)) AS Hashed_fullVisitorID,
FROM
`bigquery-public-data.google_analytics_sample.ga_sessions_*`,
UNNEST(hits) AS hits,
UNNEST(hits.product) AS hits_product
WHERE
_TABLE_SUFFIX BETWEEN '20160801'
AND '20160831'
AND geoNetwork.country = 'United States'
AND type = 'EVENT'
GROUP BY
1,
2)
'''CRM_view.view_query = CRM_query.format(PROJECT_ID)
CRM_view = client.create_table(CRM_view) *# API request*
综合 CRM 数据的输出
运行上面的作业后,我们现在有了前两个视图。要将两者结合在一起,并创建我们的最终视图,过程非常简单:
*# Build a final view, which joins GA360 data with CRM data*final_data_view = 'Final_View'
shared_dataset_ref = client.dataset(DATA_SET_ID)
final_view_ref = shared_dataset_ref.table(final_data_view)
final_view = bigquery.Table(final_view_ref)final_data_query = f'''
SELECT
g.*,
c.* EXCEPT(fullVisitorId)
FROM **{**ga360_view.full_table_id.replace(":", ".")**}** g
JOIN **{**CRM_view.full_table_id.replace(":", ".")**}** c
ON g.fullVisitorId = c.fullVisitorId
'''final_view.view_query = final_data_query.format(PROJECT_ID)
final_view = client.create_table(final_view) *# API request*print(f"Successfully created view at **{**final_view.full_table_id**}**")
用于建模的最终数据集就是这个形状。我们现在可以开始建模了。
创建我们的初始模型
在这一节中,我们将建立我们的初始 k 均值模型。我们不会关注最优 k(我们构建的集群数量)或其他超参数。
一些附加要点:
- 我们删除了作为输入的 fullVisitorId,因为它不是一个有用的特性。请记住,高基数数据对分组没有用处。
- 我们既有分类特征,也有数字特征。
- 我们不需要规范化任何数字特征,因为 BigQuery ML 会自动为我们做这件事。
- 我们不为分类特征构建一个热编码,因为 BigQuery ML 也会为我们做这件事。
构建一个函数来构建我们的模型
我们将构建一个简单的 python 函数来构建我们的模型,而不是用 SQL 做所有的事情。这种方法意味着我们可以异步启动几个模型,让 BQ 并行运行。另一种方法是使用 BigQuery 脚本(虽然串行完成,因此速度较慢)。
我们将从构建一个简单的模型开始,确保一切看起来都是正确的,然后改进我们的流程。第一步如下:
**def** makeModel (n_Clusters, Model_Name):
sql =f'''
CREATE OR REPLACE MODEL `**{**PROJECT_ID**}**.
**{**DATA_SET_ID**}**.
**{**Model_Name**}**`
OPTIONS(model_type='kmeans',
kmeans_init_method = 'KMEANS++',
num_clusters=**{**n_Clusters**}**) ASSELECT * except(fullVisitorID, Hashed_fullVisitorID)
FROM `**{**final_view.full_table_id.replace(":", ".")**}**`
'''job_config = bigquery.QueryJobConfig()
client.query(sql, job_config=job_config) *# Make an API request.*
这创建了一个函数,将为我们建立一个模型,并允许用户定义 k(我们将建立多少个集群)。为了测试,让我们调用 k=3 的函数。
makeModel(3, "test")
一旦训练完成,您现在就有了一个存储在 BigQuery 中的模型对象,您可以通过单击模型在 Bigquery UI 中查看和引用它。我们建议您在开始时回顾一下您的模型对象的细节、训练、评估和模式,以理解我们刚刚做了什么。在后面的小节中,我们将向您展示如何以编程方式检索最终模型的统计数据。
“模型评估”选项卡提供汇总统计数据
建立一个更好的模型
尽管我们刚刚完成了第一个 k-means 模型的构建,但我们仍然没有解决实现聚类过程时的主要问题——我们应该构建多少个聚类(k )?确定 k 的正确值完全取决于用例。有一些简单明了的例子可以告诉您需要多少个集群。假设您正在预处理手写数字——这告诉我们 k 应该是 10。或者,也许你的企业利益相关者只想开展三种不同的营销活动,并需要你识别三个客户群,那么设置 k=3 将是有意义的。但是,使用案例有时更加开放,您可能想要探索不同数量的聚类,以查看如何在每个聚类内以最小的误差将数据分组在一起。为此,我们可以使用肘方法,这是简单的图表损失对 k,以及戴维斯-波尔丁分数。
下面我们将创建几个模型来执行肘法并得到戴维斯-波尔丁评分。您可以更改 low_k 和 high_k 等参数。我们的流程将在这两个值之间创建模型。还有一个名为 model_prefix_name 的参数。我们建议您保留其当前值。它用于为我们的模型生成命名约定。
*# Define upper and lower bound for k, then build individual models for each k.*
*# After running this loop, look at the UI to see several model objects that exist.*low_k = 3
high_k = 15
model_prefix_name = 'kmeans_clusters_'lst = list(range (low_k, high_k+1)) *#build list to iterate through k values***for** k **in** lst:
model_name = model_prefix_name + str(k)
makeModel(k, model_name)
print(f"Model started: **{**model_name**}**")
现在我们可以开始分析我们的模型了。这样做的目的是为我们的用例确定正确的模型。
*# This will create a dataframe with each model name, the Davies-Bouldin Index, and Loss.*
*# It will be used for elbow method and to help determine optimal K*df = pd.DataFrame(columns=['davies_bouldin_index', 'mean_squared_distance'])
models = client.list_models(DATA_SET_ID) *# Make an API request.*
**for** model **in** models:
full_model_id = f"**{**model.dataset_id**}**.**{**model.model_id**}**"
sql =f'''
SELECT
davies_bouldin_index,
mean_squared_distance
FROM ML.EVALUATE(MODEL `**{**full_model_id**}**`)
'''job_config = bigquery.QueryJobConfig()*# Start the query, passing in the extra configuration.*
query_job = client.query(sql, job_config=job_config)
df_temp = query_job.to_dataframe()
df_temp['model_name'] = model.model_id
df = pd.concat([df, df_temp], axis=0)
现在,我们将绘制我们的值与聚类数的关系图,并得到一些值得分析的东西!
*# Plot the dataframe above*df['n_clusters'] = df['model_name'].str.split('_').map(**lambda** x: x[2])
df['n_clusters'] = df['n_clusters'].apply(pd.to_numeric)
df = df.sort_values(by='n_clusters', ascending=**True**)
df.plot.line(x='n_clusters', y=['davies_bouldin_index', 'mean_squared_distance'])
作者图片
如果您选择了自己的数据,或者如果您使用了一组不同的初始化标准,那么您可能会得到一些不同的值。如果您希望一致地返回相同的集群进行延伸运行,您可以通过超参数选择明确选择您的初始化。
上图中有几个标注值得讨论。首先从我们的橙色线开始(loss vs n_clusters)。我们看到,一般来说,随着集群的增加,我们的损失会减少,这是意料之中的。该线也以相对稳定的速度下降。这很常见,也是为什么用肘法不够的原因。偶尔,你可能会惊喜地发现,在某个点上,损耗变平了,形成了肘部形状。如果是这样,这表明连续的集群没有提供额外的价值。
因为我们看不到这个形状,我们可以继续看蓝线,戴维斯-波尔丁指数。一个简单的描述是,您可能希望使用该得分最低的聚类数。我们看到,在 k=5 时,我们有一个相对较低的分数,直到 k=10 时才被击败。我们决定评估 10 个独立的消费者群是没有意义的,因此我们将在 k=5 处结束。
分析我们最后的集群
如前所述,您可以在前端检查模型性能。我们建议您从这里开始,但是,有些用例需要以编程方式检索结果。为了做到这一点,使用 ML.CENTROIDS。
model_to_use = 'kmeans_clusters_5' *# User can edit this*
final_model = DATA_SET_ID+'.'+model_to_usesql_get_attributes = f'''
SELECT
centroid_id,
feature,
categorical_value
FROM
ML.CENTROIDS(MODEL **{**final_model**}**)
WHERE
feature IN ('OS','gender')
'''job_config = bigquery.QueryJobConfig()*# Start the query*
query_job = client.query(sql_get_attributes, job_config=job_config)
df_attributes = query_job.result()
df_attributes = df_attributes.to_dataframe()
df_attributes.head()
我们还可以使用下面更多的 SQL 来计算额外的汇总统计数据。两种 ML 的组合。预测和 ML。质心是有帮助的。
*# get numerical information about clusters*sql_get_numerical_attributes = f'''
WITH T AS (
SELECT
centroid_id,
ARRAY_AGG(STRUCT(feature AS name,
ROUND(numerical_value,1) AS value)
ORDER BY centroid_id)
AS cluster
FROM ML.CENTROIDS(MODEL **{**final_model**}**)
GROUP BY centroid_id
),Users AS(
SELECT
centroid_id,
COUNT(*) AS Total_Users
FROM(
SELECT
* EXCEPT(nearest_centroids_distance)
FROM
ML.PREDICT(MODEL **{**final_model**}**,
(
SELECT
*
FROM
**{**final_view.full_table_id.replace(":", ".")**}**
)))
GROUP BY centroid_id
)SELECT
centroid_id,
Total_Users,
(SELECT value from unnest(cluster) WHERE name = 'Apparel') AS Apparel,
(SELECT value from unnest(cluster) WHERE name = 'Office') AS Office,
(SELECT value from unnest(cluster) WHERE name = 'Electronics') AS Electronics,
(SELECT value from unnest(cluster) WHERE name = 'LimitedSupply') AS LimitedSupply,
(SELECT value from unnest(cluster) WHERE name = 'Accessories') AS Accessories,
(SELECT value from unnest(cluster) WHERE name = 'ShopByBrand') AS ShopByBrand,
(SELECT value from unnest(cluster) WHERE name = 'Bags') AS Bags,
(SELECT value from unnest(cluster) WHERE name = 'productPrice_USD') AS productPrice_USD,
(SELECT value from unnest(cluster) WHERE name = 'hhi') AS hhiFROM T LEFT JOIN Users USING(centroid_id)
ORDER BY centroid_id ASC
'''job_config = bigquery.QueryJobConfig()*# Start the query*
query_job = client.query(sql_get_numerical_attributes, job_config=job_config) *#API Request*
df_numerical_attributes = query_job.result()
df_numerical_attributes = df_numerical_attributes.to_dataframe()
df_numerical_attributes.head()
我们现在可以开始理解集群是如何基于上面提供的值构建的。我们看到以下内容:
第一类:服装购物者,他们也比平常购买得更多。这一部分(尽管是合成数据)偏向女性。
集群 2:最有可能按品牌购物,并对包感兴趣。该细分市场的平均购买量低于第一个集群,但是,这是价值最高的客户。
集群 3:人口最多的集群,这个购买量小,平均花费少。这部分人是一次性购买者,而不是品牌忠诚者。
群组 4:对配件最感兴趣,购买频率不如群组 1 和群组 2,但购买频率高于群组 3。
群组 5:这是一个异常值,因为只有 1 个人属于这个群组。
通过单击模型,还可以从 BigQuery 的 UI 中获得简单的输出。输出如下所示。
“模型评估”选项卡提供汇总统计数据
使用模型对新网站行为进行分组,然后将结果推送到 Google Analytics 360 进行营销激活
在我们有了一个最终确定的模型后,我们想用它来进行推理。下面的代码概述了如何对用户进行评分或将其分配到集群中。这些被标记为质心 ID。虽然这本身是有帮助的,但我们也推荐一个将这些分数吸收回 GA360 的过程。将您的 BigQuery ML 预测导出到 Google Analytics 360 的最简单方法是使用调制解调器(营销模型部署,https://github.com/google/modem)。MoDeM 帮助您将数据加载到 Google Analytics 中,以便最终在 Google Ads 中激活,显示&视频 360 和搜索广告 360。
sql_score = f'''
SELECT * EXCEPT(nearest_centroids_distance)
FROM
ML.PREDICT(MODEL **{**final_model**}**,
(
SELECT
*
FROM
**{**final_view.full_table_id.replace(":", ".")**}**
LIMIT 1))
'''job_config = bigquery.QueryJobConfig()*# Start the query*
query_job = client.query(sql_score, job_config=job_config) *#API Request*
df_score = query_job.result()
df_score = df_score.to_dataframe()df_score
新的得分用户
清理:删除模型和表
下面的简单过程将删除所有模型和表格
*# Are you sure you want to do this? This is to delete all models*models = client.list_models(DATA_SET_ID) *# Make an API request.*
**for** model **in** models:
full_model_id = f"**{**model.dataset_id**}**.**{**model.model_id**}**"
client.delete_model(full_model_id) *# Make an API request.*
print(f"Deleted: **{**full_model_id**}**")*# Are you sure you want to do this? This is to delete all tables and views*tables = client.list_tables(DATA_SET_ID) *# Make an API request.*
**for** table **in** tables:
full_table_id = f"**{**table.dataset_id**}**.**{**table.table_id**}**"
client.delete_table(full_table_id) *# Make an API request.*
print(f"Deleted: **{**full_table_id**}**")
把一切都包起来
在这个练习中,我们用 BigQuery ML 中的 k-means 完成了一些很酷的事情。最值得一提的是,我们能够将在线和离线用户级别的信息结合起来,以便更深入地了解我们客户的整体情况。我们已经对用户行为进行了建模,并详细介绍了一种确定最佳集群数量的方法。我们能够通过推理将这种洞察力应用到未来的行为中。最后,我们可以将这个推断分数导入 GA360,用于未来的营销活动。
想要更多吗?
请留下您的意见和任何建议或更正。
我是 Tai Conley,谷歌云平台的 AI/ML 专家客户工程师。你可以在 LinkedIn 上找到我。
感谢审稿人 : Abhishek Kashyap,Polong Lin 和 Oly Bhaumik。
如何构建让你作为数据科学家感到自豪的代码
入门
让你在竞争中脱颖而出
人们普遍认为数据科学家无法写出清晰易懂的代码。你不一定要这样。通过学习一些如何正确编写代码的原则,你可以将这种模式化运用到你的优势中,让你在竞争中脱颖而出。
为了找到最佳实践,我们应该只看软件工程的可靠实践。以下是一些软件工程原则,可以让你像数据科学家一样编写干净、易读、易操作的代码:
阅读文档
当谈到使用新技术时,我看到数据科学家犯的最大错误之一是忽略文档。工具或库的文档只是为了帮助用户更容易地将工具应用到他们的工作中。
当用户没有查阅文档时,开发阶段可能会花费太长时间,或者库可能没有被充分利用。
因此,当您准备使用一个从未使用过的工具时,请务必阅读并理解文档。这会让你的生活更轻松。
编写文档
既然说到这个话题,我也必须强调写文档的重要性。您可能没有开发出将被成千上万人使用的最先进的代码,但是简单地记下您的代码中的所有内容就足够了。
这对那些在你之后使用你的代码的人,对你的队友,对你几年后回头看你写的东西的时候都是有用的。
你的文档不必看起来超级专业。只要一个 word 文档描述这段代码做什么,输入是什么,输出是什么就足够了。
评论评论评论
类似于创建文档,对代码本身进行注释是一个救命稻草。养成一个习惯,在你写的每个函数的开头加上一两句话,解释这个函数做什么,预期的输入和输出是什么。如果函数中有一些复杂的逻辑,记下一些行做了什么。例如:“这行计算每列的平均值”,“这行删除平均值小于 10 的”,等等。
编码前设计
这不是颜色/布局设计。在设计中,您决定项目的总体流程。在我的脑海中,我将代码设计分为两类:
- 知道你的输入和输出需要什么:对于你写的任何东西,都会有一个输入和输出。决定什么将被输入到你的代码中。这可能是您的业务利益相关者的需求,或者是您可以在网上找到的 JSON 类型。例如“我将从互联网上下载一个 CSV 文件”或“我将发送到数据库的 json 格式的查询答案”等。
- 有目的地编写代码:当你开发代码时,在每一步,花些时间想想接下来会发生什么。你知道你更大的目标,但短期目标是什么?在项目的这个阶段,你想达到什么目的?在掌握数据科学方法上,规划步骤以内置任务的形式出现。在开始实现之前,您需要回答几个问题。以下是一名学生对这些作业的评价:
我发现,如果我不写下我的目标、假设或我想如何想象某件事的大纲……那么我的大脑很容易在设置这件事的过程中迷失或靠边站。这真的很容易忘记时间,甚至忘记我正在努力实现的具体具体的小步骤。喜欢这些有用的辅导问题!”—内森·埃克尔
清理您的代码
当然,说你需要清理你的代码有点抽象。我在这里的意思是,当你完成代码时,不要在代码中添加任何不必要的东西。这对数据科学家来说尤其是个问题。在 Jupyter 笔记本上,很可能尝试几行代码只是为了看看结果会是什么,然后忘记删除它,或者忘记跟踪哪些单元格是程序的一部分,哪些不再使用。
我所做的就是打开一个草稿本和一个最终本。我从在草稿本上编码开始。我在草稿笔记本上尝试了所有我想尝试的东西,并一步一步地实现了这些功能。而且每次我完成项目的一个阶段(比如数据探索),我都会审核并把代码转移到最终的笔记本上。这样,我总是保持我的代码有条理,在最终的笔记本上没有不必要的代码行。这也有助于将错误降到最低,因为我会在每个阶段结束时检查我的代码。
遵循命名惯例并清楚地命名
遵循严格的命名准则是软件工程中的一件大事。例如,根据您使用的语言或您工作的团队,这些可能是:
- 变量名应该以小写字母开头,变量名的第一个字母应该大写(soLikeThis)或
- 函数名应该以大写字母开头(FunctionNameXyZ)。
我不知道数据科学社区有严格的命名政策,老实说,我不认为强制执行这一政策是可行的。尽管如此,我认为与你自己的命名保持一致是个好主意。
只要决定你的风格是什么。在命名变量、类和函数时,有两种选择:大写字母(NewVariable)或无大写字母(newVariable)、下划线(new_variable)或无下划线(newVariable)。为每一种选择一种风格,并坚持下去。这将使你的代码更容易理解。
不过有一件事我很严格,那就是明确命名。你应该根据它们的角色来命名你的变量、函数和类,而不是随机的。数据帧 1 不可接受。尤其是当数据框在您的代码中起着关键作用时。这并不意味着您应该将其命名为 data frame _ where _ location _ and _ time _ is _ merged _ for _ model _ training。保持简短,让阅读你的代码的人能够理解。但是话说回来,当你获得一些经验时,命名会变得更容易,所以不要为了让它尽善尽美而给自己太大压力。
保持一个整洁的文件结构
根据我的观察,数据科学家倾向于下载数据,启动笔记本,编写代码片段,并把它们放在桌面上,只有在真正需要的时候,他们才会把所有东西放在一个文件夹中。这不仅看起来不专业,也是生产力的杀手。当他们知道他们需要首先在上周随机创建的 10 个笔记本中找到他们需要工作的笔记本时,他们想要开始工作。
将所有项目文件保存在一个地方。为你的数据准备一个文件夹,并清楚地给你的笔记本命名,这样你就知道哪一个是你需要处理的。仅此而已。没什么特别的。只要在项目开始的时候就开始这个结构,这样你就不用花几个小时去整合所有的东西。
使用版本控制
版本控制,或者换句话说,把你的代码保存在 GitHub 上会帮助你自信地做出改变,而不用害怕破坏一切。它将帮助你跟踪什么改变了,它将使你的代码在某个地方的服务器上保持愉快和舒适,这样它就不会仅仅因为有人离开你的公司而被删除。
我所说的“使用版本控制”不仅仅意味着你应该设置它。你也应该在你的项目中积极地使用它。每当你完成项目的一个新的部分时,把你的改变推送到 Git。让上传你的改变和工作成为一种日常习惯。这样,一切都保持良好和有组织的状态,对于新加入你的团队的人来说,继续工作是微不足道的。最重要的是,当某些东西崩溃时,恢复到最新的工作版本是很容易的。
有效地使用调试
调试是在代码中出现问题时找出问题所在的能力。如果没有编码背景,这是新数据科学家最常见的技能之一。但这没问题,因为就像其他事情一样,这只是一项你需要学习的技能。
调试不仅仅是盯着你的屏幕试图找出问题所在。您需要知道如何与您的代码交流,以及如何理解它在抱怨什么。这可能包括:改变变量值,注释掉行,打印变量值,看看是否一切如你所愿。如果没有,这意味着有问题,你应该解决它。
如果我能告诉你一件关于调试的事情,那应该是:调试时一次只改变一件事。当你考虑它的时候,它是科学的。如果你想知道为什么有些事情不工作,你有 5 个不同的变量,你需要一个一个地改变它们,看看哪一个影响结果。如果你同时改变两个,并看到结果的变化,你不能推断哪个是导致变化的原因。听起来很简单,但许多新数据科学家犯了一个错误,他们太急于修复错误,反过来让他们的调试持续更长时间。
总而言之,说到编码,没有人是完美的。但是如果你遵循软件工程师使用的一些简单规则,你将会改进你的编码。一旦你开始把每天编码作为你工作的一部分,你的习惯将会是最重要的,还有什么更好的方法来获得更好的习惯,然后从今天开始适应最佳实践!
🐼想更多地了解熊猫吗? 获取我的免费熊猫小抄。
如何在 5 分钟内构建新冠肺炎数据驱动的闪亮应用
利用新冠肺炎数据中心构建闪亮的应用程序。一个 20 行代码的全功能示例。
编者按: 走向数据科学 是一份以数据科学和机器学习研究为主的中型刊物。我们不是健康专家或流行病学家,本文的观点不应被解释为专业建议。想了解更多关于疫情冠状病毒的信息,可以点击 这里 。
与新冠肺炎系统相关的数据库很多,但目前没有一个虚拟平台整合了这些来源的很大一部分。这样就很难进行全面的分析,也很难将这些医学信息与外部因素,尤其是社会政治因素联系起来。考虑到这一点,新冠肺炎数据中心旨在开发一个统一的数据集,有助于更好地了解新冠肺炎。
在本教程中,我们将使用到新冠肺炎数据中心的 R 包 COVID19 : R 接口构建一个简单而完整的闪亮应用程序。
假设对闪亮的(网络应用)和情节性的(互动情节)有基本的了解,但是可以简单地通过复制/粘贴来构建一个全功能的应用。加载以下软件包以开始使用:
library(shiny)
library(plotly)
library(COVID19)
新冠肺炎(新型冠状病毒肺炎)
COVID19 R 包通过covid19()
功能提供了与新冠肺炎数据中心的无缝集成。键入?covid19
获取参数的完整列表。这里我们将使用:
country
:国名或 ISO 代码的向量。level
:粒度级别;数据按(1)国家,(2)地区,(3)城市。start
:计息期的开始日期。end
:计息期的结束日期。
定义用户界面
定义以下输入…
country
:国家名称。请注意,选项是使用covid19()
功能自动填充的。type
:要使用的度量。其中一个是c("confirmed", "tests", "recovered", "deaths")
,但还有许多其他的可供选择。完整列表见此处。level
:粒度级别(国家-地区-城市)。date
:开始和结束日期。
…以及输出:
covid19plot
: plotly 输出,将渲染一个交互的 plot。
把一切都包装成一个fluidPage
:
# Define UI for application
ui <- fluidPage(
selectInput(
"country",
label = "Country",
multiple = TRUE,
choices = unique(covid19()$administrative_area_level_1),
selected = "Italy"
), selectInput(
"type",
label = "type",
choices = c("confirmed", "tests", "recovered", "deaths")
), selectInput(
"level",
label = "Granularity",
choices = c("Country" = 1, "Region" = 2, "City" = 3),
selected = 2
), dateRangeInput(
"date",
label = "Date",
start = "2020-01-01"
),
plotlyOutput("covid19plot")
)
服务器逻辑
在 UI 中定义了反馈输入之后,我们将这些输入连接到covid19()
函数来获取数据。以下代码片段显示了如何呈现交互式绘图(ly ),当任何输入发生变化时,该绘图会自动更新。请注意,covid19()
功能使用内部内存缓存系统,因此数据不会被下载两次。多次调用该函数是非常高效和用户友好的。
# Define server logic
server <- function(input, output) { output$covid19plot <- renderPlotly({
if(!is.null(input$country)){
x <- covid19(
country = input$country,
level = input$level,
start = input$date[1],
end = input$date[2]
)
color <- paste0("administrative_area_level_", input$level)
plot_ly(x = x[["date"]], y = x[[input$type]], color = x[[color]]) }
})
}
运行应用程序
函数shinyApp
从上面实现的ui
和server
参数构建一个应用程序。
# Run the application
shinyApp(ui = ui, server = server)
在https://guidotti.shinyapps.io/h83h5/有售
结束语
我们构建了一个与 R 包 COVID19 接口的简单应用程序,它代表了一个可重用的通用架构。示例应用程序可以用作更高级的新冠肺炎数据驱动应用程序的构建块。特别是,通过covid19()
功能获得的数据集包括关于新冠肺炎案例、政策措施、地理信息和外部关键字的额外指标,这些指标允许使用世界银行公开数据、谷歌移动报告、苹果移动报告和当地政府数据轻松扩展数据集。参见完整数据集文档和 COVID19 代码片段。
新冠肺炎数据中心是免费软件,没有任何担保。请确保同意使用条款。
[1]吉多蒂,即阿尔迪亚,d .(2020)。新冠肺炎数据中心,《开源软件杂志》,5(51):2376
如何构建决策智能软件
发布我的第一个产品的四个教训
我们淹没在数据中,努力应对我们可以越来越多地建模,但仍然难以解释的复杂问题。我们现在比以往任何时候都更需要在医疗、金融和基础设施等重要领域做出明智的决策。
软件在帮助我们理解世界方面发挥着关键作用。软件产品为我们提供了克服认知盲点、扩展认知和建立联系的方法。决策智能工具代表了我们周围信息的实际应用中的一个巨大飞跃。
应用新技术是棘手的部分。我们想要利用计算和算法进步的可扩展解决方案。这就是产品领导力的用武之地。我们需要指导软件的创建,这样用户就可以利用它来推动他们组织的进步。决策智能软件在提高公司绩效方面发挥着重要作用。利用最佳实践的产品领导者通过提供可被广泛采用的解决方案来塑造成果。
我们如何最好地利用新技术来提高我们决策的质量?
在本文中,我将阐述决策智能软件的重要性和意义,并根据我设计和推出新产品的经验,介绍构建决策智能软件时需要考虑的重要因素。我将讨论管理复杂系统(水网络)的决策者用户,但是这些技术适用于不同的领域。
我希望这些经验教训对开发分析、决策支持和其他面向数据的企业产品以提高决策质量的其他技术专业人员有所帮助。
课程
- 设计最大化人机合作关系
- 诬陷问题就是问题
- 为每个用户类型和数据流中的工作流程步骤量身定制
- 开放数据,方便连接(接口是关键)
总的来说,我们运用决策智能将信息转化为行动并改善结果。决策智能通常是数据科学、商业智能、决策建模和管理的结合。组合将取决于组织类型和问题背景。
在网络规划中,决策智能结合了三种方法。我们需要优化来生成最佳计划,需要数据科学来解释它们,需要决策分析来汇聚前进的方向。
这些网络是复杂的系统,需要产生复杂数据的软件。但是如果人们不能使用数据,那么数据又有什么用呢?
我定义了 Aperture 的愿景,这是我们今年在 Optimatics 推出的一款产品,它提供了一个由人工智能驱动的平台。Aperture 在优化结果和做出商业决策的用户之间提供了一座桥梁,因此他们充分认识到了多目标优化的好处。我们的算法允许用户考虑许多标准和决策,探索和评估数以千计的计划来改善他们的网络。有什么问题吗?没有一个最佳解决方案。
多目标优化解决方案探索过程。(图片由作者提供。)
用户从寻找解决方案到试图理解一系列策略,同时平衡相互竞争的目标。在这种平衡上的出色表现对我们的客户来说非常重要。他们的赌注很高,因为每个人都需要水。所以我们的挑战是:
我们如何将这些信息转化为行动?
用于优化结果的 Aperture 仪表盘。(图片由作者提供。)
用户需要一种筛选和理解优化数据的方法。为了满足这一需求,我们构建了 Aperture ,这是一个决策智能工具包,由交互式 web 仪表盘和基于云的数据平台组成。
技术只是等式的一面。如果一个系统很难建模,或者决策者努力实现相互竞争的目标,复杂性就会增加,这意味着没有一种方法是正确的。产品必须满足用户框架和决策的需要,需要决策者和软件之间的双向接口。为了实现人工智能提高决策质量的潜力,我们必须培养难以捉摸的人的因素。
(1)设计最大化人机伙伴关系
采用企业技术的主要驱动力是效率。客户希望增加他们的投资回报。优化是公司提升业绩的有效途径。
对于人类来说,分析结果数据的广度和深度以做出明智的决策通常是不可行的。算法可以获得人类通过纯粹的计算力无法获得的洞察力。但是我们这些普通人在我下面概述的简化的、迭代的工作流程的开始和结束阶段都扮演着关键的角色。
大局决策工作流程。(图片由作者提供。)
尽管预测分析令人兴奋,但机器学习只是整个人工智能的一种方法(优化器体现了搜索范式和规划问题域)。我认为人工智能最令人兴奋的前景是增强人类的判断力——帮助我们做我们不擅长的事情。当然,其中一部分是解析大量数据,但还有更多,比如扩展我们的视角。
我们需要分工和可解释的数据,以便决策者充分利用,在现实世界和数据流之间架起一座桥梁。
我们如何最好地利用这种伙伴关系?我们应该如何弥合分歧?
以我管理的产品 Optimizer 和 Aperture 为例,我们的决策堆栈包括工程判断和领域专业知识、应用的元启发式算法(发现工具)、高性能计算和用户界面。
我们可以将二分法简化为用户(人类)和工具包的计算端(算法)。我们遵循人类算法特异性的规则,让计算机做它们最擅长的事情,为我们的用户提供最大的价值。
- [ 框架仍然需要人类来框架这个问题。即使是最先进的 AI 也无法定义自己的目标函数。人类以目标和标准的形式定义决策选项——可能性的领域——和期望的结果。
- [ 探索 ]算法对信息进行编码,评估解决方案,解析模型和场景,将决策空间映射到目标空间,然后通过统计分析对数据进行后处理,如计算决策的概率和影响。
- [ 决定 ]在工作流程的最后,由人类决定。他们使用判断和数据工具来平衡权衡,发现数据,并在统计、可视化和界面的帮助下研究一种方法。
值得注意的是,并不是所有的知识都包含在可用的数据中。模型中没有捕捉到许多现实世界的考虑因素,算法无法感知或访问这些因素。这就是为什么只有像谷歌这样的组织能够有机地充分利用数据:因为它们是建立在数据之上的。噪音——过量或质量可疑的数据——也是一个重要问题。
一个主要的难点是翻译人和计算机之间的逻辑,以及以有用的方式提取信息的方法。我希望我们的软件能够解决这个问题,解决整个工作流程,充分利用合作关系来帮助我们的用户做出决策。但是要做到这一点,我们要从框架开始。
(2)框定问题就是问题
一开始,一片寂静。然后是噪音。
客户使用软件来解决问题。对于决策智能软件来说,定义问题不仅是起点,也是最关键的一点。其他一切都取决于框架:纳入了哪些数据,可用的替代方案,以及决策者用来衡量进展的绩效指标。软件是一种工具,提供控制和定位,有时甚至是导航。高质量的决策是目的地。
用户需要能够描述约束、优先级和可用资源。数据收集和随后的解释必须以最终目标为动机,从而产生更现实和有用的结果。该框架还应该包括参考点,以便用户进行有用的比较,并指示在结果方面将集中在哪里。
技术本身并不是好的。数据并非天生有用。决策智能产品的新浪潮必须帮助指导用户定义他们的框架。有效的软件应该使决策者能够考虑全局并以最小的摩擦定义目标。但是,考虑到用户类型和他们在旅程中所处的位置,设计考虑会有所不同。
(3)根据数据流中的每个用户类型和工作流程步骤定制分析
正如我们呼吁人类和机器之间的专业化一样,我们也需要构建界面和分析内容来匹配用户及其工作流程步骤。当然,用户的旅程取决于产品应用。
我们构建 Aperture 来处理整个规划工作流程。Aperture 允许技术用户发现、比较和检查策略及其影响。他们策划优化程序计划,以便执行用户可以决定行动计划,并将其提交给利益相关者。
Aperture 技术用户旅程示例。(图片由作者提供。)
我受到启发,扩展用户工作流,为数据流中的决策者创造价值,使客户能够实现其软件订阅的全部潜力。每个工作流程步骤都是一个具有自己分析的仪表板。我们的设计原则是,每一个都应该易于共享、交互、适应和互连。仪表板数据反映了最新的优化结果,一个界面组件或仪表板中的计划选择反映在所有其他组件或仪表板中。
设计始于用户动机。高管用户希望理解数据,以做出合理的业务决策。他们的动机是追求清晰和透明,并最终管理风险。战略计划需要协调执行,并且经常是迭代的,因此它们使用预算阈值等控制点,用新的信息或优先级来细化框架。这意味着我们必须开放我们的软件。
(4)开放数据,方便连接(接口是关键)
开放数据为决策智能技术提供了一个影响放大器。通过将更多用户引入他们的生态系统,科技公司可以提高参与度,提供更多价值,并扩展他们的解决方案。
现在,我所说的开放,并不是指所有人都可以自由进入,没有任何顾虑。关键是将复杂转化为清晰。数据发现必须以上下文为基础,故事和界面要适合用户和目的,突出的数据要过滤掉可行性和噪音。所有这些都强调了监管的重要性——我将在下一篇文章中探讨这一点——以增加信息的相关性、有用性和可访问性
当着手开发后来成为 Aperture 的产品时,我注意到,对于每一个使用 Optimizer 的人来说,都会有更多的技术用户使用它产生的数据。对于这些技术用户中的每一个,下游会有其他利益相关者和决策者想要检查并提供反馈。
因此,我们有机会与从建模师和规划者到公用事业公司高管和公众的广大用户接触并向他们传递价值。为了实现这一目标,我们需要扩展我们的软件产品和工作流,为用户提供协作和共享计划的机会。是时候通过向一系列决策者开放规划过程来开放 Optimatics 了。
我们非常依赖数据可视化、映射和汇总统计来开放数据(大声喊出 d3.js 、传单、熊猫和 scikit-learn )。我们的客户可以解释、实施和交流优化结果,从而在规划水网络时提高决策质量。但是每个产品领导者都会根据他们用户的需求组装他们自己的工具包。
一年前,我开始着手解决一个问题:帮助我们的客户理解他们的数据,以提高他们商业决策的质量。我最终发现了创建一个平台的机会,这个平台可以促进数据、工具和人之间的联系。我们通过开放产品来扩大我们产品的影响,现在随着我们的用户能够获得更丰富的见解,我们的产品得到了更广泛的采用。我希望您会发现这些课程对您自己的产品开发之旅有用。
软件和人工智能可以通过有价值的数据来帮助对抗熵。但是这需要深思熟虑的产品设计和工程来实现。创新工具改善重要领域复杂决策的潜力从未如此之大。我们去建吧。
如有任何问题或意见,请随时给我留言或评论。
请继续关注我的下一篇文章,关于构建决策智能产品用于数据故事和管理。它将涵盖我们如何通过允许用户集合观点、聚合知识、将信息转化为决策、将决策转化为行动来创造可扩展的价值。
如何构建决策树
使用 Python 示例从头构建简单的决策树
决策树是数据科学中进行预测的一种流行且强大的方法。决策树也形成了其他流行的集成方法的基础,如 bagging、boosting 和 gradient boosting。它的流行是由于该技术的简单性使其易于理解。我们将讨论为几个分类问题构建决策树。首先,让我们从一个简单的分类例子开始解释决策树是如何工作的。
代码
虽然本文主要描述构建和使用决策树的细节,但是在 my GitHub 中可以找到用于拟合决策树、使用决策树进行预测以及打印绘制决策树的点文件的实际 Python 代码。
简单的例子
假设我们有 10 个不同宽度和高度的矩形。五个长方形是紫色的,五个是黄色的。数据如下所示,X1 表示宽度,X2 表示高度,Y 表示紫色矩形的等级 0 和黄色矩形的等级 1:
矩形数据
画出矩形,我们可以很清楚地看到不同的类。
矩形图
基于矩形数据,我们可以构建一个简单的决策树来进行预测。决策树由决策节点和叶节点组成。在下面的决策树中,我们从最上面的方框开始,它代表了树的根(一个决策节点)。根中的第一行文本描述了基于宽度(X1)小于 5.3 来拆分树的最佳初始决策。第二行代表最初的基尼系数,我们将在后面详细讨论。第三行代表初始水平的样本数,在本例中为 10。第四行表示节点的每个类中的项数——紫色矩形为 5,黄色矩形为 5。
矩形决策树
在按小于 5.3 的宽度(X1)分割数据后,我们得到两个叶节点,每个节点中有 5 个项目。所有紫色矩形(0)都在一个叶节点中,所有黄色矩形(1)都在另一个叶节点中。它们相应的基尼系数、样本大小和数值都会更新,以反映这种差异。
在这个非常简单的例子中,我们可以通过简单地检查矩形的宽度是否小于 5.3 来预测给定的矩形是紫色还是黄色。
基尼指数
构建决策树的关键是确定每个决策节点的最优分割。使用上面的简单例子,我们如何知道在 5.3 的宽度(X1)处分割根?答案在于基尼指数或分数。基尼指数是一个用来评估收入差距的成本函数。其定义如下:
所有类的 p(1-p)之和,其中 p 是一个节点中一个类的比例。由于 p 的和为 1,所以公式可以表示为 1 — sum(p 的平方)。基尼指数计算随机选择时某一特定特征被错误分类的概率,在 0 到 0 . 5 之间变化。
使用我们简单的 2 类示例,根节点的基尼指数是(1 — ((5/10) + (5/10) )) = .5 —矩形在 2 类中的平均分布。所以这个节点上 50%的数据集被错误分类。如果基尼系数是 0,那么在这个节点上我们的数据集将 100%被正确分类(0%不正确)。我们的目标是使用最低的基尼系数来构建决策树。
确定最佳分割
为了确定最佳分割,我们需要遍历所有特征,并将相邻训练样本之间的中点视为候选分割。然后,我们需要评估分割的成本(基尼系数),并找到最佳分割(最低基尼系数)。
让我们看一个计算某项功能的基尼系数的例子:
- 将 X1 按升序排序,我们得到第一个值 1.72857131
- 让我们计算 X1 = 2.771245 的基尼系数
- 对于类别 0,拆分是向左 1,向右 4(一个项目<= 2.771245, four items > 2.771245)
- 对于类别 1,拆分为左侧 0 和右侧 5(零项目<= 2.771245, five items > 2.771245)
- 左侧基尼系数为(1 — ((1/1) + (0/1) ) = 0.0
- 右侧基尼系数为(1 — ((4/9) + (5/9) ) = 0.49382716
- 拆分的基尼是左右两边的加权平均值(1 * 0) + (9 * 0.49382716) = .4444444
对每一行运行该算法,我们可以得到每个特征的所有可能的基尼系数:
基尼系数
如果我们看基尼系数,最低的是 0.0000,X1 = 6.642(1 级)。我们可以使用 6.642 作为阈值,但更好的方法是使用小于 6.642 的相邻要素,在这种情况下,X1 = 3.961(类 0),并计算中点,因为这表示两个类之间的分界线。所以,中点阈值是(6.642 + 3.961) / 2 = 5.30!我们的根节点现在完成了 X1 < 5.30, a Gini of .5, 10 samples and 5 in each class.
Building the Tree
Now that we have the root node and our split threshold we can build the rest of the tree. Building a tree can be divided into two parts:
- Terminal Nodes
- Recursive Splitting
终端节点
终端节点或叶子是决策树分支上的最后一个节点,用于进行预测。我们如何知道何时停止生长决策树?一种方法是显式地声明树的深度——在我们的例子中,将深度设置为 1。第一次分裂后,我们停止构建树,两个分裂的节点变成了树叶。更深层次的树会变得非常复杂,使数据过度拟合。
一棵树停止生长的另一种方式是一旦基尼系数为 0——那么就没有必要再分裂了。在我们的示例中,两个叶子的深度为 1,基尼系数为 0,因此实现终止的两种方法都满足。如果我们看终端节点,我们可以看到我们的预测。如果矩形的宽度(X1)小于 5.30,那么移动到树的左边,我们看到预测的类是 0 或紫色矩形。如果矩形的宽度(X1)大于 5.30,那么移动到树的右边,我们看到预测的类是 1 或黄色矩形。
矩形决策树
递归分割
现在我们知道了何时停止构建决策树,我们可以递归地构建该树。一旦我们有了根节点,我们就可以递归地左右分割节点,直到达到最大深度。我们从简单的例子中获得了所有的基本构件,但是为了演示递归分割,我们需要一个更复杂的例子。让我们使用著名的、有点累的 Iris 数据集,因为它在 scikit 中很容易获得,用于比较目的。
通过绘制虹膜数据,我们可以清楚地看到三个类别(Setosa、Versicolor 和 Virginica ),涵盖四个特征中的两个——萼片长度和花瓣长度:
虹膜数据集-萼片长度 x 花瓣长度
让我们递归地创建一个决策树,看看结果是什么样的。
Iris 决策树
在根节点我们有第一个花瓣长度的分割< 2.6 creating a leaf node with a Gini of 0.0 for Setosa and a decision node requiring a new split. We can clearly see the Setosa split in the graph at the midpoint between Setosa and Versicolor (petal_length = 2.6). Since the Gini is 0 for the left node, we are done and the leaf node is created just as we did with our rectangles. On the right side however, our right node has a Gini of .495. Since we have the depth set to 5, we recursively split again on the right node. This continues until we hit a depth of 5, producing the decision tree we see in the graph.
Pruning a Decision Tree
One downside of decision trees is overfitting. With enough depth (splits), you can always produce a perfect model of the training data, however, it’s predictive ability will likely suffer. There are two approaches to avoid overfitting a decision tree:
- Pre-pruning — Selecting a depth before perfect classification.
- Post-pruning — Grow the tree to perfect classification then prune the tree.
Two common approaches to post-pruning are:
- Using a training and validation set to evaluate the effect of post-pruning.
- Build a tree using a training set, then apply a statistical test (error estimation or chi-squared test) to estimate whether pruning or expanding a particular node improves the results.
I welcome constructive criticism and feedback so feel free to send me a private message.
Follow me on Twitter @The_Jim_King
这篇文章最初出现在我的网站上
参考代码和数据的文章包括:
机器学习精通——如何用 Python 从零开始实现决策树算法,杰森·布朗利 2019 年 12 月 11 日
GitHub—Joachim valente/决策树-cart,2019 年 10 月 8 日
从头开始构建和部署一个无人监督的学习烧瓶应用程序
学习构建一个部署在 Heroku 上的 web 应用程序,它使用 k-means 算法从上传的图像中挑选调色板。
图片来源:蒂娜·库珀
在这篇博文中,我将详细解释如何设置调色板生成器 web 应用程序。我将介绍设置 flask 应用程序架构的基础知识,用 python 构建 k-means 无监督学习算法的后端,用 html、css 和 javascript 构建前端。我还将介绍使用 jQuery 库连接前端和后端,以及将最终的应用程序部署到 Heroku。
这是我们将要构建的一个快速演示。
调色板生成器应用程序
选择图像文件
获取调色板
该项目的完整代码可以在我的 GitHub 上找到:
此时您不能执行该操作。您已使用另一个标签页或窗口登录。您已在另一个选项卡中注销,或者…
github.com](https://github.com/Saffafatima12/palette-generator-app)
你准备好了吗?让我们开始吧!
设置基本的 flask 应用程序架构
设置一个基本的 flask 应用程序很简单。只需遵循以下步骤:
- 用您的应用程序的名称创建一个文件夹。我称之为 demo_app
- 打开命令行并安装 flask 模块
- cd 到 demo_app 文件夹中,创建一个名为 application 的文件夹(所有的前端都在这个文件夹中)
- 现在创建。flaskenv、config.py、main.py、k-means.py 和 requirements.txt 文件(现在还不用担心这些奇怪命名的文件。我将在后面的段落中详细解释每个文件的功能)。
- 在应用程序文件夹中创建一个 routes.py 文件和一个 init。py 文件。另外创建两个文件夹 static 和 template。在 static 中,为 css、图像和 javascript 创建子文件夹。在 templates 文件夹中创建一个名为 includes 的子文件夹。
- 还可以使用以下命令安装 virtualenv 模块:pip install virtualenv。
- 现在要创建一个虚拟环境文件夹,在命令行中运行命令 py -m venv venv。这将在您的 demo_app 的根目录下创建一个名为 venv 的虚拟环境文件夹。
完成后,您的 demo_app 的结构应该如下所示:
demo_app 文件夹
应用程序文件夹
现在我们可以将注意力转向填充我们刚刚创建的文件。
**The。flaskenv 文件:**在这个文件中,我们将把 flask 环境变量设置为 development,并将该文件链接到 main.py 文件。
**main . py 文件:**在这个文件中,我们将从应用程序文件夹中导入我们的 flask 应用程序。
**config . py 文件:**在这个文件中,我们将为 cookies 创建一个密钥。一旦我们在 Heroku 上部署我们的应用程序,这将非常重要。
k-means.py 文件:我们的 k-means 算法将进入这个文件。
requirements.txt 文件:在这个文件中,您将记录应用程序运行所需的所有依赖关系。要填充这个文件,您首先需要进入虚拟环境。您可以通过简单地 cd 到根目录并从命令行运行 venv/scripts/activate 来实现这一点。现在在 venv 中运行 pip freeze > requirements.txt 来记录你当前所有的依赖关系。请记住,在构建应用程序时,您需要更新此文件及其所有依赖项。你可能会注意到烧瓶没有安装在 venv 内。只需使用 pip install flask 命令安装它,并更新 requirements.txt 文件。
_ _ init _ _。py 文件:这里我们只是初始化 app 对象并将其连接到 routes.py 文件
routes . py 文件:这里我们定义当用户路由到某个网址时运行的函数。为了检查一切是否正确,定义一个函数“run ”,返回字符串“flask app 正在运行!”当用户路由到本地服务器地址后的“/”时。
返回命令行(确保您在虚拟环境中,并运行命令‘flask run’。您应该得到一个输出,提供您的应用程序运行的地址。将它复制并粘贴到您的浏览器中,您应该会得到以下输出。
flask app 正在运行!
如果到目前为止一切正常,那么恭喜你!现在,您已经准备好进入构建调色板生成器应用程序的下一个阶段。
编写 k-means 算法文件并将其连接到应用程序
现在我们可以开始为调色板生成器应用程序编写 k-means 算法了。
K-means 算法是一种非常简单的无监督机器学习技术。该算法采用 n 个数据点,并根据数据点和聚类质心之间的距离将它们划分为 k 个聚类。该算法旨在最小化每个数据点到其所属聚类的质心的距离。聚类的数量通常由算法的外部输入来指定。对于调色板生成器应用程序,我们希望算法能够识别相似颜色的像素簇。
我们首先导入所有必要的依赖项,并创建一个 get_points 函数。get_points 函数接收一个图像,将其大小调整为 200×400 像素,并将其转换为 RGB 格式。然后,该函数为预处理图像中的每个像素创建一个 rgb 元组列表。下面给出了带注释的代码。
现在,首先我们将创建两个名为 Point 和 Clusters 的类,它们的属性如下面的代码所示。然后,我们将创建一个函数,根据两个像素的 rgb 值来计算它们之间的欧几里德距离。
现在,我们必须创建一个包含一些函数的 k-means 类。我们将首先有一个初始化器,其中包含该类的属性,即类的数量和每个类的最小差异。
然后,我们创建 calculate_center 函数,它主要计算一个聚类的平均 r,g,b 值,并将其转换为具有这些值的点类。
assign_points 函数计算图像中每个点与每个聚类的质心之间的欧氏距离,并将该点分配给与该点距离最小的聚类。它返回每个聚类的点列表。
拟合函数使用先前定义的函数从图像中的点列表中生成聚类。
最后,我们将使用我们到目前为止定义的所有东西来构造 get_colors()函数。该函数将接受一个图像和聚类数作为输入参数,并返回颜色的十六进制值列表。列表的长度将等于输入的聚类数。
用 html 和 css 构建前端
现在我们可以用 html 和 css 创建前端了。在模板文件夹中,创建一个 index.html 文件。您可以选择在 index.html 文件中编写完整的 html 代码,或者只在 index.html 文件中保留基本的 html 框架,而在 includes 文件夹中的小 html 文件中编写其余的代码。我更喜欢后一种选择,因为当代码是小片段而不是一个大文件时,编写和调试代码要容易得多。每当您希望将代码包含在一个小文件中时,只需将所有代码复制到新创建的文件中,并在 index.html 文件中包含{ % include ’ file _ path _ to _ your _ html _ file ’ % }。标题的例子是:
{ % include " Includes/header . html " % }。
对于 html,您需要创建三样东西:
- 允许用户上传图像的文件选择器
- 一个滑块/文本框,允许用户在调色板中输入他们想要的颜色数量
- k-means 算法输出中的颜色显示。
使用 css 添加一些样式。将 css 文件保存在静态文件夹的 css 文件夹中,并在 index.html 文件中引用它们。
您还可以通过链接您的。js 文件在 java script 文件夹中静态保存到 index.html 文件中。
使用 j-Query 连接前端和后端
现在您已经构建了前端,我们可以开始使用 routes.py 文件了。
在 route.py 文件中,我们将添加一个在路由到服务器地址时呈现页面的函数。
现在,我们需要有一种方法将用户输入传递给 k-means 算法,并将算法的结果返回给用户。我们将假设从页面中获得一个名为 message 的 json 对象,它有两个键:image 和 name。image key 的值是 base 64 编码的图像,而 name 的值是用户输入的颜色数。我们将把图像对象解码成 Python 可以处理的格式,然后将它传递给从 k-means.py 文件导入的 fit()函数。然后,我们将使用输出,如果你记得是一个十六进制代码的颜色列表,并将它转换成一个名为响应的字典。然后,我们将使用 jsonify 函数将字典转换成 json 对象。
完成 routes.py 文件后,我们可以将注意力转向 index.js 文件,我们将在其中编写所有的 jQuery 代码。
好的,jQuery 方面发生了很多事情,我将尽力以一种容易理解的方式来解释它。
我的想法是读取图像输入,并在应用程序上显示图像。当用户按下“Get color palette”按钮时,k-means 算法的输出用于显示颜色的名称以及调色板区域中的圆圈填充。
为此,我们首先需要定义一个函数来读取上传的图像。要理解下面的代码,您需要对 jQuery 有一些基本的了解。你的代码很有可能和我的不一样,因为我的 html 代码和你的 html 代码不一样,但是我觉得大致思路应该是一样的。因此,该函数基本上是在加载图像文件时读取图像文件输入,并从图像中剥离元数据。我已经为图像插入了一个 console.log()函数,只是为了看看是否一切正常。
我们还需要有一个功能来处理颜色和颜色名称的显示后,获得调色板按钮被点击。为此,我想出了下面的代码。
代码生成了我们在 routes.py 文件中使用的名为 message 的 json 对象。代码还引入了 routes.py 文件中的响应 json 对象,用于更改圆圈的颜色,并在应用程序的调色板区域显示颜色名称。
您可以对 jQuery 代码进行更多的调整,以进一步定制应用程序。
在 Heroku 上部署应用程序
一旦您对 jQuery 感到满意,您就可以将您的应用程序部署到 Heroku。为了部署到 Heroku,我们需要在根目录中创建一个 Procfile。
在您的虚拟环境中安装 gunicorn 包。
您还需要用以下代码创建一个 wsgi.py 文件:
另外,记得更新 requirements.txt 文件中所有添加的依赖项,并将发布地址更新为“您的应用程序名称”。herokuapp.com
现在用 git init 和 git add 命令创建一个 git 存储库(您也可以在早期创建它,并跟踪您在项目中所做的更改)
为了将内容上传到 Heroku,请在 Heroku 上创建一个帐户,并在您的机器上安装 Heroku CLI。通过键入 heroku login 并按照说明进行操作,从命令行登录 Heroku。完成后,使用命令创建一个新的 Heroku 应用程序:heroku create 。现在使用 git 命令将您的应用程序推送到 heroku:git push heroku master。您应该会收到一个很长的输出,包括您的应用程序的网址。
如果发现错误,可以使用 heroku logs-tail 命令进行调试。一旦你排除了任何错误(或者幸运的是没有任何错误),导航到网页找到你的应用程序部署在它的全部荣耀!
调色板生成器应用程序
鸣谢:
我的 k-means 算法的代码来自一个神奇的 Colab 笔记本,可以在 Venelin 的网站上找到:https://www.curiousily.com/。你绝对应该看看维尼林的博客。太棒了!😃
如何建立有效的人机交互:对机器学习和软件工程的思考
图片来源:微软研究院
为了帮助从业者设计更好的面向用户的基于人工智能的系统,微软最近发布了一套基于数十年研究并通过各种人工智能产品的严格用户研究验证的人机交互指南。这些指南涵盖了广泛的交互:从用户最初被引入人工智能系统开始,扩展到持续的交互,人工智能模型更新和改进,以及管理人工智能故障。
虽然指南描述了人工智能从业者应该创造什么来支持有效的人-人工智能交互,但这一系列帖子解释了如何在基于人工智能的产品中实施指南。**
我们关注机器学习和软件工程的含义,因为在大多数情况下,实施任何指南都不能仅仅通过前端接口调整来支持。例如,如果算法本身是不透明的,解释算法的行为(准则 11)是不可能的。在另一个例子中,如果记录基础设施和设备没有跟踪正确的信号,或者如果该信息是有噪声的,则根据用户的当前环境识别和定制信息(准则 3 和 4)可能是不可能的。
这一系列的帖子提出了机器学习从业者和软件工程师今天可以采取的行动,以实现指导方针所描述的有效的人机交互。
- 第一部分讨论了在用户与人工智能系统的初始交互过程中,你可以用来帮助他们设定正确期望的方法。
- 第二部分将讨论如何基于上下文处理和定时服务。
- 第三部分将集中在当前的方法,以减轻偏见和适应用户的社会规范。
- 第四部分将把注意力转移到系统可能出错时的处理情况。
- 第五部分将扩展支持交互体验的含义。
第一部分:最初设定正确的期望
在这一部分中,我们重点关注在人和人工智能系统之间的初始交互过程中设置正确期望的两条准则:
准则 1 :明确系统能做什么。
准则二:明确系统能做什么,做得有多好。
对于任何面向用户的软件来说,明确系统的功能和限制是非常重要的。这对于基于人工智能的系统来说可能尤为重要,因为人们通常对自己的能力抱有不切实际的期望。传递这些信息的传统技术包括用户手册、内置文档和上下文帮助。不幸的是,今天我们仍然不知道如何为人工智能软件生产这样的工件。以一个问题回答系统为例,比如你最喜欢的个人助理(比如 Alexa、Cortana、Google Assistant、Siri)。我们能精确描述它在哪些领域能回答问题,哪些领域不能吗?如果是这样,我们是否总是知道它在每个域中有多精确?如果助手能够回答历史问题,是否意味着它也能回答地理问题?机器学习工程师能自信地描述这些方面吗?膨胀的用户期望加上缺乏对人工智能系统能力的清晰了解,在最好的情况下会导致不满、不信任或产品放弃,在最坏的情况下会导致伤害、不公平和伤害。
下面,我们概述了工程师和模型开发人员现在可以采取的最佳实践和行动,以帮助终端用户清楚地了解人工智能系统的能力和局限性。
当评估一个人工智能模型的能力和局限性时,不要局限于总的、单一分数的性能数字。
弄清人工智能系统的能力和局限性,首先要对人工智能的潜在行为有一个更全面的理解。这需要重新思考当前依赖于综合、单一分数绩效数字的评估实践。
在机器学习评估中,通常使用诸如“ Model X 对于给定的基准有 90%的准确性”这样的声明来报告模型性能。这个总数很难告诉我们,我们是否应该期望整个基准测试的性能是一致的,或者在这个基准测试中是否有一些数据的准确性要低得多。在实践中,由于训练数据中的偏差或者因为一些概念可能比其他概念更难学习,后者发生得更频繁。这种行为的一个众所周知的例子是 GenderShades 研究,该研究表明,面部识别算法在性别检测方面的性能对于肤色较深的女性来说明显低于其他人口统计群体。如果系统工程师自己都不清楚这些差异,那么向最终用户和客户解释这些差异就更加困难了。
多方面、深入的误差分析可以帮助我们回答以下问题:该模型是否对所有人口统计群体都同样准确? 是否有任何环境或输入上下文使模型表现明显更好或更差? 在第 99 个错误百分点时,系统性能有多差?
要进行多方面、深入的错误分析,请考虑以下实践:
- 检查不同粒度级别的故障模式 — Pandora 是一种可以帮助以概括的方式描述故障的方法。Pandora 提供了一组在不同抽象级别上运行的性能视图:全局视图(传达整体系统性能)、集群视图(针对单个数据区)和实例视图(解释单个数据点的性能)。对于每个视图,可以查看一个或多个输入要素的组合如何与模型性能相关联。在这些视图之间来回切换使开发人员能够更好地理解不同上下文中的失败,方法是根据出错的可能性来分割数据。例如,这项工作的结果表明,对于具有丰富和多模态输入空间的系统,不同区域的性能可能非常不同,并且这些差异可能因非常不同的原因而出现。
- 检查不同数据切片的故障模式 —在语言领域, Errudite 工具允许开发人员以灵活的方式查询输入数据,并根据错误率描述结果集的特征。Errudite 通过引入具有语义意义的数据选择操作符,使数据切片变得更加容易。此外,该工具还支持临时数据编辑,从而支持反事实分析(即如果某个特定示例略有不同,会发生什么?)。
使用多个现实的基准进行评估。
使用任何指标评估人工智能的能力和局限性通常需要在一些数据上测试人工智能模型。在已知的公共基准上评估人工智能模型总是一种好的做法。与其他先进技术相比,它提供了系统性能的量化视角。然而,由于两个原因,它不应该是唯一的评价手段。
首先,在每个模型改进周期中,一次又一次地优化单个基准可能会导致隐藏的过度拟合。例如,即使模型没有在基准上训练或验证,建模决策的归纳偏差可能已经被导向基准改进。不幸的是,这种做法常常受到我们在大型比赛和学术文章中报告和奖励表现的方式的激励,这提出了一个重要的问题,即我们如何在使真实系统更加可靠的背景下重新思考这些做法。
其次,来自我们自己的应用程序的数据可能看起来与基准分布非常不同。基准人脸检测数据集可能不包括与您的应用程序将在其中操作的各种角度或光照条件相同的图像。此外,随着人们使用系统并使他们的行为适应系统,这些条件可能会随着时间而改变。
为了缓解这些问题,您可以:
- 根据多个基准而不是一个基准来监控模型。这样,您可以检查模型改进和调整是否在不同的基准上通用。
- 将基准划分为不同的用例集,并监控每个用例的性能,以便当泛化失败时,您可以将其映射回用例的类型。
- 在评估中包括来自真实应用程序的数据。如果您担心没有足够的真实应用程序使用数据,那么好消息是,对于评估,您可能不需要培训所需的那么多数据。即使是少量的真实数据也可能暴露原本隐藏的错误。
- 通过数据扩充(例如,视觉转换)、在合成对抗分布下进行测试,以及在评估过程中引入红队概念,以识别在已知基准中无法观察到的错误,来丰富您的评估数据。
还值得一提的是,任何类型的基准充实或评估都需要特别关注与隐私相关的问题,以便评估过程本身不会泄露用户敏感数据。
在检查人工智能的行为和性能时,包括以人为中心的评估指标。
为了更好地确保我们的人工智能以最终用户期望的方式行事,并与他们的价值观保持一致,我们需要在我们的评估中包括以人为中心的指标。最常用的评估指标之一是模型准确性。然而,准确性可能并不总是转化为用户满意度和成功的任务表现。对指标设计的调查表明,在机器翻译和图像字幕等领域,存在人们关心但当前指标无法代表的模型性能的隐藏维度。类似地,人们感知准确性的方式可能与计算的准确性大相径庭;这种差异取决于与存在的误差类型和向最终用户解释系统精度的方式相关的多种因素。
以人为中心的人工智能评估指标,与人类对质量的概念和期望更密切相关,正在不断出现。当在决策任务或混合主动性系统中使用一个模型来帮助人类时,这样的指标尤其重要。可以考虑使用的一些指标包括:
- 可解释性 — 人类对模型如何决策的理解程度如何?
- 公平性 — 该模型在不同的人口统计群体上是否有可比较的表现?系统是否为这些小组分配了相当数量的资源?
- 团队效用 — 人类和机器合作表现如何?团队表现是否比任何一方单独表现更好?
- 性能可说明性 — 当系统出错时,人能提前预料到吗?(下一节将详细介绍)
- 互补性——机器是简单地取代了人类,还是更专注于人类需要帮助的例子和任务?
这些度量标准的准确和正式的定义取决于领域,并且对于哪种定义最适合应用程序经常存在分歧。然而,这些讨论已经导致了一些开源贡献,以用于计算的库的形式,有时还优化了这种以人为中心的度量: InterpretML , FairLearn , AI Explainability 360 。
请记住,在一天结束的时候,这些度量标准都不能代替实际人员的评估,例如用户研究。如果人工评估对于您的情况来说太耗费资源,那么至少可以考虑使用人工注释器来检查较小的数据分区,以了解您选择的代理度量与您的应用程序场景中的人工质量概念的一致性。
部署其性能更容易向人们解释的模型。
在模型优化和超参数搜索过程中,我们可能会有多个同样或类似精确的模型假设。在这些情况下,在决定部署哪个模型时,除了准确性之外,还要考虑性能的可解释性。性能可解释性使模型更加以人为中心,因为它使人们能够更好地理解和预测模型何时可能出错,以便人们可以在需要时接管。在最近的一项以人为中心的研究中,我们表明,当一个人与一个 ML 模型合作进行决策时,团队绩效会受到人们理解和预测模型错误界限的程度的显著影响(即,模型在哪里出错,在哪里成功)。
要确定一个具有更好的性能解释能力的模型,请考虑以下因素:
- 选择具有高度简约性的模型(即,当系统出错时,你需要多少信息块来描述,错误解释有多复杂?)和低随机性(即,在多大程度上可以通过错误解释干净地将错误与成功实例分开?).
- 为了测量感知的简约性和随机性,尝试通过训练简单的可解释的基于规则的分类器,如决策树或基于先前交互的规则列表,来逼近关于错误边界的人类心理模型(即,人类对错误边界的了解)。学习到的心智模型当然只是近似或模拟,但是如果它们足够简单,我们可以确保它们不会添加任何关于人们如何学习的误导性假设。
在未来,我们希望看到更多的工作来实现更好的模型选择,通过增加损失函数或通过约束最小化,作为模型优化和训练的一部分,目标是训练模型,使人类能够发展明智和合理的信任。
在调整模型参数时,考虑错误的成本和风险。
尽管一个给定的系统可能会犯许多类型的错误,但它们发生的可能性并不相同,而且它们还可能与不同的应用程序相关成本相关联。例如,在许多医学应用中,假阴性错误的成本可能比假阳性高得多,特别是如果在疾病存在时不治疗的后果比相应的副作用更危及患者的生命。正如你可能已经猜到的,明确地估计这样的成本和风险对于高风险的决策是特别必要的。风险评估的良好实践是在部署应用程序之前进行用户研究或试点研究,以预测潜在错误的影响。
成本估计可用于通知模型参数的调整。然而,目前大多数模型被训练得更一般,并且不针对与领域相关联的成本进行定制,主要是因为这些成本对于开发者来说通常是未知的,并且它们可能从一个消费者到另一个消费者而变化。因此,大多数模型都是在简单的 0/1 损失的标准近似值上训练的,希望它们能用于一般应用。意识到这样的成本估计困难,仍然有必要注意到,至少对于整个领域共享相似且已知的非均匀错误成本的情况,像成本敏感学习或重要性采样这样的技术可以帮助捕捉不同示例的敏感性。例如,如果假阴性比假阳性代价更高,那么可以在优化过程中给这些情况分配更多的损失。其他技术包括过度(或不足)表示特定类别的实例,或者对接近决策边界的实例进行不同的加权。
当不同消费者的成本不同时,从长期来看,模型部署、编排和维护会带来额外的复杂性。然而,随着云部署服务的不断进步(例如,AzureML 上的MLOps),这项任务已经变得更加触手可及。这些服务通常倾向于将不同的模型版本容器化,并提供给不同端点的客户。
校准并解释不确定性。
到目前为止,我们介绍的技术更适合在全局级别或一组实例上描述和测量系统性能。然而,由于模型性能可以从一个实例到另一个实例而变化,在交互期间在单个实例上表达模型性能也可以帮助为最终用户设置适当的期望(例如,在单个实例上传达模型不确定性)。模型校准旨在将机器学习预测与仔细校准的置信度分数相关联,置信度分数反映了误差的概率分布。这将意味着,如果一个模型以 95%的置信度识别图像中的交通灯,如果您将模型预测准确性视为一个随机变量,则失败的可能性确实是 5%(在大量样本中)。
今天,许多开箱即用的机器学习算法都没有将校准的不确定性作为默认属性。一些例子包括朴素贝叶斯模型、支持向量机甚至神经网络。可用于不确定度校准的一些方法包括:
- 后处理技术(如普拉特缩放或保序回归),这些技术不会改变模型的训练方式,但会对模型不确定性预测进行后处理,以便输出概率最好地反映误差。幸运的是,这些技术在 scikit-learn 等流行的 ML 库中很容易得到。如果你在问自己这些技术如何应用于深度学习,这个调查提供了一个全面的总结。
- 内置技术(如自举或不确定性估计的放弃),通常适用于特定的模型类别,但也可用于更广泛的环境。例如,概念,如辍学正则化,自举和系综显示,以改善不确定性估计。虽然这些方法中的一些确实有更多的计算要求(例如,深度网络的集合),但考虑它们仍然是一个好主意,特别是对于高风险领域。
- 当难以进行精细的不确定性估计时,如果数据稀疏,“粗略”和粗粒度校准可能比根本没有校准要好。毕竟,人们不会对 75%或 76%的信心有太大的区别。但是,如果信心从 75%变为 90%,他们的决定可能会发生巨大变化。通过将原始模型输出分数映射到更大的置信度桶,粗粒度校准可以与事后技术集成。区分这些情况仍将有助于在不确定时以不同的方式表达答案,例如,“我还不知道如何回答这个问题。我不确定,但我想答案可能是…
尽管模型置信度\不确定性是表达预期性能的一种简洁明了的方式,但重要的是要意识到实践中可能出现的两个主要挑战:
- 不确定性解释——生产中使用的不确定性分数可能并不总是容易解释,尤其是对于具有高维度和丰富输出的系统。以向视觉受损的用户提供场景描述的图像字幕系统为例。该系统已经向用户提供了下面的标题“一群人围坐在桌子旁吃晚餐”,并且 80%有把握。用户应该如何理解这种信心?这是否意味着场景中根本没有人?还是说他们不是在吃饭而是在干别的?在这种情况下,向用户详细说明输出分数的语义实际上是至关重要的。虽然总结更丰富产出的不确定性仍然不是一个很好理解的问题,但一个可能的替代方法是突出特定产出块的高度不确定性(如果可用),以将注意力引导到正确的方向。
- 训练数据与真实世界分布的对比——即使使用上述技术,重要的是要意识到,与其他学习问题一样,置信度分数只会与训练数据一样好。当真实世界的数据和模型在训练期间实际看到的数据之间存在较大差距时,尽管我们尽了最大努力进行校准,但置信度得分可能仍然与准确性不一致。为此,ML 社区正在朝着以下重要方向努力:检测不符合分布的示例(T0)或在数据集偏移下校准(T2)的示例(T1)和(T3),在人类参与的情况下识别未知的未知(T4)的示例(T5),以及恶意数据偏移的对抗性鲁棒性(T6)但这无疑仍是学习中最具挑战性的问题之一。
其他考虑
这篇文章提出了几个策略来更好地理解你的人工智能的性能和能力。但是在你的应用程序中部署 AI 之前,你还应该考虑如何最好地向你的目标用户呈现关于性能和功能的信息,这些用户很可能对 AI 知之甚少。例如,生产中使用的不确定性分数可能不总是容易被最终用户解释,尤其是对于具有高维度和丰富输出的系统。
在试图设定对人工智能系统的正确期望时,本帖中建议的大多数方法都侧重于基于解释的技术,这些技术由对人工智能的能力和局限性的更好理解来支持。然而,任何类型的文档都有一些重要的缺点。也就是说,大多数人不阅读文档!在高风险的场景中,人们可能会被要求或激励这样做,但这表明社区有机会在探索替代方法以设定人工智能系统的期望方面发挥创造力。例如,最近的工作已经研究了将模型参数直接暴露给最终用户,以允许他们试验人工智能的不同性能。这不仅可以让他们更好地了解人工智能在常规使用中的表现,还可以让人们在确定他们可能会受到人工智能的影响时有一种代理感。
最后,当人工智能随着时间的推移学习和改变时,弄清楚一个系统能做什么以及它能做得多好变得越来越具有挑战性。随着时间的推移,当人们与系统交互时,他们的期望可能会改变,甚至他们想要解决的任务的定义也可能会改变。这种动态很难用静态代理度量来跟踪,我们将在以后的文章中重点讨论如何处理随时间变化的挑战。
摘要
这篇文章介绍了机器学习和工程实践者可以用来设置正确的用户期望的实践,这些期望是关于人工智能系统可以做什么以及做得如何。因为很难区分虚构的宣传和实际的功能,所以负责任的做法是尽可能多地解释产品的预期质量。虽然对于数据密集型学习系统来说,这可能仍然很困难,但如上所述的机器学习和工程实践,以及未来可能出现的其他实践,可以帮助我们传递正确的信息,并建立合理的信任。
你有你自己的实践想与社区分享吗?欢迎在下面发表评论或给我们写信,地址是 aiguidelines@microsoft.com。
作者
贝斯米拉·努什,萨勒玛·阿默什,埃克斯·卡玛,加甘·班萨尔,丹·维尔德,米哈拉·沃莱亚努,埃里克·霍维茨
如何使用 TensorFlow 2.0 构建高效的音频数据管道
使用 TensorFlow 的数据集 API 消除培训工作流程中的瓶颈
GPU 可以大大加快你的训练时间。为了获得最佳性能,您需要为他们的每个训练步骤提供足够的数据。否则,事情会变得真的缓慢。💤
作为一名研究人员,看着一个几乎没有移动的进度条,看着你的 GPU 闲置,肯定是你最沮丧的经历之一。
至少对我来说是这样。
尤其是在你研究的早期阶段,关键是你能尝试新的想法并快速测试你的假设,以朝着正确的方向前进。当你脑子里满是想法时,没有什么比等待几个小时才有结果更糟糕的了。
面临的挑战是在当前步骤完成之前,及时将数据从硬盘传输到 GPU,以便进行下一步培训。这包括从磁盘加载和解码原始字节,预处理并可能增加数据,以及准备批处理。
这正是你的数据管道的工作。
这篇文章将带领你使用tf.data
API 为音频数据构建一个高效的数据管道。
tf.data
API 使得处理大量数据、读取不同的数据格式以及执行复杂的转换成为可能。— 张量流导
我找到了关于如何为分散在网络上的音频数据建立有效的数据管道的指导和提示。我将这些编译到这篇文章和代码示例中,供您开始构建自己的代码。
我会在这篇文章的最后链接到相关的资源。
首先,你真的需要一个数据管道吗?
看情况。
只要数据集足够小,能够放入内存,就可以在开始训练之前简单地加载一次,然后反复迭代。在我们开始处理更大的数据集之前,这是我们大多数人一直采用的标准方法。
当你无法再将所有数据放入内存时,你就需要按需加载数据,让你的 GPU 保持忙碌。但是,您仍然希望能够像以前一样对数据进行洗牌、批处理和预处理。
数据管道可以帮助你实现这一点。
如何用三个简单的步骤建立你的第一个数据管道
官方 TensorFlow 教程关于加载和预处理数据包含了很多有用的信息和例子。不幸的是,没有一个例子是关于音频数据的。
不过,一个好的起点是关于加载图像的部分。
基于本教程,您可以通过三个简单的步骤使用tf.data
API 构建您的第一个数据管道:
- 创建一个由
file_paths
和labels
组成的数据集 - 编写一个解析函数,从文件路径中加载并解码 WAV 文件
- 使用
Dataset.map()
创建一个[audio, label]
对的数据集
把这些碎片放在一起,你可能会得到这样的结果:
使用 TensorFlow 数据集 API 的首个数据管道
很简单。
但这只是故事的一半。
不幸的是,对于任何合理大小的数据集,如果您遵循上面的方法,您的性能将会受到很大的影响。您的 GPU 和 CPU 将几乎闲置,但磁盘 I/O 达到最大。你的硬盘已经成为瓶颈。
让我解释一下。
你可能有一个强大的 GPU 和多核 CPU,但只有一个物理硬盘,只有一个磁头来读取数据。第 26 行的.map()
调用在可用的 CPU 核上并行运行load_audio
解析函数。
现在,有许多进程试图从磁盘上的随机位置读取数据,但仍然只有一个磁头进行读取。有了这些跳跃,这不会比一个单个读取过程快多少。
所以,如果你阅读成千上万的小文件,比如几秒钟长的 WAV 文件,寻找正确的位置和打开每个文件的开销将成为瓶颈。
但是不要担心,有一个解决办法。
使用 TFRecords 显著加快您的数据管道
TFRecords 可以存储二进制记录的序列。这意味着您可以将许多 WAV 文件放入一个 TFRecord 中,并增加每次磁盘读取的数据吞吐量。
理论上,你可以在一个单独的 TFRecord 文件中存储所有的 WAV 文件。TensorFlow 文档建议将数据分成多个 TFRecord 碎片,每个碎片的大小在 100 MB 到 200 MB 之间。实际上,为了利用并行化,拥有至少与 CPU 内核一样多的 TFRecord 碎片也是有意义的。
在一个 TFRecord 分片中有数百个 WAV 文件,您可以减少磁盘 I/O,因为您只需要打开一个文件来读取和访问许多音频文件。您增加了每次磁盘读取的数据吞吐量,并消除了磁盘 I/O 瓶颈。
这个概念实际上非常类似于在 web 和游戏开发中使用 sprite-sheets,将一堆小图像(sprite)组合成一个大图像(sheet),以提高性能和启动时间,并减少整体内存使用。
但是有一个问题。
转换成 TFRecord 格式(并读取它)需要相当多的样板文件。要转换,您需要:
- 打开一个 TFRecords 文件:
out = tf.io.TFRecordWriter(path)
- 将数据转换为三种数据类型之一:
tf.train.BytesList
、tf.train.Int64List
或tf.train.FloatList
- 通过将转换后的数据传递给
tf.train.Feature
来创建一个feature
- 通过将
feature
传递给tf.train.Example
来创建一个example
协议缓冲区 - 序列化
example
并写入 TFRecords 文件:out.write(example.SerializeToString())
如果这还不够混乱的话,看看它在代码中的样子:
要转换为 TFRecord 格式的样板文件
这里有一个小脚本来展示如何将 WAV 文件转换成 TFRecord 碎片:
将 WAV 文件转换为 TFRecord 格式
在开始复制和粘贴上面的代码之前,有必要考虑一下 TensorFlow 文档中的这句话:
没有必要转换现有代码来使用 TFRecords,除非你正在使用
*tf.data*
并且读取数据仍然是训练的瓶颈。
如果您确实发现读取数据是您的瓶颈,您可能想要查看上面脚本的这个更精细的版本。它与可用的 CPU 内核并行编写 TFRecord 碎片,并估计每个碎片应该包含的文件数量保持在 100 MB 到 200 MB 之间。
最后,每个碎片中文件的数量很重要。它需要足够大,以消除磁盘 I/O 瓶颈。在我找到压缩 TFRecords 的选项之前,每个碎片只能容纳大约 100 个 WAV 文件。我最终得到了几千个碎片,这意味着对太少的数据进行了太多的读取。
使用tf.data.TFRecordDataset
加载数据集
一旦你把 WAV 文件转换成 TFRecords,你需要对你的数据管道做一些调整:
- 列出所有 TFRecord 文件
tf.data.Dataset.list_files('*.tfrecord')
- 将 TFRecord 文件读取为
tf.data.TFRecordDataset
- 解析和解码每个序列化的
tf.train.Example
因为每个示例都存储为二进制协议缓冲区,所以您需要提供一个解析函数,将这些消息转换回张量值。
以下是这种情况的一个示例:
使用 TFRecords 的数据管道
享受高效的数据管道
就这样。这就是你如何为音频数据建立一个有效的数据管道。
我们从一个简单的数据管道开始,它基于 TensorFlow 指南中的一个介绍性示例。我们发现了磁盘 I/O 瓶颈。我们把 WAV 文件转换成 TFRecords 来消除它。最后,我们更新了数据管道来读取 TFRecords 并从中加载音频数据。
如果你想更深入一点,我会给你一些启发和帮助创建这篇文章的资源链接。你可以在下面找到它们。
但首先,我想听听你的意见。你采取的是相似还是完全不同的方法?你有什么见解想与我和其他读者分享吗?请把它们贴在评论里。
记住,一个系统的速度取决于它最慢的组件。
如果你想更深入一点
[tf.data](https://www.tensorflow.org/guide/data)
:构建 TensorFlow 输入管道- 使用
[tf.data](https://www.tensorflow.org/guide/data_performance)
API 性能更佳 - TFRecord 和
- TPU 速度数据管道:
[tf.data.Dataset](https://codelabs.developers.google.com/codelabs/keras-flowers-data/#0)
和 TFRecords - 斯坦福 CS230:构建数据管道
如何使用 Streamlit 在 Python 中构建交互式仪表盘
借助交互式仪表盘提升您的数据科学项目
如果你正在做一个可视化项目,想要展示你的发现,或者如果你正在寻找工作,想要创建一些项目组合——交互式仪表盘是以一种容易理解的方式提供信息的好方法。
细流
今天,我们将在交互式仪表盘中使用 Python 和 Streamlit。
Streamlit 是一个开源应用程序框架,它通过一个漂亮的仪表板来探索和理解数据,使数据科学家的生活变得简单。
设置简化 it
让我们首先将 Streamlit 安装到我们的系统中,然后运行 hello 命令来验证一切正常。我们可以在终端中通过 Ctrl+c 随时杀死正在运行的 app。
$ pip install streamlit
$ streamlit hello
导入库
安装 Streamlit 后,让我们导入我们将使用的所有库。
import streamlit as st
import pandas as pd
import numpy as np
import pydeck as pdk
import altair as alt
from datetime import datetime
以不同的方式显示文本
Streamlit 使我们能够轻松清理、共享和可视化数据。我们可以用不同的方式写文章来解释我们的发现,比如给图表加标题,给出标题或副标题,或者我们可以解释图表或数据的细节。
标题:设置 app 的标题,可以使用 st.title()
标题:我们可以使用 st.header()作为标题,st.subheader()作为副标题
Text :为了编写特定图形或数据的描述,我们可以使用 st.text()。 Streamlit 也支持 markdown,所以如果我们想将文本显示为 markdown,我们可以使用 st.markdown()
Latex: Streamlit 还显示 Latex 格式的数学表达式。通过使用 st.latex()
写:我们可以显示任何东西,如图形、数据框、函数、错误、模型等。通过使用 st.write()
取数据
现在我们已经安装了应用程序并导入了所有的库,下一步就是加载数据。这里我们将使用 Covid19 数据。
数据缓存
Streamlit 提供了一种缓存机制,使我们的应用程序即使在从 web 加载数据或处理大型数据集时也能保持高性能。
简化小部件
小工具可以帮助我们过滤数据,或者通过按钮、滑块、选择框等直接将数据可视化到应用程序中。让我们来看几个例子。
多选
我们可以从列列表中选择或删除行。在这里,我们创建国家名称小部件,我们可以选择国家/地区,以便只显示那些选定值的数据/图表。
选择框
图表和地图
Streamlit 支持许多流行的可视化库,如 Matplotlib、Altair、deck.gl 等等。这里是显示图表的更多细节。
条形图
牛郎星图表
动画地图
所有的代码
这个任务的所有代码都可以在这里找到:代码。
这是一个总结
这个帖子到此为止。在下一篇文章中,我们将讨论如何实时生产这些仪表板,以便其他人也可以访问您的仪表板。希望对你有帮助。