TowardsDataScience-博客中文翻译-2020-一百一十九-
TowardsDataScience 博客中文翻译 2020(一百一十九)
使用 Apache Kafka、Pandas 和 MatPlotLib 跟踪实时黄金价格
了解如何使用阿帕奇卡夫卡熊猫跟踪实时黄金价格。在条形图上绘制最新价格。

克里斯·利维拉尼在 Unsplash 上的照片
来自《走向数据科学》编辑的提示: 虽然我们允许独立作者根据我们的 规则和指导方针 发表文章,但我们并不认可每个作者的贡献。你不应该在没有寻求专业建议的情况下依赖一个作者的作品。详见我们的 读者术语 。
在我们所处的时代,跟踪、处理和分析实时数据正成为许多企业的必需品。不用说,处理流数据集正成为数据工程师和科学家最重要和最需要的技能之一。
对于本文,我假设您熟悉 Apache Kafka——一个开源的分布式事件流平台。Apache Kafka 具有内置的分区、复制和容错机制。我在过去的几个项目中使用过 Apache Kafka,用于几个用例,包括度量收集、日志收集和流处理。
在我的数据工程和数据科学课程中,Apache Kafka 是我的学生们讨论的热门话题。在我在课程中提到的许多例子中,我决定分享这个例子。
我们将从以下 API 向 Kafka 中摄取流数据。
https://forex-data-feed . Swiss quote . com/public-quotes/BBO quotes/instrument/XAU/美元
我们将首先创建一个 Kafka 生成器,它将从上面的 API 中读取数据,并不断地将数据推送到 Kafka。
我们还将创建 Kafka 消费者,它将不断地从 Kafka 读取数据。一旦消息被阅读,我们将提取最新的黄金价格,并将结果绘制在条形图上。
技术栈:Apache Kafka、Pandas、Python、kafka-python 库和 MatPlotLib
准备工作
安装 Java
sudo yum -y install java-1.8.0-openjdk.x86_64
下载阿帕奇卡夫卡
$ cd /opt
$ sudo wget [http://apache.forsale.plus/kafka/2.5.0/kafka_2.13-2.5.0.tgz](http://apache.forsale.plus/kafka/2.5.0/kafka_2.13-2.5.0.tgz)
$ sudo tar -zxf kafka_2.13-2.5.0.tgz
$ cd kafka_2.13-2.5.0
在后台启动 Zookeeper 和 Kafka 服务器
$ cd /opt/kafka_2.13-2.5.0;export JAVA_HOME=/usr/lib/jvm/jre-1.8.0;export JRE_HOME=/usr/lib/jvm/jre$ nohup sudo /bin/zookeeper-server-start.sh config/zookeeper.properties &$ nohup sudo bin/kafka-server-start.sh config/server.properties &
创造一个新的卡夫卡主题— 戈德拉特
$ sudo bin/kafka-topics.sh --create --zookeeper localhost:2181 --replication-factor 1 --partitions 1 --topic Goldrates$ sudo bin/kafka-topics.sh --list --zookeeper localhost:2181
克隆我的 Github repo:
$ git clone https://github.com/mkukreja1/blogs.git
创建一个卡夫卡制作人
在 Jupyter 中安装 Kafka Producer 笔记本。我将通过这些步骤来理解这个流程。
Kafka/gold rates/kafkaproducergoldrates . ipynb
import sys
!{sys.executable} -m pip install kafka-pythonimport time
import json
from json import dumps
**from kafka import KafkaProducer**
from time import sleep
import requests as req
KafkaProducer — 用于创建异步消息生成器的高级类。
ticker_url="[https://forex-data-feed.swissquote.com/public-quotes/bboquotes/instrument/XAU/USD](https://forex-data-feed.swissquote.com/public-quotes/bboquotes/instrument/XAU/USD)"
brokers='localhost:9092'
topic='Goldrates'
sleep_time=300
声明一些将在程序中使用的常量。 经纪人 可以是参与您的 Apache Kafka 集群的经纪人列表。
producer = KafkaProducer(bootstrap_servers=[brokers],value_serializer=lambda x: dumps(x).encode('utf-8'))
这将初始化一个新的 Kafka 生成器。该生成器将在将数据发送到代理列表之前序列化数据。数据将被转换为 JSON 并使用 utf-8 进行编码。
while(True):
print("Getting new data...")
resp = req.get(ticker_url)
json_data = json.loads(resp.text)
producer.send(topic, json_data)
time.sleep(sleep_time)
这段代码在一个永无止境的循环中运行。它从 Goldrates API 获取数据,将传入的 JSON 对象转换为 Python 对象,最后将这些数据发送到 Kafka 中的特定主题。在每次迭代之后,它休眠一个先前声明的周期=sleep_time 常数。
如果您的 Kafka Producer 运行正常,您应该会看到如下输出:
获取新数据…
获取新数据…
让这个笔记本保持运行,这样 Kafka 的消费者就可以阅读这些信息。
创造一个卡夫卡消费者
在 Jupyter 安装 Kafka 消费者笔记本。如前所述,我将通过这些步骤来理解这个流程。
Kafka/gold rates/kafkaconsumergoldrates . ipynb
import time
import json
**from kafka import KafkaConsumer**
from pandas import DataFrame
from datetime import datetime
import matplotlib.pyplot as plt
KafkaConsumer — 用于创建异步消息消费者的高级类。
brokers='localhost:9092'
topic='Goldrates'
sleep_time=300
offset='latest'
声明一些将在程序中使用的常量。 经纪人 可以是参与你的 Apache Kafka 集群的经纪人列表。Offset =latest表示代码应该从 Kafka 队列中获取 latest 消息。您也可以选择从最早的开始。
consumer = KafkaConsumer(bootstrap_servers=brokers, auto_offset_reset=offset,consumer_timeout_ms=1000)
consumer.subscribe([topic])
这初始化了一个新的 Kafka 消费者。
auto _ offset _ reset—仅在用户遇到意外问题时使用。在这种情况下,我们要求消费者重新开始阅读来自最新消息的消息。您也可以选择从最早的开始。
自动提交间隔毫秒— 两次提交之间的时间段。
最后,我们要求消费者订阅,即从定义为常量的主题中获取消息。
oldprice_dict = {}
while(True):
for message in consumer:
#print(message)
d=json.loads(message.value)
df=DataFrame(d)
for x in range(1):
#print(x)
new_price=df['spreadProfilePrices'][x][0]
ts=df['ts'][x]
#print(new_price)
print("Latest Gold Price: " + str(new_price['ask']))
datetime_time = datetime.fromtimestamp(ts/1000)
goldprice_dict[str(datetime_time)]=new_price['ask']
print("Gold Price at: " + str(datetime_time))
print("-----------------------------------------")
#print(goldprice_dict)
plt.figure(figsize=(20,10))
plt.bar(range(len(goldprice_dict)), list(goldprice_dict.values()), align='center',linewidth=0.5)
plt.xticks(range(len(goldprice_dict)), list(goldprice_dict.keys()))
plt.show()
time.sleep(sleep_time)
对于每条消息,JSON 都被加载到 Pandas 数据帧中。从数据帧中提取黄金的最新价格和时间戳,并存储在 Python 字典中。最后,Python 字典用于绘制如下条形图:
x 轴—黄金价格读数的时间戳
y 轴-黄金价格值
如果一切顺利,对于每次迭代,最新的黄金价格将绘制如下:
预期产出如下:
最新黄金价格:1902.951
2020–10–26 15:16:16
黄金价格

最新黄金价格:1902.968
2020–10–26 15:21:17
黄金价格

最新黄金价格:1902.208
黄金价格:2020–10–26 15:26:18
————————

最新黄金价格:1902.293
2020–10–26 15:31:19
黄金价格

最新黄金价格:1902.693
2020–10–26 15:36:19
黄金价格

本文的所有代码都可以在下面的链接中找到:
此时您不能执行该操作。您已使用另一个标签页或窗口登录。您已在另一个选项卡中注销,或者…
github.com](https://github.com/mkukreja1/blogs/tree/master/kafka/gold-rates)
我希望这篇文章是有帮助的。由 Datafence Cloud Academy 提供的 Apache Kafka 主题将作为大数据 Hadoop、Spark & Kafka 课程的一部分进行更详细的介绍。
跟踪每个表的 Azure Analysis Services 处理时间
使用 Kusto 查询和诊断日志
最近我一直在与 Azure Analysis Services 合作。在我们的模型中,表的数量在快速增长,处理时间也在波动和增长。从 AAS 的角度来看,我有足够的理由进一步了解哪些表占用了最多的处理时间。
在这里,我将解释我所采取的步骤,以获得关于 Azure Analysis Services 全流程从日志分析工作区到 Power BI 报告的处理步骤的详细信息。
- 将诊断日志记录设置到日志分析工作区
- 使用 Kusto 查询语言浏览日志
- 将数据导入 Power BI
诊断日志
一如既往,起点是微软文档。好吧,实际上是谷歌或者必应。但是很快我就进入了文档页面,其中包含了开始所需的所有步骤。下面列出了链接。
在这种情况下,已经完成了诊断设置的配置,以便将日志发送到日志分析工作区。如果您仍然需要这样做,文档中包含了所需的操作。这很简单。
浏览日志
下一步是在我们的日志分析工作区(LAW)中探索日志,通过编写查询来获取我们正在寻找的信息。文档中的第一个示例查询是一个很好的起点。但这并不是我们想要的,因为我们想要的是模型的更新。而不是查询时间。
最后,我编写了两个查询,并在 Power BI 中将它们连接在一起。在第一个查询中,我专门寻找刷新流程的最高级别 。
在这种情况下,“OperationName”字段和“TextData_s”字段中的值“CommandEnd”在“< Refresh”上进行过滤。下面是查询,如果要用,别忘了在第二行加上自己的服务器和数据库名。
在第二个查询中,我们获得了刷新的详细信息,并希望过滤掉尽可能多的我们不使用的操作。这个查询产生了我们正在寻找的所有详细信息。
另一个选择是,为了在最后一个查询中得到更少的结果,将它加入到“RootActivityId_g”上的第一个查询中。这样可以确保只获得与第一个查询相关的数据。
在 Power BI 中可视化
Log Analytics 工作区提供了将书面查询导出到 M 语言的可能性,并包含一些关于如何在 Power BI 中使用脚本的帮助。

将 Kursto 查询导出到 Power BI(图片由作者提供)
在将两个 Kusto 查询添加到 Power BI 报告之后,在两个表之间的“RootActivityId_g”上创建了一个关系。从“ObjectReference_s”中,可以导出表名。最后,我添加了一个度量来计算以分钟为单位的持续时间。
模型中的数据提供了对每次刷新操作持续时间的深入了解,可通过第一次查询中的“RootActivityId_g”进行识别。从第二个查询中可以看到每个表的详细信息。

第一次查询;每次刷新的状态(图片由作者提供)
如果我们考虑‘object name _ s’=‘Partition’和‘event subclass _ s’= 59,则每个表的步骤包括:
- 查询来源
- 读取数据并处理分区
- 分析和编码
- 每列每段的压缩率
- 每列处理层次结构
这是一个简单的演示模型的例子。第一行显示所选表的完整持续时间。查看开始和结束时间。

第二次查询;每个表的详细刷新信息(图片由作者提供)
结论
能够分析刷新过程中的不同步骤,并精确定位耗时的步骤,对我来说非常有价值。
在诊断日志中有大量关于您的 Analysis Services 实例的信息。例如关于“查询处理单元”(qpu_metric)和内存使用的信息。
从我的角度来看,作为一名数据工程师,用 Power BI 中的诊断日志监控 Azure Analysis Services 是 Azure 门户的一个很好的替代方案。您控制着整个流程的设计,并且可以向您的 BI 产品的最终用户提供监控解决方案,而无需登录 Azure 门户。
请让我知道你的想法或者你有什么建议给我!谢谢大家!
参考文献:
[## Azure Analysis Services 的诊断日志记录
任何 Analysis Services 解决方案的一个重要部分是监视服务器的性能。Azure 分析…
docs.microsoft.com](https://docs.microsoft.com/en-us/azure/analysis-services/analysis-services-logging) [## Azure 平台日志概述- Azure Monitor
平台日志为 Azure 资源和 Azure 平台提供了详细的诊断和审计信息
docs.microsoft.com](https://docs.microsoft.com/en-us/azure/azure-monitor/platform/platform-logs-overview) [## Kusto 入门
Azure Data Explorer 是一项用于存储和运行大数据交互式分析的服务。它基于关系…
docs.microsoft.com](https://docs.microsoft.com/en-us/azure/data-explorer/kusto/concepts/)
作为专业人士跟踪您的 ML 模型,使用 MLflow 跟踪它们。
使用 MLflow 跟踪 ML 模型的分步指南

维克多·加西亚在 Unsplash 上拍摄的照片
作为一名机器学习工程师或数据科学家,你的大部分时间都花在实验机器学习模型上,例如调整参数、比较指标、创建和保存可视化、生成报告等。然而,在许多情况下,我们通常不会以健康的方式进行这种跟踪。进行这种跟踪的一种健康、简单和有效的方式是利用促进这种类型活动的工具,例如 ML flow 。
在这篇博客中,您将学习如何安装、如何跟踪指标、如何跟踪参数以及如何保存和重用 scikit-learn ML 模型。所以这个博客分为以下几个部分:
- 什么是 MLflow ?
- 构建 ML 管道
- 跟踪 ML 管道
- 可视化
所以,让我们开始吧!
什么是 MLflow?
MLflow 是一个管理 ML 生命周期的开源平台,包括实验、再现性、部署和中央模型注册[1]。 MLflow 是基于 python 的,你可以在这里找到源代码。所以,本质上 MLflow 提供了:

图一。MLflow 功能|作者图片|创意取自来源
基本上 MLflow 提供了一套工具,让我们生活中的机器学习工程师或数据科学家更加高效和有效。
在这篇博客中,我们将重点关注跟踪参数和指标的部分,以及生成、保存和加载模型以供再次使用。所以让我们继续安装吧!
由于 MLflow 是基于 python 的安装相当容易,你可以用 pip 来完成,比如:
pip install mlflow
很好,假设安装成功,我们将构建一个简单的管道,在其中我们将训练一个决策树来生成对蘑菇数据集的预测。所以让我们努力吧!
你可以在这里找到完整的实现:https://github.com/FernandoLpz/Tracking-ML-model-MLflow
建立洗钱渠道
正如我们提到的,我们将使用蘑菇数据集(你可以在这里找到数据集:来源)来建立一个机器学习模型。因此,管道将具有以下形式:

图二。Pipeline |作者图片
在管道的每个部分,我们将执行特定的任务,例如:
- 加载数据:读取 csv 文件,加载到 pandas 数据帧
- 预处理:删除不必要的列,应用一键编码将分类数据转换成数字格式。
- 拆分数据:给定整个数据集,将生成训练集和测试集。
- 参数调整:通过网格搜索,将获得决策树的最优参数,随后将应用 k-fold 交叉验证来观察具有最优参数的模型的行为。
- 模型评估:在给定最优参数的情况下,通过使用测试数据进行计算,对模型进行测试,以计算精度。
然后,管道的每个组件的具体任务描述如下:

图 3。管道详情|作者图片
那么,没有被跟踪的管道将如下所示:
代码 1。无跟踪的管道
正如我们所看到的,属于一个类的每一个函数都代表了管道的每一个组件,每一个组件都有其特定的活动。然而,在我们的日常生活中,我们需要在不同的环境下创建模型的不同版本,也就是说,我们需要跟踪模型的变化和结果。让我们看看如何用 MLflow 来做到这一点!
跟踪 ML 管道
为了跟踪管道,我们将使用 MLflow: 提供的一些重要模块
- MlflowClient(): 创建和管理实验和运行,以及创建和管理注册的模型和模型版本[2]。
- mlflow.start_run(): 启动新的 MLflow 运行,将其设置为活动运行,在该运行下将记录度量和参数[3]。
- mlflow.log_metric(): 记录单个键值度量。该值必须始终是数字[3]。
- mlflow.log_param(): 记录当前活动运行中的单个键值参数。键和值都是字符串[3]。
- mlflow.log_artifact(): 将本地文件或目录记录为工件[3]
- ml flow . sk learn . Save _ model():将一个 scikit-learn 模型保存到本地文件系统[4]上的一个路径中。
因此,首先我们需要创建一个实验,或者如果我们已经有了一个实验,使用 id 来启动之前创建的实验,让我们看看下面的代码:
代码 2。正在初始化 MLflow 客户端
在第 15 行,我们创建了一个客户端实例。在" try-except "块中,用于创建一个命令行提供的名为" data_name 的新实验。如果已经创建了一个名为 data_name 的实验,则取该实验的 id 。
在第 29 行,我们生成一个上下文来执行 mlflow.start_run() ,使用实验 id 和运行名称作为参数。
好了,一旦以上完成,是时候开始跟踪工件、度量和参数了。让我们来看看我们是如何处理管道的 3 个组件的: load_data() ,parameter _ tuning()和k _ fold _ cross _ validation()。
代码 3。跟踪工件
第 6 行中发生的事情是,我们正在保存数据集的路径,该数据集将被加载到 MLflow 上下文中的数据帧中。
代码 4。跟踪参数
一旦找到最佳参数,就使用 mlflow.log_param() 用 MLflow 跟踪它们,如第 16、17 和 18 行所示。
代码 5。跟踪指标
正如我们所看到的,为了跟踪指标,我们必须使用 mlflow.log_metric(),,正如您在第 10 行和第 11 行中看到的。
最后,这是使用 MLflow 跟踪的整个管道的样子:
代码 6。使用 MLflow 跟踪管道
最后,由于我们正在使用一个决策树,我们可能想要保存它并将其与其他树进行比较。同样,我们还需要保存最终的模型,我们将这样做:
代码 7。使用 MLflow 跟踪树拓扑和模型
形象化
MLflow 提供了一个工具来轻松查看你实验中所做的所有轨迹,这个功能是: mlflow ui
一旦我们创建了一个实验并跟踪了工件、参数或指标,我们就可以从命令行启动 UI,如下所示:
mlflow ui
它会像这样显示和输出:
[2020-09-27 00:31:50 -0500] [26723] [INFO] Starting gunicorn 20.0.4[2020-09-27 00:31:50 -0500] [26723] [INFO] Listening at: http://127.0.0.1:5000 (26723)[2020-09-27 00:31:50 -0500] [26723] [INFO] Using worker: sync[2020-09-27 00:31:50 -0500] [26725] [INFO] Booting worker with pid: 26725
所以你只需要进入你的浏览器并访问你的本地主机。然后我们会看到类似这样的内容:

图 4。MLflow UI |作者图片
如果您单击运行日期,您可以更详细地查看一些项目,例如我们跟踪的工件:

图 5。MLflow UI 工件|作者图片
你可以在这里找到完整的实现:https://github.com/FernandoLpz/Tracking-ML-model-MLflow
结论
在这篇博客中,我们看到了 MLflow 是什么,它的优势是什么,如何从安装到跟踪一个基本管道以及解释图形界面的可视化来实现它。
参考
[2]https://www . ml flow . org/docs/latest/_ modules/ml flow/tracking/client . html
https://www.mlflow.org/docs/latest/python_api/mlflow.html
[4]https://www . ml flow . org/docs/latest/python _ API/ml flow . sk learn . html
使用 Python 追踪冠状病毒(新冠肺炎)在印度的传播
从卫生部官方网站获取新冠肺炎病例的最新州级数据,并使用 Python 库将其可视化。

Patrick Assalé 在 Unsplash 上的照片
冠状病毒或新冠肺炎无需介绍。它已经被世卫组织宣布为疫情,在过去的几周里,从健康和经济角度来看,它的影响都是有害的。关于它已经写了很多,特别是关于它的指数增长和“”的重要性的统计报告。
截至目前,我们大多数人都呆在家里工作,以避免冠状病毒的传播。我决定利用剩余时间编写一个 Python 脚本,从印度政府卫生和家庭福利部的官方网站中提取新冠肺炎病例的最新数据,并使用流行的 Python 包(如 GeoPandas、Seaborn 和 Matplotlib)将其转化为有见地的可视化。
使用的包
- Beautifulsoup —用于从 html 和 xml 文件中提取数据的库。
- Requests —用于在 python 中发出 HTTP 请求的库。
- GeoPandas —一个用 python 处理地理空间数据的库。
- PrettyTable —在视觉上吸引人的 ASCII 表格中快速而简单地表示表格数据。
- 还有其他常规包像熊猫,Matplotlib,Seaborn。
如果您的系统上没有安装上述任何软件包,请按照相应链接中提到的安装说明进行操作。
(注意,Geopandas 进一步依赖于 fiona 进行文件访问,依赖于 笛卡尔 和matplotlib进行绘图)**
搜集数据
要使用 Python 抓取网站,您需要执行以下四个基本步骤:
- 向您想要抓取的网页的 URL 发送 HTTP GET 请求,该请求将以 HTML 内容进行响应。我们可以通过使用 Python 的请求库来做到这一点。
- 分析 HTML 标签及其属性,例如类、id 和其他 HTML 标签属性。此外,确定内容所在的 HTML 标签。
- 使用 Beautifulsoup 库获取和解析数据,并在某种数据结构(如字典或列表)中维护数据。
- 输出任何文件格式的数据,如 csv,xlsx,json 等。或者使用这些列表数据,使用 Seaborn/Matplotlib 库进行可视化。
如果你是网络抓取的新手,查看这个博客的逐步解释,可以帮助你开始使用 Python 进行网络抓取。
** [## 用 BeautifulSoup 从 HTML 中提取数据
如今,每个人都在谈论数据,以及它如何帮助学习隐藏的模式和新的见解…
www.pluralsight.com](https://www.pluralsight.com/guides/extracting-data-html-beautifulsoup)
跳入代码中
导入必要的库—
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
import requests
from bs4 import BeautifulSoup
import geopandas as gpd
from prettytable import PrettyTable
网络抓取—
url = 'https://www.mohfw.gov.in/'*# make a GET request to fetch the raw HTML content*
web_content = requests.get(url).content*# parse the html content*
soup = BeautifulSoup(web_content, "html.parser")*# remove any newlines and extra spaces from left and right*
extract_contents = lambda row: [x.text.replace('\n', '') for x in row]*# find all table rows and data cells within*
stats = []
all_rows = soup.find_all('tr')for row in all_rows:
stat = extract_contents(row.find_all('td')) *# notice that the data that we require is now a list of length 5*
if len(stat) == 5:
stats.append(stat)*#now convert the data into a pandas dataframe for further processing*new_cols = ["Sr.No", "States/UT","Confirmed","Recovered","Deceased"]
state_data = pd.DataFrame(data = stats, columns = new_cols)
state_data.head()
输出:

在我们继续之前,请注意抓取的数据列实际上是“string”数据类型。我们需要将它们转换成“int”数据类型。
state_data[‘Confirmed’] = state_data[‘Confirmed’].map(int)
state_data[‘Recovered’] = state_data[‘Recovered’].map(int)
state_data[‘Deceased’] = state_data[‘Deceased’].map(int)
您也可以选择使用漂亮的表格来展示数据
table = PrettyTable()
table.field_names = (new_cols)for i in stats:
table.add_row(i)table.add_row([“”,”Total”,
sum(state_data[‘Confirmed’]),
sum(state_data[‘Recovered’]),
sum(state_data[‘Deceased’])print(table)
输出:

(这是撰写本文时最新的可用数据)
条形图—全州确诊病例总数
绘制水平条形图,显示全州确诊病例总数—
sns.set_style(“ticks”)
plt.figure(figsize = (15,10))plt.barh(state_data[“States/UT”], state_data[“Confirmed”].map(int),align = ‘center’, color = ‘lightblue’, edgecolor = ‘blue’)plt.xlabel(‘No. of Confirmed cases’, fontsize = 18)
plt.ylabel(‘States/UT’, fontsize = 18)
plt.gca().invert_yaxis()
plt.xticks(fontsize = 14)
plt.yticks(fontsize = 14)
plt.title(‘Total Confirmed Cases Statewise’, fontsize = 18 )for index, value in enumerate(state_data[“Confirmed”]):
plt.text(value, index, str(value), fontsize = 12)plt.show()
输出:

圆环图-全国确诊、恢复和死亡病例总数
group_size = [sum(state_data[‘Confirmed’]),
sum(state_data[‘Recovered’]),
sum(state_data[‘Deceased’])]group_labels = [‘Confirmed\n’ + str(sum(state_data[‘Confirmed’])),
‘Recovered\n’ + str(sum(state_data[‘Recovered’])),
‘Deceased\n’ + str(sum(state_data[‘Deceased’]))]custom_colors = [‘skyblue’,’yellowgreen’,’tomato’]plt.figure(figsize = (5,5))
plt.pie(group_size, labels = group_labels, colors = custom_colors)
central_circle = plt.Circle((0,0), 0.5, color = ‘white’)
fig = plt.gcf()
fig.gca().add_artist(central_circle)
plt.rc(‘font’, size = 12)
plt.title(‘Nationwide total Confirmed, Recovered and Deceased Cases’, fontsize = 20)plt.show()
输出:

全部确诊病例的氯普图
本文中用来绘制带有邦界的印度地图的形状文件可以从这里的 下载。
# reading the shape file of map of India in GeoDataFramemap_data = gpd.read_file(‘Indian_States.shp’)
map_data.rename(columns = {‘st_nm’:’States/UT’}, inplace = True)
map_data.head()
输出:

我注意到一些州和联邦直辖区(UT)的名称以及形状文件中的名称与政府网站上的州名不一致。因此,我修改了地理数据框架中的州/UT 名称,以便与我们的州数据框架中的名称相匹配。
更正map _ datadata frame中各州的拼写——
*map_data[‘States/UT’] = map_data[‘States/UT’].str.replace(‘&’,‘and’)
map_data[‘States/UT’].replace(‘Arunanchal Pradesh’,
‘Arunachal Pradesh’, inplace = True)
map_data[‘States/UT’].replace(‘Telangana’,
‘Telengana’, inplace = True)
map_data[‘States/UT’].replace(‘NCT of Delhi’,
‘Delhi’, inplace = True)
map_data['States/UT'].replace('Andaman and Nicobar Island',
'Andaman and Nicobar Islands',
inplace = True)*
合并州/UT 名称上的两个数据帧 state_data 和 map_data
*merged_data = pd.merge(map_data, state_data,
how = ‘left’, on = ‘States/UT’)merged_data.fillna(0, inplace = True)
merged_data.drop(‘Sr.No’, axis = 1, inplace = True)
merged_data.head()*
输出:

在印度地图上显示各州的数据—
*fig, ax = plt.subplots(1, figsize=(20, 12))
ax.axis(‘off’)ax.set_title(‘Covid-19 Statewise Data — Confirmed Cases’,
fontdict = {‘fontsize’: ‘25’, ‘fontweight’ : ‘3’})merged_data.plot(column = ‘Confirmed’, cmap=’YlOrRd’,
linewidth=0.8, ax=ax, edgecolor=’0.8',
legend = True)
plt.show()*
输出:

完整的 Python 代码
结束注释
新冠肺炎还没有大规模袭击印度。缺失的数据使得预测未来几个月疫情将如何发展变得不可能。在我结束这篇文章的时候,我为印度和世界各地的每一个人的安全和幸福祈祷。**
编者注: 走向数据科学 是一份以数据科学和机器学习研究为主的中型刊物。我们不是健康专家或流行病学家,本文的观点不应被解释为专业建议。想了解更多关于疫情冠状病毒的信息,可以点击 这里 。
Python 中的冠状病毒谷歌趋势
谷歌搜索的州级分析

冠状病毒是一种在动物和人之间传播的病毒家族。症状包括发烧、咳嗽和轻微的呼吸道疾病。在严重的情况下,感染会导致肺炎和死亡。在写这篇文章的时候,美国疾病控制中心报告了 645 例病例和 25 例死亡。在传染病研究领域,社交媒体数据已被证明是预测感冒和流感季节开始和发展的有用指标。在本帖中,我们将使用谷歌趋势 API 来衡量各州对冠状病毒的参与度。
我们将使用 python Google trends API py trends 来分析州级的“冠状病毒”谷歌搜索。
要安装 pytrends,请打开命令行并键入:
pip install pytrends
接下来,打开一个 IDE(我使用 Spyder)并导入 pytrends:
from pytrends.request import TrendReq
接下来,我们指定主机语言、时区和有效负载。我们将主机语言指定为英语(“en-US”),时区为中部标准时区,即“360”,我们可以将 google 属性过滤器(gprop)保留为空字符串。我们还设置 category = 0,对应于与该关键字相关的所有类别。
让我们看看纽约 2020 年 2 月至 2020 年 3 月的“冠状病毒”数据:
pytrends = TrendReq(hl='en-US', tz=360)
pytrends.build_payload(['Coronavirus'], cat=0, timeframe='2020-02-01 2020-03-10', gprop='', geo='US-NY')
然后,我们将数据框定义为结果 pytrends 对象,并打印前五行:
df = pytrends.interest_over_time()
print(df.head())

我们也可以使用 seaborn 和 matplotlib 来绘制搜索“冠状病毒”的时间序列:
import matplotlib.pyplot as plt
import seaborn as sns
import pandas as pdsns.set()
df['timestamp'] = pd.to_datetime(df.index)
sns.lineplot(df['timestamp'], df['Coronavirus'])plt.title("Normalized Searches for Coronavirus in NY")
plt.ylabel("Number of Searches")
plt.xlabel("Date")

让我们对马萨诸塞州的“冠状病毒”做同样的事情:

我们可以定义一个函数来获取任何状态的数据:
def get_searches(key_word, state):
pytrends = TrendReq(hl='en-US', tz=360)
pytrends.build_payload([key_word], cat=0, timeframe='2020-02-01 2020-03-10', gprop='',geo='US-{}'.format(state))
df = pytrends.interest_over_time()
print(df.head())
sns.set()
df['timestamp'] = pd.to_datetime(df.index)
sns.lineplot(df['timestamp'], df[key_word])
plt.title("Normalized Searches for {} in {}".format(key_word, state))
plt.ylabel("Number of Searches")
plt.xlabel("Date")
让我们为 NY、MA 和 CA 调用这个函数:
get_searches('Coronavirus', 'NY')
get_searches('Coronavirus', 'MA')
get_searches('Coronavirus', 'CA')

我就说到这里,但请随意从其他州提取数据。你也可以随意输入关键字。也许你会想搜索“冠状病毒症状”或“冠状病毒死亡率”。
结论
总之,在这篇文章中,我们使用 python 库“pytrends”提取了与关键词“冠状病毒”相对应的州级谷歌趋势数据。社交媒体数据,如谷歌趋势数据,可能有助于衡量公共卫生相关话题的社会参与度,如冠状病毒。这可能有助于卫生机构,如疾病预防控制中心,定量评估需求,分配资源和沟通预防措施。我希望你觉得这篇文章有用/有趣。这篇文章的代码可以在 GitHub 上找到。感谢您的阅读!
追踪新冠肺炎病毒在印度的传播
研究动态传播率以追踪新冠肺炎在整个次大陆的传播

凯文·斯特罗姆最近发表了一篇名为“我们需要追踪新冠肺炎的一个指标”的文章。这种度量被称为可变传播率,被指定为 R_t。使用美国新冠肺炎的公共数据,Systrom 计算了每个州的有效 R_t 率,并探索了 R_t 在跟踪病毒传播方面的有效性。
除了这篇文章,Systrom 还发布了一个 Python 笔记本,这样其他人就可以即插即用他的实现了。以 Kaggle 最近按州和地区划分的印度新冠肺炎案例数据集为例,有一个完整的世界可供探索。
贝当古-里贝罗公司
Systrom 实现的特定算法最初由流行病学家团队 Bettencourt 和 Ribeiro 于 2008 年发表。他们提出了计算动态 R_t 的贝叶斯方法,该方法将考虑以前的 R_t 和新的观察值——每天新病例的数量——并相应地进行调整。如果您对算法背后的数学和完整推导感兴趣,我建议您浏览之前链接的 Python Notebook,甚至是 Bettencourt 和 Ribeiro 的原始论文。
简而言之,电流 R_t 由下式计算:
- P(R_t|k) ~乘积 L(R_t|k)
Systrom 做了修改。从第 0 天开始跟踪 R_t 公司的原始模型。这有一些长期的影响。第 30 天的模型不会忘记第 1 天 R_t 的高值。这意味着 1 的行为就像一条渐近线,每个状态的 R_t 都是 1,不会更低。
为了对此进行调整,Systrom 让他的模型简单地使用 7 天的移动窗口。任何一天的 R_t 值将基于前 7 天的 R_t。这种可能性受到最高密度区间的限制,以说明数据的彻底性。给定更多的样本大小-更完整的数据集-我们将获得更小和更集中的最高密度区间。
将 Systrom 的算法映射到印度
印度的一项开源努力已经产生了一个开放的各邦新冠肺炎病例数据集。模块化代码使得清除印度数据并将其插入笔记本变得容易。要做的两个小调整是:
- 处理不同的日期格式
- 将每日新病例计数更改为累计病例计数。
从那里开始,事情变得简单明了,我能够为所有的印度州和地区制作一个运行的 R_t 图:

当前印度各邦的 R_t。数据不足 5 天的州被排除在外。未分配的病例是指没有附加州的新冠肺炎诊断。
R_t 的效果如何?
在 2020 年 5 月 1 日发布的最新名单中,印度将各区分为红色区、橙色区和绿色区。红色区域是指病毒增长率高的地区,被划分为热点。绿色区域是指没有新的新冠肺炎病例的区域。橙色区域是有病例报告的地区,但病例比红色区域少。你可以在这里找到那个列表。利用这些分界线,我将这些州分为三类:
- 红色州:红色区域比橙色或绿色区域多的州
- 橙色州:橙色区域多于红色或绿色区域的州
- 绿色州:绿色区域多于红色或橙色区域的州。
R_t 是衡量每一次新冠肺炎诊断会将病毒传播给多少人的指标。更高的 R_t 将意味着该州将是橙色或红色,因为病例数继续扩散。

每个状态 R_t 的条形/须状图。排除最高密度间隔大于 1 的状态
查看该图,我们可以看到,以红色和橙色区域为主的邦往往具有较高的 R_t 值。对于绿色的昌迪加尔邦和恰蒂斯加尔邦,它们都具有较大的最高密度区间。这是因为数据较少,尤其是缺乏日常数据。至于橙色的邦,如旁遮普和泰米尔纳德邦,红色的区域是人口密度高的地区,那里更容易在人与人之间传播,这反映了较高的 R_t。
从一个州的层面来看 R_t 描绘了一幅相当准确的画面。那些 R_t 较高的邦也可能被印度政府指定为新冠肺炎热点。
牢记上下文
这是对病毒如何渗透到人群中的一个有价值的观察,现在的预防措施,如传播的锁定效应。还有一些重要的因素需要记住。
这个模型没有考虑测试。它接受第 k 个新案例,认为数字是准确的。事实上,印度的测试设置并不普遍,只有一定比例的人口接受过测试。毫无疑问,存在未诊断的病例,这将对 R_t 产生重大影响。
这个数据集很难追踪。印度是一个拥有超过 10 亿人口的大国。由于封锁,印度有数百万人被困,其中一些人步行数百英里从主要城市地区返回家园。这个数据集是一个非凡的开源成果,但很难保证它 100%准确,在跟踪像 R_t 这样的衍生工具时应该记住这一点。
未来的工作
我将来要寻找的东西将是如何通过这个模型来解释已经测试过的人口比例。考虑到这一点,我们会对新冠肺炎的传播率有一个更准确的了解。
感谢阅读!如果你喜欢,请随时关注我!你可以在推特@EswarVinnakota 上找到我。如果你想谈谈,请随时通过 DMs 联系我或在LinkedIn上与我联系。
使用 46,166 条推文追踪戴森的公众形象
Google 自然语言 API 和 BigQuery 的一课
这项工作是个人项目,与戴森没有任何关系

作者图片
这个项目开始于新冠肺炎,就像现在所有的事情一样。鉴于我天赋的空闲时间,我决定唯一合理的方式就是编程。作为戴森学院的一名本科生,我认为看看公众如何看待更广泛的公司会很有趣。因此,带着价值 300 英镑的免费谷歌云积分,我决定尝试一下。
碰巧的是,我让我完成的代码运行的那一周是公司历史上裁员最多的一周。这并不意味着要轻视成百上千失去工作的人,它最终成为了这个项目的完美测试环境,并产生了一些非常有趣的结果。如果你不关心我是如何完成这项工作的,请跳到最后查看结果的分类。
我,以我无穷的智慧,决定将这个项目命名为“戴森-泰蒙特”。
项目概述
我在这里的目标是建立一个谷歌云项目,捕获所有包含“Dyson”一词的推文,进行一些基本的情感分析,并最终将其放入 BigQuery 数据库。主要的 Python 应用程序将是一个 Docker 容器,可以轻松可靠地部署到虚拟机上。

Dyson-timent 的逻辑架构(图片由作者提供)
正如你所看到的,即使是这样一个小项目,也有很多活动的部分。该图还遗漏了 Google Secret Manager,我用它来存储我所有的 Twitter API 密钥。
从 VM 实例到 BigQuery 数据库获取 Tweet 数据的管道是使用数据流模板建立的。Python 应用程序将 Tweet 数据(作为 JSON)发布到 Pub/Sub 主题,然后数据流为我处理导入到 BigQuery 的操作。

使用数据流模板抽象出数据处理任务(图片由作者提供)
使用这样的模板大大降低了我这边的复杂性,使我能够专注于项目的其余部分。这是使用 IAAS 提供商的众多卖点之一——他们带走了大量无聊的任务并进行设置。
谷歌主机说够了,我们来说代码。
代码
对于项目的复杂性来说,代码本身实际上并不复杂,因为大部分繁重的工作都是由 Google 的 API 处理的。我们所要做的就是调用它们,这是在 Python 中使用各种 Google Cloud 包完成的,这些包都是使用 pip 简单安装的。
谷歌云凭据
因为我希望在云中的 Docker 容器中运行之前能够在本地运行,所以我需要一种方法来处理两种不同的获取云凭证的方法。当应用程序启动时,它会寻找一个环境变量AM_I_IN_A_DOCKER_CONTAINER 。这个变量是在 docker 文件中设置的,所以如果 Python 找到了它,应用程序肯定知道它正在云中运行,并相应地设置自己。
检测代码是否正在云中运行的策略
在云中运行时,所有凭证都由 VM 实例自动处理。在本地运行时,我使用从 Google Cloud IAM 下载的服务帐户凭证文件(上面的第 16 行),并将其传递给我所有的云服务。
Twitter 流媒体
设置 Twitter 流非常简单,因为文档非常棒。
与 Twitter 流相关的代码
我简单地建立了一个StreamListener-继承类,如图所示,给了OAuthHandler我的 API 密钥和令牌,并把它们全部传递给了一个Stream 。然后通过向流传递一个tracklist来过滤流,它只是一个搜索字符串列表——在我们的例子中是[‘#Dyson’, ‘Dyson’]。
这不是一个完美的解决方案,因为搜索不够智能,无法排除任何与詹姆斯·戴森或该公司无关的“戴森”的提及。然而,我的假设是,大多数包含“戴森”的推文将与我们相关。
此时,任何包含单词“Dyson”的 Tweets 都出现在Listener 类的on_data() 方法中,在这里我们可以对它们做一些有趣的事情。
下面是传入的 Tweets 从字符串解析成 JSON 后的样子。

Python 应用程序在调试中捕获的推文(作者截图)
如你所见,仅仅一条推文就包含了大量的数据。我最终提取的字段只有created_at、id_str、text、user*.*location 和lang。这些给了我大量有价值的信息。
自然语言 API
在这个阶段,我们有来自 Tweet 的相关字段。现在开始处理。我写了一个小函数,analyse_sentiment使用他们的 Python 包将 Tweet 文本和语言代码发送给云自然语言 API 。
因为来自 Twitter 的语言代码包含在我对自然语言 API 的调用中,它可以分析世界上几乎任何语言的推文!我们不局限于简单的古英语。
从自然语言 API 中获取文本并计算和返回情感的函数
这是我喜欢现代云技术的另一个原因;你可以在一行代码(第 7 行)中完成非常复杂的事情,比如语言处理,只需要进入 Google 预先训练好的 ML 模型。
我使用的 API 端点最终返回两个值:情绪和信心。情绪从-1 到+1 排列,其中+1 表示非常积极,而-1 表示非常消极。置信度从 0 到 1 排序,并表示 ML 模型对情感分数正确的“确信”程度。
我做了一个(无可否认是武断的)决定,丢弃任何置信度低于 0.1 的推文,希望提高最终数据的质量。
谷歌地图 API
我从推文中提取的另一个非常有价值的数据是用户的位置。现在我必须承认,做这一切感觉非常老大哥。但是我做这些事是为了一个好的理由——我自己的好奇心。
user.location字段似乎由 Twitter 用户的输入填充,因为它的格式不一致。推文无所不包,从胡言乱语到“美国”到“苏格兰东海岸”。因此,这使得很难转换到适合分析的特定 GPS 坐标。
我又一次被谷歌 API 的魔力所拯救。Google Maps API 本质上允许您在 API 调用中进行 Google Maps 搜索。这是非常有用的,因为 G-Maps 擅长于获取模糊的地名,并给它们分配一个精确的 GPS 坐标。
与analyse_sentiment方法类似,我创建了一个place_search 函数,它获取用户模糊的地名,将其提供给 Google Maps,并将返回的纬度/经度数据转换为 BigQuery 理解为位置数据的形式(我们并没有忘记这些数据最终会出现在哪里!).
这种格式就是“众所周知的文本表示”(由此而来的wkt_loc)。
这样一来,剩下唯一要做的事情就是发送一个包含所有情绪和位置数据的 JSON 字符串到 Pub/Sub 主题!
这显然不是全部代码,但是这篇文章越来越长了。完整的代码,请查看我的 GitHub repo。
让我们从细节上退一步。
我们现在有代码可以通过关键字/短语来传输和过滤推文,确定推文的情绪是积极还是消极,确定推文来源的一组合理的 GPS 坐标,然后将其发送并存储在数据库中。
我觉得这太酷了。
完成了!
代码完成后,就可以部署到容器优化的计算引擎上了。因为我只做了一次,所以我使用云控制台用我的项目的最新 Docker 构建来启动一个 VM 实例。于是,在 2020 年 7 月 18 日,它开始运行。
对我的工作感到满意,我让它大部分时间在后台运行,时不时地回来检查错误,以确保我没有太快用完我的谷歌云积分(我就是这样)。
然后,在 7 月 23 日,戴森宣布,由于新冠肺炎的影响,它将裁员 900 多人。这个项目通过 Twitter 捕捉到了所有这些。
经过整整 8 天的监控,以及 BigQuery 中处理和呈现的超过 46,000 条推文,当我只剩下最后 50 个信用点时,我终于决定拔掉插头。

46166 条推文(作者截图)
是时候通过开放数据来看看这一切是否值得了。
数据
啊,我们期待已久的时刻。如果你是从上往下跳的,请再打个招呼。
随着数千条推文在我的 BigQuery 数据库中烧出一个洞,我开始分析数据。为此,我做了一个 Google Data Studio 项目,因为它非常容易与 BigQuery 交互,并且可以生成非常好看的 web 报告。
首先要做的是查看平均情绪和一段时间内推文数量的图表。

数据!请看相关的数据工作室项目(图片由作者提供)
你能看看那个吗?在大多数日子里,平均情绪徘徊在 0.1 至 0.3 之间,我们可以将其解释为总体积极的情绪。
然而,在裁员的那天(23 日),人气大幅下滑至-0.2!这清楚地表明代码正在工作!
不过,从 24 日开始,我们看到未来两天将出现大幅反弹,创下新高。推文数量也出现了大幅增长。这是为什么??我想不通。
我拥有的最后一点数据是每条推文的来源位置。我在我的 Data Studio 项目中设置了一个 Google Maps visualiser,用于查询数据库中的所有内容。点的颜色基于情感,其中-1 是深红色,0 是灰色,+1 是绿色。让我们来看看。

每条推文的位置。红色是阴性,灰色是中性,绿色是阳性(图片由作者提供)
我们来剖析一下这个。大多数推文来自英国、美国和日本。戴森在所有这些国家都有很强的影响力,所以这是意料之中的。
日本有着压倒性的积极情绪,整个国家都是绿色的。相比之下,英国似乎大多是红色和中性。典型的英国人,总是消极的。
这显示了每天的所有数据,所以让我们在这里按日期深入查看。下面的 gif 循环显示每天的信息。顶部的滑块告诉你它在 7 月的哪一天上映。

按日期分类的推文(按作者分类的图片)
很酷吧。
除了 23 号——宣布裁员的那一天,英国每天的情况都很复杂。全球似乎也普遍存在负面影响。
看看日本的反应,我们得到了 24 日和 25 日积极反弹的线索。日本总是非常绿色,但在那些日子里,它明显更密集的推文。
24 日日本发生了什么让大家这么积极?!
我在 Twitter 上搜索 7 月 24 日包含“Dyson”一词的日文推文。你瞧,我发现了下面的推文。

导致推文数量激增的推文(作者截图)
21800 条转发?!这几乎是我收集的所有推文的一半!推特上说了什么?我用谷歌翻译了一下。

(作者截图)
谜团解开了!日本大型家用电器公司山田电机(Yamada Denki)向一名转发并关注其 Twitter 账户的幸运儿赠送了一台新的戴森吸尘器。该项目一定收集了每一条转发,以及由此引发的所有其他推文。
事实上,让我们找到更多的证据。我会在地图上搜索 24 号和 25 号之间的“yamada”这个词。根据推特,有 21800 人转发了这条微博。我们的数据库里应该有类似的数字。

在互动地图中搜索山田(图片由作者提供)
20700 条推文,还不错!在这一点上,这似乎是非常确凿的,你不同意吗?
总之…
对于基于云的项目来说,Google Cloud 是一个非常有价值的工具。易于访问的 API 和 tick-box 环境设置极大地减少了您需要做的(从根本上)非常复杂的工作,如情感分析。即使对我这样的初学者来说。
我希望你觉得这很有趣,甚至可能启发你做一个类似的项目!
如果你想玩交互式地图,请查看我的数据工作室项目
完整代码见我的 GitHub repo 。
使用 tqdm 跟踪大型作业
TQDM 是 Python 的一个易于使用、可扩展的进度条包。这篇短文给了你一些有用的例子来帮助你开始。

Unsplash 上的 Volodymyr Hryshchenko
追踪你的进度
你真的无法击败一个伟大的,高质量的小包装,使你更有生产力。tqdm就是这样一个包。这是一个易于使用、可扩展的进度条 Python 包,使得向 Python 进程添加简单的进度条变得极其容易。如果你是一名专业的数据科学家或机器学习(ML)工程师,你可能已经使用或开发了算法或数据转换,这可能需要相当长的时间——可能需要几个小时甚至几天——才能完成。
对于人们来说,选择简单地将状态消息打印到控制台并不罕见,或者在一些稍微复杂一些的情况下,使用(优秀的和推荐的)内置logging模块。在很多情况下,这是好的。然而,如果您正在运行一个有数百个步骤的任务(例如,训练时期),或者运行一个有数百万个元素的数据结构,这些方法有时会有点不清楚和冗长,坦率地说有点难看。另外,在你的代码中加入一些“开发者体验”的东西(比如进度条!)也可以让你的代码更加用户友好。
给我看看代码!
这就是tqdm的用武之地。它有一个漂亮干净的 API,可以让你快速地在代码中添加进度条。此外,它还有一个轻量级的“剩余时间”估计算法内置在进度条中。在一些 ML 包中使用了tqdm包,其中最突出的可能是implicit,一个 Python 隐式矩阵分解库。在这篇文章中,我们来看看下面这个使用tqdm的模拟“训练循环”的例子:
import time
from tqdm import tqdm with tqdm(total=100) as progress:
for i in range(100):
time.sleep(0.25)
progress.update(1)
在这个简单的例子中,您设置了一个tqdm进度条,该进度条预计包含 100 个步骤。然后,您可以运行循环(步骤之间有 0.25 秒的停顿),每次在步骤完成时更新进度条。如果你也跳出了循环,你也可以任意数量的更新进度条。这是两行代码(加上 import 语句),可以在代码中得到一个很酷的小进度条。以下是您得到的结果:

一个简单的 tqdm 进度条!Mark Douthwaite 截图
熊猫集成
除了给你的程序输出添加一些很酷的小东西之外,tqdm还可以很好地与其他广泛使用的软件包集成。对于数据科学家来说,最有趣的集成可能是 Pandas,无处不在的 Python 数据分析库。看看下面的例子:
df = pd.read_csv("weather.csv")
tqdm.pandas(desc="Applying Transformation")
df.progress_apply(lambda x: x)
从技术上讲,tqdm.pandas方法猴子将progress_apply方法修补到熊猫的数据结构上,给它们一个常用的apply方法的修改版本。实际上,当我们调用progress_apply方法时,这个包用一个tqdm进度条包装了标准的 Pandas apply方法。当您处理大型数据帧时,这非常方便!

一个进度条的例子,由 Mark Douthwaite 截图的**tqdm**与pandas. 的集成生成
平行过程
这里还有另一个值得一提的常见应用:tqdm对于设置跟踪并行进程的进度条也很有用。下面是一个使用一些tqdm内置支持更新平行贴图进度条的例子:
import time
from tqdm.contrib.concurrent import process_map def my_process(_):
time.sleep(0.25)
r = process_map(my_process, range(0, 100), max_workers=2, desc="MyProcess")
在这种情况下,在每个子流程中,每次my_process调用完成时,都会有一个进度条更新。不过还有第二个用例:如果您有许多长时间运行的流程,并且希望单独跟踪它们,该怎么办?例如,如果您想避免在进程中序列化和反序列化大型对象,这可能更好。你也可以这样做:
import time
import multiprocessing as mp
from tqdm import tqdm def my_process(pos):
_process = mp.current_process()
with tqdm(desc=f"Process {pos}", total=100, position=pos) as progress:
for _ in range(100):
time.sleep(0.1)
progress.update(1) n_cpu = mp.cpu_count()with mp.Pool(processes=n_cpu, initializer=tqdm.set_lock, initargs=(tqdm.get_lock(),)) as pool:
pool.map(my_process, range(n_cpu))
这将为您提供类似以下内容的输出:

一个进度条同时跟踪 8 个任务的例子。Mark Douthwaite 截图
还有一个要点这个例子你也可以用:
一些示例代码,只给你!💛
还有一点
另外,tqdm在 Jupyter 笔记本中运行时,提供了对进度条的支持,作为格式良好的小部件。例如,在笔记本中运行以下代码:
import time
from tqdm.notebook import tnrange, tqdmfor i in tnrange(10):
time.sleep(0.1)
会产生以下结果:

Jupyter 的进度条!Mark Douthwaite 截图
您可能会注意到您在这里重新导入了tqdm。当您想要直接使用tqdm对象时,您应该以这种方式导入tqdm(如上例所示)。您应该能够直接放下上面的示例片段,并立即获得好看的进度条。哦,请确保从示例中删除time.sleep行!
原载于https://mark . douthwaite . io。
在计算机视觉应用中跟踪 Raspberry Pi 内部温度

运行计算机视觉应用程序(或一般的人工智能应用程序)会给边缘设备带来压力,尤其是在组合模型时。使用散热器和风扇可能足以防止设备节流,但在您的设备变得这么热之前得到通知会很有帮助。收到潜在不安全温度的警报后,您可以在设备烧毁之前停止应用程序,并继续监控温度——当设备充分冷却后,您可以选择重新启动程序。
为此,我们创建了一个实用程序类,使您能够跟踪和记录温度读数,并提供所收集数据的摘要。
本教程使用一个 Raspberry Pi 4 和包“gpiozero”,它默认安装在 Raspbian 操作系统映像上。欲了解更多关于此包的信息,请参见以下链接。
要完成本教程,您必须具备:
- 一个 alwaysAI 账号(免费!)
- 在你的机器上设置 alwaysAI(也是免费的)
- 诸如 sublime 之类的文本编辑器或者诸如 PyCharm 之类的 IDE,或者任何你喜欢用来编码的东西
请参见 alwaysAI 博客了解更多关于计算机视觉、开发模型、如何改变模型等等。
本教程的所有代码都可以在 GitHub 上获得。
本教程有两个主要部分:
- 添加 GPIO 零要求
- 初始化 app.py 文件中的温度跟踪器
我们开始吧!
当你有了你的免费账户并建立了你的开发者环境后,你需要下载所有的入门应用;在继续本教程的其余部分之前,使用此链接完成操作。
为本教程修改的应用程序是“对象检测器”应用程序,因此 cd 进入 starter apps 文件夹,然后进入“realtime_object_detector”文件夹。
- 添加 GPIO 零要求:
- 首先,将 requirements.txt 文件添加到您的文件夹中。
- 将内容“gpiozero”添加到 requirements.txt。这将在您部署应用程序时安装。有关依赖关系的更多信息,请参见 alwaysAI 文档。
注意:如果您的 Pi 没有连接 wifi,将无法下载 requirements.txt 中的需求
2.更新 app.py 文件以使用温度跟踪器:
- 通过在“app.py”的顶部添加以下行来导入温度跟踪器代码:
from temperature_tracker import TemperatureTracker
- 在打印描述模型、引擎、加速器和标签的语句后,我们将创建 TemperatureTracker 实例。添加以下代码行:
temperature_tracker = TemperatureTracker()
- 现在,我们需要标记开始记录温度数据的时间。在 try/with 块内,但在“while”循环之前,在启动 FPS 实例后立即添加以下代码:
temperature_tracker.start()
- 要实际捕获温度数据,请调用“update”方法。将以下代码放入 while 循环中:
temperature_tracker.update()
注意:为了可读性,我在“预测中的预测”循环之后和温度语句之前添加了一个空行。这可以在任何时候使用' text.append(" ")在流文本输出中获得更好的可读性。
- 要仅获取当前读数,而不修改存储的数据,可以使用“now”方法。这将返回温度和相应的时间戳。为此,请在上一步中添加的 update 调用之后添加以下代码行:
now = temperature_tracker.now()
- 我们可以将当前温度附加到发送给 streamer 的文本中,并检查返回的温度是否安全(至少对于 Raspberry Pi 4 是如此)。在前面两步中所做的更改下添加以下代码:
# log block showing current temperature
text.append(“{:1.2f}C/{:1.2f}F at time {}\n”.format(now[0], ((now[0]*(9 / 5)) + 32),time.strftime(‘%Y-%m-%d %H:%M:%S’, now[1])))# details whether the temperature is safe for a Raspberry Pi 4
if now[0] < temperature_tracker.MAX_TEMP_RASP4:
text.append(“Temperature is safe”)
else:
text.append(“TEMPERATURE IS NO LONGER SAFE”)
注意:如果您在应用程序停止之前没有在流媒体工具上看到警告消息,这意味着在应用程序停止之前浏览器提要没有刷新。一旦程序停止,您仍会在控制台上看到此警告消息。
- 在调用 fps.update()后,添加以下检查:
if now[0] >= temperature_tracker.MAX_TEMP_RASP4:
print(**“Maximum safe temperature reached, stopping program”**)
break
- 最后,在最后一个块中,在停止 FPS 实例后,通过添加以下内容来标记停止跟踪器的时间:
temperature_tracker.stop()
- 通过在“程序结束”打印语句前添加以下几行,捕获并打印温度汇总数据:
summary = temperature_tracker.summary()
print(summary)
就是这样!现在,您可以构建并启动您的应用程序来查看它的运行情况。您可能需要先配置应用程序,尤其是在您更改了边缘设备或从头开始创建新文件夹的情况下。您可以使用以下命令完成此操作,并在出现提示时输入所需的配置输入:
aai app configure
现在,要查看您的应用程序的运行情况,首先通过在命令行中键入以下内容来构建应用程序:
aai app deploy
构建完成后,使用以下命令启动应用程序:
aai app start
打开浏览器 localhost:5000,查看应用程序的运行情况,并在终止程序后观察控制台,查看您的温度数据摘要,如下所示。

一次追踪一颗子弹
如何在 Excel 中创建项目符号图表
假设您需要快速了解我们如何实现季度目标。好吧,让我们看看。

定性范围内的实际与目标
这给了你一个好主意吗?
几乎没有必要解释这个图表的元素。直观上很清楚。每项措施都有一个目标,成就栏嵌入在定性带中,告诉我们如何评估绩效。

这种可视化表示因其与众不同的外观而被称为“子弹图”。斯蒂芬·菲勒在他关于视觉数据交流的书中推广了这种方法。
在 Excel 中创建项目符号图
项目符号图可能是显示实际数据与目标数据的有效方法,但它并不作为一种现成的图表类型得到普遍支持。
让我们在普通 Excel 中创建一个项目符号图。
我将在 Mac 上使用 Excel 2016 进行截图。希望您的 Excel 版本(可能是未来的版本)足够相似,能够轻松地理解图表的结构。
在 Excel 术语中,基本思想是为定性带创建一个堆叠柱,并在次轴上为当前值创建一个标记线和细长柱。

我们的项目符号在 Excel 图表中的构建计划
我们将从一个单一的措施开始,一旦看起来没问题,我们将添加更多的“项目符号”。
我们走吧。
从数据开始
先看定性波段。我们将堆叠波段,因此每个波段都是根据其大小来定义的,而不是范围。
对于我们的第一张图表,让我们创建从差到优秀的等级。叠加它们的值给我们的总数是 125%。
我们还需要一个当前值和一个目标。我选择了 88%的现值和 100%的目标值。

我们第一个项目符号图的数据
创建堆积柱形图
我们需要一个堆积柱形图来形成我们的定性波段。我们将使用一些自定义格式从堆栈中取出当前值和目标值。
创建一个堆积柱形图并切换行/列,这样我们就有一个单一的柱形图。

第一步—创建堆积柱形图
创建目标标记
接下来,我们将分解目标值。
- 将目标值变为次轴上的标记线
- 删除图表中的次坐标轴,以便所有值使用相同的刻度
- 根据您的喜好格式化标记的外观

将目标分解为副轴上的标记线
格式化当前值
我们准备从堆栈中取出当前值。
- 将当前值移动到副轴。
- 增加其间隙宽度,使其看起来更薄。您可能需要修改图表区的宽度以获得正确的比例。
- 为条形使用合适的颜色。

将当前值分解为辅助轴中的一列
着色和润色
我们已经有了一个还过得去列表。根据您的喜好调整带子和记号笔的颜色。也许调整规模,以适应我们的范围 125%的最大成就。
完成后,您的图表应该如下所示:

单个度量的项目符号图
扩展到多个度量
我们现在想扩展图表以包含多个项目符号。每格一颗子弹。
首先在数据表中以列的形式提供附加度量的数据。一旦就位,您可以简单地拖动图表的数据区域来覆盖您的度量。

最后润色
添加一个图例,并对位置、比例和颜色进行一些最后的润色,应该会给你一个令人满意的最终结果。

我们已经做到了:用一个子弹图来跟踪定性区域中的多个目标
开始了。我们一次一颗子弹地追踪我们的目标…
追踪冠状病毒:你的国家、州或县的现状

在过去的一个月里,我一直在构建一个仪表板来帮助跟踪新冠肺炎的传播。它展示了世界各地确诊病例的最新数字,由纽约时报和 T2 提供,由约翰霍普金斯大学系统科学与工程中心(CSSE)的新冠肺炎数据仓库提供。
我的目标是尽可能快和清楚地提供这些信息,并且大规模地这样做,以便任何需要它的人都能得到它。
使用仪表板
可以通过以下链接找到仪表板:
您可以使用此工具查找从 1 月下旬开始的案例的当前和历史信息。

无论你是想看看其他国家是如何渡过难关的,还是想查查自己家后院有多少病例,都在一个地方,很容易找到。页面包括
- 显示不同地区病例相对数量的地图
- 疫情期间每日新增病例和总病例图
- 按国家、州或县列出的总病例数和死亡数表
- 能够回放和查看过去任何一天的信息



要找什么
随着各州开始重新开放,我们都在警惕潜在的第二波病毒。像纽约这样的州处于第一条曲线的另一边。虽然新病例的数量一直在减少,但我们不能确定一旦解除居家禁令后会发生什么。

随着各州重新开放经济,我们肯定会密切关注纽约和类似情况的地方,以确定潜在的新热点和回归热点。
例如,佛罗里达州迈阿密戴德县的病例数也有类似的情况。

另一方面,一些州可能仍然处于曲线的向上部分。


为什么它很重要
如果有一件事是清楚的,那就是冠状病毒会一直存在。然而,使用像这样的工具,我们可以确定病毒在世界不同地区传播的速度。
我们可以使用上面的曲线来确定在一段时间内具有相似病例分布的国家、州或县。通过这样做,我们从他们的经验中学习如何最好地前进。
虽然我们都在努力让曲线变平,但我的目标是提供一个清晰的画面,让我们能够做出明智的决定,帮助我们回到光明的未来。
我将很高兴收到以上任何反馈。让我知道你喜欢仪表盘什么,不喜欢什么!接下来我应该添加什么功能?欢迎在评论中告诉我,或者可以通过 Linkedin 或者 areevesman@gmail.com 的电子邮件联系到我。
穿越时空追踪冠状病毒

简介
全世界都在看着,新冠肺炎的影响与日俱增。当我们尽自己的一份力量来使曲线变平并战胜疫情时,病毒几乎已经传播到了世界的每个角落。
知识就是力量,有无数的资源可以帮助我们了解这种病毒。约翰·霍普金斯大学系统科学与工程中心的仪表盘,比如这个,给我们提供了当前世界范围内的病例和死亡人数。然而,我很难找到能让我回到过去的东西。
为了击败这种病毒,重要的是不仅要了解我们今天在哪里,而且要了解我们曾经在哪里。回顾过去的能力让我们知道疾病传播到了哪里,传播速度有多快。因此,我们对自己的现在和未来有更多的控制权。
仪表板
因为我一定不是唯一有这种感觉的人,所以我在这里创建了一个仪表板:
您可以使用这个仪表板为 1 月下旬以来的任何一天生成地图和表格。提供了(几乎)每个国家以及美国各州和县的病例和死亡人数。只需选择你想看的信息。
地图是交互式的,因此您可以放大并悬停以获取更多信息。

数据源
《纽约时报》和约翰·霍普金斯大学系统科学与工程中心正在 GitHub 上提供每日新冠肺炎病例计数文件。链接如下所示:
- 纽约时报
- 约翰·霍普金斯大学
Will has lett 的这个库收集了这些文件并实现了以下管道(更多细节可以在 README 中找到):
- 导入纽约时报和约翰霍普金斯大学的数据
- 在需要时执行类型转换
- 添加人口统计数据
- 将结果数据结构输出为一组长格式的时间序列。
有关数据收集过程中的任何注意事项或困难,请参见上面每个存储库中的自述文件。
我的存储库包括笔记本,这些笔记本从上面的存储库中提取并聚合数据以及应用程序源代码。该应用程序完全是使用 Plotly 和 Dash 在 Python 中创建的
我将很高兴收到以上任何反馈。我总是可以在 Linkedin 或 areevesman@gmail.com 的电子邮件上找到我。
使用地理空间分析追踪冠状病毒的传播
研究地理空间分析如何让我们预测冠状病毒如何传播

现场电晕病毒仪表板
我们的开源仪表板跟踪冠状病毒
重要的事情先来。
这个周末,我们在 Locale 的团队接手了一个小项目,来构建一个非启示录式的、更友好的、最小化的、易于使用的可视化 Covid19 仪表盘。该仪表板可帮助您在冠状病毒爆发时实时跟踪其爆发情况。在这里可以访问仪表盘。这个项目的数据源可以在这里找到,我们为这个项目使用的技术栈是 Vue.js , MapboxGL , DeckGL , Node.js 。
最好的部分是我们已经把代码开源了(所以请随意贡献)

科罗娜简史
冠状病毒(CoV)是一个大的病毒家族,可导致从普通感冒到更严重疾病的疾病,如中东呼吸综合征(MERS-CoV) 和严重急性呼吸综合征(SARS-CoV) 。世卫组织最近宣布一种新型冠状病毒为疫情,这种病毒已经传播到至少 114 个国家,并导致全球约 4600 人死亡。
下面是新型冠状病毒传播的简要时间表

2019 年 12 月
中国提醒世卫组织注意武汉出现的几例不寻常的肺炎病例。武汉是湖北中部的港口城市,人口 1100 万。病毒是未知的。
2020 年 1 月
中国开始出现冠状病毒阳性病例。中国首例死亡病例是一名与武汉海鲜市场有过接触的老人。截至 1 月底,中国报告了 7711 例阳性病例和 170 例死亡病例。这种新型冠状病毒还传播到了泰国、日本、俄罗斯、西班牙、瑞典和英国。
2020 年 2 月
致命的冠状病毒(新冠肺炎)蔓延到更多的国家。全球死亡人数超过 4600 人,阳性病例超过 126100 例。世卫组织宣布这种新型冠状病毒为疫情,影响除南极以外的所有大陆。世界各地的卫生专家正在夜以继日地寻找治疗这种不寻常的导致肺炎的冠状病毒的方法。
我们如何将地理空间分析用于疾病跟踪?
地图导致了流行病学的诞生。1854 年,当霍乱在伦敦爆发时,每个人都认为是空气中的微粒造成的。当时的医生琼恩·雪诺在伦敦地图上标出了所有的霍乱病例,并发现病因不是污浊的空气,而是来自街道水泵的污染水!

图片来源:维基百科
这一有史以来第一次进行的位置情报演习还催生了流行病学领域——研究疾病的发病率、分布和控制!
地理参照疾病案例为健康/环境联系提供了一个空间维度。这不仅有助于查明问题,还能描述原因或结果的强度或范围。我们可以通过识别可操作且具有生物学意义的数据模式、预测未来风险和流行轨迹以及描述一系列干预情景下的可能损失来实现这一目标。
地图有助于突出局部问题(如暴露于处置场所的疾病)和更广泛的问题(如暴露于大气臭氧减少的辐射)。它们可以作为预警工具。
隐藏在众目睽睽之下的交易模式

股票市场中有更长期的可交易模式,你不需要成为专业交易员或统计学博士也能搞清楚。
我在投资研究公司和资本市场数据供应商的销售和业务开发部门工作了 14 年。我现在正在将我的职业生涯从数据销售转向数据分析。我正在学习许多关于人工智能、机器学习以及如何使用 Python 代码来利用这些技术进行投资的很酷的东西。我发现学习新事物最快的方法是尝试去教它。所以我想分享一下我在学习过程中所学到的东西。
去年夏天,我去了一个零售“交易展”,发现了一个我认为值得研究的想法。这个想法是,有一些股票在一年中的特定时间可靠地上涨,年复一年,往往不是这样。在展会上谈论这一想法的演讲者当然是在推销一种订阅提醒服务,以便在发现这种交易“机会”时通知投资者。
但我想,“等一下。任何人都应该能够通过查看历史价格模式来验证这一想法,而我自己就有这样的技能和工具!”我是 Excel 高手,我最近学会了一些 Python 技巧,我知道如何从雅虎下载免费的历史价格数据。为什么不看看这个好玩的呢?
因此,我用 Python 编写了一个脚本来下载数据,运行一些统计数据,并将数据导出到一个潜在交易列表中,每个股票都有一个支持的 Excel 表格。我是这样做的。
第一步——从那些规模大、信誉好、盈利能力强的公司开始,这样我就有足够的历史数据可以利用,而且短期内我不必太担心基本面。我选择从标准普尔 500 指数中的上市公司开始。
接下来,我从哪里获取数据?幸运的是,雅虎财经免费提供大多数股票的完整历史收盘数据。雅虎的每个历史数据页面都包括一个免费的“下载数据”链接,只需几行 VBA 代码就可以轻松插入 Excel。此外,一个名叫的 Python 天才建立了一个免费的库 yFinance ,它用 Python 以最少的代码从雅虎下载数据。上帝保佑他!!
最后,现在我有了所有的数据,我应该计算什么样的指标才能让我找到表现出可靠的季节性表现模式的股票呢?我对“可靠”的定义是百分之八十的时间。那符合我的风险承受能力。我想找到在一年中可预测的时间上涨的股票,至少十年中有八年是这样。
我如何定义“可预测”?我决定查看 1 个月、2 个月和 3 个月的滚动时间段,每天递增,计算代表理论持有期结束的每个交易日的百分比回报。为了找到一个可靠的模式,我寻找稳定的时间段,跨越几天或几年,持有期回报持续为正。就百分比而言,这是大多数交易者的想法,我想找到这样的情况:一年中至少有 25 个交易日(10%),最好是连续几天,一只股票在过去十年或更长时间里至少有 8 个交易日表现稳定。
如果您要在 Excel 电子表格中直观显示这一点,每列代表一年,每行代表一年中的一个交易日,每个单元格包含一个持有期的百分比回报计算,以绿色显示正回报,您将会看到绿色的实心块遍布整个工作表。
体育用品制造商 VF 公司(纽约证券交易所代码:VFC)的情况如下。每一列都是一年,从 2001 年到 2019 年。每一行是一天,从三月到四月。这张表显示 VF 的股票价格在过去 20 年的 16 到 17 年中每年的 3 月和 4 月都在上涨。事实上,VF 的趋势周期从 2 月开始,3 个月的平均收益约为 6%。乘以四,你会得到大约 24%的年化收益率。

VFC 3 月至 4 月的 3 个月滚动回报
然而,我不想花几个小时查看 500 个电子表格。而是写了一个脚本,计算每只股票的一组统计数据,告诉我几分钟内想知道的事情。以下是我为每只股票计算的一些数据。
%就 VF Corp .而言,这一比例约为 18%。这意味着在 250 个交易日中的 45 个交易日,你有 80%的机会获得正回报,从而结束了 3 个月的持有期。这 45 天的大部分时间是在二月到四月。我更愿意看到这个数字在 10%或以上。
平均上涨回报率:回报率为正的每个滚动期的平均回报率。如上所述,VF 在我的计算中显示为 6%。这一开始听起来并不多,但是请记住,它只需要三个月就能实现。如果你将其乘以 12 个月,你会得到约 24%的年化回报率。
Avg StDev :所有滚动周期的收益标准差。这意味着预期回报应该在平均 67%的时间的+/-范围内。对于 VF,平均 StDev 为 11.4%。
%下跌:预期平均收益减去标准差。这意味着你至少应该在 67%的时间里超过这个回报。VF 公司的平均标准差为 11.4%。6.01%减去 11.37%等于-5.4%。这意味着你可能会在几年内损失 5.4%或更多。这是一个很好的止损阈值,它给了你一个风险与回报的指标。
最小痛点:最佳情况下,在最佳时机内下降 67%的时间间隔。这用于确定最低风险时间范围,即进入和退出交易的最佳日期。
最大连续 80%:10 年中至少有 8 年获得正回报的最长连续滚动周期数。你希望看到超过连续几年的上涨,以保证趋势是强劲的。对于 VF 来说,这个数字是 38 天,这已经很不错了。我通常喜欢看到 20 个或更多。
在过滤了超过 10%的上涨百分比和至少 10 年的历史后,我列出了大约 37 只股票,这些股票在 10 年中至少有 8 年显示出可靠的季节性上涨趋势。
我会建议立即执行这个清单上的交易吗?号码
首先,这种策略只有在整体市场上涨时才是可靠的。在查看了每只股票的回报细节后,我发现大多数股票在 2000 年至 2002 年、2008 年至 2009 年期间一直呈现负回报,许多股票在 2015 年和 2018 年底呈现负回报。然而,当我修改代码以生成一个卖空候选人列表时,没有一个符合连续年下跌期的 80%阈值。
因此,这种技术将简单地产生一个建议的多头交易列表,其中赔率肯定是对你有利的。但是在执行之前,您应该仔细看看每一个。在交易之前,你至少应该检查每只股票的技术和基本面。您可能还想在不同的回看时间范围内运行这个脚本。
时间框架很重要。我分析了 20 年的历史时间框架,但也许 5 年或 10 年更合适。例如,看看苹果公司(纽约证券交易所代码:AAPL)。回顾过去 20 年,交易 AAPL 的最佳 3 个月时间是每年的 2 月 1 日到 4 月 30 日。但是如果你看下面这张 AAPL 的表格,你会发现你只有 75%的时间有正回报。这对一些人来说可能没问题,但对我来说不是(白色太多,绿色不够)。

AAPL 3 月至 4 月的 3 个月滚动回报
然而,如果你向下滚动工作表,查看过去 10 年中从 9 月中旬到 10 月下旬结束的时间段,你会看到更多的纯绿色。这个时间框架给你 80%的机会获得持续的正回报。

AAPL 3 个月滚动回报 9 月至 10 月
这种模式背后的原因可能与苹果每年 9 月宣布新品发布的习惯有关。如果可以的话,深入了解每家公司并找出这些模式背后的基本驱动力会有所帮助。
对于 Python 代码,以下是基本步骤:
- 从 Yahoo 下载数据,并将其保存在 CSV 文件中:
for index, ticker in df_sp500_tickers.iterrows():
my_ticker = ticker['Symbol']
yf_ticker = yf.Ticker(my_ticker)
data = yf_ticker.history(period="max")
df = pd.DataFrame(data)
df.reset_index(level=0, inplace=True)
df['Symbol'] = my_ticker
df = df[['Symbol','Date','Close']]
#Yahoo has a tendency to duplicate the last row.
df.drop_duplicates(subset ="Date", keep = 'first', inplace = True)
df.to_csv(path_or_buf = my_path + "/data/" + my_ticker +".csv", index=False)
2.基于每月 20 个交易日,将价格转换为持有期回报。
def convert_prices_to_periods(): # dperiods is defined as 20, 40 or 60 days in a separate module
dfr = df.pct_change(periods = dperiods)
dfr.reset_index(level=0, inplace=True)
dfr.rename(columns={'Close':'Returns'}, inplace=True)
dfr = dfr.round(4)
3.将日期列分成月、年和日值,然后旋转数据框以创建一个顶部带有年份的表格。
def separate_date_column():
dfr['Month'] = pd.DatetimeIndex(dfr['Date']).month
dfr['Day'] = pd.DatetimeIndex(dfr['Date']).day
dfr['Year'] = pd.DatetimeIndex(dfr['Date']).year
dfr['M-D'] = dfr['Month'].astype(str)+'-'+dfr['Day'].astype(str)
pd.set_option('display.max_rows', len(dfr))def pivot_the_table():
dfr_pivot = dfr.pivot(index='M-D', columns='Year', values='Returns')
dfr_pivot.reset_index(level=0, inplace=True)
dfr_pivot = pd.DataFrame(dfr_pivot)
dfr_pivot.columns.name="Index"# Fill empty cells with EOD values from the previous trading day.
dfr_pivot.fillna(method='ffill', inplace=True)
# Drop the 2020 year column.
if 2020 in dfr_pivot.columns:
dfr_pivot.drop(2020, axis=1, inplace=True)
4.添加额外的计算列,以便于每种股票的统计计算。
def add_calculated_items():
# The lookback figure is the number (must be an integer) of years back from last year (2019) that you want to include in the analysis, i.e. the calculations below. It's probably a good idea to keep it at 20 years or less to reflect more recent market conditions. lookback = 20
start = 1
if lookback > len(dfr_pivot.columns) - 1:
start = 1
else:
start = len(dfr_pivot.columns) - lookback
dfr_pivot['YearCount'] = dfr_pivot.count(axis=1, numeric_only=True)
dfr_pivot['Lookback'] = lookback
dfr_pivot['UpCount'] = dfr_pivot[dfr_pivot.iloc[:,start:len(dfr_pivot.columns)-2] > 0].count(axis=1)
dfr_pivot['DownCount'] = dfr_pivot[dfr_pivot.iloc[:,start:len(dfr_pivot.columns)] < 0].count(axis=1)
dfr_pivot['PctUp'] = dfr_pivot['UpCount']/dfr_pivot['Lookback']
dfr_pivot['PctDown'] = dfr_pivot['DownCount']/dfr_pivot['Lookback']
dfr_pivot['AvgReturn'] = dfr_pivot.iloc[:,start:len(dfr_pivot.columns)-6].mean(axis=1)
dfr_pivot['StDevReturns'] = dfr_pivot.iloc[:,start:len(dfr_pivot.columns)-7].std(axis=1)
dfr_pivot['67PctDownside'] = dfr_pivot['AvgReturn']-dfr_pivot['StDevReturns']
dfr_pivot['MaxReturn'] = dfr_pivot.iloc[:,start:len(dfr_pivot.columns)-9].max(axis=1)
dfr_pivot['MinReturn'] = dfr_pivot.iloc[:,start:len(dfr_pivot.columns)-10].min(axis=1)
5.计算股票滚动持有期的交易统计数据。
def calc_trading_stats():
pct_uprows = (dfr_pivot.loc[dfr_pivot['PctUp'] > threshold, 'PctUp'].count() / dfr_pivot.loc[:, 'PctUp'].count()).astype(float).round(4)
max_up_return = dfr_pivot.loc[dfr_pivot['PctUp'] > threshold, 'MaxReturn'].max()
min_up_return = dfr_pivot.loc[dfr_pivot['PctUp'] > threshold, 'MinReturn'].min()
avg_up_return = dfr_pivot.loc[dfr_pivot['PctUp'] > 0.5, 'AvgReturn'].mean()
avg_up_return = np.float64(avg_up_return).round(4)
avg_down_return = dfr_pivot.loc[dfr_pivot['PctDown'] > 0.5, 'AvgReturn'].mean()
avg_down_return = np.float64(avg_down_return).round(4)
exp_return = dfr_pivot['AvgReturn'].mean().round(4)
stdev_returns = dfr_pivot['StDevReturns'].mean()
stdev_returns = np.float64(stdev_returns).round(4)
worst_return = dfr_pivot['MinReturn'].min()
pct_downside = exp_return - stdev_returns
pct_downside = np.float64(pct_downside).round(4)
least_pain_pt = dfr_pivot.loc[dfr_pivot['PctUp'] > threshold, '67PctDownside'].max()
total_years = dfr_pivot['YearCount'].max()
analyzed_years = lookback
n_consec = 0
max_n_consec = 0 for x in dfr_pivot['PctUp']:
if (x > threshold):
n_consec += 1
else: # check for new max, then start again from 1
max_n_consec = max(n_consec, max_n_consec)
n_consec = 1 max_consec_beat = max_n_consec try:
best_sell_date = dfr_pivot.loc[dfr_pivot['67PctDownside'] == least_pain_pt, 'M-D'].iloc[0]
except:
best_sell_date = "nan" try:
row = dfr_pivot.loc[dfr_pivot['M-D'] == best_sell_date, 'M-D'].index[0] - interval
col = dfr_pivot.columns.get_loc('M-D')
best_buy_date = dfr_pivot.iloc[row,col]
except:
best_buy_date = "nan"
6.如果满足% Up 行和历史条件,则创建 stat 值数组并将其附加到推荐交易列表中。
def filter_and_append_stats():
# Save the stats data separately to export to Excel for further research on each ticker if desired.
statsdata = np.array([my_ticker, hold_per, pct_uprows, max_up_return, min_up_return, avg_up_return, avg_down_return, exp_return, stdev_returns, pct_downside, worst_return, least_pain_pt, total_years, max_consec_beat, best_buy_date, best_sell_date, analyzed_years])
df_statsdata = pd.DataFrame(statsdata.reshape(-1, len(statsdata)), columns=['my_ticker', 'hold_per', 'pct_uprows', 'max_up_return', 'min_up_return', 'avg_up_return', 'avg_down_return', 'exp_return', 'stdev_returns', 'pct_downside', 'worst_return', 'least_pain_pt', 'total_years', 'max_consec_beat', 'best_buy_date', 'best_sell_date', 'analyzed_years'])
if pct_uprows > 0.1:
if total_years > 9:
df_tradelist = df_tradelist.append(dict(zip(df_tradelist.columns, statsdata)), ignore_index=True)
7.最后,将所有这些放在一个模块中,该模块提取每个 CSV 文件,计算统计数据,将它们加载到推荐交易表中,然后将数据保存在格式化的 Excel 文件中,用于每个报价器,如上所示。
def calc_3month_returns():
dperiods = 60
hold_per = "3 Mos"
interval = 90
convert_prices_to_periods()
separate_date_column() pivot_the_table() add_calculated_items() sortbydate_resetindex_export()
# Export the pivot table to CSV for further research if desired.
#dfr_pivot.to_csv(path_or_buf = my_path + "/data/" + my_ticker + "_dfr_pivot_3mo.csv", index=False)
# Save dfr_pivot to separate dataframe for exporting to Excel
dfr_3mo = pd.DataFrame(dfr_pivot) calc_trading_stats()
filter_and_append_stats()
# Save statsdata to separate dataframe for exporting to Excel
df_statsdata_3mo = df_statsdata.copy()for index, ticker in df_sp500_tickers.iterrows():
global df
global dfr
my_ticker = ticker['Symbol'] df = pd.read_csv (my_path + "/data/" + my_ticker + ".csv")
df.set_index('Date', inplace=True)
df = df['Close']
df = pd.DataFrame(df, columns=['Close'])
calc_1month_returns()
calc_2month_returns()
calc_3month_returns()
export_to_excel()
Excel 导出模块相当广泛。我使用了一个名为 XlsxWriter 的 Python 库来完成这项工作。下面是这段代码的样子:
def export_to_excel():
excel_file_path = my_path + "/data/" + my_ticker + ".xlsx"
# Create a Pandas Excel writer using XlsxWriter as the engine.
writer = pd.ExcelWriter(excel_file_path, engine='xlsxwriter')# Convert the dataframe to an XlsxWriter Excel object.
df_statsdata_1mo.to_excel(writer, sheet_name='Stats', index=False)
df_statsdata_2mo.to_excel(writer, sheet_name='Stats', startrow=2, header=False, index=False)
df_statsdata_3mo.to_excel(writer, sheet_name='Stats', startrow=3, header=False, index=False)
dfr_1mo.to_excel(writer, sheet_name='1 Mo Returns', index=False)
dfr_2mo.to_excel(writer, sheet_name='2 Mo Returns', index=False)
dfr_3mo.to_excel(writer, sheet_name='3 Mo Returns', index=False)# Get the xlsxwriter objects from the dataframe writer object.
workbook = writer.book
worksheet1 = writer.sheets['Stats']
worksheet2 = writer.sheets['1 Mo Returns']
worksheet3 = writer.sheets['2 Mo Returns']
worksheet4 = writer.sheets['3 Mo Returns']
# Add conditional formatting to highlight positive returns in green
end_column = dfr_1mo.columns.get_loc("YearCount")
grn_format = workbook.add_format({'bg_color': '#C6EFCE','font_color': '#006100'})
worksheet2.conditional_format(1, 2, 365, end_column - 1,{'type':'cell','criteria':'>','value':0,'format':grn_format})
worksheet3.conditional_format(1, 2, 365, end_column - 1,{'type':'cell','criteria':'>','value':0,'format':grn_format})
worksheet4.conditional_format(1, 2, 365, end_column - 1,{'type':'cell','criteria':'>','value':0,'format':grn_format})
# Freeze panes for scrolling
worksheet2.freeze_panes(1, 2)
worksheet3.freeze_panes(1, 2)
worksheet4.freeze_panes(1, 2)
# Save the file
writer.save()
交易列表输出如下所示,按最佳买入日期排序:

基于 3 个月滚动季节性回报的建议交易
我的主要观点是,你不需要成为一个数学博士或者是一个高速日内交易者来发现股市中的重复模式,你可以用它来增加对你有利的机会。你只需要知道去哪里找,并掌握一点编码技巧就能有效地完成。
在这种情况下,我使用 Python,但这也可以用 Excel 中的 VBA 代码轻松完成。你可以在 Github 上看到完整的 Python 脚本。请在 LinkedIn 上给我发消息,告诉我你想提供的任何反馈。
编码和交易快乐!!
来自《走向数据科学》编辑的提示: 虽然我们允许独立作者根据我们的 规则和指南 发表文章,但我们并不认可每个作者的贡献。你不应该在没有寻求专业建议的情况下依赖一个作者的作品。详见我们的 读者术语 。
情绪交易:(试图)从市场情绪中赚钱

来源:Shutterstock
很长一段时间以来,我读到投资者的情绪,通常被称为情绪,可以是一个很好的指标,通常是反向的,就近期市场价格行为而言。人们的想法是,在极端情况下,人们可能会做出不理性的行为,比如网络泡沫,甚至是 2009 年 3 月,当时美国股市触底,而空气中仍弥漫着大量恐惧。
我很好奇这是不是真的。我不否认直觉,但有时候直觉是假的。所以俗话说,“我们相信上帝……其他人都会带来数据”。
我是行为金融研究的粉丝,比如罗伯特·席勒、T2、理查德·塞勒、T4 和丹尼尔·卡内曼提出的见解,它们在理智和经验上与我联系在一起。我喜欢这个想法,我可以从那些不理性的不那么聪明的散户投资者身上获利。它打击了我的斯多葛派优越感。
但是如果很容易,别人大概已经想通了。
一个简单的情感系统
美国个人投资者协会(AAII)从 1987 年中期开始发布每周情绪指数,会员回答他们对未来 6 个月股市走向的看法。现在一个敏锐的观察者可能会说,“嘿,史蒂文,这是一个有偏见的样本,绝对不是一个简单的随机样本!”这是事实,但我们感兴趣的是市场参与者,而不是普通公民,这一数据比大多数其他情绪指标都要久远,如 VIX 或 CBOE 看跌/看涨期权比率。结果导致牛市,中性或熊市的结果。我选择使用简单的牛熊价差指标,即多头百分比减去空头百分比,因为这是一个简单的单一指标,包含了调查数据。
现在我们知道,从历史上看,至少在美国,存在一个统计上很强的股票风险溢价,这意味着投资者期望,并且通常会通过投资股票获得回报。根据 Kenneth French 数据库的数据,从 1927 年到 2020 年 5 月,投资者获得了 9.4%的年化回报率,其中 6.0%来自高于无风险利率的股票市场风险溢价。我会说,更令人印象深刻的是 4.24 的高 t-stat 和 1.078e-05 的 p 值,这表明实际的历史回报率不是运气。
因此,除非你有相反的有力证据,否则至少从历史上看,投资美国股票的结果是好的。但话说回来,提醒我一下,从 1927 年到 2020 年,谁 100%投资于股票?哦,是的,没有人!他们要么已经死了,要么已经在好几个场合认输了。在大萧条期间,谁会在 3 年半的时间里保持 84%的下降呢?因此,历史回报是不错的,但这并不意味着任何人实际上都实现了这些巨大的长期回报。
投资组合构建与分析
我们希望尽量保持大部分投资于股票,但也许不会走极端。我很好奇我们是否能在风险调整的基础上超越 if:
- 当牛熊价差处于历史区间的前十分之一时(即乐观情绪的前 10%),我们退出了市场,否则,继续投资。
我们必须从一开始就着眼于滚动的十分之一,而不是事后诸葛亮。
你可以通过 Github 这里访问 Python 代码进行下面的分析。
这里你可以看到实际的牛熊价差和 6 个月的滚动平均值,这有助于你看到信号和周噪声。

数据来源:AAII
以下是过去 6 个月股票市场的滚动配置。直觉上,市场情绪越乐观,我们的投资就越少。

数据来源:AAII
下面的图表显示了性能(y 轴对数刻度显示了相同距离变化时指数的相同百分比变化)

数据来源:AAII。性能是忽略税收和交易成本和纯粹的假设。
观察结果:
- 从 1987 年到 1995 年,市场和情绪投资组合之间的表现没有太大差异。
- 从 1995 年到 2004 年,有一个很大的分散,这是由于 AAII 战术投资组合只投资了 74%的时间,而通常 AAII 战术投资组合投资了 89%的时间。反向投资法确实不太奏效。
- 有趣的是,在 2003/2004 年,人们的乐观情绪超过了互联网泡沫的高峰期。
- 情绪指标每周都有很多噪音,如果你不小心,这样的噪音信号可能会导致高投资组合周转率和交易成本。

数据来源:AAII。性能是忽略税收和交易成本和纯粹的假设。
自 2009 年初大衰退结束至今,我们看到了与整体市场一致的表现,因为该指标通常不会超过 10%的上限。
开采愚人黄金
从最佳实践的角度来看,我们已经看到了所有的数据,但没有可行的策略。但是让我们探索一下,在交易成本之前,在实现更高的夏普比率(回报/风险)方面,技术上是否有可能超越市场。如果我们简单地使用年化回报率,我们就不会比较苹果和苹果,因为我们将在我们的数据挖掘策略中使用杠杆,波动性可能不相等。
我运行了一个 for 循环来探索以下策略的不同变体:
- 当牛熊价差超过某个百分点时(I)平仓(小心)
- 当牛熊价差低于某个百分点(j)时,做多 2x(激进)
直觉告诉我们,当其他人都非常积极的时候,我们要保守,当其他人都非常悲观的时候,我们要积极。
下面是一个热图,显示了情绪策略的夏普比率减去被动市场策略的夏普比率(越高越好)。阅读热图的方法:
- y 轴回答的问题是“在什么样的百分比我会退出市场(即持平)?”底部相当于 1.0 永远不出市,顶部相当于 0.0 永远不投资。
- x 轴回答的问题是“在多大的百分比上我才是积极进取的,对市场的敞口是两倍?”最右边相当于在 1.0 时从不使用杠杆,最左边相当于在 0.0 时一直使用两倍的杠杆。

数据来源:AAII
在这个数据挖掘练习之后,我们注意到以下情况:
- 与市场指数相比,所有使用 AAII 牛熊价差的市场时机策略的夏普比率都较低。
- 情绪时机策略的最高夏普比率出现在深红色阴影区域,主要是当你一直做多(左下)或一直杠杆操作(右下)时。请注意,由于 2 倍杠杆投资组合的波动性增加,由于复利效应,您不会获得 2 倍的几何年化回报(这是夏普比率的分子),因此 2 倍杠杆投资组合与 1 倍完全投资风险敞口的夏普比率不同。
因此,我们可以肯定地说,利用 AAII 牛熊价差的简单择时策略不起作用。然而,我们还没有探索,如果使用其他指标(如 VIX)对市场情绪进行综合衡量,是否会给我们带来不同的结果。
接下来,我将探索不同情绪指标的组合是否可以用于不同的机器学习算法,以实现卓越的风险调整性能。
注来自《走向数据科学》的编辑: 虽然我们允许独立作者根据我们的 规则和指导方针 发表文章,但我们不认可每个作者的贡献。你不应该在没有寻求专业建议的情况下依赖一个作者的作品。详见我们的 读者术语 。
Python 交易工具箱:用 Matplotlib 介绍 OHLC 图表
交易工具箱
释放 OHLC 数据的力量

由 Aditya Vyas 在 Unsplash 上拍摄的照片
在其他帖子中,我们开始探索如何根据价格计算一些基本指标(简单移动平均线和其他移动平均线),以及如何将它们与价格一起绘制在图表上。在这些例子中,我们考虑了每日价格数据,并使用收盘价来表示每天的交易。很明显,金融工具全天交易产生不止一个价格。收盘价是最相关的价格之一,但不能说明交易日发生的全部情况。
OHLC 棒线和棒线图是捕捉金融工具全天交易中产生的价格范围的传统方式:对于每一天,记录四个价格:开盘价(开盘)、最高价(高)、最低价(低)和收盘价(收盘)。

条形图并不局限于每日价格:周线图和月线图可以使用每个时期的开盘价、最高价、最低价和收盘价来构建。它们也可以应用于日内图表,通过使用小时线,或者任意间隔的线(例如 30 分钟,10 分钟,直到 1 分钟)。
与条形图类似,蜡烛图基于每天的开盘价、最高价、最低价和收盘价,但使用不同的视觉表示。开盘价和收盘价之间的范围由一个“蜡烛体表示——它根据收盘价是否高于开盘价而采用不同的颜色(通常是白色和黑色)。高点和低点分别用放置在身体上方和下方的“蜡烛芯”(称为阴影)来表示。烛台图表的使用起源于日本,与一种基于模式的分析相关。

用 Python 创建 OHLC 条形图
有几个很好的可视化资源,使我们能够用 Python 创建条形图和蜡烛图。最好的两个是 Plot.ly 和 Bokeh 。两种解决方案都允许创建专业外观的交互式图表。另一方面, Matplotlib 专注于静态图表,能够产生漂亮的出版物质量的数字。当我需要制作静态图表时,这通常是我的第一个选择。
虽然 Matplotlib 库是使 Python 成为数据可视化的良好环境的元素之一,但就 OHLC 金融图表而言,它迄今为止的表现低于其真正的潜力。在 Matplotlib 中处理 OHLC 和烛台图表绘制的包被称为 mpl-finance ,这是一个模块,它曾经是主 Matplotlib 发行版的一部分,直到它被宣布过时,只能作为一个单独的包使用。我认为,这是有原因的: mpl-finance 没有与 pandas 很好地集成,也不像 Matplotlib 的其他绘图功能那样容易使用。最近,它找到了一个新的维护者, Daniel Goldfarb ,他正致力于为 mpl-finance 创建一个新的 API,以使其更加可用,并与 pandas dataframes 保持一致。新版本应该会在 2020 年的某个时候发布。
对不久的将来的一瞥
我们可以先看看即将到来的版本 mpl-finance 将如何工作以及看起来会是什么样子。要预览新版本(撰写本文时的预发布版本),您只需运行:
pip install mplfinance
注意即将到来的版本的拼写:名称中没有'-'也没有' _ ',而当前版本安装为mpl-finance,导入(相当混乱)为mpl_finance。新版本将结束这种名称混淆。
我们现在可以创建我们的第一个价格条形图,使用在系列文章的第一篇文章中使用的相同数据。你可以在这里下载 CSV 文件。
打字:
import pandas as pddatafile = 'SPY.csv'
data = pd.read_csv(datafile, index_col = 'Date')
data.index = pd.to_datetime(data.index) # Converting the dates from string to datetime formatdata
显示了我们的 OHLC 价格数据框架:

我们现在可以导入新安装的 mpl-finance 库:
import mplfinance as mpf
创建价格条形图(最近 50 天的数据)非常简单:
mpf.plot(data[-50:], no_xgaps = True)
如果您没有使用 Jupyter,请不要忘记添加下面一行来可视化这个图表和接下来的图表:
plt.show()
这是结果:

创建蜡烛图同样简单:
mpf.plot(data[-50:], type='candlestick', no_xgaps = True)

视觉效果看起来很吸引人。no_xgaps选项是一个漂亮的功能,它消除了通常由没有交易数据的日子(如周末和公共假日)产生的缺口。
当前的 mpl-财务库
当前版本的 mpl-finance 可通过以下方式安装:
pip install mpl-finance
或者
conda install mpl-finance
如果你使用 Conda 作为包管理器。与即将发布的版本相比,当前的 mpl-finance 库需要一些数据操作来创建一个简单的 OHLC 或烛台图表。特别是:
- 我们需要将数据呈现为 OHLC 价格序列。
- 时间和日期需要明确地转换成 Matplotlib 能够理解的格式。
幸运的是,一旦新版本发布,所有这些操作都将过时,希望很快在 2020 年发布。目前,我只是给出了执行这些任务的代码,而没有过多地涉及细节。您可以直接使用它:
我们现在可以使用这些数据绘制一个条形图:
这表明:

默认情况下,收盘价高于开盘价的棒线是黑色的,而收盘价低于开盘价的棒线是红色的。图表中有一些可见的横向缺口:它们是由非交易日(周末和公共假日)产生的。移除它们需要一些额外的非平凡的过滤。
类似地,可以通过以下方式生成蜡烛图:
它产生:

释放 OHLC 数据的力量
使用 OHLC 价格而不仅仅是一个单一的系列作为收盘价开辟了一个新的可能性世界:我们可以评估每个交易日的价格范围,观察收盘价与开盘价的关系,检查价格是否高于先前的高点或低于先前的低点,等等。
这里我将展示一个相当简单的图表示例,它利用了最高价和最低价以及收盘价。这张图表来自一种交易技术,叫做“驼峰技术”。我对详细描述交易技术不感兴趣(大量信息可以在网站上找到):我们只是使用相关的图表作为发现现有趋势的有用工具。
我们在每日价格条形图上叠加了以下移动平均线:
- 40 天简单移动平均线的高点。
- 低点的 40 天简单移动平均线。
- 收盘价的 15 天指数移动平均线。
这可以编码如下:
这给出了:

两条简单的蓝色移动平均线创建了一个通道:与单一移动平均线相比,当价格既不在通道上方也不在通道下方时,我们现在有一个灰色区域。
例如,我们现在可以采用以下交易规则:
- 仅当价格棒完全位于更高的 40 天 SMA 上方时,输入多头头寸(买入)。
- 只有当价格棒完全位于下方 40 天均线下方时,才进入空头头寸(卖出)。
- 当价格在两个 40 天均线之间或者最后一根棒线穿过其中任何一个时,我们不进入任何头寸(我们在市场上保持持平)。****
另一个例子是:
- 仅当 15 日均线高于 40 日均线时,输入多头头寸。
- 仅当 15 日均线低于下方 40 日均线时,输入空头头寸。
- 在其他地方保持持平,即当 15 日均线在两个均线创建的通道内时。
在这个阶段,我们应该问这些规则(或者在关于均线的文章中提到的任何方法)是否可以用来建立一个有利可图的交易系统。换句话说,我们应该问这些想法是否会帮助我们产生利润而不是亏损,以及如何选择最佳的规则。我会在下一篇文章中尝试解决这个问题。我们将学习如何回测一个交易系统,根据历史数据计算盈利或亏损。
来自《走向数据科学》编辑的提示: 虽然我们允许独立作者根据我们的 规则和指导方针 发表文章,但我们并不认可每个作者的贡献。你不应该在没有寻求专业建议的情况下依赖一个作者的作品。详见我们的 读者术语 。
Python 交易工具箱:用指标支线剧情提升你的图表
交易工具箱
使用 matplotlib 向价格图表添加指标

在几个月的中断之后,我终于可以继续发布交易工具箱系列了。我们从学习如何在价格图表的顶部绘制指标(特别是:移动平均线)开始了这个系列。均线属于一组广泛的指标,叫做叠加指标,它们和价格有相同的尺度,因此可以在同一张图上绘制。然而,其他技术指标没有这种优势,我们需要将它们绘制在单独的区域,有时称为子图。
下面是一个取自 Yahoo!财务:

带有动量指示器的烛台图表
在这篇文章中,我们想探索如何使用 Matplotlib 创建类似的图表。我们还想探索如何利用 Matplotlib 的定制潜力,创建原创的、出版物质量的图表。首先,我们将学习如何使用 Matplotlib 获得那种子情节。然后,我们将应用它来绘制价格下方的第一个技术指标,即变化率(或 ROC )。然后,我们将看看如何在 OHLC 棒线或蜡烛图而不是折线图上做到这一点。
使用 matplotlib 的多个支线剧情
在这个阶段,我们需要深入研究 Matplotlib 如何工作的一些技术方面:这就是我们如何利用它的多图功能和工艺发布质量图表。提供的所有代码都假设您正在使用 Jupyter Notebook 。相反,如果您使用更传统的文本编辑器或命令行,您将需要添加:
plt.show()
每次创建图表时,为了使其可见。
在本系列的前两篇文章中,我们使用以下格式创建了第一个金融价格图:
plt.plot(dates, price, <additional parameters>)
你可以看第一篇关于移动平均线的文章或者第二篇关于加权和指数移动平均线的文章。
当调用该方法时, matplotlib 在后台做一些事情来创建图表:
- 首先,它创建了一个名为 figure 的对象:这是存储所有图表的容器。一个图形是自动地、安静地创建的,但是,我们可以显式地创建它,并在需要传递一些参数时访问它,例如使用指令:
fig = plt.figure(figsize=(12,6)) - 除此之外, matplotlib 创建了一个名为轴的对象(不要与轴混淆):这个对象对应于包含在图中的一个子情节。同样,这个动作通常发生在幕后。
在任何一个图形中,我们可以有多个子情节(轴)排列成一个矩阵:

有六个轴的图形
我们的第一个多重绘图图表
当涉及到有多个次要情节的图表时,有足够多的方式和方法可以让我们晕头转向。我们将只选择一个:.subplot()方法将很好地服务于我们的目的。通过其他教程,你可能会遇到一个叫做.add_subplot()的方法:.subplot()方法是.add_subplots()的包装器(这意味着它应该使它的使用更简单)。除了几个细节之外,它们的用法实际上非常相似。
每当我们向图形添加子图时,我们需要提供三个参数:
- 图表矩阵中的行数。
- 列数。
- 具体支线剧情的编号:你可以从上图中注意到,轴物体是从左到右,然后从上到下编号的。
让我们尝试构建一个通用 2x2 多曲线图的实际示例:
这给了我们下面的图表:

有四个次要情节的图表
向价格系列添加指示器子图
现在我们知道了如何创建具有多个图表的图表,我们可以应用我们的技能在价格图表的底部绘制一个指标。对于这个任务,我们将使用一个称为变化率 ( ROC )的指标。实际上,ROC 有几种不同的定义,我们在示例中使用的定义基于以下公式:

其中 lag 可以是任何大于零的整数,代表我们回头比较价格的周期数(在日线图上:天数)。例如,当我们计算滞后 9 天的每日价格的 ROC 时,我们只需查看与 9 天前相比,价格上涨(或下跌)了多少(百分比)。在本文中,我们不打算讨论如何解读 ROC 图并将其用于投资决策:这最好有一篇专门的文章,我们将在未来的帖子中进行讨论。
我们从准备环境开始:
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt# Required by pandas: registering matplotlib date converters
pd.plotting.register_matplotlib_converters()# If you are using Jupyter, use this to show the output images within the Notebook:
%matplotlib inline
对于这个练习,我从 Yahoo!为跟踪纳斯达克 100 指数表现的 ETF景顺 QQQ 信托 的每日价格融资。你可以在这里找到我正在使用的 CSV 文件。我们可以加载数据并浏览一下:
datafile = 'QQQ.csv'
data = pd.read_csv(datafile, index_col = 'Date')# Converting the dates from string to datetime format:
data.index = pd.to_datetime(data.index)data
看起来像是:

然后,我们可以计算滞后 9 天的 ROC 序列,并将其作为一列添加到我们的数据框中:
lag = 9
data['ROC'] = ( data['Adj Close'] / data['Adj Close'].shift(lag) -1 ) * 100data[['Adj Close', 'ROC']]
哪些输出:

为了使我们的示例图表更容易阅读,我们只使用了我们的可用数据的选择。下面是我们如何选择最后 100 行,对应于最近的 100 个交易日:
data_sel = data[-100:]
dates = data_sel.index
price = data_sel['Adj Close']
roc = data_sel['ROC']
我们现在准备创建第一个多点图,价格在顶部,ROC 指标在底部。我们可以注意到,与带有子图的普通图表相比,我们的指标具有与价格图表相同的日期和时间(水平轴):
fig = plt.figure(figsize=(12,10))# The price subplot:
price_ax = plt.subplot(2,1,1)
price_ax.plot(dates, price)# The ROC subplot shares the date axis with the price plot:
roc_ax = plt.subplot(2,1,2, sharex=price_ax)
roc_ax.plot(roc)# We can add titles to each of the subplots:
price_ax.set_title("QQQ - Adjusted Closing Price")
roc_ax.set_title("9-Day ROC")

我们的第一个带指标的图表
我们刚刚绘制了我们的第一个图表,价格和 ROC 在不同的区域:这个图表完成了它的工作,它使价格和指标都可见。然而,它并没有以一种视觉上非常吸引人的方式做到这一点。首先,价格和 ROC 共享同一个时间轴:没有必要在两个图表上再次应用日期标签。我们可以使用以下方法将它们从顶部图表中删除:
price_ax.get_xaxis().set_visible(False)
我们也可以用下面的方法去掉两个支线剧情之间的间隙:
fig.subplots_adjust(hspace=0)
在 ROC 的零级别添加一条水平线也是一个好主意,这样可以使它更具可读性,同时在两个垂直轴上添加标签。
fig = plt.figure(figsize=(12,10))price_ax = plt.subplot(2,1,1)
price_ax.plot(dates, price, label="Adj Closing Price")
price_ax.legend(loc="upper left")roc_ax = plt.subplot(2,1,2, sharex=price_ax)
roc_ax.plot(roc, label="9-Day ROC", color="red")
roc_ax.legend(loc="upper left")price_ax.set_title("QQQ Daily Price")# Removing the date labels and ticks from the price subplot:
price_ax.get_xaxis().set_visible(False)# Removing the gap between the plots:
fig.subplots_adjust(hspace=0)# Adding a horizontal line at the zero level in the ROC subplot:
roc_ax.axhline(0, color = (.5, .5, .5), linestyle = '--', alpha = 0.5)# We can add labels to both vertical axis:
price_ax.set_ylabel("Price ($)")
roc_ax.set_ylabel("% ROC")

更好的带指示器的图表
这张图表看起来已经更好了。然而,在创建专业外观的图表时,Matplotlib 提供了更大的潜力。以下是我们可以做的一些例子:
- 为了增强 ROC 指标的可读性,我们可以填充图和水平线之间的区域。
.fill_between()方法将服务于这个目的。 - 例如,我们可以设置日期标签的格式,只显示月份的缩写名称(例如, Jan , Feb ,…)。
- 我们可以对 ROC 纵轴上的标签使用百分比格式。
- 我们可以给两个支线剧情都添加一个网格,并设置一个背景色。
- 增加绘图和边框之间的边距(填充)。
- 为了最大化图表的 数据-墨迹比率 ,我们可以删除所有的竖线(子情节周围的边框)以及两个子情节的水平轴和垂直轴上的刻度线。
- 我们还可以将所有字体的默认大小设置为一个更大的数字,比如 14。
这是一个相当长的改进列表。不要在细节上迷失太多,下面的代码应该提供一个很好的例子:
这给出了:

带有指示器的更加定制化的图表
这个图表展示了我们通过操作默认的 Matplotlib 参数所能达到的效果。当然,我们总是可以通过应用现有的样式表来实现一些视觉上的改进,正如我们在本系列的第一篇文章中所做的,例如:
plt.style.use('fivethirtyeight')
当使用指标支线剧情时,大多数时候我们希望价格部分比图表中的指标部分占据更大的面积。为了实现这一点,我们需要操纵 Matplotlib 用来放置支线剧情的不可见网格。我们可以使用[GridSpec](https://matplotlib.org/3.1.1/tutorials/intermediate/gridspec.html) 函数来实现。这是 Matplotlib 的另一个非常强大的特性。我将提供一个简单的例子来说明如何使用它来控制两个支线剧情之间的高度比例:
哪些输出:

具有自定义高度比例的图表
顺便提一下,您可能会注意到这个图表是如何将前一个图表代码中的字体大小设置为 14 的。这是因为对rcParams Matplotlib 参数的任何更改都是永久的(直到我们重新启动系统)。如果我们需要重置它们,我们可以使用:
plt.style.use('default')
如果我们用的是 Jupyter 笔记本,还要加上%matplotlib inline。
OHLC 和蜡烛图指标
在前面的所有例子中,我们将价格绘制成了一个线形图。当我们每个交易周期只有一个数据点(在这个例子中是收盘价)时,折线图是可视化价格的好方法。通常,对于金融价格系列,我们希望使用 OHLC 条形图或蜡烛图:这些图表可以显示总结每天交易活动的所有价格(开盘价、盘高、盘低、收盘价),而不仅仅是收盘价。
为了在 Matplotlib 中绘制 OHLC 条形图和蜡烛图,我们需要使用 mplfinance 库。正如我在本系列的上一篇文章中提到的, mplfinance 的发展已经获得了新的动力,事情正在快速发展。因此,我们将只粗略地讨论如何使用它来创建我们的图表。
Mplfinance 提供了两种在图表中创建子图和添加指标的方法:
- 使用外部轴方法创建图表或多或少类似于我们目前所做的。Mplfinance 负责绘制 OHLC 或烛台图表。然后我们可以传递一个带有指示器的轴对象作为单独的子情节。
- 面板方法甚至比纯 Matplotlib 代码更容易使用: mplfinance 为我们控制所有的绘图和样式操作。为了处理图表的视觉方面,我们可以应用现有的样式或创建我们自己的样式。
在我写这篇文章的时候,外部轴方法发布还不到一周。我仍然期待着利用它来发现它所能提供的潜力。
作为使用 mplfinance 面板方法的尝试者,我们可以在单独的面板中绘制一个带有交易量的蜡烛图:
import mplfinance as mpfmpf.plot(data_sel, type='candle', style='yahoo', title="QQQ Daily Price", volume=True)

一个基本的 mplfinance 蜡烛图
我们也可以在单独的子图中添加 ROC 指示器:
# We create an additional plot placing it on the third panel
roc_plot = mpf.make_addplot(roc, panel=2, ylabel='ROC')#We pass the additional plot using the addplot parameter
mpf.plot(data_sel, type='candle', style='yahoo', addplot=roc_plot, title="QQQ Daily Price", volume=True)

我们的指标添加到图表中
结论
有几个软件包可以使用 Python 和 pandas 创建财务图表。特别是, plotly 因其创建好看的交互式图表的能力而脱颖而出。另一方面,Matplotlib 可能不会直接生成最好的图表(如果你需要的话,看看 seaborn )但是,它有巨大的定制潜力,可以创建在专业出版物中脱颖而出的静态图表。
这就是为什么我认为调整 matplotlib 的属性是非常值得的。mplfinance 的未来发展将使这些可能性变得更有吸引力。
风能交易:基于深度学习的风能预测模型
基于深度学习为能源交易者开发一个有利可图的风能需求预测模型。

Rabih Shasha 在 Unsplash 上的照片
动机
创造稳定的能源供应总是至关重要的,因为我们的现代社会真正依赖于此。这就是为什么化石燃料或核能等可预测的能源仍然是有利的。然而,能源短缺的风险将持续存在,促使我们使用金融来预测和避免未来可能导致停电的能源生产短缺。人们可以考虑利用可再生能源来应对这一挑战。
迄今为止,在这些可再生能源中,有些依赖于环境,例如风能。顾名思义,能量将由不同速度和方向的风产生。不幸的是,作为产生能量的唯一触发器,风是利用这种替代能源的明显障碍。
简而言之,我们有三方参与这一能源事务——电网运营商、能源生产商和能源交易商。电网运营商负责为社会提供稳定的电能供应。否则,如果停电,政府会对他们罚款。接下来,供应商将管理能源短缺的风险,风能生产商。通常情况下,能源交易员会帮助供应商提前预测出售给电网的能源产量。换句话说,能源交易商代表客户实现利润最大化。
目标
我们将扮演能源交易员的角色。目标是每小时获得一次 T+18 小时能量预测!使用我们的能源预测模型和给定的交易算法,我们将为我们的客户,即风能生产商,实现利润最大化。我们将使用深度学习(神经网络),特别是差分网络架构,基于时间序列数据集建立预测模型。
交易算法
来源:深度学习 Datathon 2020 (by ai4impact )
- 你需要对客户风电场的发电量进行 T+18 小时的预测。这个预测是你交易的核心
- 你的客户每向电网出售一千瓦时可获得 10 欧分。您只能向电网销售您预测的日期
- 如果实际发电量超过预测,超出部分将被电网吸收,但您的客户不会因此得到补偿。
- 如果实际发电量低于预测,您必须从现货市场购买能源(20 欧分/千瓦时)以供应电网。你有 10,000,000 欧分的现金储备,可以从现货市场购买能源。
方法学
- 检查随统计数据一起提供的数据。
- 标准化数据并设置基线风险(基于持久性)。
- 在风险函数上很好地拟合训练和测试集。测试损失应该超过基线风险。
- 提高模型性能(在减少滞后/保持零滞后的同时,不断降低风险)。
- 检查最佳模型的再现性。
检查数据
数据集
我们将使用如下两个不同的数据集:
1。风能生产 来源:法国能源传输管理局
该数据集名为 energy-ile-de-france,包含巴黎周围法兰西岛地区的统一近实时风能产量(单位为 kWh ),该数据已平均化并标准化为 1 小时的时基。数据提供时间为 2017 年 1 月 1 日至今。

数据不是真的有规律,但还是能看出一些趋势。例如,能量峰值在冬季和季节转换时最为常见。到目前为止,最大的发电量发生在 2019-2020 年冬季,高达 89000 千瓦时。数据的基本统计如下所示。
平均值= 17560.44 千瓦时
中值= 10500.0 千瓦时
最大值= 89000.0 千瓦时
最小值= 0.0 千瓦时
极差= 89000.0
标准差= 19146.63
2。风力预报 资料来源: Terra Weather
数据来自两种不同的风力预报模型(A 和 B),适用于法兰西岛地区的 8 个风电场。因此,有 16 个预报,每个预报有 2 个变量:风速(米/秒)和风向方位(北纬度-即。45 度表示风从东北方向吹来)。天气预报每天每 6 小时更新一次,并以 1 小时为时间基数进行插值。


风速图具有与能量图相似的趋势,表明这种预测数据可以作为输入特征用于我们的模型。关于这一点,最强的风出现在冬季,风速高达 12 米/秒。风数据的基本统计如下所示。

风速预测统计
与风速预测相比,风向模式很难解读。但是我们将会看到,即使这样的数据仍然可以给我们的预测模型带来好处。
最后,我们的原始数据排列如下:

原始数据集(31027 个示例)
标准化数据并设定基线风险
为了加快训练过程,我们将使用下面的公式对数据进行归一化处理,使平均值和方差都为零。

作者

标准化后的数据集
现在我们有了相同比例的每个特征。注意,我们只归一化能量和风速。风向值将在后面进行特殊处理。
接下来,我们将获得基于持久性风险的基线。我们使用均方误差(MSE)和平均绝对误差(MAE)提取基线风险。
- 持久性风险(MSE): 0.4448637
- 持久性风险(MAE): 0.6486683
开始实验!
使用差分网络结构,我们在风险函数(MSE 和 MAE)上很好地拟合训练和测试集。差异网络有助于我们更好地学习超越这个基线。提醒一下,我们的目标是获得提前 18 小时的能源预测。

作者设计的差分神经网络结构
以下是我们可以打开的几个超参数:
- 窗口输入特性(初始、差分、动量和力输入)
- 统计输入特征(平均值、标准差、最大值、最小值等)
- 优化器(Adam,SGD)
- 激活功能(Relu,Tanh)
-
隐藏层(2 至 5 层)
- 正规化(辍学,L2)
- NN 大小(8 到 256 个神经元,下一层减少 2/3)
- 子网(输入缩放、自动编码器)
- 感知器的类型(正常,平方感知器)
- 损失(MAE、MSE、动量损失、力损失)
事实上,这份名单太多了:)。但是请耐心等待,因为你会知道每个人对我们建立的模型有什么贡献。由于我们已经试验了很多次,我们将只向您展示提高我们目标的设置。
注意:每个实验使用 10000 次最大迭代和提前停止方法。
实验 1
输入缩放子网+ 4 个隐藏层(带辍学)
在第一个实验中,我们试图创建一个低 MAE(和 MSE ),它将超过基线。所以,我们希望自己的网络足够深,足够大,又不会过度拟合。因此,我们使用 Adam 来实现更好的学习,并添加一种称为 dropout layer 的正则化方法来防止过拟合。我们使用具有多重配置的 4 层网络,如下所示:
- 输入缩放子网络
- NN-尺寸:32/64/128/256
- 辍学概率:0.05/0.1/0.25
- 优化器:Adam
- 层数:4
实验一:特征选择
开窗是时间序列数据的基本操作。因此,对于输入特征,我们使用由过去 60 小时产生的能量(T-60)组成的窗口。然后,我们将窗口转换为不同动量力输入,提前时间为 18 小时。它将产生 72 个特征。这种调整有助于模型检测运动及其速率,以执行更好的聚类。
我们还添加了过去 60 小时的平均风速预测值和每个风模型在 T+18 小时的风速预测值。这又产生了 4 个特征。因此,我们总共有 76 个输入特征准备好提供给输入缩放子网。由于我们使用相对较大的输入,这个子网在将它提供给主网络之前减少了不需要的功能。
总之,这些是我们的输入功能列表:
- 差异+动量+T-60h 的力输入过去的能量产生于 18h 的提前期
- 预测的过去风速的 T-60h 平均值(模型 A)
- T+18 小时的风速预测(模型 A)
- 这同样适用于模型 B
实验一:最佳配置损耗

测试损失:0.554845(使用 MAE 作为损失函数)
实验一:评价

exp 的评估摘要。一
- 最佳测试损失/持续误差
MSE:0.280589/0.4448637
MAE:0.5544554865 - 最佳 NN 尺寸:128
- 最佳辍学概率:0.05
- 请注意,我们已经战胜了持久性,实现了零延迟。
- 训练和考输还是有很大差距的。可以考虑使用正则化,增加更多功能。
实验二
输入缩放子网+ 4 个隐藏层
(带辍学+ L2 正则化)
与之前的模型一样,我们在模型中加入了 L2 正则化。我们还运行多配置,同时考虑最佳超参数。
- 输入缩放子网络
- NN-尺寸:64/128/256
- 辍学概率:0.05/0.1
- 重量衰减:1.0E-4/1.0E-5/1.0E-6
- 优化器:Adam
- 层数:4
实验二:特征选择
我们添加了来自风向预测的新输入特征。虽然添加方向数据作为我们的输入有点无意义,但稳定的风向确实有所帮助。因此,我们不想天真地规范化方向,但我们将使用三角函数来“规范化”它。除了前一个,现在我们总共有 84 个输入功能个。
总之,这些是我们输入功能的补充:
- 过去风向预测的 T-18h(平均)sin 函数(模型 A)
- 过去风向预测的 T-18h(平均)cos 函数(模型 A)
- sin 函数中 T+18 小时的风向预报(模式 A)
- cos 函数中 T+18 小时的风向预报(模式 A)
- 这同样适用于模型 B
实验二:损失

测试损失:0.549824(使用 MAE 作为损失函数)
实验二:评价

exp 的评估摘要。2
- 最佳测试损失/持续误差
MSE:0.26769/0.4448637
MAE:0.54924/0.648864866 - 最佳 NN 尺寸:128
- 最佳辍学概率:0.1
- 最佳重量衰减:1.0E-4
- 请注意,我们已经产生了更好的测试损失,同时保持零滞后(也增加了滞后图的峰值)。
- 我们仍然可以通过向模型添加更多输入要素或图层来提高性能。
实验 3 —最终模型
输入缩放子网+ 4 个隐藏层
(带辍学+ L2 正则化)
通过设置固定的最佳超参数,以下是我们的网络配置:
- 输入缩放子网络
- NN-尺寸:128
- 辍学概率:0.1
- 重量衰减:1.0E-4
- 优化器:Adam
- 层数:4
实验三:特征选择
我们包括新的统计特征作为新的附加输入,取自能量和风速数据。最后,我们总共有88个输入特性。
- 过去产生能量的 T-60h 的平均值
- 过去产生的能量的 T-60h 的标准偏差
- 预测的 T-60h 过去风速的标准偏差(模型 A)
- 预测的 T-60h 过去风速的标准偏差(模型 B)
实验三:损失

测试损失:0.52758 (使用 MAE 作为损失函数)
实验三:评价

exp 的评估摘要。3
- 最佳测试损失/持续误差
MSE:0.258521/0.4448637
MAE:0.52758/0.6486683 - 以欧分计的净利润
MSE:1.392861351 e9
MAE:1.447243201 e9 - 我们使用最后一个模型实现了最佳测试损耗,同时没有延迟。因此,我们的利润是所有车型中最高的。
- 我们有更好的实际与训练/测试预测的散点图。虽然我们很适合训练集,但获得实际预测与测试预测的更好散点图仍然是一个挑战。
检查再现性
之前,上面的最终模型已经重复训练了 40 次,每次最多迭代 10000 次。请注意,我们使用 MAE 作为损失函数,因为它为客户提供了更高的利润。测试损失的统计如下所示。
均值= 0.540747
中值= 0.540757
最大值= 0.550977
最小值= 0.527580
范围= 0.023397
(均值-最小值)/标准差= 2.690480
最终模型预测



专门用语
- 增加更多的层减少了训练误差,但是增加了测试损失,降低了利润,尽管我们已经使用了正则化技术。因此,我们在最终模型中坚持使用 4 层。
- 自动编码器子网有助于减少输入特征的维数。然而,当添加到具有不超过 100 个特征的网络中时,它增加了我们模型的测试损失。
- 平方感知器被认为能提供比普通感知器更快更好的学习。然而,在实验过程中,它并没有在降低误差方面提高性能。
- 动量和力的损失被认为有助于减少滞后。然而,当我们将损耗加到网络上时,滞后图没有改变(仍然是零滞后),并且它使误差更高,因为网络需要将三个损耗(测试、动量和力损耗)一起最小化。
摘要
- 差分网络有效地利用时间序列数据建立预测模型,即使输入较少。
- 对于历史数据,DIFF 窗口与动量、力和统计特征相结合,可以帮助模型执行更好的预测。
- 更大更深的网络支持模型很好的记忆(小心过拟合)。
- 丢弃层(小丢弃概率)和 L2 正则化有助于网络处理过拟合问题,从而提高性能。
- 虽然 RMSE(或 MSE)作为时间序列数据中的损失函数也很受欢迎,但当使用 MAE 时,我们的模型会产生更高的利润。MSE 倾向于惩罚异常值,而 MAE 则更倾向于误差的线性。由于模型没有异常值,MAE 被证明最适合我们的模型。
本文是 ai4impact 组织的深度学习数据大会 2020 期间项目文档的一部分。
团队:Avektive
成员:Diardano Raihan,Mitchell Edbert,M. Taufiq Ismail Hatta
交通堵塞科学和可视化
曾经堵在路上,心想——如果别人开得更好,我就不会在这里了?德国物理学家 Kai Nagel 和 Michael Schreckenberg 在理解和模拟交通堵塞的出现方面取得了根本性的突破。事实证明,交通堵塞不是孤立的事件,事实上,它们是非常容易预测的。

模拟密度为 0.35 且 p =0.3 的 Nagel-Schrekenberg 交通模型。颜色指示车辆速度|塞犍陀·维维克
在 Nagel-Schrekenberg 模型中,汽车被模拟成网格单元上的离散物体。在每个时间步,车辆位置根据 4 个简单规则更新:
- 限速:所有车辆行驶速度在 0 到限速之间。在最初的论文中,速度限制是“5”,所以车辆在 0 和 5 之间行驶。
- 加速至限速:在每个时间步,只要车速低于限速,车速就会更新为当前速度+1。
- 如果太靠近前面的车辆,则减速:如果一辆车辆以其当前速度将要撞上前面的车辆,则减速以避免碰撞。例如,在时间 t 时,一辆车距离它前面的车 3 个单元,并且它的当前速度是 4。车辆的速度减慢到 2,使得它从时间 t 到时间 t+1 仅移动 2 个单元的距离,以便不与前面的车辆重叠(不引起事故)。
- 随机减速:每一个时间步长,车辆以一定概率减速 1, p 。如果 p=0.5,那么每辆车在每个时间步长都有 50%的机会减速。在这种情况下,如果速度是 3,它减少到 2,50%的时间。这代表了人类不完美的一面,导致了交通堵塞。
在视频中,你看到车辆向右移动,堵塞成核(红色车辆的补丁)与快速移动的车辆(绿色)的爆发相结合。这捕捉了走走停停的交通堵塞。密度为 0.35 表示 35%的单元被车辆占据。如果你有一个 100 格的网格,这意味着其中 35 格都是车辆。
可视化交通堵塞的时空图

密度为 0.35 且 p =0.3 的 Nagel-Schrekenberg 模型的时空图|塞犍陀·维韦克
可视化交通流的一种常用方法是时空图。深黑色区域表示车辆密度波。负斜率表示起始于时间 t=0 的最初上游交通堵塞在稍后时间向下游传播。例如,起始于位置 80 的阻塞在 t=100 时影响位置 30。
堵车的由来是什么?

p=0.3 |塞犍陀·维韦克的纳格尔-施勒肯伯格模型的速度与密度
随着车辆数量的增加,速度降低。在大约 0.2 的密度以上,速度急剧下降。这是因为在密度为 0.2 时,每辆车之间的平均间距为 1/0.2=5。为什么数字 5 很熟悉?因为 5 是可能的最大速度!因此,在这个密度以上,车辆开始感受到前面车辆的影响,并需要相应减速。

p=0.3 |塞犍陀·维韦克的内格尔-施勒肯伯格模型的通量与密度
通量密度图是理解交通堵塞出现点的另一个好方法。交通流量衡量单位时间内通过给定点的车辆数量(车辆通过量)。它基本上是一定距离内所有车辆的速度总和。在低密度下,每辆车基本上都以极限速度行驶,因此通量随着密度线性增加。然而,在较大的密度下,车辆不能以限速行驶,在某一点(这里密度= 0.2),较大数量车辆的影响被以较小速度行驶的每辆车辆抵消,导致流量减少。
有一个最佳车辆密度(单位距离的车辆数量),它使车辆吞吐量最大化。
现实世界中的交通堵塞
Nagel-Schrekenberg 模型很棒,因为它捕捉到了流量的关键特征。但是这些情节在现实世界中是什么样的呢?它们如何转化为真实的距离和速度?
2005 年 6 月 15 日,NGSIM 项目的研究人员在洛杉矶 101 号公路(也称为好莱坞高速公路)上收集了详细的车辆轨迹数据。

作为 NGSIM 项目一部分的车辆轨迹时空图|由塞犍陀·维维克绘制
时空图显示了与交通堵塞相关的独特波形。与 Nagel-Schrekenberg 模型相似,最初在上游 0.5 公里处成核的堵塞最终在大约 100 秒后到达下游 0.1 公里处。

作为 NGSIM 项目一部分的车辆轨迹数据|由塞犍陀·维维克绘制
来自飞行器轨迹数据的通量密度图看起来非常类似于 Nagel-Schrekenberg 图。然而,你看到的单位是不同的。在现实世界中,流量在大约 30 辆车/公里/车道的密度下开始下降。那一点为什么特别?包络计算的快速返回显示,30 辆车/km/车道对应于平均每辆车之间的 1000/30=33 m。一辆以 65 英里/小时(30 米/秒)速度行驶的汽车有 33/30 秒的时间——基本上是一秒钟到达前面的汽车。如果前面的人因为某种原因突然停下来,你有一秒钟的时间做出反应。
车辆以 30 辆/公里/车道的密度开始减速,因为这相当于 1 秒钟对前方车辆做出反应(以 65 英里/小时或 29 米/秒的速度行驶)。
总之,交通堵塞是人类驾驶不完善的结果。其中最重要的是开车时发出噪音的倾向——有时有人会无缘无故地停下来,或者没有以最佳状态开车。另一个原因是我们有一个有限的反应时间,这使我们远离前面的司机(通常是一秒钟)。一旦路上有太多的车辆,我们就会不舒服地靠近前面的车辆,而不会像在自由路段那样开车。下次你觉得如果人们更好地开车,城市的交通状况会有所改善,好好看看镜子里的人吧:)
如果你对自动驾驶汽车如何缓解交通堵塞感兴趣,请看看我的文章: 自动驾驶汽车能避免交通堵塞吗?
基于残差网络的交通标志分类
履行
用于交通标志分类的深度残差学习

汉斯·阿德里安·伯麦在 Unsplash 上的照片
深度卷积神经网络广泛应用于解决人工智能领域的各种计算机视觉任务。本文着重于开发一个深度学习模型,以便识别交通标志。🛑❌🚫🚷🚳
目录
- 数据分析
- 创建一个 ResNet 模型
- 模特培训
- 模型评估
- 预测
- 参考文献
首先,我们需要一个数据集来训练深度学习模型识别交通标志。 Kaggle Datasets 是为不同任务寻找数据集的最佳平台。比如机器学习(ML)、深度学习(DL)、数据科学。
这里有一个数据集,包含了 43 个类别的近 73139 个不同的交通标志图像。
大数据库的交通标志裁剪(+70%)
www.kaggle.com](https://www.kaggle.com/flo2607/traffic-signs-classification)
数据分析
在本节中,我们将使用一种简单的方法来分析数据集。
下面是一个简单的计数图,用于分析数据在类中的分布。以下代码用于绘制图表:

计数图 w.r.t 到类-作者图片
让我们来看看数据集中的一些样本。这将有助于我们理解数据。下面的代码通过绘制数据集中的 100 幅图像来实现这一目的。

来自数据集的图像-按作者分类的图像
创建一个 ResNet 模型
在这一部分,我们将创建一个深度学习模型来识别交通标志。
剩余网络
微软引入了深度剩余学习框架来克服“退化”问题,这是一项困难的优化任务。快捷连接,即跳过一层或多层。
这些快捷连接执行身份映射,并且输出被添加到堆叠层的输出中。这解决了许多问题,例如:
- 易于优化
- 它从大大增加的深度中获得准确性,产生比以前的网络架构更好的结果。
为了更好地理解深度剩余学习。使用 arxiv 上免费提供的题为“图像识别的深度残差学习”的研究论文。
[## 用于图像识别的深度残差学习
更深层次的神经网络更难训练。我们提出了一个剩余学习框架,以减轻训练…
arxiv.org](https://arxiv.org/abs/1512.03385)
我们将使用 TensorFlow 应用模块,该模块提供了不同的流行深度学习模型,并预先训练了可供使用的权重。
[## 模块:TF . keras . applications | tensor flow Core v 2 . 2 . 0
通过 TensorFlow 学习 ML 基础知识的教育资源
www.tensorflow.org](https://www.tensorflow.org/api_docs/python/tf/keras/applications/)
我们将使用没有预训练权重的 ResNet50 架构。我们在最后添加了 softmax 激活的密集层来预测类。下面是用来创建模型。
您可以看到使用 plot_model 方法创建的模型的可视化。
模特培训
这些是在训练过程中使用的参数。批量大小为 32,时期为 50,学习率为 0.001,损失度量为“分类交叉熵”,优化器为“Adam”。回调 ModelCheckpoint、EarlyStopping、ReduceLROnPlateau 和 CSVLogger 用于 ResNet50 模型的定型。你可以使用下面的链接来了解回调的具体细节。
回调是可以在训练的不同阶段执行动作的对象(例如,在一个时期的开始或结束…
keras.io](https://keras.io/api/callbacks/)
以下代码用于编译和拟合模型。
训练数据和验证数据在不同时期的准确性之间的图表。

准确性图表—作者提供的图片
训练和验证数据上的跨时期损失之间的图表。

损失图-按作者分类的图像
你可以看到损失和准确性在 20 个时期后收敛。
模型评估
分类报告
让我们看看分类报告,它有助于评估模型。
输出结果以精度、召回率、F1 分数的形式表示每个类。
混淆矩阵
混淆矩阵用于描述分类模型的性能。以下代码用于生成混淆矩阵:
合成的混淆矩阵如下所示:

混淆矩阵-作者图片
分类准确性
使用下面的代码可以获得类级精度:
预言
使用经过训练的 ResNet50 模型,来自未知数据的少量样本被用于预测类别标签。以下代码用于此目的:
未知数据的预测如下所示:

预测-作者图片
我为该任务编写的代码可以在 Kaggle Notebook 中找到。请随意使用。以下是链接:
使用 Kaggle 笔记本探索和运行机器学习代码|使用来自交通标志分类的数据
www.kaggle.com](https://www.kaggle.com/syamkakarla/traffic-sign-classification-using-resnet?scriptVersionId=35560377)
参考文献
用于图像识别的深度残差学习,何,,,任,;IEEE 关于…的会议
openaccess.thecvf.com](http://openaccess.thecvf.com/content_cvpr_2016/html/He_Deep_Residual_Learning_CVPR_2016_paper.html) [## 计算机视觉模型
“西蒙·普林斯(Simon Prince)的精彩著作提出了一种基于模型的计算机视觉方法,这种方法统一了不同的…
www.computervisionmodels.com](http://www.computervisionmodels.com/)
基于深度神经网络的交通标志识别
皮尤什·马尔霍特拉、普内特和塔尼什克·查莫拉

贾维尔·基罗加在 Unsplash 上拍摄的照片
在当今世界,随着车辆数量的增加,道路事故也在增加,据报道,印度是事故数量最多的国家之一。这是由许多原因造成的,如执法不力、粗心大意等。原因之一是人们不认识或不遵守交通标志板。因此,我们制作了一个交通标志识别器,它可以通知车辆驾驶员前方有交通标志,并跟随它。这可以减少交通事故。
卷积神经网络
卷积神经网络是深度学习的一部分,广泛用于图像识别。这些卷积神经网络由几层组成。首先,Conv2D 层用于在过滤器的帮助下进行特征提取。过滤器的数量通常是 2 的幂,如 32、64 或 128。在这一层中使用了激活功能。通常使用 ReLU(整流线性单位)激活功能。ReLU 函数被定义为最大值(0,x)。
接下来是最大池层,用于减少图像的尺寸。这样做是为了减少处理图像所需的计算能力。第三是辍学层。该脱落层用于防止过度拟合,并降低模型的复杂性。在这一层中,一些神经元被随机移除。
前三层的组合称为特征学习阶段。这三层被多次使用以改善训练。
第四个是 flatten 层,它将二维数据转换为一个完整连接层的长一维特征向量,该向量可以输入到神经网络中。
最后一层是密集层,用作输出层。最后一层的节点数量与类的数量相同。最后一个密集层使用 softmax 激活功能。Softmax 函数给出概率值(在 0 和 1 之间),以便模型可以预测哪个类的概率最高。
交通标志识别
1.资料组
我们采用了 2011 年国际神经网络联合会议(IJCNN)上举行的德国交通标志基准单幅图像分类挑战赛的数据集。链接—【kaggle.com/meowmeowmeowmeowmeow/gtsrb-german-traffic-sign
该数据集由 39,209 幅交通标志图像组成。
2.导入必要的库
我们将为此使用 Python 语言。首先,我们将导入必要的库,如用于构建主模型的 keras、用于分割训练和测试数据的 sklearn、用于将图像转换为数字数组的 PIL 以及其他库,如 pandas、numpy、matplotlib 和 tensorflow。
**import** **numpy** **as** **np**
**import** **pandas** **as** **pd**
**import** **matplotlib.pyplot** **as** **plt**
**import** **cv2**
**import** **tensorflow** **as** **tf**
**from** **PIL** **import** Image *]*
**import** **os**
**from** **sklearn.model_selection** **import** train_test_split
**from** **keras.utils** **import** to_categorical
**from** **keras.models** **import** Sequential, load_model
**from** **keras.layers** **import** Conv2D, MaxPool2D, Dense, Flatten, Dropout
**import** **tqdm**
**import** **warnings**
3.检索图像
我们将检索图像及其标签。然后将图像大小调整为(30,30),因为所有图像都应该具有相同的大小以便识别。然后将图像转换成 numpy 数组。
data = []
labels = []
classes = 43
**for** i **in** range(classes):
path = os.path.join(os.getcwd(),'train',str(i))
images = os.listdir(path)
**for** j **in** images:
**try**:
image = Image.open(path + '**\\**'+ j)
image = image.resize((30,30))
image = np.array(image)
data.append(image)
labels.append(i)
**except**:
print("Error loading image")*#Converting lists into numpy arrays bcoz its faster and takes lesser #memory*data = np.array(data)
labels = np.array(labels)print(data.shape, labels.shape)
4.分割数据集
将数据集分为训练和测试。80%训练数据和 20%测试数据。
X_train, X_test, y_train, y_test = train_test_split(data, labels, test_size=0.2, random_state=68)
print(X_train.shape, X_test.shape, y_train.shape, y_test.shape)
5.构建模型
为了构建,我们将使用 keras 库中的顺序模型。然后,我们将添加层,使卷积神经网络。在前 2 个 Conv2D 层中,我们使用了 32 个过滤器,内核大小为(5,5)。
在 MaxPool2D 层中,我们保留了池大小(2,2),这意味着它将选择图像的每个 2 x 2 区域的最大值。通过这样做,图像的尺寸将减少 2 倍。在脱落层,我们保持脱落率= 0.25,这意味着 25%的神经元被随机移除。
我们再次应用这 3 层,参数有一些变化。然后,我们应用扁平化层转换二维数据到一维向量。这一层之后是致密层、脱落层和再次致密层。最后一个密集层输出 43 个节点,因为交通标志在我们的数据集中被分为 43 个类别。该层使用 softmax 激活函数,该函数给出概率值并预测 43 个选项中哪一个具有最高概率。
model = Sequential()
model.add(Conv2D(filters=32, kernel_size=(5,5), activation='relu', input_shape=X_train.shape[1:]))
model.add(Conv2D(filters=32, kernel_size=(5,5), activation='relu'))
model.add(MaxPool2D(pool_size=(2, 2)))
model.add(Dropout(rate=0.25))
model.add(Conv2D(filters=64, kernel_size=(3, 3), activation='relu'))
model.add(Conv2D(filters=64, kernel_size=(3, 3), activation='relu'))
model.add(MaxPool2D(pool_size=(2, 2)))
model.add(Dropout(rate=0.25))
model.add(Flatten())
model.add(Dense(256, activation='relu'))
model.add(Dropout(rate=0.5))
model.add(Dense(43, activation='softmax'))
6.应用该模型并绘制精确度和损耗的图表
我们将编译该模型,并使用拟合函数来应用它。批量大小将是 32。然后我们将绘制精确度和损耗的图表。我们得到了 97.6%的平均验证准确率和 93.3%的平均训练准确率。
model.compile(loss='categorical_crossentropy', optimizer='adam', metrics=['accuracy'])history = model.fit(X_train, y_train, batch_size=32, epochs=2, validation_data=(X_test, y_test))model.save("Trafic_signs_model.h5")
*#plotting graphs for accuracy*
plt.figure(0)
plt.plot(history.history['accuracy'], label='training accuracy')
plt.plot(history.history['val_accuracy'], label='val accuracy')
plt.title('Accuracy')
plt.xlabel('epochs')
plt.ylabel('accuracy')
plt.legend()
plt.show()*#plotting graphs for loss*
plt.figure(1)
plt.plot(history.history['loss'], label='training loss')
plt.plot(history.history['val_loss'], label='val loss')
plt.title('Loss')
plt.xlabel('epochs')
plt.ylabel('loss')
plt.legend()
plt.show()
7.测试集的准确性
我们在测试集上获得了 94.7%的准确率。
**from** **sklearn.metrics** **import** accuracy_scorey_test = pd.read_csv('Test.csv')
labels = y_test["ClassId"].values
imgs = y_test["Path"].values
data=[]
**for** img **in** imgs:
image = Image.open(img)
image = image.resize((30,30))
data.append(np.array(image))
X_test=np.array(data)
pred = model.predict_classes(X_test)*#Accuracy with the test data*
print(accuracy_score(labels, pred))
图形用户界面
现在模型已经准备好了,所以我们可以在界面(GUI)上创建一个图形用户了。我们使用 tkinter 库来制作 GUI。图形用户界面代码:
输出

作者 GIF
结论
因此,我们开始了解卷积网络以及它们如何用于图像识别。我们利用卷积神经网络进行了交通标志识别,在验证集和测试集上的准确率分别达到了 97.6%和 94.7%。
完整的代码可以在下面的 github 库中找到:交通标志识别
谢谢你。
使用 TensorFlow 2.x 识别交通标志
从原始图像处理到模型评估的端到端示例

左:TensorFlow logo。图片来源:tensorflow.com 右:原始和灰度交通标志来自下面的数据
介绍
TensorFlow 是一个软件库,以其在神经网络中的灵活性和易用性而闻名。您可以在网上找到许多从图像分类到对象检测的示例,但其中许多都基于 TensorFlow 1.x。从 TensorFlow 1.0 到 2.0 有一个很大的变化,Keras 集成更紧密,重点是更高级别的 API。很多方法已经折旧了(也可以用tf.compat.v1)。模型构建变得容易多了,每个模型中的默认参数已经可以很好地用于一般用途。尽管有这么多好处,但如果您需要更改参数,它仍然提供了灵活性。
在这篇文章中,我将使用 TensorFlow 2.0(更具体地说,TensorFlow 中的 Keras)对交通标志进行分类。网络上很多地方都有数据集,但我将使用 Kaggle 上的这个。
数据概述
数据包包括Train、Test和一个test.csv的文件夹。有一个meta.csv和一个Meta文件夹来显示每个交通标志的标准图像。还有一个signname.csv用于将标签映射到它的描述。Train文件夹包含 43 个子文件夹,子文件夹的名称是其中图像的标签。例如,文件夹0中的所有图像都有一个类别标签0等等……这些图像的大小从 20x20 到 70x70 不等,并且都有 3 个通道:RGB。
所以我要做的第一件事是将所有图像的大小调整为 32x32x3,并将它们读入 numpy 数组作为训练特征。同时,我创建了另一个 numpy 数组,其中包含每个图像的标签,这些标签来自加载图像的文件夹名称。
我需要做同样的测试图像。然而,用于测试图像的标签与该图像的路径一起存储为test.csv中的ClassId 。所以我使用 pandas 来读取csv 文件,从 path 加载图像并分配相应的ClassId。
从训练集中,我随机抽取了 20%作为模型训练过程中使用的验证集。训练和验证的模型精度将为我们提供关于欠拟合或过拟合的信息。
接下来,我将图像转换成灰度,并对每个像素进行归一化处理。归一化使模型收敛更快。
这是 RGB 和灰度图像之间的比较。灰度图像仍然保留其特征,并且可以被识别,但是尺寸小得多。

原始图像和灰度图像的比较
模型构建
我将使用 Yann LeCun 等人在 1998 年发表的著名 LeNet,输入形状为 32x32x1。第一个卷积层的深度为 6,过滤器大小为(5,5),跨距为(1,1)。使用了有效的填充(即没有填充)。因此,该层的宽度(或高度)为 32–5+1 = 28,即形状为 28x28x6。该层的激活是 relu。
第一个卷积层之后是最大轮询层。它通过仅选择相邻像素的最大值像素来有效地缩减数据。LeNet 使用(2,2)内核大小。默认步幅与内核相同,这意味着从其中选择最大值的像素组之间没有重叠。现在输出的形状变成了 14x14x6。
下一个 LeNet 有第二个卷积层,深度为 16,过滤器大小为(5,5)和 relu 激活函数,后面是最大池层。输出的宽度(或高度)现在是(14–5+1)/2 = 5,即形状是 5x5x16。
然后,数据在完全连接的层之前被展平。输出的形状是 5x5x16 = 400。随后是 2 个完全连接的层,尺寸为 120 和 84,relu 作为两者的激活功能。增加一个脱落层以减少过度拟合。最后是大小为 43(类的数量)的全连接层。Softmax 用于返回每个类别的概率。
模型训练和评估
Keras 的培训非常简单。我们只需要指定优化器、损失函数和验证度量。在 10 个时期内,训练和验证的准确度都在 0.97 以上。对于脱层,没有明显的过度拟合。另一方面,增加训练只会产生最小的改善,所以我只在 10 个周期后就停止了。
我们还可以绘制每个时期训练和验证的模型性能。事实上,该模型似乎相当一般化,并且没有过度拟合训练数据。

每个历元的训练和验证的准确性和损失
最后,利用该模型对测试集的标签进行预测。精度在 0.925 左右。
结论
之前我写过一篇关于从零开始构建神经网络的文章,这需要核心的线性代数。通过使用 TensorFlow 这样的库,任务变得容易多了,模型也更强大了。
可以从 这里 获取完整代码。
使用 AWS Sagemaker | PyTorch 训练 GAN 并生成人脸

在 Unsplash 上拍摄的“我的镜头人生”
我想你已经听说过或研究过 GAN。如果你以前没有听说过,那么生成敌对网络(GAN)是一种神经网络架构,允许我们创建合成数据、图像或视频。它已经成为深度学习中一个有趣的子领域。不同类型的 GAN 包括 DCGAN、CycleGAN(CGAN)、GauGAN、StyleGAN、Pix2Pix 等。由于它是如此受欢迎,新类型的甘文件和建筑出现,因为我们说话!
虽然有许多不同的 GAN 架构,但它们都有一个共同点。为了训练一个 GAN,他们需要大量的计算能力,他们渴望 GPU。所以在本地环境中训练一个 GAN 真的很难,除非你有时间和金钱建立一个好的分布式 GPU。否则你可以利用云来训练甘。云环境可以用于各种神经网络训练,并且不限于 GAN 的。我在本地环境中运行时遇到了问题,所以我使用了云,并且能够轻松地进行培训并快速将其部署到生产环境中!
有不同的云提供商,我觉得 AWS 在许多领域领先于其他云提供商。特别是在机器学习领域,AWS 有不同的服务可以利用。所以在这篇博客中,我们将看看由 AWS 提供的 Sagemaker 服务。

Amazon SageMaker 是一个完全托管的服务,它为我们提供了快速构建、训练和部署机器学习(ML)模型的能力。SageMaker 的另一个巨大优势是,机器学习模型可以用更少的努力更快地部署到生产中。是的,一些云提供商比 AWS 便宜,但是 sagemaker 在部署方面有其他优势。在开发模型时,如果您有本地 GPU 环境,您也可以利用它。
在这个博客中,我们将生成新的面孔(再次!)通过训练名人数据集。为了生成新的图像,我将使用我的本地 GPU 环境(以节省一些钱)进行开发和健全性测试,并使用 Sagemaker 来训练一个成熟的模型。我还将展示如何为部署创建端点。
由于有大量关于 AWS 帐户设置和本地环境设置的文章,我将跳过这一部分。如果你有任何问题,请在评论区提问。Sagemaker 可以通过 AWS 服务控制台页面访问。

现在 Jupyter 笔记本有两种选择。
- 使用本地环境
- Sagemaker 环境
当地环境: 如果你有一个适合 Jupyter 笔记本的当地环境,那么恭喜你!通过使用本地环境进行开发和健全性测试,您可以节省一些钱。您安装 Sagemaker python 包并在本地使用 Sagemaker 函数。如果您的 GPU 启用了 Cuda,那么您可以使用它来测试整个代码并提交您的作业 sagemaker。下面是设置本地环境的步骤。全部代码都在我的 Github 页面中
步骤 1:安装包
在你的虚拟环境中安装 Sagemaker python 包https://pypi.org/project/sagemaker/
第二步:连接到你的 AWS 账户
,假设你已经创建了一个 AWS 账户,并且拥有 Sagemaker 和 S3 bucket 访问权限。您还可以在您。AWS/配置文件。
您还需要一个 IAM 角色来执行 sagemaker。它需要完全访问 Sagemaker。
import sagemaker
import boto3sagemaker_session = sagemaker.Session(boto3.session.Session(
aws_access_key_id='xxxxxxxxxxxxx',
aws_secret_access_key='xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
region_name='update your AWS region'))bucket = sagemaker_session.default_bucket()prefix = 'sagemaker/dcgan'role = 'sagemaker_execution_role'
您可以通过在 S3 存储桶中上传测试数据来测试连接,并使用以下命令进行检查
input_data = sagemaker_session.upload_data(path=data_dir, bucket=bucket, key_prefix=prefix)
input_data
如果你没有得到任何错误和数据是在 S3 桶,那么你是好的开始。如果您遇到任何错误,请调试并纠正问题。或者,您可以在这里提供 S3 桶链接,并从 S3 下载数据进行本地测试。
Sagemaker 环境: 如果没有本地环境,可以启动 sagemaker Jupyter 笔记本。这将启动一个计算实例,并为 Jupyter 笔记本电脑部署所需的容器。
步骤 1:启动笔记本
转到笔记本实例部分 sagemaker 并创建一个笔记本实例

接下来,您可以设置 S3 bucket 和 IAM 角色。根据您的需求和规模选择云实例的大小和其他技术细节。

现在,我们可以开始“创建”了。AWS 需要一些时间来准备笔记本。我们可以在控制台上看到笔记本实例处于“挂起”状态。

准备好后,点击“打开 Jupyter”笔记本。你现在可以开始训练你的 GAN 了。
甘模型训练:
我用 PyTorch 来训练 GAN 模型。在训练之前,它需要一些预处理。如果您使用本地环境,您需要上传 S3 存储桶中的数据。下面是您需要执行的一些处理。
- 转换输入图像并使它们具有相同的大小。
def get_dataloader(batch_size, image_size, data_dir):
"""
Batch the neural network data using DataLoader
:param batch_size: The size of each batch; the number of images in a batch
:param img_size: The square size of the image data (x, y)
:param data_dir: Directory where image data is located
:return: DataLoader with batched data
"""
transform = transforms.Compose([transforms.Resize(image_size),
transforms.ToTensor()])
dataset = datasets.ImageFolder(data_dir,transform=transform)
#rand_sampler = torch.utils.data.RandomSampler(dataset, num_samples=32, replacement=True)
#dataloader = torch.utils.data.dataloader.DataLoader(dataset, batch_size=batch_size,shuffle=False, sampler=rand_sampler)
#dataloader = torch.utils.data.dataloader.DataLoader(dataset, batch_size=batch_size,shuffle=True)
return dataloader
测试时,您可以对输入数据集使用随机采样器,并在数据加载器中使用它。
2.缩放图像
缩放图像是神经网络中的一个重要步骤。在执行 GAN 时尤其如此。
def scale(x, feature_range=(-1, 1)):
''' Scale takes in an image x and returns that image, scaled
with a feature_range of pixel values from -1 to 1\.
This function assumes that the input x is already scaled from 0-1.'''
# assume x is scaled to (0, 1)
# scale to feature_range and return scaled x
min, max = feature_range
x = x * (max - min) + min
return x
3.创建模型
当执行 GAN 时,需要训练两种类型的网络。一个是生成器,另一个是鉴别器。发电机的输入来自潜在空间或噪声。一个生成器被训练来生成图像,一个鉴别器被训练来检测图像是真是假。在生成器和鉴别器之间玩这个游戏的最终输出是来自生成器的看起来像真实图像的真实输出。

如前所述,GAN 还有其他架构。然而,这是甘背后的想法。型号代码在 Github repo 的 model.py 中提供。我已经使用卷积测试编写了一个 DCGAN 模型
4.训练模型
这是我们要利用云的一步。在 sagemaker 中运行许多 epochs 之前,使用样本数据在本地环境中测试完整的工作流。
一些超参数需要调整,如学习率、β1 和β2。我从这篇论文中选出了它https://arxiv.org/pdf/1511.06434.pdf
一旦进行了健全性测试,就该将这项工作提交给 sagemaker 了。使用 sagemaker PyTorch API 创建一个估计器对象,并调用 fit 方法。
from sagemaker.pytorch import PyTorchestimator = PyTorch(entry_point="train.py",
source_dir="train",
role=role,
framework_version='0.4.0',
train_instance_count=4,
train_instance_type='ml.p2.xlarge',
hyperparameters={
'epochs': 15,
'conv_dim': 64,
})estimator.fit({'training': input_data})
上面的代码需要注意以下几点:
- 你可以改变 ML 框架。Sagemaker 支持 PyTorch、Tensorflow 等所有主流框架。
- 需要指定所有代码所在的源目录,如我的 GitHub 存储库中所示。
- 需要指定 Pytorch 框架版本。培训目录还应包含 requirement.txt 文件,其中包含数据处理和培训中使用的所有软件包。
- 实例类型取决于您需要多大的计算实例。如果你正在训练一个 GAN,我至少更喜欢 p2.xlarge,因为它包含 GPU。建议使用支持 GPU 的计算服务器。否则模型将永远训练下去。
一旦你调用了 fit 方法,它应该会创建一些日志,如下图所示。它正在启动一个计算实例并训练模型。


不同的颜色突出显示它使用不同的计算实例。我们还打印鉴频器和发电机损耗。现在你可以让它训练,直到它完成。
如果您的训练时间很长,您的内核会话可能会结束。不要担心,因为我们在云中训练,我们可以很容易地附加到我们通过下面的代码运行的会话。可以在 sagemaker 控制台中找到作业名称。
estimator = estimator.attach('sagemaker-job-name-2020-xxxxx')
一旦模型被训练,你就可以部署它了。
5.部署模型:
将模型部署到另一个计算能力较低的计算实例。但是,如果您需要 GPU 进行预测,请使用 p2.xlarge 或更高版本。该模型还可以通过实例计数参数以分布式方式提供服务。
predictor = estimator.deploy(initial_instance_count = 1, instance_type = ‘ml.m5.large’)

部署后,您可以获得端点名称
6.结果-生成面孔
部署模型后,是时候从我们训练好的模型生成人脸了。
#Generate random noise
fixed_z = np.random.uniform(-1, 1, size=(16, 100))
fixed_z = torch.from_numpy(fixed_z).float()sample_y = predictor.predict(fixed_z)

我已经在我的 Github repo 中添加了所有文件。
制作:
一旦我们部署了模型和端点,我们就可以创建一个可以通过 API 网关调用的 AWS Lambda 函数。API 可用于从任何应用程序生成图像。
所有的代码和包都在我的 Github 中找到,我希望你能利用这个故事中的回购。
有问题吗?评论?请在评论区留下您的反馈。
获取代码
要获得文章的完整工作代码和其他更新,请订阅我的 简讯 。
训练一个 GPT 2 变形金刚来写哈利波特!
用数据做酷事!

哈利波特图片,来源:【https://unsplash.com/photos/WE7YfTGpXlg
介绍
自然语言处理是近来越来越受欢迎的领域。世界各地的许多公司都在利用自然语言处理的能力和该领域的创新,从文本中提取有意义的见解并生成文本。在过去的几年里,随着谷歌大脑的注意力是你所需要的全部论文的发表,变形金刚架构已经进一步革新了这个领域。在几乎所有经典的 NLP 任务中,如机器翻译、问题回答、阅读理解、常识推理和总结,基于 Transformer 的架构击败了最先进的架构。从那时起,所有的科技巨头,如谷歌、脸书、OpenAI、微软,都在各种应用中试验 Transformer。
一个成为头条新闻的应用程序是语言生成任务,其中变形金刚能够根据提示生成有意义的文本。第一个头条新闻是 HuggingFace 的对话变形金刚网页,任何人都可以通过给出提示来生成他们自己的人工智能生成的文本。在这里,我们将探讨如何在语言生成中使用变形金刚。同样在博客的后面,我们将分享如何在你自己的语料库上训练 transformer 语言模型的代码。我们用哈利波特系列训练了一个 GPT-2 模型。当输入时,经过训练的模型能够生成像哈利波特书籍一样的文本。请参见下面的示例。我的 Github 上有完整的代码。
有趣的观察:1。模特得知海格有一双大脚!, 2.吉德罗·洛哈特写书,3。新书可以出现在霍格沃茨的书架上。

哈利波特 GPT2 模型输出
原文全文发表在我的网站这里。
什么是语言模型?
语言模型是学习预测单词序列概率的模型。简单地说,语言模型本质上是在给定一些文本的情况下预测下一个单词。通过在特定文本上训练语言模型,有可能使模型学习该文本的写作风格。虽然过去存在各种各样的语言模型,但在谷歌大脑团队推出变形金刚之后,它们变得更加强大(“注意力是你所需要的全部”)。
随着 transformers 的出现,多个小组能够为语言模型创建和训练定制的架构。一个这样的团体是开放人工智能社区,他们引入了 GPT(T2 的缩写,生成预训练转换器)。GPT 模型于 2018 年发布,但不幸的是,在发布后不久,它就被 BERT 从 GLUE 排行榜上除名了。但是,在 2019 年 2 月,OpenAI 通过对庞大的 15 亿个参数进行训练,扩大了他们的模型,这反过来又赋予了它类似人类的写作能力。它被命名为open ai 的 GPT-2”。

图 1: 出自https://blog.openai.com/better-language-models/。
变形金刚和 GPT 2
Transformers 是语言模型背后的基本架构。转换器主要由两个基本组件组成:编码器和解码器。
如上图所示,编码器和解码器都有可以堆叠在一起的模块,如 Nx 所示。主要地,编码器和解码器都具有前馈和多头注意力组件。

论文中的变压器架构—“关注是你所需要的”
在将输入和输出传递给组件之前,它们被嵌入到一个 n 维空间中。输入和输出组件中的一个重要步骤是位置编码,其中我们向转换器提供关于单词位置的信息。这些编码被添加到每个单词的嵌入中,生成的嵌入被传递给转换器。
编码器块具有多个编码器块,而解码器块具有相同数量的解码器块。块数是一个超参数,可以在训练时进行调整。

编码器-解码器架构
编码器-解码器堆栈的工作描述如下:
- 输入嵌入被传递到第一编码器。
- 嵌入在通过编码器的前馈和自关注层之后被转换。
- 转换后的输出被传递到下一个编码器。
- 最后一个编码器将输出传递给堆栈中的所有解码器。
编码器和解码器中的自我注意层在文字处理中起着非常重要的作用。它使模型能够查看输入序列中的其他单词,以更好地理解当前单词的上下文。
除了前馈和注意组件,解码器还有另一个注意层(掩蔽多头注意),帮助解码器关注输入序列的特定部分。
OpenAI 在两次迭代中扩展了语言生成任务的变形金刚概念:GPT 和 GPT-2。GPT 架构使用 12 层解码器,带掩蔽自关注头,训练 100 个纪元。使用 GPT-2 模型,词汇量扩大到 50,257 个单词。上下文大小也从 512 个令牌增加到了 1024 个令牌,并且使用了更大的批量 512。
潜入代码!
在这篇博客中,我们将利用令人敬畏的 HuggingFace 的变形金刚库来训练我们自己的 GPT-2 模型,使用哈利波特书中的文本。我们将向模型提供一个句子提示,模型将完成文本。为了训练模型,我们会喂饱所有哈利波特的书,让模型从中学习。
我们已经克隆了 huggingface repo,并更新了代码以正确执行语言模型训练和推理。请跟随我的 Github 回购。
下载哈利波特书籍并预处理文本
第一步是下载所有的哈利波特书籍并预处理文本。我们把前四本书的课文刮下来,合并在一起。然后我们写了一小段代码来删除合并文本中不必要的文本,比如页码。最后,GPT 新协议模型需要训练和验证文本。因此,我们将前 90%的数据作为训练样本,其余的数据作为验证样本。这里的预处理代码是。
训练一架 GPT-2 模型
为了训练模型,我们使用脚本 run_lm_finetuning.py。该脚本将模型类型及其大小以及预处理文本作为输入。该脚本还提供了一组可以调整的超参数,以便定制训练过程。培训的代码片段是:
cd examples ## Move to examples directory
python run_lm_finetuning.py \
--output_dir=output \
--model_type=gpt2 \
--model_name_or_path=gpt2-medium \
--do_train \
--train_data_file='input_data/train_harry.txt' \
--do_eval \
--eval_data_file='input_data/val_harry.txt'\
--overwrite_output_dir\
--block_size=200\
--per_gpu_train_batch_size=1\
--save_steps 5000\
--num_train_epochs=2
代码中使用的参数如下:
这里使用的参数解释如下:
- Output_dir 是存储模型权重的文件夹名称。
- Model_type 是模型的名称。在我们的例子中,我们在 gpt-2 架构上训练,我们使用“gpt-2”。
- 模型名称或路径是我们定义要使用的模型大小的地方。(“gpt2”代表小型,“gp T2-中型”代表中型型号,“gp T2-大型”代表大型型号)
- Do_train 本质上是一个标志,我们定义它来训练模型。
- train_data_file 用于指定训练文件名。
- Do_eval 是一个标志,我们定义是否评估模型,如果我们不定义它,就不会计算出困惑分数。
- Eval_data_file 用于指定测试文件名。
- gradient_accumulation_steps 是一个参数,用于定义在执行向后/更新传递之前要累积的更新步数。
- Overwrite_output_dir 是一个参数,当指定该参数时,将使用新的权重覆盖输出目录。
- block_size 是一个参数,根据该参数,训练数据集将被截断成这个大小的块用于训练。
- Per_gpu_train_batch_size 是用于训练的每个 GPU/CPU 的批处理大小。
- 保存步骤-允许您在最终设置权重之前定期保存权重
- num_epochs —确定运行多少个 epoch。
我们根据 4 本哈利波特的文本训练了一个中型的 GPT-2 模型。这款车型在一辆 GTX 1080 Ti 上训练只花了 10 分钟。训练模型的困惑分数是 12.71。阅读这个博客来了解更多关于困惑分数的信息。但是记住,分数越低,模型越好。
推理脚本
一旦模型被训练,我们就可以使用它进行推理。推理脚本是 run_generation.py
为了进行推理,输入文本首先通过标记器进行编码,然后结果通过一个生成函数进行传递,在该函数中,基于温度、top-p 和 k 值等参数生成文本。
进行推断的代码片段是:
cd examples
python run_generation.py --model_type gpt2 --model_name_or_path output --length 300 --prompt "Malfoy hadn’t noticed anything."
这些参数解释如下:
- 模型名称或路径:这是存储训练模型权重的文件夹路径。
- Prompt:这是输入提示,其余文本将基于该提示生成。
- Length:该参数控制输出中要生成的字符的长度。
一些可以调整的附加参数是:
- 温度:这个参数决定了模型在单词选择上的冒险程度。

来源:https://medium . com/hugging face/how-to-write-with-transformer-5ee 58d 6 f 51 fa
- p:这个参数控制延续的范围有多广。将其设置为高,以考虑所有延续。将其设置为低,以便仅考虑可能的延续。整体效果类似于温度,但更微妙。
- k:该参数控制概率序列中的波束或并行搜索的数量。值越高,精度越好,但速度越慢。
- 种子:该参数有助于设置种子。
- Repetition _ penalty:该参数惩罚重复所选单词的模型。
下面是模型输出的另一个例子。非常有趣的是看到这个模型创造的隐形人的故事。

经过训练的哈利波特模型的另一个输出
结论
变形金刚的出现真正革新了许多自然语言处理任务,语言生成就是其中之一。语言生成模型的潜力是巨大的,可以在许多应用程序中利用,如聊天机器人、长答案生成、编写自动报告等等。在这篇博客中,我们了解了 transformers 的工作原理,它们是如何用于语言生成的,以及一些例子说明了任何人如何利用这些架构来训练他们自己的语言模型并生成文本。
总的来说,我对 NLP、变形金刚和深度学习非常感兴趣。我有自己的深度学习咨询公司,喜欢研究有趣的问题。我已经帮助许多初创公司部署了基于人工智能的创新解决方案。请到 http://deeplearninganalytics.org/.的— 来看看我们吧
你也可以在 https://medium.com/@priya.dwivedi 的看到我的其他作品
如果你有一个我们可以合作的项目,请通过我的网站或 info@deeplearninganalytics.org 联系我
参考
- 变形金刚——注意力是你需要的全部。这是开始这一切的文件
- 伯特
- GPT-2 模型
- 抱脸回购
训练一个神经网络,在 10 分钟内对图像进行分类并优化 CPU 推理
最小化设置/转移学习/快速优化
英特尔 OpenVINO 工具包的可示教机器 2.0 图像项目
有大量关于简化训练和优化预训练推理模型的资源。然而,用最少的努力在现成的硬件上训练一些定制的东西来优化性能似乎仍然遥不可及!
在本文中,我们将利用迁移学习的概念,其中一个训练有素的图像分类模型用于在您的设备浏览器中使用可示教机器 (GUI)训练我们的定制用例(例如,您餐具室中的物品),并使用英特尔 OpenVINO 工具包优化 CPU 推理,而无需任何痛苦的软件安装(当然只需 10 分钟!).
优化有必要吗?跳到最后的性能比较。
实时演示
我事先需要做什么准备?
- 第六代至第十代英特尔酷睿或英特尔至强处理器
(即,如果您购买了 2016 年以后发布的英特尔设备) - Docker 安装在可以上网的 Linux 系统上hub.docker.com
- 使用摄像设备(如网络摄像头)
训练并将模型导出到您的本地机器

为培训捕获数据
- 在你带摄像头的笔记本电脑或桌面设备上,导航到https://teachablemachine.withgoogle.com/train/image
- 编辑 类别 标签(如麦片盒、曲奇)并根据需要添加
- 使用 Hold to Record 按钮从实时预览中捕捉几帧
- 对每个班级重复这一过程,最后点击训练模型按钮
注意:不要切换你的浏览器标签,让训练结束 - 在最右边的预览面板中测试,点击导出模型
- 在上导出你的模型..弹出点击第二个第二个第三个标签第三个张量流
- 选择 Keras 后,点击下载我的模型
这可能需要一些时间,但一个名为 converted_keras.zip 的文件最终会被下载。

导出 TF Keras 模型以进行局部推理和优化
将任何可示教的机器 2.0 图像项目 Keras 模型转换为 CPU 优化的 OpenVINO IR 格式
我已经创建了一个带有实用程序的 repo 来自动执行此操作,所以您不必这么做!确保系统上安装了 docker 、internet access、 unzip、和 git 。
克隆或下载/提取回购:
git clone [https://github.com/ojjsaw/teachable-machine-openvino.git](https://github.com/ojjsaw/teachable-machine-openvino.git)
替换 repo 目录中的自定义 converted_keras.zip 文件,并从 repo 根目录运行以下脚本。第一次下载 docker 图像可能需要几分钟时间。
./util_conv_teachable_to_openvino.sh

我的 repo 中的 Util 脚本,用于自动转换
在测试映像上运行英特尔 CPU 优化推理
确保捕获与您的定制训练模型相关的测试图像,并运行下面的代码。
仅在 TF Keras v1.15.0 模型到 OpenVINO IR 转换(包括移除训练节点)后,在 test.jpg 上使用 OpenVINO 推理 4ms 。
docker run --rm -it -v ${PWD}:/workdir openvino/ubuntu18_dev:latest /bin/bashcd /workdirpython3 teachable_img_openvino_classify.py frozen_model.xml frozen_model.bin labels.txt test.jpg

OpenVINO CPU 推断时间:4 ms(test.jpg)
性能比较
可教机器网站提供 TF Keras python 代码用于本地推理。
在我的例子中,使用默认示例代码和模型的预测函数花费了804 ms(~ 1.2 fps),而在上一节的同一个test.jpg 图像上使用 OpenVINO python 代码仅仅花费了 4ms (~250fps) 。****
docker run --rm -it -v ${PWD}:/workdir tensorflow/tensorflow:1.15.0 bashcd /workdirpip install Pillowpython teachable_img_keras_orig_classify.py keras_model.h5 test.jpg

原 TM2.0 推断时间:804 ms(test.jpg)****
在一个实时摄像机上运行 OpenVINO 推理
第一个命令支持从 docker 呈现 OpenCV 预览窗口。
xhost + docker run --rm -it --privileged -v ${PWD}:/workdir -e DISPLAY=$DISPLAY -v /tmp/.X11-unix:/tmp/.X11-unix -v /dev/video0:/dev/video0 openvino/ubuntu18_dev:latest /bin/bash cd /workdir python3 teachable_livecam_openvino_classify.py frozen_model.xml frozen_model.bin labels.txt

在 TM2.0 导出/转换模型上使用网络摄像头进行 OpenVINO 推理
结论
确实存在用日常硬件在本地快速训练定制图像分类模型的流程。
不需要修改代码,只需点击几下鼠标。
考虑到不需要任何额外的努力,在英特尔 CPU 上使用 OpenVINO 推理技术来减少(800 毫秒到 4 毫秒)的额外努力也是完全值得的!
用 python 训练神经网络来预测机器人动力学
了解如何处理真实的机器人数据。
我作为机器人学习研究员的生活主要是数据科学(理解数据)和一点有趣的数学(强化学习)。这里有一个片段,你可以自己运行,并把经验带到你的项目(和职业生涯)中去!

一堆有趣的机器人平台,来自我的资格考试演讲。我们希望这些能飞起来!
深度学习和机器人技术
神经网络在多个数字领域,特别是计算机视觉和语言处理领域已经成为最先进的技术。神经网络是复杂的函数逼近(能够足够精确地拟合任何函数,从一个有趣的定理),那么为什么不将它们用于机器人呢?
在机器人中,我们想为两件事建立动力学模型:
- 计划采取行动。行动完成任务。这就制造出了有用的机器人。我们都想要有用的机器人。
- 了解系统。了解该系统可以为机器人开辟更多的未来行动和任务。

经典的神经网络图解。密集连接的非线性激活函数可以适合任何具有足够数量神经元的函数。
最优化问题
当用神经网络学习时,将预测系统动态中的离散步骤。动态系统采取如下所示的形式:
- s 是系统的状态(如物理位置)。
- 一个 是代理的动作(如电机电压)。
- f 是机器人的真实动力学。
- w 是扰动(随机性,或系统的未建模部分)。

动力系统的一般形式。
我们将用神经网络(有意地)建模的是底层函数, f ,我们隐式地捕捉数据中的扰动, w 。我们如何做到这一点?为了数据的“美观”,我们做了几件事。精确意味着数据接近均匀分布,平均值为 0,标准偏差为 1。



我们对学习到的动力学(用θ下标表示)进行建模,以预测状态的变化,而不是真实的状态。右边的 delta 状态是从上一个时间步到下一个时间步的简单变化。
在神经网络的环境中,这看起来像什么?具体来说,我们可以最小化状态变化和函数逼近之间的差的范数平方。这是一个均方差。在 PyTorch 这里实现。

最先进的技术是使用像 Adam 优化器、 Swish 激活函数(ReLU 的平滑版本)、学习率调度器等等。让神经网络工作的重要方法是数据标准化。如果你仔细看代码,我用一种非常模块化的方式处理它。没有它,将模型拟合到真实数据几乎是不可能的。

我玩的机器人——底部) Crazyflie 微型飞行器和顶部) Ionocraft 离子推进器。
代码(PyTorch)
这就是你如何运行代码。我包含了一个名为 ml 的 conda 环境(PyTorch,AI Gym,Mujoco 等等)。培训师代码可在学习目录中找到。参数存储在配置文件中。
(ml) user: dynamicslearn nato$ python learn/trainer.py robot=iono_sim
机器人
知识库中有经过预过滤的数据,这些数据来自通过强化学习学习控制的实验机器人的真实飞行。它是加州大学伯克利分校的一个活跃的研究资料库。
代码包括两个飞行机器人的模拟器,分别是 Crazyflie 和 Ionocraft 。这两个都允许你以 100 赫兹控制机器人,模拟器以 1000 赫兹计算动力学。你可以在这里找到动态模拟器。特定的机器人为不同的致动器定义力的转换。
丰富 NN 对象
当处理复杂任务时(弄清楚如何为机器人建模),拥有一个可配置的网络对象会有所帮助。你会在这里看到一系列的旗帜:
- 一个 prob 标志——它从 MSE 变成了一个高级损失函数,具有来自的更好的方差正则化。
- 一个历史参数——我们希望将过去的状态传递到预测器中(当状态进化得更快时,一些潜在的动态就会进化)。
- 一堆参数 — 隐藏宽度、深度、落差等。
- SciKitLearn 规范化标量——对于获得可行的优化表面非常重要(没有这个你可能会得到 NaN 的)。
注意,我用 X 表示状态,用 U 表示动作。
class GeneralNN(nn.Module):
def __init__(self, **nn_params):
super(GeneralNN, self).__init__()
# Store the parameters:
self.prob = nn_params['training']['probl']
self.hidden_w = nn_params['training']['hid_width']
self.depth = nn_params['training']['hid_depth'] self.hist = nn_params['history']
self.n_in_input = nn_params['du'] * (self.hist + 1)
self.n_in_state = nn_params['dx'] * (self.hist + 1)
self.n_in = self.n_in_input + self.n_in_state
self.n_out = nn_params['dt'] self.activation = Swish()
self.d = nn_params['training']['dropout']
self.E = 0 # clarify that these models are not ensemble self.scalarX = StandardScaler()
self.scalarU = MinMaxScaler(feature_range=(-1, 1))
self.scalardX = MinMaxScaler(feature_range=(-1, 1)) self.init_training = False
动态创建层
对于 PyTorch 新手来说,这可能是文章中最有用的部分。它让你创建一个不同深度和宽度的网络(另一种方法是对配置进行硬编码)。
layers = []
layers.append(('dynm_input_lin', nn.Linear(
self.n_in, self.hidden_w))) # input layer
layers.append(('dynm_input_act', self.activation))
for d in range(self.depth):
layers.append(
('d_l' + str(d), nn.Linear(self.hid_w, self.hid_w)))
layers.append(('dynm_act_' + str(d), self.activation))
layers.append(('d_o_l', nn.Linear(self.hid_w, self.n_out)))
self.features = nn.Sequential(OrderedDict([*layers]))
训练模型
省略公开可用的优化器代码,以及在许多教程中详述的,我们可以有一个很好的训练函数作为我们的顶级对象。让较低层次的抽象来处理细节。
def train_model(X, U, dX, model_cfg, logged=False):
if logged: log.info(f"Training Model on {np.shape(X)[0]} pts")
start = time.time()
train_log = dict() train_log['model_params'] = model_cfg.params
model = hydra.utils.instantiate(model_cfg) model.train_cust((X_t, U_t, dX_t), model_cfg.params) end = time.time()
if logged: log.info(f"Trained Model in {end-start} s")
return model, train_log
这个存储库还有更多的功能。我很乐意回答问题、提出请求和反馈。或者你可以在这里了解我的研究。
我在新型机器人的基于模型的强化学习方面的工作目录。最适合高考验的机器人…
github.com](https://github.com/natolambert/dynamicslearn) [## 自动化大众化
一个关于机器人和人工智能的博客,让它们对每个人都有益,以及即将到来的自动化浪潮…
robotic.substack.com](https://robotic.substack.com/)
使用决策树训练回归模型
对于复杂的非线性数据

作者图片
决策树是一种非参数监督学习方法,能够发现数据中复杂的非线性关系。他们可以执行分类和回归任务。但是在本文中,我们只关注带有回归任务的决策树。为此,等价的 Scikit-learn 类是DecisionTreeRegressor。
我们将从讨论如何用决策树训练、可视化和预测回归任务开始。我们还将讨论如何正则化决策树中的超参数。这样可以避免过拟合的问题。最后,我们将讨论决策树的一些优点和缺点。
代码约定
我们使用以下代码惯例来导入必要的库并设置打印样式。
目标受众
我假设您对决策树中使用的术语及其幕后工作原理有基本的了解。在本教程中,将重点介绍模型超参数调整技术,如k-折叠交叉验证。
问题陈述
我们打算在流行的加州住房数据集(Cali _ housing . CSV)中为非线性特征经度和 MedHouseVal (房屋中值)构建一个模型。
让我们看看数据集的前几行。

这个数据集有 20640 个观察值!
可视化数据
为了查看上述两个特征之间的关系,我们使用 seaborn 创建了以下散点图。

正如您在散点图中看到的,这两个特征之间存在复杂的非线性关系。决策树回归是一种强大的模型,能够在数据中发现这种复杂的非线性关系。
建立模型
让我们使用 sci kit-learn decision tree regressor 类来训练我们的模型。
让我们想象一下我们的模型。

我们可以使用 Graphviz 来可视化这个模型的树形图。

作者图片
我们的树中有 4 个叶节点。这是因为我们设置了 max_depth=2 。叶节点的数量相当于 2^max_depth 。超参数 max_depth 控制分支的复杂度。
在 max_depth=2 的这种情况下,模型不太符合训练数据。这就是所谓的 欠配 的问题。
让我们用 max_depth=15 创建一个不同的模型。通过重复相同的步骤,我们可以创建以下模型。

这里我们不能可视化树形图,因为它有 32768 (2 ⁵)个叶节点!在这种情况下,当 max_depth=15 时,模型非常适合训练数据,但是它不能推广到新的输入数据。这就是所谓的 过拟合 的问题。在这里,模型已经适应了数据的噪声,它试图记住数据,而不是学习任何类型的模式。
因此,当我们创建最佳模型时,我们应该避免欠拟合和过拟合的情况。
那么,超参数 max_depth 的最佳值是多少呢?找出 max_depth 的最佳值(不能太小也不能太大)称为 超参数调整 。
决策树回归的超参数调整
主要有两种方法。
- 使用 Scikit-learntrain _ test _ split()函数
- 使用 k 折叠交叉验证
使用 Scikit-learntrain _ test _ split()函数
这是一个实现起来非常简单的方法,但也是一个非常有效的方法。你需要做的就是将原始数据集拆分成两部分,分别叫做 训练集 和 测试集 。我们可以使用 sci kit-learntrain _ test _ split()函数轻松做到这一点。输入是特征矩阵— X 和目标向量— y 。我们通常会保留大约 10%-30%的测试数据。超参数 random_state 接受整数。通过指定这一点,我们可以确保在不同的执行中有相同的分割。
然后我们用 X_train 、 y_train 训练模型,用 X_test 、 y_test 测试模型。这是针对从 1 到 20 范围内的 max_depth 超参数的不同值进行的,并绘制测试误差与训练误差。

在树深度= 7 的点上,测试误差开始增加,尽管训练误差持续减小。从该图中,我们可以确定 max_depth 超参数的最佳值为 7。
使用 k 倍交叉验证
一种更有前途的调整模型超参数的方法是使用 k 折叠交叉验证。通过使用这种方法,您可以同时调整多个超参数。这里,在训练集上进行训练,之后在验证集上进行评估,并且可以在测试集上进行最终评估。然而,将原始数据集划分为 3 个集合(训练、验证和测试)大大减少了训练过程的可用数据。
作为一个解决方案,我们使用一个叫做 k 的过程——折叠交叉验证其中 k 是折叠的次数(通常是 5 或 10)。在k-折叠交叉验证中,
- 我们首先使用 train_test_split() 函数将原始数据集分为训练集和测试集。
- 列车组进一步分为k-折叠数。
- 使用褶皱的k1训练模型,并在剩余的褶皱上进行验证。
- 该过程进行 k 次,并且在每次执行时报告性能测量。然后取平均值。
- 找到参数后,对测试集进行最终评估。
下图说明了 k -fold 交叉验证程序。

图片来自 Scikit-learn 官网
可以使用网格搜索方法和 k 折叠交叉验证来调整超参数。等效的 Scikit-learn 函数是 GridSearchCV 。它为指定的 k 折叠数找到所有超参数组合。
假设您想在 DecisionTreeRegressor 中为以下两个超参数从所有超参数组合中找出最佳组合。
- 最大深度:1–10(10 个不同的值)
- min_samples_split: 10,20,30,40,50 (5 个不同的值)
下图显示了超参数空间。如果取一个点(一个组合), GridSearchCV 函数会搜索该组合,并使用这些值以及 k 折叠交叉验证来训练模型。同样,它搜索所有的组合(这里是 10 x 5= 50!).所以,它执行 50 x k 次!

作者图片

最佳值
现在,我们可以使用这些最佳值创建最佳模型。它避免了过拟合和欠拟合的情况。

决策树的优势
- 不需要特征缩放
- 可用于非线性数据
- 非参数:数据中很少的潜在假设
- 可用于回归和分类
- 易于想象
- 容易理解
决策树的缺点
- 决策树训练在计算上是昂贵的,尤其是当通过 k 折叠交叉验证调整模型超参数时。
- 数据的微小变化会导致决策树结构的巨大变化。
本教程由 鲁克山·普拉莫迪塔数据科学 365 博客作者设计创作。
本教程中使用的技术
- Python (高级编程语言)
- numPy (数字 Python 库)
- 熊猫 (Python 数据分析和操作库)
- matplotlib (Python 数据可视化库)
- seaborn (Python 高级数据可视化库)
- Scikit-learn (Python 机器学习库)
- Jupyter 笔记本(集成开发环境)
本教程中使用的机器学习
- 决策树回归
2020–10–26
在 Amazon SageMaker 中训练 TensorFlow 模型

数据集中的交通标志示例
介绍
Amazon SageMaker 是一个云机器学习平台,允许开发人员在云中创建、训练和部署机器学习模型。我之前用 TensorFlow 2 通过我的板载 CPU 对交通标志进行分类。今天我打算在亚马逊 SageMaker 上做。SageMaker 有几个优点:它为计算能力提供了更多的选择(并行计算的不同 CPU 和 GPU),我可以将模型部署为端点。
整个分析是在 SageMaker 的笔记本上完成的,而训练和预测是通过调用更强大的 GPU 实例来完成的。为了完整起见,本文将包括从预处理到端点部署的整个流程。
探索性分析和预处理
数据集可以从 Kaggle: GTSRB —德国交通标志识别基准下载。
以下是数据集的概述:
- 每个图像的大小略有不同
- 训练示例数= 39209
- 测试示例数量= 12630
- 班级数量= 43
这些图像的大小从 20x20 到 70x70 不等,都有 3 个通道:RGB。
所以我要做的第一件事是将所有图像的大小调整为 32x32x3,并将它们读入 numpy 数组作为训练特征。同时,我创建了另一个 numpy 数组,其中包含每个图像的标签,这些标签来自加载图像的文件夹名称。
我为一个类创建了一个调整大小和标记的函数。
对所有的类重复该过程。为了避免在笔记本断开连接的情况下再次进行预处理,我以 pickle 形式存储了数据。下次我想获得处理过的数据时,我可以简单地从 pickle 加载它们。
测试数据集也是如此。现在,我可以看到训练和测试数据集中的类分布。

训练和测试数据集的类分布
正如我们所看到的,在训练集中,样本的最大和最小数量分别为 2200 和 210 个。相差 10 倍。大约有 60%的课程只有< 1000 个例子。阶级分布不均衡。如果按原样使用,则存在一些示例数量较少的类别可能无法很好分类的风险。这将在后面的章节中详细讨论。
然后,我将训练数据分为训练集和验证集。
最后,我将数据标准化并转换成灰度。
以下是预处理后数据集的摘要。
- 训练示例数= 31367
- 验证示例数量= 7842
- 测试示例数量= 12630
- 图像数据形状= (32,32,1)
- 班级数量= 43
模型构建
我使用卷积神经网络(CNN)作为分类模型。总的来说,与传统的神经网络或其他传统的机器学习模型相比,CNN 在对大型数据集上的图像进行分类方面做得更好。
最大池层用于减少前一层输出的大小,从而使训练更快。脱落层也用于减少过度拟合。
网络架构类似于 LeNet,每层的详细信息如下:
培养
sagemaker.tensorflow.TensorFlow估算器处理定位脚本模式容器、上传脚本到 S3 位置和创建 SageMaker 培训任务。
为了让模型访问数据,我将它们保存为。npy 文件并上传到 s3 bucket。
训练脚本是一个独立的 python 文件。在培训作业期间,SageMaker 将在指定的实例上运行这个脚本。
现在,我可以定义训练作业并训练模型。我使用了一个ml.p2xlarge 实例。10 个历元后,验证精度为 0.979。完成这项工作花费了实例 158 秒。
端点部署和预测
模型已部署。我再次使用了一个ml.p2xlarge 实例。现在我们可以评估模型的性能了。一个度量是整个模型的准确性。
然而,更好的方法是查看每个类的准确性。由于某些类别的示例比其他类别少 10 倍,因此预计模型会偏向那些示例较多的类别。
从下图可以看出,大部分类的准确率> 90%。只有 5 个职业有 90%的准确率< 80%. Going back the class distribution plot, we can see all these 5 classes have no. of examples ~ 200. It is also noted that not all classes with ~ 200 examples has a lower accuracy.

Accuracy per class
Conclusion and Future Improvement
I used the latest TensorFlow framework to train a model for traffic sign classification. The pipeline includes pre-processing, model construction, training, prediction and endpoint deployment. The validation accuracy is 0.979 and testing accuracy is 0.924. Most of the classes have accuracy >而只有 5 个职业有准确率< 80%. The root cause is likely to be the small training size for these classes. Future improvement for this model could include image augmentation.
你可以从 这里 访问笔记本和训练脚本。
用尼莫和闪电用 3 行代码训练对话式人工智能
实践教程
使用 NeMo 和 Lightning 大规模训练最先进的语音识别、NLP 和 TTS 模型

作者图片
NeMo (神经模块)是英伟达的一个强大框架,旨在轻松训练、构建和操纵最先进的对话式人工智能模型。NeMo 模型可以在多 GPU 和多节点上训练,有或没有混合精度,只需 3 行代码。继续阅读,了解如何使用 NeMo 和 Lightning 在多个 GPU 上训练端到端语音识别模型,以及如何根据自己的使用情况扩展 NeMo 模型,如在西班牙语音频数据上微调预训练的强大 ASR 模型。
在本文中,我们将重点介绍 NeMo 中的一些优秀特性,在 [LibriSpeech](http://In this article we covered some of the great out of the box features within NeMo, steps to build your own ASR model on LibriSpeech and fine-tuning to your own datasets across different languages.) 上构建自己的 ASR 模型的步骤,以及如何跨不同语言在自己的数据集上微调模型。
构建 SOTA 对话式人工智能
NeMo 提供了一个轻量级的包装器来开发跨不同领域的模型,特别是 ASR(自动语音识别)、TTS(文本到语音)和 NLP。NeMo 开箱即用,提供从头开始训练流行模型的示例,如谷歌研究所发布的臭名昭著的语音合成 Tactotron2 模型,以及微调预训练变压器模型(如 Megatron-LM )的能力,用于下游 NLP 任务,如文本分类和问题回答。
NeMo 还对各种语音识别模型提供现成的支持,提供预训练的模型以便于部署和微调,或者提供从零开始训练的能力,并且易于修改配置,我们将在下面详细讨论。
它使研究人员能够扩展他们的实验规模,并在模型、数据集和训练程序的现有实现基础上进行构建,而不必担心缩放、模板代码或不必要的工程。
NeMo 建立在 PyTorch 、 PyTorch Lightning 和许多其他开源库的基础上,提供了许多其他突出的功能,例如:
- 使用 ONNX 或 PyTorch TorchScript 导出模型
- 通过tensort进行优化,并使用 NVIDIA Jarvis 进行部署
- 在 NGC 有大量 SOTA 预培训车型
由闪电驱动
NeMo 团队没有从头开始构建对多个 GPU 和多个节点的支持,而是决定使用 PyTorch Lightning 来处理所有的工程细节。每个 NeMo 模型实际上都是一个照明模块。这使得 NeMo 团队可以专注于建立人工智能模型,并允许 NeMo 用户使用闪电训练器,它包括许多加快训练速度的功能。通过与 PyTorch Lightning 的紧密集成,NeMo 保证可以在许多研究环境中运行,并允许研究人员专注于重要的事情。
大规模培训端到端 ASR 模型
为了展示使用 NeMo 和 Lightning 来训练对话式人工智能是多么容易,我们将建立一个可以用来转录语音命令的 end 2 end 语音识别模型。我们将使用 QuartzNet 模型,这是一种用于 E2E(端到端)语音识别的完全卷积架构,它带有预先训练的模型,在大约 3300 小时的音频上训练,在使用更少参数的同时超越了以前的卷积架构。在大规模部署模型时,模型参数的数量成为准确性的关键权衡因素,尤其是在流式语音识别至关重要的在线设置中,如语音助理命令。

NVIDIA 的 QuartzNet BxR 架构
我们使用 LibriSpeech 作为我们的训练数据,这是一本流行的有声读物,标签为 dataset。NeMo 带有许多预设的数据集脚本,用于下载和格式化数据,以进行训练、验证和测试,可以在这里看到。
我们使用预设的 QuartzNet 配置文件定义模型配置,修改我们的数据输入以指向我们的数据集。
训练我们的模型只需要 3 行代码:定义模型配置,初始化闪电训练器,然后训练!
为了提高速度,您可以增加 GPU 的数量,并启用原生混合精度。两者都非常容易使用闪电训练器。
你可以利用所有的 Lightning 特性,比如检查点和实验管理以及更多的!关于 NeMo 中的 ASR 功能的互动视图,请查看 Google Colab。
定制您的模型
NeMo 使得训练技术或模型改变的实验变得极其容易。假设我们想用 Adam 替换我们的优化器,并更新我们的学习率计划以使用预热退火。这两者都可以通过配置文件来完成,而不需要使用预先构建的 NeMo 模块来修改代码。
利用低资源语言的迁移学习
我们已经看到 NVIDIA 在最近的论文中展示的在语音识别中应用迁移学习的令人印象深刻的结果。与从头开始训练相比,微调一个强大的预训练模型在收敛性和准确性方面显示出优势。

NVIDIA从零开始比较训练和微调预训练模型
NeMo 使获得迁移学习的好处变得简单。下面我们使用我们预先训练的英语 QuartzNet 模型,并在普通语音西班牙语数据集上进行微调。我们更新了训练数据输入、词汇表和一些优化配置。我们让教练处理剩下的事情。
从尼莫开始
在本文中,我们介绍了 NeMo 的一些现成特性,在 LibriSpeech 上构建自己的 ASR 模型的步骤,以及跨不同语言对自己的数据集进行微调的步骤。
这里有大量的 Google Colab 教程可供选择,涵盖了 NLP、语音识别和语音合成。你也可以在 PyTorch Lightning docs 这里找到更多信息。
用您自己的语言培训 GPT-2
一步一步的指导,训练你自己的 GPT-2 文本生成模型在你选择的语言从零开始

Jr Korpa 在 Unsplash 上拍摄的照片
我们都知道,随着注意力网络和《变形金刚》的发展,现代自然语言处理(NLP)在过去几年里取得了突飞猛进的进步。它为大量的新算法铺平了道路,为 NLP 的不同任务实现了最先进的(SOTA)。
OpenAI 一直是提供自己的语言模型(现已发布 GPT-3)的领导者之一,该模型是在巨大的互联网数据语料库上训练的。因为,GPT-3 是最近的现象,目前是英语,只能通过 OpenAI 提供的 API 访问,我们将注意力转移到它的早期版本,即 GPT-2 。要了解 GPT-2 的内部螺母和螺栓,我建议你通过这个链接。关于注意力和变形金刚的更多深度,这里有一些很好的链接:
- 杰伊·阿拉玛的《变形金刚》
- 哈佛 NLP 的注释变压器
GPT-2 也发布了英语版本,这使得人们很难用不同的语言生成文本。
那么,为什么不在你最喜欢的文本生成语言上训练你自己的 GPT-2 模型呢?这正是我们要做的。所以,事不宜迟,让我们开始吧。
对于演示,我已经考虑了非拉丁字母脚本(孟加拉语在这里),因为为什么不!!我已经为模型使用了 Huggingface 的实现。
1.收集数据。
所有数据科学家都同意,收集高质量的数据是最重要的阶段之一。因此,我们假设您已经有了一个包含。txt 文件,清除并存储所有数据。为方便起见,您可以使用 Wikipedia 文章数据,它是可用的,可以通过以下代码下载。
python wikipedia_download.py --lang bn
这将创建一个包含所有维基百科文件的文件夹,如下所示:

文件列表截图
2.标记化
现在,第二步是将数据标记化。为此,我们使用下面的类。
关于标记化的一些注释:
- 我们使用 BPE (字节对编码),这是一种子字编码,这通常不会将不同形式的字视为不同。(例如,最大将被视为两个标记:“大”和“est”,这是有利的,因为它保留了大和最大之间的相似性,而“最大”添加了另一个标记“est”,使其不同)。此外,它不像字符级编码那样低级,字符级编码不保留特定单词的任何值。
- 另一个小而微妙的地方是第 13 行代码中的 NFKC (规范化形式兼容性组合)。它是标准的 Unicode 兼容格式之一。如果语言是英语,这没有多大关系,但是因为我们使用孟加拉语,它包含一种不同的字符形式,所以我们使用这种特殊的语言。更多信息请点击链接
因此,我们在这里做的是将我们的数据标记化,并将其保存在一个文件夹中。将在指定目录下创建两个文件( merges.txt 和 vocab.json )。要运行该文件,请使用以下代码:
from tokenise import BPE_token
from pathlib import Path
import os# the folder 'text' contains all the files
paths = [str(x) for x in Path("./text/").glob("**/*.txt")]tokenizer = BPE_token()# train the tokenizer model
tokenizer.bpe_train(paths)# saving the tokenized data in our specified folder
save_path = 'tokenized_data'
tokenizer.save_tokenizer(save_path)
3.模型初始化
在真正的魔法开始之前,我们需要确保炮兵准备好了。让我们从一些初始化开始。
import tensorflow as tf
from transformers import GPT2Config, TFGPT2LMHeadModel, GPT2Tokenizer# loading tokenizer from the saved model path
tokenizer = GPT2Tokenizer.from_pretrained(save_path)tokenizer.add_special_tokens({
"eos_token": "</s>",
"bos_token": "<s>",
"unk_token": "<unk>",
"pad_token": "<pad>",
"mask_token": "<mask>"
})# creating the configurations from which the model can be made
config = GPT2Config(
vocab_size=tokenizer.vocab_size,
bos_token_id=tokenizer.bos_token_id,
eos_token_id=tokenizer.eos_token_id
)# creating the model
model = TFGPT2LMHeadModel(config)
我们还从所有文档中创建一个字符串,并对其进行标记。
single_string = ''
for filename in paths:
with open(filename, "r", encoding='utf-8') as f:
x = f.read() single_string += x + tokenizer.eos_tokenstring_tokenized = tokenizer.encode(single_string)
在我们对整个字符串进行编码后,我们现在继续制作 TensorFlow 数据集,将数据分割成相等的间隔,以便我们的模型可以学习。这里我们使用的块大小为 100(每个示例中的令牌长度),批量大小为 16。这是保持低,否则我们可以在 RTX 2060 GPU 上轻松运行它。
examples = []
block_size = 100
BATCH_SIZE = 12
BUFFER_SIZE = 1000for i in range(0, len(string_tokenized) - block_size + 1, block_size):
examples.append(string_tokenized[i:i + block_size])
inputs, labels = [], []for ex in examples:
inputs.append(ex[:-1])
labels.append(ex[1:])dataset = tf.data.Dataset.from_tensor_slices((inputs, labels))
dataset = dataset.shuffle(BUFFER_SIZE).batch(BATCH_SIZE, drop_remainder=True)
4.模特培训
现在是我们期待已久的部分,制作模型和训练。因此,我们定义了优化器、损失函数和指标,并开始训练。
# defining our optimizer
optimizer = tf.keras.optimizers.Adam(learning_rate=3e-5, epsilon=1e-08, clipnorm=1.0)# definining our loss function
loss = tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True)# defining our metric which we want to observe
metric = tf.keras.metrics.SparseCategoricalAccuracy('accuracy')# compiling the model
model.compile(optimizer=optimizer, loss=[loss, *[None] * model.config.n_layer], metrics=[metric])
现在,让我们训练模型
num_epoch = 10
history = model.fit(dataset, epochs=num_epoch)
5.预言;预测;预告
为了进行预测,我们只需要简单地对输入文本进行编码,并将其传递给模型
text = "লালমোহনবাবু "# encoding the input text
input_ids = tokenizer.encode(text, return_tensors='tf')# getting out output
beam_output = model.generate(
input_ids,
max_length = 50,
num_beams = 5,
temperature = 0.7,
no_repeat_ngram_size=2,
num_return_sequences=5
)

输出的屏幕截图
现在,如果你是一个孟加拉人,那么你可以指出,虽然句子的语法是正确的,但它看起来不连贯。没错,但是对于这个演示,我已经尽可能地简化了这个演示。
6.保存模型
好吧,在长时间的训练之后,如果我们结束了我们的训练,所有我们训练过的模型都丢失了,我们又需要从头开始训练它,这有什么好处呢?因此,让我们保存模型和记号化器,以便我们可以从我们停止的地方重新训练
from transformers import WEIGHTS_NAME, CONFIG_NAME
import osoutput_dir = './model_bn_custom/'# creating directory if it is not present
if not os.path.exists(output_dir):
os.mkdir(output_dir)model_to_save = model.module if hasattr(model, 'module') else model
output_model_file = os.path.join(output_dir, WEIGHTS_NAME)
output_config_file = os.path.join(output_dir, CONFIG_NAME)# save model and model configs
model.save_pretrained(output_dir)
model_to_save.config.to_json_file(output_config_file)# save tokenizer
tokenizer.save_pretrained(output_dir)
奖金
我们已经完成了所有的艰苦工作,所以要加载保存的模型和标记器,我们只需要执行两行代码,一切都准备好了。
tokenizer = GPT2Tokenizer.from_pretrained(output_dir)
model = TFGPT2LMHeadModel.from_pretrained(output_dir)
瞧啊。现在你可以用你自己的语言训练你自己的模型。创造出可以与任何语言的最佳文学作品相媲美的内容。
未来范围:
这个博客给出了如何用任何语言训练 GPT-2 模型的框架。这与现有的一些预训练模型不相上下,但要达到这种状态,我们需要大量的训练数据和计算能力。
参考资料:
[## 如何使用转换器和记号赋予器从零开始训练一个新的语言模型
在过去的几个月里,我们对我们的 transformers 和 tokenizers 库做了一些改进,目标是…
huggingface.co](https://huggingface.co/blog/how-to-train) [## 如何生成文本:使用不同的解码方法通过转换器生成语言
近年来,由于大规模语言生成的兴起,人们对开放式语言生成越来越感兴趣。
huggingface.co](https://huggingface.co/blog/how-to-generate)
培训定制 NER
本文解释了如何使用 spacy 和 python 从训练数据中训练和获取自定义命名的实体。
文章解释了什么是 spacy,spacy 的优点,以及如何使用 spacy 进行命名实体识别。现在,所有的就是训练你的训练数据从文本中识别自定义实体。

spaCy 是什么?
S paCy 是一个用于高级自然语言处理的开源软件库,用编程语言 Python 和 Cython 编写。该库在麻省理工学院的许可下发布,其主要开发者是软件公司 Explosion 的创始人 Matthew Honnibal 和 Ines Montani。
与广泛用于教学和科研的NLTK不同,spaCy 专注于提供生产使用的软件。从 1.0 版本开始,spaCy 还支持 深度学习 工作流,允许连接由流行的 机器学习 库训练的统计模型,如 张量流 ,py torch或MXNet
为什么不是 NLTK?
虽然 NLTK 提供了对许多算法的访问来完成一些事情,但是 spaCy 提供了最好的方法。它提供了迄今为止发布的任何 NLP 库的最快和最准确的语法分析。它还提供了对更大的词向量的访问,这些词向量更容易定制。
自定义-NER

步骤:1 安装说明
点
安装后,您需要下载一个语言模型
康达
第二步:模型训练
完成第一步后,您就可以开始培训了。
→首先,导入定制创建过程所需的必要包。
→现在,主要部分是为输入文本创建您的自定义实体数据,其中命名实体将在测试期间由模型识别。
→定义要处理的训练模型所需的变量。
→接下来,加载流程的空白模型以执行 NER 动作,并使用 create_pipe 功能设置只有 NER 的管道。
→这里,我们想通过禁用除 NER 之外的不必要的流水线来训练识别器。 nlp_update 函数可用于训练识别器。
产出:培训损失
*100%|██████████| 3/3 [00:00<00:00, 32.00it/s]{'ner': 10.165919601917267}100%|██████████| 3/3 [00:00<00:00, 30.38it/s]{'ner': 8.44960543513298}100%|██████████| 3/3 [00:00<00:00, 28.11it/s]{'ner': 7.798196479678154}100%|██████████| 3/3 [00:00<00:00, 33.42it/s]{'ner': 6.569828731939197}100%|██████████| 3/3 [00:00<00:00, 29.20it/s]{'ner': 6.784278305480257}*
→为了测试训练好的模型,
→最后,将模型保存到保存在 output_dir 变量中的路径。
一旦保存了训练好的模型,就可以使用
在 GitHub 上可以找到完整的源代码。
结论
我希望你现在已经明白了如何在斯帕西 NER 模型的基础上训练你自己的 NER 模型。感谢阅读!
培训、验证和测试—成功比较模型性能
虽然建立模型本身就是一项具有挑战性的任务,但是比较它们的性能也是一项同样重要的工作。如果表现好得令人难以置信,那很可能是真的,原因如下。
如果您已经使用了机器学习算法,您可能会遇到像 train_test_split()这样的函数或类似的声音函数,这些函数在 scikit-learn、TensorFlow 或其他库中都可用。训练一个模型是做出好的预测的第一步,然而识别预测能力有多好是一个不同的问题。拆分数据对于建立训练、比较和测试模型的坚实基础是必要的。
我会把这个给你,模型选择据称不是最有趣或最令人兴奋的任务,但它对每个与数据打交道的人来说是必不可少的。
抛出模型和机器学习算法很容易,几乎任何人都可以做到——挑战在于正确地进行分析。
让我们从我们为什么要做这个开始:每个数据集都包含被称为真实以及随机效果的模式。让我们假设我们正在查看某个随机数据集。如果我们创建了一个允许我们预测值的模型,那么这个模型就会暴露于真实的和随机效应。我们不知道的是,哪些数据点是随机的,因此在这个特定的数据集中显示出完全独特的模式,哪些数据点是“典型的”(真实的效果),因此在其他新数据中看起来相似。
让我们反过来说,如果我们有一个只有完美“真实”数据的数据集,我们的模型将很好地拟合,并且在新数据上也表现得同样好,假设真实的影响也存在于任何其他数据集中。
但说真的,这只是假设。数据包含随机模式,我们的模型将适合随机和真实数据——这正是为什么我们需要测量所有模型的准确性。
真实世界的数据集将包含随机和真实的影响,因此不太可能有 100%准确的模型-即使如此,也很可能过度拟合模型训练的数据。此外,新的数据点还将包含(全新的)随机效应,我们的模型不太可能很好地捕捉到这些效应。
我们要回答的问题很简单,“我的模型准确吗”****“我的模型有多准确?”。基本上有两个切入点来回答这个问题,我有一个
- 单一模式或有
- 几种型号
必须对它们进行训练、评估和相互比较。为什么知道我们看的是单一模型还是不同模型很重要?因为根据这个假设,我们需要稍微不同地拆分我们的数据。
一般而言,交叉验证是评估模型性能和/或调整超参数的首选方式,然而,在处理各种模型时,应用不同的(子)数据集并查看模型性能通常非常方便。
如果你对交叉验证(或蒙特卡洛交叉验证)更感兴趣,请点击以下链接:
交叉验证是最先进的。它不是一个有趣的任务,但对每个处理数据的人来说是至关重要的…
towardsdatascience.com](/cross-validation-7c0163460ea0)
数据分割
单一模型案例:为了测试我们的模型的预测准确性,将数据分成训练部分和测试部分似乎是非常直观的,这样模型可以在一个数据集上训练,但在不同的新数据部分上测试。
这个测试的原因很简单,假设我们使用完整的数据集来训练模型,然后使用相同的数据来预测模型的准确性。自然,我们期望模型表现良好,因为所有的数据点都是“已知的”。这正是过度拟合这个术语所指的。我们感兴趣的是,该模型是否也能够处理刚刚介绍的数据——测试数据。
训练单个模型非常简单。我们将数据分成两个数据集:
- 模型拟合的训练数据
- 评估模型准确性的测试数据
简单看一下 R 文档会发现一个将数据分成训练和测试的示例代码——如果我们只测试一个模型,这是正确的做法。如果我们有几个模型要测试,数据应该被分成三部分——但是我稍后会讲到。
# 0.8 is the size of the training data
train_index <- sample(1:nrow(adult), 0.8 * nrow(adult))
test_index <- setdiff(1:nrow(adult), train_index)# Build X_train, y_train, X_test, y_test
X_train <- adult[train_index, -15] # income = column 15
y_train <- adult[train_index, "income"] # income = column 15X_test <- adult[test_index, -15] # income = column 15
y_test <- adult[test_index, "income"] # income = column 15
为了断定我们的训练模型是否具有良好的预测能力,我们简单地使用训练模型并预测对测试数据的响应。这些预测可以用来与真实的响应变量进行比较。以下演示代码说明了我们如何在简单的单模型情况下测量准确性(类似伪代码):
model <- classifier( y_train ~ X_train ) # y explained by X
predictions <- predict( model, X_test )RMSE( pred=predictions, obs=y_test ) # predictions vs. Obs
多模型案例。现在它变得不那么直观了。如果我们创建了几个模型——让我们假设我们改变了一个特定的超参数几次,以查看对模型的影响,或者我们使用了不同的分类模型,如 SVM 和 KNN——这些例子中的每一个都将被视为不同的模型。

坚实模型的关键,拆分你的数据[1]
让我们更具体地假设有 10 个模型,与所有其他模型相比,每个模型的特征在于具有不同的超参数。例如,模型可能在较大的 XYZ 值、较小的λ值等方面有所不同。—你明白我的意思了。
此时,我们在相同的训练数据上训练了 10 个模型。每个模型都有自己的精确度,有些会做得更好,有些会做得更差。现在的问题是,我们如何选择最佳模型,以及我们如何估计模型的“真实”准确性?
第一步:在每种型号中选择合适的型号
由于我们目前看到的 10 个模型只向介绍了训练数据,因此我们需要一个我们的模型以前没有见过的数据部分——验证数据。
我们可以使用验证数据来计算每个模型的准确性指标(每个模型都有不同的 hyper/参数)。这使我们能够按类型和它们的最佳得分对模型进行排序,并选择最佳的 k 个模型,以便稍后测试整体性能。我通常定义 k=2 或 3。
例如,如果我们有 10 个模型,它们有 3 个不同的基础类型(例如 3 个 KNN 模型、4 个购物车模型和 3 个支持向量机模型),我们只需从每个类别中选择最佳模型。
培训和验证到此结束。
第二步:测量你的模型的真实精度
手头有三个模型,我们必须不要假设验证数据上的性能是每个模型的“真实”性能。在这一点上,我们介绍最后一步,伴随着测试数据集。对于 k 个最佳(前 3 名)模型中的每一个,我们使用该模型对测试数据进行分类,并计算测试误差指标。
这听起来像是我们在重新做同样的事情,只是针对三个模型并使用测试集— 但是为什么呢?回忆一下我关于随机和真实效果的简短独白。验证后的最佳模型(就最高精度而言)可能是好的,但也有可能一个模型可能受益于更适合验证数据中的随机模式——随机效应——而这只是偶然。
这不是我们想要衡量我们的模型,我们不想选择一个模型,假设它是更好的,但在现实中,该模型只是由于运气优于。出于这个原因,测试集引入了另一层,将模型受益于纯粹随机性的机会降至最低。
第三步,即最终评估,到此结束。我们只是从三个模型中选出最好的。该模型是拟合的(训练数据),在验证数据上执行前 3 名,并且证明在测试数据上具有最好的准确性。**
如果现在观察到的精度比验证集上的精度低很多,我们该怎么办?
简单的回答是,没什么。坚持模型。我们通过合法的步骤来评估和比较我们所有的模型。如果结果最终显示较低的分数,那么这就是模型在新的和看不见的数据上的表现。不要再次运行此过程,例如使用不同的数据分割,直到您的最终模型做得更好—为什么?在相同的设置下重新应用这个过程来寻找更好的模型,仅仅意味着,您可能找到了一个过度拟合数据的模型,但是在将来的新数据上可能再次表现不佳。****
为了让这个从几个型号中选择最佳型号的过程更加有形,我们用更多的结构和术语来描述这些步骤:
- 训练数据集:训练数据集用于训练模型。该数据集应该是数据集的最大部分。当针对训练数据集进行测试时,模型通常表现良好,这仅仅是因为错误被低估(“向下偏差”)。
- 验证数据集:比较多个模型时需要。验证数据集用于验证模型并从中挑选最佳模型。
- 测试数据集:最终确定所选车型的真实性能。测试误差可能会高于在前面步骤中观察到的误差。这一步让我们在所有的模型中选择一个最终模型。****

模型选择过程
如前所述,我们不知道哪些数据点是随机效应,哪些不是。分割数据的方法有很多种,但一般来说,选择随机数据点是一个好主意——R 的 sample 或 Python 的 random.sample 函数可能非常方便。
这篇文章通过大量模型下的挑战模型选择提供了简短的指导。尽管这个主题肯定不会出现在最令人兴奋的机器学习讲座中,但遵循既定的程序来挑选和验证数据模型是至关重要的。
{下次见}
通过关系推理使用自我监督学习进行无标记数据训练
这个故事的目的是介绍使用表示学习在深度学习中训练未标记数据的新方法

背景和挑战📋
在现代深度学习算法中,对未标记数据的人工标注的依赖是主要限制之一。为了训练一个好的模型,通常我们需要准备大量的标注数据。在只有少量类和数据的情况下,我们可以使用来自标注公共数据集的预训练模型,并使用您的数据微调最后几个图层。然而,在现实生活中,当您的数据相当大时(商店中的产品或人脸,..)并且对于仅具有几个可训练层的模型来说学习将是困难的。此外,未标记的数据(例如,文档文本、互联网上的图像)的数量是不可计数的。将它们全部标记出来几乎是不可能的,但是不利用它们绝对是一种浪费。
在这种情况下,使用新的数据集从头开始再次训练深度模型将是一种选择,但这需要花费大量的时间和精力来标记数据,而使用预训练的深度模型似乎不再有帮助。这就是自我监督学习诞生的原因。这背后的想法很简单,它服务于两个主要任务:
- 代理任务:深度模型将从没有注释的未标记数据中学习可概括的表示,然后将能够利用隐含信息自生成监控信号。
- 下游任务:表示将针对监督学习任务进行微调,例如使用较少数量的标记数据进行分类和图像检索(标记数据的数量取决于基于您需求的模型的性能)
提出了许多不同的训练方法来学习这样的表示:相对位置[1]😗***模型需要理解对象的空间上下文,以告知部件之间的相对位置;拼图[2]😗* 模型需要将 9 个洗牌后的小块放回原来的位置;彩色化[3]: 该模型已经被训练为对灰度输入图像进行彩色化;准确地说,任务是将该图像映射到量化颜色值输出上的分布;计数特征【4】:该模型通过缩放 和平铺,利用输入图像的特征计数关系学习特征编码器; SimCLR [5]: 该模型通过潜在空间中的对比损失,最大化同一样本的不同增强视图之间的一致性,来学习视觉输入的表示。**
然而,我想介绍一种有趣的方法,它能够像人一样识别事物。人类学习的关键因素是通过比较相关和不同的实体来获取新知识。因此,如果我们可以通过关系推理方法[6]在自我监督的机器学习中应用类似的机制,这将是一个重要的解决方案。****
关系推理范式基于一个关键的设计原则:使用关系网络作为无标签数据集上的可学习函数,以量化同一对象的视图之间的关系(内部推理)和不同场景中不同对象之间的关系(内部推理)。通过在标准数据集(CIFAR-10、CIFAR-100、CIFAR-100–20、STL-10、tiny-ImageNet、SlimageNet)、学习时间表和主干网(浅层和深层)上的性能,评估了通过关系推理在自我监督机器学习中利用类似机制的可能性。结果表明关系推理方法在所有条件下都大大超过了最好的竞争对手,平均准确率为 14%,比最新的最先进方法高出 3%,如本文【6】所示。****
技术亮点📄

关系定义示例(图片由作者提供)
最简单的解释,关系推理只是一种方法论,试图帮助学习者理解不同对象(思想)之间的关系,而不是单独学习对象。这可以帮助学习者根据他们的差异轻松区分和记忆对象。关系推理系统[6]有两个主要组成部分:主干 结构和关系头。关系头用于托词任务阶段,用于支持底层神经网络主干学习未标记数据集中的有用表示,然后它将被丢弃。主干结构在托词任务中训练后用于下游任务,如分类或图像检索。
- 前期工作:关注场景内关系,意思是
同一物体中的所有元素都属于同一个场景(比如一个篮子里的球);标签数据集上的训练,主要目标是关系头[7]。**** - 新方法:关注同一对象不同视角之间的关系(内推理)和不同场景不同对象之间的关系(间推理);在未标记的数据和关系头上使用关系推理是在底层主干中学习有用表示的借口任务。****
让我们讨论一下关系推理系统中某个部分的重点:
- 小批量增加:
如前所述,本系统引入了内推理和间推理?那么我们为什么需要它们呢?当没有给出标签时,不可能创建相似和不相似的对象对。为了解决这个问题,应用了自举技术,形成了内推理和间推理,其中 :
- 内部推理包括对同一对象的随机增强进行采样{ A1A2 }(积极的一对)(例如,对同一篮球的不同看法)
- 交互推理包括耦合两个随机对象{ A1B1}(负对)(如随机球篮球)
此外,随机增强函数(例如几何变换、颜色失真)的使用也被认为使场景间的推理更加复杂。这些增强功能的好处迫使学习者(骨干)注意更广泛的一组特征(例如,颜色、大小、纹理等)之间的相关性。).例如,在{脚球,篮球}对中,颜色本身就是该类别的一个强有力的预测因素。然而,随着颜色以及形状大小的随机变化,学习者现在很难区分这对之间的差异。学习者必须看一看另一个特征,因此,它导致更好的表示。
2。度量学习
度量学习的目的是使用距离度量来拉近相似输入(肯定)的表示,同时移开不同输入(否定)的表示。然而,在关系推理中,度量学习有着根本的不同:

度量学习和关系推理的比较

学习指标—对比损失(图片由作者提供)

学习指标—关系得分(作者图片)
3。损失函数
学习的目标是呈现对上的二元分类问题。因此,我们可以使用二元交叉熵损失来最大化伯努利对数似然,其中关系分数 y 表示通过 sigmoid 激活函数诱导的表示成员的概率估计。

损失函数
最后,本文[6]还提供了在标准数据集(CIFAR-10,CIFAR-100,CIFAR-100–20,STL-10,tiny-ImageNet,SlimageNet),不同主干(浅层和深层),相同学习时间表(epochs)上的关系推理结果。结果如下,要了解更多信息,你可以看看他的论文。
实验评估📊
在本文中,我想在公共图像数据集 STL-10 上重现关系推理系统。该数据集包括 10 个类(飞机、鸟、汽车、猫、鹿、狗、马、猴子、船、卡车),颜色为 96x96 像素。
首先,我们需要导入一些重要的库
STL-10 数据集由 1300 个标记图像组成(500 个用于训练,800 个用于测试)。然而,它还包括 100000 张未标记的图片,这些图片来自类似但更广泛的图片分布。例如,它包含其他类型的动物(熊、兔子等。)和交通工具(火车、公共汽车等。)除了标记集中的那些

STL-10 数据集
然后我们将根据作者的建议创建关系推理类
为了比较关系推理方法在浅层和深层模型上的性能,我们将创建一个浅层模型(Conv4)并使用深层模型的结构(Resnet34)。
**backbone = Conv4() *# shallow model*
backbone = models.resnet34(pretrained = False) *# deep model***
根据作者的建议,设置了一些超参数和增强策略。我们将在未标记的 STL-10 数据集上用关系头训练我们的主干。

每批图像样本(K =16)
到目前为止,我们已经创建了训练模型所需的一切。现在,我们将在 10 个时期和 16 个增强图像(K)中训练主干和关系头部模型,通过 1 个 GPU Tesla P100-PCIE-16GB,浅层模型(Conv4)花费了 4 个小时,深层模型(Resnet34)花费了 6 个小时(您可以自由更改时期的数量以及另一个超参数以获得更好的结果)
**device = torch.device("cuda:0") if torch.cuda.is_available() else torch.device("cpu")backbone.to(device)
model = RelationalReasoning(backbone, feature_size)
model.train(tot_epochs=tot_epochs, train_loader=train_loader)
torch.save(model.backbone.state_dict(), 'model.tar')**
在训练我们的主干模型之后,我们丢弃关系头,并且仅将主干用于下游任务。我们需要用 STL-10 (500 张图片)中的标记数据来微调我们的主干,并在测试集中测试最终的模型(800 张图片)。训练和测试数据集将在没有扩充的情况下加载到 Dataloader 中。
我们将加载预训练的主干模型,并使用简单的线性模型将输出要素与数据集中的多个类连接起来。
***# linear model*
linear_layer = torch.nn.Linear(64, 10) # if backbone is Conv4
linear_layer = torch.nn.Linear(1000, 10) # if backbone is Resnet34*# defining a raw backbone model*
backbone_lineval = Conv4() *# Conv4* backbone_lineval = models.resnet34(pretrained = False) *# Resnet34*# load model
checkpoint = torch.load('model.tar') # name of pretrain weight
backbone_lineval.load_state_dict(checkpoint)**
此时,只有线性模型将被训练,主干模型将被冻结。首先,我们将看到微调 Conv4 的结果
然后在测试集上检查
Conv4 在测试集上获得了 49.98%的准确率,这意味着主干模型可以在未标记的数据集中学习有用的特征,我们只需要用很少的时期进行微调就可以获得很好的结果。现在让我们检查深度模型的性能。
然后在测试数据集上进行评估
在测试集上,我们可以获得 55.38%的准确率。在本文中,主要目标是再现和评估关系推理方法,以教导模型在没有标签的情况下区分对象,因此,这些结果是非常有希望的。如果你觉得不满意,你可以通过改变超参数如增强数、历元数或模型结构来自由地做实验。
最后的想法📕
自监督关系推理在定量和定性方式上都是有效的,并且具有从浅到深结构的不同大小的主干。通过比较学习的表征可以很容易地从一个领域转移到另一个领域,它们是细粒度的和紧凑的,这可能是由于准确性和增强数量之间的相关性。根据作者的实验[4],在关系推理中,扩充的数量在影响对象群的质量方面起着主要作用。自监督学习在许多方面具有成为机器学习的未来的强大潜力。
如果你想进一步讨论,可以联系我。这是我的 Linkedin
尽情享受吧!!!👦🏻
参考
[1]卡尔·多施等人。al,通过上下文预测的无监督视觉表征学习,2015。
[2]迈赫迪·诺鲁齐等人。al,通过解决拼图游戏实现视觉表征的无监督学习,2017。
[3]张等。al,彩色图像彩色化,2016。
[4]迈赫迪·诺鲁齐等人。al,通过学习计数进行表征学习,2017。
[5]陈婷等人视觉表征对比学习的简单框架,2020。
6 马西米利亚诺·帕塔基奥拉等人。al,
表征学习的自监督关系推理,2020。
7 亚当·桑托罗等人。al,关系递归神经网络,2018。
使用 cuML 训练机器学习模型的速度提高 150 倍
显卡的三倍内存将训练速度提高了 10 倍!
动机
Sklearn 是一个很棒的库,有各种各样的机器学习模型,可以用来训练数据。但是,如果您的数据很大,可能需要很长时间来训练您的数据,尤其是当您尝试不同的超参数以找到最佳模型时。
有没有一种方法可以让你训练机器学习模型的速度比用 Sklearn 快 150 倍,而且改动最小?是的,你可以用 cuML。
下面是对比使用 Sklearn 的RandomForestClassifier和 cuML 的RandomForestClassifier.训练同一个模型所需时间的图表

cuML 是一套快速、GPU 加速的机器学习算法,专为数据科学和分析任务而设计。它的 API 和 Sklearn 的差不多。这意味着您可以使用您用来训练 Sklearn 模型的相同代码来训练 cuML 的模型。
在本文中,我将使用不同的模型来比较这两个库的性能。我还将展示如何增加显卡的内存来提高 10 倍的速度。
安装累计
要安装 cuML,请按照第页的说明进行安装。确保在安装库之前检查先决条件。你可以安装所有的软件包,也可以只安装 cuML。如果电脑空间有限,我推荐安装 cuDF 和 cuML。
虽然在很多情况下,你不需要安装 cuDF 来使用 cuML,但是 cuDF 是 cuML 的一个很好的补充,因为它是一个 GPU 数据框架。

确保选择适合您电脑的选项。
创建数据
由于当有大量数据时,cuML 通常比 Sklearn 工作得更好,我们将使用sklearn.datasets.创建一个有 40000 行数据的数据集
from sklearn import datasets
X, y = datasets.make_classification(n_samples=40000)
将数据类型转换为np.float32,因为一些 cuML 模型要求输入为np.float32.
X = X.astype(np.float32)
y = y.astype(np.float32)
支持向量机
我们将创建函数来训练模型。使用该功能将使我们更容易比较不同的模型。
def train_data(model, X=X, y=y):
clf = model
clf.fit(X, y)
我们使用 iPython 的神奇命令%timeit将每个函数运行 7 次,然后取所有实验的平均值。
Average time of sklearn's SVC 48.56009825014287 s
Average time of cuml's SVC 19.611496431714304 s
Ratio between sklearn and cuml is 2.476103668030909
cuML 的 SVC 比 sklearn 的 SVC 快 2.5 倍!
我们通过剧情来形象化一下吧。我们创建一个函数来绘制模型的速度。我使用 cutecharts 是因为它让条形图更可爱。

更好的显卡
由于 cuML 的模型在运行大数据时比 Sklearn 的模型更快,因为它们是使用 GPU 训练的,如果我们将 GPU 的内存增加两倍会发生什么?
我使用的是外星人 M15 笔记本电脑,配有 NVIDIA GeForce 2060 和 6.3 GB 的显卡内存,用于之前的比较。
现在我将使用一台配备 NVIDIA Quadro RTX 5000 和 17 GB 显卡内存的 Dell Precision 7740 来测试 GPU 内存增加时的速度。
Average time of sklearn's SVC 35.791008955999914 s
Average time of cuml's SVC 1.9953700327142931 s
Ratio between sklearn and cuml is 17.93702840535976

哇!cuML 的 SVM 在显卡内存 17 GB 的机器上训练时比 Sklearn 的 SVM 快 18 倍!而且比在显卡内存 6.3 GB 的笔记本电脑上训练的速度还要快 10 倍。
这就是为什么如果我们使用像 cuML 这样的 GPU 加速库,我们的机器中最好有一个好的 GPU。
随机森林分类器
Average time of sklearn's RandomForestClassifier 29.824075075857113 s
Average time of cuml's RandomForestClassifier 0.49404465585715635 s
Ratio between sklearn and cuml is 60.3671646323408

cuML 的 RandomForestClassifier 比 Sklearn 的 RandomForestClassifier 快 60 倍!如果训练一只 Sklearn 的 RandomForestClassifier 需要 30 秒,那么训练一只 cuML 的 RandomForestClassifier 只需要不到半秒。多疯狂啊。
更好的显卡
Average time of Sklearn's RandomForestClassifier 24.006061030143037 s
Average time of cuML's RandomForestClassifier 0.15141178591425808 s.
The ratio between Sklearn’s and cuML is 158.54816641379068

在我的 Dell Precision 7740 笔记本电脑上训练时,cuML 的 RandomForestClassifier 比 Sklearn 的 RandomForestClassifier 快 158 倍!
最近邻分类器
Average time of sklearn's KNeighborsClassifier 0.07836367340000508 s
Average time of cuml's KNeighborsClassifier 0.004251259535714585 s
Ratio between sklearn and cuml is 18.43304854518441

注意:y 轴上的 20m 表示 20ms。
cuML 的 KNeighborsClassifier 比 Sklearn 的 KNeighborsClassifier 快 18 倍。
更好的显卡
sklearn 的 KNeighborsClassifier 的平均时间为 0.075111903228545 秒
cuml 的 KNeighborsClassifier 的平均时间为 0.000151379921

在我的 Dell Precision 7740 笔记本电脑上进行训练时,cuML 的 KNeighborsClassifier 比 Sklearn 的 KNeighborsClassifier 快 50 倍。
总结
你可以在 这里找到 其余比较的代码。****
下面是两个表格,总结了上两个库之间不同型号的速度:
-
外星人 M15 — GeForce 2060 和 6.3 GB 的显卡内存
-
Dell Precision 7740-Quadro RTX 5000 和 17 GB 的显卡内存
令人印象深刻,不是吗?
结论
恭喜你!您刚刚了解到,与 Sklearn 相比,在 cuML 上训练不同模型的速度有多快。如果使用 Sklearn 训练你的模型需要很长时间,我强烈建议尝试一下 cuML,考虑到与 Sklearn 的 API 相比,代码几乎没有任何变化。
当然,如果有一个像 cuML 这样使用 GPU 来执行代码的库,你的显卡越好,平均来说训练就越快。
查看 cuML 的文档,了解其他机器学习模型的详细信息。
本文的源代码可以在这里找到。
此时您不能执行该操作。您已使用另一个标签页或窗口登录。您已在另一个选项卡中注销,或者…
github.com](https://github.com/khuyentran1401/Data-science/tree/master/machine-learning/cuml)
我喜欢写一些基本的数据科学概念,并尝试不同的算法和数据科学工具。你可以在 LinkedIn 和 Twitter 上和我联系。
星这个回购如果你想检查我写的所有文章的代码。在 Medium 上关注我,了解我的最新数据科学文章,例如:
[## Yellowbrick 简介:可视化机器学习预测的 Python 库…
您将 f1 分数提高到了 98%!但这是否意味着你的模型表现更好呢?
towardsdatascience.com](/introduction-to-yellowbrick-a-python-library-to-explain-the-prediction-of-your-machine-learning-d63ecee10ecc) [## PyTorch 是什么?
想想 Numpy,但是有强大的 GPU 加速
towardsdatascience.com](/what-is-pytorch-a84e4559f0e3) [## 使用 Python 和情感分析探索和可视化您的 LinkedIn 网络
希望优化您的 LinkedIn 个人资料?为什么不让数据为你服务呢?
towardsdatascience.com](/sentiment-analysis-of-linkedin-messages-3bb152307f84) [## 如何用支持向量机学习非线性数据集
支持向量机简介及其在非线性数据集上的应用
towardsdatascience.com](/how-to-learn-non-linear-separable-dataset-with-support-vector-machines-a7da21c6d987)
训练你的思维,用 5 个步骤进行递归思考
如何轻松解决递归问题?

在您的编程之旅中,有几个里程碑是您需要克服的。例如,熟悉类,理解指针,掌握代码函数化的艺术,等等。对于新手来说,最难学的编程概念之一是递归,对于已经编程一段时间的人来说,这是需要掌握的。
当我第一次开始编码的时候——大约十年前——我很难理解递归。虽然有些人可以自然地递归思考,但其他人——包括我自己——却不能。
但是,
递归思维是你可以训练你的大脑去做和掌握的事情,即使你天生没有这种能力。
回到 2012 年,我看到一本非常酷的书,叫做《像程序员一样思考》。这本书教会我开发一个思维过程,帮助我解决多年来的各种编程问题。它给了我一个程序员在职业生涯中取得成功所需的正确思维过程的基础。
这些年来,我扩展了书中提出的那些技术,以便熟悉各种编程概念。
在本文中,我将向您介绍我用来解决任何递归问题的不同步骤。虽然我将使用 Python 来演示这些步骤,但是这些步骤可以应用于任何编程语言。他们的逻辑不是特定于语言的。
让我们—让我们,让我们,让我们—开始吧!
训练你递归思考的步骤
为了演示这个过程,我们来看一个简单的问题。假设我们需要对任何给定数字的位数求和。例如,123 的和是 6,for 57190 是 22,以此类推。
所以,我需要写一个代码,用递归来解决这个问题。在你说之前,我知道有其他方法可以解决这个问题——可以说是更好更容易的递归方法。但是,为了这篇文章,让我们考虑递归解决方案。
1-首先使用 iterable 方法。
这一步对大多数人来说相对简单。我们有一个数字,想用循环对它的数字求和。基本上,我们需要知道如何计算数字中有多少位数,然后逐一求和。
一种方法是将包含数字的变量转换为 str,这样更容易迭代。
num = 123 #The variable containing the number we want to sum its digits
num = str(num) #Casting the number to a str
s = 0 #Initialize a varible to accumulate the sum
#Loop over the digits and add them up
for i in range(len(num)):
s += int(num[i])
2-提取函数的参数。
使用循环解决问题将有助于我们理解答案,并找出它的所有非递归方面。现在我们有了答案,我们可以剖析它,提取我们需要继续前进的信息。
此时,我们需要问自己一个问题,如果我把它写成一个函数形式,函数参数会是什么?
这个问题的答案取决于你使用的编程语言。对于 Python,我们可以说只需要一个参数,就是变量num。
def sumOfDigitsIter(num):
num = str(num)
s = 0
for i in range(len(num)):
s += int(num[i])
return s
3-扣除最小的问题实例。
一旦我们得到了函数参数,我们将对它们进行更深入的研究。在我们的例子中,我们只有一个参数 num。
我们现在需要做的是根据参数找到最小的问题实例。回想一下,我们的目的是对一个给定数字的位数求和。这个问题最简单的例子是如果数字只有一个数字。
例如,如果num = 3,那么答案将等于变量本身,因为没有更多的数字相加。
4-将解决方案添加到最小问题实例中。
让我们再来看看我们刚刚写的函数:
def sumOfDigitsIter(num):
num = str(num)
s = 0
for i in range(len(num)):
s += int(num[i])
return s
按照这个函数的逻辑,如果参数 num 只有一个数字,那么仍然会执行循环。为了避免这种情况,我们可以添加一个条件语句,该语句只在输入数字的位数为 1 时执行。
def SumOfDigitsIter(num):
num = str(num)
if len(num) == 1:
return num
else:
s = 0
for i in range(len(num)):
s += int(num[i])
return s
5-扩展你的功能。
这是递归发生的步骤。到目前为止,我们并不太关心递归;我们在合乎逻辑地解决这个问题。这是从递归开始,从逻辑上考虑,然后转化为递归解决方案的最佳方法。
现在,让我们考虑一下函数的else部分。
else:
s = 0
for i in range(len(num)):
s += int(num[i])
return s
你可以把递归想象成展开一个问题实例,然后再次滚动它。我们可以从另一个角度来看这个问题,我们可以说 123 的和是 1+23 的和,23 的和是 2 +和 3——也就是 3。
因此,回滚到 123 = 1 +2 +3 =6。
让我们把它转换成代码。我们已经写好了最后一部分,它处理一位数。我们需要做的是将 num 个变量分解成一位数。
这就是递归。
def SumOfDigits2(num):
num = str(num)
if len(num) == 1:
return num
else:
return num[0] + sumOfDigits(num[1:])
所以,每次,我都调用相同的函数,但是输入更少。一旦到了最后一位数,我最终会得到 1 + 2 + 3 这样的数,最终加起来就是 6。
瞧,我得到了一个递归函数。
另一个例子
让我们考虑另一个例子,假设我们想要得到两个相同长度的整数列表的绝对差的和。例如,如果我们有[15,-4,56,10,-23]和[14,-9,56,14,-23],答案将是 10。
第一步:用循环求解
assert len(arr1) == len(arr2)
diffSum = 0
for i in range(len(arr1)):
diffSum += abs(arr1[i]-arr2[i])
第二步:提取参数
参数表= [arr1,arr2]。
第三步:扣除最小实例
这个问题的最小实例是当两个列表为空时;在这种情况下,没有区别,所以答案是 0。
第四步:求解最小实例。
if len(arr1) == 0:
return 0
现在你可能会说,为什么不确保 len(arr2) == 0 呢?assert 语句已经确保了两个列表具有相同的长度,这意味着如果我想检查它们是否为空,我只需要检查其中一个的长度。
第五步:递归
就像 digits sum 示例一样,如果我想对两个列表之间的绝对差求和,我可以得到前两个元素之间的差,然后是第二个,然后是第三个,依此类推,直到我用完所有的元素。
def recDiff(arr1,arr2):
assert len(arr1) == len(arr2)
if len(arr1) == 0:
return 0
else:
return abs(arr1[0]-arr2[0]) + recDiff(arr1[1:],arr2[1:])
外卖食品
递归被认为是程序员在成为“优秀程序员”的过程中需要经历的高级技术之一然而,一开始把一个人的脑袋包起来是相当棘手的,许多人发现很难掌握。
遵循简单明了的五个步骤,你可以轻松解决任何递归问题:
- 首先使用循环解决问题。
- 从中提取可能的输入,如果你想把它变成一个函数的话。
- 演绎问题最简单的版本。
- 写一个函数来解决这个问题的最简单的例子。
- 使用该函数编写一个新的递归函数。
虽然我提出了 5 个步骤,但是你不需要明确地经历所有的 5 个步骤,因为你会更加精确。随着时间的推移和练习,你将能够在头脑中执行这些步骤,而不用明确地写下来。
我把这个留给你,递归是一个需要掌握的具有挑战性的编程概念。所以,只要坚持下去,有耐心,多多练习;这是你真正掌握诀窍的唯一方法。
训练 GAN 从正态分布中采样
可视化生成性对抗网络的基础
在最初的生成对抗网络论文中,Ian Goodfellow 描述了一个简单的 GAN,当经过训练时,它能够生成与从正态分布中采样的样本不可区分的样本。此过程如下所示:

图 1:GAN 论文中发表的第一张图,展示了 GAN 如何将均匀噪声映射到正态分布。黑点是真实的数据点,绿色曲线是 GAN 产生的分布,蓝色曲线是鉴别器对该区域中的样本是否真实的置信度。这里 x 表示样本空间, z 表示潜在空间。(来源: Goodfellow 2014 )
此任务最简单的解决方案是让 GAN 逼近正态分布的逆 CDF 、x=φ⁻(z)。这是对 GANs 的直观而吸引人的看法:它们是在给定一组样本的情况下从未知分布中随机抽样的工具。在上面的例子中,这是正态分布。其他包括所有手写数字的分布和所有人脸的分布。描述人脸的累积分布函数已被证明是一项艰巨的任务,这就是为什么 GANs 及其生成照片级人脸图像的能力(以及其他许多令人兴奋的结果)成为如此热门的话题。
不幸的是,人们很容易陷入狂热,一头扎进复杂的 GAN 应用;大多数人从 MNIST 数据集、开始他们的 GAN 之旅,尽管其样本空间为 784 维。我以前探索过将 GANS 应用于二维问题,但发现即使这样也太复杂了,无法恰当地形象化近似分布。因此,我决定用《甘正典》中发表的第一幅图来说明这个问题。
问题是
我们将训练 GAN 从标准正态分布 N(0,1)中抽取样本。因此,样本 x 位于从 -∞ 到+∞的一维样本空间中。作为随机性的来源,GAN 将被赋予从均匀分布 U(-1,1)中提取的值。因此,值 z 位于范围从-1 到 1 的一维潜在空间中。因此,GAN 应该接近g(z)=φ⁻(f(z)),使得 f(z) 具有 U(0,1)分布。 f 的可能实现包括:

图 2:保持一致性的[-1,1]到[0,1]的可能映射
这些映射中的一个显然是“最佳”的,因为它简单且可解释。将逆 CDF 应用于这三个函数会产生以下结果:

图 3:图 2 中描述的三个函数的 x =φ⁻(f(z))
所有这三种映射都将 z~U(-1,1) 转换为 x~N(0,1) ,但是蓝色曲线是迄今为止最简单和最容易解释的。不幸的是,众所周知,神经网络不关心这些有价值的特征。
解决方案
一个简单的、完全连接的 GAN 被训练 600 个训练步骤(代码如下)。在每一步之后,发生器和鉴别器的测量结果都是可视化的,如下所示:

图 4:GAN 学习从 600 个时期的标准正态分布采样的动画。(左上)发生器和鉴别器的精度和损耗(右上)GAN 的观测概率密度和真实 N(0,1)密度(左下)潜在空间到样本空间的 GAN 映射和真实映射φ⁻((z+1)/2)(右下)鉴别器对样本空间的该区域中的值为真实值的置信度
左上角:指标
生成器和鉴别器都使用二进制交叉熵损失(或对数损失),这在这里与精度一起显示。在步骤 200 之后,两个网络在剩余的训练中基本上保持同步。众所周知,发电机损耗与 GAN 输出质量无关,但它仍然是一个有用的跟踪指标;如果发生器或鉴别器明显优于另一个,那么 GAN 将可能无法收敛。
右上角:概率密度
右上面板显示 GAN 的观测概率密度与标准正态分布的真实概率密度。理想情况下,当训练结束时,这两个人会准确地排成一行。正如你所看到的,有一个强烈的波动行为,因为甘反复过校正。
左下方:已学习的映射
左下方的面板显示了本实验中使用的真实映射,以及 GAN 学习到的映射。正如你所看到的,GAN 正在逼近非直观映射φ⁻((1-z)/2),并取得了成功。
右下角:鉴别器置信度
此面板显示了鉴别器对样本空间中该点的值为实数的置信度(即,从实数分布中提取,而不是 GAN)。从动画中可以立即看出两个观点。首先,鉴别器在识别整个样本空间中真实样本和虚假样本的相对频率方面相当有效;当真实样本多于虚假样本时,可信度增加,反之亦然。第二个观察是样本空间的极端,真实样本很少,显示出巨大的信心波动。这反映在右上图中,在该图中,极端区域中置信度的增加实际上促进了该区域中的样品生成(因此产生了波动行为)。
结果:
GAN 总共被训练了 30000 步,并学会产生以下功能:

图 5:30000 步训练后的 GAN
粗略地看一下 GAN 的输出,它确实呈现正态分布。然而,通过应用核密度估计(图 5,右上方),我们看到该分布只是一个粗略的近似值。这个缺点在 1 维设置中是明显的,在 1 维设置中,分布可以容易地可视化,但是识别“真实的”面部生成函数和由 GAN 学习的面部生成函数之间的偏差是一个困难得多的问题;事实上,在评估生成图像的 GANs 时,我们经常看到它们生成各种各样的真实样本,并认为这就足够了。确保 GAN 输出和鉴频器的中间激活具有与真实样本相似的统计特性是一个活跃的研究领域。
左下角的面板显示 GAN 已经学习了从潜在空间到样本空间的非直觉映射,这是在更高维度问题中呈指数增长的问题。这是不幸的,原因有二:首先,理想的 GAN 的输入应该与有用的、人类可理解的特征相关联。在这种情况下,这将是“增加潜在价值增加样本价值”,而在实践中,我们发现相反的情况发生。在生成人脸的情况下,可能是“增加这个潜在特征增加鼻子尺寸”或者“增加那个潜在特征增加头发亮度”。相反,典型地,潜在特征被映射到不止一个样本特征,例如鼻子尺寸和头发颜色,或者一个样本特征被映射到多个潜在特征。这使得将 GANs 应用于创造性任务变得困难。
另一个问题是,GAN 可以学习将潜在空间中的两个附近的点,比如说 z=0.49 和 z=0.51 ,映射到样本空间中非常远的点(如图 3 中分段函数的情况)。这使得训练变得复杂,因为中间值 z=0.50 ,将可能被映射到样本空间中的某个非常不现实的中间点。这在我以前关于同一主题的文章中已经观察到,并将在下一篇文章中进一步探讨。
所以你有它;甘的训练从来都不简单,即使是一维问题。作为结束语,我想重申我最喜欢的一条数据科学建议:将一切可视化,不管你认为自己了解多少。毕竟,科学上最激动人心的发现并不是来自“发现了!”而是“嗯,这太奇怪了…”
代码可从以下 github repo 获得:
可视化 GAN 训练,因为它学习将 U(-1,1)映射到 N(0,1)
github.com](https://github.com/ConorLazarou/medium/blob/master/12020/visualizing_gan_uni2norm/1_uniform_to_normal_1D.py)
在谷歌的人工智能平台上训练一个模型

尼古拉·塔拉先科在 Unsplash 上的照片
谷歌 ML 教程
如何使用谷歌云服务在 Python 中训练模型
欢迎阅读本系列关于在 Google 云平台上进行机器学习的第一篇文章!
我们将看看人工智能平台。它是与机器学习严格相关的工具的子集,其中包括:
- AI 平台训练,用于在云上训练/调优模型
- AI 平台预测,在云上托管训练好的模型
- 人工智能管道,使用 Kubernetes 和 Docker 图像创建一步一步的流程
和许多其他人。
免责声明:我与谷歌没有任何关系,我只是决定
写这些文章来分享我在日常工作中使用这些工具获得的知识。
对于这第一篇文章,我将重点关注 AI 平台培训,这是一个使用定制代码和可定制机器在云上运行
培训任务的产品。我认为使用人工智能平台训练你的模型的主要优点是:
- 你可以使用更强大的资源(比如多个内核或者一个 GPU)而没有很多麻烦去实例化它们
- 你可以与你的团队共享代码,然后使用一个通用的云基础设施复制相同的结果
在本教程中,我们将使用训练的定义编写实际的 Python 应用,运行本地测试,并在云平台上执行训练作业。
对于本教程,您需要
- 一个活跃的谷歌云平台账户(你可以访问主页建立一个新账户)和一个 GCP 项目
- 安装在工作站上的 Python 3、 gcloud 和 gsutil
- 用于教程的数据集,UCI 的 银行营销数据集 。在这里
你可以找到关于数据集的文档。我们将使用完整版本。
第一步:将数据存储在谷歌存储器上
在本地机器上下载数据集之后,转到 GCP 项目的 Google 存储控制台。创建一个新的 bucket(我称之为bank-marketing-model)并在其中加载数据集。现在,您的桶应该看起来像这样:

作者图片
或者,以一种更古怪的方式,您可以从命令行使用gsutil。进入包含数据集的本地目录并运行
gsutil mb gs://bank-marketing-model
gsutil cp ./bank-additional-full.csv gs://bank-marketing-model
gsutil mb创造了水桶gsutil cp将文件从本地路径复制到 GCS 存储桶
步骤 2:编写 Python 培训应用程序
如果你正在阅读这篇文章,很可能你已经知道如何编写一个端到端的 Python 程序来训练一个机器学习模型。无论如何,因为我们计划在云上训练模型,有一些步骤在某种程度上不同于通常的 Kaggle-ish 代码。
首先要记住的是,你只能使用 scikit-learn、XGBoost 或
Tensorflow 来训练你的模型(Pytorch 处于测试阶段,有一种方法可以使用定制的 Python 环境,但我们会在另一篇文章中看到)。
因此,Python 应用程序的基础是:
- 从存储中下载数据集
subprocess.call([
'gsutil', 'cp',
# Storage path
os.path.join('gs://', STORAGE_BUCKET, DATA_PATH),
# Local path
os.path.join(LOCAL_PATH, 'dataset.csv')
])df = pd.read_csv(os.path.join(LOCAL_PATH, 'dataset.csv'), sep=';')
- 做一些数据准备(分割训练测试、缺失插补等)并创建管道
train, test = train_test_split(df, test_size=args.test_size,
random_state=42)...pipeline = Pipeline([
# The ColumnTransformer divide the preprocessing process between
# categorical and numerical data
('data_prep',
ColumnTransformer([
('num_prep', StandardScaler(), num_features),
('cat_prep', OneHotEncoder(), cat_features)
])),
# ML model
('model',
RandomForestClassifier(
random_state=42,
n_jobs=args.n_jobs,
n_estimators=args.n_estimators,
max_depth=args.max_depth,
max_features=args.max_features if args.max_features is not
None else 'sqrt',
min_samples_split=args.min_samples_split,
class_weight='balanced',
max_samples=args.max_samples
))
])
- 训练模型
pipeline.fit(train, y_train)
- 获取一些性能指标
results = pd.DataFrame(
{'accuracy': [accuracy_score(y_train, pred_train),
accuracy_score(y_test, pred_test)],
'precision': [precision_score(y_train, pred_train, pos_label='yes'),
precision_score(y_test, pred_test, pos_label='yes')],
'recall': [recall_score(y_train, pred_train, pos_label='yes'),
recall_score(y_test, pred_test, pos_label='yes')],
'f1': [f1_score(y_train, pred_train, pos_label='yes'),
f1_score(y_test, pred_test, pos_label='yes')]},
index=['train', 'test']
)
- 将训练好的模型和结果存储在存储器上
subprocess.call([
'gsutil', 'cp',
# Local path of the model
os.path.join(LOCAL_PATH, 'model.joblib'),
os.path.join(args.storage_path, 'model.joblib')
])
subprocess.call([
'gsutil', 'cp',
# Local path of results
os.path.join(LOCAL_PATH, 'results.csv'),
os.path.join(args.storage_path, 'results.csv')
])
你可以在 Github 上找到完整的代码。
注意:在代码的顶部有一些你可以调整的参数。
步骤 3:在本地测试代码
在云平台上提交训练过程之前,最好在本地测试代码。无论是本地培训还是云端培训,我们都会使用gcloud;要运行的命令非常相似,但也有一些不同。
首先,用 AI 平台在本地训练模型,我们可以写一个命令“还原”这个逻辑:gcloud ai-platform(用 AI 平台)local(本地)train(训练模型)。我们添加了一些参数,其中一些特定于训练过程,另一些在我们的定制 Python 应用程序中定义。
进入主目录和src目录后,完整的命令如下:
gcloud ai-platform local train \
--module-name=src.train \
--package-path=./src \
-- \
--storage-path=gs://bank-marketing-model/test \
--n-estimators=25
让我们来看看参数:
module-name是要运行的 Python 模块的名称,以directory.python_file的形式package-path是模块目录的路径—-告诉我们开始发送自定义参数
由于这是一次测试,我们为随机森林指定了少量的树。
请记住,即使这是一个本地测试运行,它也会创建所有的工件,并将它们保存到您的存储桶中。如果一切运行无误,我们就可以切换到云上的实际训练了!
第四步:在人工智能平台上训练
现在我们已经编写了应用程序并对其进行了测试,我们终于可以在平台上训练我们的模型了。例如,我们可以实例化一个多核机器类型,并将森林的训练并行化。
如前所述,bash 代码非常相似,但是我们必须调用另一组命令。我们用jobs submit training代替local train:
gcloud ai-platform jobs submit training $JOB_NAME \
--module-name=src.train \
--package-path=./src \
--staging-bucket=gs://bank-marketing-model \
--region=$REGION \
--scale-tier=CUSTOM \
--master-machine-type=n1-standard-8 \
--python-version=3.7 \
--runtime-version=2.2 \
-- \
--storage-path=gs://bank-marketing-model/$JOB_NAME \
--n-estimators=500 \
--n-jobs=8
让我们再一次看看新的参数:
module-name和package-path与之前相同staging-bucket指向存储训练工件的 Google 存储桶region是谷歌云地区(此处为完整列表)。由于为这些作业实例化的机器位于世界各地,您可能希望指定在哪里实例化机器。如果您不确定如何设置该参数,只需设置离您最近的区域即可!scale-tier指定用于培训的机器类型。这是一个
高级参数,您可以将其设置为默认配置(如BASIC或STANDARD_1),或者将其设置为CUSTOM并使用master-machine-type参数来使用机器的低级定义。在我们的示例中,我们使用一台带有 8 内核的标准机器。我们也可以指定highmem(更多内存)或highcpu(更多虚拟 CPU)来代替standard``。这里的[是scale-tier`和可用机器的完整列表](https://cloud.google.com/ai-platform/training/docs/machine-types#compare-machine-types)。python-version和runtime-version参数指定安装在机器上的软件包的主干。对于每个运行时 ( 这里是完整列表)我们都有一个或多个可以使用的 Python 版本,以及一个已经安装的 Python 包列表。
额外提示:如果你想安装一个运行时列表中没有的包,在你的应用程序顶部添加一个“pip”命令,比如
subprocess.call(["pip "," install ",包名)
- 对于我们的自定义参数,我们设置了更多的估计器,并指定并行运行 8 个作业。
注意:上面代码中的$JOB_NAME不是一个有效的名称,但是它是对之前定义的 bash 变量的引用。每次指定一个
不同的工作名称是非常重要的。例如,在 cloud_train 和
脚本中可以看到,我们可以指定
JOB_NAME=bank_marketing_$(date +%Y%m%d_%H%M%S)
所以后缀每次都会随着实际的日期和时间而改变。
如果你是第一次使用这个工具,它可能会要求你启用一个特定的 Google API(像ml.googleapis.com):接受启用它并继续。如果一切正常,该命令应该返回如下内容

作者图片
在你的谷歌人工智能平台作业控制台中,你应该会看到新的作业正在运行

作者图片
现在,您可以通过两种方式监控培训作业:
- 使用建议的命令
gcloud ai-platform jobs stream-logs $JOB_NAME - 点击作业控制台上的查看日志

作者图片
万岁!作业正确结束。让我们来看看我们的储物桶:

作者图片
我们有了model.joblib对象、具有模型性能的results.csv文件以及由作业自动创建的用于存储 Python 应用程序的文件夹。
让我们来看看模型的性能…我们可以下载结果文件,或者键入命令
gsutil cat gs://your-bucket/your/path/to/results.csv
直接在命令行上查看文件。
对于我的训练模型,我得到了以下结果:
Measure | Train | Test
------------|----------|----------
Accuracy | 81.53% | 81.01%
Precision | 37.00% | 36.40%
Recall | 91.42% | 90.05%
F1 | 52.68% | 51.85%
这些是相当好的结果,但是我们可以通过 调整模型的超参数 来做得更好。在下一篇文章中,我们将看到如何稍微改变训练应用程序,使 AI 平台使用贝叶斯优化过程搜索最佳超参数!
感谢阅读,我希望你会发现这是有用的!
使用 BigQuery ML 为 Google Analytics 数据训练推荐模型
协同过滤推荐报纸文章
这篇文章演示了如何实现一个 WALS(矩阵分解)模型来进行协同过滤。对于协同过滤,我们不需要知道任何关于用户或内容的信息。本质上,我们需要知道的只是 userId、itemId 和特定用户对特定项目的评价。

图片由 geralt (cc0)提供
使用花费在页面上的时间作为评级
在这种情况下,我们正在处理报纸文章。该公司不会要求用户给文章打分。然而,我们可以用花在网页上的时间来代替评分。
以下是获取每个用户在每篇报纸文章上花费的持续时间的查询(通常,我们还会添加一个时间过滤器(“最近 7 天”),但我们的数据集本身仅限于几天。):
WITH CTE_visitor_page_content AS (
SELECT
# Schema: [https://support.google.com/analytics/answer/3437719?hl=en](https://support.google.com/analytics/answer/3437719?hl=en)
# For a completely unique visit-session ID, we combine combination of fullVisitorId and visitNumber:
CONCAT(fullVisitorID,'-',CAST(visitNumber AS STRING)) AS visitorId,
(SELECT MAX(IF(index=10, value, NULL)) FROM UNNEST(hits.customDimensions)) AS latestContentId,
(LEAD(hits.time, 1) OVER (PARTITION BY fullVisitorId ORDER BY hits.time ASC) - hits.time) AS session_duration
FROM
`cloud-training-demos.GA360_test.ga_sessions_sample`,
UNNEST(hits) AS hits
WHERE
# only include hits on pages
hits.type = "PAGE"
GROUP BY
fullVisitorId,
visitNumber,
latestContentId,
hits.time )
-- Aggregate web stats
SELECT
visitorId,
latestContentId as contentId,
SUM(session_duration) AS session_duration
FROM
CTE_visitor_page_content
WHERE
latestContentId IS NOT NULL
GROUP BY
visitorId,
latestContentId
HAVING
session_duration > 0
LIMIT 10
结果看起来像这样:

将评级字段缩放至[0,1]
我们希望花更多时间看一篇文章的访问者会更喜欢它。然而,会话持续时间可能有点可笑。注意第 3 行显示的 55440 秒(15 小时)。我们可以绘制数据集中会话持续时间的直方图:

因此,让我们按会话持续时间的中位数(平均持续时间将受到异常值的显著影响)来缩放和裁剪它的值:
...normalized_session_duration AS (
SELECT APPROX_QUANTILES(session_duration,100)[OFFSET(50)] AS median_duration
FROM aggregate_web_stats
)
SELECT
* EXCEPT(session_duration, median_duration),
CLIP(0.3 * session_duration / median_duration, 0, 1.0) AS normalized_session_duration
FROM
aggregate_web_stats, normalized_session_duration
其中剪辑定义如下:
CREATE TEMPORARY FUNCTION CLIP_LESS(x FLOAT64, a FLOAT64) AS (
IF (x < a, a, x)
);
CREATE TEMPORARY FUNCTION CLIP_GT(x FLOAT64, b FLOAT64) AS (
IF (x > b, b, x)
);
CREATE TEMPORARY FUNCTION CLIP(x FLOAT64, a FLOAT64, b FLOAT64) AS (
CLIP_GT(CLIP_LESS(x, a), b)
);
现在,会话持续时间已调整,并在正确的范围内:

将所有东西放在一起,并将其具体化为表格:
CREATE TEMPORARY FUNCTION CLIP_LESS(x FLOAT64, a FLOAT64) AS (
IF (x < a, a, x)
);
CREATE TEMPORARY FUNCTION CLIP_GT(x FLOAT64, b FLOAT64) AS (
IF (x > b, b, x)
);
CREATE TEMPORARY FUNCTION CLIP(x FLOAT64, a FLOAT64, b FLOAT64) AS (
CLIP_GT(CLIP_LESS(x, a), b)
);
CREATE OR REPLACE TABLE advdata.ga360_recommendations_data
AS
WITH CTE_visitor_page_content AS (
SELECT
# Schema: [https://support.google.com/analytics/answer/3437719?hl=en](https://support.google.com/analytics/answer/3437719?hl=en)
# For a completely unique visit-session ID, we combine combination of fullVisitorId and visitNumber:
CONCAT(fullVisitorID,'-',CAST(visitNumber AS STRING)) AS visitorId,
(SELECT MAX(IF(index=10, value, NULL)) FROM UNNEST(hits.customDimensions)) AS latestContentId,
(LEAD(hits.time, 1) OVER (PARTITION BY fullVisitorId ORDER BY hits.time ASC) - hits.time) AS session_duration
FROM
`cloud-training-demos.GA360_test.ga_sessions_sample`,
UNNEST(hits) AS hits
WHERE
# only include hits on pages
hits.type = "PAGE"
GROUP BY
fullVisitorId,
visitNumber,
latestContentId,
hits.time ),
aggregate_web_stats AS (
-- Aggregate web stats
SELECT
visitorId,
latestContentId as contentId,
SUM(session_duration) AS session_duration
FROM
CTE_visitor_page_content
WHERE
latestContentId IS NOT NULL
GROUP BY
visitorId,
latestContentId
HAVING
session_duration > 0
),
normalized_session_duration AS (
SELECT APPROX_QUANTILES(session_duration,100)[OFFSET(50)] AS median_duration
FROM aggregate_web_stats
)
SELECT
* EXCEPT(session_duration, median_duration),
CLIP(0.3 * session_duration / median_duration, 0, 1.0) AS normalized_session_duration
FROM
aggregate_web_stats, normalized_session_duration
培训推荐模型
表格数据现在看起来像这样:

我们可以按如下方式训练模型:
CREATE OR REPLACE MODEL advdata.ga360_recommendations_model
OPTIONS(model_type='matrix_factorization',
user_col='visitorId', item_col='contentId',
rating_col='normalized_session_duration',
l2_reg=10)
AS
SELECT * from advdata.ga360_recommendations_data
注意:如果您正在使用按需定价计划,您将会收到一条错误消息“训练矩阵分解模型不可用于按需使用。Totrain,请根据 BigQuery 公共文档中的说明设置预订(弹性或常规)。”。这是因为当基于数据定价时,矩阵分解往往变得昂贵。灵活插槽基于计算进行定价,而且便宜得多。所以, 设置 flex 槽位——你可以使用它们少至 60 秒。
您将使用 l2_reg 的值,它会为您的数据集产生合理的误差。你怎么知道合理?检查评估选项卡:

您希望平均值和中位数绝对误差相似,并且比 0.5 小得多(随机概率将是 0.5)。
做预测
以下是如何为每个用户找到前 3 条建议:
SELECT
visitorId,
ARRAY_AGG(STRUCT(contentId, predicted_normalized_session_duration)
ORDER BY predicted_normalized_session_duration DESC
LIMIT 3)
FROM ML.RECOMMEND(MODEL advdata.ga360_recommendations_model)
WHERE predicted_normalized_session_duration < 1
GROUP BY visitorId
这会产生:

尽情享受吧!
非常感谢卢卡·塞姆普雷和伊万·琼斯帮助弄清楚谷歌分析数据。
用对比学习训练一个初步的说话人确认模型
深入分析
说话人确认,深度学习,对比学习

我们的 Android 应用
对于我在大学的 Android 开发课程的小组项目组件,我们的团队构建并部署了一个身份验证系统,该系统通过说话者的语音档案进行身份验证。
继我描述语音认证系统的高级架构的上一篇文章(见下一段)之后,本文试图深入研究所使用的深度学习模型的开发过程。
我之前的文章可以在这里找到(一个带有移动部署的初级语音认证系统)。

不要害怕使用你的声音(照片由杰森·罗斯韦尔在 Unsplash 上拍摄)
在这篇短文中,我将描述开发语音认证模型的不同阶段,并讨论在此过程中收集到的一些个人学习经历。
下面是这篇文章的概述:
- 问题陈述
- 高级模型设计
- 数据预处理
- 通过对比学习的语音编码器
- 用于认证的二元分类器
- 模型性能
问题陈述
在我们开始之前,我们需要明确我们是如何尝试构建语音认证问题的。
语音认证主要有两种方式(广义而言):说话人识别和说话人确认。虽然这两种方法非常相关,但是当比较两个系统的相关安全风险时,它们将导致两个系统具有非常不同的特征。
问题定义:
- 说话人识别 : n 分类任务:给定一个输入话语,从 n 个已知说话人(类)中识别出正确的说话人。
- 说话人确认:二进制分类任务:给定说话人具有声明身份的输入话语,确定该声明身份是否正确。
我们可以看到,在说话人识别中,我们假设给定的输入话语属于我们已经知道的说话人(也许是办公室环境),并且我们正试图从 n 个已知的类别/说话人中挑选出最接近的匹配。
相反,在说话人验证中,我们假设我们不知道给定的输入话语属于谁(事实上我们不需要知道)。我们关心的是给定的一对输入话语是否来自同一个人。
准确地说,从整个说话人验证系统的角度来看,系统“知道”说话人自称是某个“已知”的人,而从模型的角度来看,模型只接收一对语音样本,并确定它们是否来自同一个人/来源。
想象一下使用常规的用户名和密码认证系统。系统知道你自称是谁(用户名),并检索参考密码的存储副本。然而,密码检查器只是检查参考密码是否与输入密码匹配,并将认证结果返回给验证系统。
在这个项目中,语音认证问题被框定为说话人确认问题。
旁白:
【* *在这一点上,值得注意的是,还有另一类语音分析问题叫做说话人日记化,它寻求分离出多人同时说话的源信号。
发言者日记涉及多人同时发言的场景(想象将麦克风放在两个坐满谈话者的桌子中间),我们试图隔离每个独特发言者的语音音频波形。这可能需要多个话筒从不同角度捕捉同一场景,或者只需要一个话筒(最困难的问题)。
我们可以看到这很容易变得非常复杂。一个例子是在给定对话的音频记录中识别谁在说话。在试图说出说话者的名字(说话者识别)之前,必须分离出对话中每个人的语音信号(说话者日记化)。当然,混合方法是存在的,这是一个活跃的研究领域。**]
高级模型设计
为迁移学习转换数据 —对于这项任务,我想尽可能地利用迁移学习,以避免独自构建复杂且高性能的模型。
为了实现这个目标,语音音频信号被转换成类似某种图像的频谱图(更准确地说,是 melspectrograms)。将音频转换成 melspectrograms 后,我可以使用 PyTorch 中任何流行的图像模型,如 MobileNetV2、DenseNet 等。

来自其中一个扬声器的 melspectrogram 的样本/切片
事后看来,我意识到我可以使用基于小波变换(WT)的方法,而不是基于傅立叶变换(FT)的 melspectrogram,来获得“更清晰”的光谱图图像。Youtube 视频中的一个演示比较了心脏 ECG 信号的小波变换和傅立叶变换之间的“图像质量”差异,结果是小波变换得到的频谱图在视觉上比傅立叶变换得到的频谱图更加“清晰”。
利用对比学习 —每个人都熟悉的对比学习的经典例子是使用三连音损失的设置。该设置每次编码 3 个样本:参考样本、阳性样本和阴性样本(2 个候选样本)。目标是减小参考样本和正样本之间的编码向量的距离,同时增加参考样本和负样本之间的编码向量的距离。
在我的方法中,我不想将候选样本的数量限制在 2 个。相反,我使用了一种类似于 SimCLR 的方法。艾尔。2020),其中使用多个候选样本,其中一个是阳性样本。然后“对比分类器”被迫从一堆候选样本中挑选出阳性样本。在后面的部分中有更多的细节。
两阶段迁移学习方法——为了解决说话人确认问题,我们将分两个阶段训练模型。
首先,将通过对比学习来训练说话人语音编码器。如上所述,对比学习将涉及多个候选样本,而不是通常的三重损失设置的正负对。
第二,然后在预训练的语音编码器之上训练二进制分类器。这将允许语音编码器在用于该转移学习任务(二进制分类器)之前被单独训练。
数据预处理
vox Cele 1 数据集——为了训练一个模型来识别一个说话者的声音轮廓(不管那意味着什么),我选择了使用vox Cele 1公共数据集。
VoxCeleb1 数据集包含野外多个说话者的音频片段,即说话者在“自然”或“常规”设置中说话。正在采访数据集中的讲话者,并且管理数据集中的音频片段,使得每个片段包含讲话者正在讲话的采访片段。该数据集包含在不同采访环境下对每个说话者的多次采访,并使用各种类型的设备,为我提供了我希望我的语音认证系统能够处理的可变性。
对于这个项目,只使用了音频数据(视频数据可用)。还有其他认证系统试图整合多种模式的数据(例如视频结合音频来检测语音是否是现场制作的),但我认为这超出了我的项目范围。
音频波形到声谱图 —为了能够利用流行的图像模型架构,语音音频信号被转换成类似于某种图像的声谱图。
首先,来自同一说话者的多个短音频样本被组合成一个长音频样本。由于 melspectrogram 基于短时傅立叶变换(STFT),因此可以将整个长音频样本一次性转换成 melspectrogram,并且可以从长频谱图中获得更小的频谱图切片。由于长音频样本是由来自单个唯一说话者的较小音频样本组成的,因此从长样本中提取切片应该会迫使模型专注于挑选出每个单独说话者的语音简档的独特特征。

来自一个扬声器的串联音频样本
接下来,使用 LibROSA (Librosa)库将长音频样本转换成 melspectrograms。以下是使用的关键参数:
- 目标采样率:22050
- STFT 之窗:2048
- STFT 跳长度:512
- 梅尔斯:128
功率谱然后被转换成对数标度的分贝数。因为我们是用图像网络进行分析,所以我们希望谱图中的“图像”特征在某种程度上均匀分布。

一个扬声器的 melspectrogram
创建“图像” —通过从长光谱图中截取较小的光谱图来进行采样。
光谱图“图像”以 RGB 图像格式创建为 128 x 128 x 3 阵列。在长光谱图上选择一个随机起始点,并通过将窗口滑动半步(128/2=64)获得三个 128×128 光谱图切片。然后通过[-1,1]之间的最大绝对值对“图像”进行归一化。
最初,我将同一个光谱切片复制了 3 次,将“灰度”图像转换为“RGB”图像。然而,我决定通过为每个“RGB”图像放置 3 个略有不同的切片,将更多信息打包到每个光谱图“图像”中,因为 3 个通道不具有普通 RGB 图像所具有的通常意义。使用这种滑动技术后,性能似乎略有提高。
通过对比学习的语音编码器
数据采样 —以下是用于语音编码器对比学习的数据采样的一些细节。[* *频谱图切片将被称为“图像”]
对于每个时期,将 200 个(总共 1000 个)随机全长光谱图载入存储器(“子样本”)(由于资源限制,不是所有的 1000 个全长光谱图都可以一次载入)。
对于每一行,使用 1 个参考和 5 个候选。候选图像包含 4 幅负图像和 1 幅正图像(随机混洗),并且图像是从子样本中随机生成的。
每个 epoch 有 2,000 行,批处理大小为 15 (MobileNetV2)和 6 (DenseNet121)。

语音编码器对比学习设置
多连体编码器网络 —对于编码器网络,使用的基本型号为 MobileNetV2 和 DenseNet121。编码器层大小为 128(替换基本模型中的 Imagenet 分类器)。
为了便于对比学习,构建了多连体编码器模型包装器作为 torch 模块。包装器对每个图像使用相同的编码器,并且便于参考图像和候选图像之间的余弦相似性计算。
对比损失+类内方差减少 —对比损失和方差减少这两个目标被连续最小化(由于资源限制,理想情况下,这两个目标的损失应该相加并最小化)。计算每批的对比损失,同时每 2 批进行方差减少(类内 MSE)。
对于【对比损失】的争夺,继陈等人之后。艾尔。(2020)中,该问题被公式化为 n 分类问题,其中该模型试图从所有候选中识别正面图像。针对参考编码计算所有候选编码的余弦相似性,并且针对余弦相似性计算 softmax,从而产生概率。按照正常的 n 分类问题,交叉熵损失被最小化。
对于类内方差减少,目标是在编码空间将来自同一类的图像推得更近。对来自相同类别/说话者的图像进行采样,并计算平均编码向量。针对平均值(类内方差)计算编码的 MSE 损失,并且在反向传播之前将 MSE 损失缩放 0.20。
用于认证的二元分类器
数据采样 —以下是说话人确认二进制分类器使用的数据采样的一些细节。
对于每个时期,将 200 个(总共 1000 个)随机全长光谱图载入存储器(“子样本”)(由于资源限制,不是所有的 1000 个全长光谱图都可以一次载入)。
对于每个参考图像,生成 2 个测试图像,1 个正图像和 1 个负图像。这为每个参考图像产生 2 对/行,真实对(正)和视点替用图像对(负)。
图像是从子样本中随机生成的。每个 epoch 有 4,000 行,批处理大小为 320 (MobileNetV2 和 DenseNet121)。

说话人确认二进制分类器设置
验证二元分类器网络 —底层编码器网络是来自对比学习步骤的预训练编码器网络,其中权重在训练期间被冻结。
二进制分类器被设置为一个连体网络,其中计算来自输入对的编码向量的绝对差。然后在绝对差值层的顶部建立二元分类器。
模型培训(其他详细信息)
学习速率循环 —循环学习速率提高了对比学习步骤和二元分类器步骤中的模型准确性。
torch.optim.lr_scheduler。使用 CyclicLR(),步长(默认)为 2000,循环模式(默认)为“三角形”,没有动量循环(Adam 优化器)。
用于循环的学习率范围如下:
- 带对比学习的语音编码器: 0.0001 到 0.001
- B 二进制分类器: 0.0001 到 0.01
模型性能
正如预期的那样,模型性能不如最先进的模型。我认为促成因素是:
- melspectrogram 不是我能使用的最好的信号转换。基于小波变换的方法可能产生更高质量的频谱图“图像”。
- 使用的基本图像模型不是最先进的模型,因为我不能在我的中等大小的 GPU (3GB VRAM)上使用非常大的模型。也许像 ResNet 或 ResNeXt 这样更强大的模型可能会产生更好的结果。
- 只使用了一个语音数据集(VoxCeleb1)。更大数量和种类的数据肯定是可以使用的(但是,唉,最后期限越来越近了)。
以下是可比模型的等误差率(EER ):
- 我最好的模特 EER 19.74% (VoxCeleb)
- 乐和奥多比(2018),从零开始的最佳模型 EER 10.31% (VoxCeleb)
- 荣格等人。艾尔。(2017 年), EER 7.61% (RSR2015 数据集)
其他发现
基础模型大小 —使用更大的基础模型提高了分类性能(不足为奇)(MobileNetV2 vs DenseNet121)。
类内方差减少的效果 —类内方差减少提高了两个基础模型的分类性能。事实上,方差减少后的 MobileNetV2 的性能提高到了与 DenseNet121 相当的水平。

ROC(左)和 DET(右)曲线
下面是使用 MobileNetV2 和 DenseNet121 的二元分类分数分布[P(is_genuine)],有方差减少和无方差减少。

MobileNetV2 基本型号—不含/含方差减少(左/右)

DenseNet121 基本型号—无/有方差减少(左/右)
参考
陈,t .,科恩布利思,s .,m .和辛顿,g .,2020。视觉表征对比学习的简单框架。arXiv 预印本 arXiv:2002.05709
Jung,j .,Heo,h .,Yang,I .,Yoon,s .,Shim,h .和 Yu,h .,2017 年 12 月。基于 d 矢量的说话人确认系统。在 2017 人工智能、网络与信息技术国际研讨会(ANIT 2017) 。亚特兰蒂斯出版社。
n . le 和 j . m . Odobez,2018 年 9 月。基于类内距离方差正则化的鲁棒区分性说话人嵌入。在散置(第 2257–2261 页)。
在您自己的数据集上训练 TensorFlow 快速 R-CNN 对象检测模型
遵循本教程,您只需更改两行代码,就可以将对象检测模型训练到您自己的数据集。

寻找红血球、白血球和血小板!
计算机视觉正在彻底改变医学成像。算法正在帮助医生识别他们可能漏掉的十分之一的癌症患者。甚至有早期迹象胸部扫描可以帮助鉴别新冠肺炎,这可能有助于确定哪些病人需要实验室检测。
为此,在本例中,我们将使用 TensorFlow 对象检测 API 训练一个对象检测模型。虽然本教程描述了在医学成像数据上训练模型,但它可以很容易地适应任何数据集,只需很少的调整。
不耐烦?直接跳到 Colab 笔记本这里。
我们示例的各个部分如下:
- 介绍我们的数据集
- 准备我们的图像和注释
- 创建 TFRecords 和标签映射
- 训练我们的模型
- 模型推理
在整个教程中,我们将利用 Roboflow ,这是一个大大简化我们的数据准备和训练过程的工具。Roboflow 对于小数据集是免费的,所以我们将为这个例子做好准备!
我们的示例数据集:血细胞计数和检测(BCCD)
我们的示例数据集是 364 幅细胞群图像和 4888 个标识红细胞、白细胞和血小板的标签。最初由 comicad 和 akshaymaba 于两年前开源,可在https://public . robo flow . ai获得。(请注意,Roboflow 上的版本与最初版本相比,包含了微小的标签改进。)

截图 via Roboflow 公众。
幸运的是,这个数据集是预先标记的,所以我们可以直接为我们的模型准备图像和注释。
了解患者红细胞、白细胞和血小板的存在及其比例是识别潜在疾病的关键。使医生能够提高识别上述血细胞计数的准确性和处理量,可以大大改善数百万人的医疗保健!
对于你的定制数据,考虑以自动化的方式从谷歌图片搜索中收集图片,并使用像 LabelImg 这样的免费工具给它们加标签。
准备我们的图像和注释
直接从数据收集到模型训练会导致次优的结果。数据可能有问题。即使没有,应用图像增强也会扩展数据集并减少过度拟合。
为对象检测准备图像包括但不限于:
- 验证您的注释是否正确(例如,图像中没有任何注释超出框架)
- 确保图像的 EXIF 方向正确(即图像在磁盘上的存储方式不同于在应用程序中的查看方式,请参阅更多)
- 调整图像大小并更新图像注释以匹配新调整的图像大小
- 检查数据集的健康状况,如其类别平衡、图像大小和纵横比,并确定这些如何影响我们想要执行的预处理和增强
- 各种可以提高模型性能的颜色校正,如灰度和对比度调整
与表格数据类似,清理和扩充图像数据比模型中的架构更改更能提高最终模型的性能。
让我们看看数据集的“健康检查”:

可用此处。
我们可以清楚地看到,我们的数据集中存在很大的类别不平衡。在我们的数据集中,红细胞明显多于白细胞或血小板,这可能会给我们的模型训练带来问题。根据我们的问题环境,我们可能希望将一个类的识别优先于另一个类。
此外,我们的图像都是相同的大小,这使得我们的调整的决定更加容易。

可用此处。
当检查我们的对象(细胞和血小板)如何分布在我们的图像中时,我们看到我们的红细胞到处都是,我们的血小板有点向边缘分散,我们的白细胞聚集在我们图像的中间。考虑到这一点,当检测红细胞和血小板时,我们可能会厌倦裁剪图像的边缘,但如果我们只是检测白细胞,边缘就显得不那么重要了。我们还想检查我们的训练数据集是否代表样本外图像。例如,我们能否期望白细胞通常集中在新收集的数据中?
对于您的自定义数据集,按照这个简单的逐步指南将您的图像及其注释上传到 Roboflow。
创建 TFRecords 和标签映射
我们将使用速度更快的 R-CNN 的 TensorFlow 实现(稍后将详细介绍),这意味着我们需要为 TensorFlow 生成 TFRecords,以便能够读取我们的图像及其标签。TFRecord 是一种包含图像及其注释的文件格式。它在数据集级别序列化,这意味着我们为训练集、验证集和测试集创建一组记录。我们还需要创建一个 label_map,它将我们的标签名称(RBC、WBC 和血小板)映射为字典格式的数字。
坦白说,TFRecords 有点繁琐。作为开发人员,您的时间应该集中在微调您的模型或使用您的模型的业务逻辑上,而不是编写冗余代码来生成文件格式。因此,我们将使用 Roboflow 通过几次点击来为我们生成 TFRecords 和 label_map 文件。
首先,访问我们将在这里使用的数据集:https://public.roboflow.ai/object-detection/bccd/1(注意,我们使用的是数据集的特定版本。图像尺寸已调整为 416x416。)
接下来,点击“下载”系统可能会提示您使用 email 或 GitHub 创建一个免费帐户。
下载时,您可以下载各种格式的文件,下载到您的机器上,或者生成一个代码片段。出于我们的目的,我们希望生成 TFRecord 文件并创建一个下载代码片段(不是本地下载文件)。

导出我们的数据集。
会给你一段代码来复制。该代码片段包含一个到源图像、它们的标签以及一个划分为训练集、验证集和测试集的标签映射的链接。抓紧了!
对于您的自定义数据集,如果您按照的分步指南上传图像,系统会提示您创建训练、有效、测试分割。您还可以将数据集导出为您需要的任何格式。
训练我们的模特
我们将训练一个更快的 R-CNN 神经网络。更快的 R-CNN 是一个两级对象检测器:首先它识别感兴趣的区域,然后将这些区域传递给卷积神经网络。输出的特征图被传递到支持向量机(VSM)用于分类。计算预测边界框和基本事实边界框之间的回归。更快的 R-CNN,尽管它的名字,被认为是一个比其他推理选择(如 YOLOv3 或 MobileNet)更慢的模型,但稍微更准确。为了更深入的探究,请考虑这篇文章中的更多内容!
更快的 R-CNN 是 TensorFlow 对象检测 API 默认提供的许多模型架构之一,包括预训练的权重。这意味着我们将能够启动一个在 COCO(上下文中的公共对象)上训练的模型,并使它适应我们的用例。

利用 TensorFlow 模型动物园。(来源)
TensorFlow 甚至在 COCO 数据集上提供了几十个预先训练好的模型架构。
我们还将利用 Google Colab 进行计算,这是一种提供免费 GPU 的资源。我们将利用 Google Colab 进行免费的 GPU 计算(最多 12 小时)。
我们的 Colab 笔记本就是这里的。这个所基于的 GitHub 库就是这里的**。******
您需要确保使用您自己的 Roboflow 导出数据更新单元格调用的代码片段。除此之外,笔记本按原样训练!
关于这款笔记本,有几点需要注意:
- 为了运行初始模型,训练步骤的数量被限制为 10,000。增加这个值可以改善你的结果,但是要注意过度拟合!
- 具有更快 R-CNN 的模型配置文件包括在训练时的两种类型的数据扩充:随机裁剪,以及随机水平和垂直翻转。
- 模型配置文件的默认批量大小为 12,学习率为 0.0004。根据你的训练结果调整这些。
- 该笔记本包括一个可选的 TensorBoard 实现,使我们能够实时监控模型的训练性能。
在我们使用 BCCD 的例子中,经过 10,000 步的训练后,我们在 TensorBoard 中看到如下输出:

总的来说,我们的损失在 10,000 个纪元后继续下降。

我们正在寻找合适的盒子,但我们有过度适应的风险。

回忆可能表明一个类似的过度拟合的故事。
在这个例子中,我们应该考虑收集或生成更多的训练数据,并利用更大的数据扩充。
**对于您的自定义数据集,只要您将 Roboflow 导出链接更新为特定于您的数据集,这些步骤基本相同。留意你的冲浪板输出是否过度配合!
模型推断
当我们训练我们的模型时,它的拟合度存储在一个名为./fine_tuned_model的目录中。我们的笔记本中有保存模型拟合的步骤——要么本地下载到我们的机器上,要么通过连接到我们的 Google Drive 并将模型拟合保存在那里。保存我们模型的拟合不仅允许我们在以后的生产中使用它,而且我们甚至可以通过加载最新的模型权重从我们停止的地方恢复训练!
在这个特定的笔记本中,我们需要将原始图像添加到/data/test 目录中。它包含 TFRecord 文件,但是我们希望我们的模型使用原始(未标记的)图像来进行预测。
我们应该上传我们的模型没有见过的测试图像。为此,我们可以将原始测试图像从 Roboflow 下载到我们的本地机器上,并将这些图像添加到我们的 Colab 笔记本中。
重新访问我们的数据集下载页面:https://public.roboflow.ai/object-detection/bccd/1
点击下载。对于格式,选择 COCO JSON 并下载到您自己的计算机上。(实际上,您可以下载除 TFRecord 之外的任何格式,以获得独立于注释格式的原始图像!)
在本地解压缩该文件后,您将看到测试目录的原始图像:

现在,在 Colab 笔记本中,展开左侧面板以显示测试文件夹:

右键单击“测试”文件夹并选择“上传”现在,您可以从本地机器上选择您刚刚下载的所有图像!
在笔记本中,其余的单元将介绍如何加载我们创建的已保存、已训练的模型,并在您刚刚上传的图像上运行它们。
对于 BCCD,我们的输出如下所示:

我们的模型在 10,000 个纪元后表现得相当好!
**对于您的自定义数据集,这个过程看起来非常相似。你不用从 BCCD 下载图像,而是从你自己的数据集中下载图像,然后相应地重新上传。
下一步是什么
你做到了!您已经为自定义数据集训练了对象检测模型。
现在,在生产中使用这个模型回避了识别您的生产环境将会是什么的问题。例如,您会在移动应用程序中、通过远程服务器、甚至在 Raspberry Pi 上运行模型吗?如何使用模型决定了保存和转换其格式的最佳方式。
根据你的问题考虑这些资源作为下一步:转换到 TFLite (针对 Android 和 iPhone),转换到 CoreML (针对 iPhone 应用),转换到远程服务器上使用,或者部署到 Raspberry Pi 。
另外,订阅 Roboflow 更新当我们发布更多类似的内容时,第一个知道。
使用自定义数据集训练 YOLOv3 对象检测模型
遵循此指南,您只需更改一行代码,就可以将对象检测模型训练到您自己的数据集。 故事在这里发表过 。

在我们的引导式示例中,我们将训练一个模型来识别棋子。(完整视频)
不耐烦? 跳到 Colab 笔记本。
物体检测模型非常强大——从在照片中寻找狗到改善医疗保健,训练计算机识别哪些像素构成物品释放了近乎无限的潜力。然而,阻止构建新应用程序的最大障碍之一是采用最先进的、开源的和免费的资源来解决定制问题。
在本帖中,我们将介绍如何使用简化图像管理、架构和培训的工具为对象检测准备自定义数据集。在每一节中,我们将首先遵循我为一个特定示例所做的工作,然后详细说明您需要对自定义数据集进行哪些修改。我的建议是,在将这些步骤应用于您的问题之前,您应该一步一步地重复我的过程。
让我们从清楚地描述我们的过程开始。
任何给定的机器学习问题都是从一个格式良好的问题陈述、数据收集和准备、模型训练和改进以及推理开始的。通常,我们的过程不是严格线性的。例如,我们可能会发现我们的模型在一种类型的图像标签上表现很差,我们需要重新收集该示例的更多数据。

通用的机器学习工作流程。
问题陈述
国际象棋是一种充满智慧和策略的有趣游戏。提高你的游戏能力需要了解你以前在哪里犯了明显的错误,以及在同样的情况下一个比你强的玩家可能做出了什么举动。
因此,拥有一个能够识别游戏状态并记录下每一步棋的系统是很有价值的。这不仅需要确定给定的棋子是什么,还需要确定该棋子在棋盘上的位置——这是从图像识别到物体检测的一次飞跃。出于本文的目的,我们将把问题限制在对象检测部分:我们能否训练一个模型来识别哪个棋子是哪个棋子以及这些棋子属于哪个玩家(黑人或白人),以及一个模型在推理中找到至少一半的棋子。

图像处理问题,改编自斯坦福的cs 231n 课程
对于你的非象棋问题陈述,考虑将问题空间限制在一个特定的棋子上。此外,考虑模型性能的最低可接受标准。(在这个例子中,我们被限制为仅仅识别正确的边界框,并且为可接受的标准设置相对较低的标准。更严格地说,我们可能想要识别我们模型的 IOU 或mAP——但那是另一篇文章。)
数据收集
为了识别棋子,我们需要收集和注释棋子图像。
我在收集数据时做了一些假设。首先,我所有的图像都是从同一个角度拍摄的。我在棋盘旁边的桌子上架起了一个三脚架。作为推论,这将要求我的相机处于捕捉训练数据的同一角度——并不是所有的棋手都可能在比赛前架起三脚架。其次,我创建了 12 个不同的类:六个棋子乘以两种颜色各一个。

来自我们数据集的(未标记的)图像示例。
最终,我收集了 292 张照片。我标记了这些图片中的所有部分,总共有 2894 个注释。该数据集在此公开。对于标签,有许多高质量、免费和开源的工具可用,如 LabelImg 。我正好用了 RectLabel ,3 美元/月。

我在 RectLabel 中标记的示例图像。
对于你的非象棋问题,考虑收集与你的模型在生产中的表现相关的图像。这意味着确保你有一个相似的角度,光线,质量和框架中的物体。定型集考虑的模型在生产中可能遇到的情况越多,其性能就越好。添加标签时,最好绘制包含整个对象的边界框,即使对象和边界框之间有少量空间。简而言之,不要用你的边界框剪切掉任何底层对象。如果一个物体挡住了另一个物体的视线,贴上标签,就好像你可以看到整个物体一样(见上面的白主教和白鲁克的例子)。
如果你正在寻找已经带注释的图像,考虑像 Roboflow 或 Kaggle 这样的网站上的物体检测数据集。
数据准备
直接从数据收集到模型训练会导致次优的结果。数据可能有问题。即使没有,应用图像增强也会扩展数据集并减少过度拟合。
为对象检测准备图像包括但不限于:
- 验证您的注释是否正确(例如,图像中没有任何注释超出框架)
- 确保图像的 EXIF 方向正确(即,图像在磁盘上的存储方式不同于在应用程序中查看的方式,查看更多信息
- 调整图像大小并更新图像注释以匹配新调整的图像大小
- 各种可以提高模型性能的颜色校正,如灰度和对比度调整
- 格式化注释以匹配模型输入的需求(例如,为 TensorFlow 生成 TFRecords,或者为 YOLO 的某些实现生成一个平面文本文件)。
与表格数据类似,清理和扩充图像数据比模型中的架构更改更能提高最终模型的性能。
Roboflow Organize 专为无缝解决这些挑战而构建。事实上,Roboflow Organize 将您需要编写的代码减少了一半,同时为您提供了更多的预处理和增强选项。
让我们学习加载 MNIST 以外的影像数据集!( 来源 )
对于我们的特定国际象棋问题,我们已经预处理的国际象棋数据是可用的这里。
我们将使用名为“416 x416-自动定向”的下载版本
在我们的教程中,我们使用 416x416 的图像,因为(1)我们想要比最初捕获的 2284 × 1529 尺寸更小的图像,以便更快地训练,(2)对于 YOLOv3,32 的倍数对于其架构来说是最有性能的。

注意在这个导出中,我们的预处理包括“自动定向”和“调整大小”
我们的图像还应用了“自动定向”,这是一个 Roboflow 预处理步骤,可以去除非直观方向的 EXIF 数据。自动定向很重要,因为图像有时存储在磁盘上的方向与我们用来查看它们的应用程序不同。如果不加以纠正,这可能会导致我们的模型出现无声的故障(参见黑客新闻上的讨论)。我建议默认打开这个选项。
要下载此数据集,请选择“416 x416-自动定向”在右上角,选择“下载”系统会提示您创建一个免费帐户,并重定向回 chess 公共数据集页面。
现在,Roboflow 允许你下载各种格式的图片和注释。您还可以将它们本地下载到您的计算机上,或者生成一个代码片段,用于将它们直接下载到 Jupyter 笔记本(包括 Colab)或 Python 脚本中。
对于我们的问题,我们将使用一个 Keras YOLOv3 实现,它调用一个注释的平面文本文件。我们还将利用 Google Colab 进行培训,因此在导出选项中选择“显示下载代码”。

这些是您将为下载格式选择的选项。

Roboflow 生成一个代码片段,你可以直接放入 Jupyter 笔记本,包括 Colab。一定不要公开分享你的钥匙!
对于你的非象棋问题,你可以创建一个免费的 Roboflow 账户,上传图像,预处理,增强,并导出到你的模型可能需要的任何注释格式。为此,请遵循快速启动。
模特培训
我们将使用的模型架构被称为 YOLOv3,或者你只看一次,作者是 Joseph Redmon 。这种特定的模型是一次性学习器,这意味着每幅图像只通过网络一次来进行预测,这使得该架构的性能非常高,在预测视频馈送时每秒可查看高达 60 帧。从根本上说,YOLO 把一幅图像分成几个子部分,并对每个子部分进行卷积,然后汇集起来进行预测。这里有一个关于 YOLO 的深度潜水推荐。
现在,即使我们在自定义数据集上训练我们的模型,使用另一个已经训练好的模型的权重作为起点仍然是有利的。想象一下,我们想要尽可能快地爬上一座山,而不是完全从零开始创建我们自己的路径,我们将从假设别人的路径比我们随机尝试猜测曲折路径更快开始。
为了给我们的模型计算提供动力,我们将使用 Google Colab,它提供免费的 GPU 计算资源(在浏览器打开的情况下长达 24 小时)。

随着验证损失的下降,我们的模型正在改进。
在我们的笔记本中,我们主要做六件事:
- 选择我们的环境、模型架构和预适应权重(其他人的“线索”)
- 通过上面分享的 Roboflow 代码片段加载我们的数据
- 确定我们的模型配置,比如训练多少个时期,训练批量大小,我们的训练与测试集的大小,以及我们的学习率
- 开始训练(…等待!)
- 使用我们训练好的模型进行推理(预测!)
- (可选奖励)将我们新训练的重量保存到我们的 Google Drive 中,这样我们就可以在未来做出预测,而不必等待训练完成
现在,复制一份这个 Colab 笔记本,然后在那边继续我们的教程。
对于您的非象棋问题,为了训练这个相同的架构,您只需要更改一个 URL,就可以在您的自定义数据集上训练一个 YOLOv3 模型。该 URL 是 Roboflow 下载 URL,我们通过它将数据集加载到笔记本中。此外,你也可以调整训练参数,比如设置一个较低的学习率或者训练更多/更少的时期。
推理
一旦我们的模型完成训练,我们将使用它来进行预测。进行预测需要(1)设置 YOLOv3 模型架构(2)使用我们在该架构下训练的自定义权重。
在我们的笔记本中,这一步发生在我们调用 yolo_video.py 脚本时。该脚本接受视频文件或图像的路径、自定义权重、自定义锚点(在本例中我们没有训练任何内容)、自定义类、要使用的 GPU 数量、描述我们是否预测图像而不是视频的标志,以及预测的视频/图像的输出路径。
在我们的例子中,我们将使用自定义权重和自定义类名来调用脚本。该脚本编译一个模型,等待图像文件的输入,并为找到的任何对象提供边界框坐标和类名。边界框坐标以左下角像素(mix_x,min_y)和右上角像素(max_x,max_y)的格式提供。
作为一个重要的标注,我们在这个例子中加载的自定义权重实际上并不是 YOLOv3 架构的最佳值。我们将加载“初始重量”而不是最终的训练重量。由于 Colab 计算的限制,我们的模型无法训练最终的权重。这意味着我们的模型没有达到应有的性能(因为只有 YOLO 的主干架构适应了我们的问题)。从好的方面来看,这是免费计算!
请注意,您可以在左侧的 Colab 笔记本中看到可用的文件(如果您已经有注释,请通过找到“+Code”和“+Text”下方的小右箭头来展开面板)。在 keras-yolo-3 文件夹中,你可以看到我们可用的所有图像。
我建议在运行您的预测脚本时尝试这些图像,以了解我们模型的性能:
- 00a7a49c47d51fd16a4cbb17e2d2cf86.jpg 白王作品!+骑士
- 015d0d7ff365f0b7492ff079c8c7d56c.jpg 黑皇后搞混了
- 176b28b5c417f39a9e5d37545fca5b4c.jpg 只找到五个
- 4673f994f60a2ea7afdddc1b752947c0.jpg 白车(国王认为)
- 5ca7f0cb1c500554e65ad031190f8e9f.jpg 白棋(错过白王)
- fbf15139f38a46e02b5f4061c0c9b08f.jpg 黑王成功了!
再翻回上面你抄的笔记本,我们来试试这个剧本。
对于你的非象棋题,这一步会大体相同。如果您想将此模型应用于视频馈送或尝试不同的权重,请确保遵循笔记本中详细说明的参数。如果你在一个非 Colab 的环境中训练,这个环境也可以处理产生模型的最终权重,确保提供这些权重的路径,而不是我们的“initial_weights”你可以考虑在笔记本或谷歌硬盘上创建一个名为“预测”的目录,并将模型的预测结果写在那里。
下一步是什么
你做到了!您已经为象棋和/或自定义数据集训练了对象检测模型。
现在,在生产中使用这个模型回避了识别您的生产环境将会是什么的问题。例如,您会在移动应用程序中、通过远程服务器、甚至在 Raspberry Pi 上运行模型吗?如何使用模型决定了保存和转换其格式的最佳方式。
根据你的问题考虑这些资源作为下一步:转换到 TFLite (用于 Android 和 iPhone),转换到 CoreML (用于 iPhone 应用),转换到远程服务器上使用,或者部署到 Raspberry Pi 。
想要更多?

加入 Roboflow 社区进行物体检测讨论!
订阅 Roboflow 更新当我们发布更多这样的内容时,第一个知道。
用 CGI 训练 AI
我们仅使用合成数据来训练计算机视觉模型,以识别 raspberry pi 板上的组件。

树莓 Pi 组件使用 100%合成数据。
在本文中,我们介绍了如何训练计算机视觉模型(AI)仅使用合成数据(CGI)来检测树莓派的子成分。
利用合成数据进行训练是一种越来越受欢迎的方式,可以满足渴望数据的深度学习模型的需求。这个项目使用的数据集可以在 app.zumolabs.ai [1]免费获得。我们希望让每个人都能轻松使用合成数据,并计划在未来发布更多数据集。
问题
树莓派是一款非常受爱好者欢迎的单板电脑。我们的目标是检测板上的一些子组件:引脚连接器、音频插孔和以太网端口。虽然这是一个玩具问题,但它与你在现实世界中看到的并不遥远——在现实世界中,使用计算机视觉自动检测组件和缺陷可以提高制造的速度和可靠性。
数据
为了生成合成数据,我们首先需要对象的 3D 模型。幸运的是,在当今世界,大多数物体已经存在于虚拟世界。SketchFab、TurboSquid 或 Thangs 等资产聚合网站已经将 3D 模型商品化[2]。给聪明人的建议:如果你在网上找不到模型,试着直接联系制造商,或者自己扫描并制作模型。

(上)合成图像和(下)分割蒙版。
然后,我们使用游戏引擎(如 Unity 或 Unreal Engine)从各种摄像机角度和各种照明条件下拍摄我们的 3D 模型的数千张图像。每幅图像都有一个相应的分割蒙版,用于分割图像中的不同部分。在以后的文章中,我们将更深入地研究创建合成图像的过程(敬请关注!).
所以现在我们有成千上万的合成图像,我们应该很好,对不对?不要!在真实数据上测试综合训练的模型以了解模型是否成功地推广到真实数据是非常重要的。模拟产生的数据和真实数据之间存在差距,称为模拟真实差距。一种思考方式是,深度学习模型会在最小的细节上过度拟合,如果我们不小心,许多这些细节可能只存在于合成数据中。
对于这个问题,我们手动注释了一个由十几幅真实图像组成的小型测试数据集。手动注释既耗时又昂贵。重要的是要注意,如果我们使用真实的图像进行训练,我们将不得不手动注释成千上万的图像,而不是仅仅一小部分用于测试!不幸的是,这是目前的做事方式,是我们试图改变的现状。摆脱这种手动注释过程是构建更好的人工智能的关键一步。
我们可以开始缩小 sim2real 差距的一种方法是通过一种被称为域随机化 [3][4】的技术。这种策略包括虚拟角色扮演的随机化特性,尤其是背景和角色扮演本身的视觉外观。这具有下游效应,使得我们基于该数据训练的模型对于颜色和光照的变化更加鲁棒。这也被称为网络的概括能力。

领域随机化图像。

领域随机化:增加合成数据分布的方差。
模特与培训
现在我们来看看模型。有许多不同类型的计算机视觉模型可供选择。利用深度学习的模型是目前最受欢迎的。它们非常适合探测任务,比如这个项目。我们使用了 PyTorch 的 torchvision 库中基于 ResNet 架构的模型[5]。合成数据将与任何模型架构一起工作,因此请随意试验并找到最适合您的用例的模型。
我们用四个不同的合成数据集训练我们的模型,以显示域随机化和数据集大小如何影响我们的真实测试数据集的性能:
- 数据集 A —一万五千张逼真的合成图像。
- 数据集 B —一万五千域随机合成图像。
- 数据集 C — 6 千张逼真的合成图像。
- 数据集 D — 6 千域随机合成图像。

与真实数据相比,合成数据的平均精度。
我们使用 mAP(平均精度)来衡量我们的计算机视觉模型的性能。需要注意的是,性能指标可能非常随意,因此请务必查看模型预测,以确保您的模型能够发挥应有的性能。正如我们预测的那样,模型的性能随着我们使用的合成数据越多而提高。深度学习模型几乎总是会随着更大的数据集而改进,但是,更有趣的是,用域随机合成数据集进行训练会导致我们的真实测试数据集的性能显著提升。
结论
TLDR:在这篇文章中,我们训练了一个计算机视觉模型,使用完全合成的数据来检测树莓派的子成分。我们使用了域随机化技术来提高我们的模型在真实图像上的性能。然后,哒哒!我们训练过的模型处理真实数据,尽管它从未见过一张真实图像。
感谢您的阅读,请务必亲自在 app.zumolabs.ai 查看数据集!如果您有任何问题或对合成数据感到好奇,请发送电子邮件至 info@zumolabs.ai ,我们喜欢聊天。
参考文献
[1] Zumo 实验室数据门户。(app.zumolabs.ai)
[2]3D assets sites:sketch fab(sketch fab . com),TurboSquid (turbosquid.com),Thangs (thangs.com)。
[3]莉莲翁。“Sim2Real 传输的域随机化”。(https://lilian Weng . github . io/lil-log/2019/05/05/domain-randomization . html)。
[4] Josh Tobin 等人,“将深度神经网络从模拟转移到现实世界的领域随机化”IROS,2017。(https://arxiv . org/ABS/1703.06907)。
[5]GitHub 上的火炬视觉。(https://github . com/py torch/vision)。
使用半监督学习为结构化数据训练更好的深度学习模型
利用未标记的样本来提高神经网络的性能。

众所周知,深度学习在应用于文本、音频或图像等非结构化数据时效果很好,但在应用于结构化或表格数据时,有时会落后于梯度增强等其他机器学习方法。
在这篇文章中,我们将使用半监督学习来提高深度神经模型在低数据区应用于结构化数据时的性能。我们将展示,通过使用无监督的预训练,我们可以使神经模型的性能优于梯度推进。
这篇文章基于两篇论文:
我们实现了一个深度神经架构,类似于 AutoInt 论文中介绍的内容,我们使用多头自我关注和特征嵌入。预训练部分摘自 Tabnet 论文。
方法描述:
我们将研究结构化数据,即可以写成包含列(数字、分类、序数)和行的表格的数据。我们还假设我们有大量的未标记样本可以用于预训练,少量的标记样本可以用于监督学习。在接下来的实验中,我们将模拟这个设置来绘制学习曲线,并在使用不同大小的标记集时评估该方法。
数据准备:
让我们用一个例子来描述我们如何在将数据输入神经网络之前准备数据。

在本例中,我们有三个样本和三个特征{ F1 、 F2 、 F3 },以及一个目标。 F1 为分类特征,而 F2 和 F3 为数值特征。
我们将为 F1 的每个模态 X 创建一个新特征 F1_X ,如果 F1 == X 为 1,否则为 0。
转换后的样本将被写入一组(特征名,特征值)。
例如:
第一个样本- > {( F1_A ,1)、( F2 ,0.3)、( F3 【T10,1.3)}
第二个样本- > {( F1_B ,1)、( F2 ,0.4)、( F3 ,0.9)}
第三个样本- >
特征名称将被送入嵌入层,然后与特征值相乘。
型号:
这里使用的模型是一系列多头注意力块和逐点前馈层。在训练时,我们也使用注意力集中跳过连接。多头注意力块允许我们对特征之间可能存在的交互进行建模,而注意力集中跳过连接允许我们从特征嵌入集合中获得单个向量。

简化模型-作者图片
预训练:
在预训练步骤中,我们使用完整的未标记数据集,我们输入特征的损坏版本,并训练模型来预测未损坏的特征,类似于在去噪自动编码器中所做的。
监督训练:
在训练的监督部分,我们在编码器部分和输出之间添加跳过连接,并尝试预测目标。

简化模型-作者图片
实验:
在下面的实验中,我们将使用四个数据集,两个用于回归,两个用于分类。
- Sarco :约有 50k 个样本,21 个特征,7 个连续目标。
- 在线新闻:约有 40k 个样本,61 个特征,1 个连续目标。
- 成人普查:约有 40k 个样本,15 个特征,1 个二元目标。
- 森林覆盖:约有 50 万个样本,54 个特征,1 个分类目标。
我们将比较预先训练的神经模型和从零开始训练的神经模型,我们将关注低数据状态下的性能,这意味着几百到几千个标记样本。我们还将与一种流行的梯度增强实现进行比较,这种实现叫做 lightgbm 。
森林覆盖:

成人人口普查:

对于这个数据集,我们可以看到,如果训练集小于 2000,预训练是非常有效的。
在线新闻:

对于在线新闻数据集,我们可以看到预训练神经网络非常有效,甚至超过了所有样本大小 500 或更大的梯度提升。

对于 Sarco 数据集,我们可以看到预训练神经网络非常有效,甚至超过了所有样本大小的梯度提升。
附注:重现结果的代码
重现结果的代码可从这里获得:https://github.com/CVxTz/DeepTabular
使用它你可以很容易地训练一个分类或回归模型->
**import** pandas **as** pd
**from** sklearn.model_selection **import** train_test_split**from** deeptabular.deeptabular **import** DeepTabularClassifier**if** __name__ == **"__main__"**:
data = pd.read_csv(**"../data/census/adult.csv"**) train, test = train_test_split(data, test_size=0.2, random_state=1337) target = **"income"**num_cols = [**"age"**, **"fnlwgt"**, **"capital.gain"**, **"capital.loss"**, **"hours.per.week"**]
cat_cols = [
**"workclass"**,
**"education"**,
**"education.num"**,
**"marital.status"**,
**"occupation"**,
**"relationship"**,
**"race"**,
**"sex"**,
**"native.country"**,
] **for** k **in** num_cols:
mean = train[k].mean()
std = train[k].std()
train[k] = (train[k] - mean) / std
test[k] = (test[k] - mean) / std train[target] = train[target].map({**"<=50K"**: 0, **">50K"**: 1})
test[target] = test[target].map({**"<=50K"**: 0, **">50K"**: 1}) classifier = DeepTabularClassifier(
num_layers=10, cat_cols=cat_cols, num_cols=num_cols, n_targets=1,
) classifier.fit(train, target_col=target, epochs=128) pred = classifier.predict(test) classifier.save_config(**"census_config.json"**)
classifier.save_weigts(**"census_weights.h5"**) new_classifier = DeepTabularClassifier() new_classifier.load_config(**"census_config.json"**)
new_classifier.load_weights(**"census_weights.h5"**) new_pred = new_classifier.predict(test)
结论:
已知无监督预训练可以提高计算机视觉或自然语言领域中神经网络的性能。在这篇文章中,我们证明了它在应用于结构化数据时也可以工作,使其与其他机器学习方法(如低数据区的梯度推进)相竞争。
训练效率使用自定义数据集检测对象模型
在具有不同类别数量的自定义对象检测任务上训练和使用 EfficientDet 的教程。我们还在博客上发布了 如何在你自己的数据集 上训练效率。
-
- *注:YOLOv5 已出版。如果您特别是为了效率而来到这里,请为效率而停留。否则考虑在 Colab 中运行 YOLOv5 PyTorch 教程。在几分钟内,您将拥有一个基于自定义数据的高性能、训练有素的 YOLOv5 模型。训练 YOLOv5 。
谷歌大脑团队最近发表了 EfficientDet,重新思考卷积神经网络的模型缩放。在本帖中,我们提供了一个教程,教你如何在你自己的数据上训练和使用 EfficientDet,使用不同数量的类。
原文:请看这篇关于如何训练效率的博文。
如果你想直接进入代码实现,跳到我们的 EfficientDet Training Colab 笔记本。Colab 可以免费使用,并提供了一个配有 GPU 计算资源的 python 编程环境。
在我们已经看到的任务中(截至 2020 年 4 月), EfficientDet 在对象检测模型架构中以最少的训练周期实现了最佳性能,使其成为高度可扩展的架构,尤其是在计算能力有限的情况下。这与 EfficientDet 作者发表的结果一致。

EfficientDet 在速度和准确性方面都非常高效( Source )
EfficientDet 是 EfficientNet 的对象检测版本,基于 EfficientNet 在图像分类任务中取得的成功。EfficientNets 来自一系列模型,这些模型在基准任务上实现了高性能,同时控制了许多效率参数,如模型大小和 FLOPS。该网络以一系列型号 d0-d7 交付,基本型号被认为比型号更小的 YOLOv3 性能更好(不久将推出更多)。
在这篇文章中,我们探索了在自定义数据集上实现 EfficientNet 的 PyTorch,演示了如何对自己的数据集做同样的事情。
我们的示例数据集
我们的数据集包含棋盘上 292 个棋子的图像。每个棋子都标有一个描述棋子类别{白骑士、白卒、黑皇后……}的边界框。我们的自定义数据集总共有 12 个类,这与进行培训的 COCO 中的类的数量不匹配。别担心!模型架构将无缝地适应您的定制数据集包含的类的数量。

roboflow.ai 公共数据集中的标签图像
准备数据
直接从数据收集到模型训练会导致次优的结果。数据可能有问题。即使没有,应用图像增强也会扩展数据集并减少过度拟合。
为对象检测准备图像包括但不限于:
- 验证您的注释是否正确(例如,图像中没有任何注释超出框架)
- 确保图像的 EXIF 方向正确(即图像在磁盘上的存储方式不同于您在应用程序中查看的方式,查看更多信息)
- 调整图像大小并更新图像注释以匹配新调整的图像大小
- 各种可以提高模型性能的颜色校正,如灰度和对比度调整
- 格式化注释以匹配模型输入的需求(例如,为 TensorFlow 生成 TFRecords,或者为 YOLO 的某些实现生成一个平面文本文件)。
与表格数据类似,清理和扩充图像数据比模型中的架构更改更能提高最终模型的性能。
Roboflow Organize 专为无缝解决这些挑战而构建。事实上,Roboflow Organize 将您需要编写的代码减少了一半,同时为您提供了更多的预处理和增强选项。
对于我们特定的国际象棋问题,我们已经预处理过的国际象棋数据可以在 Roboflow 上获得。
要么将此数据集下载到您的免费 Roboflow 帐户,要么创建 COCO JSON 格式的下载。

在 Roboflow 的数据集上点击“下载”允许我们选择任何注释输出格式。
在选择了我们的注释格式之后,Roboflow 提供了一个 curl 脚本(“Show Download Code”),我们可以在其中访问我们的数据集。
然后,我们可以使用这个 curl 脚本将数据导入到我们正在使用的 Colab 笔记本中。Colab 是一个由 Google 提供的 Jupyter 笔记本 python 编程环境,提供免费的 GPU 使用。Colab 可以免费启动,但是如果您的笔记本闲置 15 分钟左右,可能会超时。
跳到我们的 EfficientDet Colab 笔记本。
一旦我们的数据下载完毕,我们将检查文件夹结构。Coco json 数据来自我们在 Roboflow 中设置的数据中确定的训练、验证和测试分割。检查 train 文件夹,我们看到我们的数据以一组图像和一个注释文件的形式保存下来。然后,我们以我们的模型所期望的方式创建文件结构,但是不需要额外的数据清理!
培养
对于培训,我们通过 signatrix 导入了 EfficientDet 的 pytorch 实现。我们的实现使用 EfficientDet-d0 的基本版本。我们从 EfficientNet 基础主干进行训练,不使用网络检测器部分的预训练检查点。我们在训练集中训练了 20 个纪元。实现自然地适应训练类的数量,这与 TensorFlow 中最初的网络版本相比是一个很好的对比。

训练收敛:这个网络的自动性质甚至为你调整学习速度!
推理
在训练期间,我们的模型保存。onnx 文件,可以在推理时轻松调用。我们调用这些文件来设置一个推断检测器,并简单地将一个图像、我们的类列表和一个预测阈值传递给我们的推断器。预测阈值可以根据您的使用情况动态调整,以控制精确度和召回率。
我们见证了快速的推理时间,根据一些测试图像,看起来网络很快适应了我们的自定义图像检测问题!

EfficientDet 模型似乎很快就推广到了国际象棋
减轻我们的体重
我们输出训练过的模型。onxx 文件到 Google Drive 以备将来使用。您可以简单地将这些文件拉回来,并为您的应用程序重新构建推理器!
在笔记本的底部,我们提供了一个示例,说明如何将训练好的模型权重拉回来,并在您的应用程序中使用它们进行推理。在以后的文章中,我们将提供更多关于如何在您的应用程序中使用 EfficientDet 的细节。
这就是您所拥有的——一种快速而简单的方法,可以根据您自己的数据,针对您自己的定制用例,使用不同数量的类,开始构建 EffienctDet 原型。
后续步骤
在未来的博客文章中,我们将对 EfficientDet 模型和 YoloV3 进行更细致的评估,包括训练时间、模型大小、内存占用和推理时间。我们还计划分解 EfficientDet 的架构,以便更好地理解魔法是如何发生的。
在亚马逊 SageMaker 上训练机器学习模型
说到 SageMaker
短暂的集群、实验、可视化等等

用 SageMaker 实验可视化深度学习模型
现在是午夜。你已经花了几个小时来微调你的脚本,并且你正在赶在明天的截止日期之前把它放到服务器上。你正在你的大规模 for 循环中构建朴素贝叶斯、逻辑回归、XGBoost、KNN 和任何模型。您终于解决了本地机器上的问题,并准备扩展您宝贵的脚本,但是当它开始运行时,您会看到…到底发生了什么?随机打印报表?你怎么知道它起作用了?如果它坏了你会怎么做?你怎么知道你的模特在做你想让她们做的事?
现实是,你不需要单干。有成千上万的其他数据科学家和机器学习工程师与你走在同一条路上,幸运的是,在你追求目标的过程中,你可以利用桌面上的相当多的技术。
在这里,我将带你在 Amazon SageMaker 上训练机器学习模型,这是一个完全托管的解决方案,用于构建、训练和部署机器学习模型,由 Amazon Web Services 精心开发。我们将介绍如何在 SageMaker 上引入您自己的模型,用调试器分析培训作业,用实验管理项目,以及在多个 EC2 实例上扩展作业。
让我们跳进来吧!谁知道呢,在本教程结束的时候,你甚至可以用你自己的巧妙的可视化来展示它。
建立亚马逊 SageMaker 工作室

Studio 将开发与计算分离
首先,让我们来设置您的环境。SageMaker Studio 是一个完全集成的机器学习 IDE。它将开发与计算分离开来,让您在维护 IDE 的同时,轻松地单独修改和配置 EC2 实例。你可以用 IAM 或者 SSO 凭证设置工作室,更多详情请点击这里。
克隆存储库
接下来,为 SageMaker 示例克隆 Github 存储库。打开 Studio,创建一个新的终端,然后运行这个命令。
git clone [https://github.com/awslabs/amazon-sagemaker-examples.git](https://github.com/awslabs/amazon-sagemaker-examples.git)
接下来,导航到这个目录:Amazon-sage maker-examples/sage maker-debugger/mnist _ tensor _ plot
然后打开笔记本!

使用 SageMaker 调试器可视化深度学习模型
让我们一起来解决这个问题。马上,确保您添加了一些依赖项。
! python -m pip install plotly
! python -m pip install smdebug
!pip install sagemaker
!pip install awscli
!pip install nbformat==4.2.0
一旦您安装好这些,我们就可以开始您的工作了!

这些第一行非常普通,你会在 SageMaker-Examples 中看到它们中的大部分。我们正在导入 SageMaker Python SDK ,然后指向新的 SageMaker 调试器库。这既有一个调试器钩子配置,也有一个集合配置。这两样我们都需要。
接下来,让我们设置我们的估计!

评估员是我们在 SageMaker 上配置培训工作的方式。您已经得到了具有运行作业权限的执行角色,然后是 EC2 实例配置。看到这有多小了吗?只需要 2 行代码来指定你需要一个ml . M4 . xlarge。****
接下来,我们要指向我们的 入口点脚本 。这是您的示例附带的文件;它使用 MXNet 估算器 来完成 Docker 文件的抽象。
记住,只要你能在 Docker 上运行你的代码,你就能在 SageMaker 上运行。
在这里,我们使用所谓的脚本模式,或者使用短暂集群在 SageMaker 上扩展你自己的模型脚本的能力。这些是 EC2 实例,它们在您的作业开始时加速,在您的作业结束时减速。这使得它们更易于扩展、保护和支付。
定义了模型之后,连同框架的版本,我们准备添加 调试器钩子配置 。这告诉 SageMaker that)我们想给我们的工作添加调试器,以及(B)我们想让它做什么。在这种情况下,我们抓住所有的张量。
SageMaker 调试器去神秘化

不要被名字误导,SageMaker 调试器是非常先进的解决方案!网络是,你要在你的深度学习模型中收集张量。然后,你要分析那些。SageMaker 调试器 自带 18 条规则,你可以通过零代码修改 将这些应用到你的深度学习模型中。也就是说,只要你在使用 SageMaker 深度学习容器,你就不需要修改你的脚本就可以开始使用 SageMaker 调试器。
启动您的培训工作
现在,适合你的模型,我们出发去比赛!

在亚马逊 SageMaker 上创建一个新的培训职位
如果你是一个精明的 AWS 用户,你会知道回到 AWS 控制台调查你的培训工作。导航至 SageMaker 登录页面左侧的培训,然后选择培训工作。****

这将打开你工作的视野!您将看到从开始的状态显示,以及关于您的数据在哪里、您正在使用的图像、您的模型工件要去哪里以及日期和时间的所有细节。
请记住,默认情况下,您在 SageMaker 中的所有作业都会被存储和记录。
这意味着不仅可以在作业运行时监控作业,还可以返回到您停止的地方,甚至是几个月前,重新开始一个模型,这应该非常简单。
现在我们去找那些张量。
将张量从你的模型复制到你的本地工作室实例中
接下来,运行一个快速命令来获取存储张量数据的 S3 路径。

接下来,运行命令将数据从 S3 复制到本地 Studio 实例。

我不得不承认,这两行很可能是我在 AWS cloud 上最喜欢的命令。它们如此简单,却又如此有效。!aws s3 cp和!aws s3 sync我们所做的就是将我们的数据从 S3 转移到 Studio 上的代码中。它非常有效。
可视化你的深度学习模型

现在,有趣的部分来了。让我们用一个叫做tensor_plot的软件包来建立一个你的网络的交互式可视化!
我们正指向一个名为debug-output的文件夹,你要确定那是你从 S3 复制张量的地方。

使用 SageMaker 调试器为任何深度学习模型创建交互式视觉
相当牛逼!记住, 你可以为你在 SageMaker 上训练的任何深度学习模型设置这种可视化。 你只需要添加那个调试钩子配置。也可以在 XGBoost 模型上运行这个!
使用 SageMaker 实验管理项目
接下来,我们将学习如何使用 SageMaker 实验来管理我们的项目。
我将带您浏览一下这个笔记本中的一些代码片段,可以在这里找到。
[amazon-sagemaker-examples](https://github.com/awslabs/amazon-sagemaker-examples)/[sagemaker-experiments](https://github.com/awslabs/amazon-sagemaker-examples/tree/master/sagemaker-experiments)/mnist-handwritten-digits-classification-experiment.ipynb

用 SageMaker 实验管理机器学习项目
首先,了解这在 SageMaker 中是如何组织的很有帮助。你有实验、试验和试验组件。实验是你正在进行的总体学习项目,就像你的计算机视觉解决方案,或者你正在构建的预测模型。

SageMaker 实验故障
这个实验将被分解成多个试验,每个试验将大致对应一个培训工作。因此,如果你正在为一个分类项目测试 XGBoost、KNN、逻辑回归和 SVM,你会把每个尝试列为一个试验。当然,您可以为这些试验指定您感兴趣的对象指标。
接下来,每个试验将有一个试验组件。这些将是那个试验的步骤,比如你应用的预处理技术, SageMaker 处理 你运行的作业,或者训练作业。
这就是你如何追溯你的结果。在你找到一个合理的模型后,只要它通过实验被追踪,你可以毫不夸张地 跟随那个模型 的血统在下游再造它。
这里有一些代码片段,以及漂亮的实验图像!

为 SageMaker 实验安装依赖项
首先,确保安装了依赖项。
接下来,设置您的导入,以及您的 SageMaker 凭证。
现在,让我们通过 SDK 创建一个实验!一些需要注意的事情。(1)这实际上只有 4 行代码。(2)你有一次命名的机会。如果你像我一样,尝试了几次,得到了你真正想要的版本,你会想要开发一个命名版本策略。
version_num = ‘v1’my_experiment = Experiment.create(
experiment_name=”my-model-{}”.format(version_num),
description=”My first experiment”,
sagemaker_boto_client=sm)
接下来,让我们给你的实验添加预处理步骤。这里,我们将记录我们设置的参数。在这种情况下,这就是我们将用于标准化的平均值和标准偏差。
请注意,您可以在这里记录您正在设置的参数。运行** SageMaker 自动驾驶 作业时,可以看到如何记录 SageMaker 处理作业,将您的特征工程步骤与模型训练关联起来。**

用 SageMaker 实验记录参数
按照笔记本创建评估者。在这个例子中,你实际上是要遍历一个潜在隐藏层的列表来构建你的网络。
一旦你创建了评估器,下面是我们如何添加一个实验配置。
estimator.fit(
inputs={‘training’: inputs},
job_name=cnn_training_job_name,
experiment_config={
“TrialName”: cnn_trial.trial_name,
“TrialComponentDisplayName”: “Training”,
}
在调用estimator.fit()时,您添加了将此培训工作与您的实验相关联所需的其余组件,即将其作为试验记录。
现在,单击左侧的实验选项卡,然后右键单击您的实验,以在试验组件详细视图中查看。**

SageMaker Experiments 为试验组件提供了视觉效果
相当花哨!请记住,每次您在 Studio 中创建一个实验,包括使用 SDK,它都会显示在 Experiments 选项卡中。要查看试用组件列表,只需点击右键。

接下来,突出显示其中一个试验,右键单击在试验详情中打开。
这将为您提供针对具体工作的培训。
一旦您进入试验细节视图,您就可以通过直接引用培训工作和预处理步骤来检查您项目的血统。****
除此之外,您可以创建图表来分析您的模型在特定试验中的性能。

使用 SageMaker 实验可视化结果
就这样结束了!我希望你喜欢这个教程。请记住,您可以在我们的 Github 页面上找到更多示例 ,或者在这里找到我们的其他 开发者资源。****
直接从 GitHub 训练 ML 模型
在这篇文章中,我将向您展示如何直接从 GitHub 训练机器学习模型。我最初在 GitHub Satelite 2020 上展示了这个研讨会,你现在可以观看录音。
我们都知道软件 CI/CD。你编码,你构建,你测试,你发布。与机器学习类似,但也有很大不同。你从数据开始,你将做一些数据验证,数据处理和数据分析。然后,在远程 GPU 或您拥有的特定集群上训练该模型。运行模型验证,部署模型,进行预测。一旦您的模型部署到生产中,您就可以监控预测。如果模型显示出性能漂移的迹象,您甚至可以基于此重新训练模型。我们在这篇文章中建议的是一种直接从 GitHub 训练模型的简单方法。换句话说,这是一个 GitHub 加 MLOps 的解决方案。
什么是 MLOps?
MLOps(“机器学习”和“运营”的复合)是数据科学家和运营专业人员之间进行协作和沟通的实践,以帮助管理生产机器学习生命周期。类似于软件开发领域的 DevOps 术语,MLOps 希望提高自动化程度并改善生产 ML 的质量,同时还关注业务和法规要求。MLOps 适用于整个 ML 生命周期——从与模型生成(软件开发生命周期、持续集成/持续交付)、编排和部署的集成,到健康、诊断、治理和业务指标。
如何从 GitHub
训练你的 ML 模型为了建立这种直接从 GitHub 训练模型的能力,我们使用了 GitHub Actions——一种自动化开发工作流的方式,下面是它的工作方式:一旦你写好了代码,你就把它推到 GitHub 的一个特定分支。你创建一个拉取请求,并在你的 PR 中评论“ /train ”之后,它将触发使用 cnvrg.io CORE 的模型训练,这是一个你可以在你自己的 Kubernetes 上免费部署的社区 ML 平台。就这样,该命令将自动提供资源并启动培训管道。

此模型训练管道在远程 GPU 上训练 TensorFlow 模型。它正在 Kubernetes 上进行模型部署,并最终将其指标发布回 GitHub。

上面是一个我们构建的 GitHub 动作如何工作的真实例子。所以,如你所见,我把新代码放进了 GitHub。然后 leah4kosh,我同事对我的拉请求做了 /train 评论,触发了模型训练,把结果推回到这个拉请求。

这是 cnvrg.io 中训练管道的样子。正如你所看到的,在管道的末端,它将结果推回到 GitHub 在执行期间,cnvrg.io 跟踪所有不同的模型、参数和指标,并将其发送回触发模型训练的同一个 PR。
为了构建这个 GitHub 动作,我们使用了 ChatOps 来跟踪和监听对 pull 请求的不同评论。我们使用 Ruby 安装 cnvrg CLI ,然后使用 cnvrg CLI 训练机器学习管道。
现在你知道了!这就是你如何直接从 GitHub 训练一个 ML 模型。
不平衡数据的训练模型
了解阶级不平衡,并学习如何规避它

Elena Mozhvilo 在 Unsplash 上拍摄的照片
类别不平衡是指数据中不同类别的样本数量不同。在机器学习的实际应用中,经常会遇到具有不同程度类别不平衡的数据集:从中度不平衡(例如,医学图像中 10%被诊断患有疾病,90%没有)到极端不平衡(例如,工厂中的异常检测,其中可能有 1/10,000 批次失败)。
大多数根据不平衡数据训练的模型会偏向于预测较大的类别,并且在许多情况下,可能会完全忽略较小的类别。当训练数据中存在类别不平衡时,由于较大类别的先验概率增加,机器学习模型通常会对较大类别进行过度分类。
结果,属于较小类的实例通常比属于较大类的实例更经常被错误分类。在许多用例中,例如医疗诊断,这与我们想要实现的正好相反,因为罕见类别(例如疾病)是正确预测的最重要类别是很常见的。为了实现这一点,我们需要在训练模型时以某种方式处理类的不平衡。
今天我们将复习:
- 阶级失衡的症状是什么;
- 类别不平衡如何影响模型性能;
- 处理不平衡数据的可能解决方案是什么,以及每种方法的优缺点;
- 在这种情况下,评估模型时首选哪些度量。
识别阶层失衡
通过查看数据中目标类的分布,很容易识别类不平衡。在 Peltarion 平台中,直方图显示分布,位于数据集视图中每列的上方。
如果您注意到您希望模型预测的列的非均匀分布,那么您有一个不平衡的类问题,需要采取一些措施来处理它。

图 1:不平衡的阶级分布的例子。图片来自 Peltarion 平台。

图 2 :(相当)平衡的阶级分布的例子。图片来自 Peltarion 平台。
它如何影响模型性能
在每个类别的比例显著不同的情况下进行分类是有问题的,因为预测模型通常可以通过简单地“猜测”所有新的示例属于在训练数据中观察到的最常见的类别来达到高精度。由于准确性是我们通常优化的目标——通常通过分类交叉熵损失间接实现——我们经常会发现琐碎的多数猜测模型。例如,如果只有 5%的房屋受到水毁的影响,我们可以构建一个模型,猜测没有房屋受到水毁,但仍然获得 95%的准确性。虽然 95%是一个令人愉快的高比例,但该模型可能不会达到预期效果,即很好地区分受到水损害的房屋和没有受到水损害的房屋。
从神经网络的角度来看,这可以通过以下替代方式来理解。如果在上述 95/5 水损害案例中,我们的批量为 20,则平均只有一个样本来自阳性类别。所述批次的梯度更新将“看到”19 个负样本和一个正样本,使得一个正样本很难影响梯度的方向。
如何解决阶层失衡
有不同的方法可以用来处理阶级不平衡的问题。它们通常可以分为数据级和算法级方法。
数据级方法修改训练分布,降低不平衡程度。平均而言,这使得梯度更新能够从每个类中“看到”相似数量的示例。
- 欠采样丢弃从较大类别中随机选择的样本。这会导致信息丢失,因为一些样本被从训练数据中移除,并且模型不能利用这些样本中包含的信息。
- 过采样从较小的类中复制随机选择的样本,这导致多次显示学习算法完全相同的样本。这有过度拟合这些稀有样本的风险。
- 或者,您可以将数据扩充与过采样结合使用,以降低过度拟合的风险。数据扩充包括通过模仿观察到的类别分布来构建合成训练样本。对于图像,你可以使用这些技术进行放大。
算法级方法调整学习过程,以便在训练期间增加较小类的重要性。一种常见的方法是在损失函数中使用类权重。
在模型训练期间,计算每批的总损失,然后在减少该损失的方向上迭代更新模型参数。损失是实际值和模型预测值之间的误差,是该批次中所有样本的总和。默认情况下,每个样本同等计入总损失。然而,使用类别加权,总和被调整为加权总和,使得每个样本对损失的贡献与样本的类别权重成比例。
这样,属于较小类别的样本可以对总损失做出较大的贡献。这反过来意味着,当执行参数更新时,学习算法将更加关注它们。回头参考上面给出的以神经网络为中心的解释,正类的高类权重将为批中的单个正样本提供影响梯度更新的“能力”。
一种常见的方法是分配与训练数据中的类频率成反比的类权重。平均而言,这相当于在梯度更新上给予所有类别同等的重要性,而不管我们从训练数据中的每个类别获得多少样本。这反过来防止模型简单地基于它们增加的先验概率对较大的类进行过度分类。
Peltarion 平台支持类别加权,即根据上述策略设置权重。这是您需要做的来启用它:在建模视图中,点击您的目标块,然后选中“使用类权重”复选框。就是这样!
如何衡量绩效
在处理不平衡数据时,我们不建议将分类准确性作为主要评估标准。当测试在非常不平衡的数据上训练的分类模型时,观察到高的评估准确度并不罕见。在这种情况下,精确度仅仅反映了底层的类别分布。你要避免这种情况!
在这种情况下,区分微观平均和宏观平均是有用的。这些是衡量绩效的非常有用的概念。所以我们开始了【1】:
- 某个度量的微平均值将集合所有类的贡献来计算平均度量。
- 某个度量的宏观平均值将为每个类独立计算度量,然后取平均值。
因此,微观平均赋予每个样本同样的重要性,这意味着,样本数量越多,相应类别对最终得分的影响越大,从而有利于大多数类别。相反,宏平均值赋予每个类相同的重要性,因此可以更好地反映模型的性能——考虑到您的目标是拥有一个对所有类(包括少数类)都性能良好的模型。
除了准确性,研究社区还经常使用其他衡量标准来评估基于不平衡数据训练的模型,即精确度、召回率和 F1 分数【T0【2】。如前所述,优先考虑宏观平均精度、召回率和 F1 分数,而不是微观平均分数。特别是对于二元分类问题,利用 ROC-AUC 分数或者更适合不平衡数据集的 PR-AUC 分数【3】。在 Peltarion 平台中,您可以在评估视图中检查所有这些测量,以评估您的模型。
最后,混淆矩阵是这类问题的基本评估工具。参见下面的混淆矩阵,其评估了在具有来自不同音乐风格的歌词的数据集上训练的 BERT 分类模型,用于上面所示的直方图:在图 3 中,模型在原始数据上训练(其明显不平衡),而在图 4(平台外)中,过采样用于在模型训练之前平衡类别。请注意,不平衡数据中的大多数类别“摇滚”是如何主导预测的。在对相当平衡的数据训练模型之后,混淆矩阵呈现出更强的对角线,表明处理数据不平衡提高了整体分类性能。

图 3:不平衡类别分布的混淆矩阵。图片来自 Peltarion 平台。

图 4 :(相当)平衡的类分布的混淆矩阵。图片来自 Peltarion 平台。
总结
现在,您已经准备好:
- 向世界解释为什么在不平衡数据上训练 ML 模型往往不是小事;
- 确定你自己的数据集有多倾斜;
- 调整你的数据或你的学习算法以避免不平衡;
- 最后,采用正确的评估方法来比较您的模型。
干杯!
参考文献
[1] M. Sokolova,G. Lapalme,分类任务的性能测量的系统分析 (2009),信息处理&管理
[2] H .何, E. A .加西亚,从不平衡数据中学习 (2009),IEEE 知识与数据工程汇刊
[3] J. Davis,M. Goadrich,精确召回与 ROC 曲线的关系 (2006),ICML 06:第 23 届机器学习国际会议论文集
带进度条的培训模型
如何跟踪您的 ML 实验的进展

tqdm是一个用于添加进度条的 Python 库。它允许您配置并显示一个进度条,其中包含您想要跟踪的指标。其易用性和多功能性使其成为跟踪机器学习实验的完美选择。
我将本教程分为两部分。我将首先介绍tqdm,,然后展示一个机器学习的例子。对于本文中的每个代码片段,我们将从 Python 的time库中导入sleep函数,因为它将让我们减慢程序速度以查看进度条更新。
from time import sleep
Tqdm
可以用pip install tqdm安装 tqdm。这个库附带了各种迭代器,每个迭代器都有我将要介绍的特定用途。
tqdm是默认的迭代器。它接受一个迭代器对象作为参数,并在遍历该对象时显示一个进度条。
输出是
100%|█████████████████████████████████| 5/5 [00:00<00:00, 9.90it/s]
您可以看到带有9.90it/s的漂亮输出,意味着每秒 9.90 次迭代的平均速度。迭代的“it”可以被配置成其他的东西,这就是我们将在下一个例子中看到的。
trange遵循与 Python 中的range相同的模板。例如,给trange迭代次数。
Proving P=NP: 100%|████████████| 20/20 [00:02<00:00, 9.91carrots/s]
在这个例子中,你可以看到我们添加了一个(笑话)描述,描述了我们正在做的事情以及每次迭代的单元。
在执行过程中更新进度栏
tqdm有两个方法可以更新进度条中显示的内容。
要使用这些方法,我们需要将tqdm迭代器实例赋给一个变量。这可以通过 Python 中的=操作符或with关键字来完成。
例如,我们可以用数字i的除数列表来更新后缀。让我们用这个函数来得到除数的列表
这是我们带有进度条的代码。
如果你觉得自己是一个杰出的 Python 程序员,你可以像这样使用with关键字
使用with会自动调用块末尾的pbar.close()。
这里是显示在i=6的状态。
Testing even number 6: 70%|██████████████▋ | 7/10 [00:03<00:01, 1.76carrots/s, divisors=[1, 2, 3]]
跟踪损失和准确性
在本节中,我们使用 PyTorch 编写的神经网络,并使用tqdm对其进行训练,以显示损失和准确性。这是模型
这是一个简单的感知器模型,我们可以用它来处理和分类 MNIST 数据集中的数字图像。以下加载 MNIST 数据集的代码灵感来自于 PyTorch 示例。
我们刚刚加载了数据,定义了模型和设置,现在可以运行训练实验了。
我再次使用sleep功能暂停程序,这样我们就可以看到进度条的更新。正如你所看到的,我们只是应用了我们之前在这里学到的东西,特别是用tepoch.set_postfix和tepoch.set_description让你更新进度条显示的信息。下面是程序运行时的输出截图
Epoch 1: 15%|▉ | 142/937 [00:16<01:32, 8.56batch/s, accuracy=89.1, loss=0.341]
这给了我们如何在实际中使用tqdm的想法。
结论
你可以用tqdm实现更多,比如让它适应 Jupyter 笔记本,精细配置进度条更新或嵌套进度条,所以我推荐你阅读文档了解更多:https://github.com/tqdm/tqdm
感谢您的阅读!
原载于 2020 年 10 月 12 日https://adamoudad . github . io。
训练网络识别 X 射线与肺炎

迁移学习如何在有限的数据和时间内领先
编者按: 走向数据科学 是一份以数据科学和机器学习研究为主的中型刊物。我们不是健康专家或流行病学家,本文的观点不应被解释为专业建议。想了解更多关于疫情冠状病毒的信息,可以点击 这里 。
2020 年,新冠肺炎疫情爆发,整个世界陷入停滞。科学界一直致力于在潜在的治疗方法上取得医学突破。这已经成为与这种病毒快速传播的竞赛,这也是为什么我们看到有史以来进展最快的临床试验。世界各地的数据科学家一直在通过利用数据来帮助这一过程。但是新冠肺炎患者的数据收集是一个持续的过程。

来源:吉菲。
有限的时间和数据是当今的一个挑战,迁移学习似乎是一个很好的解决方案。它将使我们能够使用根据具有相似结构的数据预先训练的模型。例如,使用已经在患有类似疾病的患者身上预先训练的模型。这也让我们有机会利用深度神经网络的学习能力,如果从头开始训练,将需要大量的数据和计算资源。
关于数据
数据来源于 Kaggle 。它包含从广州市妇女儿童医疗中心的 1 至 5 岁儿童患者回顾队列中选择的胸部 X 射线图像(前-后)。目标是将这些 x 光图像分类为肺炎正常或阳性。

数据集目录结构。来源:作者。
使用 Kaggle API 将全部数据直接导入 Google Colaboratory。所有的分析都是在相同的 GPU 上完成的。代码可以在这里找到。

从训练集中随机选择的患者的 x 射线。来源: Kaggle 。
了解剩余网络或 resnet
残差网络(ResNets)是卷积网络,它是作为使用“普通”卷积网络时通常面临的退化问题的解决方案而引入的。网络使用“跳过”连接来跳过深层网络的层。

网络就像天桥连接。来源:照片由贾里德·默里在 Unsplash 上拍摄
跳过连接也可以被视为将先前层的输出添加到后面层的身份映射或功能。在前向传播中,ResNets 将被跳过的子网的输出(称为“残余”)推至零(T15)。这使得真实输出几乎等于从其开始跳跃连接的子网的输出,从而由于更深的架构而减少了可观的信息损失。
ResNets 的一个额外的好处是,在反向传播期间,跳跃连接也传播梯度流。跳过具有非线性激活函数的层使得初始梯度(来自较高层)更快地到达较早的层,这解决了消失梯度的问题。
本项目实施了 ResNet50。
ResNet 50 架构:ResNet 50 是一个 50 层的 ResNet,使用了“瓶颈设计”来提高计算效率。瓶颈设计表明他们使用 3 层堆栈。这三层分别是 1x1,3x3,1x1 卷积。两侧的 1x1 回旋用于减小然后恢复尺寸。因此,3x3 层变得像一个具有较小输入&输出尺寸的瓶颈。ResNet50 拥有超过 2300 万个可训练参数。该网络已经在由 1000 个类组成的 ImageNet 数据集上、在 128 万幅图像的训练数据集上进行了预训练,在 50k 幅图像上进行了验证,并在另外 100k 幅图像上进行了测试。
【跳过】到建模…

资料来源: GIPHY
调整数据集的预训练模型
这些数据呈现出二元分类问题,具有以下两个类别:正常 & 肺炎。由于最初的 ResNet50 网络用于将图像分类到 1000 个类别中的一个,为了利用预先训练的架构及其权重,该网络的顶部被移除。因此,最初的全连接图层被替换为全局平均池图层,随后是全连接图层密集图层和输出图层。尝试了其他组合,但这给出了最好的测试集性能。

调整后的模型架构。来源:作者。
模型预处理
增加更多通道: 使用 Keras 中的图像数据生成器函数对图像进行预处理。数据的胸部 X 射线图像是灰度图像,其由单个通道组成。而 ResNet50 模型是在具有 3 个通道的 ImageNet 数据集的 RGB 图像上训练的。使用生成器函数的颜色模式参数,灰度图像被转换为具有 3 个通道。
更多图像变换: 此外,使用水平翻转、缩放、高度/宽度移动和剪切变换来增强训练集中的图像。ResNet50 预处理功能也应用于增强的训练图像、原始验证&测试图像。

训练集的 9 幅增强图像的样本。注意颜色的变化。来源:作者。
使用上述模型,达到的最佳测试精度为 83% !
进一步微调
- ****Keras 中批处理规范化的争论:一些文献表明,由于 Keras 中的批处理规范化层在训练和推理阶段的工作方式不同,它可能会造成准确性度量的差异。因此,通过冻结除 ResNet50 基础模型的批标准化层之外的所有层,模型被训练。然后训练所有剩余的未冻结层(批量标准化层&附加层)。
base_model = ResNet50(weights='imagenet',include_top=False,input_shape=(150,150,3))x = base_model.output #adding the resnet model# freezing all layers except the batch normalization layers
for layer in base_model.layers:
if isinstance(layer, BatchNormalization):
layer.trainable = True
else:
layer.trainable = False
- ****超参数的调整:为了改善初始模型中的缓慢收敛,尝试了 Adam 优化器的不同学习速率和β_ 1 值。选择 0.01 的学习率和 0.9 的β_ 1。查看批量大小,尝试了 2 的不同幂。32 的批量给出了最好的测试结果。
- ****自定义回调函数:此外,观察到当训练模型期间的验证损失低于 0.1 时,模型给出最佳测试精度。为了实现这一点,创建了一个自定义回调函数来训练模型,直到验证损失降到 0.1 以下,耐心参数为 2。
#early stopping with custom call back
class EarlyStoppingByLossVal(Callback):
def __init__(self, monitor=['val_loss'],patience=0, value=0.00001, verbose=0):
super(Callback, self).__init__()
self.monitor = monitor
self.value = value
self.verbose = verbose
self.patience = patience def on_train_begin(self, logs=None):
# the number of epoch the model has waited when loss is below the required value
self.wait = 0 def on_epoch_end(self, epoch, logs={}):
current = logs.get(self.monitor)
if current is None:
warnings.warn("Early stopping requires %s available!" % self.monitor, RuntimeWarning)
if current < self.value:
self.wait +=1
if self.wait >= self.patience:
if self.verbose > 0:
print("Epoch %05d: early stopping" % epoch)
self.model.stop_training = True
微调后达到的最佳测试精度为 93.75% 。AUROC 曲线为 0.98 。

最佳测试集性能的混淆矩阵和 ROC 曲线。来源:作者。
未来范围
同时,该模型在测试集上表现良好,增加了代表不同地区和人口统计学的患者数据,并且进一步的超参数调整可以改善模型结果。使用专门针对 X 射线图像预先训练的模型,如 ChestXNet 也可以给出更好的结果。这个项目的目的不是做出任何官方声明,而是帮助未来的研究。任何诊断都只能由专业医疗人员做出!

资料来源:吉菲。
TL;速度三角形定位法(dead reckoning)
根据世界卫生组织的消息,“重症新冠肺炎患者最常见的诊断是重症肺炎”。本文展示了一个与新冠肺炎相关的迁移学习的使用案例。一个预先训练的 ResNet 模型已被用于将患者的 x 光片分类为“正常”或感染了肺炎。
使用神经网络读取时钟
神经网络可以从模拟时钟图像中检测时间吗?

照片由壁纸照明弹
我对一个想法非常感兴趣,那就是使用神经网络从模拟时钟图像中读取时间。为了完成这个任务,我需要一个包含时钟图像的数据集,但是 web 上没有任何可用的数据集。另一种方法是从网上下载时钟图像,并手动标记它们。这是一个相当耗时的过程。
最后,我决定编写一个 python 脚本来生成动画时钟图像和它们各自的标签。以下是由脚本生成的时钟图像的几个示例。

生成的时钟图像
这些图像不像真实世界的时钟图像那样逼真,但如果能看到神经网络也能在这些图像上进行训练,那将是非常令人兴奋的。
以下是数据集的链接,可以在 Kaggle 上找到。
该数据集包含 50K 个生成的模拟时钟图像。
www.kaggle.com](https://www.kaggle.com/shivajbd/analog-clocks)
在设计神经网络之前,让我们看看如何从时钟中读取时间。
我们如何阅读时间?

一只钟
为了从时钟中读取时间,我们需要两个值,一个小时和一个分钟。一个小时可以取 0 到 11 之间的值,其中 0 只不过是 12 点。同样,一分钟可以取 0-59 之间的值。时针的长度小于分针。这就是我们区分分针和时针的方式。最重要的是,时钟中至少有一个标记(顶部 12 点)。
现在让我们设计一个可以类似地读取时间的神经网络。
设计神经网络
如你所知。我们的目标是将时钟图像输入神经网络,并从中获取时间值。因此,网络必须输出 2 个值,小时、分钟和分钟。让我们一个一个地介绍每个案例。
小时值
小时值可以从 0 到 11。我们可以把这看作是一个分类任务,我们总共有 12 个类。并且网络必须选择其中一个类别。
微小价值
分钟值可以从 0 到 59。这意味着有 60 个可能的值。明智的做法是将它视为一项回归任务。因为在这种情况下,我们希望尽可能接近地预测分钟值。
现在让我们看看下面的架构图,它解释了完整的网络架构。

时间阅读器神经网络
一开始,网络包含卷积层,它将从图像中提取有用的特征。在卷积层之上,有两个分支的全连接层。一个分支用于检测小时,一个分支用于检测分钟。
因为预测小时值是一项分类任务。在小时分支中将有 12 个输出节点。我们在输出节点的顶部应用了一个 Softmax 激活。
在分支中,只有一个输出节点激活了线性,因为在回归中我们只需要一个值。线性激活本质上就是不激活。这里我就不赘述分类回归的细节了。
整个网络准备就绪,可以从时钟图像读取时间。下面是在 Keras 中创建这个网络的代码。
Keras 中的神经网络
训练网络
我们已经设计了网络架构,并在 Keras 中创建了它。是时候把数据输入网络并训练它了。我们来快速讨论一下网络的输入和目标。
投入
我的时钟数据集包含(300300)大小的 RGB 图像。在将图像传送到网络之前,我将所有图像转换为灰度,并将尺寸缩小到(100100)。我这样做是为了在更少的内存中加载图像,并使训练更快。另一方面,由于分辨率低,我们可能会丢失一些信息。
目标
目标是小时和分钟的值。我将分钟值除以 60,使其保持在(0–1)的范围内。神经网络在小范围输出上表现更好。
培养
我已经在 40K 时钟图像上训练了 10 个时期的网络。在培训期间,我不断降低学习速度,增加批量。500 张测试图像的最终结果如下。
Hour, accuracy: ~99.00Minute, Mean Absolute Error: ~3.5
平均而言,网络能够读出接近 3.5 分钟的时间。不费吹灰之力就让人印象深刻。
下面是我的 Jupyter 笔记本的一个片段,显示了网络阅读时间。

网络阅读时间
以下是这个完整项目的 GitHub 链接。我还在回购中保存了训练好的模型。感谢阅读。
该软件使用卷积神经网络从时钟图像中读取时间。语言:Python 3.6…
github.com](https://github.com/shivaverma/Clock-Reader)
仅在 7 个细胞中使用 PyTorch 从头开始训练神经网络
神经网络
MNIST 手数字识别使用 PyTorch 在短短 7 个细胞使用神经网络(多层感知器)从零开始

用 PyTorch 进行 MNIST 手形数字识别
博客内容:
- 安装和导入模块
- 重新处理和加载数据集
- 设计模型
- 训练模型
- 可视化输出
安装:
先做第一件事。无论操作系统如何,只需运行下面的命令,该命令将安装运行下面代码片段所需的所有模块。如果你使用 anaconda,那么你也可以用 conda 命令安装它。
pip install torch torchvision numpy matplotlibconda install torch torchvision numpy matplotlib
导入语句:
import torch用于添加构建神经网络的所有必要模块,而torchvision用于添加其他功能,如数据的预处理和转换。numpy用于处理图像数组,matplotlib用于显示图像。
import torch
import torchvision
import torch.nn as nn
import torch.nn.functional as F
from torchvision import datasets, transforms
import matplotlib.pyplot as plt
from torch import optim
import numpy as np
%matplotlib inline
PyTorch 拥有transform模块,可以将图像转换为张量,并预处理每幅图像,使其标准化,标准偏差为 1。torchvison有内置的数据集MNIST手形数字,我将用它来进一步解释下面所有的代码片段。DataLoader是 PyTorch 模块,将图像和它对应的标签组合在一个包中。所以我们可以很容易地同时访问这两个东西。请注意,我们将batch_size添加为 64,以便在一次迭代中创建一批 64 个图像。
transform = transforms.Compose([
transforms.ToTensor(),
# transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])
trainset = torchvision.datasets.MNIST('~/.pytorch/MNIST_data/', train=True, transform=transform, download=True)
trainloader = torch.utils.data.DataLoader(trainset, batch_size=64, shuffle=True)

来源:男高音
要训练任何神经网络,首先我们必须了解图像的输入大小、输出类别的数量以及神经网络的隐藏层。因此检查trainloader.dataset.train_data.shape我们将得到 64,1,28,28,这表示 64 幅图像,高度和宽度为 28,通道 1 为灰度图像。
输入尺寸:所以输入尺寸是 784,是图像的高(28)和宽(28)的乘积。图像只有一个通道,所以不需要在输入尺寸中添加它。
输出大小:我们有从 0 到 9 的数字,因此共有 10 个可能的类别选项。因此,输出大小为 10
隐藏层:输入层和输出层之间的层基本上称为隐藏层。在我们的例子中,我们有一个 784 个节点的输入图像,输出大小为 10,因此在两者之间,我们添加了 128 和 64 层。因此,我们的网络将从 784 扩展到 128,再扩展到 64 到 10。
input_size = trainloader.dataset.train_data.shape[1] * trainloader.dataset.train_data.shape[2]
hidden_layers = [128,64]
output_size = 10

用于手数字 MNIST 分类的简单神经网络(多层感知器)
torchvision具有nn模块,该模块具有构建神经网络的所有功能。最初,将输入大小添加到第一个隐藏层,即 784 到 128,然后是 ReLU(激活函数)。从 128 到 64 具有相同的 ReLU 激活功能,而 64 到 10 在最后一层。为了得到概率分布,我们添加了最后一层LogSoftmax,维度= 1,因为我们有 64 个图像批次,所以它将在输出中给出 64x10 的结果。
为了计算神经网络的误差和错误,我们添加了NLLLoss交叉熵损失(负对数似然损失)作为标准(误差函数),优化器作为学习率为 0.003 的SGD(随机梯度下降)。
model = nn.Sequential(
nn.Linear(input_size, hidden_layers[0]),
nn.ReLU(),
nn.Linear(hidden_layers[0], hidden_layers[1]),
nn.ReLU(),
nn.Linear(hidden_layers[1], output_size),
nn.LogSoftmax(dim=1)
)
print(model)
criterion = nn.NLLLoss()
optimizer = optim.SGD(model.parameters(), lr=0.003)
培训:
现在,我们通过传递图像和相应的标签,成功地定义了模型及其训练模型的时间。但在此之前,我们将图像从 28x28 展平到 784x1,并将所有梯度设置为零,以训练模型的权重和偏差。
最后model(images)将训练模型,criterion将计算损失。loss.backward()用于反向传播,optimizer.step()将根据反向传播的权重和偏差更新权重。
我们把损失印在每个训练时期。只要确保你的训练损失会随着纪元的增加而减少。如果你没有在每个时期减少损失,那么你在代码中犯了一些错误。
epochs = 5
for e in range(epochs):
running_loss = 0
for images, labels in trainloader:
# Flatten the Image from 28*28 to 784 column vector
images = images.view(images.shape[0], -1)
# setting gradient to zeros
optimizer.zero_grad()
output = model(images)
loss = criterion(output, labels)
# backward propagation
loss.backward()
# update the gradient to new gradients
optimizer.step()
running_loss += loss.item()
else:
print("Training loss: ",(running_loss/len(trainloader)))
可视化:
在神经网络的预测阶段,我们将图像及其概率分布传递给可视化图像。ax1是任意数字的原始图像,ax2是概率分布。只需将 xlabel 和 ylabel 设置在 0–9 和地块标题之间。
def view_classify(img, ps):
ps = ps.data.numpy().squeeze()
fig, (ax1, ax2) = plt.subplots(figsize=(6,9), ncols=2)
ax1.imshow(img.resize_(1, 28, 28).numpy().squeeze())
ax1.axis('off')
ax2.barh(np.arange(10), ps)
ax2.set_aspect(0.1)
ax2.set_yticks(np.arange(10))
ax2.set_yticklabels(np.arange(10))
ax2.set_title('Class Probability')
ax2.set_xlim(0, 1.1)
plt.tight_layout()
预测:
关闭梯度,因为我们使用相同的模型,这将开始训练,所以我们关闭所有的梯度,并获得测试图像的概率分布。所有的概率都是对数,所以我们将其转换为 0–1,并使用该函数可视化图像。
# Getting the image to test
images, labels = next(iter(trainloader))# Flatten the image to pass in the model
img = images[0].view(1, 784)# Turn off gradients to speed up this part
with torch.no_grad():
logps = model(img)# Output of the network are log-probabilities, need to take exponential for probabilities
ps = torch.exp(logps)
view_classify(img, ps)
使用 PyTorch,您可以在 MNIST 手数字识别数据集上从头开始训练一个神经网络。
现在,是庆祝的时候了,因为你实现了!!!

来源:男高音
谢谢你阅读我的博客,感谢我的努力。请随时评论和提问,并提出您的建议。你可以在 LinkedIn 、 Twitter 和我的网站上与我联系,了解更多深度学习项目。快乐学习!!!
用 PyTorch 训练 Leela Zero 神经网络
用 PyTorch、PyTorch Lightning 和 Hydra 为 Leela Zero 实现了一个简单的培训管道

闪电和九头蛇(?)— 来源
最近,我一直在寻找加快我的研究和管理我的实验的方法,特别是围绕编写训练管道和管理实验配置,我发现了这两个新项目,分别名为 PyTorch Lightning 和 Hydra 。PyTorch Lightning 帮助您快速编写培训管道,而 Hydra 帮助您以干净的方式管理配置。
为了在更真实的环境中练习使用它们,我决定为围棋引擎 Leela Zero 编写一个训练管道。我选择这样做是因为这是一个范围很广的项目,具有有趣的技术挑战,涉及使用多个 GPU 在大数据集上训练巨大的网络。此外,我以前很喜欢为国际象棋实现 AlphaGo 的小版本,所以我认为这将是一个有趣的附带项目。
在这个博客中,我将解释这个项目的主要细节,这样你就可以很容易地理解我做了什么。你可以在这里阅读我的代码:https://github.com/yukw777/leela-zero-pytorch
莉拉·零
第一步是弄清楚 Leela Zero 神经网络的内部工作方式。我大量参考了 Leela Zero 的文档及其 Tensorflow 培训管道。
神经网络体系结构
Leela Zero 的神经网络由一个 ResNet“塔”组成,有两个“头”,策略头和值头,如 AlphaGo Zero 论文中所述。所有卷积滤波器都是 3x3,除了在策略和值头开头的滤波器是 1x1,如本文中所示。游戏和棋盘特征被编码为形状张量[批量大小、棋盘宽度、棋盘高度、特征数量],并首先通过 ResNet 塔传送。然后,该塔提取抽象特征,并通过每个头来计算下一步行动的策略概率分布和游戏的价值,以预测游戏的赢家。
您可以在下面的代码片段中找到网络的实现细节。
Leela 零神经网络在 PyTorch 中的实现
权重格式
Leela Zero 使用一个简单的文本文件来保存和加载网络权重。文本文件中的每一行都有一系列数字,代表网络每一层的权重。先有残塔,后有政策头,再有价值头。
卷积层有 2 个权重行:
- 形状卷积权重[输出、输入、过滤器大小、过滤器大小]
- 渠道偏差
Batchnorm 层有 2 个权重行:
- 批处理方式
- 批次方差
内部产品(完全连接)层有 2 个重量行:
- 形状的层权重[输出,输入]
- 输出偏差
我编写了单元测试来确保我的权重文件是正确的。我使用的另一个简单的健全性检查是计算层数,并将其与加载我的权重文件后 Leela Zero 所说的进行比较。层数的公式为:
n_layers = 1 (version number) +
2 (input convolution) +
2 (input batch norm) +
n_res (number of residual blocks) *
8 (first conv + first batch norm +
second conv + second batch norm) +
2 (policy head convolution) +
2 (policy head batch norm) +
2 (policy head linear) +
2 (value head convolution) +
2 (value head batch norm) +
2 (value head first linear) +
2 (value head second linear)
到目前为止,这似乎很简单,但是有一个奇怪的实现细节需要注意。Leela Zero 实际上使用卷积层的偏差来表示下一批范数层的可学习参数(gamma和beta)。这样做是为了在添加批次标准图层时,权重文件的格式(只有一行用于图层权重,另一行用于偏差)不必更改。
目前,Leela Zero 仅使用批次定额的beta项,并将gamma设置为 1。那么,实际上如何使用卷积偏差来产生与应用 batch norm 中的可学习参数相同的结果呢?让我们先来看看批量定额的等式:
y = gamma * (x — mean)/sqrt(var — eps) + beta
由于莉拉零点将gamma设为 1,等式变成:
y = (x — mean)/sqrt(var — eps) + beta
现在,假设x_conv是没有偏差的卷积层的输出。然后,我们要给x_conv增加一些偏差,这样当你在没有beta的情况下通过批范数运行它时,结果和在上面提到的只有beta的情况下通过批范数方程运行x_conv是一样的。以方程式的形式:
(x_conv + bias — mean)/sqrt(var — eps) =
(x_conv — mean)/sqrt(var — eps) + beta x_conv + bias — mean =
x_conv — mean + beta * sqrt(var — eps) bias = beta * sqrt(var — eps)
因此,如果我们在权重文件中将卷积偏差设置为beta * sqrt(var — eps),我们将获得所需的输出,这就是 LeelaZero 所做的。
那么,我们实际上如何实现这一点呢?在 Tensorflow 中,您可以通过调用tf.layers.batch_normalization(scale=False)来告诉 batch norm 层只忽略gamma项,并完成它。不幸的是,在 PyTorch 中你不能设置批处理规范化层只忽略gamma;将affine参数设置为False : BatchNorm2d(out_channels, affine=False)只能忽略gamma和beta。所以,我设置批处理规范化来忽略这两者,然后简单地在后面添加一个张量,它代表beta。然后,我使用公式bias = beta * sqrt(var — eps)计算权重文件的卷积偏差。
培训渠道
在弄清楚 Leela Zeros 的神经网络的细节后,是时候解决训练管道了。正如我提到的,我想练习使用两个工具——py torch Lightning 和 Hydra——来加速编写训练管道和干净地管理实验配置。让我们深入了解我如何使用它们的细节。
PyTorch 闪电
编写培训管道是迄今为止我最不喜欢的研究部分:它涉及大量重复的样板代码,并且很难调试。正因为如此,PyTorch 闪电对我来说就像一股清新的空气。它是一个轻量级的库,在 PyTorch 之上没有很多辅助的抽象,在编写训练管道时负责大部分样板代码。它允许您关注培训管道中更有趣的部分,如模型架构,并使您的研究代码更加模块化和可调试。此外,它支持开箱即用的多 GPU 和 TPU 培训!
为了将 PyTorch Lightning 用于我的训练管道,我必须做的最多的编码工作就是编写一个类,我称之为NetworkLightningModule,它继承了LightningModule来指定我的训练管道的细节,并将其传递给Trainer。你可以遵循 PyTorch Lightning 官方文档,了解如何编写自己的LightningModule的细节。
水螅
我一直在寻找好的解决方案的另一部分研究是实验管理。当你进行研究时,不可避免地要运行无数的实验变量来测试你的假设,以一种可扩展的方式跟踪它们是极其重要的。到目前为止,我一直依靠配置文件来管理我的实验变体,但是使用平面配置文件很快变得难以管理。模板是解决这个问题的一种方法。然而,我发现模板最终也会变得混乱,因为当您覆盖多层值文件来呈现您的配置文件时,很难跟踪哪个值来自哪个值文件。
另一方面,Hydra 是一个基于组合的配置管理系统。您可以组合多个较小的配置文件来构成最终配置,而不是使用单独的模板和值文件来呈现最终配置。它不如基于模板的配置管理系统灵活,但是我发现基于组合的系统在灵活性和可维护性之间取得了很好的平衡。Hydra 就是这样一个系统,它是专门为研究脚本定制的。它的调用有点笨拙,因为它要求您将它用作脚本的主入口点函数的装饰器,但我实际上认为这种设计选择使它易于与您的训练脚本集成。此外,它允许您通过命令行手动覆盖配置,这在运行您的实验的不同变体时非常有用。我使用 Hydra 来管理不同规模的网络架构和培训管道配置。
估价
为了评估我训练过的网络,我用 GoMill 来运行围棋比赛。这是一个在 GTP 引擎之间运行比赛的库,Leela Zero 就是其中之一。你可以在这里找到我用的的锦标赛配置。
结论
通过使用 PyTorch-Lightning 和 Hydra,我能够大大加快编写训练管道的速度,并有效地管理实验配置。我希望这个项目和博客帖子也能帮助你的研究。你可以在这里查看代码:【https://github.com/yukw777/leela-zero-pytorch
用张量流训练神经网络进行价格预测
了解如何使 DNN 更有效地解决回归问题:TensorFlow 和 Keras 实用指南。

模型学习曲线的演变
使用深度神经网络来解决回归问题可能看起来有些矫枉过正(而且经常如此),但对于一些拥有大量高维数据的情况,它们可以胜过任何其他 ML 模型。
当你学习神经网络时,你通常会从一些图像分类问题开始,如 MNIST 数据集——这是一个显而易见的选择,因为高维数据的高级任务是 dnn 真正蓬勃发展的地方。
令人惊讶的是,当你试图将你在 MNIST 学到的东西应用到回归任务中时,你可能会挣扎一段时间,直到你的超级先进的 DNN 模型比基本的随机森林回归器更好。有时候你可能永远也到不了那个时刻…
在本指南中,我列出了使用 DNN 解决回归问题时学到的一些关键技巧和诀窍。该数据是一组近 50 个要素,描述了华沙的 25k 处房产。我在上一篇文章中描述了特征选择过程:处理空间数据时的特征选择和错误分析因此,现在我们将重点关注使用所选特征创建预测每平方米房价的最佳模型。
本文使用的代码和数据源可以在 GitHub 上找到。
1.入门指南
当训练深度神经网络时,我通常遵循以下关键步骤:
- A) 选择默认架构——层数、神经元数、激活
- B) 正则化模型
- C) 调整网络架构
- D) 调整学习率和时期数
- 使用回调提取最佳模型
通常创建最终的模型需要几遍所有这些步骤,但是要记住的一件重要的事情是:一次做一件事。不要试图同时改变架构、规则和学习速度,因为你不知道什么是真正有效的,可能要花几个小时在原地打转。
在你开始为回归任务构建 dnn 之前,有 3 件关键的事情你必须记住:
- 将你的数据标准化,让训练更有效率
- 对所有隐藏层使用 RELU 激活功能——使用默认的 sigmoid 激活,你将一事无成
- 对单神经元输出层使用线性激活函数
另一个重要的任务是选择损失函数。均方误差或平均绝对误差是两种最常见的选择。我的目标是最小化平均百分比误差,并在 5%的误差范围内最大化所有建筑的份额,我选择 MAE,因为它对异常值的惩罚更少,也更容易解释——它几乎可以告诉你每个报价平均每平方米偏离实际值多少美元。
还有一个与我的目标直接相关的函数——平均绝对百分比误差,但是在用 MAE 测试它之后,我发现训练效率较低。
2.基本 DNN 模型
我们从一个具有 5 个隐藏层的基本网络开始,每隔一层神经元的数量递减。
tf.keras.backend.clear_session()
tf.random.set_seed(60)model=keras.models.Sequential([
keras.layers.Dense(512, input_dim = X_train.shape[1], activation='relu'),
keras.layers.Dense(512, input_dim = X_train.shape[1], activation='relu'),
keras.layers.Dense(units=256,activation='relu'),
keras.layers.Dense(units=256,activation='relu'),
keras.layers.Dense(units=128,activation='relu'),
keras.layers.Dense(units=1, activation="linear"),],name="Initial_model",)model.summary()

我们使用 Adam optimizer,并从训练每个模型 200 个时期开始——模型配置的这一部分将保持不变,直到
点 7。
optimizer = keras.optimizers.Adam()model.compile(optimizer=optimizer, warm_start=False,
loss='mean_absolute_error')history = model.fit(X_train, y_train,
epochs=200, batch_size=1024,
validation_data=(X_test, y_test),
verbose=1)
初始模型学习曲线

初始模型学习曲线(从时期 10 开始)
我们的第一个模型被证明是相当失败的,我们在训练数据上有惊人的过度拟合,并且我们的验证损失在纪元 100 之后实际上在增加。
3.辍学正规化
退出可能是 DNN 正则化的最佳答案,适用于各种规模和架构的网络。在训练期间,应用 Dropout 会在每个时期随机丢弃层中的一部分神经元,这将迫使剩余的神经元更加通用-这将减少过度拟合,因为一个神经元不再能够映射一个特定的实例,因为它在训练期间不会总是在那里。
我建议阅读原始论文,因为它很好地描述了这个想法,并且不需要多年的学术经验来理解它——辍学:防止神经网络过度拟合的简单方法
tf.keras.backend.clear_session()
tf.random.set_seed(60)model=keras.models.Sequential([
keras.layers.Dense(512, input_dim = X_train.shape[1], activation='relu'),
**keras.layers.Dropout(0.3),**
keras.layers.Dense(512, activation='relu'),
** keras.layers.Dropout(0.3),**keras.layers.Dense(units=256,activation='relu'),
**keras.layers.Dropout(0.2),**
keras.layers.Dense(units=256,activation='relu'),
**keras.layers.Dropout(0.2),**
keras.layers.Dense(units=128,activation='relu'),
keras.layers.Dense(units=1, activation="linear"),],name="Dropout",)
Dropout 后的(0.x)指定了您想要丢弃的神经元份额,这转化为您想要正则化的程度。我通常在最大的层中从大约(0.3–0.5)的下降开始,然后在更深的层中降低其刚性。这种方法背后的想法是,更深层次网络中的神经元往往有更具体的任务,因此丢弃太多会增加太多的偏差。
辍学模型学习曲线

辍学模型学习曲线(从第 10 个时期开始)
分析修改后模型的学习曲线,我们可以看到我们正朝着正确的方向前进。首先,我们设法从先前模型的验证损失中取得进展(由灰色阈值线标记),其次,我们似乎用轻微的欠拟合来代替过拟合。
4.用批处理规范化处理濒死/爆炸神经元
当使用 RELU 激活的几个层工作时,我们有很大的神经元死亡的风险,这对我们的性能有负面影响。这可能导致我们在之前的模型中看到的拟合不足,因为我们可能实际上没有使用我们神经元的大部分,这基本上将它们的输出减少到 0。
批量标准化是处理这一问题的最佳方式之一,当应用时,我们对每一批的每一层的激活输出进行标准化,以减少极端激活对参数训练的影响,从而降低消失/爆炸梯度的风险。描述该解决方案的原始论文比参考的前一篇论文更难阅读,但我仍然建议尝试一下— 批量标准化:通过减少内部协变量转移来加速深度网络训练
tf.keras.backend.clear_session()
tf.random.set_seed(60)model=keras.models.Sequential([
keras.layers.Dense(512, input_dim = X_train.shape[1], activation='relu'),
** keras.layers.BatchNormalization(),**
keras.layers.Dropout(0.3),
keras.layers.Dense(512, activation='relu'),
** keras.layers.BatchNormalization(),**
keras.layers.Dropout(0.3),keras.layers.Dense(units=256,activation='relu'),
** keras.layers.BatchNormalization(),**
keras.layers.Dropout(0.2),
keras.layers.Dense(units=256,activation='relu'),
** keras.layers.BatchNormalization(),**
keras.layers.Dropout(0.2),
keras.layers.Dense(units=128,activation='relu'),
keras.layers.Dense(units=1, activation="linear"),],name="Batchnorm",)
BatchNorm 模型学习曲线

BatchNorm 模型学习曲线(从第 10 个时期开始)
添加批量标准化有助于我们让一些神经元复活,这增加了我们的模型方差,将欠拟合变为轻微的过拟合——训练神经网络通常是一场猫捉老鼠的游戏,在最佳偏差和方差之间进行平衡。
另一个好消息是,我们仍在改进验证错误。
5.将激活函数改为漏 RELU
漏 RELU 激活函数是对 RELU 函数的轻微修改,它允许一些负面激活漏过,进一步降低了神经元死亡的风险。漏 RELU 通常需要更长的时间来训练,这就是为什么我们将训练这个模型另外 100 个纪元。

漏 RELU 激活
tf.keras.backend.clear_session()
tf.random.set_seed(60)model=keras.models.Sequential([
keras.layers.Dense(512, input_dim = X_train.shape[1]),
** keras.layers.LeakyReLU(),**
keras.layers.BatchNormalization(),
keras.layers.Dropout(0.3),
keras.layers.Dense(512),
**keras.layers.LeakyReLU(),**
keras.layers.BatchNormalization(),
keras.layers.Dropout(0.3),keras.layers.Dense(units=256),
** keras.layers.LeakyReLU(),**
keras.layers.BatchNormalization(),
keras.layers.Dropout(0.2),
keras.layers.Dense(units=256),
** keras.layers.LeakyReLU(),**
keras.layers.BatchNormalization(),
keras.layers.Dropout(0.2),
keras.layers.Dense(units=128),
**keras.layers.LeakyReLU(),**
keras.layers.Dense(units=1, activation="linear"),],name="LeakyRELU",)
泄漏 ReLU 模型学习曲线

泄漏 ReLU 模型学习曲线(从第 10 个时期开始)
似乎漏 RELU 减少了过度拟合,并给了我们一个更健康的学习曲线,即使在 300 个时代后,我们也可以看到改进的潜力。我们几乎达到了以前模型的最低误差,但我们设法做到了这一点,而没有过度拟合,这给我们留下了增加方差的空间。
6.用具有 1024 个神经元的附加隐藏层扩展网络
在这一点上,我对基本模型感到足够满意,可以通过添加另一个具有 1024 个神经元的隐藏层来使网络变得更大。新的层次也有最高的辍学率。由于整体架构的变化,我还对较低级别的辍学率进行了实验。
tf.keras.backend.clear_session()
tf.random.set_seed(60)model=keras.models.Sequential([
**keras.layers.Dense(1024, input_dim = X_train.shape[1]),
keras.layers.LeakyReLU(),
keras.layers.BatchNormalization(),
keras.layers.Dropout(0.4),**
keras.layers.Dense(512),
keras.layers.LeakyReLU(),
keras.layers.BatchNormalization(),
keras.layers.Dropout(0.3),keras.layers.Dense(512),
keras.layers.LeakyReLU(),
keras.layers.BatchNormalization(),
keras.layers.Dropout(0.3),
keras.layers.Dense(units=256),
keras.layers.LeakyReLU(),
keras.layers.BatchNormalization(),
keras.layers.Dropout(0.2),
keras.layers.Dense(units=256),
keras.layers.LeakyReLU(),
keras.layers.BatchNormalization(),
**keras.layers.Dropout(0.01),**keras.layers.Dense(units=128),
keras.layers.LeakyReLU(),
**keras.layers.Dropout(0.05),**
keras.layers.Dense(units=1, activation="linear"),],name="Larger_network",)
更大的网络模型学习曲线

更大的网络模型学习曲线(从时期 10 开始)
扩展网络架构似乎正朝着正确的方向发展,我们略微增加了方差以获得学习曲线,这接近最优平衡。我们还设法使我们的验证损失几乎与过度拟合的 BatchNorm 模型持平。
7.学习率下降,训练效率提高
一旦我们对网络架构满意了,学习率就是最重要的超参数,需要调优。我决定使用学习率衰减,这允许我在开始时更快地训练我的模型,然后随着更多的时期降低学习率,以使训练更加精确。
**optimizer = keras.optimizers.Adam(lr=0.005, decay=5e-4)**
选择正确的起始速率和衰减具有挑战性,需要反复试验。在我的例子中,事实证明 Keras 中默认的 Adam 学习率(0.001)有点高。我开始时的学习率是 0.005,经过 400 多次后,学习率降到了 0.001。

学习率在 400 个时期内衰减
学习率衰减模型学习曲线

学习率衰减模型学习曲线(从时期 10 开始)
调整学习率帮助我们最终改善了验证误差结果,同时仍然保持学习曲线健康,没有过度拟合的太大风险——甚至可能还有一些空间来为另一个 100 个时期训练模型。
8.使用回调在最佳时期停止训练
在选择我们的最佳模型之前,剩下的最后一项任务是使用回调在最佳时期停止训练。这允许我们在达到最小误差的精确时期检索模型。这种解决方案的最大优点是,如果你想训练 300 或 600 个纪元,你真的不需要担心——如果你的模型开始过度拟合,回调会让你回到最佳纪元。
checkpoint_name = 'Weights\Weights-{epoch:03d}--{val_loss:.5f}.hdf5'
checkpoint = ModelCheckpoint(checkpoint_name, monitor='val_loss', verbose = 1, save_best_only = True, mode ='auto')
callbacks_list = [checkpoint]
您需要定义您的回调:checkpoint_name 指定您希望在哪里以及如何保存每个纪元的权重,checkpoint 指定回调应该如何表现——我建议监视 val_loss 以进行改进,并且仅当纪元在这方面取得一些进展时才进行保存。
history = model.fit(X_train, y_train,
epochs=500, batch_size=1024,
validation_data=(X_test, y_test),
**callbacks=callbacks_list,**
verbose=1)
然后你需要做的就是在拟合你的模型的同时添加回调。
回调模型学习曲线

回调模型学习曲线(从第 10 个时期开始)
使用回调允许我们检索在第 468 个时期训练的最佳模型——接下来的 30 个时期没有改善,因为我们开始过度适应训练集。
9.模型进化总结
比较模型之间的验证损失

为了达到期望的模型输出,我们花了 7 个步骤。当我们的主要目标是减少过度拟合时,我们设法在几乎每一步都有改进,在 batch_norm 和 1024_layer 模型之间有一个平台。老实说,提炼这 7 个步骤,可能花了我 70 个步骤,所以请记住,训练 DNNs 是一个迭代的过程,如果你的进步停滞了几个小时,不要推迟。
10.DNN vs 兰登森林
最后,与之前的文章中基于相同数据训练的基本随机森林回归器相比,我们的最佳 DNN 表现如何?
在两个关键 KPI 中我们的随机森林得分如下:
- 绝对误差在 5%以内的预测份额= 44.6%
- 平均百分比误差= 8.8%
我们最好的深度神经网络得分:
- 绝对误差在 5%以内的预测份额= 43.3% (-1.3 个百分点)
- 平均百分比误差= 9.1% (+0.3 个百分点)
我们现在可以哭了吗?经过几个小时的精心训练,我们先进的神经网络怎么可能没有打败一个随机森林?老实说,有两个关键原因:
- 就训练 DNNs 而言,25k 记录的样本大小仍然很小,我选择尝试这种架构,因为我每个月都在收集新数据,我相信在几个月内,我将达到更接近 100k 的样本,这将为 DNN 带来所需的优势
- 随机森林模型的拟合度非常高,尽管在验证集上有很高的性能,但我不确定它是否也能很好地概括其他属性——在这一点上,我可能仍然会在生产中使用 DNN 模型,因为它更可靠。
总而言之,我建议不要从 DNN 开始解决回归问题。除非您在一个非常复杂的项目中使用数百个 k 样本,否则随机森林回归通常会更快地获得初始结果-如果结果证明是有希望的,您可以继续进行 DNN。训练有效的 DNN 需要更多的时间,如果您的数据样本不够大,它可能永远不会达到随机森林的性能。
[1]:尼提什·斯里瓦斯塔瓦。(2014 年 6 月 14 日)。辍学:防止神经网络过度拟合的简单方法
[2]:谢尔盖·约菲。(2015 年 3 月 2 日)。批量标准化:通过减少内部协变量转移加速深度网络训练
使用域随机化训练没有真实数据的对象检测器
在没有预算的情况下解决专用对象检测器的模拟 2 真实传输
深度学习最近已经成为解决物体检测问题的首选方法。然而,与这项技术的许多其他用途一样,注释训练数据既麻烦又耗时,尤其是如果您是一家有特定用例的小公司。在这篇文章中,我介绍了我们在物体检测的合成数据生成方面的一些工作,并展示了一些实例。
最近,许多研究论文和私营企业都将注意力集中在图像中的自动目标检测上。金字塔滑动盒检测器的时代已经一去不复返,卷积神经网络的时代已经到来。世界各地的研究人员正在微调网络,并在他们的训练计划中添加铃铛和哨子,以提高大型数据集的分数,如帕斯卡 VOC 或可可。
但是如果你不想在申请中找到 COCO 提供的任何课程呢?通常情况下,您必须为所选感兴趣对象(OOI)的新数据集提供资金,这可能会非常昂贵和耗时,尤其是如果您是一家小公司。好的数据集从不同的角度、在不同的光照下显示你的 OOI,并且有许多额外的差异,使得检测器不会过度适应一个特定的版本;例如,在单个人身上训练人体探测器,或者在单个汽车模型上训练汽车探测器,都是适得其反的。
自从训练数据存在以来,随着噪声被添加到信号输入和图像区域上的黑盒,增强的训练数据一直是一件事情。随着对域随机化(DR) 的需求和引入,这种范式略有改变,这是一种用于改善机器人机动和需要大量训练数据的类似任务的强大工具。DR 依赖于对手头任务不重要的随机参数,这样网络就学会忽略这些参数。假设你想用相机检测一个树莓派。它们有不同的颜色,可以有不同的背景和照明环境。它们也可能被各种其他物体阻挡,并在许多方向上靠近或远离摄像机。这些是您想要随机化的参数。在这种情况下,你不希望随机化的一件事是物体的一般物理形状。
多亏了现代计算机图形学,我们可以控制这些参数,并在给定树莓的 3D 模型的情况下,连续渲染树莓的无限多种变化。这给我们留下两条路:使用良好的照明和真实的环境渲染图像,可能使用一些高级的 CG 编程方法,如光线跟踪,或者使用低细节和次优照明渲染更快的图像。
巧合的是,我们一直在用任意数量的随机参数对合成数据单独进行训练对象检测器的实验。我们在这方面的理念是,我们可以在更短的时间内提供越多不同的训练数据,我们的模型就可以更好、更快地学习概括。一个真实的、看起来自然的物体的图像突然变成了模型可能已经看到的另一个变体。
创建合成数据生成器
我们在实验室中选择了五个具有匹配 3D 模型的对象,并着手创建一个快速合成图像生成器。由于我以前使用 Unity 的经验和项目的规模,这是我们选择的引擎。系统的整体思路很简单;完全随机化对象及其环境的所有参数,这些参数对于确定其类别和位置并不重要。这主要是实验性的,为了观察完全域随机化提供的机会。

随机生成的 Unity3D 场景,包含光源、随机“垃圾”对象和感兴趣的对象。
我们将相机参数、材质颜色和其他材质属性、场景中灯光的强度、颜色和数量以及许多其他东西随机化,以便尽可能创建最多样的数据集。OOI 被随机缩放、旋转和定位。然后场景被渲染两次;一次用于生成图像,一次用于生成边界框。这里可以看到一个随机生成场景的例子。
下面,我展示了系统输出的一些奇怪的树莓图片。红框只是告诉我们边界框在哪里,当然不是训练数据的一部分。如您所见,输出看起来一点也不真实。事实上,很难立即看出它们代表了什么。然而,我可以在我的笔记本电脑上每小时生成几十万个这样的东西,这给了我们的模型一个很好的机会来学习物体的形状表示,我们假设,学习忽略其他一切。

用作训练数据的随机生成的树莓 Pi 图像的示例
我决定在 MobilenetV2 上训练单次多盒检测器(SSD)模型,因为我们对在移动应用中使用训练好的模型感兴趣。这意味着精度预计会比基于 VGG19 的模型略低,但该模型应该训练和运行得更快。我编写了一个快速脚本,从 Unity 应用程序中连续获取图像,并将它们写入 Tensorflow。记录文件。这允许我们使用带有 model_main.py 的迁移学习,这是一个 Tensorflow 脚本,它根据一些可配置的参数重新训练现有的模型。然后我们可以使用 Tensorboard 可视化结果。我还准备了一个使用标签标注的真实图像的小测试数据集。
在合成数据上训练对象检测器
这项努力向我们展示的一件事是,根据合成数据训练的模型往往非常不稳定。在验证步骤之间,损失和图结果波动很大。解决这个问题的一个关键是增加批量。此外,我们在没有硬示例挖掘的情况下测试训练,硬示例挖掘是在对象检测训练中已经成为标准的方法。这里的假设是,一些例子可能被赋予了很高的权重,以至于模型在评估时对它们进行了过度拟合。证据是,在每个验证步骤中,几个真实图像预测了在位置和大小上非常相似的边界框和类,尽管图像甚至不包含该类的对象。这方面的例子如下所示。

使用硬示例挖掘进行预测的示例。左:预测。右:地面真相。
培训损失和评估损失之间也有很大的差异。这是意料之中的,因为图像的类型并不相似。由于训练损失比评估损失收敛得快得多,后者很难随着前者的减缓而改善。额外的数据扩充技术和训练方法在这里派上用场;我们包括随机黑色斑块、像素偏移和高斯斑点,以进一步增加检测器的难度,并在网络中引入信号丢失。如果你试图在家里这样做,要小心;需要耐心。这个想法需要一段时间来“点击”探测器,此时地图得分将开始增加。一旦正则化损失开始持续下降,通常会出现“咔哒声”。在此之前,猜测可能看起来完全是随机的,损失可能会大幅波动。
结果和考虑
下面,我们展示了第 28,000 步的一些结果。训练如此多的迭代所需的图像数据量需要几个小时才能生成。这些图像来自 Tensorboard,显示了评估集的预测和实际边界框。

在第 28,000 步做出的正确预测。左:预测。右:地面真相。
这张图片展示了网络做出的一些不错的预测。尽管(很可能)从未接受过任何像这样逼真的训练,但该网络似乎能够相对准确地预测和分类树莓皮和钻头,即使在场景和 OOI 前面散布着随机物体。
此时,网络已经开始学习对象的强表示。然而,需要更多的数据来更好地区分实际物体和背景噪声。下面是一些例子。

在第 28,000 步做出的预测很差。左:预测。右:地面真相。
这些图像显示了在第 28000 步做出的一些错误预测。请注意,探测器仍然可以探测到物体的大致轮廓,只是不是正确的轮廓。它预测为 Raspberry Pi 的对象也是那里最详细的对象,这表明它已经以某种方式了解到许多线条和轮廓可能表示该类。还要注意,这些置信度明显低于先前图像中正确预测的类别,这表明在可视化期间稍高的阈值可能会减轻这个问题。


为了更好地显示结果,我向您展示运行在 android 的 TensorflowLite 对象检测器演示应用程序中的检测器。这些结果基于之前讨论的配置,即没有硬示例挖掘,但我让模型在几百万张图像上训练。在合成数据生成时间中,这大致相当于在你离开办公室时开始生成,而在你第二天早上上班时就完成了。
这个项目是一个有趣的实验,它向我们证明了合成数据是真实数据的一个可行的替代品,在时间和金钱方面的成本都要低得多。事实上,这个项目没花我们一分钱。所有的部分都是免费使用和开源的。这只是证明,无论你是业余爱好者,项目负责人,还是介于两者之间的任何人,你都有机会使用最新的深度学习来创建出色的软件,而不需要你付出任何成本,只需要一点努力。
我在 Alexandra Institute 的视觉计算实验室工作,这是一家丹麦非营利公司,专门研究最先进的 it 解决方案。在我们的实验室,我们专注于利用最新的计算机视觉和计算机图形研究。我们目前正在研究数据注释、生成和增强技术,以允许较小的公司和个人开始深度学习。我们永远欢迎合作!
使用 PyTorch XLA 在 TPU 上并行训练
立即使用所有 TPU 内核训练您的模型,速度快很多倍!

图片取自 TPUs 上的 Google cloud 博客
摘自卡格尔·TPU 文件:
现在可以在 Kaggle 上免费获得 TPU。TPU 是专门从事深度学习任务的硬件加速器。它们在 Tensorflow 2.1 中通过 Keras 高级 API 得到支持,在较低的级别上,在使用自定义训练循环的模型中得到支持。
您每周最多可以使用 30 小时的 TPU,一次最多可以使用 3 小时。
Kaggle 文档只提到了如何使用 Tensorflow 在 TPU 上训练模型,但我想使用 PyTorch 来完成。PyTorch 有 XLA,我们要用它来运行我们在 TPU 的代码。无论如何,我面临的问题是没有关于如何做的单一信息来源。到处都是!
我做了相当多的研究,发现了Abhishek tha kur的这个惊人的内核。他解释了如何在 TPU 8 核处理器上并行训练。他甚至有一个 youtube 视频,解释了在 TPU 上的训练。看看这里https://www.youtube.com/watch?v=2oWf4v6QEV8。
好吧,那我们开始吧!
首先,我们需要安装 torch xla,为此,您需要做的就是将这两行代码复制、粘贴到 colab 或 kaggle 上并运行它们:
接下来是重要的进口产品:
必需的 XLA 进口
所以,我用 TPU 训练我的模特去参加一个卡格尔比赛。很简单的一个叫:植物病理学 2020 。你可以去看看。我将跳过数据预处理、代码建模,因为这是另一篇博客的主题。这里,我们只关心在 TPU 上运行这个模型。我将为您附上完整的 Ipython 笔记本的链接。
所以直接跳到训练代码,我将强调并行运行模型所需的东西。第一件重要的事情是我们的数据加载器的分布式采样器:
xm.xrt_world_size()检索参与复制的设备数量。(基本上是核心数)
xm.get_ordinal()检索当前进程的复制序号。序数范围从 0 到xrt_world_size()-1
接下来是并行训练模型,传统的DataLoader 必须做成一个ParallelLoader对象,然后传递给训练函数。为此我们这样做,pl.ParallelLoader(<your-dataloader>, [device])
这里的device是device = xm.xla_device()。我们只是简单地,指定我们发送我们的模型运行。在这种情况下,它是一个 TPU 或者 PyTorch 喜欢称之为 XLA 设备(如果你是 PyTorch 用户,那么你可以认为它类似于用于向 GPU 发送张量的torch.device('cuda'))
Torch.xla 有自己特定的要求。你不能简单地用xm.xla_device()做一个设备,然后把模型传给它。
有了这个:
- 优化器必须使用
xm.optimizer_step(optimizer)步进。 - 你必须用
xm.save(model.state_dict(), '<your-model-name>)保存模型 - 你得用
xm.master_print(...)打印。 - 对于并行训练,我们首先定义分布式训练和有效采样器,然后我们将数据加载器包装在
torch_xla.distributed.parallel_loader(<your-data-loader>)中,并创建一个torch_xla.distributed.parallel_loader对象,如我上面所解释的。 - 在将它传递给训练和验证函数时,我们指定这个
para_loader.per_device_loader(device)。这是您将在训练函数中迭代的内容,即我们传递一个并行加载器,而不是一个数据加载器(仅用于并行训练)。
有了所有的规范,现在你就可以在 kaggle 的 TPU 上训练你的模型了,不仅仅是 kaggle,还有 colab。我知道这不太合理。但这只是一个初步的解释给你,你可以来参考的细节。一旦你看到完整的代码,你就会明白一切。祝你好运!正如所承诺的,这里是我为 Kaggle 完成的内核:)
这里还有我的 Kaggle 内核链接https://www . ka ggle . com/abhiswain/py torch-TPU-efficient net-b5-tutorial-reference。如果你觉得这很有用,你可以投赞成票!你也可以去那里直接运行它,亲眼看看它的神奇之处。
让我告诉你一些事情,我做了这个作为自己的参考,但决定分享它。让这成为你深度学习之旅的一站:)
火炬 XLA 文件:T orch X LA
使用自定义数据集训练 Tensorflow 对象检测 API,以便在 Javascript 和 Vue.js 中工作
开场白
本文是 Gilbert Tanner 关于如何使用 Tensorflow 对象检测 API 创建自己的对象检测器的精彩教程的翻版。
我遵循完全相同的步骤,但有一些不同,并添加了一些我在设置和训练中遇到的事情。
我要感谢作者的原创内容,当然也要归功于他。
在这篇文章中,我们将使用 OS X + Anaconda 环境,这样就可以很容易地移植到谷歌云平台,例如,如果你愿意的话…另外使用 GPU 而不是 CPU 进行计算🚀
我还必须说,我是在我的研究时间里开发了这个实验的,我在 Manifiesto 工作,在那里我是一名前端/在线开发人员🤗。
设置工作 Python 环境
从:
https://www.anaconda.com/distribution/下载并安装 Anaconda(在我的例子中,Python 3.7 适用于 OS X)
对于我们马上要创建的新环境,我将使用实际的 Anaconda 安装附带的同一个 Python 版本。
要知道哪个版本只需运行:
conda info
然后创建并激活环境:
conda create --name tf-object-detection python=3.7.4
conda activate tf-object-detection
安装 Tensorflow 对象检测 API
安装 Tensorflow 模型和依赖关系
选择一个您想要工作的文件夹,并创建一个名为 tensorflow 的目录。我将在以下地点工作:
/Users/<username>/projects/tensorflow
然后克隆 Tensorflow 模型存储库,运行:
git clone https://github.com/tensorflow/models
现在您需要安装所有的依赖项:
conda install Cython
conda install contextlib2
conda install pillow
conda install lxml
conda install jupyter
conda install matplotlib
conda install tensorflow=1
安装 COCO API
为此,返回到您的项目文件夹,克隆 Github 项目库并执行以下命令:
git clone [https://github.com/cocodataset/cocoapi.git](https://github.com/cocodataset/cocoapi.git)
cd cocoapi/PythonAPI
make
cp -r pycocotools <path_to_tensorflow>/models/research
Protobuf 安装/编译
这是因为 Tensorflow 对象检测 API 的使用。proto 文件,这些文件需要编译成。py 文件,以便对象检测 API 正常工作。Protobuf 可以编译这些文件。
Protobuf 可以从:
https://github.com/protocolbuffers/protobuf/releases
将下载的文件放在你想要的任何地方,例如在项目文件夹中。
提取之后,你需要进入 models/research 并使用 protobuf 从 object_detection/protos 目录下的 proto 文件中提取 python 文件。
为此,我们将使用一个小脚本。
保存在 research 文件夹中,命名为 use_protobuf.py
import os
import sys
args = sys.argv
directory = args[1]
protoc_path = args[2]
for file in os.listdir(directory):
if file.endswith(".proto"):
os.system(protoc_path+" "+directory+"/"+file+" --python_out=.")
然后你可以使用它:
python use_protobuf.py <path_to_directory> <path_to_protoc_file># for example in our particular case
python use_protobuf.py object_detection/protos /Users/<username>/projects/protoc/bin/protoc
添加必要的环境变量
我们需要将 research 和 research/slim 文件夹添加到我们的环境变量中,并运行 setup.py 文件。
export PYTHONPATH=$PYTHONPATH:<PATH_TO_TF>/TensorFlow/models/researchexport PYTHONPATH=$PYTHONPATH:<PATH_TO_TF>/TensorFlow/models/research/object_detectionexport PYTHONPATH=$PYTHONPATH:<PATH_TO_TF>/TensorFlow/models/research/slim
注意:每次关闭 shell 会话或停用 Anaconda 环境时,都需要添加 $PYTHONPATH 环境变量。
现在导航到tensor flow/models/research并运行:
python setup.py build
python setup.py install
测试 Tensorflow 对象检测 API 安装完成后,我们可以通过从 object_detection 文件夹运行Object _ Detection _ tutorial . ipynb来测试一切是否正常。
注意:重要的是要考虑到本教程适用于 Tensorflow 2.0,并且您必须在您的环境中安装 tensor flow-如果没有,只需运行conda install tensor flow = 2
jupyter notebook
然后从浏览器窗口选择object _ detection _ tutorial . ipynb,按照说明操作即可。
您还可以通过在 python shell 中导入 object_detection 来检查一切是否正常:
import object_detection
如果没有错误,那么您可以假设一切都正常工作。
收集数据
出于我们的目的,我们将使用一组代表 Jagermeister 酒瓶的图像。为了尽快做到这一点,我们将使用 python 脚本来废弃 google images,以实现流程自动化。
脚本可以从
【https://github.com/hardikvasa/google-images-download】
下载,例如将下载的文件放在项目文件夹中,命名为 google-images-download 。
然后只需导航到 python 脚本所在的文件夹并执行:
python google_images_download.py --keywords "jagermeister bottle" --limit 100 --format jpg
因为图像的分辨率可能非常不同,有些可能相当大,我们希望缩放它们以更快地完成训练过程。
下面是一个小脚本:
from PIL import Image
import os
import argparsedef rescale_images(directory, size):
for img in os.listdir(directory):
im = Image.open(directory+img)
im_resized = im.resize(size, Image.ANTIALIAS)
im_resized.save(directory+img)if __name__ == '__main__':
parser = argparse.ArgumentParser(description="Rescale images")
parser.add_argument('-d', '--directory', type=str, required=True, help='Directory containing the images')
parser.add_argument('-s', '--size', type=int, nargs=2, required=True, metavar=('width', 'height'), help='Image size')
args = parser.parse_args()
rescale_images(args.directory, args.size)
要使用该脚本,我们需要保存它,例如,用名称transform _ image _ resolution . py保存,然后进入命令行并键入:
python transform_image_resolution.py -d <image_dir>/ -s 800 600
注意: image_dir 文件夹中的图像将被覆盖,因此如果需要,请进行备份。
注意:重要的是要检查下载的图像文件夹,以避免重复和删除损坏的文件,如果有的话。
标签数据
现在,我们必须将大约 80%的图像移动到目标 _ 检测/图像/训练目录中,将另外 20%的图像移动到目标 _ 检测/图像/测试目录中。
为了标记我们的数据,我们需要某种标记软件。
在这个特殊的例子中,我们将使用 LabelImg。
就我们使用 Anaconda 而言,我们只需要遵循以下说明:
# install pyqt (version 5)
conda install pyqt# download LabelImg and place it in 'projects' folder
git clone [https://github.com/tzutalin/labelImg.git](https://github.com/tzutalin/labelImg.git)# navigate to labelImg directory
cd labelImg# execute labelImg.py
make qt5py3;./labelImg.py
打开每个训练和测试文件夹,然后使用“创建矩形盒”按钮标记每个图像,并点击保存。我们将为每个箱子使用“ jagermeister 瓶”标签。
保存后,您将看到一个 XML 文件出现在相同的目录中,与我们刚刚标记的图像同名。
为培训生成 TFRecords
为了创建 TFRecords,我们将使用 Dat Tran 的浣熊探测器中的两个脚本; xml_to_csv.py 和 generate_tfrecord.py 文件。
下载并放置在 object_detection 文件夹中。你可以去下面的参考资料部分,看看我是从哪里下载的。
我们现在需要修改 xml_to_csv.py 脚本,这样我们就可以正确地将创建的 xml 文件转换成 csv 文件。
# Old:
def main():
image_path = os.path.join(os.getcwd(), 'annotations')
xml_df = xml_to_csv(image_path)
xml_df.to_csv('raccoon_labels.csv', index=None)
print('Successfully converted xml to csv.')# New:
def main():
for folder in ['train', 'test']:
image_path = os.path.join(os.getcwd(), ('images/' + folder))
xml_df = xml_to_csv(image_path)
xml_df.to_csv(('images/'+folder+'_labels.csv'), index=None)
print('Successfully converted xml to csv.')
然后,我们可以使用脚本打开命令行并键入:
python xml_to_csv.py
正如您所看到的,在图像目录中创建了两个文件。一个名为 test_labels.csv 另一个名为 train_labels.csv
注:如果你得到“没有模块名为'熊猫'”的错误,只需做一个 conda 安装熊猫。
在将这些 csv 文件转换为 TFRecords 之前,我们需要更改 generate_tfrecords.py 脚本。
出发地:
# TO-DO replace this with label map
def class_text_to_int(row_label):
if row_label == 'raccoon':
return 1
else:
None
收件人:
def class_text_to_int(row_label):
if row_label == 'jagermeister bottle':
return 1
else:
None
现在,可以通过键入以下命令来生成 TFRecords:
# train tfrecord
python generate_tfrecord.py --csv_input=images/train_labels.csv --image_dir=images/train --output_path=train.record# test tfrecord
python generate_tfrecord.py --csv_input=images/test_labels.csv --image_dir=images/test --output_path=test.record
这两个命令生成一个训练记录和一个测试记录文件,可用于训练我们的目标探测器。
注意:如果得到“模块 tensorflow 没有属性 app”这样的错误是因为你在使用 Tensorflow 2.0 所以我们需要在 generate_tfrecord.py 文件中修改一行。
出发地:
# line 17
import tensorflow as tf
收件人:
# line 17
import tensorflow.compat.v1 as tf
或者,如果您愿意,可以回滚到 Tensorflow 1.0,而不是:
conda remove tensorflow
conda install tensorflow=1
配置培训
在开始培训之前,我们需要创建一个标签映射和一个培训配置文件。
创建标签映射
标签映射将 id 映射到名称。我们将把它放在位于 object_detection 目录下的一个名为 training 的文件夹中,文件名为 labelmap.pbtxt
item {
id: 1
name: 'jagermeister bottle'
}
每个项目的 id 号应该与 generate_tfrecord.py 文件中指定项目的 id 相匹配。
创建培训配置
现在我们需要创建一个培训配置文件。
我们将使用fast _ rcnn _ inception _ v2 _ coco模型,该模型可从:
https://github . com/tensor flow/models/blob/master/research/object _ detection/g3doc/detection _ model _ zoo . MD下载
下载并解压缩文件,将其放入 object_detection 文件夹。
文件夹名称看起来会像faster _ rcnn _ inception _ v2 _ coco _ 2018 _ 01 _ 28
我们将从名为faster _ rcnn _ inception _ v2 _ pets . config的示例配置文件开始,该文件可以在示例文件夹中找到。
可以从:
https://github . com/tensor flow/models/tree/master/research/object _ detection/samples/configs下载
保持相同的名称,将其保存到培训文件夹中,用文本编辑器打开,以便更改几行代码。
第 9 行 : 将类别数量更改为您想要检测的对象数量(在我们的示例中为 1)。
第 106 行:将微调检查点改为 model.ckpt 文件的路径
fine_tune_checkpoint:
"/Users/<username>/projects/tensorflow/models/research/object_detection/faster_rcnn_inception_v2_coco_2018_01_28/model.ckpt"
第 123 行:将 input_path 改为 train.record 文件的路径:
input_path:
"/Users/<username>/projects/tensorflow/models/research/object_detection/train.record
第 135 行:将输入路径改为测试记录文件的路径:
input_path:
"/Users/<username>/projects/tensorflow/models/research/object_detection/test.record
第 125-137 行:将标签地图路径改为标签地图文件的路径:
label_map_path:
"/Users/<username>/projects/tensorflow/models/research/object_detection/training/labelmap.pbtxt
第 130 行:将 num_example 更改为测试文件夹中图像的数量。
num_examples: 10
培训模式
我们将使用位于object _ detection/legacy文件夹中的 train.py 文件。我们将把它复制到 object_detection 文件夹中,然后在命令行中键入以下内容:
更新:或者我们可以使用 object_detection 文件夹中的 model_main.py 文件来代替。
python model_main.py --logtostderr --model_dir=training/ --pipeline_config_path=training/faster_rcnn_inception_v2_pets.config
如果一切都设置正确,训练应该很快开始。
注意:如果你得到一个类似“模块 tensorflow 没有属性 contrib”的错误是因为你正在使用 Tensorflow 2.0。有两种方法可以解决这个问题:
- 更新用于 Tensorflow 2.0 的脚本
- 回滚到 Tensorflow 1.0
最简单的方法是执行回滚,因此请执行以下操作
# uninstall Tensorflow 2.0
conda remove tensorflow# install Tensorflow 1.X
conda install tensorflow=1
培训模式进展
一旦训练开始,你会每隔 5 分钟左右(取决于你的硬件)看到当前的损失被记录到 Tensorboard。我们可以通过打开第二个命令行来打开 Tensorboard,导航到 object_detection 文件夹并键入:
tensorboard --logdir=training
现在在 localhost:6006 打开一个网页
你应该训练模型,直到它达到一个令人满意的损失。
按 Ctrl+C 可以终止训练过程
导出推理图
一旦模型被训练,我们需要生成一个推理图,它可以用来运行模型。为此,我们需要首先找出保存的最高步骤编号。
为此,我们需要导航到培训目录,寻找索引最大的 model.ckpt 文件。
然后,我们可以通过在命令行中键入以下命令来创建推理图:
python export_inference_graph.py --input_type image_tensor --pipeline_config_path training/faster_rcnn_inception_v2_pets.config --trained_checkpoint_prefix training/model.ckpt-XXXX --output_directory inference_graph
其中 XXXX 代表最高的数字。
将保存的模型转换为 TensorflowJS
首先,我们需要在我们的环境中安装 TensorflowJS。
通常我们会使用 conda 来安装包,但不幸的是在资源库中没有。
所以我们将通过 python pip 来使用它:
pip install tensorflowjs
安装完成后,我们可以执行以下命令,以便将我们保存的模型转换为 tensorfowjs 能够理解的内容:
tensorflowjs_converter --input_format=tf_saved_model <input_dir> <output_dir>
例如,如果我们在目录/Users/
tensorflowjs_converter --input_format=tf_saved_model --output_node_names='detection_boxes,detection_classes,detection_features,detection_multiclass_scores,detection_scores,num_detections,raw_detection_boxes,raw_detection_scores' --saved_model_tags=serve --output_format=tfjs_graph_model inference_graph/saved_model inference_graph/web_model
请注意,转换选项是专门为转换对象检测 SavedModel 而设置的,并且已经使用 saved_model_cli 命令检索了 output_node_names 配置:
saved_model_cli show --dir inference_graph/saved_model --tag_set serve --signature_def serving_default#output will print something like this:
#detection_boxes
#detection_classes
#detection_features
#detection_multiclass_scores
#detection_scores
#num_detections
#raw_detection_boxes
#raw_detection_scores
在下面的参考部分中对要点进行了全面解释。
在 Vue.js 应用程序上测试模型
现在我们已经正确地转换了模型,我们可以在运行 Javascript 的 web 应用程序环境中使用它。
为了让事情变得更顺利,我们可以使用下面参考文献部分中列出的演示应用程序。
您正在寻找的存储库是adriagil/tfjs-vue-example
我必须停下来一会儿,感谢fresh someone分享这个伟大的 Vue.js 演示应用程序,它使用预先训练的 coco-ssd 模型,在浏览器中与网络摄像头一起使用。我刚刚修改了代码,以便与我训练过的模型一起工作,主要包括如何提供和加载模型,以及如何检索所需的张量来分析数据。
这里有原 app 如果你感兴趣https://github.com/freshsomebody/tfjs-coco-ssd-vue-example
只需导航到那里,并在您的首选位置克隆或下载。
然后将上一步转换后的模型复制到下载项目的根目录下, web_model 文件夹。
然后导航到应用程序根目录并运行:
npm install
完成后,在你选择的文本编辑器中打开 App.vue 文件。
您现在可以看到 web 应用程序的源代码了,太棒了!🍺
为了正确加载转换后的模型,我们需要通过 web 服务器来完成。
我们将使用一个节点包来完成所谓的 http-server 。
我们需要做的就是使用以下命令激活 web 服务器:
node_modules/http-server/bin/http-server -c1 --cors .
并且可以通过 http 在 访问模型 http://localhost:8081/web _ model/model . JSON加载到浏览器中。
最后但同样重要的是,我们需要编译或服务前端应用程序,用下面的命令测试一切:
*npm run serve*
在 http://localhost:8080 中,您现在应该会看到网络摄像头流和相应的包围红色 Jaggermeister 瓶子的边框。
太棒了,我们完成了!👏👏👏

万岁 90%的检测分数🎉
结论
尽管这次经历的结果还不错,但有 2 件重要的事情需要考虑。
第一,在 OS X 工作不是最好的选择。
我建议你转换到 Linux 或 Windows 环境,但前提是你要使用 GPU,否则也是一样的废话😅
一个更好的解决方案(我们知道合适的 GPU 卡非常昂贵)是在谷歌云平台上完成。他们提供了一个非常好的环境和免费的学分供你在任何训练实验中使用。
第二件糟糕的事情是,我们选择的初始模型是精确的,但是在电脑浏览器上非常慢,在移动设备上更差。所以我现在正在训练另一个基于 MobileNet 的模型,它应该会表现得更好🤞希望如此。
我的模型也没有优化,也许这有所帮助,但我需要做一些研究如何实现它。我们走着瞧。
如果你喜欢,想要更多这样的或有任何问题,请不要犹豫与我联系…如果你来到这里,你值得拥有它!再次感谢激励我写这篇文章的作者们。
那都是乡亲们!
【2020 年 2 月 24 日更新】
正如我之前所说,网络浏览器的性能对于实时视频分析来说是不可接受的,所以我决定做一些研究。
感谢 stackoverflow 社区,我找到了许多建议,比如优化模型或使用另一个模型再次返回培训流程。我决定尝试另一个模型,因为我训练的 rcnn-inception 模型非常精确,但并没有真正考虑到我需要的应用程序。
我这次挑的是 ssd_mobilenet_v2_coco 。
经过一个周末的模型训练和近 30000 步——我知道这对开发来说不是必需的,但值得在真实情况下尝试——是测试的时候了。结果远远好于我的第一次尝试,所以现在预测率几乎与可接受的帧速率流体。
所以吸取教训;在训练前选择合适的模型来满足你的需求。
参考
用 Vue.js 制作的演示应用程序,用于在浏览器中使用定制的 Tensorflow 图形模型…
github.com](https://github.com/adriagil/tfjs-vue-example)
将 TF 模型转换为 TFJS 模型的代码段
更新 Dez/2019:安装现已推出 Jupyter 笔记本。2019 年 11 月更新:试用 Tensorflow v2.0 和…
gilberttanner.com](https://gilberttanner.com/blog/installing-the-tensorflow-object-detection-api) [## 创建您自己的对象检测器
使用 Tensorflow 对象检测 API 创建您自己的对象检测器
towardsdatascience.com](/creating-your-own-object-detector-ad69dda69c85) [## datit ran/浣熊数据集
这是我收集的数据集,用于使用 TensorFlow 的对象检测 API 训练我自己的浣熊检测器。图像是…
github.com](https://github.com/datitran/raccoon_dataset) [## hardikvasa/Google-图片-下载
用于“搜索”和“下载”数百张谷歌图片到本地硬盘的 Python 脚本!这个程序让你…
github.com](https://github.com/hardikvasa/google-images-download) [## AttributeError:模块“tensorflow”没有属性“app”
感谢贡献一个堆栈溢出的答案!请务必回答问题。提供详细信息并分享…
stackoverflow.com](https://stackoverflow.com/questions/58258003/attributeerror-module-tensorflow-has-no-attribute-app) [## 模块“tensorflow”没有属性“contrib”
感谢贡献一个堆栈溢出的答案!请务必回答问题。提供详细信息并分享…
stackoverflow.com](https://stackoverflow.com/questions/55870127/module-tensorflow-has-no-attribute-contrib) [## 使用 Tensorflow.js 运行对象检测
我正在使用 Tensorflow.js 进行对象检测。我正在尝试在…中运行自定义对象检测 tensorflow.js 模型
stackoverflow.com](https://stackoverflow.com/questions/59719522/running-object-detection-using-tensorflow-js) [## 输入看起来形状错误问题#193 tensorflow/tfjs
要从社区获得帮助,请查看我们的 Google 群组。TensorFlow.js 版本通过 Yarn 安装。var 版本=…
github.com](https://github.com/tensorflow/tfjs/issues/193) [## 执行转换后的 SSD _ mobilenet _ v2 _ oid _ v4 _ 2018 _ 12 _ 12 模型时出现“输入张量计数不匹配”
解散 GitHub 是超过 4000 万开发者的家园,他们一起工作来托管和审查代码,管理项目,以及…
github.com](https://github.com/tensorflow/tfjs/issues/1683)*
训练一个神经网络做 18 种不同的事情。

卡通形象
卷积神经网络(CNN) 架构对于视觉任务来说是非常通用的。在本文中,我将讲述我在 18 个不同的分类任务中使用相同网络架构的经验。
分类任务包括面部特征,如下巴长度(3 个等级)、头发类型(111 种类型)和头发颜色(10 种头发颜色)等。
我将使用谷歌提供的 100k 图像数据集这里。我为这些实验编写的代码可以在这里找到。
对于这些实验,我使用了 10K 版本的数据集。在最初的探索中,数据集由 10 个文件夹组成。首要任务是从网站下载数据集并提取出来。您将看到这 10 个文件夹:
['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']
每个子文件夹中都有。png '图像文件和一个. csv 描述符文件。
['cs11502169095236683120.csv',
'cs11502169095236683120.png',
'cs11502298889929094331.csv',
'cs11502298889929094331.png',
'cs11502404786906647764.csv',
'cs11502404786906647764.png',
'cs11502407216397343631.csv',
'cs11502407216397343631.png',
'cs11502919926067511421.csv',
'cs11502919926067511421.png']
让我们做一个快速的可视化:(在 github 中检查我的代码)

有对应的。csv 文件(与图像同名),其描述格式如下:
“脸型”,4,7
“面部 _ 头发”,14,15
“头发”,29,111
“眼睛 _ 颜色”,2,5
…
这些描述中的每一个都可能是“特征”,我们可以沿着这些特征建立一个深度学习网络来对图像进行分类。根据数据集描述页面可知,“这些集合中的每个卡通人脸都由 18 个组件组成,这些组件在 10 个图稿属性、 4 个颜色属性、 4 个比例属性上有所不同。每个属性的选项数量(将成为每个模型的类)的范围是下巴长度 3,发型 111。
根据数据集设计页面,“这些组件中的每一个及其变化都是由同一个艺术家 Shiraz Fuman 绘制的,产生了大约 250 个卡通组件艺术品和 ~10^13 可能的组合”。
正如我所承诺的,我将建立总共 18 个网络,它们都应该被专门化(希望如此)为特征分类器。在随后的文章中,我将用迁移学习和多标签分类的几种不同方法来解决这个问题。
首先是网络定义:

我使用的神经网络的结构。我用 Keras 做了一个。你会注意到,在每一个卷积层之后,我都做了一个最大池化、一个批量归一化和一个丢弃。这些层的确切顺序实际上是一个见仁见智的问题,不应该影响性能。您可以颠倒 batch_norm 和 dropout 的顺序,看看它是否工作得更好。我的猜测是,无论如何都不会有太大的改变。注意只有最终的密集层对于不同的分类具有不同数量的节点。
各层的一点背景
卷积层帮助网络学习移位或空间不变特征,并将这种先验信念引入网络结构。从这个意义上说,卷积神经网络是神经网络的正则化版本,有助于大大减少学习的参数数量。如上图所示,这种网络结构需要训练的参数略多于一百万。
最大池层数 用于基于样本的离散化,目的是对输入表示进行下采样。您可以看到,要素地图从(256,256,3)的输入大小开始,由于最大池化图层和选定的跨距,随着它穿过网络,慢慢变得越来越窄和越来越深。
批量标准化 是一种将输入移至零均值和单位方差的方法。一个非常高层次的理解是,这有助于使数据跨功能进行比较。众所周知,它能提高学习速度(尽管对其有效性还有其他解释)。
最后, Dropout 是一种正则化技术,近似训练具有许多不同架构的大量神经网络。它通过随机丢弃每层中各种节点的激活来实现这一点(由丢弃概率指定)。效果是它在训练过程中引入了噪声,因此像任何正则化技术一样帮助网络更好地泛化。
现在,继续训练网络。为了快速迭代,我想利用 keras 'image _ dataset _ from _ directory,因为它负责图像大小转换、验证分割、插值和批处理。该函数产生一个张量流数据集,操作和处理起来非常简单。
train _ dataset = TF . keras . preprocessing . image _ dataset _ from _ directory(
training _ dir,
labels = " extruded ",
label_mode="int ",
class_names=None,
color_mode="rgb ",
batch_size=32,
image_size=(256,256),
shuffle=True,
seed=42,
validation_split=.2,【t
因此,为了方便起见,我编写了一个函数,将文件复制到一个缓存临时目录中。我使用了一个 SSD 位置来加速 IO。该功能是在 github repo 中提供的 copy_images_to_labels_folder。
此外,我设置了一个 tensorboard 回调来可视化损失。这是为分类“脸型”而构建的神经网络的可视化示例。

数据集中总共有 6 种脸型。神经网络的结构似乎很擅长区分脸型,这可以从训练和验证损失非常快地接近 0 看出。训练了 30 个时期的模型的最终准确度是 100%。有趣的是,验证损失比训练损失下降的速度快得多。这是因为训练是拖尾平滑的测量。

对于“面部颜色”检测器,网络显示过度拟合,如绿色(验证)和粉色(训练)度量之间的差异所示。这应该通过增加正则化参数(如退出概率)或通过获得更多的训练数据来减轻。最终网络获得了一个不错的准确度。或者可能在从 rgba 到 rgb 的转换过程中缺少 alpha 通道会把事情弄糟。
最后,下表显示了各种网络获得的精度。我用 NVIDIA 2080 Ti 训练了所有这些。

CNN 在相同架构上训练 30 个时期后获得的准确度。第一项是空白的,因为有一些损坏的数据。第二列表示每个面部维度的类别数。
第一次,我觉得一切都很好,但对于一些像“眼睫毛”这样的情况,网络之前没有收敛。具体来说,损失值为“nan ”,这表示爆炸梯度或消失梯度问题。以下是我发现的 nan 在训练 convnet 时出现的主要原因。这并不意味着是一份详尽的清单。
爆炸渐变——你的 LR 太大了
b) 故障损失函数 —您使用的是自定义损失,还是正确使用标准损失。有一次,我使用了一个节点数少于我预测的类数的输出层,结果一整天都是 nans。
c) 输入数据 —是否存在腐败实例?就像这里的第一个网络一样。
d) 超参数 —其中一些具有相互依赖性,因此在更改默认值之前,请查看文档。
e)LR 不合适** —尝试使用 Adam 这样的自适应技术,看看是否有帮助。**
最后,可解释性。
我利用了一种技术,通过一种叫做综合梯度的过程,将深度网络的预测归因于其输入特征。下图可以解释为网络在做出决策时“关注”的地方。( github 页面用于代码和其他可视化)。参考页面了解更多信息。
下巴长度:97%

首先是下巴长度分类器:正常梯度和综合梯度似乎都发现了下巴曲率的重要性。然而,仍然没有完全磨练出来。
眼睛颜色:85%

眼睛颜色分类器。正常梯度看起来一点不错。你几乎要眯着眼看它。积分梯度没那么大。也许是因为我没有正确设置一些实现超参数。
眼眉距离:84%

这个是眉毛距离分类器。渐变是准确的。
眼镜颜色:54.66%

正常梯度看起来像他们在预期的位置。但是颜色区分绝对不是这个 CNN 的强项。你能猜到原因吗?(提示:考虑渠道信息—它们何时以及如何组合)。我将在以后的帖子中包括一个缓解策略。
面部颜色:26.04%

这又是一个与颜色分类有关的坏问题。CNN 似乎是色盲。(有些人会说,这并不总是坏事)。因此,只看梯度图也是一种了解神经网络是否至少在做直观的事情的好方法。(顺便说一下,这张图片被错误分类了)。
这是这次培训的情况:

训练显示了验证损失的剧烈变化,训练曲线相对平滑。也有迹象表明过度合身。考虑到我几乎完全关闭了退出(为了速度),并且依赖于批处理规范化所具有的一点点正则化效果,一个快速的补救实验将是使用一个合适的退出级别。
发色:96.13%

头发颜色分类器比预期的要好得多。
发型:99.70%

具有 111 个类别的发型分类器在仅 30 个时期内具有超过 99.7 %的验证准确度,并且表现得令人惊讶地好。
如果你想知道更多关于可解释性和可解释人工智能的一般子领域的信息,请看这篇帖子。
耗时分析:
我花了大约 3 个小时编写代码,所有 18 个模型在 NVIDIA 2080 Ti 上训练了 30 个时代。
结论:
在这篇文章中,我分享了我建立和训练 CNN 的经验,在没有任何干预和超参数调整的情况下,解决了 18 个不同的分类任务。这些网络的性能肯定可以通过利用更多数据、迁移学习、更健壮的架构或更仔细地选择超参数来提高,但这几乎不是重点。关键是,CNN 是相当通用的,现在做得很好,不需要花太多时间。
如果您觉得这篇文章或代码有帮助,或者有建议,让我们在评论部分继续讨论。
对计算机视觉、生成网络或强化学习感兴趣?未来的文章请关注我,并在 Link din上关注我。
用一行代码训练、跟踪、分析、解释和服务模型

贾斯汀·杰拉姆从 @jusspreme 拍摄的照片
自动化模型训练、跟踪、分析、解释和服务文件生成,每一项只需一行代码
作为一名数据科学家,快速实验极其重要。如果一个想法不起作用,最好是快速失败,尽早发现。
说到建模,快速实验已经相当简单了。所有模型实现都遵循相同的 API 接口,因此您所要做的就是初始化模型并训练它。当您必须解释、跟踪和比较每个模型时,问题就来了。
你是把你所有的模型做成一个大笔记本,然后上下滚动,还是用一个目录表来查看不同模型的结果?你会为每个型号创建不同的笔记本,然后在笔记本之间来回翻转吗?如果您开始调整参数,您如何跟踪模型的迭代?你把工件存放在哪里,以便日后再次访问或进一步分析?
我将通过训练多个模型来演示解决这些问题的方法,每个模型只有一行代码,轻松查看整体结果,分析模型,解释模型,在 MLFlow 中跟踪模型,并使用 Aethos 为它们提供服务。
准备数据
我们将使用 Aethos 快速准备数据。有关如何使用 Aethos 分析和转换数据集的更多信息,请点击这里查看我之前的博文。从 Aethos repo 载入泰坦尼克号的训练数据。
import aethos as at
import pandas as pddata = pd.read_csv('[https://raw.githubusercontent.com/Ashton-Sidhu/aethos/develop/examples/data/train.csv')](https://raw.githubusercontent.com/Ashton-Sidhu/aethos/develop/examples/data/train.csv')
将数据传入 Aethos。
df = at.Data(data, target_field='Survived')

这篇文章的重点是建模,所以让我们快速预处理数据。我们将使用幸存者、Pclass、性别、年龄、票价和上船特征。在此插入
df.drop(keep=['Survived', 'Pclass', 'Sex', 'Age', 'Fare', 'Embarked'])df.standardize_column_names()
替换“年龄”和“已装船”列中缺少的值。
df.replace_missing_median('age')
df.replace_missing_mostcommon('embarked')
对年龄和票价列中的值进行规范化,并对性别、阶级和上船特征进行编码。
df.onehot_encode('sex', 'pclass', 'embarked', keep_col=False)
df.normalize_numeric('fare', 'age')

使用 Aethos,转换器适合训练集并应用于测试集。只需一行代码,您的训练集和测试集就已经完成了转换。
系统模型化
来训练 Sklearn,XGBoost,LightGBM 等。使用 Aethos 的模型,首先从数据争论对象转换到模型对象。
model = at.Model(df)
模型对象的行为方式与数据对象相同,因此如果您有已经处理过的数据,您可以用与数据对象相同的方式启动模型对象。
接下来,我们将使用 MLFlow 启用实验跟踪。
at.options.track_experiments = True
现在一切都设置好了,训练模型并获得预测就像这样简单:
lr = model.LogisticRegression(C=0.1)

要使用 Gridsearch 使用最佳参数训练模型,请在初始化模型时指定gridsearch参数。
lr = model.LogisticRegression(gridsearch={'C': [0.01, 0.1]}, tol=0.001)

这将返回具有由 Gridsearch 评分方法定义的最佳参数的模型。
最后,如果您想交叉验证您的模型,有几个选项:
lr = model.LogisticRegression(cv=5, C=0.001)
这将对您的模型执行 5 重交叉验证,并显示平均分数和学习曲线,以帮助衡量数据质量、过度拟合和欠拟合。


您也可以将它与 Gridsearch 一起使用:
lr = model.LogisticRegression(cv=5, gridsearch={'C': [0.01, 0.1]}, tol=0.001)
这将首先使用 Gridsearch 用最佳参数训练一个模型,然后交叉验证它。目前支持的交叉验证方法有 k-fold 和分层 k-fold。

要一次训练多个模型(串行或并行),在定义模型时指定run参数。
lr = model.LogisticRegression(cv=5, gridsearch={'C': [0.01, 0.1]}, tol=0.001, run=False)
让我们再找几个模特来训练:
model.DecisionTreeClassification(run=False)
model.RandomForestClassification(run=False)
model.LightGBMClassification(run=False)
您可以通过运行以下命令来查看排队模型和定型模型:
model.list_models()

现在,默认情况下,并行运行所有排队的模型:
dt, rf, lgbm = model.run_models()
你现在可以去喝杯咖啡或吃顿饭,而你所有的模型同时接受训练!
分析模型
默认情况下,您训练的每个模型都有一个名称。这允许您使用相同的模型对象和 API 训练同一模型的多个版本,同时仍然可以访问每个单独模型的结果。在 Jupyter 笔记本中按下Shift + Tab可以在功能头看到每个型号的默认名称。

您也可以通过在初始化函数时指定您选择的名称来更改模型名称。
首先,让我们比较一下我们训练过的所有模型:
model.compare_models()

您可以根据各种指标查看每个型号的性能。在数据科学项目中,有一些预定义的指标,您希望将模型与这些指标进行比较(如果没有,您应该这样做)。您可以通过选项来指定那些项目度量。
at.options.project_metrics = ['Accuracy', 'Balanced Accuracy', 'Zero One Loss']
现在,当您比较模型时,您将只看到项目度量。
model.compare_models()

您还可以通过运行以下命令来查看单个模型的指标:
dt.metrics() # Shows metrics for the Decision Tree model
rf.metrics() # Shows metrics for the Random Forest model
lgbm.metrics() # Shows metrics for the LightGBM model
您可以使用相同的 API 来查看每个模型的 RoC 曲线、混淆矩阵、错误分类预测的指数等。
dt.confusion_matrix(output_file='confusion_matrix.png') # Confusion matrix for the Decision Tree model

rf.confusion_matrix(output_file='confusion_matrix.png') # Confusion matrix for the random forest model

解释模型
Aethos 配备了自动 SHAP 用例来解释每个模型。您可以查看任何模型的 force、decision、summary 和 dependence 图,每个图都有可定制的参数来满足您的用例。
通过指定output_file参数,Aethos 知道为特定的模型保存和跟踪这个工件。
lgbm.decision_plot(output_file='decision_plot.png')

lgbm.force_plot(output_file='force_plot.png')

lgbm.shap_get_misclassified_index()
# [2, 10, 21, 38, 43, 57, 62, 69, 70, 85, 89, 91, 96, 98, 108, 117, 128, 129, 139, 141, 146, 156, 165, 167, 169]
lgbm.force_plot(2, output_file='force_plot_2.png')

lgbm.summary_plot(output_file='summary_plot.png')

lgbm.dependence_plot('fare', output_file='dep_plot.png')

要获得更自动化的体验,请运行:
lgbm.interpret_model()

这将显示一个交互式仪表板,您可以在其中使用石灰、SHAP、莫里斯敏感度等来解释您的模型的预测。
在 MLFlow 中查看模型
当我们训练模型和查看结果时,我们的实验会被自动跟踪。如果从命令行运行aethos mlflow-ui,将会启动一个本地 MLFlow 实例,供您查看实验的结果和工件。导航到 localhost:10000。

您的每个模型和任何保存的工件都被跟踪,并且可以从 UI 中查看,包括参数和度量!要查看和下载模型工件,包括特定模型的 pickle 文件,单击 date 列中的超链接。

您将获得模型的度量以及模型参数的详细分解,并且在底部,您将看到您为特定模型保存的所有工件。

注意:交叉验证学习曲线和平均分数图总是作为工件保存。
要更改实验的名称(默认为 my-experiment),请在启动模型对象(exp_name)时指定名称,否则每个模型都将被添加到 my-experiment 中。

服务模型
一旦您决定了要用于预测的模型,您就可以使用 FastAPI 和 Gunicorn 通过 RESTful API 轻松地生成所需的文件来为模型提供服务。
dt.to_service('titanic')

如果您熟悉 MLFlow,您也可以随时使用它来服务您的模型。打开保存部署文件的终端。我建议将这些文件转移到 git 存储库中,以便对模型和服务文件进行版本控制。
按照说明构建 docker 容器,然后独立运行它。
docker build -t titanic:1.0.0 ./
docker run -d --name titanic -p 1234:80 titanic:1.0.0
现在导航到 localhost:1234/docs 来测试您的 API。现在,您可以通过向 127.0.0.1:1234/predict 发送 POST 请求来提供预测服务。


现在有一件重要的事情需要注意,这是运行默认配置,在没有保护它并为生产使用配置服务器之前,不应在生产中使用。在未来,我将自己添加这些配置,这样您就可以或多或少地将一个模型直接投入生产,只需进行最小的配置更改。
这个特性仍处于初级阶段,我将继续积极开发和改进它。
你还能做什么?
虽然这篇文章是一个半全面的 Aethos 建模指南,但您也可以运行统计测试,如 T-Test、Anovas,使用预训练的模型,如 BERT 和 XLNet 进行情感分析和问题回答,使用 TextRank 执行提取摘要,训练 gensim LDA 模型,以及聚类、异常检测和回归模型。
完整的例子可以看这里!
反馈
我鼓励所有关于这个帖子或 Aethos 的反馈。你可以在推特上给我发信息,或者在 sidhuashton@gmail.com 给我发电子邮件。
任何 bug 或功能请求,请在 Github repo 上创建问题。我欢迎所有功能请求和任何贡献。如果你想为一个开源项目做贡献,这个项目是一个很好的开端——如果你需要帮助,可以随时给我发消息。
人工智能的训练、验证和测试阶段——以一种你永远不会忘记的方式解释
跟着憨豆先生参加他的第一次微积分考试…

像往常一样,我们最喜欢的笨手笨脚的人忘了买指定的教科书,但是,幸运的是,他从黑板上草草写下了一个方程(现在是 90 年代),现在他有一个晚上的时间来学习足够通过你的考试。顺便说一句,在这个故事中,你是他的微积分教授——我很欣赏你的耐心。
如果你在机器学习 (ML)和人工智能的背景下听说过验证,但你不太确定所有这些大惊小怪都是关于什么的——验证只是有史以来最重要的 应用人工智能 概念之一,没什么大不了的——那么这就是你一直在等待的类比。想象一下憨豆先生即将参加他的第一次微积分考试…
如果你更喜欢视频版本,我来告诉你这个例子。
ML/AI 中的训练和调整阶段
憨豆先生找到了他藏起来的那个方程式,并开始为明天的考试做准备。他没有其他例子(数据点)或资源来帮助他,他也懒得写下任何明确的规则来解释微积分是如何工作的,所以他所能做的就是在他的方程中寻找模式:

除非另有说明,否则学生可以假定为\(x \in \mathbb{R}\)美元。
就像一个 AI 算法一样,他的目标是找到一个数据模式,他可以把它变成一个配方(模型),成功地把他从“=”左边的输入带到右边的输出。这正是在一个应用人工智能项目的训练和调整步骤中所发生的事情(在我的分步指南中的步骤 6-7)。**
培训就是根据可用示例中的模式制作食谱。
所以让我们再仔细看看那个例子…我们能看到什么模式吗?

找到了。憨豆先生找到了一个!抓住那个 8,把它翻过来,就有答案了。

这个配方当然适用于这个例子。非常合身。恭喜你!憨豆先生该不该直接冲考?根据我评分的一些本科考试,答案并不明显。但是我敢打赌,你——我亲爱的开明的读者——比那更好。如果你是憨豆先生,你会先做些别的事情,对吗?
我几乎能听到你从这里一直在喊。“试试另一个例子!!!"没错。你和憨豆先生的不同之处在于,你们都会尝试另一个……但是你足够聪明,可以在考试前做这件事。换句话说,是在一个有挽回余地的低风险环境中,而不是在一个没有倒退的高风险环境中。
ML/AI 中的验证阶段
机器学习中的验证( step 8 )正是如此。验证为学生创造了一个安全、低风险的机会,让他们在没有明确研究过的例子上尝试他们的食谱。这就是他们获得是否准备好真正考试的信号的方式。
验证给了你的项目一些挽回的空间。
(憨豆先生很幸运有你这样的教授。谢谢你给他发来额外的例子,让他在舒适安全的宿舍里尝试!)
所以,憨豆先生准备尝试一个新的。我们来验证一下!

憨豆先生应用他的模型…

他把那个 5 翻过来,嘣!答案已经送达。

然后他会根据你提供的正确答案检查你的表现,然后…

可悲的是,数学不是这样工作的。
迭代时间!
现在怎么办?
憨豆先生看到了他的模型表现有多差,他有一个选择:要么从头开始,要么就去考试。
如果他选择重新开始,他可能会回到培训(步骤 6 )并在他已经拥有的数据中搜索新的模式(那个微不足道的数据点),或者他可能会更进一步,并考虑调整他正在处理的数据(如果是我,我会求你提供一堆额外的例子,这将意味着回到我的指南中的步骤 2)。
学生(和 ML/AI 工程师)被允许在寻找模式和验证他们的新配方之间来回跳跃。(但是不要熬夜学习!)
最后,在凌晨 3 点,憨豆先生找到了一种模式,它可以正确地工作在您给他的所有验证示例上。

研究这些微积分对憨豆先生的外貌造成了损害。
您在凌晨 3:08 收到以下电子邮件:
“亲爱的教授,
我发现了一个对你给我的那些验证例子 100%有效的配方。我可能是你教过的最好的微积分学生。不如我们节省时间,我跳过你的考试?我睡懒觉的时候你可以给我 A+。
真诚,
m . r . Bean
你说呢,教授?
ML/AI 中的测试阶段
你简直是在咆哮,“当然不是!!!!"
为什么不呢?通过回答这个问题,你就已经回答了为什么每个自尊的应用人工智能项目都需要验证和测试。
每一个自尊的应用 ML/AI 项目都需要验证和测试。
憨豆先生可能在欺骗自己,以为自己理解了那些方程式,而实际上他并不理解。据你所知,他可能已经拼凑出一个疯狂的食谱,过度拟合,呃,扭曲自己,以适应那些他整晚都在盯着的具体例子。也许他已经记住了所有的答案,这就是他的秘诀——凭记忆查找答案。不能保证他理解的任何东西足以处理新问题。
去参加考试,憨豆先生!
所以我们不要相信他的话。ML/AI 的一个关键原则是我们从不相信…我们测试。
ML/AI 布丁的证明总是在吃的过程中。
如果憨豆先生试图通过告诉我他的配方或“理解”来说服我放过他——嗯,这很可爱,但就像所有经验丰富的应用人工智能专家一样,如果他没有向我展示他在从未见过的新例子上的表现,我不会信任他。(虽然理解他的食谱可以帮助我帮助他改进,所以可解释性在训练阶段非常方便。)
ML/AI 的一个关键原则是我们从不相信…我们测试。
当然,憨豆先生尽了最大努力,他的最终食谱反映了他最好的理解。这并不意味着他应该及格。不幸的是,有时一个学生(或系统)的最好成绩还不够好。也许他的例子(数据)不够丰富,也许他没有足够的数据点来揭示有用的模式(抱歉,仅仅一个例子永远不足以学习如何解决极限微积分问题),或者也许他的算法——呃,大脑——找不到获胜的秘诀。憨豆先生运气不好——当任务具有挑战性时,不是每个学生或人工智能系统都会成功。这就是生活。

那么,《憨豆先生的最好》够好吗?你应该给他一张通行证,让他继续下一步(在那里你生产他在野外的数学)还是应该委婉地建议他转专业?
测试阶段可以让你发现你的学生对任务的掌握程度。
让我们通过邀请他参加一个高风险的考试来找出答案,在这个考试中,他将在他可能记不住的新例子上尝试他的食谱…如果他这次失败了,游戏就结束了。祝你好运,憨豆先生!

本文是第 1 部分,共 2 部分。故事在这里继续。
感谢阅读!人工智能课程怎么样?
如果你在这里玩得开心,并且你正在寻找一个为初学者和专家设计的有趣的应用人工智能课程,这里有一个我为你制作的娱乐课程:
在这里欣赏整个课程播放列表:bit.ly/machinefriend
喜欢作者?与凯西·科兹尔科夫联系
让我们做朋友吧!你可以在 Twitter 、 YouTube 、 Substack 和 LinkedIn 上找到我。有兴趣让我在你的活动上发言吗?使用表格取得联系。
轨迹预测的发展(1/2)

詹姆斯·科尔曼在 Unsplash 上拍摄的照片
无人驾驶汽车
无人驾驶汽车依赖于此
“如果你认识到自动驾驶汽车将防止车祸,人工智能将负责减少世界上主要的死亡原因之一。”—马克·扎克伯格
每当我们想到人工智能世界,汽车行业立即浮现在我们脑海中。自动驾驶汽车是一个迷人的未来,似乎不是一个遥远的现实。我们只是坐在车里看电影,然后它带你去目的地。
但是汽车能完全自动驾驶,注意一个环境中的所有语境,有那么容易吗?在过去的几年中,已经发表了许多论文来预测汽车和行人未来可能的轨迹,这些轨迹是社会可以接受的。
问题:自动驾驶汽车最大的挑战之一会是什么?
回答:了解行人行为及其未来轨迹。
人类运动可以被描述为多模态,即人类有可能在任何给定的时刻向多个方向移动。而这种行为是自动驾驶汽车最大的挑战之一。因为他们必须穿越一个以人类为中心的世界。
在第一部分,我将简要讨论三篇论文,它们的主要目的是预测行人未来可能的轨迹。
社会 GAN
这是最初的论文之一,其中指出使用 GAN 来预测人类可能的轨迹。
这篇论文试图通过预测人类在社会上看似合理的未来轨迹来解决这个问题,这将有助于无人驾驶汽车做出正确的决定。
目标:
该文件旨在解决两大挑战:
- 在场景中的所有人之间建立一个计算高效的交互模型。
- 去学习和创造多种被社会接受的轨迹。
方法

图一。 截图来自社会甘研究论文
提出了一种基于 GAN 的编解码网络,该网络由每个人的 LSTM 网络和模拟他们之间交互的池模块组成。
整个模型(如图图 1 所示。 ) 可以用 3 种不同的成分来表示:
发电机
发生器由编码器和解码器组成。对于每个人,编码器将输入作为 X_i。它嵌入每个人的位置,并在时间 t. 将其作为固定长度向量提供给 LSTM 单元
LSTM 权重在场景中的所有人之间共享,这将有助于池模块开发人与人之间的交互。
与之前的工作不同,本文使用了以下两种方法:
a)对于反向传播期间的简单训练过程,代替预测二元高斯分布,解码器直接产生人的位置的(x,y)坐标。
b)不是直接提供社会背景作为编码器的输入,而是提供一次作为解码器的输入。这导致速度增加到的 16x 倍。
鉴别器
鉴别器由一个编码器组成,每个人都有 LSTM 层。这个鉴别器的想法是区分真实轨迹和虚假轨迹。
理想情况下,如果这些轨迹不被社会接受或不可行,它应该将其归类为、。
汇集模块

图 2。 截图来自社会甘论文
这种方法的基本思想如图 2 所示。该方法计算人 1(用红色表示)和所有其他人(用蓝色和绿色表示)的相对位置。然后,它与隐藏状态连接,并通过 MLP(多层感知)独立处理。
最终,每个元素被顺序汇集以计算一个人的 1 汇集向量 P1。
这种方法减少了考虑特定网格内人员的限制(图 2 中的 S-Pool 网格)。)
损耗
本文使用了 3 种不同的损失:
- 对抗性损失:这种损失是典型的 GAN 损失,有助于区分真假轨迹。
- L2 损失:该损失获取预测轨迹和真实轨迹之间的距离,并测量生成的样本与真实样本之间的距离。
- 品种损失:这种损失有助于产生多种不同的轨迹,即多模态轨迹。这个想法很简单,对于每一个输入,从 N(0,1)中随机抽样‘z’来预测 N 个不同的可能结果。最终,选择具有最小 L2 值的最佳轨迹。

苏菲:细心的甘
这篇论文扩展了社会 GAN 的工作,并试图在物理和社会信息的帮助下预测智能体的未来路径。
虽然目的仍然与社交 GAN 相同,但本文也借助每帧图像添加了风景信息。
网络学习两种类型的注意力成分:
- 身体注意力:这种注意力成分有助于注意和处理周围的局部和全局空间信息。正如的论文 : “例如,当到达一条弯曲的路径时,我们更关注曲线而不是环境中的其他约束”
- 社会关注:在这个组件中,想法是给予周围环境中其他代理的运动和决策更多的关注。例如:“在走廊里行走时,我们更关注前面的人,而不是后面的人”
方法

图 3。 截图来自苏菲研究论文。
本文提出的方法分为 3 个模块(如图图 3 所示。)。
特征提取模块
该模块以两种不同的形式从输入中提取特征,第一作为每一帧的图像,第二作为每一帧的每个代理在某一时刻的状态' t '。
为了从图像中提取视觉特征,他们使用了 VGGnet-19 作为 CNN 网络。该网络的权重由 ImageNet 初始化。为了从所有代理的过去轨迹中提取特征,他们使用与社交 GAN 类似的方法,并使用 LSTM 作为编码器。
为了理解每个代理之间的交互并捕捉每个代理轨迹对另一个代理的影响,在社交 GAN 中使用了池模块。本文提到了这种方法的两个局限性:
- 最大值函数可能会丢弃输入的重要特征,因为它们可能会失去唯一性
- 最大值运算后,所有轨迹将被连接在一起,这可能会导致具有相同的连接要素制图表达。
由于这些限制,它们定义了一个排序结构。在此,他们用排序作为排列不变函数而不是 max (用于社交 GAN)。他们通过计算目标代理和其他代理之间的欧几里德距离来对代理进行排序。
注意模块
在身体或社会注意力的帮助下,本模块有助于突出下一模块输入的重要信息。
这个想法是,随着人类更多地关注环境中的某些障碍或物体,如即将到来的转弯或走向它们的人,需要学习类似的注意力。
如前所述,这个网络倾向于学习两种不同的注意力。
在物理注意中,来自 GAN 模块的 LSTM 的隐藏状态和来自视觉环境的学习特征被提供作为输入。这有助于了解更多的物理约束,如路径是直的还是弯曲的,当前的移动方向和位置是什么,等等。
在社会关注中,从特征模块学习到的 LSTM 特征连同来自 GAN 模块的 LSTM 的隐藏状态被提供作为输入。这有助于关注所有对预测未来轨迹很重要的因素。
GAN 模块
该模块利用突出显示的输入特征,为每个智能体生成一条符合所有社会和物理规范的现实未来路径。
这个 GAN 模块主要受社交 GAN 的启发,几乎没有进一步的变化。
生成器的输入是从注意力模块中选择的特征以及从多元正态分布中采样的白噪声‘z’。
损耗
这种方法使用了 2 个损耗,也类似于社会 GAN。
- 对抗性损失:这种损失有助于学习区分生成样本和真实样本。
- L2 损耗:该损耗类似于社会 GAN 中使用的“品种损耗”。
社交方式
在这篇论文中,他们还试图预测行人的轨迹及其相互作用。然而,他们也致力于解决所有先前方法中的一个问题:模式崩溃。
模式崩溃 是多模态的反义词。在这种情况下,发生器试图产生相似的样本或相同的样本集,从而在输出中产生相似的模式。
为了解决模式崩溃问题,本文使用 info-GAN 代替 L2 损耗或品种损耗。
方法

图 4。 截图来自社会方式研究论文
该方法包括 3 个不同的部分:
发电机
发生器由一个编码器-解码器网络组成。在这里,每个代理的过去轨迹被输入到各自的 LSTM-E(编码器)中,该编码器对代理的历史进行编码。每个 LSTM-E 的输出被输入到注意力集中和解码器中。
对于解码,来自 LSTM E 的未来轨迹、隐藏状态、噪声向量“z”、潜在代码“c”和来自注意力集中的重要交互代理特征被馈送到解码器。
潜在代码“c”有助于最大化所产生的输出的分布和“c”之间的交互信息的下限。
注意力集中
这篇论文使用了与《苏菲:一个细心的甘》相似的方法。
然而,除了代理之间的欧几里德距离(在 Sohpie 中使用)之外,还使用了另外两个特征:
- 方位角 : “代理 1 的速度向量与连接代理 1 和代理 2 的向量之间的角度。”
- 最近接近的距离 : “如果两个智能体都保持当前速度,则两个智能体在未来可能达到的最小距离。”
注意力权重由隐藏状态和上述三个特征之间的标量积和 softmax 运算获得,而不是排序。
鉴别器
鉴别器由基于 LSTM 的多层密集编码器组成。从发生器为每种制剂生成的轨迹和地面实况轨迹被输入鉴别器。
作为输出,提供了生成的轨迹是真实的概率。
损耗
此过程中使用了两种损耗:
- 对抗性损失:这是正常的 GAN 损失,有助于区分真假样品。
- 信息损失:这种损失的基本思想是最大化互信息。这是通过最小化显著变量‘c’的负损失可能性实现的。
结果
这三篇论文都试图从以前的方法中学习,并获得了一些新的见解。
有两个指标用于评估该应用程序:
- 【ADE】:生成轨迹与地面真实轨迹在所有预测时间步上的平均 L2 距离。
- 【最终位移误差(FDE) :这是在最终预测时间步长上,生成轨迹与地面真实轨迹之间的最小距离。
有 5 个数据集,用作该应用的基准测试。所有这些方法都在所有这些数据集上进行了测试,它们提供了有价值的可比结果。

图 5。 社交方式结果截图
从图 5。可以说,所有三种方法在一些数据集上都显示出有希望的结果。然而,我认为随着超参数调整和小的调整,结果也可能会混乱。我相信这三种方法都有一定的优势,可以用于这方面的进一步研究。
结论
对场景中的人体运动预测以及人与人的交互进行建模的问题对于自动驾驶汽车来说是具有挑战性的,但也是至关重要的。如果不对这种行为进行建模,无人驾驶汽车就不可能完全投入使用。
如何对人与人的交互进行建模是上述方法之间的主要区别。从理论和建议的方法来看,我认为关注两个智能体之间的距离和方位角是前进的最关键的方法之一。
但这完全是我的观点。可以有多种方式来实现和增强这一点。我们也将在第 2 部分中看到这一点。
以自动驾驶汽车为重点,我将在第 2 部分继续介绍更多方法,重点是汽车的轨迹预测。
我很乐意就这份文件和这一领域进行任何进一步的讨论。你可以在这里发表评论,或者通过我的 LinkedIn 个人资料联系我。
参考
- 亚历山大·阿拉希,克拉塔斯·戈埃尔,社交 LSTM:拥挤空间中的人类轨迹预测,CVPR 2016
- 阿格里姆·古普塔,贾斯廷·约翰逊,李菲菲,社会 GAN:具有生成性对抗网络的社会可接受轨迹,CVPR 2018
- Amir Sadeghian,Vineet Kosaraju,Ali Sadeghian, Sophie:一个专注的 GAN,用于预测符合社会和物理约束的路径,CVPR 2018
- 卡伦·西蒙扬,安德鲁·齐泽曼,用于大规模图像识别的极深度卷积网络,ICLR,2015
- 社会方式:用 GANs 学习行人轨迹的多模态分布,CVPR 2019
转录器
基于变压器的手写识别体系结构

[ 图像来源
数字化手写文档以改善存储、访问、搜索和分析是一个迫在眉睫的挑战。在深度学习革命之前,不存在以可扩展的方式实现这一目标的明确路径。
随着计算机视觉和语言处理的进步,可靠的自动化手写识别指日可待。为此,我努力设计了一个实用的应用程序,平衡了准确性、可推广性和推理速度。这篇文章是对这一尝试的回顾,也是对设计选择和训练程序的解释。在这里查看一个演示。
阅读手写文本是非常困难的。风格(如草书与印刷体和印刷体)、大小、间距、修饰和易读性之间存在极大的差异。拼写错误、划掉和遗漏也很常见。
解决这个问题的许多方法将任务分成 3 个独立的部分:分割、特征提取和分类。【1,13,2】
- 检测和分割图像中的文本区域。
- 从每个文本片段中提取高维特征。
- 从给定的词汇(例如单词或字符)中对这些特征进行分类。
鉴于手写的异质性,自动文本检测和分割可能容易出错,并且通常需要定制的预处理。另一种方法是将文本识别框架为序列对序列问题。二维图像输入和一维文本输出。NLP 中表现最好的序列转导模型包括一个编码器-解码器结构,通常包括一个注意机制。其中最具革命性的可能是转换器架构【3】,它的独特之处在于它仅依赖于对输入和输出的编码表示,而不借助任何形式的递归或卷积。在这里可以找到对 Transformer 架构的一个很好的解释:带注释的 Transformer 。
这并不是第一次尝试在端到端的方法中用注意力取代分段。【5】然而,我没有用注意力模块来增加传统的递归层,而是利用 Transformer 架构来超越以前的最先进的结果。亲自试用转录器应用!
架构
Transformer 架构基于多头关注(“比例点积”)层,之后是按位置完全连接的网络。点积或乘法注意力比加法注意力更快(计算效率更高),尽管在更大的维度上表现较差。缩放有助于调整乘法的收缩梯度。根据这篇论文,“多头注意力允许模型在不同位置共同关注来自不同表征子空间的信息。”编码器和解码器都利用自我关注层。解码器还包括编码器输出上的源注意力层。
图像适配器
机器翻译(Transformer 就是为机器翻译而设计的)和手写识别之间的显著区别在于,图像在输入到 Transformer 之前必须被处理成一个连续的表示。
为此,预训练的卷积神经网络(ResNet18)在最终的最大池层之前被截断,以输出空间特征图。沿空间维度对要素地图进行展平、归一化和转置,以创建粗略的一维表示序列。(注 : ResNet18、ResNet34 和 Xception 模型经过测试,精度没有显著差异。我选择了 3 个中最小的一个。)
编码器
Transformer 编码器中的自我关注有助于从图像适配器输出的扁平化空间表示中解析语义(和顺序)关系。
解码器
变压器解码器是自动回归的,这意味着先前生成的令牌通过自我关注被考虑在内。为了加速训练,“之前生成的令牌”被实际的目标序列嘲讽(ala 老师强制)。然后,解码器可以使用更高效的批量矩阵乘法来一次生成所有输出令牌,而不是进行推理所需的耗时的顺序解码。在注意力计算中使用字节掩码,以防止解码器在正在生成的内容之前关注位置。
序列的二维空间编码
因为转换器不使用任何卷积或递归,所以模型不包含内置的空间或顺序感知。需要位置编码。在原始论文中,使用了固定的正弦编码。作者推测,这将有助于模型学习相对位置,并推断出比训练中看到的更长的序列长度。
CNN 具有隐含的位置感知,因此输入图像不需要额外的编码。然而,目标序列确实需要位置编码。有趣的是,当包含换行符(' \n ')时,多行段落有一个潜在的第二维度。利用这个换行符,我使用了一个已学习的二维空间编码方案,该方案基于离最近的换行符的距离(大约宽度)和自序列开始以来的换行符的数量(大约高度)。
(注:语言模型也作为模型的补充进行了实验。一个预先训练的 AWD-LSTM【6】语言模型产生了准确性的提高,但推理成本太大。双向转换编码器【7】语言模型是另一个潜在的选择。BERT 结合了令牌两端的上下文,并且是非自回归的,这意味着推理时间的增加是最小的。由于 GPU 的限制,我用一个经过提炼的版本【8】进行了实验,但是即使在预训练之后也没有看到准确性的提高。)
数据
这项任务的大部分训练都是基于流行的 IAM 手写数据库。该数据集由 657 位不同作者的 1539 页扫描手写文本组成。IAM 是建立在 20 世纪 70 年代编纂的英国英语文本语料库(Lancaster-Oslo/Bergen)的基础上的。这些文本的内容跨越包括新闻报道,科学写作,散文和流行的传说体裁。
理解深度学习算法性能受训练数据质量的约束;这个数据集对于现代的美式英语应用程序来说并不理想。然而,作为最大的公开可用的带注释的手写图像汇编,它是该领域中最受研究应用欢迎的数据集,并提供了与以前的体系结构的良好基线比较。
除了 1,539 页的文本之外,数据集还被进一步分割成约 13k 行和约 115k 孤立词。这些额外的分段对于扩展训练数据集至关重要。
在扩展之前,需要对数据进行彻底检查,并手动纠正注释/分段错误。删除了 15 页的测试集,即大约 1%的总数据。小测试规模并不理想,但数据集的整体规模较小,这是必要的。
单词组合
通过组合从分词列表中随机选择的图像来创建第二数据集。从一个单词到 4 行,每行 4 个单词,以随机配置创建了 50k 个新图像。
行连接
另一个数据集是通过连接随机选择的长度为 3 至 13 行的行图像(按高度归一化)而创建的。两万张这样的图片被制作出来。
合成字体
另一个策略是使用谷歌字体来创建类似手写文字的图像。来自维基百科、IMDB 电影评论和开源书籍的文本使用 95 种不同大小的手写字体进行渲染,以创建大约 129k 不同长度的图像。背景噪声、模糊、随机填充/裁剪、像素倾斜等。以反映原始数据集以及一般手写文本的有机不规则性。
下载的手写样本
为了提高算法在 IAM 数据集之外的泛化性能,另一个数据集由 161 幅人工注释的图像构成,这些图像可在互联网上公开获得。这些图像用 11 种不同的图像调制组合进行处理,产生包含 1771 个图像的最终数据集。
标记化
对于涉及文本序列的任务,标记化至关重要。字符标记化有很多好处。字符大小是相对标准的。词汇表很小,几乎没有超出词汇表的标记。然而,字符推断是缓慢的(1000+字符的自回归顺序解码需要时间……),并且如上所述,手写的异质性意味着字符经常重叠、难以辨认,甚至被省略。
单词标记化是直观的,因为在人类阅读中,单词似乎优先于字符*。推理时间比用字符快得多。但是,为了限制词汇表外的标记,词汇表的大小必须非常大。
一个固定长度的子词分词器 SentencePiece 被用作折衷。使用一个习得的、单字母语言模型编码【9】,,我创建了一个 10k 子词单位的词汇表。重要的是,添加了额外的特殊标记来表示空格、换行符、字母和单词的大写指示符以及标点符号。(注意 : 10k、30k 和 50k 词汇表都经过了测试,10k 是性能最好的,同时保持模型占用空间较小。赢赢!)
子词标记化提供了良好的推理速度、适中的词汇量和很少的词汇外标记。然而,文本图像显然不能划分成子词单元。我发现同时使用字符和子词标记进行训练有助于编码更健壮的特征表示,并提高两者的准确性。
通过一封特别的信向你传达一个爱好者的信息。TL;dr:而单词被识别为组块;人类阅读(尤其是在困难的条件下)包括单词和字符两个过程。]
培训
数据集被组合成 3 个连续的训练组。该模型首先在单词组合数据集(256 像素的 50k 图像)上进行训练,以比较建筑设计决策,测试超参数设置,并为后面的组进行预训练。然后,该模型在合成字体生成的数据(512 像素的 129k 图像)上进行训练,以读取长的、格式良好的文本序列。最终的手写数据集(25k 图像,512 像素)由主要的 IAM 页面图像、连接的行和下载的文本图像组成,用于微调手写文本的模型。
数据增强包括:旋转、扭曲、对比度和亮度的随机变化。
转录参数
- 模型尺寸:512
- 激活函数:GELU [10]
- 层数:4
- 注意头:8
- 辍学率:0.1
- AdamW 优化器[11]:(修复自适应梯度算法的权重衰减)
- 最大学习率:1e–3(根据“1 周期”政策[12]而变化)
- 贝塔系数:(0.95,0.99),ε:1e–8
- 动量:(0.95,0.85)
- 重量衰减:1e–2
- 标签平滑度:0.1
结果
以下与以前发表的架构的比较在科学上并不严谨,因为这个项目的目的是构建一个实用的工具,而不是发表一篇学术论文。因此,训练/测试分割并不是所有架构的标准,其他架构的结果基于预先分段的行级数据集,而 Transcribr 架构是针对整页数据集进行测量的。然而,使用公布的结果作为一个松散的比较,Transcribr 架构证明是非常有竞争力的。


来自 IAM 手写测试数据集的 4 个图像的 Transcribr(单词块令牌)结果
局限性
在这个项目中,我受到了一些限制。主要障碍是直接影响计算资源的预算。培训是在 NVIDIA P6000 (24GB 内存,3840 个 CUDA 内核)上进行的。对转录应用的推断是在一个只有 1GB RAM 的不起眼的 1CPU 上进行的,并且以可怜的大约 3 令牌/秒的速率转录图像:(
(注:在一台 2015 款 MacBook Pro 上用 4 核 16GB RAM 进行本地测试推理,产生了约 30 令牌/秒的良好转录率。)
数据的数量和质量是另一个因素。合成数据和增强在实现这些结果方面至关重要,但仍然是多样化、高质量笔迹样本的拙劣替代品。当在包含不同的数据集外图像的测试集上测试时,Transcribr 的性能相当差。


4 个非数据集测试图像的转录结果
架构选择优先考虑推理时间,而不是其他因素,包括准确性。为此,该模型尽可能保持精简,以大约 50.8 米的参数进行称重。使用贪婪解码代替更精确但昂贵的波束搜索。transcript br 应用使用不太准确但更快的单词片段标记化方案。
结论
我没有达到最初的目标,即构建一个有用的(快速、通用且准确的)工具。Transcribr 既不快速也不通用:/可以使用量化和提取等技术来进一步减小模型大小,将 python 模型转换为 C++将加快推理速度。从更多种类的来源和上下文中收集更多的手写图像将提高概括的准确性。
然而,从学术数据集的狭窄范围来看,我的结果令人鼓舞。我使用的一些新技术包括:
- 使用转换器架构进行手写识别
- 基于换行符的 2d 学习位置编码(' \n ')
- 句子片段标记化,而不是单词或字符
- 同时训练令牌和字符解码器
- 使用手写字体生成合成数据
这些技术使 Transcribr 的表现超过了之前发表的结果,并表明即使是财力不足的独立研究人员也可以帮助推动深度学习的边界(研究,如果不是实际的话)。
更新(2020 年 3 月)
量化和 TorchScript 序列化将模型大小从 203MB 减少到 21MB,并将转录速率提高到更合理的约 15 令牌/秒。生产应用程序现在大约需要 10 秒来处理测试集中的图像,而不是以前可怕的 1 分钟。巨大的进步!
链接
-
[1] J. Chung & T. Delteil,“一种计算高效的全页脱机手写文本识别流水线方法”,2019 年
-
[2] C. Wigington 等人,“开始、跟随、阅读:端到端全页手写识别”,2018
-
[3] A. Vaswani 等人,“注意力是你所需要的全部,2017
-
[4] T. Bluche,“用于端到端手写段落识别的联合行分割与转录”,2016
-
[5] T. Bluche 等人,“扫描、出席和阅读:带 MDLSTM 注意力的端到端手写段落识别”,2016
-
[6] S. Merity 等人,“正则化和优化 LSTM 语言模型”,2017
-
[7] J. Devlin 等人,“ BERT:用于语言理解的深度双向变换器的预训练”,2019
-
[8] V. Sanh 等人,“蒸馏伯特,伯特的蒸馏版本:更小、更快、更便宜、更轻”,2020 年
-
[9] T. Kudo,“子词正则化:用多个候选子词改进神经网络翻译模型”,2018
-
[10] D. Hendrycks & K. Gimpel,“高斯误差线性单位(GELUS) ”,2018 年
-
[11] I. Loshchilov & F. Hutter,“解耦权重衰减正则化”,2019 年
-
[12] L. Smith,“神经网络超参数的规范方法:第 1 部分—学习速率、批量大小、动量和权重衰减”,2018 年
-
迪库松夫人
将艺术图像风格转换为任何内容图像
深度学习概念和实用程序,以开始神经风格转移!

你有过被过去的革命画家所画的愿望吗?你有没有被画家遗留在这个世界上的错综复杂和精美的艺术逗乐过?让我们深入探讨如何使用机器学习来使用这些无可挑剔的风格重现我们的内容。到本文结束时,您将熟悉各种机器学习概念和实用程序,它们将使您能够将任何样式转换为自己选择的任何内容图像。
卷积神经网络
卷积神经网络也称为 CNN,是神经类型转移所需的基本构建模块。这些类型的深度网络用于检测图像中的图案/笔画,并且通常用于图像分类和生成。下面是一个基本 CNN 的结构。

CNN 意味着在一个层中的部分激活上进行卷积,由许多层上的过滤器(内核)大小定义,直到我们最终到达我们的输出层,它根据遇到的输入对我们的图像进行分类。你可以从这里熟悉 CNN 的各种细微差别。
我们的主要想法是使用 CNN 中间层提供的不同激活,使用我们的内容和风格图像作为输入。我们将定义两个独立的 损失函数 ,内容损失和样式损失,这将跟踪我们生成的图像与我们的内容图像和样式图像有多“远”。我们的神经网络的目标将是最小化这些损失函数,从而在内容和风格方面改进我们生成的图像。
迁移学习
我们将使用预先训练的模型,而不是从头开始训练 CNN 模型,这将节省我们所有的训练和计算。我们将使用一个 VGG-19 模型,这是一个 19 层的 CNN。使用预训练模型背后的主要思想是,使用在不同任务上训练的模型,并将该学习应用于新任务。您可以使用任何带有预训练权重的 VGG19 开源实现。预训练的重量可以从这里获得。这个是我用的实现,礼遇 deeplearning.ai 。
神经网络模型需要大量的计算和时间,因此,GPU 肯定会派上用场。一定要试试 Google Colab ,它可以让你在一次会话中免费访问 GPU 超过 12 个小时。
神经类型转移
必需的包
我们将使用 TensorFlow 作为我们的深度学习框架。还导入了其他几个库来启用一些实用程序,如保存、导入、调整大小和显示图像。
注意:nst_utils 不是一个库,而是我们目录中的 python 文件的名称,在这个目录中,我们实现并存储了预先训练好的 VGG-19 模型。
内容图像和样式图像
我们将使用巴黎的卢浮宫博物馆作为我们的内容图像,砂岩作为我们的风格图像。

卢浮宫博物馆

砂岩
标准化数据
我们需要根据我们的 VGG-19 模型所需的输入来预处理我们的图像。该图像由 3 个 RGB 通道上 0–255 范围内的像素值组成。图像也有不同的分辨率,取决于它包含的像素数量。
注意:输入图像中的通道对于彩色图像(即 RGB 图像)保持为 3,而对于灰度图像保持为 1。根据分辨率,改变的是宽度和高度,即像素数。
假设我们的图像有一个维度(W,H,C),其中 W 和 H 分别是宽度和高度,C 是通道的数量。需要输入到我们的 VGG-19 模型中的输入张量的维数必须是(m,300,400,3)并且是归一化的。变量 m,也称为批处理大小 i 是一个超参数,它通过使用 np.expand_dims()实用程序扩展我们的图像维度来处理单个图像训练示例,从而被设置为 1。我们的函数返回预处理过的图像。
内容成本
生成的图像应该在内容上与我们的内容图像相似。为此,我们从一个层中选择激活来表示我们的内容图像的激活。CNN 中的早期层检测图像的边缘和纹理等低级特征,而较深的层检测对象等高级特征。我们选择了最后一层的激活来代表我们内容的激活。这又是一个超参数,可以随意调整结果。
在为我们挑选了合适的中间层之后,我们将内容图像设置为我们的 VGG-19 模型的输入,并通过正向传播获得 Ac 作为激活。我们对生成的图像重复相同的过程,并获取 Ag 作为激活。注意为了清楚起见,Ac 和 Ag 是多维向量。内容成本 J 定义为:

风格成本
直观上,图像的风格可以被视为不同通道的纹理和像素相对于彼此如何变化。风格可以通过 Gram 矩阵定量定义。在这种情况下,Gram 矩阵捕获矩阵与其转置矩阵的内积。风格也可以被识别为激活之间的相关性,在单个层中的不同通道之间的相关性。为了计算不同通道之间的相关性,我们得到维数为(nw,nh,nc)的 3-D 激活输出,并将其展平到(nh*nw,nc)的维数。

计算这种扁平化版本的 gram 矩阵实质上将为我们提供一个通道(对应于一行)中不同激活之间的相关性,以及不同通道(对应于一列)。

在计算了 gram 矩阵之后,为了定义样式成本,我们选择了一个样式层,即 VGG-19 中的一个层,我们的样式成本将基于该层的激活。假设‘G’被定义为单个样式层的激活的 gram 矩阵,上标‘S’和‘G’分别表示作为样式图像和生成图像的输入图像。那么单层的样式成本被定义为:

最小化上述成本将最终帮助我们在生成的图像中重新定义风格。
该成本函数仅针对单层激活。如果我们从不同的层次【合并】风格成本,我们会得到更好的结果。为了计算样式成本,迭代多个样式层(一个超参数)。每一层都提供了反映单个层对样式贡献多少的权重。
在此功能中,可以调整可变的“权重”,以将权重分配给各个风格层。我们已经在所有的样式层之间保持了相等的权重。所有的权重总和为 1。
显示图像
为了显示并最好保存我们的输出图像,我们需要撤销我们对原始图像所做的所有更改。这包括反规格化和剪裁输出的像素,使我们的像素严格保持在 0–255 的范围内。
构建我们的模型
我们已经评估了构建模型所需的所有预处理和成本函数。为了构建我们的模型,我们首先用 Tensorflow 创建一个交互会话。交互式会话使自己成为默认会话,使我们在运行 run() 或 eval() 函数时不能显式调用我们的会话。
我们在目录中指定路径,从中可以获取我们的内容和样式图像。
我们将我们的网络最终将最小化的最终成本定义为内容成本和风格成本的加权和。

总成本
权重和𝛽也是超参数,可以对其进行调整,以便为我们生成的图像中的内容和风格分配优先级。它们定义了内容和风格之间的相对权重。
现在让我们将所有的函数编译成最终的模型!
我们分别从内容和样式路径加载内容和样式图像,并定义成本函数所需的内容和样式层(超参数)。
在我们的模型中,我们已经初始化了生成的图像(变量“input_image”)作为我们的内容图像。这允许我们的模型更快地收敛,因为我们生成的图像将更快地匹配并最终收敛到我们的内容图像。
注意:不要简单的赋值 input_image = content_image。这将通过引用分配输入图像,这将在输入图像发生变化时改变内容图像。
在加载我们预先训练的 VGG-19 模型之后,我们通过将内容图像和风格图像分别作为输入通过我们的 CNN 网络来计算内容成本和风格成本。
在定义了总成本之后,使用带有参数和𝛽的内容成本和样式成本,我们将这个总成本传递给我们的优化器来最小化。优化器是一种梯度下降算法,帮助我们的模型更新权重,以便我们的成本函数收敛到最小值。
在我们的训练程序中,我们使用 Adam 作为优化算法。
我们以 2 的学习率运行我们的模型 2000 次迭代(时期)。在 Google Colab 提供的 GPU 上运行这个模型大约需要 5-10 分钟。
使用我们的模型返回的像素,并将这些像素传递给我们的实用程序“display_image ”,就可以输出我们生成的图像,如图所示。

生成的图像
我们可以清楚地分辨出砂岩风格的卢浮宫博物馆的轮廓,正如我们所希望的那样!继续用你自己的克洛德·莫内风格的照片来试试这个模型吧!
瞧啊。由此,我们成功地实现了神经风格的转移。剩下你要做的就是找到新的内容和风格,让你的内在艺术家展现出来!
你可以试着看看不同学习率和不同时期生成的图像的变化。为内容和风格成本指定不同的层次也是一个值得探索的有趣方面。尝试分析其他预训练模型,如 Inception 或 ResNet ,看看它们是否能提供任何进一步的改进。您可以跟随这个库来获得我们模型的编译版本,并分析对应于每次迭代的损失。
您可以关注更多资源:
万事如意!
图像分类中的迁移学习:我们真正需要多少训练数据?
训练数据集的大小如何影响通过迁移学习训练的分类器的性能的实验评估。

弗兰基·查马基在 Unsplash 上拍摄的照片
术语迁移学习指的是通过在某个(通常是大的)可用数据集上训练的神经网络获得的知识的杠杆作用,用于解决很少训练样本可用的新任务,将现有知识与从特定于任务的数据集的少数样本中学习的新知识相结合。因此,迁移学习通常与数据扩充等其他技术一起使用,以解决缺乏训练数据的问题。
但是,在实践中,迁移学习到底能有多大帮助,我们到底需要多少训练实例才能使它有效呢?
在这个故事中,我试图通过应用两种迁移学习技术(例如,特征提取和微调)来解决图像分类任务来回答这些问题,改变模型训练的示例数量,以便了解数据的缺乏如何影响所采用方法的有效性。
实验案例研究
为实验迁移学习选择的任务包括将花的图像分为 102 个不同的类别。这项任务的选择主要是由于 flowers 数据集的容易获得性,以及问题的领域,该问题足够通用,适合于有效地应用迁移学习,其中神经网络在众所周知的 ImageNet 数据集上进行了预训练。
所采用的数据集是由 M. Nilsback 和 A. Zisserman [3]创建的 102 类别花数据集,它是属于 102 个不同类别的 8189 个标记花图像的集合。对于每个类,有 40 到 258 个实例,并且所有数据集图像都有显著的比例、姿态和光照变化。102 个类别的详细列表以及相应的实例数量可在这里找到。

图 1 :从 102 类别数据集中提取的图像示例。
为了创建不同大小的训练数据集并评估它们如何影响训练网络的性能,将原始的花卉图像集分成训练集、验证集和测试集几次,每次采用不同的分割百分比。具体来说,使用下表中所示的百分比创建了三个不同的训练集(从现在起将被称为大型、中型和小型训练集)。

表 1 :用于执行实验的数据集的实例数量和分割百分比(参考完整的未分割 flowers 数据集)。
所有的分割都采用分层采样来执行,以避免引入采样偏差,并以这种方式确保所有获得的训练、验证和测试子集都代表整个初始图像集。
采用的策略
上述图像分类任务是通过采用两种流行的技术来解决的,这两种技术在利用预训练的 CNN 应用迁移学习时通常使用,即特征提取和微调。
特征抽出
特征提取基本上包括获取以前训练的网络的卷积基,通过它运行目标数据,并在输出的基础上训练新的分类器,如下图所示。

图 2 :应用于卷积神经网络的特征提取:在保持相同卷积基的同时交换分类器。“冻结”表示体重在训练期间不会更新。
堆叠在卷积基础之上的分类器可以是全连接层的堆叠,或者仅仅是单个全局池层,两者之后都是具有 softmax 激活功能的密集层。关于应该采用哪种分类器没有特定的规则,但是,如 Lin 等人所描述的。al [2],仅使用单个全局池层通常导致较少的过拟合,因为在该层中没有要优化的参数。
因此,由于实验中使用的训练集相对较小,所选择的分类器仅包括单个全局平均汇集层,其输出被直接馈送到 softmax 激活层,该层输出 102 种花类别中每一个的概率。
在训练过程中,只有顶级分类器的权重被更新,而卷积基的权重被“冻结”,从而保持不变。
以这种方式,浅层分类器从先前由源模型为其领域学习的现成表示中学习如何将花图像分类到可能的 102 个类别中。如果源域和目标域是相似的,那么这些表示可能对分类器有用,并且一旦被训练,转移的知识因此可以带来其性能的改进。
微调
微调可以被视为比特征提取更进一步的步骤,特征提取包括选择性地重新训练先前用于提取特征的卷积基的一些顶层。通过这种方式,由最后一层学习的源模型的更抽象的表示被稍微调整,以使它们与目标问题更相关。
这可以通过解冻卷积基的一些顶层,保持所有其他层冻结,并用先前用于特征提取的相同分类器联合训练卷积基来实现,如下图所示。

图 3 :特征提取对比微调。
需要指出的是,根据 F. Chollet 的说法,预训练卷积基的顶层只有在它上面的分类器已经被预先训练过的情况下才能被微调。原因是如果分类器还没有被训练,那么它的权重将被随机初始化。结果,在训练期间通过网络传播的误差信号将会太大,并且未冻结的权重将会被更新,从而破坏了先前由卷积基学习的抽象表示。
出于类似的原因,也建议采用比用于特征提取的学习速率更低的学习速率来执行微调。
此外,值得一提的是,只有顶层未被冻结的原因是,较低层指的是一般的与问题无关的特征,而顶层指的是与问题相关的特征,这些特征更多地与网络最初被训练的特定领域相关联。因此,由第一层学习的特征通常适合于处理大量的域,而由顶层学习的特征需要针对每个特定的域进行调整。
实验
所有实验都是在 Google 联合实验室云平台上使用 Keras 和 Tensorflow 2.0 作为后端开发和执行的。
ResNet50-v2 是被选择用于执行实验的源网络,并且 ImageNet 数据集是它已经被预训练的源域。该网络的选择完全是任意的,因为为了实验的目的,也可以使用在大型数据集上预先训练的任何其他最先进的网络(我只是决定在由 Keras 应用模块提供的几个预先训练的模型中选择一个基于 ResNet 的网络)。
对于在本文开头定义的每个生成的训练数据集(例如,大、中、小数据集),已经执行了以下实验:
- 特征抽出
- 微调
- 数据增强的特征提取
- 通过数据扩充进行微调
在每个实验中,加载的图像总是被调整到 224×224 像素。这是应用于数据集图像的唯一预处理操作。
数据扩充
数据扩充是一种技术,它由“组成,通过生成每个训练实例的许多真实变量来人工增加训练数据集的大小【1】。在所进行的实验中,这是通过四个简单的图像处理操作来实现的:
- 随机裁剪,最小裁剪尺寸等于原始图像尺寸的 90%
- 垂直和水平轴上的随机镜像
- 随机亮度调节,最大亮度增量为 0.2
由于 Tensorflow 被用作后端,所以上面定义的所有操作都是使用框架提供的 tf.image 模块实现的,该模块很容易与用于构建向开发的模型提供数据的输入管道的 tf.data API 集成。
特征抽出
有了 Keras,这个堆叠在预训练 ResNet 上的分类器可以很容易地实现如下:
conv_base = tf.keras.applications.ResNet50V2(
include_top=False,
weights='imagenet',
input_shape=(IMAGES_SIZE, IMAGES_SIZE, 3),
pooling='avg'
)model = Sequential()
model.add(conv_base)
model.add(Dense(len(np.unique(labels)), activation='softmax'))
请注意,通过将参数“None”和“avg”分别传递给“include_top”和“pooling parameters”,ResNet50V2 类已经构建了一个网络,用全局平均池层替换了最后一个 softmax 层。
为了执行特征提取,有必要冻结预训练卷积基的权重,使得它们在整个模型的训练期间不会更新。这可以通过简单地将卷积基的每一层的属性“可训练的”设置为假来实现:
for layer in conv_base.layers:
layer.trainable = False
因此,创建的综合模型如下:
Model: "sequential"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
resnet50v2 (Model) (None, 2048) 23564800
_________________________________________________________________
dense (Dense) (None, 102) 208998
=================================================================
Total params: 23,773,798
Trainable params: 208,998
Non-trainable params: 23,564,800
正如所料,该模型只有 208,998 个可训练权重,对应于最后一个 softmax 激活的全连接层的权重。
在所有执行的实验中,上面定义的模型仅被训练 30 个时期,使用批量大小 16、具有学习速率 1e-4 的 Adam 优化器、稀疏分类交叉熵损失和具有耐心 10 的早期停止回调(监控确认损失度量)。
微调
微调可以通过解冻之前用于特征提取的模型的最后几层,然后以较低的学习速率重新训练来实现。
应该解冻多少层的选择取决于
源和目标域和任务的相似程度。在这种情况下,由于 flower 域与 ImageNet 域没有太大的不同,因此只解冻卷积基础的最后两层是合理的,在 ResNet50-v2 的情况下,这两层是“conv5_block3”和“post”层。这可以通过以下方式完成:
for layer in conv_base.layers:
if layer.name.startswith('conv5_block3'):
layer.trainable = True
if layer.name.startswith('post'):
layer.trainable = True
编译完成后,将要微调的整体模型如下所示:
Model: "sequential_2"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
resnet50v2 (Model) (None, 2048) 23564800
_________________________________________________________________
dense_2 (Dense) (None, 102) 208998
=================================================================
Total params: 23,773,798
Trainable params: 4,677,734
Non-trainable params: 19,096,064
可以注意到,由于 ResNet50 的架构,即使仅解冻卷积基础的最后两层,可训练参数的数量也是显著的。这是将解冻层限制为最后两层的另一个原因,因为解冻更多的层会导致更多的可训练权重,这可能会导致过度拟合,因为模型微调的数据集大小有限。
在所有执行的实验中,使用先前用于特征提取的相同配置来训练模型,唯一的不同是,这次模型以 1e-5 的学习率(比用于特征提取的学习率低 10 倍)被训练更多的时期。
评估指标
选择用于评估被训练的分类器的性能的度量是 F1 分数,其对应于精确度和召回率的调和平均值,并且代表了比较两个不同分类器的性能的简单且有效的方式。
由于我们正在处理多类分类问题,所有类的平均精度和召回率用于计算 F1 分数。这些平均值可以用两种不同的方法计算:通过微平均或宏平均。
对于微平均,考虑所有类别的真阳性(TP)、假阳性(FP)和假阴性(FN)来计算平均精度和召回值。取而代之的是宏平均,首先评估每个类的精度和召回率,然后通过对不同类获得的结果进行平均来计算各自的平均值。
下图通过显示计算平均精度所需的相应方程,阐明了微观平均和宏观平均之间的区别(平均召回率可以类似地计算)。

图 4 :分别通过微观和宏观平均计算平均精度的方程。
尽管微观平均的计算成本更高,但它强调了模型在通用性较低的类别上表现良好的能力(例如,较少的示例),而宏观平均则缓解了这一问题[4]。由于这些原因,由于 102 种花的类别具有不同的普遍性,下一节中给出的所有 F1 分数都是使用微平均计算的,因此,如果一个模型对于具有较少实例的类别表现不佳,这将在其最终的平均 F1 分数中得到强调。
结果
下图总结了在不使用任何数据扩充策略的情况下,应用“简单”特征提取和微调时,从三个不同数据集获得的结果。

图 5 :在没有采用数据扩充的情况下,应用特征提取和微调在三个训练数据集上获得的 F1 分数(微平均)。
正如所料,对于所有数据集,通过微调 ResNet 获得的结果优于仅通过特征提取获得的结果。事实上,通过微调预训练的卷积基,其与域特定特征相关联的最后层的权重被轻推,使得先前由网络在 ImageNet 域上学习的表示适应于新的 flowers 域,因此它们变得更有代表性和更有效,从而导致更高的 F1 分数。
最重要的结果是,仅通过在仅由大约 800 个示例组成的最小数据集上微调 ResNet50-v2,仍有可能达到 0.79 的微观平均 F1 分数。
这一结果转化为这样一个事实,即通过迁移学习,开发的模型能够学习如何将花卉图像分类(具有相当的准确性)到 102 个可能的类别中,平均而言,每个类别仅看到 8 个图像。
下图显示了进行与之前相同的实验所获得的 F1 分数,但是这一次使用了之前描述的数据扩充技术。

图 6 :应用特征提取和微调,采用数据扩充,在三个数据集上获得的 F1 分数(微观平均)。
从图 4 和图 5 所示的两个图表中,可以注意到,当执行特征提取时,数据扩充不会带来任何好处,因为所有数据集的 F1 分数保持不变。然而,这可能与在所有三个数据集上用特征提取训练的分类器仅被训练了 30 个时期的事实有关。

表 1 :用于执行实验的数据集的实例数量和分割百分比(参考完整的未分割 flowers 数据集)。
回想大、中、小集合的大小,为了方便起见,在上面的表格中再次示出,与在其他集合上获得的 F1 分数相比,在小训练集合上获得的 F1 分数更有意义,因为与小训练集合相关联的测试集合分别是与中、大集合相关联的测试集合的两倍和十倍。这是假设仅用 800 幅图像(大约)训练的分类器实际上表现良好的另一个原因。
下图中的图表显示了训练数据的缺乏对通过迁移学习训练的分类器的影响程度。

图 7 :与大型数据集相比,中型和小型数据集的 F1 分数和训练数据集大小减少的百分比。
图表的蓝色条表示在中型和小型数据集上训练的分类器相对于在大型数据集上训练的分类器的 F1 分数减少百分比(仅考虑每个数据集获得的最佳 F1 分数),而橙色条对应于中型和小型数据集相对于大型数据集的训练集大小减少百分比。
该图表强调了通过迁移学习训练的分类器如何在缺乏训练数据的情况下特别稳健:
将训练分类器的数据集的大小减少 50%会导致 F1 分数仅下降 2%,而将数据集的大小减少 87%会导致 F1 分数仅下降 14%。
最后,有趣的是,数据扩充基本上不会为大中型数据集带来任何性能改善,但它允许我们在小型数据集上达到略高的 F1 分数,但只有在进行微调时。下图中的图表更清楚地表明了这一点,该图显示了在对三个不同的训练集执行微调时,数据扩充带来的 F1 改进百分比。说到底,数据增广带来的 F1 最大提升只有 2.53%。

图 8 :与仅仅“普通”微调相比,使用数据增强的微调的 F1 改进百分比。
结论
再次提出故事开始时提出的问题,即为了使迁移学习有效,到底需要多少训练实例,根据实验结果,可能的答案是,在这个具体的案例研究中,每班十个实例就足够了。事实上,即使采用只有大约 800 个训练样本的小数据集,仍然有可能在可能的 102 个类别上训练出具有显著准确性的分类器,其性能与在较大数据集上训练的分类器的性能相差不远。
因此,这些结果证实了在数据非常少的情况下迁移学习的有效性,显示了使用迁移学习方法训练的分类器的性能如何仅受到训练数据集大小的轻微影响。由于这个原因,数据扩充不会严重影响通过特征提取或微调训练的分类器的性能,即使它仍然带来轻微的改进,因此可以被认为是值得使用的,特别是考虑到它实现起来相对快速和简单。
然而,有必要指出的是,用于执行实验的数据集在通过迁移学习训练的分类器的优异性能中起着决定性的作用,因为所选数据集的 flower 域与 ImageNet 数据集的域没有太大的不同,在 ImageNet 数据集上卷积基已经被预先训练(即使 102 个类别 Flower 数据集的少数类别被包括在 ImageNet 类别集中)。如果使用属于完全不同于 ImageNet 域的特定域的数据集(例如,由标记的 X 射线照片的集合组成的数据集)进行实验,那么由预训练网络学习的表示可能对在目标数据集上训练的分类器没有用,从而导致更差的性能,而不管训练集的大小。
总之,假设目标域类似于所采用的卷积基已被预训练的域,特征提取和微调允许即使在极其有限的数据集上也能实现高性能,当很少训练数据可用时,以这种方式使迁移学习优于从头训练。
参考
[1] Aurlien Gron,用 Scikit-Learn 和 Ten-
sorFlow 进行机器学习:构建智能系统的概念、工具和技术第 1 期。 (2017)、奥莱利传媒
、、、水城颜【2】、网络中的网络(2013)
【3】玛利亚-艾琳娜·尼尔斯巴克和安德鲁·齐塞曼、 (2008)、印度计算机视觉、图形和图像处理会议
、【4】法布里吉奥·塞巴斯蒂安尼、(2008)
资源
Log-Mel 谱图图像分类中的 VGG-16 迁移学习

作为我之前帖子的后续,我将对 RAVDESS 音频数据集应用迁移学习,希望提高模型的准确性。回顾一下,迁移学习是一种深度学习方法,其中已经在一项任务上训练的模型被用作训练类似任务的模型的起点。在 DJ Sarkar 的这篇文章中,他用例子为理解迁移学习提供了很好的指导。
我们将首先尝试使用 VGG-16 预训练模型作为我们数据集的特征提取器,在这里我们冻结预训练模型的卷积块并修改密集层。然后,我们将尝试图像增强的预训练模型。
所以让我们开始吧!
数据准备
在导入必要的库之后,我们必须导入带有标签的训练和测试图像。
# For training set onlyimport globangry = glob.glob('/content/drive/My_Drive/train_logmel/angry/*.*')
calm = glob.glob('/content/drive/My_Drive/train_logmel/calm/*.*')
disgust = glob.glob('/content/drive/My_Drive/train_logmel/disgust/*.*')
fearful = glob.glob('/content/drive/My_Drive/train_logmel/fearful/*.*')
happy = glob.glob('/content/drive/My_Drive/train_logmel/happy/*.*')
neutral = glob.glob('/content/drive/My_Drive/train_logmel/neutral/*.*')
sad = glob.glob('/content/drive/My_Drive/train_logmel/sad/*.*')
surprised = glob.glob('/content/drive/My_Drive/train_logmel/surprised/*.*')data = []
labels = []for i in angry:
image=tf.keras.preprocessing.image.load_img(i, color_mode='rgb',
target_size= (224,224))
image=np.array(image)
data.append(image)
labels.append('Angry')
for i in calm:
image=tf.keras.preprocessing.image.load_img(i, color_mode='rgb',
target_size= (224,224))
image=np.array(image)
data.append(image)
labels.append('Calm')
for i in disgust:
image=tf.keras.preprocessing.image.load_img(i, color_mode='rgb',
target_size= (224,224))
image=np.array(image)
data.append(image)
labels.append('Disgust')
for i in fearful:
image=tf.keras.preprocessing.image.load_img(i, color_mode='rgb',
target_size= (224,224))
image=np.array(image)
data.append(image)
labels.append('Fearful')
for i in happy:
image=tf.keras.preprocessing.image.load_img(i, color_mode='rgb',
target_size= (224,224))
image=np.array(image)
data.append(image)
labels.append('Happy')
for i in neutral:
image=tf.keras.preprocessing.image.load_img(i, color_mode='rgb',
target_size= (224,224))
image=np.array(image)
data.append(image)
labels.append('Neutral')
for i in sad:
image=tf.keras.preprocessing.image.load_img(i, color_mode='rgb',
target_size= (224,224))
image=np.array(image)
data.append(image)
labels.append('Sad')
for i in surprised:
image=tf.keras.preprocessing.image.load_img(i, color_mode='rgb',
target_size= (224,224))
image=np.array(image)
data.append(image)
labels.append('Surprised')train_data = np.array(data)
train_labels = np.array(labels)
让我们来看看训练集中的图像示例:

作者图片
数据预处理
- 标准化数据
X_train = X_train.astype('float32')
X_test = X_test.astype('float32')
X_train /= 255
X_test /= 255
2.一键编码目标类
lb = LabelEncoder()
y_train = np_utils.to_categorical(lb.fit_transform(y_train))
y_test = np_utils.to_categorical(lb.fit_transform(y_test))
VGG-16 模型
首先,我们导入 VGG16 并设置必要的参数:
from keras.applications import VGG16vgg_model = VGG16(weights='imagenet',include_top=False, input_shape=(224, 224, 3))
权重= 'imagenet' :使用预训练的权重,而不是从头开始训练模型
include_top=False : 我们希望加载没有分类器层的模型,并添加我们自己的模型
input_shape=(224,224,3) :指定数据集中图像的首选形状
接下来,我们冻结卷积模块:
for layer in vgg_model.layers:
layer.trainable = False# Make sure you have frozen the correct layers
for i, layer in enumerate(vgg_model.layers):
print(i, layer.name, layer.trainable)

然后创建我们的密集层:
x = vgg_model.output
x = Flatten()(x) # Flatten dimensions to for use in FC layers
x = Dense(512, activation='relu')(x)
x = Dropout(0.5)(x) # Dropout layer to reduce overfitting
x = Dense(256, activation='relu')(x)
x = Dense(8, activation='softmax')(x) # Softmax for multiclass
transfer_model = Model(inputs=vgg_model.input, outputs=x)
最后,我们编译并拟合模型:
learning_rate= 5e-5
transfer_model.compile(loss="categorical_crossentropy", optimizer=optimizers.Adam(lr=learning_rate), metrics=["accuracy"])history = transfer_model.fit(X_train, y_train, batch_size = 1, epochs=50, validation_data=(X_test,y_test))
经过 50 个时代,我们达到了 69%的准确率。


作者图片
这比以前的模型表现稍好,但必须有一种方法来提高模型的准确性!让我们尝试使用 VGG-16 作为图像增强的特征提取器。在处理小数据集时,图像增强是添加更多训练数据的好方法。
图像增强
使用 ImageDataGenerator,我们可以增强图像:
train_datagen = ImageDataGenerator(rescale=1./255, zoom_range=0.3, rotation_range=40, width_shift_range=0.3, height_shift_range=0.3, shear_range=0.3, horizontal_flip=True, fill_mode="nearest")train_generator = train_datagen.flow(train_data, train_lb, batch_size=1)val_datagen = ImageDataGenerator(rescale=1./255)val_generator = val_datagen.flow(test_data,val_lb,batch_size=1)
接下来,我们将构建我们的深度学习模型,编译该模型,然后拟合它:
x = vgg_model.output
x = Flatten()(x) # Flatten dimensions to for use in FC layers
x = Dense(512, activation='relu')(x)
x = Dropout(0.5)(x) # Dropout layer to reduce overfitting
x = Dense(256, activation='relu')(x)
x = Dense(8, activation='softmax')(x) # Softmax for multiclass
transfer_model = Model(inputs=vgg_model.input, outputs=x)learning_rate= 5e-5
transfer_model.compile(loss="sparse_categorical_crossentropy", optimizer=keras.optimizers.Adam(lr=learning_rate), metrics=["accuracy"])history = transfer_model.fit_generator(train_generator, validation_data=val_generator, epochs=100, shuffle=True, verbose=1)
经过 100 个时期后,我们获得了 78%的准确率


作者图片
如您所见,这并不比我们之前的模型表现得更好,当前的模型过度拟合了训练数据。
我将研究其他预训练模型(如 Inception_V3 和 Resnet50 ),并探索微调,而不是使用预训练模型作为特征提取器。感谢您的阅读!😃
水果分类的迁移学习
迁移学习的简要概述,以及如何利用预训练模型通过 InceptionV3 进行深度学习的示例。

来自 Freerange 的 Jack Moreh 的“一个假想的人工智能大脑的图像”
或者你们当中不知道什么是深度学习的人,它是机器学习中的一种类型,属于人工智能的范畴。这种类型的技术不是我,机器人人工智能,我们作为一个物种,离真正开发这样的东西还很远。然而,当我们谈论像人类一样行动的可编程计算机时,深度学习和许多其他类型的机器学习方法非常接近。

“神经网络图”
深度学习
简而言之,深度学习是一种机器学习方法,它超越了可能只需要一两层学习的更小的学习方法,这可能被称为浅层学习。但是,深度学习是一层层的学习。代表这些层的模型被称为神经网络,其名称源于对神经生物学的研究,但不要混淆,因为它实际上不是一个模拟大脑功能的网络。神经网络有许多不同的形式,但我们将只关注我们的迁移学习中的一种,即卷积神经网络。

“卷积过程”
那么什么是卷积神经网络呢?
卷积神经网络只是训练网络(模型)以给出精确分类的另一种方法。在所有其他神经网络中,卷积神经网络在计算机视觉学习方面表现出色。CNN 或 covnets 的惊人之处在于,它们从图像中学习的模式是平移不变的,这意味着如果它们在图像的角落中找到一个模式,它们就会在任何不同的图像角落中识别出相同的模式,而常规网络必须一次又一次地重新学习它。Covnet 还能够学习模式的空间层次,这意味着 cov net 的每一层都将学习不同的东西。第一层可以学习小模式,下一层可以学习作为第一层特征的较大模式。这些特征是如何通过卷积函数获得的。

“卷积运动的 gif 图”,Narges Khatami,来自维基共享资源
卷积
卷积函数用于获得卷积层的特征图(特征矩阵)。在基线上,covnets 已经配置了由一个内核组成的权重。核用于从输入图像(输入层)中获得独特的特征,就像它可以用于收集输入图像的锐度、边缘或收集关于如何检测边缘的信息一样。这个函数可以表示为 nn,这是一个包含许多唯一值的矩阵。内核在输入图像的顶部进行*(滑动和相乘),假设输入图像是(10,10),内核是(3,3)。第一张幻灯片(stride)将在输入图像的左上角乘以 9 个像素,以在称为特征图的新矩阵的左上角产生单个像素的输出。
更新:第二次乘法应该是 4 * 2 = 8,用 8 代替 1 的乘积,得到所有乘积的和。

由 Krut Patel 从走向数据科学2019;詹姆斯·纳尔逊注释,2020 年。
当内核像这样滑过输入图像时,这种乘法继续进行。

由 Krut Patel 从走向数据科学2019;詹姆斯·纳尔逊注释,2020 年。

由 Krut Patel 从走向数据科学2019;詹姆斯·纳尔逊注释,2020 年。
这个连续的过程不会停止,直到整个特征矩阵已经被这些卷积值填充,并且一旦特征矩阵完成,它就被堆叠在卷积层内。如果网络被设计成这样,则另一个内核将产生具有相同输入图像的另一个特征矩阵,以将下一个特征矩阵存储在相同的卷积层中。

“制作特征矩阵时会发生什么”
简而言之,卷积神经网络的训练是关于定位每个核的所有正确值,以便当输入图像通过各层时,它将激活最后一个输出层上的不同神经元,以预测和准确分类图像。
什么是迁移学习,它有什么帮助?
迁移学习让每个人的生活变得更轻松、更美好。尽管从头开始创建卷积神经网络很有趣,但它们可能有点贵并且耗费大量计算能力。因此,为了减少网络所需的功率量,我们使用转移学习,这是已经在另一个图像上经过训练的预训练权重,以便提高我们网络的性能。使用预训练模型是一个最佳选择,因为它们已经在数以百万计的其他图像上进行配置和训练,这些图像由数千个类别组成,每次持续多天,以提供我们所需的高性能预训练权重,以便轻松训练我们自己的网络(Aditya Ananthram,2018)。
实际应用
现在,为了展示迁移学习能力的实际应用,我将介绍所使用的数据、选择的预训练模型、模型架构,然后是代码。

“意大利的一个农贸市场替身”,作者是散养农场的梅里泽
数据的描述
数据集包含 81,104 张不同水果和蔬菜的图像,由每张水果和蔬菜图像的 120 个唯一分类组成。图像的总数被分成训练和测试数据集。训练数据集包含 60,486 幅图像,测试数据集是 20,618 幅图像。
所有图像的大小都是 100x100 像素,是用罗技 C920 相机收集的,该相机用于拍摄水果/蔬菜(Mihai Oltean,2019)。所有的水果和蔬菜都被种植在一个装有低速马达的轴内,在那里它们被记录下来,每一个持续时间为 20 秒。水果和蔬菜测试图像是用 5X 智能手机拍摄的。
模型

“一个‘图腾’旋转的图像。你还在做梦吗?”照片由现代灵感的灰烬在 Unsplash 上拍摄
选择的迁移学习模型叫做 InceptionV3 。该模型是一个卷积神经网络,在架构上设计为 48 层深度,对 299×299 的图像形状进行训练。最初的 Inception 架构网络被称为“GoogLeNet”,这是一个 27 层深度卷积神经网络,早在 2014 年就已建立(Shaikh,2018)。该模型的名称来自克里斯托弗·诺兰导演的电影“盗梦空间”,基于深入梦境的概念“梦中的梦”,转化为卷积神经网络中的卷积神经网络。
GoogLeNet 设计背后的想法是消除在处理更深层次的神经网络时经常发现的过度拟合问题。当数据集太小并且正在大型神经网络中进行训练时,通常会发生过度拟合,过度拟合带来的问题是对模型的验证准确性(测试准确性)的错误陈述。测试准确性是衡量训练好的网络准确预测它没有看到的图像的精确度。设计一个巨大的网络来产生这种准确性的解决方案是创建一个稀疏连接的神经网络来代替完全连接的神经网络(Shaikh,2018),这就是为什么 GoogLeNet 模型在 2014 年以 80%以上的预测准确性赢得了 ImageNet 视觉识别挑战。
模型架构
InceptionV3 模型连接到底部的两个完全连接的层,但在此连接之前,其维度从 3D 减少到 1D,具有全球平均池 2D 。汇集还将为每个特征矩阵输出一个响应。在汇集之后,架构的下一层是具有 512 个单元(神经元)的第一密集隐藏层,其将连接到具有 10 个神经元的最终输出层,以匹配水果和蔬菜类的数量。这就是 InceptionV3 架构的样子。

英特尔的 Milton-Barker Adam 的“预训练 InceptionV3 模型的架构设计”
这是连接到架构的底层全连接层的样子。

“附加到完全连接的层的 InceptionV3 模型的底层架构布局”
还值得一提的是,微调这些预先训练的模型以及与它们相关联的权重。我可以从模型中选择我想要使用的权重,它可以是上半部分、下半部分、中间部分,或者我可以冻结所有权重。这样做意味着我冻结的预训练模型的任何部分都不会是可以为我正在制作的模型更新的可训练权重。我还可以选择模型被训练的图像的权重,但是在这个例子中,通过反复试验,我选择不冻结权重。我对 InceptionV3 的实现将使用在 ImageNet 上预先训练的权重。“ImageNet 是一个根据 WordNet 层次结构(目前只有名词)组织的图像数据库,其中层次结构的每个节点都由成百上千的图像描述。目前,我们平均每个节点有超过 500 张图像。”(《影像网》,2017 年)
最后是代码
现在,您已经对数据集的外观和模型架构有了一个概念,是时候执行了。
准备数据和训练网络
加载库。
先做最重要的事情;我们必须加载必要的库。加载库时,确保导入所有必需的模块,以便我们可以准备数据和训练模型。
*# read in libraries*
import tensorflow as tf
from tensorflow.keras import backend, models, layers, optimizers
import numpy as np
from tensorflow.keras.layers import GlobalAveragePooling2D
from tensorflow.keras.callbacks import EarlyStopping
from sklearn.model_selection import train_test_split
from tensorflow.keras.utils import plot_model
from IPython.display import display
from PIL import Image
from tensorflow.keras.preprocessing.image import ImageDataGenerator
import os, shutil
from tensorflow.keras.models import Model
np.random.seed(42)
加载数据并准备好。
接下来,为了准备数据,我们需要用 ImageDataGenerator 设置一个 train_datagen 和 test_datagen。然后,使用这些生成器调整训练数据和测试数据的图像大小,以匹配预训练模型的像素图像输入。以确保神经网络不会学习不相关的模式,并反过来提高整体性能。
*# Specify the base directory where images are located.*
base_dir = '/kaggle/input/fruits/fruits-360/' *# Specify the traning, validation, and test dirrectories.*
train_dir = os.path.join(base_dir, 'Training')
test_dir = os.path.join(base_dir, 'Test')*# Normalize the pixels in the train data images, resize and augment the data.*
train_datagen = ImageDataGenerator(
rescale=1./255,*# The image augmentaion function in Keras*
shear_range=0.2,
zoom_range=0.2, *# Zoom in on image by 20%*
horizontal_flip=True) *# Flip image horizontally* *# Normalize the test data imagees, resize them but don't augment them*
test_datagen = ImageDataGenerator(rescale=1./255) train_generator = train_datagen.flow_from_directory(
train_dir,
target_size=(299, 299),
batch_size=16,
class_mode='categorical')test_generator = test_datagen.flow_from_directory(
test_dir,
target_size=(299, 299),
batch_size=16,
class_mode='categorical')

准备 InceptionV3 模型
既然已经准备好了图像,现在是时候导入并设置迁移学习的预训练 InceptionV3 模型了。
*# Load InceptionV3 library*
from tensorflow.keras.applications.inception_v3 import InceptionV3*# Always clear the backend before training a model*
backend.clear_session()*# InceptionV3 model and use the weights from imagenet*
conv_base = InceptionV3(weights = 'imagenet', *#Useing the inception_v3 CNN that was trained on ImageNet data.*
include_top = False)

创建一个功能 API 模型。
现在,让我们将预训练的 InceptionV3 模型权重与密集层(完全连接的层)相结合,并减少两者之间的模型维度。
*# Connect the InceptionV3 output to the fully connected layers*
InceptionV3_model = conv_base.output
pool = GlobalAveragePooling2D()(InceptionV3_model)
dense_1 = layers.Dense(512, activation = 'relu')(pool)
output = layers.Dense(120, activation = 'softmax')(dense_1)
显示功能 API 模型。
为了了解模型架构,我们可以将功能 API 模型作为一个整体来显示,以直观地看到网络的深度。
*# Create an example of the Archictecture to plot on a graph*
model_example = models.Model(inputs=conv_base.input, outputs=output)
*# plot graph*
plot_model(model_example)
(模型太大,无法在介质上显示,请点击此链接查看)
定义模型并编译它。
为了让我们训练模型,我们需要定义函数式 API 模型,并使用类别交叉熵作为损失函数以及具有学习速率和动量参数的随机梯度下降来编译该模型。
*# Define/Create the model for training*
model_InceptionV3 = models.Model(inputs=conv_base.input, outputs=output)*# Compile the model with categorical crossentropy for the loss function and SGD for the optimizer with the learning*
*# rate at 1e-4 and momentum at 0.9*
model_InceptionV3.compile(loss='categorical_crossentropy',
optimizer=optimizers.SGD(lr=1e-4, momentum=0.9),
metrics=['accuracy'])
查看 GPU 要使用的设备列表。
现在我建议使用 GPU 来训练这个模型,因为 InceptionV3 模型有超过 2100 万个参数,在 CPU 上训练可能需要几天才能完成。如果你有一个 GPU,你可以使用你自己的,但我使用了 Kaggle 的 GPU 提供给他们的笔记本电脑,这花了我大约 20-25 分钟来完成培训。找到可用的 GPU 设备,以便加快训练过程。
*# Import from tensorflow the module to read the GPU device and then print*from tensorflow.python.client import device_lib
print(device_lib.list_local_devices())

训练模型。
在找到要使用的 GPU 后,我们将把它合并到我们的代码中,最终使用 train_generator 为训练数据训练模型,并将 validation_data 参数设置为 test_generator。
*# Execute the model with fit_generator within the while loop utilizing the discovered GPU*
import tensorflow as tf
with tf.device("/device:GPU:0"):
history = model_InceptionV3.fit_generator(
train_generator,
epochs=5,
validation_data=test_generator,
verbose = 1,
callbacks=[EarlyStopping(monitor='val_accuracy', patience = 5, restore_best_weights = True)])

99%的验证准确性和 0.0187 的损失是非常好的。
显示模型的测试精度和测试损失值
现在,让我们通过绘制各个时期的训练精度/验证精度和训练损失/验证损失来看看我们的模型是什么样子,然后打印最终的测试精度和测试损失。
*# Create a dictionary of the model history*
import matplotlib.pyplot as plt
history_dict = history.history
loss_values = history_dict['loss']
val_loss_values = history_dict['val_loss']
acc_values = history_dict['accuracy']
val_acc_values = history_dict['val_accuracy']
epochs = range(1, len(history_dict['accuracy']) + 1)*# Plot the training/validation loss*
plt.plot(epochs, loss_values, 'bo', label = 'Training loss')
plt.plot(epochs, val_loss_values, 'b', label = 'Validation loss')
plt.title('Training and validation loss')
plt.xlabel('Epochs')
plt.ylabel('Loss')
plt.legend()
plt.show()*# Plot the training/validation accuracy*
plt.plot(epochs, acc_values, 'bo', label = 'Training accuracy')
plt.plot(epochs, val_acc_values, 'b', label = 'Validation accuracy')
plt.title('Training and validation accuracy')
plt.xlabel('Epochs')
plt.ylabel('Accuracy')
plt.legend()
plt.show() *# Evaluate the test accuracy and test loss of the model*
test_loss, test_acc = model_InceptionV3.evaluate_generator(test_generator)print('Model testing accuracy/testing loss:', test_acc, " ", test_loss)



结果分析
对 120 类果蔬图像的准确预测结果表明,测试准确率为 99%,损失值为 1.8%。损失价值是衡量我们的产出与我们的期望之间的距离。训练分 5 个阶段进行,每个阶段大约需要 20-25 分钟,在 ka ggle GPU 的帮助下可以达到这一精度,从而加快了整个过程。训练数据是一个 3781 步的过程(迭代),每 16 个样本取一批数据,向前和向后传播给我们一遍。一遍等于一次迭代。
结论
总之,迁移学习是训练数据集识别和分类图像的一种非常有效的方法。它允许快速设置,而无需从头开始详细设计卷积神经网络架构,并且它利用预训练模型的先前训练来提供高精度。你可以去我项目的 Kaggle 笔记本了解更多信息。
参考
阿迪蒂亚·阿南瑟拉姆。(2018 年 10 月 17 日)。Keras 中使用迁移学习的初学者深度学习。2020 年 4 月 24 日检索,来自 Medium 网站:https://towards data science . com/keras-transfer-learning-for-初学者-6c9b8b7143e
f .谢赫(2018 . 10 . 18)。从头开始理解 Inception 网络(带 Python 代码)。2020 年 5 月 7 日检索,来自 Analytics Vidhya 网站:https://www . Analytics vid hya . com/blog/2018/10/understanding-inception-network-from-scratch/
f . chollet(2018)。用 Python 进行深度学习。庇护岛(纽约,州联合):曼宁,警察。
文件:Valid-padding-convolution.gif —维基共享。(2018 年 7 月 6 日)。2020 年 5 月 6 日检索,来自 wikimedia.org 网站:https://commons . wikimedia . org/wiki/File:Valid-padding-convolution . gif
获取蓝色背景的人脑智能概念的免费库存照片在线|下载最新免费图片和免费插图。(2020).2020 年 5 月 9 日检索,来自 freerangestock.com 网站:https://freerangestock . com/photos/65677/concept-of-intelligence-with-human-brain-on-blue-background . html
在线获取意大利水果和蔬菜供应商的免费图片|下载最新免费图片和免费插图。(2020).检索于 2020 年 5 月 9 日,来自 freerangestock.com 网站:https://freerangestock . com/photos/37652/fruit-and-vegetables-vendor-Italy . html
https://github.com/syt123450,的 syt123450。(2020).图层 GlobalPooling2d。2020 年 5 月 6 日检索,来自 Tensorspace.org 网站:【https://tensorspace.org/html/docs/layerGlobalPooling2d.html
ImageNet。(2017).2020 年 5 月 7 日检索,来自 Image-net.org 网站:【http://www.image-net.org/
克鲁特·帕特尔。(2019 年 9 月 8 日)。卷积神经网络-数据科学初学者指南。2020 年 4 月 24 日检索,来自 Medium 网站:https://towards data science . com/convolution-neural-networks-a-beginners-guide-implementing-a-m NIST-hand-written-digit-8aa 60330d 022
米哈伊·奥尔特安。(2020).水果 360。检索于 2020 年 5 月 6 日,来自 Kaggle.com 网站:https://www.kaggle.com/moltean/fruits
米尔顿-巴克,A. (2019 年 2 月 17 日)。用于急性髓细胞/淋巴细胞白血病分类的 Inception V3 深度卷积架构。2020 年 5 月 6 日检索,来自 intel.com 网站:https://software . Intel . com/en-us/articles/inception-v3-deep-convolutional-architecture-for-classification-acute-myeloid-lymphoblastic
普拉哈尔·甘尼什。(2019 年 10 月 18 日)。卷积核的类型:简化—面向数据科学。2020 年 4 月 24 日检索,来自 Medium 网站:https://towardsdatascience . com/types-of-convolution-kernels-simplified-f 040 CB 307 c 37
Unsplash。(2020).现代灵感的灰烬。检索于 2020 年 5 月 9 日,来自 Unsplash.com 网站:https://unsplash.com/@modernafflatusphotography
可视面包屑。(2016).2020 年 4 月 27 日检索,来自 mathworks.com 网站:https://www . mathworks . com/help/deep learning/ref/inceptionv3 . html
维基百科贡献者。(2020 年 5 月 2 日)。我,机器人(电影)。2020 年 5 月 5 日检索,来自维基百科网站:https://en . Wikipedia . org/wiki/I,Robot(film)
用 PyTorch 迁移学习
以及为什么您不应该从头开始编写 CNN 架构
如今,训练深度学习模型,尤其是与图像识别相关的模型,是一项非常简单的任务。有很多原因可以解释为什么你不应该过分强调架构,主要是有人已经为你做了这一步。至于其他的,你得继续读下去。

由 Unsplash 上的 drmakete 实验室拍摄的照片
源代码: Colab 笔记本
如今,作为一名工程师,你唯一应该关注的是数据准备——在深度学习的世界中,这个术语概括了数据收集、加载、规范化和扩充的过程。
今天的议程很简单——解释什么是迁移学习以及如何使用它,然后是模型培训的实际示例,包括有无预培训架构。
听起来很简单,所以让我们直入主题吧!
数据集下载和基本准备
先说进口。这里我们有常见的嫌疑人,如 Numpy 、 Pandas 和 Matplotlib ,但也有我们最喜欢的深度学习库——py torch——以及它所提供的一切。
import os
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from datetime import datetimeimport torch
import torch.nn as nn
import torch.nn.functional as F
from torch.utils.data import DataLoader
from torchvision.utils import make_grid
from torchvision import models, transforms, datasets
我们正在用 Colab 或者更准确地说是 Colab Pro 编写这段代码,所以我们将利用 GPU 的能力进行训练。如果您不知道 Colab 是什么,或者想知道升级到 Pro 版本是否值得,请随时查看这些文章:
[## Google Colab:它与支持 GPU 的笔记本电脑相比如何?
Colab 简介、运行时、性能比较…以及疑难解答
towardsdatascience.com](/google-colab-how-does-it-compare-to-a-gpu-enabled-laptop-851c1e0a2ca9) [## Colab Pro:物有所值吗?
10 美元有多大作用?我们做了测试。你做阅读。
towardsdatascience.com](/colab-pro-is-it-worth-the-money-32a1744f42a8)
因为我们是在 GPU 上训练,而这可能不适合你,所以我们需要一个健壮的方法来处理这个问题。这里有一个标准方法:
device = torch.device(‘cuda:0’ if torch.cuda.is_available() else ‘cpu’)
device**>>> device(type=’cuda’, index=0)**
如果你正在进行 cpu 方面的培训,它应该会显示类似于 type='cpu' 的内容,但是因为 Colab 是免费的,所以没有必要这样做。
现在进入数据集。为此,我们将使用狗或猫数据集。它有大量不同大小的图像,我们将在后面处理。现在我们需要下载并解压它。方法如下:
%mkdir data
%cd /content/data/
!wget [http://files.fast.ai/data/dogscats.zip](http://files.fast.ai/data/dogscats.zip)!unzip dogscats.zip
大约一分钟后,根据你的网速,数据集就可以使用了。现在,我们可以将它声明为一个数据目录——这不是必需的,但会节省我们一点时间。
DIR_DATA = ‘/content/data/dogscats/’
数据准备
第一部分的第一部分现在完成了。接下来,我们必须对训练和验证子集应用一些转换,然后用 DataLoaders 加载转换后的数据。以下是我们应用的转换:
- 随机旋转
- 随机水平翻转
- 调整到 224x224 大小—预训练体系结构需要
- 转换为张量
- 正常化
代码如下:
train_transforms = transforms.Compose([
transforms.RandomRotation(10),
transforms.RandomHorizontalFlip(p=0.5),
transforms.Resize(224),
transforms.CenterCrop((224, 224)),
transforms.ToTensor(),
transforms.Normalize(
mean=[0.485, 0.456, 0.406],
std=[0.229, 0.224, 0.225]
)
])valid_transforms = transforms.Compose([
transforms.Resize(224),
transforms.CenterCrop((224, 224)),
transforms.ToTensor(),
transforms.Normalize(
mean=[0.485, 0.456, 0.406],
std=[0.229, 0.224, 0.225]
)
])
现在我们用数据加载器加载数据。这一步也很简单,您可能很熟悉:
train_data = datasets.ImageFolder(os.path.join(DIR_DATA, ‘train’), transform=train_transforms)
valid_data = datasets.ImageFolder(os.path.join(DIR_DATA, ‘valid’), transform=valid_transforms)torch.manual_seed(42)
train_loader = DataLoader(train_data, batch_size=64, shuffle=True)
valid_loader = DataLoader(valid_data, batch_size=64, shuffle=False)class_names = train_data.classes
class_names**>>> ['cats', 'dogs']**
如果我们现在对单个批次进行逆归一化并将其可视化,我们会得到这样的结果:

快速看一下上面的图像,就可以看出我们的转换工作符合预期。
数据准备部分现在已经完成,在下一部分,我们将声明一个定制的 CNN 架构,训练它,并评估性能。
定制建筑 CNN
对于这一部分,我们想做一些非常简单的事情——3 个卷积层,每个卷积层后面是 max-pooling 和 ReLU,然后是一个完全连接的层和一个输出层。
这是这个架构的代码:
class CustomCNN(nn.Module):
def __init__(self):
super().__init__()
self.conv1 = nn.Conv2d(in_channels=3, out_channels=32, kernel_size=3, stride=1)
self.conv2 = nn.Conv2d(in_channels=32, out_channels=64, kernel_size=3, stride=1)
self.conv3 = nn.Conv2d(in_channels=64, out_channels=64, kernel_size=3, stride=1)
self.fc1 = nn.Linear(in_features=26*26*64, out_features=128)
self.out = nn.Linear(in_features=128, out_features=2) def forward(self, x):
x = F.relu(self.conv1(x))
x = F.max_pool2d(x, kernel_size=2, stride=2)
x = F.relu(self.conv2(x))
x = F.max_pool2d(x, kernel_size=2, stride=2)
x = F.relu(self.conv3(x))
x = F.max_pool2d(x, kernel_size=2, stride=2)
x = x.view(-1, 26*26*64)
x = F.relu(self.fc1(x))
x = F.dropout(x, p=0.2)
x = self.out(x)
return F.log_softmax(x, dim=1)torch.manual_seed(42)
model = CustomCNN()
model.to(device)
在这里,我们可以定义一个优化器和标准,并准备好进行培训:
custom_criterion = nn.CrossEntropyLoss()
custom_optimizer = torch.optim.Adam(model.parameters(), lr=0.001)
因为你可以访问源代码、和 train_model 函数太长了,我们决定不把它放在这里。因此,如果您继续学习,请参考源代码。我们将为 10 个时期训练模型:
custom_model_trained = train_model(
train_loader=train_loader,
test_loader=valid_loader,
model=model,
criterion=custom_criterion,
optimizer=custom_optimizer,
epochs=10
)
一段时间后,获得的结果如下:

无论如何,结果并不可怕,但我们如何才能做得更好?转学为救。
迁移学习法
你可以很容易地在网上找到正式的定义。对我们来说,迁移学习意味着下载一个预制的架构,它是在 1M 以上的图像上训练的,并调整输出层,以便它可以根据你的需要分类许多类别。
因为我们这里只有猫和狗,所以我们需要将这个数字修改为 2。
现在,我们将下载预训练版本的 ResNet101 架构,并使其参数不可训练——因为网络已经训练好了:
pretrained_model = models.resnet101(pretrained=True)for param in pretrained_model.parameters():
param.requires_grad = False
太好了!让我们看看输出图层是什么样子的:
pretrained_model.fc**>>> Linear(in_features=2048, out_features=1000, bias=True)**
所以这个架构默认有 1000 个可能的类,但是我们只需要两个——一个用于猫,一个用于狗。以下是调整方法:
pretrained_model.fc = nn.Sequential(
nn.Linear(2048, 1000),
nn.ReLU(),
nn.Dropout(0.5),
nn.Linear(1000, 2),
nn.LogSoftmax(dim=1)
)pretrained_model.to(device)
这就是我们要做的。
我们仍然需要定义一个优化器和一个标准,但是你知道怎么做:
pretrained_criterion = nn.CrossEntropyLoss()
pretrained_optimizer = torch.optim.Adam(pretrained_model.fc.parameters(), lr=0.001)
训练过程与定制架构相同,但是我们不需要这么多的纪元,因为我们已经知道权重和偏差的正确值。
pretrained_model_trained = train_model(
train_loader=train_loader,
test_loader=valid_loader,
model=pretrained_model,
criterion=pretrained_criterion,
optimizer=pretrained_optimizer,
epochs=1
)
一段时间后,获得的结果如下:

多神奇啊?不仅准确度提高了,而且我们还通过不训练太多纪元节省了大量时间。
现在你知道迁移学习能做什么,以及如何和为什么使用它。让我们在下一部分总结一下。
结论
这就是 PyTorch 最简单的迁移学习指南。当然,如果网络更深,定制模型的结果可能会更好,但这不是重点。重点是,没有必要强调多少层是足够的,以及最佳超参数值是多少。至少在大多数情况下是这样。
请务必尝试不同的架构,并随时在下面的评论部分告诉我们结果。
感谢阅读。
喜欢这篇文章吗?成为 中等会员 继续无限制学习。如果你使用下面的链接,我会收到你的一部分会员费,不需要你额外付费。
[## 通过我的推荐链接加入 Medium-Dario rade ci
作为一个媒体会员,你的会员费的一部分会给你阅读的作家,你可以完全接触到每一个故事…
medium.com](https://medium.com/@radecicdario/membership)
使用 ggplot2 转换数据并创建美丽的可视化效果
创建可视化的目的是探索数据,发现隐藏的趋势和交流趋势。为了创造任何观想,我们需要一个我们希望探索的问题,我们需要可以帮助我们回答这个问题的数据。我们要探讨的问题是“新冠肺炎事件如何改变了人们的流动模式?”我们将使用的数据比较了因新冠肺炎而在不同地方发生的基线移动趋势的变化,这些数据由 Google 在这里提供。
遵循本教程将帮助您理解如何转换 R 中的数据并绘制堆积条形图。以下是最终结果:

新冠肺炎事件导致欧洲国家人口流动模式的变化
感兴趣的列的描述:
国家代码— "country_region_code"
国家名称— "country_region"
零售/娱乐空间变更—" Retail _ and _ Recreation _ percent _ avg "
杂货店/药房空间变更—"杂货店 _ and _ 药房 _percent_avg"
停车位变更— "parks_percent_avg"
公交站空间变更—" Transit _ stations _ percent _ avg "
工作场所空间变更— "workplaces_percent_avg "
如果您仅对 ggplot2 定制感兴趣,请跳至步骤 3。
我们的数据探索之旅开始了:
步骤 0:导入库
# Importing librarieslibrary("dplyr")
library("magrittr")
library("plotly")
library("ggplot2")
library("tidyr")
library("cowplot")
第一步:读取数据
# Reading downloaded csv
data <- read.csv(file = 'Global_Mobility_Report.csv')# Print column names in dataframe
colnames(data)

数据中的列
步骤 2:转换数据
我们将为欧洲国家创建可视化,因此我们将不得不过滤掉其他国家。
# Required country codes
country_region_code <- c("GB","SK","SI","SE","RO","PT","PL","NO","NL","MT","LV","LU","LT","IT","IE","HU","HR","GR","FR","FI","ES","EE","DK","DE","CZ","BG","BE","AT")# Creating a subset using required country codes
subset = data[data$country_region_code %in% country_region_code ,]# Check countries in subset
unique(subset$country_region)

将包含在可视化中的国家
在我们的数据中,我们列出了每天的移动趋势变化,但我们希望绘制整个期间的变化,因此我们必须汇总数据。为此,我们将使用country_region_code进行分组,并为每个移动类别计算mean。
# Aggregating data to get average percent change
aggdata <- subset %>%group_by(country_region_code, country_region) %>%
summarize(retail_and_recreation_percent_avg = mean(retail_and_recreation_percent_change_from_baseline, na.rm = TRUE),
grocery_and_pharmacy_percent_avg = mean(grocery_and_pharmacy_percent_change_from_baseline, na.rm = TRUE),
parks_percent_avg = mean(parks_percent_change_from_baseline, na.rm = TRUE),
transit_stations_percent_avg = mean(transit_stations_percent_change_from_baseline, na.rm = TRUE),
workplaces_percent_avg = mean(workplaces_percent_change_from_baseline, na.rm = TRUE),
residential_percent_avg = mean(residential_percent_change_from_baseline, na.rm = TRUE))colnames(aggdata)

聚合数据中的列
我们将添加另一列overall_mob_percent,它将显示流动性百分比的总体变化,以便我们可以将受流动性变化影响最大的国家的数据排序为最小。
# Adding additional average change column
aggdata <- transform(aggdata, overall_mob_percent=(retail_and_recreation_percent_avg+grocery_and_pharmacy_percent_avg+ parks_percent_avg+transit_stations_percent_avg+ workplaces_percent_avg+residential_percent_avg))colnames(aggdata)

新列`总体百分比'
目前,我们的数据以宽格式存储,其中每一类流动性变化都有单独的列。

列形式的类别
在使用gather绘图之前,我们必须将数据转换成长格式。
# Gathering data
aggdata_tsfm <- gather(aggdata, key = "parameter", value = "percent", -country_region_code, -country_region, -overall_mob_percent)colnames(aggdata_tsfm)

列“参数”有类别,“百分比”有相关值
订购数据。
# Sort based on overall_mob_percent
aggdata_tsfm <- aggdata_tsfm[order(aggdata_tsfm$overall_mob_percent),]
将country_region转换为因子,以便在我们的绘图中保留顺序。
# Converting to factor for preserving sequence in our visualisation
aggdata_tsfm$country_region <- factor(aggdata_tsfm$country_region,
levels = unique(aggdata_tsfm$country_region))
第三步:创建堆积条形图
这里,aggdata_tsfm是我们的数据框架,x轴是国家,y轴是移动值的百分比变化,我们将fill用不同的地点类别堆积条形图。我们已经将position设置为stack来创建一个堆叠条形图。您可以将position设置为dodge来创建并排条形图。为了给我们的条形块一个黑色轮廓,我们将color设置为黑色。
# Creating the plot
mainplot <- ggplot(aggdata_tsfm, aes(width = 0.9, fill=parameter, y=percent, x=country_region)) +
geom_bar(position="stack", stat="identity", color="black")
添加水平线以区分-ve、+ve y 轴,因为我们的数据在 y 轴上有正值也有负值。
# Adding line to differentiate -ve and +ve y axis
mainplot <- mainplot + geom_hline(yintercept=0, color = “white”, size=2)
添加 y 刻度,因为默认情况下刻度数非常少。我们的刻度将从-250 增加到 100,增加 50。
# Adding y ticks
mainplot <- mainplot + scale_y_continuous(breaks = round(seq(-250, max(100), by = 50),1))
我们将定制我们的地块的图例,以改变颜色,标签和顺序。
您可以使用 ColorBrewer 为定性和定量数据提供漂亮的调色板,并为色盲人士进行了优化,而不必纠结于使用哪种调色板。
一旦我们选择了颜色,我们就可以通过设置values参数来使用它们。要更改图例中的标签名称,我们可以设置labels。为了改变标签的顺序,我们使用breaks来指定所需的顺序。
# Changes to legend
mainplot <- mainplot + scale_fill_manual(name="Mobility categories",values = c("retail_and_recreation_percent_avg" = "#8dd3c7",
"grocery_and_pharmacy_percent_avg" = "#ffffb3",
"parks_percent_avg" = "#bebada",
"transit_stations_percent_avg" = "#fb8072",
"workplaces_percent_avg" = "#80b1d3",
"residential_percent_avg" = "#fdb462"),
labels=c("retail_and_recreation_percent_avg" = "Retail/Recreation",
"grocery_and_pharmacy_percent_avg" = "Grocery/Pharmacy",
"parks_percent_avg" = "Parks",
"transit_stations_percent_avg" = "Transit stations",
"workplaces_percent_avg" = "Workplaces",
"residential_percent_avg" = "Residential"),
breaks=c("residential_percent_avg", "workplaces_percent_avg",
"transit_stations_percent_avg",
"retail_and_recreation_percent_avg", "parks_percent_avg",
"grocery_and_pharmacy_percent_avg"))
改变我们的标题。
# plot title
mainplot <- mainplot + ggtitle(“Changes in movement of people due to COVID-19”)
设置我们的情节的副标题和标题。
# plot subtitle, caption
mainplot <- mainplot + ggtitle(“Changes in movement of people due to COVID-19”)
设置 x 和 y 标签。
# Setting x and y labels
mainplot <- mainplot + xlab(“Countries”) + ylab(“Percent change in mobility rate across different activities”)
由于 x 轴有国家名称,我们将旋转文本以避免文本重叠。
# x axis markings rotated
mainplot <- mainplot + theme(axis.text.x = element_text(angle = 90))
更改文本大小以提高可读性。
# change text size
mainplot <- mainplot + theme(text = element_text(size=20))
现在我们可以画出我们开始时展示的图表。
# Plotting
mainplot

看着可视化,更容易从数据中得到推论,比如人们在住宅区的活动增加了。负流动性最小的地方是杂货店和药店,这表明这些地方的客流量仍在增加,但不像过去那样了。
如果您希望在一个网格中绘制多个图表,您可以使用cowplot的plot_grid轻松完成。图resplo、parplot、recplot、groplot、traplot和worplot是使用与上述步骤 3 中演示的相同方法创建的每个类别的不同图,我们将它们绘制在网格中。
# Plotting a grid of plots
plot_grid(resplot, parplot, recplot, groplot, traplot, worplot,
ncol = 2, nrow = 3)

新冠肺炎事件导致欧洲国家人口流动模式的变化
我们已经看到了使用 ggplot2 创建强大的可视化是多么容易,以及如此多的定制你的情节的方法。我们几乎没有探索 ggplot2,它提供了如此多的东西。我强烈推荐探索 ggplot2 提供的其他图表和功能。
感谢阅读。
使用 Nyströ方法将数据转换到高维核空间
Nyströ方法将作为一种使核空间显式地可用于解决非线性问题的方法来介绍。

约书亚·索蒂诺在 Unsplash 上拍摄的照片
介绍
在机器学习领域,有许多算法,监督的(SVM,岭回归)和非监督的(k-means,PCA),它们可以被内核化以将问题映射到更高维的内核空间。这有助于解决更复杂的问题,但也带来了对核矩阵的依赖性,这种依赖性与给定样本的数量成平方关系。对于具有大量样本的问题,这可能会非常耗费成本和内存,因此很难应用。
本文将介绍 Nyströ方法,这是一种通过计算低秩核近似来有效存储和计算核矩阵的方法。此外,本文将说明 Nyströ方法可用于将任何样本从特征空间映射到近似的核空间。将给出一个示例来演示内核映射的潜在应用和由此产生的优势。
本文的目的是展示使用 Nyströ方法的优势和机会,无论是作为后续分类的预处理步骤,还是作为提高基于内核的算法性能的一个选项。
内核基础知识的简短回顾
核函数使得能够在高维核空间中操作,而不需要显式地将特征空间𝑋X 映射到核空间φφ。这是一个优点,因为对于一些映射函数(𝜙(𝑥)ϕ(x ),内核空间中的最终维数可能变得无限大,因此,映射过程非常耗费成本,甚至根本不可能计算。
基于核的算法利用了某些函数可以在另一个空间中被表示为内积的事实,而不是使用函数𝜙(𝑥对样本进行显式映射。
或者反过来:对于某些函数𝜙(𝑥),有函数𝜅(𝑥,𝑦),这样𝜅(𝑥,𝑦)= 。因此,使用核给仅使用给定特征空间的内积/距离操作的算法带来了优势。
常见的内核函数𝜅(𝑥,𝑦有:
- 径向基函数核
- 多项式核
- Sigmoid 内核
将核应用于给定的特征空间𝑋意味着必须计算包含𝜅(𝑥,𝑦结果的𝑛×𝑛核矩阵𝐾,使得

很明显,随着𝑛n.数量的增加,𝐾K 的大小呈二次方增加。因此,对于较大的数据集,计算可能会占用大量内存和时间。
Nyströ方法简介
假设𝜅(𝑥,𝑦)是导致核空间的函数,其中核维数𝑑大于样本数𝑛n,那么𝐾的最大秩必定是𝑚𝑖𝑛(𝑛,𝑑)=𝑛.所以,对于φ中的𝑛独立样本,k 是满秩的。现在让我们考虑,并非所有的𝑑d 维度都是必需的,使用具有𝑘
使用 nyströ方法计算低秩核矩阵𝐾̃can:从 x 中随机抽取𝑘样本。设 XS 是 x 的子集,仅包含这些样本,因此

设(𝑘×𝑘)矩阵𝑊是在 k 中转置的 c 和 c 的重叠,则近似核可以表示为

这个公式的推导和更详细的描述可以在[1][2]中看到。
因此,可以使用(𝑛×𝑘)和(𝑘×𝑘)矩阵来保存整个近似核。因为𝑘通常选择得比𝑛小得多,这在内存效率方面带来了巨大的优势。此外,核函数只需应用𝑛×𝑘次数,这可以大大减少计算时间。
定义参数 k
选择𝑘的值总是在近似质量和计算时间之间进行权衡。在内核近似的整个领域中有许多最近的研究,以及关于如何最佳地估计𝑘或者如何最有效地采样𝑘值来计算 c 的一些想法[3][4][5]。这些方法要么试图保证特定的运行时间与最坏情况下的近似质量保证相结合,要么保证近似内核的某些统计性能。
在这里讨论这些方法超出了范围。考虑这一主题的论文,可以在参考文献中找到。
一般来说,应该选择𝑘,这样𝑑≤𝑘
将样本映射到近似核空间
出于某些原因,比如数据的可视化或者不能直接使用内核的模型的应用,显式地访问φ可能是有用的。为了这个目的,我们可以修改 nyströ公式,以具有从核矩阵内积到显式近似核空间φ̃.的有效映射过程

可以看出,仅使用一次矩阵乘法就可以实现到低秩核空间的映射。𝑊的必要分解可以使用 Cholesky 分解、奇异值分解或特征分解来完成。
关于这一点,通过在内存中保存𝑋𝑆,任何任意采样𝑥都可以映射到内核空间:
- 计算𝑐,使𝑐[i]包含𝜅(𝑥,𝑋𝑆[i 的结果]))
- 计算𝜙̃ (𝑥)=𝑐𝑊^(−1/2)
实验——解决核空间中的非线性问题
以下示例演示了如何使用 Nyströ方法将非线性问题映射到近似核空间。因此,RGB 内核将被近似,并且所获得的内核部分 c 和𝑊将被用于将给定的特征空间变换到内核空间。
首先,让我们创建一个非线性问题:为了可视化的目的,数据具有包括 75 个样本的二维特征空间。在给定的空间中,有两类以不可线性分离的方式排列。在下面的图中,这两个类以不同的颜色显示。
然后,我们来定义核函数,也就是 RBF 核。这个内核有一个参数是γ,通常设置为 1/(2𝜎,𝜎是𝑋.的方差
现在,nyströ映射应用于𝑋和𝑘=100.结果是φ̃与𝑘维度。
出于可视化的目的,我们使用 PCA 来提取 3 个最相关的特征(具有最高方差的维度)。得到的 3 个维度在散点图中可视化。
散点图显示,来自第三维(PCA)的附加信息是决定性的,使得给定的问题可以线性分离。
为了证实这个视觉结果,我们将对这个问题应用线性回归,首先在特征空间,其次在核空间。此外,我们将比较两个模型的准确度训练分数(定义为(𝑇𝑃+𝑇𝑁)/(𝑇𝑃+𝑇𝑁+𝐹𝑃+𝐹𝑁))。因为问题是非线性的,而回归只能解决线性问题,所以在特征空间中应用的模型肯定是不合适的,从而导致较低的准确度分数。这意味着,如果第二个模型实现了非常高的训练精度,则问题必须在核空间中是线性可解的。
特征空间中的线性回归模型仅达到约 10%的训练精度。不出所料,这个问题是由于它的非线性太复杂,无法通过简单的线性回归来解决,而且该模型非常不适合。相比之下,在更高维度空间中应用的相同模型导致 100%的准确度,这意味着每个单个样本都可以被模型正确识别。
摘要
Nyströ方法可用于以非常有效的方式计算和保存核矩阵。此外,由于 Nyströ方法带来的高效映射过程,它使所有分类器都可以使用内核的全部功能。因此,如果与基于 Nyströ的特征映射相结合,线性回归等简单易懂的分类器可用于解决复杂的非线性问题。
参考
[1]https://en . Wikipedia . org/wiki/Low-rank _ matrix _ approximations
[2] Williams,C.K.I .和 Seeger,m .使用 Nystroem 方法加速内核机器。神经信息处理系统进展,2001
[3]亚历克斯·吉滕斯和迈克尔·马奥尼。重新审视 Nyströ方法以改进大规模机器学习。《第 30 届机器学习国际会议论文集》,第 567–575 页,2013 年。
[4]弗朗西斯·巴赫。低秩核矩阵近似的精确分析。《第 26 届学习理论会议论文集》,第 185-209 页,2013 年。
[5]艾哈迈德·阿拉维,迈克尔·马奥尼。具有统计保证的快速随机化核方法。arXiv:1411.0306,2015。
将 Jupyter 笔记本转变为电子书
将你的 Jupyter 笔记本转换成 PDF、EPUB 和 AWZ3 格式的精美电子书的一些技巧。不要像我一样花几个小时研究!

Kourosh Qaffari 在 Unsplash 上拍摄的照片
一个月前,我决定开始学习,这是一门专注于数据科学实用方面的在线课程。在 Jupyter 笔记本上写一本电子书似乎是一个不错的选择,因为它提供了文本、可视化和代码的完美结合。虽然写一本电子书本身已经是一个挑战,但我在将笔记本转换成电子书格式时遇到了很多问题。这些技巧对技术作家和希望在电子阅读器上查看笔记本的数据科学家很有用。遵循这些建议,这样你就不会像我一样花几个小时去研究了!
编译脚本将 Jupyter Notebook 转换成 PDF、EPUB 和 AWZ3 格式的精美电子书。编译…
github.com](https://github.com/romanorac/jupyter-notebook-to-ebook)
这里有几个你可能会感兴趣的链接:
- [Labeling and Data Engineering for Conversational AI and Analytics](https://www.humanfirst.ai/)- [Data Science for Business Leaders](https://imp.i115008.net/c/2402645/880006/11298) [Course]- [Intro to Machine Learning with PyTorch](https://imp.i115008.net/c/2402645/788201/11298) [Course]- [Become a Growth Product Manager](https://imp.i115008.net/c/2402645/803127/11298) [Course]- [Deep Learning (Adaptive Computation and ML series)](https://amzn.to/3ncTG7D) [Ebook]- [Free skill tests for Data Scientists & Machine Learning Engineers](https://aigents.co/skills)
上面的一些链接是附属链接,如果你通过它们进行购买,我会赚取佣金。请记住,我链接课程是因为它们的质量,而不是因为我从你的购买中获得的佣金。
1.创建 Github 存储库
我建议你从一个专门的 Github 库开始,它将帮助你跟踪你的电子书的版本。Github 免费提供私有库,所以没有理由不使用它。JupyterLab 还有一个 Git 扩展,可以帮助您跟踪版本之间的变化:
JupyterLab 刚刚成为一个成熟的 IDE,具有代码辅助、调试和 Git 等功能——欢迎来到未来……
towardsdatascience.com](/3-must-have-jupyterlab-2-0-extensions-41024fe455cc)
请确保将以下内容添加到。gitignore,这样您就不会意外地将不必要的文件推到您的 Git 存储库中:
*.csv
.DS_Store
.ipynb_checkpoints/
*.mov
*.pdf
*.html
*.azw3
*.epub
cpdf
2.在自己的笔记本中组织每一章
按章节组织笔记本中的内容,便于写作时专注于某个主题。它减少了干扰,也简化了对版本控制差异的审查。
以这种方式组织电子书还有另一个好处,这一点在最后变得很明显。电子书中的每一章都应该在新的一页开始,而不是在前一页的中间。

第二章从这一页的中间开始。
我花了几个小时寻找一种解决方案,在将 Jupyter 笔记本导出到 HTML 或 PDF 时,在章节之间添加分页符,但运气不好。
然后我想到了一个主意!将 Jupyter 笔记本中的每一章转换成 PDF,然后将它们合并成最终的 PDF。
3.不要用 Jupyter 的导出笔记本当 PDF
JupyterLab 有一个简洁的“将笔记本导出为 PDF”功能,起初看起来像是节省时间。JupyterLab 的导出功能不考虑 CSS,所以熊猫数据帧在 PDF 中是普通格式。

有很多列的 Dataframe 在 JupyterLab 看起来很棒,但是当我们把它导出到 PDF 时会发生什么?

当我们将笔记本导出为 PDF 时,一个包含许多列的数据框架会被分解到多个页面上。
我试图说服自己“出口”看起来没有那么糟糕,但我仍然不满意。在花了一些时间进行研究后,我尝试将笔记本导出为 HTML 格式,在 Chrome 浏览器中打开,并使用 Chrome 的保存为 PDF 功能。

Jupyter 笔记本导出为 HTML 格式,用 Chrome 浏览器保存为 PDF 格式。
啊哈!数据帧格式现在看起来更好了。
4.不要手动保存到 PDF
拥有多个章节并将每个章节导出为 HTML 并保存为 PDF 是一个繁琐的过程。我们能自动化它吗?
Chrome 有一个无头模式,所以我们可以从命令行保存 HTML 到 PDF。这在理论上应该行得通,但对我来说行不通。
chrome --headless --print-to-pdf=Chapter1.pdf --print-to-pdf-no-header Chapter1.html
尽管使用了-print-to-PDF-no-header 标志,但使用 Chrome 导出的 PDF 仍填充了页眉和页脚。
经过研究,我发现了 wkhtmltopdf 工具,它使用 Qt WebKit 渲染引擎将 HTML 渲染为 pdf 和各种图像格式。在 macOS 上,您可以使用 Brew 安装它。该工具使我们能够自定义 CSS 和边距。
Chrome 浏览器生成了一个不错的 PDF,所以我从 Chrome 中提取 CSS 格式并保存到一个 custom.css 文件中,这是 wkhtmltopdf 工具默认采用的:
div#notebook {
font-size: 18px;
line-height: 26px;
}img {
max-width: 100% !important;
page-break-inside: avoid;
}tr, img {
page-break-inside: avoid;
}*, *:before, *:after {
background: transparent !important;
box-shadow: none !important;
text-shadow: none !important;
}p, h2, h3 {
orphans: 3;
widows: 3;
page-break-inside: avoid;
}*, *:before, *:after {
page-break-inside: avoid;
background: transparent !important;
box-shadow: none !important;
text-shadow: none !important;
}*, *:before, *:after {
page-break-inside: avoid;
background: transparent !important;
box-shadow: none !important;
text-shadow: none !important;
}
获取 HTML 并输出 pdf 的 wkhtmltopdf 命令:
wkhtmltopdf — enable-internal-links -L 10mm -R 9.5mm -T 10mm -B 9.5mm Chapter1.html Chapter1.pdf
5.合并 pdf
我使用 cpdf 工具将 pdf 合并成最终的 PDF。我下载了 cpdf 工具,放在 Jupyter 笔记本的文件夹里。要合并 pdf,请使用以下命令:
./cpdf Chapter1.pdf Chapter2.pdf -o Ebook.pdf

最终合并的 PDF。
6.将 Jupyter 笔记本转换为 EPUB 格式
我们把每一章都放在一个单独的笔记本里。让我们用 nbmerge 工具合并笔记本。可以用 pip 安装:pip install nbmerge。
nbmerge Chapter1.ipynb Chapter2.ipynb > Ebook.ipynb
JupyterLab 的“导出到 HTML”命令也可以导出 CSS,这对于 PDF 来说很好,但是对于电子书来说就有问题了,因为它太复杂了。Jupyter 带有 nbconvert 工具,可以导出不同的格式。要将笔记本导出为不带 CSS 的 HTML 格式:
jupyter nbconvert --to html Ebook.ipynb --template=basic
我们需要安装 Calibre ,将 HTML 转换成 EPUB。如果你是一个狂热的电子书读者,我敢肯定你以前见过 Calibre。Calibre 是一个用于电子书管理的跨平台开源套件。
运行以下命令将 HTML 转换为 EPUB 和 AWZ3(命令在 macOS 上有效):
/Applications/calibre.app/Contents/MacOS/ebook-convert Ebook.html Ebook.epub
/Applications/calibre.app/Contents/MacOS/ebook-convert Ebook.html Ebook.azw3

在苹果 iBooks 上阅读 EPUB 电子书。
7.让我们把所有的东西放在一起
作为软件开发人员,我们将事情自动化。我编写了一些可以很好地打包到 bash 脚本中的命令。因此,每当我在 Jupyter 笔记本中进行更改时,我都可以运行编译脚本来创建电子书的新版本:
#!/bin/bashnbmerge Chapter1.ipynb Chapter2.ipynb > Ebook.ipynbjupyter nbconvert --to html Ebook.ipynb --template=basic/Applications/calibre.app/Contents/MacOS/ebook-convert Ebook.html Ebook.epub/Applications/calibre.app/Contents/MacOS/ebook-convert Ebook.html Ebook.azw3jupyter nbconvert --to html Chapter1.ipynbjupyter nbconvert --to html Chapter2.ipynbwkhtmltopdf --enable-internal-links -L 10mm -R 9.5mm -T 10mm -B 9.5mm Chapter1.html Chapter1.pdfwkhtmltopdf --enable-internal-links -L 10mm -R 9.5mm -T 10mm -B 9.5mm Chapter2.html Chapter2.pdf./cpdf Chapter1.pdf Chapter2.pdf -o Ebook.pdf
我还用上面提到的命令创建了一个 Git 存储库:
编译脚本将 Jupyter Notebook 转换成 PDF、EPUB 和 AWZ3 格式的精美电子书。编译…
github.com](https://github.com/romanorac/jupyter-notebook-to-ebook)
在你走之前
在 Twitter 上关注我,在那里我定期发布关于数据科学和机器学习的。

用熊猫改变现实
使用转置、融合、合并等功能重塑数据。

尼克·伍德在 Unsplash 上的照片
熊猫让 python 变得简单
纯 python 是一种非常清晰的语言。但是熊猫真的让它变笨了。简化 python 脚本语言使得更复杂的编程变得更加容易。DataFrame 操作使数学、科学、探索、艺术和魔法变得简单而直观。图书馆是研究者最好的朋友。
熊猫让我们可以更专注于研究,而不是编程。我们发现熊猫易学、易用、易维护。最重要的是,它提高了我们的生产力。”(* Roni Israelov,博士 ,投资组合经理, AQR 资本管理)*
改变现实
数据科学项目的第一步是将数据集加载到 pandas 数据框架中,并使用 检查前五行。(head())法,翻着白眼,不可置信地呻吟,并对数据集的作者缺乏远见感到绝望。他们不知道您需要列级聚合吗?在 Excel 中,这将是一场复制粘贴的噩梦。熊猫给你一条走出迷雾的路,非常聪明,可读性强。

照片由 Ibrahim Rifath 在 Unsplash 上拍摄
本文的目标
在本文中,我将演示几个 pandas DataFrame 操作,用于转换数据和构建可重现的函数,您可以使用这些操作来自动化 ETL-Transform 的“T ”,并节省您的脑力和耐心来关注真正重要的问题。指导我的数据集设计的许多原则来自于 R tidyverse。
1.下载数据
我们将使用世界银行的数据,该数据给出了自 1960 年以来每个国家的年度 GDP。乍一看,很可怕。专栏的年份?

2.想象你的目标。
现在我可以看到数据是如何排列的,我需要设想转换的最终目标,并考虑完成转换所需的步骤。
这是我希望的最终结果,一个显示年份、GDP 四分位数和平均 GDP 百分比变化的 3 列数据框架。这将用于创建一个线图,显示每个四分位数每年的增长百分比。这有助于可视化全球经济趋势,并衡量某些事件和趋势对全球 GDP 不同四分位数的影响。(滚动到底部查看最终图表)

3.移项
我们需要得到每个国家 GDP 的百分比变化,这样我们就可以按四分位数计算总平均值。现在,数据框架指数是按国家的,所以我们需要将数据框架转换成按年份的指数,以国家为列。以年份为索引,以国家为列,这将非常适合于计算每一列(即各个国家)各行的百分比变化。

4.百分比变化
熊猫有一个很优秀的 DataFrame 操作叫做 DataFrame.pct_change()。 此操作向下计算列,并用该列与其上一行之间的百分比变化替换该值。

5.转置回原始形状
使用转换后的 GDP 百分比变化数据框架,将行和列转置回原始形状。

6.熔化数据帧
别忘了我们还有原始的 GDP 数据框架。计算每个国家的四分位数需要原始数据框架和未转换的国内生产总值分数。我们现在必须做的是融合国内生产总值和国内生产总值百分比变化数据框架,以便在下一步将其合并。

国内生产总值数据框架

国内生产总值百分比差异数据框架
7.合并数据帧
在我们迄今所做的一切之后,与熊猫融合是如此容易,这是一种解脱。我们可以简单地对原始 GDP 数据执行 DataFrame.merge(),并告诉它将百分比差异数据作为参数进行合并。

日常管理—删除、重命名、转换为数字、删除 NAs
您会注意到 _x 和 _y 后缀,Pandas 很好地分配了它们,这样我们就不会被合并后的数据集弄糊涂了。现在我们必须删除不必要的列,重命名这些列,并将 year 转换为 numeric(它被错误地作为对象读入)。

8.计算每年 GDP 的四分位数
我们需要的是每个国家的 GDP 指标,以了解它在年度 GDP 分布中的位置。四分位数是根据数据在后 25%(按计数,而不是值)、26–50%、51–75%和 76–100%中的位置对数据进行分组的极好方法。我们将使用这个优秀的 StackOverflow 答案,结合 groupby()、transform()和 lambda(),为该年计算的 GDP 四分位数创建一个新列。

9.平均 GDP 百分比变化——按四分位数
现在我们有了创建最终数据帧的所有部分。我们将再次使用data frame . group by()**按年份和四分位数分组,并将平均值计算集中在 GDP_pct_change 上。**

你现在可以呼吸了。我们已经走了这么远!
当我们第一次用 检查时,数据集几乎无法辨认。头()。我们现在可以看看过去 60 年的全球 GDP 变化,并检查不同的 GDP 四分位数对全球趋势的反应。
让我们用一个简单的 Seaborn line 情节来享受我们的劳动成果吧。

谢谢你陪我坚持下来!数据清理和转换是分析过程中至关重要的一部分,我们刚刚经历了一次令人费解的旅程,数据采用了许多不同的形状和维度。
用蜂鸟把你的 ML 模型变成 Pytorch
机器学习
利用张量加速您的机器学习模型

在过去的几年里,深度学习的能力已经大大增加了。随着这一点,许多服务于你的神经网络的标准已经找到了通往大众的道路,如 ONNX 和 TVM 。
这种流行导致了对通过利用张量计算来优化深度学习管道、训练、推理和部署的关注。
相比之下,传统的机器学习模型,如随机森林,通常在推理任务上是基于 CPU 的,并且可以受益于基于 GPU 的硬件加速器。
用蜂鸟把你训练过的机器学习模型转换成 Pytorch
现在,如果我们可以在传统的随机森林中使用神经网络的许多优势,会怎么样呢?更好的是,如果我们能够转换随机森林并且利用 GPU 加速推理会怎么样?
这就是微软的蜂鸟的用武之地!它将你的机器学习模型转换为张量计算,这样它就可以使用 GPU 加速来加速推理。
在本文中,我不仅将描述如何使用这个包,还将描述相应论文和路线图中的基础理论。
注:蜂鸟只是用来加快时间做预测的,不是用来加快训练的!
1.理论
在深入研究这个包之前,重要的是要理解为什么这种转换是可取的,以及为什么可以这样做。
为什么要把你的模型转换成张量?
在某种程度上,张量是一个 N 维数据数组(见下图)。在 TensorFlow 的上下文中,它可以被视为一个矩阵的一般化,允许您拥有多维数组。您可以对它们执行优化的数学运算,而无需知道每个维度在语义上代表什么。例如,如何使用矩阵乘法来同时操作几个向量。

张量的简化。注意,在数学、物理和计算机科学中,张量的应用和定义可能不同!
将你的模型转换成张量有几个原因:
- 张量作为深度学习的中坚力量,在让深度学习走向大众的背景下,得到了广泛的研究。张量运算需要显著优化,以便深度学习在更大规模上可用。这极大地加快了推断的速度,从而降低了进行新预测的成本。
- 与使用传统的机器学习模型相比,它允许一个更加统一的标准。通过使用张量,我们可以将传统模型转换为 ONNX,并在所有人工智能解决方案中使用相同的标准。
- 神经网络框架中的任何优化都可能导致传统机器学习模型的优化。
转换算法模型
向张量的转换不是一项简单的任务,因为有两个模型分支:代数(例如,线性模型)和算法模型(例如,决策树)。这增加了将模型映射到张量的复杂性。
在这里,算法模型的映射尤其困难。张量计算已知用于执行大量或对称运算。这对于算法模型来说是很难做到的,因为它们本质上是不对称的。
让我们以下面的决策树为例:

要映射到张量的示例决策树。此处检索到。
决策树分为三个部分:
- 输入特征向量
- 四个决策节点(橙色)
- 五个叶节点(蓝色)
产生的神经网络的第一层是连接到四个决策节点(橙色)的输入特征向量层。在这里,所有条件一起评估。接下来,通过使用矩阵乘法来一起评估所有叶节点。
作为神经网络,生成的决策树如下所示:

作为神经网络的决策树。此处检索到。
所产生的神经网络引入了冗余度因为所有条件都被评估。然而,通常只评估一条路径。这种冗余部分地被神经网络向量化计算的能力所抵消。
注:模型转化为张量的策略还有很多,在他们的论文里有描述,这里这里和这里。
加速
根据他们的论文,与传统模型相比,GPU 加速的使用大大提高了推理速度。

比较使用 Sklearn 的相同模型与使用 Hummingbird 的神经网络的推理结果。此处检索到。
上述结果清楚地表明,将您的森林转换为神经网络可能是值得的。
我想亲眼看看在启用了 GPU 的 Google 联合实验室服务器上的结果会是什么样子。因此,我在谷歌联合实验室上做了一个快速的,但绝不是科学的实验,结果如下:

在启用 GPU 的情况下,在 Google Colab 上运行二元分类模型 100 次的结果。数据集是随机生成的,包含 100000 个数据点。看来用蜂鸟推断肯定比基础款快。实验的代码可以在这里找到。
我们可以清楚地看到,在使用单一数据集时,对新数据的推断速度大大加快。
2.使用
蜂鸟是
幸运的是,使用这个包非常简单。很明显,作者已经花费了大量的时间来确保这个包可以直观地在许多模型上使用。
现在,我们从通过 pip 安装包开始:
pip install hummingbird-ml
如果您还想安装 LightGBM 和 XGboost 依赖项:
pip install hummingbird-ml[extra]
然后,我们简单地开始创建我们的 Sklearn 模型,并在数据上对其进行训练:
这样做之后,我们实际上要做的唯一一件事就是导入蜂鸟并使用convert函数:
得到的model只是一个torch.nn.Module,然后可以像平常使用 Pytorch 一样使用。
正是转换到 Pytorch 的便利首先吸引了我对这个包的注意。只需几行代码,您就已经改造了您的模型!
3.路标
目前,以下型号已实施:
- 大多数 Scikit-learn 模型(例如,决策树、回归和 SVC)
- LightGBM (分类器和回归器)
- XGboost (分类器和回归器)
- ONNX。ML (TreeEnsembleClassifier 和 TreeEnsembleRegressor)
虽然一些模型仍然不见踪影,但是他们的路线图表明他们正在前进:
- 特征选择器(例如,变量阈值)
- 矩阵分解(例如 PCA)
- 特征预处理(例如,MinMaxScaler、OneHotEncoder 等。)
你可以在这里找到蜂鸟的完整路线图。
感谢您的阅读!
如果你像我一样,对人工智能、数据科学或心理学充满热情,请随时在 LinkedIn 上添加我,或者在 Twitter 上关注我。
点击下面的一个帖子,了解更多关于其他有趣的软件包的信息:
使用稳定基线和 Gym 训练 SOTA RL 算法
towardsdatascience.com](/reinforcement-learning-in-a-few-lines-of-code-d6c8af1e0fd2) [## 借助 Streamlit 快速构建和部署应用
将您的 Streamlit 应用程序部署到 Heroku,展示您的数据解决方案
towardsdatascience.com](/quickly-build-and-deploy-an-application-with-streamlit-988ca08c7e83) [## 打开黑盒:如何利用可解释的机器学习
使用 PDP、LIME 和 SHAP 制定可解释的决策,为利益相关方创造价值
towardsdatascience.com](/opening-black-boxes-how-to-leverage-explainable-machine-learning-dd4ab439998e)
解释:ML 变换和缩放
何时使用变换或缩放?哪个定标器?哪些算法?

在建模之前,所有不同的实体应该在相同的比例上;保罗·卡萨尔斯在 Unsplash 上的照片
缩放、标准化和变换是数字特征工程的重要步骤,它们正被用于处理倾斜的特征并为建模而重新缩放它们。机器学习和深度学习算法高度依赖于输入数据质量。如果数据质量不好,再高性能的算法也是白搭。就像垃圾进:垃圾出一样简单。
在建模之前,数据科学家有如此多的预处理步骤需要测试和验证。这进一步增加了围绕变换和缩放的效果和使用的混乱。在这里,我们将尝试使用公共数据集来回答一些问题
何时使用变换或缩放?使用哪个定标器?哪些算法受益?
本练习使用的数据是来自家庭信用违约风险的主数据文件。数据被整理以预测客户的还款能力。数据是针对所有贷款申请的,在我们的数据示例中,一行代表一个贷款申请。
数据集可以从数据下载。而 Jupyter 笔记本可以从— 笔记本下载。
公布数据集是为了说明每个申请人的还贷能力?下面是目标特征和一些独立特征的分布。目标特征存在数据不平衡问题,正类只占全部数据的 8%。

目标特征:贷款违约
以下是一些重要的数字独立特征及其直方图。选择它们只是为了解释练习。他们都在寻找不同的范围和规模,例如AMT _ annuity在 mn 范围内,但 OWN_CAR_AGE 最大可以开到 90。
AMT _ 年金;金额 _ 贷方;金额 _ 商品 _ 价格;金额 _ 收入 _ 总额;天数 _ 出生;OWN _ CAR _ AGE

数字特征直方图:原始数据
下面是原始数据的合并箱线图,由于一个空间中不同的比例和倾斜特征,它看起来高度倾斜。

组合箱线图:原始数据
转换
正态分布特征是统计算法中的一种假设。深度学习和回归型算法也受益于正态分布的数据。
需要进行变换来处理倾斜的要素并使其呈正态分布。使用平方根/立方根/ 对数变换可以将右斜特征变换为正态特征。
根据上述直方图金额 _ 年金、金额 _ 信用、金额 _ 商品 _ 价格、金额 _ 收入 _ 总额、&自有 _ 汽车 _ 年龄为偏态数字特征,天数 _ 出生为正态分布。
偏斜可能是由于以下两个原因之一
- 存在极端异常的异常值,对我们来说可能不重要。
- 或者说特征的自然分布是偏斜的,而尾部对我们很重要。这是大多数现实生活中的情况

对数变换简介:如左图所示,正值的对数函数输出增长非常缓慢。因此,与较低的观测值相比,较高的值被边缘化得更多。
变换的效果:对数变换后,倾斜的数字特征可能呈正态分布。例如,在下图中,金额信用经过对数变换后呈正态分布。

日志转换前后:金额 _ 信用
- 对数变换对倾斜目标特征的影响(回归的情况): 对数变换可以将倾斜特征视为常态。并且,如果我们的目标特征是正态分布的,该算法将给予所有样本同等的重要性。它也被称为同质性。这相当于处理分类目标特征中的不平衡数据问题,就像我们在给定的数据集中一样。所以有一个正态分布的目标特性是很好的。
- 对数变换对偏斜的独立特征的影响: 对数变换可以使独立特征像上面一样正态化,其中 AMT_CREDIT 在对数之后接近正态分布。但是它可能不会改善目标和独立特征之间的关系。因此,处理独立的倾斜特征可能有益于也可能无益于建模精度,这完全取决于两者之间的原始因果关系。
缩放比例
缩放是重新缩放数据所必需的,当我们希望在我们的算法中使用相同的缩放比例来比较要素时,会用到缩放。而且,当所有特征都处于相同的比例时,它也有助于算法更好地理解相对关系。
如果从属特征转换为正态,则应在转换后应用缩放。
缩放后哪些算法可能受益?缩放有助于基于距离的算法,也有助于加快收敛
线性&逻辑回归、KMeans/ KNN、神经网络、 PCA 将受益于规模化
缩放后哪些算法可能不会受益?有些算法与缩放无关。基于熵&信息增益的技术对单调变换不敏感。
基于树的算法、决策树、随机森林、增强树(GBM、light GBM、xgboost) 可能无法从扩展中受益。
D 在缩放/标准化/规范化过程中,我们将遵循 sklearn 词汇表,因此使用通用单词 S 缩放而不是标准化或规范化是一个不错的选择
训练数据上拟合的定标器模型将用于转换测试集。切勿在测试数据上再次安装定标器
Sklearn 主要有以下四种定标器
1.最小最大缩放器
2。鲁棒定标器
3。标准缩放器
4。标准化者。
Minmax 定标器应该是定标的首选。对于每个特征,每个值减去相应特征的最小值,然后除以同一特征的原始最大值和最小值的范围。它的默认范围在[0,1]之间。
下面是最小最大缩放后所有 6 个特征的直方图。在缩放之前,我们没有对任何功能进行对数转换。MinMaxScaler 没有改变特性的内部分布,也把每个人放在了同一个尺度上。

最小最大缩放后的直方图
下面是缩放后所有 6 个特征的组合箱线图。并且,都在[0,1 ]的范围内。每个要素值之间的内部空间得到了保持,并且与原始数据相比,它们的相对分布看起来也更好

最小最大缩放器后的箱线图
当数据含有大量异常值,并且我们希望消除它们的影响时,可以使用鲁棒定标器。但是不重要的离群值应该首先被移除。RobustScaler 减去列的中值,然后除以四分位数范围。
下图是鲁棒缩放器后的特征直方图。尽管直方图看起来与原始数据分布相似,但它们各自的内部距离空间并不像原始数据那样保持不变。

鲁棒缩放器后的直方图
此外,如下面的方框图所示,现在范围不在[0,1]内。并且每个特征的值之间的相对空间被扭曲并且现在不相同。在这种情况下,使用 robustscaler 会向建模过程传递关于底层数据的错误信息。

鲁棒定标器后的箱线图
StandardScaler 重新调整每一列,使其具有 0 个平均值和 1 个标准差。它通过减去平均值并除以标准差来标准化特征。如果原始分布不是正态分布,可能会扭曲要素之间的相对空间。
以下是应用标准缩放器后的要素直方图,分布看起来类似于原始数据分布,但它们并不相同,它们各自的内部观测距离在标准缩放过程中发生了变化。

以下是标准缩放后要素的组合箱线图,正如预期的那样,它扭曲了要素值之间的相对距离,而在最小-最大值后它们看起来更好

标准定标器后的方框图
规格化器应用于行,而不是列,所以 sklearn 用户不应该混淆,也不应该使用规格化器。Normalizer 的一些应用案例是在同一时间序列中比较多个实体,即在给定时期内多个股票的股票移动
结论
- 在建模之前,应该对偏斜的目标特征进行正态性处理,尤其是当异常值也很重要时
- 在分析过程中,应了解如何处理倾斜的从属特征及其影响
- MinMaxScaler 应该是缩放的首选
- 实验和观察可以帮助我们进一步决定正确的方法
感谢阅读。如果你喜欢过这篇文章,你可能也会喜欢 熊猫指数——隐形 灵魂、 熊猫支点&堆叠
使用 OpenCV 进行转换
关于如何用 Python 的 OpenCV 转换图像的指南

如果你曾经不得不使用一个可以位图化的图像增强器,你可能知道这有多困难。在本文中,我们将了解变换,并探索移动、旋转、裁剪和调整大小等内容。
要求
对于示例,我们将使用 OpenCV、NumPy 和 Matplotlib。
import cv2
import numpy as np
import matplotlib.pyplot as plt
在这里,我学习了 OpenCV 的一些基础知识,比如读取、显示和修改图像的一些属性。本文中的例子将从那里开始,但我认为你不需要阅读它来跟上这一点。
变换矩阵
从一些简单的东西开始,我们将读取图像并将颜色序列从 BGR 转换为 RGB。
然后,我们将构建一个包含图像变换信息的变换矩阵。为简单起见,我们可以将该矩阵视为:
[[size, rotation, location], ←x-axis
[rotation, size, location]] ←y-axis
默认矩阵或不会改变任何东西的矩阵是:
[[1, 0, 0]
[0, 1, 0]]
这意味着,100%的大小,零旋转,位置不变——再一次,我过于简化了。
在我们算出矩阵后,我们可以用。形状和用途。warpAffine 来转换图像。
让我们检查一个没有任何转换的例子,以了解这个过程。
# read img and convert color
img = cv2.imread('img3.jpeg')
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)# build a transformation matrix
matrix = [[1, 0, 0], #x
[0, 1, 0]] #y
t = np.float32(matrix)# get the sizes
h, w = img.shape[:2]# transform
img = cv2.warpAffine(img, t, (w, h))# plot
fig, ax = plt.subplots(1, figsize=(12,8))
ax.axis('off')
plt.imshow(img)

好吧,我们可以试着移动它。我们会用不同的矩阵重复所有的事情。
# Change location
matrix = [[1, 0, **300**], #x
[0, 1, **-300**]] #y

移动图像。
现在,让我们改变一切,看看它是如何工作的。
# Change size, rotation, and location
matrix = [[0.5, 0.3, 450], #x
[-0.3, 0.5, 600]] #y

移动、旋转和缩放图像。
有意思。你可能已经注意到我们有两种不同的“尺寸”——让我们来看看它们有什么不同。
我们可以在矩阵中将宽度缩放 0.5。
matrix = [[**0.5**, 0, 0], #x
[0, 1, 0]] #y

按 0.5 缩放的宽度。
我们可以把宽度除以 2 英寸。
img = cv2.warpAffine(img, T, (**int(w/2)**, h))

宽度除以 2。
我想理解转换矩阵是必要的,但是它有点过于复杂了。
旋转
OpenCV 有一些更方便的方法来构建它,比如 getRotationMatrix2D 方法,我们可以在一个方法中设置所需的图像位置、旋转角度和缩放因子。
img = cv2.imread('Images/3.jpg')
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)h, w = img.shape[:2]# (w/2, h/2) -> center of the image **T = cv2.getRotationMatrix2D((w/2, h/2), 90, .5)**img = cv2.warpAffine(img, T, (w, h))fig, ax = plt.subplots(1, figsize=(12,8))
ax.axis('off')
plt.imshow(img)

移动、旋转和缩放图像。
如果我们只需要将整个画面旋转 90 度。trasponse 是一种更简便的方法。
img = cv2.imread('Images/3.jpg')
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)img = cv2.transpose(img)fig, ax = plt.subplots(1, figsize=(12,8))
ax.axis('off')
plt.imshow(img)

转置图像。
镜子
我们可以创造一种镜像效果,比如倒影。我们只需要。随着图像和我们想要反映的方向翻转。
img = cv2.imread('Images/2.jpeg')
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)fig, ax = plt.subplots(1, figsize=(12,12))ax = plt.subplot(221)
plt.imshow(img)img2 = cv2.flip(img, 1)
ax = plt.subplot(222)
plt.imshow(img2)img3 = cv2.flip(img, 0)
ax = plt.subplot(223)
plt.imshow(img3)img4 = cv2.flip(img, -1)
ax = plt.subplot(224)
plt.imshow(img4)

原始图像和镜像图像。图片由赖爷 Subiyanto
种植
很多时候,我们可能需要图像的特定部分,而不是整个图像。如果是这种情况,我们可以通过分割 NumPy 数组轻松地裁剪图片。
切片的第一部分与高度有关,所以如果我们把它看作一个表格,它就像行号,第二部分指的是宽度,所以它就像列号。
img = cv2.imread('Images/2.jpeg')
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)fig, ax = plt.subplots(1, figsize=(12,8))
ax.axis('off')# [height start: height end, width start: width end]
# [first row : last row, first column: last column]
plt.imshow(img**[500:1000, 250:500]**)

裁剪的图像。
猜测我们希望从哪个像素开始和结束裁剪并不是最佳选择,所以让我们构建一个简单的缩放函数来理解我们如何系统地完成这项工作。
# scale function
**def px(size, proportion):
return int(size * proportion)**# read, convert and get height/ width
img = cv2.imread('Images/2.jpeg')
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
h, w = img.shape[:2]# crop
img = img[**px(h, .25):px(h, .40)**, #25% to 40%
**px(w, .10):px(w, .35)**] #10% to 35%# plot
fig, ax = plt.subplots(1, figsize=(12,8))
ax.axis('off')
plt.imshow(img)

用函数裁剪
调整大小
默认情况下,调整大小是通过线性插值实现的,但是我们也可以尝试其他方法来看看它们之间的区别。
让我们尝试使用。resize。我们需要图像和所需的大小,在这里我们可以指定我们想要的插值方法。尺寸可以是新照片的尺寸,也可以是 x 和 y 的比例因子。
# read, convert and get height/ width
img = cv2.imread('Images/2.jpeg')
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
h, w = img.shape[:2]# crop
img = img[px(h, .33):px(h, .35),
px(w, .18):px(w, .25)]# figure
fig, ax = plt.subplots(1, figsize=(12,16))# resize - nearest
ax = plt.subplot(311) **img_scaled = cv2.resize(img, None, fx=3, fy=3, interpolation = cv2.INTER_NEAREST)**
plt.imshow(img_scaled)
plt.title('nearest')# resize - default/ linear
ax = plt.subplot(312)
**img_scaled = cv2.resize(img, None, fx=3, fy=3)** plt.imshow(img_scaled)
plt.title('default/ linear')# resize - cubic
ax = plt.subplot(313)
**img_scaled = cv2.resize(img, None, fx=3, fy=3, interpolation = cv2.INTER_CUBIC)** plt.imshow(img_scaled)
plt.title('cubic')

不同的插值。
另一种调整大小的方法是使用金字塔。让我们快速看一下这是如何工作的。
# read and convert
img = cv2.imread('Images/ufo.jpg')
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)# crop
img = img[50:150, 200:400]# figure
fig, ax = plt.subplots(1, figsize=(12,12))# img
ax = plt.subplot(311)
plt.imshow(img)
plt.title('regular size')# downsize
**small = cv2.pyrDown(img)**ax = plt.subplot(312)
plt.imshow(small)
plt.title('smaller')# upsize
**regular = cv2.pyrUp(small)**ax = plt.subplot(313)
plt.imshow(regular)
plt.title('regular again')

金字塔法— 原图来自维基百科
我们可以看到,就像其他方法一样,分辨率有一些损失,但总的来说这是一个很好的方法。
好的,在我们结束前再来一个。
对我们的数组进行切片不仅对裁剪有用,我们还可以用它来操作图像的特定部分,甚至用切片来替换不同的部分。
# read and convert
img = cv2.imread('Images/ufo.jpg')
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)img2 = cv2.imread('Images/8.jpg')
img2 = cv2.cvtColor(img2, cv2.COLOR_BGR2RGB)# replace
img2[200:250, 1000:1100] = small# plot
fig, ax = plt.subplots(1, figsize=(12,12))
plt.imshow(img2)

图片由马克·阿斯特霍夫(没有飞碟)
好了,我们看到了如何移动、旋转和调整图像大小。我们还了解了裁剪、镜像以及如何处理图片的切片。
感谢阅读我的文章。我希望你喜欢它。
参考资料:
OpenCV 几何图像变换;
OpenCV 对数组的操作;
OpenCV 转置示例;
OpenCV 金字塔;
变形金刚模型…这一切是如何开始的?
Transformer 模型彻底改变了自然语言处理领域,但是,这一切是如何开始的呢?为了理解当前最先进的架构,并真正理解为什么这些模型成为该领域的突破,我们必须在时间上走得更远,因为我们知道 NLP 是从哪里开始的:当我们第一次在 NLP 中引入神经网络时。
将神经模型引入 NLP 找到了克服传统方法无法解决的挑战的方法。最显著的进步之一是序列对序列模型:这种模型通过一次预测一个单词来生成输出序列。序列到序列模型对源文本进行编码,以减少歧义并实现上下文感知。

序列对序列模型(图片由作者提供)
在任何语言任务中,语境都起着至关重要的作用。为了理解单词的意思,我们必须了解一些使用它们的情况。Seq2Seq 模型通过查看标记级别来实现上下文:上一个单词/句子生成下一个单词/句子。引入这种嵌入在空间中的上下文表示具有多个优点,例如避免了由于相似的上下文数据被映射为彼此靠近而导致的数据稀疏,并且提供了生成合成数据的方式。
然而,语言中的语境是非常复杂的。很多时候,只关注前一句话是找不到上下文的。需要长程相关性来实现上下文感知。Seq2Seq 模型与递归神经网络(LSTM 或格鲁)一起工作。这些网络具有记忆机制,在处理序列时调节信息流,以实现“长期记忆”尽管如此,如果一个序列足够长,他们将很难将信息从较早的时间步骤传递到后面的时间步骤。
当试图处理文本的整个段落时,RNNs 将达不到要求。他们受到梯度消失问题的困扰。梯度值用于更新神经网络的权重,从而进行学习。当梯度随着时间反向传播而收缩时,就会出现消失梯度问题。如果一个梯度值变得极小,对学习贡献不大。此外,RNNs 的拓扑结构非常耗时,因为对于每个反向传播步骤,网络都需要看到整个单词序列。

CNN 对数路径(图片由作者提供)
作为尝试解决这些问题的一种方法,在 NLP 中引入了卷积神经网络的使用。使用卷积创建对数路径。网络可以在对数卷积层中“观察”整个序列。然而,这提出了一个新的挑战:位置偏差。我们如何确保我们在文本中观察到的立场是那些给出更多见解的立场?为什么关注序列的位置 X 而不是 X-1?
此外,挑战不仅在于找到一种编码大量文本序列的方法,还在于能够确定文本的哪些部分对于获得上下文感知是必不可少的。并非所有的文本对理解都同样重要。为了解决这个问题,Seq2Seq 模型中引入了注意机制。
注意力机制受到动物视觉注意力的启发,它们专注于视觉输入的特定部分来计算适当的反应。Seq2Seq 架构中使用的注意力寻求向解码器提供更多上下文信息。在每一个解码步骤中,解码器都被告知应该对每个输入单词给予多少“关注”。

Seq2Seq 车型中的关注度(图片由作者提供)
尽管环境意识有所提高,但仍有很大的提高空间。这些方法的最大缺点是这些体系结构的复杂性。
这就是 transformer 模型出现的原因。transformer 模型引入了这样一种思想,即不在已经很复杂的 Seq2Seq 模型上添加另一种复杂的机制(attention );我们可以通过忘记其他一切,只专注于注意力来简化解决方案。
这个模型消除了递归,它只使用矩阵乘法。它一次处理所有输入,而不必按顺序处理。为了避免失去顺序,它使用位置嵌入来提供每个元素在序列中的位置信息。尽管消除了递归,但它仍然提供了一种编码器-解码器架构,如 Seq2Seq 模型中所见
因此,在看到我们在以前的模型中面临的所有挑战之后,让我们深入研究 transformer 模型与 Seq2Seq 模型相比解决了什么问题。
变压器技术深度探讨

变压器架构(图片由作者提供)
当我们需要处理整个段落以获得上下文时,RNN 有所欠缺,而变形金刚能够识别长期依赖关系,实现上下文感知。我们还看到,RNN 自己努力确定文本的哪些部分给出了更多的信息,为此他们需要增加一个额外的层,一个双向 RNN 来实现注意力机制。相反,转换器只有在全神贯注的情况下才能工作,这样它才能在不同的层次上确定上下文的基本部分
另一个关键的区别是 transformer 模型消除了递归。通过消除递归,减少了顺序运算的次数,降低了计算复杂度。在 RNNs 中,对于每个反向传播步骤,网络需要看到整个单词序列。在变换器中,所有输入被同时处理,降低了计算复杂度。这也带来了一个新的优势,我们现在可以并行训练。能够将训练样本分割成几个独立处理的任务可以提高训练效率。
那么模型如何在不使用递归的情况下保持序列顺序呢?
使用位置嵌入。该模型采用 n 个单词嵌入的序列。为了模拟位置信息,将位置嵌入添加到每个单词嵌入中。

位置嵌入(作者图片)
使用不同维数的正弦和余弦函数创建位置嵌入。用由这些函数的组合产生的模式对单词进行编码;这导致序列中位置的连续二进制编码。
transformer 模型使用多头注意力对输入嵌入进行编码,这样做时,它会以向前和向后的方式处理输入,因此序列中的顺序会丢失。因此,它依赖于我们刚刚解释过的位置嵌入。
转换器有三种不同的注意机制:编码器注意、编码器-解码器注意和解码器注意。那么注意力机制是如何工作的呢?它基本上是一个向量乘法,根据向量的角度,可以确定每个值的重要性。如果向量的角度接近 90 度,那么点积将接近零,但是如果向量指向相同的方向,那么点积将返回更大的值。
每个键都有一个相关的值,对于每个新的输入向量,我们可以确定这个向量与值向量的关系,并使用 softmax 函数选择最接近的项。

多头关注(图片由作者提供)
变形金刚有多头注意力;我们可以把它想象成 CNN 的过滤器,每一个都学会注意一组特定的单词。一个人可以学会识别短期依赖性,而另一个人可以学会识别长期依赖性。这提高了上下文意识,当不清楚时,我们可以理解术语指的是什么;比如用代词之类的词。
transformer 架构有助于创建基于海量数据集的强大模型。即使不是每个人都可以训练这些模型。我们现在可以利用迁移学习来使用这些预先训练的语言模式,并根据我们的具体任务对它们进行微调。
变形金刚模型彻底改变了这个领域。他们已经在许多任务中超越了基于 RNN 的架构,并将继续在 NLP 领域产生巨大的影响。
变压器神经网络:一步一步的野兽崩溃

变压器神经网络是一种新颖的架构,旨在解决序列到序列的任务,同时轻松处理长距离依赖性。在论文2017【1】中提出“注意力是你所需要的一切”。这是自然语言处理领域目前最先进的技术。
在直接跳到 Transformer 之前,我会花一些时间来解释我们使用它的原因以及它是从哪里出现的。(如果您想跳过这一部分,直接进入 Transformer 主题,但我建议您按顺序阅读,以便更好地理解)。
所以,故事从 RNN(递归神经网络)开始。
RNN
什么是 RNN?和简单的 ANN 有什么区别?主要区别是什么?
rnn 是随着时间推移推出的前馈神经网络。

来源:科拉的 (CC0)。
与普通的神经网络不同,RNNs 被设计成接受一系列输入**而没有预先确定的大小限制。“系列”作为该序列的任何输入,与它们相邻的输入有某种关系或对它们有某种影响。**

RNN 建筑。来源:科拉赫的 (CC0)。
基本的前馈网络也能“记住”东西,但它们记住的是在训练期间学到的东西。此外,虽然 rnn 在训练时学习类似,但是它们在生成输出时记住从先前输入中学到的东西。

图示长期依赖关系的图像。来源:科拉赫的 (CC0)。
它被用于不同类型的模型中-
1.)向量-序列模型- 它们以固定大小的向量作为任意长度的输入输出向量,例如在图像字幕中,图像作为输入给出,输出描述图像。
2.)序列-向量模型- 取任意大小的向量,输出固定大小的向量。电影的情感分析将对任何电影的评论评定为正面或负面,作为固定大小的向量。
3.)序列对序列模型——最流行、使用最多的变体,把输入作为一个序列,把输出作为另一个序列,变量大小。例如,语言翻译,用于股票市场预测的时间序列数据。
其缺点-
- 训练缓慢。
- 长序列导致梯度消失,或者说,长期依赖性的问题。简单来说,在回忆旧联系的时候,它的记忆力并没有那么强。
对于 Eg 。“云在 ____ 里。”
很明显,下一个词将是天空,因为它与云联系在一起。这里我们可以看到云和预测词之间的距离更小,所以 RNN 可以很容易地预测它。
但是,再举一个例子,
“我和我的父母在德国长大,我在那里生活了很多年,对他们的文化有一定的了解,这就是为什么我能说一口流利的 ____ 语。”
这里预测的单词是德语,它与德国直接相连,但在这种情况下,德国与预测的单词之间的距离更大,因此 RNN 很难预测。
因此,不幸的是,随着这种差距的扩大,rnn 变得无法连接,因为它们的记忆随着距离而消失。
LSTM

来源:科拉赫的 (CC0)。
长短期记忆- 特殊种类的 RNN,专为解决消失梯度问题而制作。他们有能力学习长期的依赖性。长时间记住信息实际上是他们的默认行为,而不是他们努力学习的东西!

这个分支允许传递信息并跳过对单元的长时间处理。来源:科拉赫的 (CC0)。
LSTM 神经元与正常神经元不同,它有一个分支,允许传递信息并跳过当前细胞的长时间处理,这允许记忆保留更长的时间。它确实改善了消失梯度问题的情况,但并不令人惊讶,就像它在 100 个单词之前会很好,但在 1000 个单词之后,它开始失去控制。
但是像简单的 RNN 一样,它的训练速度也很慢,甚至更慢。
它们一个接一个地按顺序接受输入,这不能很好地用尽 GPU,GPU 是为并行计算而设计的。
如何并行化顺序数据?? (这个问题我会回去的。)
目前,我们正在处理两个问题-
- 消失梯度
- 慢速训练
解决渐变消失问题:
注意
它回答了我们应该关注输入的哪一部分的问题。
我将用一种稍微不同的方式来解释注意力。让我们来看一个例子-
假设有人给了我们一本机器学习的书,让我们给出分类交叉熵的信息。有两种做法,一是通读全书,带着答案回来。第二,转到索引,找到“损失”一章,转到交叉熵部分,阅读分类交叉熵部分。
你认为更快的方法是什么?
像第一种方法一样,可能要花整整一周的时间来读完整本书。而在第二档,几乎不需要 5 分钟。此外,我们从第一种方法得到的信息将更加模糊和多样,因为它基于太多的信息,而从第二种方法得到的信息将精确到需求。
我们在这里做了哪些不同的事情?
在前一种情况下,我们没有特别关注这本书的任何部分,而在后一种情况下,我们将注意力集中在损失一章,然后进一步将注意力集中在交叉熵部分,在那里解释了分类交叉熵的概念。其实这是我们人类大多数人都会做的事情。
神经网络中的注意力有点类似于我们在人类中发现的。他们关注输入的某些部分的高分辨率,而输入的其余部分是低分辨率的[2]。
假设我们正在制造一台 NMT(神经机器翻译机),
请看这个动画,它展示了一个简单的序列到序列模型是如何工作的。

经典序列到序列模型的工作原理。来源:贾勒马的 (CC BY-NC-SA 4.0)。
我们看到,对于编码器或解码器的每一步,RNN 都在处理其输入,并生成该时间步的输出。在每一个时间步中,RNN 根据它看到的输入和先前的输出更新它的隐藏状态。在动画中,我们看到隐藏状态实际上是我们传递给解码器的上下文向量。
**“注意”的时间。
对于这些类型的模型,上下文向量被证明是有问题的。模型在处理长句时有一个问题。或者说他们正面临长句中消失梯度的问题。因此,在一篇论文[2]中出现了一个解决方案,注意力被引入。它极大地提高了机器翻译的质量,因为它允许模型根据需要专注于输入序列的相关部分。

注意使用序列对序列模型。来源:贾勒马的 (CC BY-NC-SA 4.0)。
这个注意力模型在两个方面不同于经典的序列对序列模型
- 与简单的 seq-to-seq 模型相比,编码器向解码器传递更多的数据。以前,只有编码部分的最后的隐藏状态被发送到解码器,但是现在编码器将所有的隐藏状态(甚至是中间状态)传递到解码器。
- 解码器部分在产生其输出之前进行额外的步骤。解释如下-
解码器最后一步如下进行
- 它检查它接收到的每个隐藏状态,因为编码器的每个隐藏状态大多与输入句子的特定单词相关联。
- 我给每个隐藏的州打分。
- 然后每个分数乘以各自的 softmax 分数,从而放大高分的隐藏状态,淹没低分的隐藏状态。(请参考下图以获得清晰的视觉效果。)**

来源:贾勒马的 (CC BY-NC-SA 4.0)。
这种评分练习在解码器端的每个时间步进行。
现在当我们把所有的东西放在一起:
- 注意力解码器层采用
令牌的嵌入和初始解码器隐藏状态,RNN 处理其输入并产生输出和新的隐藏状态向量(h4)。 - 现在我们使用编码器隐藏状态和 h4 向量来计算这个时间步长的上下文向量 C4。这就是注意力概念被应用的地方,这就是为什么它被称为注意力步骤。
- 我们在一个向量中连接(h4)和 C4。
- 现在,这个向量被传递到一个前馈神经网络,前馈神经网络的输出指示这个时间步长的输出字。
- 这些步骤将在下一个时间步骤中重复。(请参考下面的幻灯片以获得清晰的视觉效果。)**

最后一步。来源:贾勒马的 (CC BY-NC-SA 4.0)。
所以,这就是注意力的工作方式。
例如,在图像字幕问题中的注意力工作:-

图像字幕问题中的注意加工。资料来源:百货公司 (CC0)。
现在,记住我之前提出的问题-
如何将 顺序数据并行化??
所以,我们的弹药来了——
变形金刚
2017 年发表的一篇名为“注意力是你所需要的一切”的论文进入了画面,它介绍了一种基于注意力层的编码器-解码器架构,称为变压器。
一个主要的区别是输入序列可以并行传递,这样可以有效地利用 GPU,也可以提高训练的速度。并且它基于多头注意层,消失梯度问题也被大幅度克服。本文是基于 transformer 在 NMT 上的应用。
所以,我们之前强调的两个问题在这里都得到了一定程度的解决。
例如,在由简单的 RNN 组成的翻译器中,我们以连续的方式输入序列或句子,一次一个单词,以生成单词嵌入。由于每一个单词都依赖于前一个单词,其隐藏状态也随之动作,所以需要一步一个脚印。而在 transformer 中则不是这样的,我们可以同时传递一个句子的所有单词,同时确定单词嵌入。所以,它实际上是如何工作的,让我们看看未来-
架构:-

来源:arXiv:1706.03762【cs。CL】。
1。编码器模块-

来源:arXiv:1706.03762【cs。CL】。
这是一个事实,计算机不理解单词,它对数字,向量或矩阵起作用。所以,我们需要把我们的单词转换成一个向量。但是这怎么可能。于是,嵌入空间的概念就来了。它就像一个开放的空间或字典,意思相似的单词被组合在一起,或者在该空间中彼此靠近。这个空间被称为嵌入空间,在这里,每个单词,根据其含义,被映射并被赋予特定的值。所以,这里我们把单词转换成向量。

来源:arXiv:1706.03762【cs。CL】。
但我们将面临的另一个问题是,不同句子中的每个单词都有不同的含义。因此,为了解决这个问题,我们借助了位置编码器。它是一个向量,根据单词在句子中的位置给出上下文。
词→嵌入→位置嵌入→最终向量,称为上下文。
我们的输入已经准备好,现在进入编码器模块。
多头注意力部分-

来源:arXiv:1706.03762【cs。CL】。
现在,变形金刚的主要本质来了,“自我关注”。
它关注的是一个特定的单词与句子中其他单词的相关程度。它被表示为一个注意力向量。对于每个单词,我们可以生成一个注意力向量,该向量捕获该句子中单词之间的上下文关系。

来源:百货公司 (CC0)。
它所面临的唯一问题是,对于每一个单词,它在句子中的自身价值要高得多,尽管我们倾向于它与句子中其他单词的相互作用。因此,我们确定每个单词的多个注意力向量,并进行加权平均,以计算每个单词的最终注意力向量。

资料来源:百货公司 (CC0)。
由于我们使用了多个注意力向量,它被称为多头注意力块。
前馈网络-

来源:arXiv:1706.03762【cs。CL】。
现在,第二步是前馈神经网络。这是应用于每个注意力向量的简单前馈神经网络,其主要目的是将注意力向量转换为下一个编码器或解码器层可接受的形式。

来源:arXiv:1706.03762【cs。CL】。
前馈网络“一次一个”地接受注意力向量。这里最棒的是不像 RNN 的情况,这里每个注意力向量都是彼此独立的。因此,并行化可以在这里应用,这就造成了所有的差异。

编码器的输出。来源:arXiv:1706.03762【cs。CL】。
现在,我们可以将所有单词同时传递到编码器模块,并同时获得每个单词的编码向量集。
2。解码器模块-

来源:arXiv:1706.03762【cs。CL】。
现在,就像我们正在训练一个从英语到法语的翻译,所以为了训练,我们需要给出一个英语句子和它翻译的法语句子,让模型学习。所以,我们的英语句子通过编码器模块,法语句子通过解码器模块。

来源:arXiv:1706.03762【cs。CL】。
首先,我们有嵌入层和位置编码器部分,它将单词转换为各自的向量,这与我们在编码器部分看到的类似。
蒙面多头注意力部分-

来源:arXiv:1706.03762【cs。CL】。
现在它将通过自我注意块,在这里为法语句子中的每个单词生成注意向量,以表示每个单词与同一个句子中的每个单词的相关程度。(就像我们在编码器部分看到的一样)。
但这个区块被称为掩蔽多头注意力区块,我将简单解释一下——
为此,我们需要知道学习机制是如何工作的。首先,我们给出一个英语单词,它将使用之前的结果翻译成法语版本,然后它将与实际的法语翻译(我们输入到解码器模块中)进行匹配和比较。在比较两者之后,它将更新它的矩阵值。这就是它在几次迭代后的学习方式。
我们观察到的是,我们需要隐藏下一个法语单词,这样一开始它会使用以前的结果来预测下一个单词本身,而不知道真正的翻译单词。如果它已经知道下一个法语单词,学习就没有意义了。因此,我们需要隐藏(掩盖)它。

这是一个英法翻译的例子。资料来源:百货公司 (CC0)。
我们可以从英语句子中取任意一个单词,但只能取法语句子的前一个单词来学习。因此,在执行矩阵运算的并行化时,我们确保矩阵应该屏蔽稍后出现的单词,将它们转换为 0,这样注意力网络就不能使用它们。

来源:arXiv:1706.03762【cs。CL】。
现在,来自前一层的结果注意力向量和来自编码器块的向量被传递到另一个多头注意力块。(这部分也是编码器模块的结果进入图像的地方。在该图中还可以清楚地看到,来自编码器模块的结果将传到这里。)。这就是为什么它被称为编解码器关注块的原因。
因为对于每个英语和法语句子,每个单词都有一个向量。这个模块实际上完成了英语和法语单词的映射,并找出了它们之间的关系。所以,这是主要的英语到法语单词映射发生的部分。
这个模块的输出是英语和法语句子中每个单词的注意力向量。每一个向量代表两种语言中与其他词的关系。

来源:arXiv:1706.03762【cs。CL】。
现在,我们将每个注意力向量传递到一个前馈单元,它将使输出向量形成易于被另一个解码器模块或线性层接受的形式。
线性层是另一个前馈层。它用于在翻译后将维度扩展为法语中的单词数。
现在,它通过 Softmax 层,将输入转换为人类可以理解的概率分布。
并且在翻译之后以最高的概率产生结果单词。
下面是谷歌的人工智能博客【6】中举例说明的例子,我把它放在这里供你参考。

概述-变压器网络的工作。来源:谷歌 AI (CC0)。
转换器从为每个单词生成初始表示或嵌入开始。这些由空心圆圈表示。然后,使用自我关注,它从所有其他单词中聚合信息,根据整个上下文信息为每个单词生成一个新的表示,用实心球表示。然后对所有单词并行重复该步骤多次,连续生成新的表示。
解码器的操作类似,但从左到右一次生成一个字。它不仅关注其他先前生成的单词,还关注由编码器生成的最终表示。
这就是变压器的工作原理,它现在是 NLP 中最先进的技术。它使用了一种 自关注机制 并解决了并行化问题,产生了非常好的结果。甚至谷歌也使用 BERT 来为普通的自然语言处理应用程序预先训练模型。
参考文献-
- 注意力是你所需要的一切;Ashish Vaswani,Noam Shazeer,Niki Parmar,Jakob Uszkoreit,Llion Jones,Aidan N. Gomez,Lukasz Kaiser,Illia Polosukhin。
- 基于注意力的神经机器翻译的有效方法;放大图片作者:Christopher D .
- 科拉的博客。
- 杰伊·阿拉玛的博客。
- 百货商店:Youtube。
Transformer-XL 回顾:超越定长上下文
“Transformer-XL:超越定长语境的注意力语言模型”述评
这篇论文(“Transformer-XL:固定长度上下文之外的注意力语言模型”)发表在顶级 NLP 会议之一的 ACL 2019 上,作者是谷歌 AI 的研究人员。它提出了 Transformer-XL,这是一种新的架构,能够在不破坏时间一致性的情况下,超越固定长度的上下文理解自然语言。它的主要创新是段级递归机制和新颖的位置编码方案。与传统的 Transformer 模型不同,它可以捕获长期的依赖关系,并解决上下文碎片问题,这是 vanilla Transformer 的主要局限性。实验表明,Transformer-XL 学习依赖的时间比 RNNs 和 vanilla Transformer 长得多。Transformer-XL 还在大型基准数据集的评估中取得了一流的结果。
论文链接:https://www.aclweb.org/anthology/P19-1285.pdf
1.背景
语言建模是自然语言处理中的一个重要课题。人们提出了许多像 BERT 和 ELMo 这样的无监督预训练方法。然而,建模长期依赖仍然是一个挑战。递归神经网络(RNNs),尤其是长短期记忆网络(LSTM)已经成为建模长期依赖的标准解决方案。在 LSTMs 中引入门控和梯度削波技术提高了对长期依赖性建模的能力,但不足以解决这一挑战。此外,由于梯度消失和爆炸,很难优化用于建模长期依赖性的 RNNs。

图一。线段长度为 4 的普通变形金刚。来源:【变压器-XL】【戴等,2019】
转换器被提出来解决这个问题,它允许单词对之间的直接连接,并且比 LSTMs 更好地捕捉长期依赖性。作者定义了原始变形金刚和香草变形金刚。然而,转换器是用固定长度的上下文实现的。它将输入分割成段,并在每个段内进行训练(图 1)。因此,转换器无法捕获超过预定义上下文长度的长期依赖关系。并且固定长度的片段不考虑句子边界,导致上下文碎片,从而导致低效的优化和性能损失。在评估过程中,它通过在每一步中将输入移动一个位置来一次在一个位置进行一次预测,其中数据段从头开始处理。所以评估程序是昂贵的。
为了解决这些限制,作者提出了 Transformer-XL。它重用先前片段中的隐藏状态来支持长期依赖并解决上下文碎片。并且它采用相对位置编码方案来避免时间混淆。
2.变压器-XL

图二。线段长度为 4 的 Transformer-XL 型号。来源: Transformer-XL 【戴等,2019】
2.1 分段级重现
在训练期间,为前一段计算的隐藏状态序列是固定的,并被缓存以作为扩展上下文重用(图 2)。在每个段中,每个隐藏层接收前一个隐藏层的输出和前一个段的输出。它通过使用来自几个先前片段的上下文信息来增加最大可能的依赖性。尽管解决了上下文碎片问题,但是这种片段级递归机制提高了评估速度,因为它可以前进整个长片段,并且使用来自先前片段的表示而无需重新计算。
2.2 相对位置编码
天真地应用递归引入了另一个技术挑战。也就是说,位置信息是不一致的,并且来自不同片段的标记具有相同的位置编码,这被称为时间混淆。为了应对这一挑战,Transformer-XL 采用了新的相对位置编码。位置信息偏差被编码在隐藏状态中,这不同于在初始嵌入中结合偏差的其他方法。使用带有可学习转换的固定嵌入使得它更直观,并且更可推广到更长的序列。相对位置编码使得段级递归成为可能,因此 Transformer-XL 可以比普通的 Transformer 模型建立更长期的依赖关系。
3.实验和结果
作者将 Transformer-XL 应用于单词级和字符级数据集,包括 WikiText-103、text8、enwik8、十亿单词和 Penn Treebank,并将其与其他模型进行比较。

表 1:WikiText-103 的结果。来源:【变压器-XL】【戴等,2019】
在 WikiText-103 数据集上,Transformer-XL 达到了 18.3 的困惑度,相比之下,之前最先进的(SoTA)结果(Baevski & Auli)达到了 20.5 的困惑度(表 1)。

表 2:环境观察 8 的结果。来源:【变压器-XL】T5【戴等,2019】
在 enwik8 数据集上,12 层 Transformer-XL 实现了 1.06 比特每字符(bpc),这与 Al-Rfou 等人之前的 SoTA 结果相似..24 层 Transformer-XL 将 SoTA bpc 从 1.06 提高到 0.99(表 2)。

表 3:文本 8 的结果。来源: Transformer-XL 【戴等,2019】
在 enwik8 上使用相同的超参数时,Transformer-XL 将 SoTA bpc 从 1.13 降至 1.08(表 3)。

表 4:十亿字的结果。来源:【变压器-XL】【戴等,2019】
十亿字数据集只有短期依赖性,但 Transformer-XL 也实现了新的 SoTA 结果,将 SoTA 困惑度从 23.7 降低到 21.8(表 4)。

表 5:宾夕法尼亚树库的结果。来源:【变压器-XL】T5【戴等,2019】
在只有 100 万训练令牌的单词级 Penn Treebank 数据集上,与没有两步微调的其他模型相比,Transformer-XL 将 SoTA 困惑度从 55.3 提高到 54.52(表 5)。这表明 Transformer-XL 可以很好地在小数据集上推广。

表 6:相对有效上下文长度比较。来源: Transformer-XL 【戴等,2019】
作者提出了一个新的度量,相对有效上下文长度(RECL),它定义在一个模型组上,长上下文的增益通过相对于最佳短上下文模型的相对改进来衡量。RECL 中的参数 r 限制了 top- r 硬示例上的比较。如表 6 所示,Transformer-XL 可以模拟比 RNN 长 80%到 133%的依赖性,比普通 Transformer 长 291%到 447%的依赖性。它表明段级递归和相对位置编码都有助于 Transformer-XL 的较长 RECL。在 WikiText-103 和十亿字数据集上的消融研究也表明,Transformer-XL 优于其他模型,因为它可以对递归和新编码的长期依赖性进行建模。
此外,由于不需要重新计算,Transformer-XL 在评估过程中比普通变压器快 1874 倍。
4.结论
Transformer-XL 在多个数据集上获得新的 SoTA 困惑或 bpc 结果。结合递归和相对位置编码,它可以模拟比 RNNs 和普通变压器更长期的依赖性,并在评估过程中大大降低计算成本。Transformer-XL 在其他领域也很有效,比如生成长文章和改进语言模型预处理方法,比如 BERT 和 ALBERT。
5.相关著作
你所需要的只是注意力
本文提出了 Transformer,这是一种完全依赖于注意力机制来模拟输入和输出之间的全局依赖关系的新型模型架构。Transformer 模型允许更多的并行化,因此需要更少的培训时间。Transformer 在 WMT 2014 年英语到法语翻译任务上取得了新的 SoTA 结果。原始变压器是本文介绍的 Transformer-XL 的基础。
引文:Ashish Vaswani、Noam Shazeer、Niki Parmar、Jakob Uszkoreit、Llion Jones、Aidan Gomez、ukasz Kaiser 和 Illia Polosukhin。2017.你需要的只是关注。在神经信息处理系统进展中,第 5998–6008 页。
(2)具有更深自我关注的字符级语言建模
本文提出了一种用于角色级建模的深度、非递归的 transformer 模型。具有因果注意的变压器自我注意层用于处理固定长度的输入和预测即将出现的字符。Al-Rfou 等人设计了三个辅助损耗来训练深度变压器网络,其性能优于 LSTMs,并在 text8 和 enwik8 数据集上获得了新的 SoTA 结果。但是,它使用固定长度的数据段,因此它无法捕获任何超过预定义上下文长度的长期依赖关系。这一限制促使作者设计 trans former-XL 来模拟长期依赖性。
引文:Rami Al-Rfou,Dokook Choe,Noah Constant,Mandy Guo 和 Llion Jones。2018.具有更深自我关注的字符级语言建模。在AAAI 人工智能会议记录,第 3159-3166 页。
(3) Bert:用于语言理解的深度双向转换器的预训练
介绍了一种新的语言表示模型——来自变压器的双向编码器表示(BERT)。它被设计成用未标记的文本预先训练双向语言表示。然后,预训练的 BERT 可以通过一个额外的输出层微调到各种任务,并实现 SoTA 结果。实际上,BERT 只是将长文本分成固定长度的较短片段,导致了上下文碎片问题。Transformer-XL 解决了上下文碎片问题,因此它可以用于改进 BERT,然后在不同类型的任务中获得新的 SoTA 结果。
引文:雅各布·德夫林、张明蔚、肯顿·李和克里斯蒂娜·图塔诺瓦。2019.Bert:用于语言理解的深度双向转换器的预训练。在计算语言学协会北美分会 2019 年会议论文集:人类语言技术,第 4171–4186 页。
(4)具有相对位置表征的自我注意
本文提出了一种将序列元素之间的相对位置表示或距离结合到变压器的自注意机制中的方法。与绝对位置表示相比,相对位置编码可以提高 WMT 2014 英德数据集上的翻译质量。它启发了 Transformer-XL 的作者们去衍生一种新形式的相对位置编码。Transformer-XL 的新的相对位置编码解决了时间混淆问题,并且在经验上具有更好的通用性。
引文:彼得·肖、雅各布·乌兹科雷特和阿希什·瓦斯瓦尼。2018.自我注意与相对位置表征。在计算语言学协会北美分会 2018 年会议记录:人类语言技术中,第 464-468 页。
(5) ALBERT:一个用于语言表达自我监督学习的 Lite BERT
本文提出了两种新的技术来减少 BERT 中的参数,有助于降低存储和训练时间的成本。它还引入了句子顺序预测的自我监督损失,重点是建立句子间的一致性模型。它用比 BERT-large 更少的参数在不同的基准数据集上建立新的 SoTA 结果。与 BERT 一样,它也将长文本分割成固定长度的片段,从而导致潜在的上下文碎片问题。Transformer-XL 可用于解决 ALBERT 中的上下文碎片问题,从而进一步提高其性能。
引文:兰,陈明达,萨巴斯蒂安古德曼,凯文金佩尔,皮尤什夏尔马,拉杜索里科特。2019.ALBERT:一个用于语言表达自我监督学习的 Lite BERT。在国际学术交流会议。
变形金刚(电影名)
或者我喜欢称之为类固醇引起的注意。💉💊

阿瑟尼·托古列夫在 Unsplash 上的照片
不,这篇文章不是关于美国科幻动作电影系列——这里没有擎天柱。它也与用于将能量从一个电路转移到另一个电路的电气设备无关。你会问,这是怎么回事?
这是有史以来最科幻的领域之一,人工智能——特别是自然语言处理,它在传递信息方面非常出色,并得到了很好的利用。(看我在那里做了什么。😛)
这篇文章基于一篇论文:注意力是你所需要的全部。附:作者选择这个标题并不是开玩笑,因为你需要所有的注意力来处理这个问题。但是不要让这吓到你,这是非常值得的!!
什么是变压器?
NLP 中的 Transformer 是一个新颖的架构,旨在解决序列到序列的任务,同时轻松处理长距离依赖。它完全依靠自我关注来计算其输入和输出的表示,而不使用序列对齐的 RNNs 或卷积。🤯
如果你还记得我以前的帖子,理解深度学习中的注意力,我们讨论了许多模型在处理长期依赖时如何以及为什么会失败。注意力的概念在某种程度上让我们克服了这个问题,现在在《变形金刚》中,我们将建立在注意力之上,并释放其全部潜力。
一点点关注怎么改变了 AI 游戏!
towardsdatascience.com](/attaining-attention-in-deep-learning-a712f93bdb1e)
潜入变形金刚之前需要知道的几件事
自我关注
让我们从重新审视 NLP 宇宙中的注意力开始。理解深度学习中的注意力。(我为这些明目张胆的自我广告道歉,但是认真读一读。在变形金刚下会对你帮助大很多。我保证。)
注意力让我们在预测我们的输出序列 时,专注于我们输入序列的部分。如果我们的模型预测单词“胭脂”(红色的法语翻译),我们很可能在我们的输入序列中找到单词“红色”的高权重年龄。所以,在某种程度上,注意力让我们在输入单词“ rouge ”和输出单词“ red ”之间建立了某种联系/关联。
自我注意,有时被称为内部注意,是一种与单个序列的不同位置相关的注意机制,以便计算该序列的表示。
简单来说, 自我关注帮助我们在同一个句子中建立相似的联系。 看下面这个例子:
“I poured water from the *bottle* into the ***cup*** until **it** was ***full***.”
it => cup“I poured water from the **bottle** into the cup until **it** was ***empty***.”
it=> bottle
通过改变一个词“满了”——>——空了”它的参照对象为变了。如果我们在翻译这样一个句子,我们就会想知道“这个词它指的是什么。
模型中可能存在的三种注意力:
- 编解码器注意 : 注意输入序列和输出序列之间的关系。
- 输入序列中的自我关注 : 关注输入序列中的所有单词。
- 输出序列中的自我注意: 这里我们要警惕的一点是,自我注意的范围仅限于给定单词之前出现的单词。这防止了模型训练期间的任何信息泄漏。这是通过屏蔽在每一步之后出现的单词来实现的。因此,对于步骤 1,只有输出序列的第一个字不被屏蔽,对于步骤 2,前两个字不被屏蔽,依此类推。
键、值和查询:
我刚才在这个标题中随机给你的三个词是向量,因为抽象对于计算自我注意力是有用的,下面会有更多的细节。这些通过将您的输入向量( X )乘以训练时学习的权重矩阵来计算。
- 查询向量:q=X * Wq。把这个当做现在的词。
- 关键向量😗*k=X * Wk。把这当成价值向量的索引机制。类似于哈希映射中的键值对,其中键用于唯一地索引值。
- 值向量: v = X * Wv。把这个当做输入单词中的信息。
我们想要做的是获取查询 q 并通过对 q 和 k 做点积来找到最相似的键 k 。最接近的查询关键字乘积将具有最高值,随后是 softmax,它将驱动具有接近 0 的较小值的 q.k 和具有较大值的 q.k 向 1 移动。这个 softmax 分布乘以v。乘以~1 的值向量将得到更多关注,而乘以~0 的值向量将得到较少关注。这些 q、k 和 v 向量的大小被各种实现称为“”。

这些值代表 q、k 和 I 的指数。
所有这些矩阵 Wq、Wk 和 Wv 都是在模型训练期间联合训练时学习到的。
从 q、k 和 v 计算自我注意力:

自我关注的公式。资料来源:论文。
如果我们为输入单词计算自我注意,
- 第一步: 将 qᵢ乘以 word 的 kⱼ关键向量。
- 第二步: 然后用这个乘积除以关键向量的维数的平方根。
完成此步骤是为了获得更好的梯度流量,这在前面步骤中点积值过大的情况下尤为重要。因为直接使用它们可能会将 softmax 推入梯度流量非常小的区域。 - 第三步: 一旦我们有了所有 j 的分数,我们就把这些通过一个 softmax。我们得到每个 j 的归一化值。
- 第四步: 用 v ᵢ 向量乘以每个 j 的 softmax 分数。
这里的想法/目的是,非常类似的注意,通过将它们乘以来自 softmax ~1 的高概率分数,仅保留我们想要关注的输入单词的值 v ,并且通过将它们驱向 0 来移除其余的值,即通过将它们乘以来自 softmax 的低概率分数~0 来使它们非常小。

计算第 I 个输入单词的自我注意输出。如果你正在寻找自我注意和注意之间的类比,考虑 z 服务于上下文向量的目的,而不是全局对齐权重。
变形金刚
⚠️ 我们将把这个可怕的野兽分解成小野兽,这一切都将变得有意义。(我承诺#2)

(左)变压器架构。资料来源:论文。(右图)为了更好地理解,对其进行了抽象。
Beast #1:编码器-解码器堆栈
编码器 :编码器将输入的符号表示序列 (x ₁ ,…,x ₙ ) 映射到表示序列 z = (z ₁ ,…,z ₙ ) 。把它们想象成经过某种后处理的自我关注的输出。
每个编码器有两个子层。
- 输入向量上的多头自我关注机制(想想并行化的和高效的自我关注的兄弟)。
- 一个简单的位置式全连接前馈网络(想想后处理)。
查看这张伯特中使用的编码器模块的绝对炸弹 3D 图。说真的,你不能错过这个!!!这就像一个全新层次的理解。
解码器 :给定 z ,解码器然后一次生成一个符号输出序列 (y ₁ ,…,y ₘ ) 。
每个解码器有三个子层。
- 一种 屏蔽 多头自关注机制对上一次迭代的输出向量。
- 编码器输出上的多头注意机制和解码器中的屏蔽多头注意。
- 一个简单的,位置式的全连接前馈网络(想想后处理)。
另外几点:
- 在原始论文中,编码器堆栈中有 6 层(2 个子层版本),解码器堆栈中有 6 层(3 个子层版本)。
- 模型中的所有子层以及嵌入层产生相同维度的输出。这样做是为了方便剩余的连接。
编码器-解码器堆栈中的 beast # 2—多头注意:

编码器和解码器中的三种注意以及前馈神经网络。
我们刚刚注意到,每个子层的输出需要具有相同的维度,在本文中为 512。zᵢ需要有 512 个维度。vᵢ需要有 512 个维度,因为 zᵢ只是 vᵢs.的加权和
此外,我们希望允许模型关注不同的位置,这是通过使用不同的 q、k 和 v 向量集多次计算自我关注度,然后取所有这些输出的平均值,以获得我们的最终 z 。
因此,我们不是处理这些庞大的向量并平均多个输出,而是将我们的 k、q 和 v 向量的大小减少到某个更小的维度——同时减少 Wq、Wk 和 Wv 矩阵的大小。我们将 k、q、 v、的多个集合( h )统称为一个注意力头,因此得名多头注意力。最后,我们将它们连接起来,而不是求平均值来得到最终的 z 。
级联向量的大小将会太大而不能馈送到下一个子层,所以我们通过将其乘以另一个学习矩阵 Wo 来缩小它。

(左)缩放的点积注意力。(右)多头注意力由几个并行运行的注意力层组成。资料来源:论文。
多个注意头允许模型在不同位置共同注意来自不同表示子空间的信息,这被平均在单个注意头中所抑制。
Beast #3—输入和输出预处理:
使用某种形式的嵌入来表示输入单词。编码器和解码器都是这样做的。
单词嵌入本身缺乏任何位置信息,而这在 rnn 中是通过它们的顺序性质实现的。同时,在自我关注中,由于 softmax,任何这样的位置信息都丢失了。
为了保留位置信息,转换器将向量注入到各个输入嵌入中(可以使用单词嵌入来对应于输入单词)。这些向量遵循模型学习的特定周期函数(例如:具有不同频率的各种正弦/余弦的组合,简而言之,彼此不同步),并且能够基于值确定单个单词彼此的位置。
这个注入的向量被称为“位置编码,并被添加到编码器和解码器堆栈底部的输入嵌入中。
Beast #4 —解码器堆栈:重访
解码器堆栈在每一步的输出都反馈到下一个时间步的解码器,这与 RNNs 中前一步的输出被用作下一个隐藏状态非常相似。正如我们对编码器输入所做的那样,我们将位置编码嵌入并添加到这些解码器输入中,以保留每个单词的位置。这种位置编码+单词嵌入组合然后被馈入一个被掩蔽的多头自我注意。
解码器堆栈中的这个自我关注子层被修改,以防止位置关注后续位置——你不能看未来的单词。该屏蔽确保位置 i 的预测仅依赖于小于 i 位置的已知输出。
编码器堆栈的输出随后被用作多组关键向量【k】和值向量 v ,用于“编码器解码器注意”——在图中以绿色显示——层。这有助于解码器关注该步骤的输入序列中上下文相关的部分。(类似于全局注意力向量的部分。)的 q 向量来自于“输出自我注意力”层。
一旦我们得到解码器的输出,我们再次进行 softmax 来选择单词的最终概率。
结论
让我们以一个快速的总结性复习来结束。
- 我们从理解什么是自我关注以及如何从这些 v,k,q 向量计算自我关注开始。
- 多头注意力是对自我注意力的有效修改,它使用多个更小的 v,k,q 集合,并连接每个集合的输出,以获得最终的 z.
- 然后我们看到了这三种自我关注是如何在模型中使用的。
- 随后对编码器和解码器堆栈的输入进行预处理。
参考文献+推荐阅读
-
了解深度学习中的深度注意力如果你面临注意力的问题。
-
图示变压器——有很好的视觉效果和解释。
-
YouTube 视频#1:获得模型直觉的绝佳资源。特别是如果你想进一步了解位置编码的话。
-
YouTube 视频# 2:Lukasz Kaiser 关于这篇解释自我关注的论文的讲话。他是这篇论文的作者之一。
我很高兴你坚持到了这篇文章的结尾。🎉我希望你的阅读体验和我写这篇文章时一样丰富。💖**
如果你喜欢这篇文章,请点击这里查看我的其他文章。
如果你想联系我,我会选择推特。
变压器是图形神经网络
这篇文章的目的是在 NLP 的 Transformer 架构背后建立直觉,以及它与图上神经网络的联系。

阿瑟尼·托古列夫在 Unsplash 上的照片
工程师朋友经常问我:图深度学习听起来很棒,但是有没有大的商业成功案例?在实际应用中部署了吗?
除了那些显而易见的——在 Pinterest 、 Alibaba 和 Twitter 的推荐系统——一个稍微微妙的成功故事是Transformer architecture,它已经被 storm 带走了NLP行业 。
通过这篇文章,我想在图形神经网络(GNNs) 和变形金刚之间建立联系。我将谈论 NLP 和 GNN 社区中模型架构背后的直觉,使用等式和数字建立联系,并讨论我们如何一起工作来推动进展。
让我们从讨论模型架构的目的开始——表示学习。
自然语言处理中的表征学习
在高层次上,所有神经网络架构将输入数据的表示构建为向量/嵌入,其编码关于数据的有用的统计和语义信息。这些潜在的或隐藏的表征然后可以被用来执行一些有用的事情,比如对一幅图像进行分类或者翻译一个句子。神经网络学习通过接收反馈来建立越来越好的表示,通常是通过误差/损失函数。
对于自然语言处理(NLP),传统上,递归神经网络 (RNNs)以顺序方式构建句子中每个单词的表示,即、一次一个单词。直观地说,我们可以把 RNN 层想象成一条传送带,上面的文字从左到右进行自回归处理。最后,我们为句子中的每个单词获得一个隐藏的特征,我们将它传递给下一个 RNN 层或用于我们选择的 NLP 任务。

RNNs 以连续的方式构建句子中每个单词的表示,即、一次一个单词。另一方面,变形金刚使用一种注意力机制来计算出在句子中的重要性。
最初为机器翻译引入的变形金刚已经逐渐取代主流 NLP 中的 rnn。该架构采用了一种全新的表示学习方法:完全消除递归,变形金刚使用一种注意力 机制来构建每个单词的特征,以计算出在句子中的所有其他单词对于前述单词的重要性。知道了这一点,单词的更新特征就是所有单词的特征的线性变换的总和,通过它们的重要性来加权。
回到 2017 年,这个想法听起来非常激进,因为 NLP 社区非常习惯于用 RNNs 处理文本的顺序-一次一个单词-风格。论文标题大概是火上浇油吧!
为了重述,扬尼克·基尔彻制作了一部出色的 视频概述 。
分解变压器
让我们通过将前面的段落翻译成数学符号和向量的语言来发展对架构的直觉。我们从层 ℓ 到层 ℓ+1 更新句子 S 中第 i 个单词的隐藏特征 h 如下:

其中 j∈ S 表示句子中的单词集合,而 Q,K,V 是可学习的线性权重(分别表示用于注意力计算的 Q uery, K ey 和 V 值)。对句子中的每个单词并行执行注意机制,以在 one shot 中获得它们的更新特征 RNNs 上的变形金刚的另一个加分项,它逐词更新特征。
我们可以通过以下管道更好地理解注意机制:

吸收单词 h_i^ℓ 的特征和句子“h_j^ℓ”中的其他单词集合;∀ j∈ S,我们通过点积计算每一对(I,j)的注意力权重 w_{ij},随后是所有 j 的 softmax。最后,我们通过对由它们相应的 w_{ij}加权的所有 h_j^ℓ's 求和来产生单词 I 的更新的单词特征 h_i^{ℓ+1}。句子中的每个单词并行地经历相同的流水线来更新其特征。
多头注意力机制
让这种点积注意力机制发挥作用被证明是棘手的——糟糕的随机初始化会破坏学习过程的稳定性。我们可以通过并行执行多个注意力“头部”并连接结果(每个头部现在有单独的可学习权重)来克服这一点:

其中 Qk,Kk、V^k 是第 k 注意头的可学习重量,而 O 是向下投影,以匹配各层的 h_i^{ℓ+1} 和 h_i^ℓ 的尺寸。
多个头部允许注意力机制实质上“对冲赌注”,从上一层查看隐藏特征的不同转换或方面。我们稍后会详细讨论这一点。
比例问题和前馈子层
推动最终变压器架构的一个关键问题是,在之后的单词的特征,注意机制可能处于不同的标度或大小 : (1)这可能是由于当对其他单词的特征求和时,一些单词具有非常尖锐或非常分散的注意权重 w_{ij} 。(2)在单个特征/向量条目级别,跨多个注意力头(每个注意力头可能以不同的比例输出值)进行连接可以导致最终向量 h_i^{ℓ+1} 的条目具有大范围的值。按照传统的 ML 智慧,在管道中添加一个规范化层似乎是合理的。
变形金刚用 图层名 克服了问题(2),该图层名在特征级标准化并学习仿射变换。此外,通过特征尺寸的平方根缩放点积注意力有助于抵消问题(1)。
最后,作者提出了另一种控制尺度问题的“技巧”:一种具有特殊结构的位置式两层 MLP 。在多头关注之后,他们通过一个可学习的权重将 h_i^{ℓ+1} 投射到一个(荒谬的)更高维度,在那里它经历了 ReLU 非线性,然后被投射回其原始维度,接着是另一个归一化:

老实说,我不确定过度参数化的前馈子层背后的确切直觉是什么,似乎也没有人对此提出疑问!我认为图层归一化和缩放点积没有完全解决突出显示的问题,所以大 MLP 是一种重新缩放彼此独立的特征向量的方法。
邮件我 如果你了解更多!
变形层的最终图片如下所示:

Transformer 架构也非常适合非常深的网络,使 NLP 社区能够在模型参数和数据方面扩展*。每个多头注意力子层和前馈子层的输入和输出之间的剩余连接是堆叠变压器层的关键(但为了清楚起见在图中省略)。*
gnn 构建图形的表示
让我们暂时离开 NLP 一步。
图形神经网络(gnn)或图形卷积网络(gcn)在图形数据中构建节点和边的表示。它们通过邻域聚合(或消息传递)来实现,其中每个节点从其邻居收集特征,以更新其周围的局部图结构的表示。堆叠几个 GNN 图层使模型能够在整个图中传播每个节点的特征-从其邻居到邻居的邻居,等等。**

以这个表情符号社交网络为例:GNN 产生的节点特征可用于预测任务,如识别最有影响力的成员或提出潜在的联系。
在其最基本的形式中,gnn 更新节点 i 的隐藏特征 h (例如😆)在层 ℓ 通过节点自身特征的非线性变换 h_i^ℓ 添加到来自每个相邻节点的特征集合h_j^ℓj∈n(I):

其中 U,V 是 GNN 层的可学习权重矩阵,而 σ 是诸如 ReLU 的非线性。在这个例子中, N (😆) = { 😘,😎,😜,🤩 } 。
可以用其他输入大小不变的聚合函数来代替对邻域节点 j∈ N(i)】的求和,例如简单的平均值/最大值或者更强大的函数,例如经由注意机制和的加权求和。
听起来熟悉吗?
也许管道有助于建立联系:

如果我们要进行多个并行的邻居头聚合,并用关注机制即的加权和来代替邻居 j 上的求和,我们将得到图关注网络** (GAT)。加上归一化和前馈 MLP,瞧,我们有一个图形转换器!**
句子是完全连接的单词图
为了使这种联系更加明确,可以把一个句子看作一个完全连通图,其中每个单词都与其他每个单词相连。现在,我们可以使用 GNN 为图(句子)中的每个节点(单词)构建特征,然后我们可以用它来执行 NLP 任务。

大体上,这就是变形金刚正在做的事情:它们是具有多头注意力的 gnn,作为邻居聚合函数。标准 gnn 从它们的局部邻域节点 j∈ N(i) 聚集特征,而 NLP 的 Transformers 将整个句子 S 视为局部邻域,在每一层从每个单词 j∈ S 聚集特征。
重要的是,各种特定问题的技巧——如位置编码、因果/掩蔽聚合、学习率计划和广泛的前期培训——对变形金刚的成功至关重要,但在 GNN 社区中却很少出现。与此同时,从 GNN 的角度来看《变形金刚》可以启发我们摆脱架构中的许多花里胡哨。
我们能从彼此身上学到什么?
现在我们已经在变形金刚和 GNNs 之间建立了联系,让我来给大家一些想法…
全连通图是 NLP 的最佳输入格式吗?
在统计 NLP 和 ML 之前,像诺姆·乔姆斯基这样的语言学家专注于发展语言结构的形式化理论,例如语法树/图。已经尝试过了,但是也许 Transformers/gnn 是更好的架构,让语言理论和统计 NLP 的世界更近一步?

来源:维基百科
如何学习长期依赖?
全连通图的另一个问题是,它们使得学习单词之间的长期依赖关系变得困难。这仅仅是由于图中的边数与节点数即的平方比例关系,在一个 n 单词句子中,转换器/GNN 将对 n^2 单词对进行计算。事情失去了控制。****
NLP 社区对长序列和依赖性问题的观点很有趣:根据输入大小使注意力机制稀疏或自适应,在每一层中添加递归或压缩,以及使用位置敏感散列进行有效的注意力都是更好的转换器的有希望的新思路。
有趣的是,我们可以看到来自 GNN 社区的想法混合在一起,例如,,句子的二元划分,图稀疏化似乎是另一种令人兴奋的方法。

来源:叶等,2019
变形金刚在学习‘神经句法’吗?
已经有几篇来自 NLP 社区的有趣的 论文关于变形金刚可能在学习什么。基本前提是,对一个句子中的所有词对进行关注——目的是识别哪些词对最有趣——使变形金刚能够学习类似于任务特定语法的东西。多头注意力中的不同头也可能“看”不同的句法属性。
在图的术语中,通过在全图上使用 GNNs,我们能从 GNN 如何在每一层执行邻域聚合中恢复最重要的边——以及它们可能包含的内容吗?我还不太相信这个观点。

来源:克拉克等人,2019
为什么要多个头像关注?为什么关注?
我更赞同多头机制的优化观点——拥有多个注意力头可以提高学习能力并克服糟糕的随机初始化。例如,这些 论文表明,在培训后,变压器头可以被“修剪”或移除,而不会对性能产生重大影响。
多头邻域聚合机制在 GNNs 中也被证明是有效的,例如,GAT 使用相同的多头注意力, MoNet 使用多个高斯核来聚合特征。虽然发明多头技巧是为了稳定注意力机制,但它能成为挤出额外模型性能的标准吗?
相反,具有更简单的聚合函数如 sum 或 max 的 gnn 不需要多个聚合头进行稳定的训练。如果我们不必计算句子中每个词对之间的成对兼容性,这对变形金刚不是很好吗?
变形金刚能从转移注意力中获益吗?Yann Dauphin 及其合作者的最近的 工作提出了一种替代的 ConvNet 架构。变形金刚也一样,最终可能会做与康文网类似的事情。

来源:吴等,2019
为什么训练变形金刚这么难?
阅读新的《变形金刚》论文让我觉得,在确定最佳学习率计划、预热策略和衰减设置时,训练这些模型需要类似于黑魔法的东西。这可能仅仅是因为模型是如此庞大,而所研究的 NLP 任务是如此具有挑战性。
但是最近的 结果 暗示这也可能是由于架构内归一化和剩余连接的特定排列。
在这一点上,我在咆哮,但这让我怀疑:我们真的需要多个昂贵的成对注意力、过度参数化的 MLP 子层和复杂的学习时间表吗?
我们真的需要碳足迹巨大的模型吗?
对于手头的任务,具有良好归纳偏差的架构难道不应该更容易训练吗?
进一步阅读
要从 NLP 的角度深入了解 Transformer 架构,请查看这些令人惊叹的博客帖子:图解 Transformer 和注释 Transformer 。
此外,这个博客并不是第一个将 gnn 和变形金刚联系起来的博客:这是 Arthur Szlam 关于注意力/记忆网络、gnn 和变形金刚的历史和联系的精彩演讲。同样,DeepMind 的众星云集的立场文件引入了图网络框架,统一了所有这些想法。对于代码演练,DGL 团队有一个关于 seq2seq 作为图问题和构建变形金刚作为 gnn 的很好的教程。
在我们的下一篇文章中,我们将做相反的事情:使用 GNN 架构作为 NLP 的变形金刚(基于 的变形金刚库)🤗HuggingFace )。
最后,我们写了一篇最近的论文将变形金刚应用于草图。一定要去看看!
更新
变形金刚 GNNs 与T3【渐变】 于 2020 年 9 月出版!
该帖子最初出现在 NTU 图形深度学习实验室网站,也被翻译成了中文和俄文。一定要在 Twitter 、 Reddit 或 HackerNews 上加入讨论!
引用
在学术语境或书籍中的归属,请将此作品引用为
柴坦尼亚·k·乔希,《变形金刚是图形神经网络》,The Gradient,2020。
BibTeX 引用:
@ article { Joshi 2020 Transformers,
作者= {Joshi,Chaitanya},
标题= { Transformers is Gradient 神经网络},
期刊= {The Gradient},
年份= {2020},
如何发布= { \ URL {https://The Gradient . pub/Transformers-are-gaph-Neural-Networks/},**
NeurIPS 2019 上的变形金刚
ml 评论
NeurIPS 2019 上与变压器相关的论文

在我之前在 EMNLP 2019 上关于 BERT 的报道之后,如果不对 NeurIPS 2019 上关于变形金刚的论文做一个简短的回顾,那将是一种犯罪。关于神经信息处理系统的会议和研讨会于 12 月 8 日至 14 日在温哥华举行。像往常一样,有很多令人惊叹的想法和先进的研究。以下是其中的一些。
维尔伯特:为视觉和语言任务预先训练与任务无关的视觉语言表征
http://arxiv.org/abs/1908.02265
介绍了一种学习图像内容和自然语言的任务不可知联合表示的新方法。
ViLBERT(视觉和语言 BERT)由两个并行的 BERT 风格模型组成,在图像区域和文本片段上运行。每个流都是一系列的变压器块和新颖的共注意变压器层,它们被引入以实现模态之间的信息交换。通过从预先训练的对象检测网络(更快的 R-CNN)提取边界框及其视觉特征,每个图像被表示为一组区域特征。

ViLBERT 模型由视觉(绿色)和语言(紫色)处理的两个并行流组成,它们通过新的共注意变压器层进行交互。
有两个预训练任务:掩蔽多模态建模和多模态比对预测。屏蔽的多模态建模任务遵循标准 BERT 中的屏蔽语言建模任务——屏蔽大约 15%的单词和图像区域输入,并在给定剩余输入的情况下给模型分配重构它们的任务。在多模态对齐任务中,模型已经呈现了图像-文本对,并且必须预测图像和文本是否对齐,即文本是否描述了图像。
在许多已建立的视觉和语言任务中,ViLBERT 模型优于最先进的模型:视觉问题回答、视觉常识推理、参考表达式和基于标题的图像检索。

将我们的 ViLBERT 模型的转移任务结果与现有的最先进和合理的建筑烧蚀进行比较。
Ouroboros:关于加速训练基于 Transformer 的语言模型
http://arxiv.org/abs/1909.06695
毫无疑问,变形金刚在许多任务中都取得了巨大的成就,但是训练它们可能是一个漫长而昂贵的过程。解决这个问题的一个可能的方法是并行化。
当训练时模型太大而不适合单个设备时,模型并行化仍然是一个公开的问题。当一个模型变得太大而不适合单个计算设备时,最简单的解决方案是将模型层分布在多个设备上。
提出了一种新的模型并行算法来并行化基于转换器的语言模型的训练。这种算法可以在不损失精度的情况下显著提高速度。

每批计算时间的加速(在 K 个 GPU 上)
可视化和测量伯特的几何
http://arxiv.org/abs/1906.02715
在本文中,作者试图证明变形器具有一组语义和句法信息的中间表示。为了找到句法信息,他们在注意力向量的顶端训练线性模型。该模型必须预测两个单词之间依赖关系的存在和类型。二元探针实现了 85.8%的准确度,而多类探针实现了 71.9%的准确度。这个简单的线性探测的成功表明句法信息被编码在注意向量中。

一对有序标记的全模型注意力向量包含所有注意力头部和层中该对的标量注意力值。
第二部分是关于语义信息。人们很自然地会推测,变形金刚捕捉到了一个词在特定句子中的特定含义。
对于具有 n 个义项的给定单词,他们制作最近邻分类器,其中每个邻居是训练数据中给定词义的 BERT 基嵌入的质心。为了对一个新单词进行分类,他们找到这些质心中最近的一个,如果该单词没有出现在训练数据中,则默认为最常用的意义。简单的最近邻分类器实现了 71.1 的 F1 分数,高于当前的技术水平,精度随着层单调增加。这是一个强有力的信号,表明语境嵌入代表了词义信息。

词义消歧任务的 F1 分数。
支持树形结构变压器的新颖位置编码
【https://openreview.net/pdf?id=SJerEhR5Km 号
变压器适用于顺序建模任务。但是在某些情况下,使用纯面向序列的方法可能会导致丢失有价值的结构信息,例如,当我们想要表示层次结构时。这项工作为树结构数据引入了新的位置编码。这有可能将转换器扩展到从自然语言解析树到程序抽象语法树的设置范围。
这种方法有两种评估方法:合成翻译任务和生成的 CoffeeScript 与 JavaScript 代码之间的翻译。实验表明,使用这种编码的模型在面向树的任务中可以胜过序列转换器。

与 tree2tree LSTMs 比较的综合任务的整个程序错误率。

CoffeeScript-JavaScript 翻译任务的整个程序错误率数据。
用于语言建模的张量化转换器
http://arxiv.org/abs/1906.09777
我认为这是最有趣的作品。也许不是结果,而是使用张量分解来降低模型复杂性的事实。
如您所知,考虑如何减少内存和计算转换器所需的数量是非常重要的。现有的模型压缩方法主要分为参数剪枝和共享、低秩逼近、知识转移、转移卷积滤波器和张量分解方法。
基于参数共享的思想,本文重点研究了 transformer 中多头注意力的压缩。同时,它们还结合了低秩近似方法来降低参数和计算复杂度。提出了一种新的自我注意方法——多线性注意。

(左)使用塔克分解的单块注意。(右)基于块项张量分解的多线性注意。
在所有实验中,新架构都取得了与 SoTA 相当的结果,但参数更少。

结果(PPL)和模型参数与十亿人的最新结果。

在 PTB 和 WikiText-103 上使用最新的结果进行结果和压缩。

结果和压缩与变压器对 WMT-16 英语到德语的翻译。
变形金刚解释
对谷歌 Transformer 模型的详尽解释;从理论到实施

这篇文章是对谷歌研究的著名论文“注意力是你所需要的全部”中的变压器模型的深入阐释。该模型是序列转导任务(任何涉及将一个序列转换成另一个序列的任务)中许多 SOTA(现有技术)方法的先驱。以下是这篇帖子的内容:
内容
- 序列对序列模型的概述
- 为什么是变压器?
- 注意?自我关注!
- 什么是变压器?
- 结论
这将是一个漫长的,所以坐稳了!
序列到序列模型概述

GNMT 中的 seq2seq,由谷歌人工智能博客可视化
序列到序列编码器-解码器结构是序列转换任务的基础。它实质上建议立即编码完整的序列,然后使用该编码作为产生解码序列或目标序列的上下文。
人们可能会把这与人类倾向于首先完整地“听”一个句子(序列),然后相应地作出反应,无论是对话、翻译还是任何类似的任务。
seq2seq 模型由分别位于编码器和解码器的独立 rnn 组成。编码序列是编码器网络中 RNN 的隐藏状态。使用这个编码序列和(通常)单词级生成模型,seq2seq 生成目标序列。由于编码是在单词级进行的,因此对于较长的序列,很难在编码器中保持上下文,因此将众所周知的注意机制与 seq2seq 结合,以“注意”序列中对目标序列的生成有显著贡献的特定单词。注意力是根据输入序列中的单个单词对目标序列生成的影响来衡量它们。
为什么是变形金刚?
使用 RNNs 的序列对序列是很棒的,注意它甚至更好。那变形金刚有什么了不起的?
rnn 的主要问题在于它们不能在处理时提供并行化。RNN 的处理是连续的,也就是说,我们不能计算下一个时间步长的值,除非我们有当前的输出。这使得基于 RNN 的方法进展缓慢。
然而,脸书研究中心解决了这个问题,他们建议使用基于卷积的方法,该方法允许将并行化与 GPU 相结合。这些模型建立了单词之间的层次表示,其中在序列中出现得较近的单词在较低的层次上相互作用,而出现得较远的单词在层次中的较高层次上起作用。ConvS2S 和 ByteNet 就是两个这样的型号。引入层次结构是为了解决长期的依赖性。

通过 Michal Chromiak 的博客形成 ConvS2S 的多步关注
虽然这实现了并行化,但它的计算量仍然很大。与 RNNs 和 CNN 提供的结果质量相比,它们每层的操作数量更不合理。最初的变压器论文已经提出了这些参数对于合格模型的比较:

基于计算效率度量的 RNN、CNN 和自我注意模型的比较
这里,d(或 d_model )是单词的表示维度或嵌入维度(通常在 128-512 的范围内),n 是序列长度(通常在 40-70 的范围内),k 是卷积的核大小,r 是受限自我注意的注意窗口大小。从表中,我们可以推断出以下几点:
- 显然,自我关注的每层计算复杂度远低于其他人。
- 关于顺序操作,除了 RNNs,所有其他方法都提供并行化,因此它们的复杂度是 O(1)。
- 最后一个度量是最大路径长度,它表面上意味着参与长期依赖或远距离单词的复杂性。因为卷积模型使用分层表示,所以它们的复杂度是 nlog(n ),而自我注意模型在同一个步骤中关注所有单词,因此它们的复杂度是 O(1)。
转换器使用自关注机制,其中关注权重是使用输入序列中的所有单词一次性计算的,因此它有利于并行化。除此之外,由于转换器中的每层操作是在相同序列的字之间进行的,所以复杂度不超过 O(n d)。因此,transformer 被证明是有效的(因为它使用了注意力),同时也是一个计算高效的模型。
注意?自我关注!
在上一节中,我们讨论了转换器使用自我关注作为有效计算的手段。在这一节,我们将破译,到底什么是自我关注,它是如何在变压器使用。
查询、键和值
作为一般惯例,注意机制遵循查询、键、值模式。所有这三个都是来自输入序列的单词,它们以某种模式相互操作。查询和键最初经历某些操作,然后输出(通常)与值相乘。这将在下一小节中变得更清楚,我们将看到自我关注如何工作的图示描述。
自我关注

自我关注
如前所述,自我注意是“注意”来自同一序列的单词。
从表面上看,自我关注决定了一个单词对句子的影响
在上图中,自我注意的运作是用一个句子的例子来解释的,“这就是注意”。单词“This”与句子中的其他单词连用。类似地,计算所有单词的注意力权重(这里是“是”和“注意力”)。这里没有‘隐藏状态’的概念。输入被直接使用。这从架构中移除了的顺序性,从而允许并行化。
在变形金刚的情况下, 多头注意 被使用,这将在后面的帖子中介绍。
什么是变压器?
到目前为止,我们已经看到了在 Transformer 中实现的机制。此后,我们将实际看到这些毗邻的机制和几个具体到模型的组成部分是如何被纳入。
我们将尝试自下而上地构建一个变压器
位置编码

通过“注意力是你所需要的一切”在 Transformer 中进行位置编码
如果你观察,自我注意的计算没有单词在序列中排序的概念。尽管 rnn 很慢,但它们的顺序性确保了单词的顺序得以保留。因此,为了引出单词在序列中定位的概念,将位置编码添加到常规输入嵌入中。位置编码的维度与嵌入( d_model )相同,便于两者的求和。在该论文中,位置编码是使用以下方法获得的:

通过“注意力是你所需要的一切”使用正弦波进行位置编码
这里, i 是维度, pos 是字的位置。我们用正弦表示维度的偶数(2i),余弦表示奇数(2i + 1)。位置编码有几种选择—学习的或固定的。这是固定的方法,因为论文陈述的学习和固定的方法获得了相同的结果。
背后的一般思想是,对于固定的偏移 k,PEₚₒₛ₊ₖ可以表示为 PEₚₒₛ.的线性函数
掩饰

通过“注意力就是你需要的全部”在多头注意力中使用掩蔽
在变形金刚的多头注意机制中使用了两种遮罩。

衬垫掩模的加工
- 填充掩码:序列的输入向量应该长度固定。因此, max_length 参数定义了转换器可以接受的序列的最大长度。长度大于 max_length 的所有序列都被截断,而较短的序列用零**填充。然而,零填充既不应该有助于注意力计算,也不应该有助于目标序列生成。下图解释了填充遮罩的工作原理。这是变压器中的可选操作。

前瞻掩模的工作
- 前瞻掩码:在解码器处生成目标序列时,由于转换器使用自关注,它倾向于包括来自解码器输入的所有单词。但是,实际上这是不正确的。只有当前单词之前的单词可以有助于下一个单词的生成。掩蔽的多头注意力确保了这一点。下图解释了前瞻掩码的工作原理。
下面的代码片段解释了填充是如何起作用的:
*>>> a = tf.constant([0.6, 0.2, 0.3, 0.4, 0, 0, 0, 0, 0, 0])
>>> tf.nn.softmax(a)
<tf.Tensor: shape=(10,), dtype=float32, numpy=
array([0.15330984, 0.10276665, 0.11357471, 0.12551947, 0.08413821,
0.08413821, 0.08413821, 0.08413821, 0.08413821, 0.08413821],
dtype=float32)>
>>> b = tf.constant([0.6, 0.2, 0.3, 0.4, -1e9, -1e9, -1e9, -1e9, -1e9, -1e9])
>>> tf.nn.softmax(b)
<tf.Tensor: shape=(10,), dtype=float32, numpy=
array([0.3096101 , 0.20753784, 0.22936477, 0.25348732, 0\. ,
0\. , 0\. , 0\. , 0\. , 0\. ],
dtype=float32)>*
标度点积注意力

通过“关注是你所需要的一切”的成比例的点产品关注
这是主要的“注意力计算”步骤,我们之前在自我注意部分已经讨论过了。这包括几个步骤:
- 这是一个矩阵点积运算。首先,查询和键经历这个操作。该操作可以用数学方法表示如下:
马特穆尔(q,K) = Q.Kᵀ
- Scale: 点积运算的输出可能会导致较大的值,这可能会扰乱后面部分的 softmax。因此,我们通过将它们除以缩放因子 √dₖ.来缩放它们
- 遮罩:可选的填充遮罩已经在上面的遮罩部分讨论过了。
- soft max:soft max 函数将数值降低到一个概率分布,即[0,1]。

通过“注意力是你所需要的全部”得出的比例点积注意力的最终等式
比例点积注意力是多头注意力的主要组成部分,我们将在下一小节中看到。
多头注意力

通过的多头关注
多头注意力本质上是前面讨论的所有微观概念的整合。
在相邻的图中, h 是头数。就数学而言,多头注意力的初始输入被分成 h 部分,每个部分都有查询、键和值,用于序列中的 max_length 字,用于 batch_size 序列。Q、K、V 的尺寸称为深度,计算如下:
深度= d _ 模型// h
这就是 d_model 需要被h整除的原因。因此,在分割时, d_model 形状矢量被分割成形状深度的 h 矢量。这些向量作为 Q、K、V 被传递给缩放的点积,并且通过再次将 h 向量整形为形状 d_model 的 1 个向量,输出为“ Concat ”。这个重组的向量然后通过前馈神经网络层。
点式前馈网络和残余漏失

通过“关注是你所需要的一切”的逐点 FFN 和剩余辍学
逐点前馈网络块本质上是两层线性变换,在整个模型架构中使用相同,通常在关注块之后。
对于正则化,在将每个子层的输出添加到子层的输入并进行归一化之前,对每个子层的输出应用缺失。
最后,我们有完整的变压器!

变压器 via “注意力是你所需要的一切”
所有的组件被组装在一起,以建立变压器。编码器模块在左边,解码器模块在右边。
编码器和解码器块可以可选地微调到 Nₓ单位以调整模型。
结论
在这篇文章中,我们看到了序列转导任务中基于 RNN 的方法的问题,以及革命性的 Transformer 模型如何解决这些问题。后来,我们研究了变压器的基本机制及其工作原理。最后,我们看到了各种组件和底层机制如何与转换器一起工作。
我已经在这里介绍了使用 TensorFlow 进行抽象文本摘要的 Transformer 的逐步实现。
参考
《注意力是你所需要的全部》变形金刚原纸:【https://arxiv.org/abs/1706.03762
*Seq2Seq 论文:【https://arxiv.org/abs/1409.3215 *
巴林岛注意事项:https://arxiv.org/abs/1409.0473
ConvS2S 和 ByteNet 论文:https://arxiv.org/abs/1705.03122,https://arxiv.org/abs/1610.10099
从关注到自我关注到变形金刚
towardsdatascience.com](/self-attention-and-transformers-882e9de5edda) [## 变形金刚-你需要的只是注意力。
变形金刚——不仅仅是看上去那么简单!我们到了吗?良好的...不完全是,但是...消除复发怎么样…
mchromiak.github.io](https://mchromiak.github.io/articles/2017/Sep/12/Transformer-Attention-is-all-you-need/#.XreFtRbhU5l)
一些很棒的变形金刚形象:
讨论:黑客新闻(65 分,4 条评论),Reddit r/MachineLearning (29 分,3 条评论)翻译…
jalammar.github.io](http://jalammar.github.io/illustrated-transformer/)*
简化多标签分类的变压器。
伯特、XLNet、罗伯塔等。对于多标签分类—逐步指南
作为一名数据科学家,我一直在学习文本分类的最新技术,我发现没有太多容易的例子来适应 transformers (BERT,XLNet 等。)进行多标签分类…所以我决定自己试试,这就是!
作为对其他多标签文本分类博客帖子的敬意,我将使用有毒评论分类挑战数据集。
这篇文章附有一个互动的谷歌 Colab 笔记本,所以你可以自己尝试一下。您所要做的就是将 train.csv、test.csv 和 test_labels.csv 文件上传到实例中。让我们开始吧。

在本教程中,我将使用拥抱脸的变形金刚库以及 PyTorch(带 GPU) ,尽管这可以很容易地适应 TensorFlow — 如果这与多类分类教程一起获得牵引力,我可能稍后会为此编写一个单独的教程。下面我将训练一个 BERT 模型,但是我将向你展示在这个过程中为其他 transformer 模型改编这个代码是多么容易。
导入库
加载和预处理训练数据
毒性数据集已经被清理并被分成训练集和测试集,因此我们可以加载训练集并直接使用它。
每个 transformer 模型需要不同的标记化编码,这意味着句子的标记化方式和注意力屏蔽的使用可能会因您使用的 transformer 模型而异。令人欣慰的是,HuggingFace 的变形金刚库使得为每个模型实现变得极其容易。在下面的代码中,我们加载了一个预训练的 BERT 记号赋予器,并使用“batch_encode_plus”方法来获取记号、记号类型和注意掩码。随意加载适合您想要用于预测的模型的记号赋予器。例如,
BERT:
tokenizer = BertTokenizer.from_pretrained('bert-base-uncased', do_lower_case=True) XLNet:
tokenizer = XLNetTokenizer.from_pretrained('xlnet-base-cased', do_lower_case=False) RoBERTa:
tokenizer = RobertaTokenizer.from_pretrained('roberta-base', do_lower_case=False)
接下来,我们将使用 10%的训练输入作为验证集,这样我们就可以在训练时监控分类器的性能。在这里,我们希望确保我们利用了“分层”参数,这样就不会有看不见的标签出现在验证集中。为了进行适当的分层,我们将所有只在数据集中出现一次的标签,并强制它们进入训练集。我们还需要创建 PyTorch 数据加载器来加载用于训练/预测的数据。
加载模型和设置参数
加载适当的模型可以如下所示,每个模型已经包含了一个单一的密集层用于分类。
BERT:
model = BertForSequenceClassification.from_pretrained("bert-base-uncased", num_labels=num_labels)XLNet:
model = XLNetForSequenceClassification.from_pretrained("xlnet-base-cased", num_labels=num_labels)RoBERTa:
model = RobertaForSequenceClassification.from_pretrained('roberta-base', num_labels=num_labels)
优化器参数可以通过几种方式进行配置。这里我们使用了一个定制的优化参数(我在这方面做得更成功),但是,您可以只传递注释中所示的“model.parameters()”。
火车模型
使用“分类交叉熵”作为损失函数,将 HuggingFace 库配置为开箱即用的多类分类。因此,变压器模型的输出类似于:
outputs = model(batch_input_ids, token_type_ids=None, attention_mask=batch_input_mask, labels=batch_labels)**loss, logits = outputs[0], outputs[1]**
但是,如果我们避免传入标签参数,模型将只输出逻辑值,我们可以使用这些逻辑值来计算多标签分类的损失。
outputs = model(batch_input_ids, token_type_ids=None, attention_mask=batch_input_mask, labels=batch_labels)**logits = outputs[0]**
下面是这样做的代码片段。这里我们使用“具有 Logits 的二元交叉熵”作为我们的损失函数。我们可以很容易地使用标准的“二元交叉熵”、“汉明损失”等。
为了验证,我们将使用微 F1 精度来监控跨时代的训练表现。为此,我们必须利用模型输出中的逻辑值,将它们传递给 sigmoid 函数(给出[0,1]之间的输出),并对它们进行阈值处理(0.50)以生成预测。然后,这些预测可用于计算相对于真实标签的准确度。
维奥拉。我们已经准备好训练,现在运行它…我的训练时间在 20-40 分钟之间,取决于最大令牌长度和使用的 GPU。
预测和指标
我们的测试集的预测类似于我们的验证集。在这里,我们将加载、预处理和预测测试数据。
输出数据帧
创建显示句子及其分类的输出数据框架。
奖励—优化微 F1 精度的阈值
迭代阈值以最大化微 F1 精度。
就是这样!有问题请评论。这里是 Google Colab 笔记本的链接,以防你错过。如果您有任何私人问题,请随时通过 LinkedIn 或 Twitter 与我联系。
参考资料:
https://electricenjin . com/blog/look-out-Google-Bert-is-here-to-shake-up-search-queries
https://github.com/google-research/bert
https://github.com/huggingface/transformers
【https://pytorch.org/docs/stable/nn.html#bcewithlogitsloss 号
【https://arxiv.org/abs/1706.03762
变形金刚(最先进的自然语言处理)
《变形金刚》第 1/3 部分 vs 谷歌 QUEST 问答标注(Kaggle top 5%)。

作者图片
这是一个由 3 部分组成的系列,我们将经历变形金刚、伯特和动手 Kaggle 挑战—Google QUEST Q&A Labeling以观看变形金刚的行动(在排行榜上排名第 4.4%)。在这一部分(1/3)中,我们将了解变形金刚是如何成为各种现代自然语言处理任务及其工作方式的艺术。
TheTransformer是谷歌和多伦多大学的研究人员于 2017 年在论文 Attention is All You Need 中提出的深度学习模型,主要用于自然语言处理(NLP)领域。
像递归神经网络(RNNs)一样,转换器被设计为处理顺序数据,如自然语言,用于翻译和文本摘要等任务。然而,与 rnn 不同,转换器不要求按顺序处理顺序数据。例如,如果输入数据是自然语言句子,转换器不需要在结束之前处理它的开头。由于这个特性,Transformer 允许比 RNNs 更多的并行化,因此减少了训练时间。
变压器是围绕注意力机制的概念设计的,注意力机制是为了帮助记忆神经机器翻译中的长源句子而设计的。
听起来很酷,对吧?让我们看一看引擎盖下面,看看事情是如何运作的。

图像来源
变压器基于编码器-解码器架构,该架构由一组编码层组成的编码器和一组解码层组成的解码器组成,前者一层接一层地迭代处理输入,后者对编码器的输出执行相同的操作。
因此,当我们将一个句子传递到转换器中时,它被嵌入并传递到一个编码器堆栈中。最终编码器的输出随后被传递到解码器堆栈中的每个解码器模块。解码器堆栈随后产生输出。
变换器中的所有编码器模块都是相同的,同样,变换器中的所有解码器模块也是相同的。

来源:http://jalammar.github.io/illustrated-transformer/
这是一个非常高层次的变压器表示,当理解变压器如何在现代 NLP 任务中如此有效时,它可能没有多大意义。不要担心,为了让事情更清楚,我们现在将讨论编码器和解码器单元的内部结构…
编码器
编码器有两个部分,自我关注,和一个前馈神经网络。

来源:http://jalammar.github.io/illustrated-transformer/
编码器的输入首先流经自我关注层,该层帮助编码器在编码特定单词时查看输入句子中的其他单词。基本上,对于每个输入单词“x ”,自我关注层生成一个向量 Z ,使得它在生成 Z 之前将所有的输入单词(x1,x2,x3,…,xn)放入图片中。稍后我会在博客中解释为什么要将所有的输入单词嵌入到图片中,以及它是如何生成 Z 的,但是现在,只需要记住编码器子组件的这些简短的概要。
自我注意层的输出被馈送到一个前馈神经网络。前馈神经网络为每个输入 Z 生成一个输出,前馈神经网络的输出被传递到下一个编码器模块的自关注层,依此类推。
现在我们已经知道了编码器内部的所有内容,让我们来理解每个组件内部发生的张量运算。
首先是输入:
我们知道变压器用于 NLP 任务,因此我们处理的数据通常是句子的语料库,但由于机器学习算法都是关于矩阵运算的,我们首先需要将人类可读的句子转换为机器可读的格式(数字)。为了将句子转换成数字,我们使用“单词嵌入”。这一步很简单,句子中的每个单词都表示为一个 n 维向量(n 通常是 512),对于变形金刚,我们通常使用单词的手套嵌入表示。
还有一种叫做 位置编码 的东西,适用于这些嵌入,但我稍后会谈到。一旦我们有了每个输入单词的嵌入,我们将这些嵌入同时传递给自我关注层。
自我关注层的训练参数:
不同的层具有不同的学习参数,例如,密集层具有权重和偏差,而卷积层具有内核作为学习参数,类似地,在自我关注层中, 我们有 4 个学习参数:
-查询矩阵:【Wq】
-关键矩阵:Wk
-值矩阵:Wv
-输出矩阵:Wo
前 3 个可训练参数有特殊用途,它们用于生成 3 个新参数:
-查询:【Q】* -关键:K -值:V这些参数稍后用于生成输出*
需要记住的几点是:
-输入张量 x 具有 n 行和 m 列,其中 n 是输入单词的数量, m 是每个单词的向量大小,即 512。
-输出张量 Q、K、V、 和 Z 有 n 行和 dk列,其中 n 是输入字数, dk 是 64。 m 和 dk 的值不是随机值,而是提出这种架构的研究人员发现的最佳值。**

来源:http://jalammar.github.io/illustrated-transformer/
在如上所述计算了 3 个参数 Q,K,V 之后,自我关注层然后为每个输入单词计算 分数,向量。
点状产品注意:
自我关注层的下一步是计算每个输入单词对应的向量分值。这个分数计算是将注意力机制带入生活的最关键的步骤之一(嗯…不是字面上的意思)。向量的大小为 n 其中n是输入单词的数量,并且该向量的每个元素是一个数字,该数字表示其对应的单词对当前单词的贡献有多大
让我们考虑一个例子来得到直觉——
“动物没有过马路是因为它太累了”在上面的句子中, 这个词 it 指的是动物而不是路。对我们来说,这很容易掌握,但对一台没有注意力的机器来说就不一样了,因为我们知道语法是如何工作的,而且我们已经发展出一种感觉,那就是它指的是 动物 而不是像 穿越 或 道路这样的词。 这种语法感是经过训练后出现在变形金刚身上的,但事实是,对于一个给定的单词,它会考虑输入的所有单词,然后有能力选择它认为贡献最大的一个单词,这就是注意力机制的作用。 对于上面的句子,为单词 it 生成的得分向量会有 11 个数字,每个数字对应输入句子中的一个单词。对于训练有素的模型,该得分向量在位置 2 和 8 处将具有较大的数字,因为位置 2(动物)和 8(它)处的单词对 it 贡献最大。 可能看起来是这样的:
【2, 60 ,4,5,3,8,5, 90 ,7,6,3】
注意位置 2 和 8 的值大于其他位置的值。****

来源:http://jalammar.github.io/illustrated-transformer/
我们来看看这些分数是如何在自我关注层产生的。到目前为止,对于每个单词,我们有 Q,K,V 向量。为了生成分数向量,我们使用一种叫做点积注意力的东西,其中我们在 Q 和 K 向量之间取点积来生成分数。 Q 的值对应于我们正在计算得分的查询词,在上面的例子中,该词是,而有 n 个的值 K、 分别对应于输入词的关键向量。
所以,如果我们想为单词 it 生成分数:******
- 我们取它的查询向量: Q
- 我们取输入句子的关键向量: K1,K2,K3,…,Kn。
- 我们取 Q 和 K 之间的点积,得到 n 的分数。
计算完分数后,我们通过将分数除以(【dk】)的平方根来对分数进行归一化,平方根是向量【Q,K,V. 的列维度。这一步是强制性的,因为 transformer 的创建者发现通过 sqrt 对分数进行归一化。的 dk 的 给出更好的结果。******
标准化分数向量后,我们使用 softmax 函数对其进行编码,使得输出与原始分数成比例,但所有值的总和为 1。
一旦我们准备好“软最大化”分数,我们简单地将每个分数元素与对应的值向量 V 相乘,这样,在这个操作之后,我们得到 n 个值向量V:V1,V2,V3,…,Vn** 。
现在为了获得自我关注的输出 Z ,我们简单地把所有的 n 个值向量相加。****

简而言之,所有自我关注的步骤

来源:http://jalammar.github.io/illustrated-transformer/
上图说明了自我关注层的步骤。
多头关注:
现在我们知道了注意力头是如何工作的,以及它有多神奇,这就有一个陷阱了。单个注意力头有时会错过输入中对聚光灯单词贡献最大的一些单词,如在前面的例子中,有时注意力头在预测单词 it 时可能没有注意到单词动物,这可能会导致问题。为了解决这个问题,我们使用了多个注意力头,而不是单个注意力头,每个注意力头都以类似的方式工作。这个想法有助于我们减少任何单个注意力头的错误或误算。
这也被称为 多头关注 。

两种不同注意力的得分用橙色和绿色表示。我们可以看到,一个注意力集中者更关注像动物、十字路口这样的词,而另一个注意力集中者更关注像街道、疲倦这样的词。 ( 图像来源)。
在变形金刚中,多头注意力通常使用 8 个注意力头。
现在注意,单个注意力头的输出是 64 维的,但是如果我们使用多头注意力,我们将得到 8 个这样的 64 维向量作为输出。

来源:http://jalammar.github.io/illustrated-transformer/
原来有一个最后的可训练参数输出矩阵 Wo 我之前提到的在这里发挥作用。
在自我关注的最后一层,所有的输出【Z0,Z1,Z2,…,Z7】被连接并与 Wo 相乘,使得最终的输出 Z 的维数为 64。**
下图显示了上面讨论的所有步骤:

来源:http://jalammar.github.io/illustrated-transformer/
位置编码:
记得在首先是输入部分,我提到了位置编码,让我们看看它们是什么以及它们如何帮助我们。我们目前的 awesome transformer 的问题是,它没有考虑输入单词的位置。与 RNN 不同,我们用时间步长来表示哪个单词在前面和后面,在《变形金刚》中,因为单词是同时输入的,所以我们需要某种位置编码来定义哪个单词在哪个单词后面。
位置编码帮助了我们,因为它为输入嵌入提供了位置感,我们首先为每个输入单词生成位置嵌入,然后将这些位置嵌入添加到相应单词的单词嵌入中,以生成带有时间信号的嵌入。****
提出了许多方法来生成位置嵌入,如一键编码向量或二进制编码,但研究人员发现最有效的方法是使用以下等式来生成嵌入:

作者图片
当我们为最大长度为 50 的句子绘制 128 维位置编码时,它看起来类似于:

*每行代表嵌入向量(*作者图片 )
剩余连接:
最后,编码器中还增加了一个称为剩余连接或跳过连接的功能,允许前一层的输出绕过中间的层。
它在有许多隐藏层的深层网络中很有帮助,如果中间的任何层没有太大用处或没有学到太多,跳过连接有助于绕过该层。
另一个需要注意的是,当剩余连接被添加并且结果被归一化时。

带跳跃连接的编码器单元。(图像来源)。
解码器
解码器与编码器非常相似。像编码器一样,它也有自关注和前馈网络,但它也有一个称为编码器-解码器关注的附加块夹在两者之间。
编码器-解码器关注层的工作方式与多头自我关注类似,只是它从其下一层创建查询矩阵,并从编码器堆栈的输出中获取键和值矩阵。
其余 2 层的工作方式与编码器单元中的完全相同。**

编码器解码器架构。(图片来源)。
解码器堆栈的输入是顺序的,不同于编码器堆栈中的同时输入,这意味着第一个输出字作为输入传递到解码器,解码器使用它生成第二个输出,现在该输出再次作为输入传递到解码器,并使用它生成第三个输出,依此类推…

变压器的工作。(图片来源)。
解码器的输出被传递到具有 softmax 激活的线性层,使用该线性层来预测正确的字。

来源:http://jalammar.github.io/illustrated-transformer/
一旦变换器使用前向传播来预测单词,就使用像交叉熵这样的损失函数将预测与实际标签进行比较,然后使用反向传播来更新所有可训练参数。
嗯,这是理解学习如何在变形金刚中发生的一种简化方式。有更多的变化,如采取完整的输出句子来计算损失。想了解更多,你可以看看杰伊·阿拉玛在《变形金刚》上的博客。
到此,我们就到此为止了。希望阅读愉快。
我要感谢所有的创作者,他们创作了我写这篇博客时提到的精彩内容。
参考链接:
- **应用人工智能教程:https://www.appliedaicourse.com/
- https://arxiv.org/abs/1706.03762
- http://jalammar.github.io/illustrated-transformer/T21
- http://primo.ai/index.php?title=Transformer
- https://en . Wikipedia . org/wiki/Transformer _(machine _ learning _ model)
- https://medium . com/inside-machine-learning/what-a-transformer-d 07 DD 1 fbec 04
最终注释
感谢您阅读博客。我希望它对那些渴望做项目或学习 NLP 新概念的人有用。
在第 2/3 部分中,我们将讨论 BERT(变压器的双向编码器表示)。
在第 3/3 部分中,我们将经历一次动手的 Kaggle 挑战——Google QUEST Q&A Labeling来看看变形金刚的行动(在排行榜上排名第 4.4%)。
在 LinkedIn 上找到我:www.linkedin.com/in/sarthak-vajpayee
和平!☮
将数据转化为行动
数据科学的商业价值取决于人工智能项目与企业战略、文化和资源的一致性

图片来源:EB 局
许多公司正在大力投资人工智能,因为它们认为有必要“对每天捕获的大量数据做点什么”。Gartner 计算出今年的投资为 3830 亿美元,尽管事实上绝大多数人工智能项目都没有现实世界的应用。如果人工智能的潜在好处很难被忽视,那么成功的项目取决于权衡人工智能的潜力和组织现实的能力。管理层如何将数据科学中的实验与企业战略结合起来,鼓励他们的数据科学团队开发直接适用于业务的模型,并展示他们的投资如何影响底线?
金融欺诈案是一个非常明显的例子。行业观察家估计,仅在线信用卡欺诈一项就将在当前日历年内给金融界造成 320 亿美元的损失。【ii】根据 Javelin Strategy &的研究,如今银行使用传统的基于规则的系统检测欺诈交易需要 40 多天。这些偏见的实际成本甚至更高,因为 20%的客户在经历诈骗后会更换银行。数据科学可以将这一挑战转化为机遇,促进实时索赔评估并提高欺诈检测的准确性。
然而,尽管过去十年有多个人工智能项目,传统银行和金融科技都无法消除这一挑战。在实践中,当前将数据科学应用于欺诈检测的工作遵循以下三种场景之一。异常检测建立在当前基于规则的系统之上,表明低效流程是问题所在,解决方案是检测不遵守财务流程的情况。第二种方法使用预测分析来探索购买行为,表明行为特征是欺诈检测的关键。最后,第三种方法试图利用支持向量机和神经网络的蛮力,这种方法基于这样一种信念,即规范分析的进步肯定会产生预期的结果。考虑到实际成本和潜在收益,管理层应该在哪个场景中投资?
商业价值矩阵
Kees Pronk 和我在几年前引入了 Business Value Matrix(BVM)来探索数据、环境和商业价值之间的关系。【iii】矩阵基于几个前提。经理和他们的客户看待数据的角度不同,因为他们看待价值的角度不同。数据的价值取决于企业战略、文化和背景。该算法的商业价值并不来自其精确性,而是来自其在解决客户挑战方面的用途。最后,管理者的工作不仅仅是分析数据,而是利用数据来激励行动。让我们检查一下矩阵的结构,然后应用它来评估欺诈检测中不同的基于数据的方案。

商业价值矩阵
业务价值矩阵(BVM)建议可以将数据汇总,以沿着三个轴探索客户的挑战:利益相关者认为价值来自哪里,他们在哪个汇总级别寻找价值证明,以及他们使用什么指标来建议成功。对于一个数据科学团队来说,矩阵可以用来提供三个可交付成果——探索人工智能项目的范围和企业文化之间的最佳匹配,建议他们需要在哪里向他们的利益相关者提供价值证明,以及提供可以激励管理层将数据转化为行动的指标。让我们依次探索每一把斧子。
价值从何而来?第一个问题强调了每个利益相关者如何确定问题,以及他或她认为解决方案在哪里。对于一些利益相关者来说,一个成功的企业是建立在良好记录的、组织良好的流程之上的——客户的挑战揭示了可以检测、阐明和解决的流程缺陷。对于其他利益相关者来说,成功的项目是围绕理解他们的内部和外部客户而建立的:业务挑战需要努力更好地理解推动人们想要与组织合作的目标、愿景和激励。最后,在第三种情况下,企业是围绕技术建立的,这是一种根深蒂固的信念,即信息和物理技术的创新本身就可以积极影响底线。考虑到他们对商业价值的特殊观点,涉众收集和监控不同种类的数据,作为组织当前问题和未来机会的证据。
利益相关者在哪一级寻找价值证明?第二个问题提出了提供概念证明的三种可能性。那些相信个人可以改变公司表现的人;个人生产力的数据是组织成功的代表。对其他人来说,一个企业的好坏取决于它最薄弱的环节——在这里,组织作为一个整体如何运作的数据是成功的最佳指标。最后,对于某些经理或股东来说,唯一真正的证据是市场本身——生产率指标必须根据客户满意度和市场份额来分析。这里的数据科学家需要了解哪些类型的数据对利益相关者来说是重要的,并在利益相关者寻求概念证明的层面上集中精力提供证据。
利益相关者如何衡量成功?对于一定数量的决策者来说,成功是一个效率问题,数据需要揭示相关活动的成本和收益。对于其他人来说,有效性是一个更好的衡量成功的标准——数据需要反映组织与其客户之间关系的质量。对于其他人来说,创新是关键指标,数据需要说明组织如何应对外部威胁和机遇。对于其他人来说,利用率提供了突出组织资源随着时间的推移使用得如何的主要度量。在每一种情况下,理解利益相关者如何衡量成功是提供数据的先决条件,这些数据将帮助他们将证据转化为个人或集体行动。
异常检测寻求组织流程
BVM 能帮助数据科学家改善信用卡欺诈吗?异常检测算法建立在传统的基于规则的方法之上。学习模型是在被标记为欺诈或合法的连续输入数据流上训练的。在测试阶段,人工代理会被告知偏离可接受模式的情况,以供审查。因此,该模型被训练为接受银行交易内容的“常态”基线。这个基线本身并不反映消费者行为,而是反映了对组织过程的遵守程度。异常检测将吸引专注于改进欺诈检测流程的利益相关者,而不是那些对更好地了解消费者行为或获得技术优势感兴趣的人。

图片来源:潘金淑·TRAN
自动化基于规则的欺诈检测
如果这里的价值在于改进过程,那么涉众会在哪里寻找概念证明呢?他们不太可能在个体的基础上寻找证据,这可能是在理解测试数据中真阳性的数量时获得的。也有可能,但也不太可能,他们会对使用异常检测来获得市场竞争优势感兴趣,因为无论数据模型多么高效,都不会对市场份额产生什么影响。最有可能为整个业务部门寻找概念证明:一个更好的过程将使所有员工受益,而不管他们的个人知识或贡献如何。
最后,对于对改进流程感兴趣的赞助商,他们将如何衡量成功?一方面,效率指标,包括减少欺诈交易或减少检查每笔交易的成本,肯定会受到欢迎。另一方面,度量有效性的指标在这里没有什么帮助,因为产生的数据对改善组织和客户之间的关系没有什么直接作用。同样,与创新相关的指标在这里也没有什么意义,因为目标是细化流程,而不是设计新的服务产品。为了迎合相信价值来自过程的涉众,异常检测通常需要在组织级别上提供围绕效率度量的概念证明。
聚类算法寻求改善对消费者行为的理解
哪种欺诈检测方法会吸引对了解消费者行为更感兴趣的利益相关方?预测分析利用模式聚类和/或神经网络来识别交易数据流中的异常值..包括 K 均值、基于密度或均值漂移聚类的方法可用于研究消费行为,允许机器学习模型阐明与零售购物或电子商务交易相关联的欺诈行为的足迹。探索消费模式将吸引赞助商,他们更感兴趣的是提高组织对消费者行为特殊性的理解,而不是标准化操作流程。

图片来源:Dhruv Sharma
探索消费者消费模式集群,了解欺诈行为
如果这里的价值集中于更好地理解人性,那么利益相关者在哪里寻找概念的证明呢?需要仔细考虑三种情况。使用人口统计和行为数据阐明个人消费模式的能力当然是一个优势,尽管对尊重消费者数据保护权的担忧可能会在这方面造成障碍。对整个市场进行概念验证可能被证明是不现实的,因为溢价很可能被放在组织为目标人群提供的个性化服务上。探索行为数据来识别消费者特征似乎是一个诱人的选择。
对于那些相信价值来自理解人类行为的利益相关者,他们将如何衡量成功。?效率指标在这里不太重要,因为它们衡量的是与现有组织流程相关的成本,而不是开发新市场和服务的机会。在描述组织和消费者之间的关系质量时,围绕服务提供的有效性的度量将具有更大的吸引力。最后,创新也将在设计新服务方面获得动力,使本组织能够更好地为其目标群体服务。开发人类和机器智能的预测分析为更相信人而不是过程或技术的利益相关者提供了一条有吸引力的前进道路。
单靠技术能提供竞争优势吗?
规范分析旨在为组织提供解决方案,在“欺诈签名”出现并造成持久损害之前识别它们。数据科学团队可以通过分析数亿笔交易并将其提交给预测分析引擎(集成随机森林、朴素贝叶斯、支持向量机等)来构建估算模型。)提供一旦发现欺诈行为应采取的措施的建议。规范分析从预测模型中获取输入,结合规则和基于约束的优化,自动做出决策。这种类型的机器学习有效地将“人类”判断从计算最高可能真阳性率(TPR)和最低可能假阳性率(FPR)的等式中去除。

图片来源:凯瑟琳·梅尔彻,罗莎丽亚·西里波
基于推荐代理的规定性分析
如果这里的价值主张是技术,那么利益相关者在哪里寻找概念证明呢?需要仔细考虑三种情况。识别个人消费模式的能力肯定是一个优势,尽管对尊重消费者数据保护权的担忧可能会带来障碍。采用说明性分析也可能会遭到员工和经理的抵制,他们认为欺诈检测是他们获得报酬提供的技能的一部分。尽管如此,向整个市场推销概念证明对寻找创造可持续竞争优势的途径的利益相关者来说还是很有吸引力的。
相信价值来自技术的赞助商将如何衡量成功?考虑到开发可行的欺诈检测解决方案的成本,很难确定效率指标。考虑到员工、消费者和监管机构可能的抵制,围绕技术有效性的衡量标准也没有多大意义。鉴于这些机器学习模型的透明度和可解释性的挑战,组织学习也是极不可能的。规范性技术的支持者只能用创新的标准来证明这些新的欺诈管理使用场景的价值。
结论
数据科学的终极目标不是分析数据,而是解决业务问题。成功的人工智能项目取决于管理能力,以将机器学习的潜力与企业战略、文化和可用资源中反映的组织现实相结合。商业价值矩阵(BVM)提供了一个框架,通过关注利益相关者对价值来源(流程、人员或技术)的理解来探索这些现实,他们在哪里寻找证明价值(在个人层面、组织或整个市场),以及他们如何定义成功(效率、有效性、利用率、创新……)。
我们探讨了 BVM 在欺诈管理中的潜在价值。基于异常检测、预测和规定分析的三种潜在方法对应于流程、人员和技术价值的对比视图。在通过矩阵探索利益相关方的观点时,数据科学团队可以提供他们的管理层正在寻找的数据,以增加其组织的价值。数据科学不是对每天捕获的大量数据做些什么,而是做些什么来帮助管理层将数据转化为行动。
— — -
【I】Denis co Rayome,(2019) 为什么 85%的人工智能项目会失败,Tech Republc
【iii】Schlenker,l .和 Pronk,K. (2009),价值之旅,Goodfellow
Lee Schlenker 是 BAI 的校长,也是商业分析和社区管理教授。他在 LinkedIn 上的个人资料可以在查看
用 GANs 将真实照片转化为艺术大师作品

创造力:人类独有的
艺术——创造原创事物的能力,利用一个人无限的创造力和想象力——这是我们人类愿意相信自己独有的东西。毕竟,就写实绘画而言,迄今为止还没有任何其他动物或计算机能与人类的艺术技巧相媲美。

我是说猴子们尽力了。这是…抽象艺术| 来源
即使人工智能最近取得了进步,计算机仍然在创造性方面苦苦挣扎。他们擅长计算、分类和执行编程任务,但他们永远无法真正达到人类的创造力水平。人类的创造力无疑是独一无二的,独一无二的……那是在生殖对立网络被构想出来之前。在 2014 年,关于 GANs 的原始论文提出了一个新的评估生成模型的系统——可以创建的模型——使用两个模型。
永恒的猫和老鼠的游戏
GAN 由两个模型组成,生成模型 G 和判别模型 D 以及真实数据集。你可以把 G 想象成造假者,努力制造越来越像真钱的钱 D 想象成 cop ,努力区分钱是真的还是假的。真实数据集充当 cop 与伪造者的输出进行比较的参考。

典型的甘氏架构| 来源
一开始,伪钞制造者会很糟糕,因为他不知道如何让这些钱看起来像真钱,但是在每次警察确定假钞后,他会变得更好,并从错误中学习。请记住,鉴别者在鉴别真币和假币方面也越来越好——他正在成为一名更好的警察。这种循环一遍又一遍地继续,直到生成器非常擅长创建看起来像训练数据的数据,甚至连鉴别器都分辨不出来。
使用循环 GANs 的图像-图像翻译
在创建新的、看起来相似的数据时,经典的 GAN 架构很好,但在尝试改变现有图像时,它就不那么好用了。此外,传统的图像-图像翻译方法需要带有成对示例的数据集。

同一地点冬季/夏季视图的配对示例| 来源
这些成对的例子是与直接相关的数据点——它们显示了原始图像和对它的期望修改。例如,训练数据集需要在冬季和夏季包含相同的景观。然而,这些数据集具有挑战性,并且难以准备——有时甚至是不可能的,艺术就是如此。根本没有蒙娜丽莎或其他伟大艺术品的摄影等价物。
为了解决这个问题,我们使用了一个周期 GAN。这是一种特殊类型的生成对抗网络,对 GAN 架构进行了扩展:循环一致性。这是一个概念,即第一个生成器输出的图像可以用作第二个生成器的输入,第二个生成器的输出应该与原始图像匹配——撤销第一个生成器对原始图像所做的操作。

一个循环 GAN 用于斑马到马的图像-图像翻译| 来源
可以这样想:你正在使用谷歌翻译把一些东西从英语翻译成西班牙语。然后你打开一个新的标签页,将西班牙语复制粘贴回谷歌翻译,谷歌翻译会将其翻译成英语。在这一切的最后,你会再次期待原来的句子。这是一个循环 GAN 的原理,它作为一个附加损耗来衡量第二个发生器产生的输出与原始图像之间的差异, 而不需要 成对的例子。
用于风格转换的循环 GAN 架构
如果我们的模特被训练成法国艺术家塞尚的风格的图像,那么甘的循环是如何工作的。
数据集
- 数据集 1 :真实照片
- 数据集 2 :塞尚的艺术作品
生成对抗网络
- 甘 1 :将真实照片(数据集 1)转换成塞尚风格的艺术品(数据集 2)
- 甘 2 :将塞尚风格的艺术品(数据集 2)转化为真实的照片(数据集 1)
正向周期一致性损失
- 将真实照片(集合 1)输入到 GAN 1
- 来自 GAN 1 的塞尚风格艺术品的输出照片
- GAN 1 至 GAN 2 生成的塞尚风格艺术品的输入照片
- 从 GAN 2 输出真实照片
- 使用鉴别器将真实照片(集合 1)与从 GAN 2 输出的真实照片进行比较
反向循环一致性丧失
- 将塞尚艺术品(收藏 2)的照片输入到 GAN 2
- 从 GAN 2 输出真实照片
- 将 GAN 2 生成的真实照片输入到 GAN 1
- 从 GAN 1 输出塞尚风格的艺术品
- 使用鉴别器将塞尚原画(系列 2)与甘 1 的塞尚风格作品进行比较
通过最大限度地减少这两种损失,我们的模型最终将学会如何将真实的照片转化为塞尚风格的艺术品——由于我们有不止一个而是两个甘,我们也可以将塞尚的原始艺术品转化为真实的照片。如果我们希望我们的模型将我们的照片转换成另一位艺术家的风格,我们只需要将数据集 2 中的塞尚作品替换为另一位艺术家的作品,比如梵高或毕加索。
真的——我们能够用甘斯做的只是坚果。我的意思是,即使对于一个人来说,试图模仿一个艺术家的风格,并以一张照片作为灵感,也是一件令人生畏的任务。有些人把他们的一生都奉献给了这项工作,但对于一个甘来说,他们可以在几分钟内将任何训练有素的风格应用到任何图片上。坚果!
结果呢

创造性计算的未来
虽然称它们为杰作可能有点夸张,但毫无疑问,在我们曾经认为安全的东西方面,人工智能正在迅速赶上人类——艺术天赋和创造力。我只解释了 GANs 的一个应用,我个人认为它很有趣,但 GANs 现在被用于无数的方面,从生成逼真的人脸到提高图像质量——在这一切的核心,只是一个骗子和警察之间的游戏。

用 Python 将文本文件转换成数据表
从任何文本文件中提取信息的可重用方法

Maksym Kaharlytskyi 在 Unsplash 上的照片
在本文中,我描述了如何将一组文本文件转换成可用于自然语言处理和机器学习的数据表。为了展示我的方法,我使用了 D. Greene 和 P. Cunningham 在 2006 年发表的原始 BBC 新闻文章数据集。
在进入 IDE 并开始编码之前,我通常会遵循一个过程,包括理解数据、定义输出以及将所有内容翻译成代码。我认为编码前的任务通常是最重要的,因为它们有助于更有效地构建和遵循编码过程。

我这个项目的三步流程
1.数据理解
在能够从文本文件中提取任何信息之前,我们希望知道它的信息是如何构造的,以及文本文件是如何存储的以及存储在哪里(例如,名称、目录)。
结构
为了理解这种结构,我们看一下一些文本文件,了解一下数据的结构。
Claxton hunting first major medal
British hurdler Sarah Claxton is confident she can win her first major medal at next month's European Indoor Championships in Madrid.
The 25-year-old has already smashed the British record over 60m hurdles twice this season, setting a new mark of 7.96 seconds to win the AAAs title. "I am quite confident," said Claxton. "But I take each race as it comes. "As long as I keep up my training but not do too much I think there is a chance of a medal." Claxton has won the national 60m hurdles title for the past three years but has struggled to translate her domestic success to the international stage.
...
在新闻文章的上下文中,可以容易地假设第一和第二部分分别对应于标题和副标题。以下各段是文章的正文。查看示例数据,我们还发现这些片段由新行分隔,这些新行可用于拆分文本。
储存;储备
要编写一个自动运行每个文本文件的脚本,我们需要知道文本文件是如何存储的。因此,我们对目录的命名和组织感兴趣。潜在地,我们需要重新构造东西,这样我们就可以更容易地遍历文件。


文本文件的命名和组织
幸运的是, BBC 新闻数据集已经为自动化信息提取构建好了结构。从上面的截图中可以看出,文本文件根据它们的类型存储在不同的目录中。每个流派的名称也是相似的,由前导零(如果文件号小于 100)、文件号和“.”组成。txt”。
2.输出定义
基于数据理解步骤的见解,我们可以定义输出中应该包含哪些信息。为了确定输出,我们必须考虑上一步的学习,并考虑输出的潜在用例。
基于我们可能从文本文件中提取的信息,我提出了两种不同的机器学习训练用例:
- 文本分类(基于文本的类型预测)
- 文本生成(基于文本的标题或副标题生成)
为了满足这两个潜在用例的需求,我建议提取以下信息。

文本文件信息抽取的目标输出
我还将包括文本的长度(以记号的数量表示),以便以后更容易过滤更短或更长的文本。为了存储提取的数据,我建议使用制表符分隔值(.tsv)文件,因为逗号或分号可以出现在文本列中。
3.编码
由于前面的步骤,我们知道我们正在处理的数据以及在转换过程结束时我们想要输出什么样的信息。现在你可能知道了,我喜欢把任务分成更小的部分。编码步骤也不例外:)通常,我会将编码分成至少三个不同的部分,并将它们包装在单独的函数中:
- 读取和分割文件
- 提取信息
- 构建数据框
为了使这个新闻文章提取器可重用,我创建了一个实现这些函数的新类。
读取和分割文件
为了用 python 读取文件,我们需要由目录和文件名组成的相应路径。正如我们在数据理解步骤中观察到的,文件存储在它们对应的流派目录中。这意味着要访问一个文件,我们需要基本路径(对我来说是“数据”)、类型和名称。
如果文件存在,我们要读取它,用新的行字符(' \n ')分割它,过滤空字符串,并将剩余的文本部分作为列表返回。在文件不存在的情况下(例如,文件数大于可用文件数),我们希望返回一个空列表。如果文件不存在,我更喜欢这样做,而不是使用异常或不返回任何异常。
def read_and_split_file(self, genre: str, file_name: str) -> list:
text_data = list()
current_file = os.path.abspath(os.path.join('data', genre, file_name))
if os.path.exists(current_file):
open_file = open(current_file, 'r', encoding="latin-1")
text_data = open_file.read().split('\n')
text_data = list(filter(None, text_data))
return text_data
正如您在上面的代码中看到的,使用操作系统包。因此,我们需要导入这个包。
提取信息
为了提取文本文件的信息并为下一步做好准备,我建议对每种体裁都这样做。这意味着,我们循环遍历相应流派目录中的每个文件。通过保存一个 current_number 变量,我们可以用前导零来格式化文件名,然后通过调用上面实现的方法来读取和拆分文件。
如果返回的列表是空的,我们想要停止循环,因为这意味着我们到达了循环的末尾,并且在目录中没有任何新的文件。
否则,我们将阅读和拆分功能返回的信息添加到特定的数据容器中,如标题、副标题和文本。因为我建议在最终输出中也提供文本的令牌计数,所以我们可以使用 nltk 包对文本进行令牌化,并将令牌列表的长度添加到 token_counts 列表中。最后,我们将 current_number 加 1,继续提取下一个文件。
def extract_genre_files(self, genre: str) -> pd.DataFrame:
found = True
current_number = 1
titles = list()
subtitles = list()
texts = list()
token_counts = list()
while found:
file_name = "{:03d}.txt".format(current_number)
text_data = self.read_and_split_file(genre, file_name)
if len(text_data) != 0:
titles.append(text_data[0])
subtitles.append(text_data[1])
article_text = ' '.join(text_data[2:])
texts.append(article_text)
token_counts.append(len(nltk.word_tokenize(article_text)))
current_number += 1
else:
found = False
genres = [genre] * len(titles)
data = {'genre': genres, 'title': titles, 'subtitle': subtitles, 'text': texts, 'token_counts': token_counts}
data_frame = pd.DataFrame(data)
return data_frame
在完成类型文件的循环之后,我们基于存储在特定列表中的提取信息创建一个数据帧。与上一步类似,我们需要导入两个包(nltk 和 pandas)。还请确保您已经下载了 nltk 包的“punkt”数据,因为它是标记文本所必需的。
import nltk
# nltk.download('punkt')
import pandas as pd
构建数据框
在最后一步,我们必须在现有流派上创建一个循环,通过调用上面实现的方法提取每个流派的信息,连接每个流派的输出,最后将连接的数据帧保存为带有所需分隔符的 csv。
def transform_texts_to_df(self, name, genre_list, delimiter = '\t'):
article_df_list = list()
for genre in genre_list:
article_df_list.append(self.extract_genre_files(genre))
df = pd.concat(article_df_list)
df.to_csv(name, sep=delimiter)
return df
在实现了类及其方法之后,我们需要创建 ArticlecsvParser 类的一个实例,并调用 transform_texts_to_df 方法,方法是为生成的 CSV 提供所需的名称和一个包含所有流派的列表。就这样。
if __name__ == "__main__":
genre_list = ['business', 'entertainment', 'politics', 'sport', 'tech']
parser = ArticleCSVParser()
df = parser.transform_texts_to_df('bbc_articles.csv', genre_list)
print(df.head())
结论
在本文中,我展示了如何将文本文件转换成数据帧,并将其保存为 csv/tsv。为了对不同的数据集重用该类,只需创建一个从 ArticleCSVParser 继承的新类,并覆盖必须更改的方法。
您也可以在这个存储库中找到完整的代码和数据集。
我希望你喜欢并快乐编码!
如何转换熊猫数据框架中的变量
衍生和修改变量的方法,使其符合目的
无论是准备数据以提取洞察力,还是为模型设计特征,我认为处理数据的个人的基本技能之一是将数据可靠地转换为所需格式的能力。作为数据清理、数据准备、数据管理、数据操作、数据争论、数据丰富、数据预处理(咻!😅),经常需要将变量/列/特性转换成更合适的形式。换句话说,原始数据往往需要改头换面✨才能更有用。

在 Unsplash 上 Leone Venter 拍摄的照片
0.Python 设置🔧
我假设读者(👀是的,你!)可以访问并熟悉 Python,包括安装包、定义函数和其他基本任务。如果你是 Python 的新手,这个是一个入门的好地方。
我已经在 Jupyter Notebook 中使用并测试了 Python 3.7.1 中的脚本。在我们开始推导之前,让我们确保你有正确的工具。
⬜️确保安装了所需的软件包:熊猫和 nltk
我们将使用以下强大的第三方软件包:
- numpy: 数值计算库和
- 熊猫:数据分析库。
1.数据📦
为了使事情易于管理,我们将创建一个小的数据框架,它将允许我们在下一节中监视每个任务的输入和输出。在一个假设的世界里,我收集了一些弹珠🔮,让我们假设下面的数据框包含我拥有的每种弹珠的详细信息。(嘶!您可能需要将代码复制到 Jupyter 笔记本或代码编辑器中,以获得更好的格式。)
# Import packages
import numpy as np
import pandas as pd# Update default settings to show 2 decimal place
pd.options.display.float_format = '{:.2f}'.format# Create a small dataframe
df = pd.DataFrame({'name': ['bob 2012', 'Ava 2013', 'Aby 007', 'XYZ 8', 'GRZ x7', 'Boo VIII', 'Joy 2020'],
'p_date': ['2020-02-01', '2020-05-01', '2020-06-30', '2020-04-15', '2020-01-04', '2020-03-21', '2020-07-08'],
'count': [80, 30, 10, 60, 40, 20, np.nan],
'colour': ['pink', 'teal', 'velvet', 'pink', 'green', 'teal', 'pink'],
'radius': [1, 2, 3, 4, 5, 6, 7],
'unit': ['cm', 'inch', 'cm', 'cm', 'inch', 'cm', 'cm']})
df

1.1.数据字典📘
每行代表一种大理石。列定义为:
◼ 名称:每颗弹珠的名称(第一部分是型号名称,第二部分是版本)
◼ 购买日期:我购买一种弹珠的日期
◼ 计数:我拥有多少颗特定种类的弹珠
◼ 颜色:该种类的颜色
◼ 半径:该种类的半径测量值(对🙊)
◼ 单位:单位为半径
如果这没有多大意义,不要太担心,因为这只是一个玩具数据。
2.转换变量🎨
在这一节中,我们将看一些转换不同数据类型的例子。我们将创建包含转换的新列,以便原始变量不会被覆盖。在可能的情况下,提供了实现相同转换的替代代码以供参考。从这些备选方案列表中,希望您能找到一两个技巧,用于日常数据操作。现在,是时候改头换面了!✨
请注意,所示的一些方法的底层逻辑可以应用于任何数据类型。
2.1.转换数值变量
🔦类型:基于两个条件创建一个条件变量。
🔒任务: 半径不能直接跨种类比较,因为它们用不同的单位表示。让我们创建一个变量,以厘米为单位显示半径,以保持一致性。
🔑答:我们将调用新变量 radius_cm 。假设 1 英寸等于 2.54 厘米,我们可以将条件总结如下:
1)如果单位是“厘米”,则radius _ cm = radius 2)如果单位是“英寸”,则radius _ cm =2.54** radius*
我们可以使用下面的脚本创建半径 _ 厘米:
# Method using np.where
df['radius_cm'] = np.where(df['unit']=='inch', 2.54 * df['radius'], df['radius'])# ============== ALTERNATIVE METHODS ==============
## Method using np.select
# df['radius_cm'] = np.select([df['unit']=='inch'], [2.54 * df['radius']], default=df['radius'])## Method applying lambda function with if
# df['radius_cm'] = df[['radius', 'unit']].apply(lambda x: 2.54 * x[0] if x[1]=='inch' else x[0], axis=1)## Method A using loc
# df.loc[df['unit']!='inch', 'radius_cm'] = df['radius']
# df.loc[df['unit']=='inch', 'radius_cm'] = 2.54 * df['radius']## Method B using loc (works as long as df['radius'] has no missing data)
# df['radius_cm'] = df['radius']
# df.loc[df['unit']=='inch', 'radius_cm'] = 2.54 * df['radius']# Inspect results
df

💡快速提示:要在 Jupyter 笔记本中注释或取消注释代码,如果你还不知道的话,选择一段代码并使用[Ctrl/Cmd + /]快捷键。
_________________________________________________________________🔦类型:根据 2 个条件创建一个条件变量(分类)。
🔒任务:根据其半径创建一个描述大理石尺寸的变量,单位为厘米。
🔑答:我们将新变量的大小叫做。让我们将“大”定义为半径为 5 厘米或更高的弹珠,任何低于 5 厘米的则定义为“小”。所以条件是:
1)如果 radius_cm ≥ 5 那么 size = '大'2)如果 radius_cm < 5 那么 size = '小'
我们可以使用下面的脚本创建大小:
# Method applying lambda function with if
df['size'] = df['radius_cm'].apply(lambda x: 'big' if x>=5 else 'small')# Inspect results
df.sort_values('radius_cm')

为了避免重复,我没有为这个任务提供任何替代方法,因为第一个任务中的任何方法都可以在这里使用。
📌练习:通过引用第一个任务中显示的方法,尝试使用不同的方法进行相同的转换。
🔦类型:创建一个计算变量。
🔒任务:为弹珠计算球体体积。
➗公式:

🔑答:我们可以使用下面的脚本创建卷:
# Method using eval
pi = np.pi
df.eval("volume = 4/3 * @pi * radius**3", inplace=True)# ============== ALTERNATIVE METHOD ==============
## Method using simple operators
# df['volume'] = 4/3 * np.pi * df['radius']**3## Method using pandas wrappers
# df['volume2'] = df['radius'].pow(3).mul(4/3*np.pi)## Short description for pow, mul and a few other wrappers:
## add: for addition
## sub: for subtractaction
## mul: for multiplication
## div: for division
## pow: for exponential power (Tip: can use pow(0.5) or pow(1/2) for square root)# Inspect results
df

_________________________________________________________________🔦类型:将数值分割成等宽的条块(Discritise)。
🔒任务:创建一个变量,根据它们的计数将弹珠分成两个等宽的格子。
🔑答:我们将新变量称为 cut 。
◼最小计数 = 10
◼最大计数 = 80
◼范围计数 =最大—最小= 70
◼箱宽度=范围/箱数= 70 / 2 = 35
由于计数的范围是从 10 到 80 个弹珠,具有 2 个箱将意味着第一个箱是 10 到 45,第二个箱是 45 到 80 目前,我们已经用默认设置right=True将面元定义为包含最右边的边。这意味着,如果我们有 45 个弹珠,它将落入较低的箱子(即 10–45)。通过在下面的函数中添加right=False,我们可以很容易地改变这种行为,以排除最右边的边缘。也请参见我在下一个任务中的注释。
我们可以使用下面的脚本创建切割:
# Method using pandas.cut
df['cut'] = pd.cut(df['count'], bins=2, labels=['bin1', 'bin2'])# Inspect results
df[['count', 'cut']].sort_values('count')

🔦类型:将数值分割成大小相等的块(Discritise)。
🔒任务:创建一个变量,根据弹球的计数,将弹球分成两个大小相等的桶(即分位数)。
🔑答:我们将调用新变量 qcut 。
◼排序计数升序:10、20、30、40、60、80
◼ #记录= 6
◼ #分位数= 2
◼ #每个分位数的记录数= #记录数/ #分位数= 6 / 2 = 3
因为 c 计数中有 6 个非缺失值,具有相等大小的存储桶意味着第一个分位数将包括:10、20、30 和
值得注意的是,如果您有兴趣查看等宽或等大小箱子的确切分界点,一种方法是从函数中省去label参数。查看精确的截止点将使离散化时如何处理边缘上的点变得清晰。
所以无论如何…回到 qcut ,我们可以使用下面的脚本创建它:
# Method using pandas.qcut
df['qcut'] = pd.qcut(df['count'], q=2, labels=['q1', 'q2'])# Inspect results
df[['count', 'cut', 'qcut']].sort_values('count')

注意到切割和q 切割的区别了吗?
2.2.转换分类变量或字符串变量
🔦类型:基于 3+条件(组)创建一个条件变量。
🔒任务:创建一个变量,将粉色缩写为“PK”,将蓝绿色缩写为“TL”,将所有其他颜色(天鹅绒和绿色)缩写为“OT”。
🔑答:我们将新的变量叫做 colour_abr 。所以条件是:
1)如果颜色是‘粉色’那么颜色 _ ABR=‘PK’2)如果颜色是‘蓝绿色’那么颜色 _ ABR =‘TL’
3)如果颜色不是‘天鹅绒’就是‘绿色’那么颜色 _ ABR=‘OT’。
我们可以使用下面的脚本创建 colour_abr :
# Method using replace
df['colour_abr'] = df['colour'].replace({'pink': 'PK', 'teal': 'TL', 'velvet': 'OT', 'green': 'OT'})# ============== ALTERNATIVE METHODS ==============
## Method A using map
# mapping = {'pink':'PK', 'teal': 'TL', 'velvet': 'OT', 'green': 'OT'}
# df['colour_abr'] = df['colour'].map(mapping)## Method B using map (works as long as df['colour'] has no missing data)
# df['colour_abr'] = df['colour'].map({'pink':'PK', 'teal': 'TL'})
# df['colour_abr'].fillna('OT', inplace=True)## Method using nested np.wheres
# df['colour_abr'] = np.where(df['colour']=='pink', 'PK', np.where(df['colour']=='teal', 'TL', 'OT'))## Method using np.select
# df['colour_abr'] = np.select([df['colour']=='pink', df['colour']=='teal'], ['PK', 'TL'] , default='OT')## Method applying lambda function with nested ifs
# df['colour_abr'] = df['colour'].apply(lambda x: 'PK' if x=='pink' else ('TL' if x=='teal' else 'OT'))## Method using list comprehension
# df['colour_abr'] = ['PK' if x=='pink' else ('TL' if x=='teal' else 'OT') for x in df['colour']]## Method A using loc
# df.loc[df['colour'] == 'pink', 'colour_abr'] = 'PK'
# df.loc[df['colour'] == 'teal', 'colour_abr'] = 'TL'
# df.loc[df['colour'].isin(['velvet', 'green']), 'colour_abr'] = 'OT'## Method B using loc (works as long as df['colour'] has no missing data)
# df['colour_abr'] = 'OT'
# df.loc[df['colour'] == 'pink', 'colour_abr'] = 'PK'
# df.loc[df['colour'] == 'teal', 'colour_abr'] = 'TL'# Inspect results
df[['colour', 'colour_abr']].sort_values('colour_abr')

如果我们只是重命名类别而不是分组,我们也可以使用下面中的任何一种方法。cat 访问器除了上面显示的方法之外:
# Create a copy of colour and convert type to category
df['colour_abr'] = df['colour'].astype('category')# Method using .cat.rename_categories
df['colour_abr'].cat.rename_categories({'green':'GN', 'pink':'PK', 'teal':'TL', 'velvet': 'VT'}, inplace=True)# ============== ALTERNATIVE METHOD ==============
## Method using .cat.categories
## Make sure to get the order of the categories right
## Check the order with by running df['colour_abr'].cat.categories
# df['colour_abr'].cat.categories = ['GN', 'PK', 'TL','VT']# Inspect results
df[['colour', 'colour_abr']].sort_values('colour_abr')
有关的更多信息,请参见本文档。卡特彼勒存取器。🐱
🔦类型:解析字符串(从字符串中提取一部分)。
🔒任务:解析名称,这样我们就有了型号和版本的新列。
🔑答:我们现在将使用中的一个方法。str 访问器提取部件:
# Method using .str.split
df[['model', 'version']] = df['name'].str.split(' ', expand=True)# ============== ALTERNATIVE METHOD ==============
## Method applying lambda function
# df['model'] = df['name'].apply(lambda x: x.split(' ')[0])
# df['version'] = df['name'].apply(lambda x: x.split(' ')[1])# Inspect results
df[['name', 'model', 'version']]

🔦类型:连接或合并列(与上述任务相反)。
🔒任务:将模型(大写)和半径中的值合并到一个新列中。
🔑答案:我们现在将使用下面的脚本来连接:
# Method using + operator
df['mod_rad'] = df['model'].str.upper() + '_' + df['radius'].astype(str)# ============== ALTERNATIVE METHOD ==============
## Method using chained .str methods
# df['mod_rad'] = df['model'].str.upper().str.cat(df['radius'].astype(str), sep="_")# Inspect results
df

关于的更多信息,参见本文档。字符串存取器。🎻
2.3.转换日期时间变量
🔦类型:解析日期时间(从日期时间中提取一部分)。
🔒任务:提取一周的天,以及购买的年。
🔑答:我们现在将使用中的方法。dt 存取器提取零件:
# Convert type to datetime
df['p_date'] = pd.to_datetime(df['p_date'])# Method using .dt.day_name() and dt.year
df['p_dname'] = df['p_date'].dt.day_name()
df['p_year'] = df['p_date'].dt.year# Inspect results
df[['p_date', 'p_dname', 'p_year']]

_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _📌练习:尝试从 p_date 中提取月和日,并找出如何将 p_year 、 p_month 、 p_day 组合成一个日期。
关于的更多信息,参见本文档。dt 存取器。🕟
通过滚动左边的窗格到这里,您可以浏览前面讨论的访问器的可用方法。当心熊猫。Series.xxx.yyy 其中 xxx 可以替换为 cat 、 str 或 dt,yyy 是指方法。
3.把所有东西放在一起📃
现在我们将熟悉赋值,它允许我们一次创建多个变量。测试完成后,我们可以结合如下步骤:
# Convert type to datetime
df['p_date'] = pd.to_datetime(df['p_date'])# Derive variables
df = df.assign(radius_cm = np.where(df['unit']=='inch', 2.54 * df['radius'], df['radius']), # Referenced radius as radius_cm hasn't been created yet
size = list(map(lambda r, u: 'big' if ((u=='cm') & (r>=5)) | ((u=='inch') & (2.54*r>=5))
else 'small', df['radius'], df['unit'])),
volume = 4/3 * np.pi * df['radius']**3,
cut = pd.cut(df['count'], bins=2, labels=['bin1', 'bin2']),
qcut = pd.qcut(df['count'], q=2, labels=['q1', 'q2']),
colour_abr = df['colour'].replace({'pink': 'PK', 'teal': 'TL', 'velvet': 'OT', 'green': 'OT'}),
# If you find a way to combine the following 2 lines in 1 line inside assign(), feel free to teach me how
model = df['name'].str.split(' ', expand=True)[0],
version = df['name'].str.split(' ', expand=True)[1],
mod_rad = df['name'].str.split(' ', expand=True)[0].str.upper() + '_' + df['radius'].astype(str),
p_dname = df['p_date'].dt.day_name(),
p_year = df['p_date'].dt.year)# Inspect results
df

这个脚本看起来是不是有点忙乱?如果你一行一行地看,你会发现每一行都是我们从第 2 节中学到的代码的一个稍微变形的版本。
最后一点,在创建变量时,如果你犯了一个错误,你可以用正确的变量覆盖不正确的变量,或者使用⛔️:下面的脚本删除它
# Method A using del
del(df['var_name'])# ============== ALTERNATIVE METHODS ==============
## Method B using del
# del df['var_name']## Method using drop
# df.drop(columns='var_name', inplace=True)

您想访问更多这样的内容吗?媒体会员可以无限制地访问媒体上的任何文章。如果您使用 我的推荐链接成为会员,您的一部分会费将直接用于支持我。
谢谢你看我的帖子。我希望你已经学到了一些东西,✂️.如果你感兴趣,这里有我其他一些帖子的链接:
◼️️NLP 介绍—第 1 部分:Python 中的预处理文本
◼️NLP 介绍—第 2 部分:引理满足和词干化的区别
◼️NLP 介绍—第 3 部分:TF-IDF 解释
◼️NLP 介绍—第 4 部分:Python 中的监督文本分类模型
继续改造!保持,保持变换变量!🎤(跟着唱!🙋)
你继续改造!你继续,继续变换变量!🎶
继续改造!保持,保持变换变量!🎵
去转换你的数据…🎼
你猜到我的歌参考了吗?(提示: L[a-z]{4} )
再见🏃💨
从机械工程师过渡到机器学习工程师(或数据科学家)
我自己从一个物理机器的世界到另一个充满数字机器的世界的旅程故事。

图片提供:【https://www.core77.com
我有机械工程(ME)背景,因为我所有的学位都在我这里。在我获得学士学位后,当数据革命成形时,我正在机器人领域接受高等教育。当时人们更熟悉“大数据”这个词,而不是数据科学。然后,我迷上了机器学习,并从那时起开始将我的职业道路转向数据科学。我有一个机器人背景的良好开端,特别是在编程方面,所以我不必从零开始。尽管如此,我在尝试进入数据科学领域时遇到了许多障碍。
当时我很困惑,因为我不知道从哪里开始,我需要提高什么技能,我应该如何形成我的简历,等等。如果你正在阅读这篇文章,并且有类似的自我背景,我希望你会发现这很有帮助。
数据科学真的比机械工程好吗?
在我们决定为在线课程或其他不可逆转的行为投入时间和金钱之前,花点时间认真思考一下自己的动机可能是件好事。如果仅仅是因为这份 21 世纪最性感的工作,很有可能当热情消退后,你会在浪费投资和迷失在航行中后留下困惑和沮丧。
在我看来,任何两个工作之间的比较都是无关紧要的,因为任何领域都有其利弊。然而,作为一个在这两个领域都呆过一段时间的人,我想指出这两个领域的一些特点,仅仅反映我个人的观点。
机械工程:
- 医用电气技术往往在行业内长期有效。例如,卡尔曼滤波器、PID 控制等。那些现在都在用,可能比我还老。这样,你就不必在你的学术教育之后经常学习和更新自己,至少在理论知识方面。(持续学习对于保持领先地位并不重要。)
- 有限的工具集:有机器控制、硬件设计等标准工具的基准。同样,这又回到了第一点,你不必经常更新。
- 并不总是要求高等教育学位。这是我的主观观察。这是因为这样一个事实,为了正常工作,并被认为是一个高技能的机械工程师,一个人需要一个长期的工作经验,而不是在一个特定的 ME 浓度高等教育学位。通常情况下,我发现硕士学位足以让一个人在整个职业生涯中追求我。
- 有点无聊,因为大多数问题都有很好的定义和经过测试的解决方案。这项工作的主要部分通常是选择正确的解决方案和工具。此外,机器是一致的和可预测的,因此一次有效的解决方案往往会在很长一段时间内有效。
数据科学具有几乎完全相反的特征。
数据科学:
- 技术日新月异。此外,你不仅要应对 AI/ML 的变化,还要应对更广泛的软件工程领域,尤其是当你选择成为一名 ML 工程师的时候。
- 明面上是广阔的。我几乎从来没有发现任何 ML 应用程序仅仅通过一个设计就可行。你可能会说“好吧,这难道不是一件好事吗,我只需要找到至少一种做事的方法,并且坚持不懈地做下去”。不完全是。你不仅要成为这个领域的主管,你还得成为兼容。您选择的工具可能无法与团队的其他成员一起工作。
- 高等教育学位往往是必备的,至少能帮你通过简历筛选。根据这项研究,不到 30% 的数据科学家或 ML 工程师,没有硕士或博士学位。但更有趣的是,根据同一项研究,不到 20% 的数据科学家和不到 50% 的 ML 工程师拥有计算机科学学位。所以不要担心,如果你已经有了一个博士学位,你可能不需要再考一个。
- 可以充满惊喜,因为问题空间是巨大的,并且经常存在人类交互层。毕竟,不管人工智能应用程序是什么领域,它们最终都会暴露给人类用户。因为人类是不可预测的,所以你的解决方案的生命周期也是不可预测的。有些可能会持续到你离开公司的那一天,有些可能明天就需要重新设计。
- 普遍有吸引力的薪酬待遇。
我没有将上述特征分为优点和缺点,因为我认为这在很大程度上取决于个人。例如,如果你想要一个稳定的生活和一个常规的工作空间,那么我的无聊就不是一个缺点。
需要多长时间?
执行转换需要大量的时间和精力。作为任何一种投资,你可能要考虑多久可以收回利息。对某些人来说,这种转变可能是自然的,需要时间、数月甚至数年才能完成,正如本文中的。对于和其他来说,它可能会被顿悟的时刻所推动,并被一个仓促的计划所执行。以我的经验来看,如果你已经有了一份全职工作,通常需要几年时间,如果你在读大学,时间可能会短一点。主要是因为你需要空闲时间去发展新的技能。同样,这也很大程度上取决于你已经有什么,所以这个时间框架只是供参考。
怎么做过渡?
我很喜欢“过渡”这个词。你从一个状态过渡到另一个状态,而不是“跳到”或“开始一个新的”。换句话说,通过认识到你拥有的 ME 背景,并战略性地开发数据科学中所需的新技能,你将获得成功。
由于数据科学中的技能通常分为 3 个主要类别:数学/统计、领域知识和编程,因此我将相应地构建这一部分,每个部分都有一个“可重用性分数”,我认为在过渡中我们可以重用 ME 背景有多难。
数学/统计
可重用性评分:易。
如果有一件事是我对自己的背景最有信心的,那就是对数学和统计学的严谨和扎实的理解。回顾你的学业成绩单,数一数你学过的数学相关课程,你可能会感到惊讶。挑战在于,每当我和我的朋友谈论在大学学习数学时,我都会得到这样的回答:“是的,是的,我仍然在等待有一天我可以在我的现实生活中使用格林定理”。不,你不会。但那不是学习数学的目的。它给你“精神食粮”。它给你解决问题的动力。
我给你举个例子。如果我让你计算这个函数的一阶导数。简单,就是y = 2x。但是,你如何利用这种技能赚钱呢(除了给一个高中生做家教)?
这里有个窍门,重要的不是数字和数学公式,而是数学背后的直觉。一阶导数告诉你函数增加或减少的速度。因此,在函数为常数的区域,它在 0 附近,而在其他地方不为零。让我们看看我们能用这种直觉做些什么。
现在,假设你是一名工程师,一家汽车制造厂的制造经理向你抱怨说,在让机器出厂之前,他们花了一大笔钱请一名测试人员来数机器上的螺丝数量。如果您能够自动化这项任务,那么在退出测试人员角色之后,您将获得 20%的利润。现在我们谈论的是真正的钱。

你拍一张测试者的照片。图片提供:https://en.wikipedia.org/wiki/Sobel_operator
你拍一张测试者的照片。你观察到螺丝和机身颜色不一样,那么如果有办法让它在照片中“弹出来”,你就可以有一个程序来“数”它们。因为你记得我们可以使用一阶导数来寻找函数显著变化的区域,所以你意识到这些螺钉的边缘应该具有非零的一阶导数。

应用一阶导数滤波后的相同照片。图片提供:【https://en.wikipedia.org/wiki/Sobel_operator
对图像进行一阶导数滤波,瞧!螺丝钉像星星一样明亮。
现在你可以开发一个计算机视觉系统来代替(可怜的)测试员,并收取你的份额。所有这些都是基于基本的一阶导数直觉。
再举个例子:如果我让你写下概率链规则,那就是小菜一碟:P(A,B)=P(A|B)*P(B)。但是这有什么用呢?
假设有一天,你的高级经理问了这样一个问题:“今年我们花费 2000 美元恢复 XYZ 服务器的可能性有多大?你知道,如果它倒下了。”这是一个数据科学家应该能够回答的有效且常见的业务问题。你会回过头来定义:
- 事件 A:我们必须在服务器 XYZ 上花费 2000 美元。
- 事件 B:服务器 XYZ 关闭。
因此,P(A|B)是你必须支付 2000 美元来修复服务器 XYZ 的概率,假设它确实坏了,P(B)是服务器坏了的概率。确定这些概率的一个快速而又肮脏的方法可能是:
- P(A|B):你把公司过去所有关于安装服务器 XYZ 的费用的发票翻出来,画一个柱状图,就截止到 2000 美元,假设曲线下的面积是 2%,即安装费用超过 2000 美元的概率。
- P(B):你翻查公司过去所有的服务器日志,找出所有有宕机的年份,然后除以总年数就可以了。假设在 10 年期间,它在 2 年内被破坏了两次,因此是 20%。
(请注意,异常检测是一个巨大的主题,这只是一个过于简化的解决方案。但我认为这足以说明问题。)
所以,你的公司今年必须支付 2000 美元来修复服务器的最终概率是0.2 * 0.02 = 0.004,或者只有 0.4%。
任何学过概率统计 101 的人都可以编写链式法则,但是只有能够“翻译”它来回答商业问题,你才能得到这份工作。
领域知识
可重用性评分:中等。
与其他类型的工程不同,在其他类型的工程中,工程师的技能或工作成果直接产生最终用户购买的产品或服务,数据科学通常不直接创造价值。这就是领域知识至关重要的原因。数据科学家需要彻底了解业务,然后才能将人工智能模型应用于业务。
通常,我看到人工智能模型通过以下设置来创造利润:
- 取代由人类完成的单调乏味的任务(生产成本更低)。例如 RPA、自动化流程控制、需要对事物进行分类的任务等。
- 提高生产率(生产更快或销售更好)。例如过程优化器、AI 调度器、劳动力优化、推荐系统等。
- 基于发现的可行见解,修改/创建新的业务模式。例如,基于调查数据发现客户使用产品的新方式的分析,从而指导新的广告活动。
- 防止潜在损失。例如安全 AI、流失预测等。
这可能是显而易见的,但所有这些设置都回答了一个问题:
如何利用提出的 AI 模型盈利?
可以通过美元计数或其他 KPI 来衡量。当一个人第一次开始研究数据科学时,他们在第一个项目中可能会有很多顾虑。它可能是方法的新颖性、模型性能、计算复杂性、最新技术水平或模型评估等。对于整个解决方案的工作来说,所有这些都很重要。但是他们都应该以这个问题为导向。这听起来可能很实用,但实际上是。在任何事情发生之前,这个问题在步骤 0 应该有一个满意的答案。
这种转变有点困难,因为对于一个机械工程师来说,你不必真的关心价值是如何创造的。但你所拥有的,是如何设计和优化流程 : 机制设计、流程控制、热力学、奥托循环、顶点设计等训练有素的心态。都是过程!在某种程度上,商业模式本身就是一个过程。有更多的不确定性,因为人类参与其中(人类是随机的),但是你已经被训练在过程中模拟不确定性!
然而,你所带来的(你的面向过程的心态)只是交通工具,你仍然需要燃料来运行。你有没有想过你住的地方附近的美食街是怎么赚钱的?或者你穿的这件衬衫是在地球另一端的一个国家制造的,然后被运到你那里去买?只要把你的想法画在纸上,你会感到惊讶的。你有“良好的库存”,而不是“储气库”(只是一种对气体容器的花哨的热力学行话);你有了“数据提取管道”,而不是“流体导管”;不是“流量”,而是“数据传输速率”。物理定律是普遍适用的,它们在数字世界也同样适用。
编程;编排
可重用性评分:数据科学家:中等,ML 工程师:硬。
我区分这两者,因为在我看来,ML 工程师是可以做建模的核心软件工程师。因此,编程的能力水平需要达到极致。这种区别当然是相对的,因为肯定有数据科学家编写生产代码。
这是最需要花时间去掌握的部分。“编程”在这里有点用得不好,因为它不包括整个软件工程实践:网络、API、CI/CD、Dockerization 等等,你能想到的。不幸的是,ML 工程师之路需要他们(这里我假设数据科学家的背景包含在分析和可视化任务中,换句话说,就是那些不必编写生产代码的人。还是那句话,主观)。
TDS 上有无数的在线课程和精彩的文章,告诉你应该如何提高这一技能,所以我不会再重复了。我只想分享一点:封装的伟大概念,我指的不仅仅是 OOP。这对我的学习帮助很大。
您可以孤立地学习几乎任何软件工程工具/技能,并且一个完整的软件应用程序可以被分解成粒度模块,您可以分别学习每个模块。这太美了,我在机械工程专业做不到。这就产生了一个极其有效的学习策略:分而治之。我有很多文件夹,比如:python-practice、spark-practice、Docker-practice、gRPC-practice、k8s-practice等等。每个不超过 5 个文件包含该工具最基本的示例代码。一旦你掌握了足够数量的这些“乐高积木”,设计 solution architect 将充满乐趣。你可以自由决定你的人工智能模型如何与其他模块互动。
请注意,将所有模块组合在一起以形成一个功能应用程序需要付出巨大的努力,否则,软件工程师就不会存在,因为人们只需选择模块,一个“通用”程序就会编译出所需的应用程序,就像你在麦当劳自助点餐亭订购一样。这是一个你可以用来快速学习的有效方法。
当我离开的时候,有两个工具我不得不花相当多的时间来学习: git 和 SQL 。这很有意义,因为在我这里,你不必管理代码,也不必管理关系数据库。对当时的我来说,Dropbox 足以分享代码,后跟前缀如“backup_20080202”的文件夹足以进行版本控制,Excel 可以保存表格。生活简单又容易。所以我必须用 git 和 SQL 来升级游戏。
git :我一直认为通过了解一些 git 命令行,我可以流利地使用 git,直到我需要和我的同事合并代码。因此,我推荐的最佳实践是和你的朋友一起做一些结对编程项目:课程项目、爱好项目,或者和你的同事一起开发一个小功能。你越早这样做,你就越早意识到 git 不仅仅是版本控制,你会过得越好。
SQL :在我看来,你用的是什么版本的 SQL 并不重要,只要你理解了SQL 中所有不同种类的连接并且知道如何检查结果,你就是优秀的。为什么?因为语法错误很容易发现,所以平台会说“错误:不能将字符串类型转换为整数。”,你的查询会失败,你会知道的。但是连接是无声的杀手。如果您使用了错误的连接类型,或者您没有仔细检查就相信了您的查询结果,那么很可能只有当您的经理叫您进来并质疑您的“荒谬”图表时,您才会发现这一点。例如,如果在不检查重复项的情况下将一个销售表与其他一些产品信息表联接起来,那么联接后的表将会有重复的行。换句话说,一个售出的产品可能会出现不止一次,在你的视觉化视图中,销售量表明你的公司是财富 500 强,而不是一个创业公司。没有人会认真对待你的陈述。
最后的话
简而言之,我们讨论了:
- 数学/统计:你已经从教育中获得了所有需要的基础训练。挑战在于将这些知识运用到解决现实世界的问题中。
- 领域知识:理解人工智能应用的本质目的是创造利润有助于你在看待商业模式时将镜头转向不同的角度。毕竟都是过程。
- 编程:分而治之。利用好封装的性质快速学习。
这些东西在我的旅程中给了我很大的帮助,我希望它们对你也有用。
在这个数据领域工作在很多方面都是有益的,但对我来说,这是当一个模型被部署到生产中并看着它运行时我的喜悦,就像当我建造一个机器人并第一次看到整个东西运行时一样。
我们毕竟是工程师。
“像伟大的工程师一样进行机器学习,而不是像你不是伟大的机器学习专家一样。”
快乐(机器)学习!
使用 googletrans 库翻译熊猫数据框

谷歌翻译标志
Googletrans 是一个免费的 python 库,它使用了 Google Translate API 。在本文中,我们将解释如何使用这个库来翻译 Python 中的字符串和数据帧。
翻译字符串
Googletrans 是一个第三方库,我们可以使用 pip 来安装。安装完库之后,我们导入模块 googletrans。
第一步是创建一个翻译器对象。然后,我们使用方法 translate ,将我们想要翻译的字符串作为第一个参数传递。该方法返回一个对象,其属性为。文本提供如下翻译后的字符串:
可以使用 src 和 dest 参数指定源语言和目的语言。如果没有提供,googletrans 会尝试检测该语言并将其翻译成英语。下面,我们把前面的字符串 Hola Mundo (西班牙语)翻译成意大利语和德语。
我们可以使用 查询支持的翻译语言。语言属性。该属性返回一个字典,其中包含了 googletrans 中可用的语言。
如前所述,translate 方法返回一个翻译后的对象。该对象具有以下属性:

翻译方法属性
到目前为止,我们已经将一个字符串翻译成了英语、意大利语和德语。现在,我们想完成一项更具挑战性的任务:翻译一个完整的数据帧。
翻译数据帧
我们要翻译的数据框可通过以下链接下载:
[## 马德里阿云达芒托群岛的门户网站
马德里大医院的门户网站——移民。西班牙语课程中的人物
datos.madrid.es 是 Madrid s 数据服务,包含近 500 个数据集,涵盖商业、交通、旅游、文化等广泛话题。目前,越来越多的欧洲城市提供开放的数据门户,允许公司、市民、研究人员和其他公共机构使用这些数据。数据集通常以该国语言提供。就datos . Madrid . es而言,大部分数据集都有西班牙语版本。
在本文中,我们翻译了包含 2018 年第一学期期间在市政办公室参加外国人西班牙语课程的学生的信息的数据集。该数据集包括关于学生的信息,如他们参加的课程级别、性别、年龄、国籍、教育水平和行政地位。
首先我们从datos . Madrid . de下载 csv 文件。然后,我们使用Pandas . read _ CSV函数将其加载到 Pandas 数据框中,并使用 pandas 可视化前 5 行。data frame . head方法。

数据集包含 8 栏:(1)级别,(2)性别,(3) Edad,(4)行政状况,(5)国家地理,(6)地区地理,(7)专业类别,和(8)研究级别。
现在,我们从翻译开始!首先,我们使用 熊猫来翻译列名。DataFrame.rename 功能如下:
使用 的熊猫。DataFrame.columns 的属性,我们可以检查翻译是否正确进行。在本文中,我们只是翻译列名,但还需要进一步的数据清理(用下划线代替空格,用小写字母代替大写字母)。
翻译完列名后,我们翻译其余的数据(单元格值)。首先,我们创建一个字典,其中的关键字是英语术语,值是西班牙语的原始术语。
在改变数据框架之前,我们检查由 googletrans 所做的翻译。正如我们所观察到的,有些术语翻译不正确(例如 A2 或 A2 DELE)。在这种情况下,我们以下列方式手动更改它们:
现在,我们可以通过使用熊猫来修改数据帧。DataFrame.replace** 函数,使用之前创建的字典作为函数的输入。**

英语数据框架
正如我们所观察到的,整个数据都被翻译了💪工作完成:)
有趣的读物
- https://pypi.org/project/googletrans/
- http://zetcode.com/python/googletrans/
- https://www . code project . com/Tips/1236705/How-to-Use-Google-Translator-in-Python
感谢阅读!!🍀
用 60 行 Python 语言翻译任意两种语言
作为一名 NLP 工程师,我很快就要失业了😅

介绍
我记得当我在 2015 年建立我的第一个 seq2seq 翻译系统时。从处理数据到设计和实现模型架构,这是一项繁重的工作。所有这些就是把一种语言翻译成另一种语言。现在模型变得更好了,围绕这些模型的工具也变得更好了。HuggingFace 最近将来自赫尔辛基大学的 1000 多个翻译模型整合到他们的变形金刚模型动物园中,它们很不错。制作这个教程让我感觉很糟糕,因为构建一个翻译系统就像从变形金刚库中复制文档一样简单。
总之,在本教程中,我们将制作一个转换器,它将自动检测文本中使用的语言并将其翻译成英语。这很有用,因为有时您将在一个包含来自许多不同语言的文本数据的领域中工作。如果你只用英语建立一个模型,你的性能会受到影响,但是如果你能把所有的文本标准化为一种语言,你可能会做得更好。
非常有才华的 Chema Bescós 将这篇文章翻译成西班牙语,如果英语不是你的第一语言,你可以在这里找到。
💾数据💾
为了探索这种方法的有效性,我需要一个包含许多不同语言的小文本的数据集。来自 Kaggle 的 Jigsaw 多语言有毒评论分类挑战非常适合这一点。它有一个超过 223,000 条标注为有毒或无毒的英语评论的训练集,以及一个验证集中来自其他语言的 8,000 条评论。我们可以在英语训练集上训练一个简单的模型。然后使用我们的翻译转换器将所有其他文本转换成英语,并使用英语模型进行预测。
看一下训练数据,我们看到在六个类别中的每一个类别中都有大约 22 万个英语*示例文本。

训练数据的视图
事情变得有趣的地方是验证数据。验证数据不包含英语,而是包含来自意大利语、西班牙语和土耳其语的示例。

验证数据的示例
🕵️♀️确定了🕵️♀️语
自然地,将任何语言标准化为英语的第一步是识别我们未知的语言是什么。为此,我们求助于来自脸书的优秀的快速文本库。这个图书馆里有很多令人惊奇的东西。这个图书馆名副其实。它真的很快。今天我们只使用它的语言预测能力。
识别任意字符串是哪种语言就这么简单。我对验证集进行了测试,以了解模型的表现。坦率地说,我对它开箱即用的表现感到惊讶。在 8000 个例子中,Fasttext 只错分了 43 个。在我的 MacbookPro 上运行也只用了 300 毫秒。从这两方面来看,这都是非常荒谬的🍌。如果你仔细观察,你会发现在一些西班牙语的错误预测中,它预测了加利西亚语或欧西坦语。这些语言在西班牙及其周边地区使用,源自西班牙。因此,某些情况下的预测失误并没有我们想象的那么糟糕。

Fasttext API 的语言标识在我们的验证集上的混淆矩阵。
🤗变形金刚(电影名)🤗
既然我们可以预测一个给定的文本是哪种语言,那么让我们看看如何翻译它。来自 HuggingFace 的变形金刚库一直让我惊叹不已。他们最近在他们的模型动物园中增加了超过 1000 个翻译模型,每一个都可以用来翻译大约五行代码中的任意文本。我几乎是直接从文档中窃取的。
lang = "fr"
target_lang = "enmodel_name = f'Helsinki-NLP/opus-mt-{lang}-{target_lang}'# Download the model and the tokenizer
model = MarianMTModel.from_pretrained(model_name)
tokenizer = MarianTokenizer.from_pretrained(model_name)
# Tokenize the text
batch = tokenizer([text], return_tensors="pt", padding=True)
# Make sure that the tokenized text does not exceed the maximum
# allowed size of 512
batch["input_ids"] = batch["input_ids"][:, :512]
batch["attention_mask"] = batch["attention_mask"][:, :512]# Perform the translation and decode the output
translation = model.generate(**batch)
tokenizer.batch_decode(translation, skip_special_tokens=True)
基本上,对于任何给定的语言代码对,你都可以下载一个名为Helsinki-NLP/optus-mt-{lang}-{target_lang}的模型,其中lang是源语言的语言代码,target_lang是我们要翻译到的目标语言的语言代码。如果您想将韩语翻译成德语,请下载Helsinki-NLP/optus-mt-ko-de模型。就这么简单🤯!
我对文档做了一点小小的修改,将 input_ids 和 attention_mask 窗口设置为只有 512 个令牌长。这很方便,因为大多数转换器模型最多只能处理 512 个令牌的输入。这可以防止我们弄错较长的文本。如果你试图翻译很长的文本,这会引起问题,所以如果你使用这段代码,请记住这个修改。
sci kit-学习管道
下载完模型后,让我们轻松地将它整合到 sklearn 管道中。如果你读过我以前的帖子,你可能知道我喜欢 SciKit 管道。它们是构成特征化和模型训练的一个很好的工具。考虑到这一点,让我们创建一个简单的转换器,它将接受任何文本数据,预测它的语言,并翻译它。我们的目标是通过运行以下命令来构建一个与语言无关的模型:
from sklearn import svm
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.pipeline import Pipelineclassifier = svm.LinearSVC(C=1.0, class_weight="balanced")
model = Pipeline([
('translate', EnglishTransformer()),
('tfidf', TfidfVectorizer()),
("classifier", classifier)
])
这个管道将把任何文本中的每个数据点翻译成英语,然后创建 TF-IDF 特征,然后训练一个分类器。这个解决方案使我们的特性与我们的模型保持一致,并使部署更容易。通过在一个管道中完成特征、训练和预测,它还有助于防止特征与模型不同步。
现在我们知道了我们要努力的方向,让我们来建造这个英语变压器吧!上面的大部分代码你已经看到了,我们只是把它们拼接在一起。😄
- 第 15–18 行—确保 fasttext 模型已经下载并可以使用。如果不是,它会将其下载到 temp
/tmp/lid.176.bin。 - 第 24 行——建立了可以用赫尔辛基浪漫模型翻译的语言代码。该模型可以很好地处理大量的语言,并且可以为我们节省大量的磁盘空间,因为我们不必为每种语言下载单独的模型。
- 第 27–30 行—定义我们要翻译的语言。我们想要创建一个允许的语言列表,因为每个模型大约 300MB,所以如果我们下载 100 个不同的模型,我们将得到 30GB 的模型!这限制了语言的设置,这样我们就不会在磁盘空间不足的情况下运行系统。如果你想翻译这些代码,你可以将ISO-639–1代码添加到这个列表中。
- 第 32–38 行—定义一个函数来执行快速文本语言预测,就像我们上面讨论的那样。你会注意到我们也过滤掉了
\n字符。这是因为 Fasttext 自动假定这是一个不同的数据点,如果它们存在,就会抛出一个错误。 - 第 41 行——定义了转换,也是魔法发生的地方。这个函数将把任何语言的字符串列表转换成英语的字符串列表。
- 第 48–50 行—检查当前字符串是否来自我们的目标语言。如果是的话,我们会将其添加到我们的翻译中,因为它已经是正确的语言了。
- 第 54–55 行—检查预测的语言是否能被浪漫模型处理。这有助于我们避免下载一堆额外的语言模型。
- 第 56–65 行—应该看起来很熟悉,它们只是拥抱脸文档的翻译代码。这个部分下载正确的模型,然后对输入文本执行翻译。
就是这样!超级简单,它可以处理任何事情。需要注意的是,这段代码被写得尽可能易读,而且非常慢。在这篇文章的最后,我包括了一个更快的版本,批量预测不同的语言,而不是为每个数据点下载一个模型。
🤑结果🤑
我们现在可以使用以下工具训练和测试我们的模型:
from sklearn import svm
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.pipeline import Pipelineclassifier = svm.LinearSVC(C=1.0, class_weight="balanced")
model = Pipeline([
('translate', EnglishTransformer()),
('tfidf', TfidfVectorizer()),
("classifier", classifier)
])
model.fit(train_df["comment_text"].tolist(), train_df["toxic"])
preds = model.predict(val_df["comment_text"])
在英文训练集上训练一个简单的 TF-IDF 模型,并在验证集上进行测试,我们得到了 0.15 的有毒评论 F1 分数!太可怕了!预测每一类有毒物质的 F1 值为 0.26。使用我们新的翻译系统预处理所有输入并翻译成英语,我们的 F1 变成了. 51。这几乎是 4 倍的提高!

翻译和未翻译模型之间的性能比较
请记住,这里的目标是简单的翻译,而不一定是 SOTA 在这项任务上的表现。如果你真的想训练一个有毒评论分类模型,获得良好的性能来微调像 BERT 这样的深度 transformer 模型。
如果你喜欢这篇文章,可以看看我的另一篇关于使用文本和 SciKit-Learn 的文章。感谢阅读!: )
BERT 可以在许多 NLP 任务中为您提供最先进的结果,并且只需要几行代码。
towardsdatascience.com](/build-a-bert-sci-kit-transformer-59d60ddd54a5) [## Good Grams:如何为您的问题找到预测性的 N-Grams
找出哪些单词对你的问题有预测性是很容易的!
towardsdatascience.com](/good-grams-how-to-find-predictive-n-grams-for-your-problem-c04a5f320b39)
更快的变压器
正如所承诺的,这里是一个更快版本的英语变压器的代码。在这里,我们按照预测的语言对语料库进行排序,并且对于每种语言只加载一次模型。在此基础上使用 transformer 对输入进行批处理,可以使速度更快。
平移不变性与平移等价性
平移不变性和平移等方差经常被混淆为同一事物,但它们是 CNN 的不同属性。要了解不同之处,请阅读下文。

卷积神经网络已经成为基于图像和视频的任务(如分类、定位、分割等)的首选架构。他们在以前被认为使用基本图像处理技术很难完成的任务中表现出了超人的水平。它使分类任务变得相对容易执行,而不需要像 CNN 变革之前那样向模型输入手动精选的特征。
卷积神经网络的灵感来自诺贝尔奖得主科学家 Hubel 博士和 Wiesel 博士的工作,他们展示了大脑中视觉皮层的工作。他们在一只部分麻醉的猫的视觉皮层中插入微电极,使她不能移动,并在它的视网膜上移动一条亮线。在这个实验中,他们注意到了以下场景:
- 当这条线位于视网膜上的特定位置时,神经元就会放电。
- 神经元的活动根据线的方向而变化。
- 神经元有时只有在线路向特定方向移动时才会放电。
这个经典实验展示了视觉皮层是如何以一种分层的方式处理信息,提取越来越复杂的信息。他们表明,在视觉皮层中有一个代表视野的地形图,,附近的细胞在这里处理来自附近视野的信息。这给了 CNN 的稀疏交互的概念,在这里,网络聚焦于本地信息,而不是获取完整的全球信息。这一特性使得 CNN 在图像相关任务中提供了最先进的性能,因为在图像中,附近的像素比远处的像素具有更强的相关性。此外,他们的工作确定了视觉皮层中的神经元以精确的结构排列。具有相似功能的细胞被组织成列,微小的计算机器将信息传递到大脑的更高区域,在那里形成视觉图像。这类似于 CNN 架构的设计方式,其中较低层提取边缘和其他公共特征,而较高层提取更多特定于类别的信息。总之,他们的工作揭示了视觉皮层神经元如何对图像特征进行编码,图像特征是帮助我们建立对周围世界的感知的物体的基本属性。

图 1 : Hubel 和 Weisel 的实验直观解释。

图 2:
卷积神经网络提供了优于传统全连接层的三个基本优点。首先,它们具有稀疏连接,而不是完全连接的连接,这导致参数减少,并使 CNN 有效地处理高维数据。第二,权重共享发生在相同的权重在整个图像上共享的情况下,导致减少的存储器需求以及平移等变(稍后将解释)。第三,CNN 使用了一个非常重要的二次采样或汇集的概念,其中最突出的像素传播到下一层,丢弃其余的像素。这提供了固定大小的输出矩阵,这通常是分类和对平移、旋转的不变性所需要的。
平移等方差:
平移等方差或仅等方差是卷积神经网络的一个非常重要的属性,在卷积神经网络中,图像中对象的位置不应该是固定的,以便它被 CNN 检测到。这仅仅意味着如果输入改变,输出也会改变。准确地说,如果 f(g(x)) = g(f(x)),则称函数 f(x)与函数 g 等变。如果我们有一个函数 g,它将图像的每个像素向右移动一个像素,即 I'(x,y) = I(x-1,y)。如果我们对图像应用变换 g,然后应用卷积,结果将与我们对 I '应用卷积,然后对输出应用平移 g 的结果相同。当处理图像时,这仅仅意味着如果我们将输入向右移动 1 个像素,那么它的表示也将向右移动 1 个像素。
CNN 的平移等价性是通过权重共享的概念实现的。由于在图像之间共享相同的权重,因此,如果对象出现在任何图像中,则不管它在图像中的位置如何,它都将被检测到。该属性对于诸如图像分类、对象检测等应用非常有用,在这些应用中,对象可能多次出现或者对象可能处于运动中。
卷积神经网络对于某些其他变换,如图像的缩放或旋转变化,并不是自然等变的。需要其他机制来处理这种转换。

图 3 。由于翻译等变性质,检测到各种 cat 实例。

图 4。在同一帧中检测多只狗时看到的平移等方差特性。
平移不变性:
平移不变性经常与平移等价性混淆,许多人,甚至是专家都分不清两者的区别。
平移不变性使 CNN 具有平移不变性。平移不变性意味着,如果我们平移输入,CNN 仍然能够检测输入所属的类别。
平移不变性是汇集操作的结果。在传统的 CNN 架构中,有三个阶段。在第一阶段,该层对输入执行卷积运算以给出线性激活。在第二阶段,产生的激活通过非线性激活函数,例如 sigmoid、tanh 或 relu。在第三阶段,我们执行池操作来进一步修改输出。
在池化操作中,我们用附近输出的汇总统计来替换某个位置的 convnet 的输出,例如最大池化情况下的最大值。因为在最大池化的情况下,我们用最大值替换输出,因此即使我们稍微改变输入,也不会影响大多数池化输出的值。在不需要物体的精确位置的情况下,平移不变性是一个有用的属性。例如,如果你正在建立一个检测人脸的模型,你需要检测的只是眼睛是否存在,它的确切位置是不必要的。而在分割任务中,需要精确的位置。
池化的使用可以被视为添加了一个强先验,即该层学习的函数必须对平移不变。当先验正确时,可以大大提高网络的统计效率。

图五。显示平移不变性,尽管输入向右移动,但表示也移动。

图六。平移不变性的例子
平移不变性和平移等方差的属性在一种称为数据扩充的技术中得到部分利用,当我们拥有较少的训练数据或希望在更丰富的数据集上进行模型训练时,这种技术会很方便。在数据扩充中,我们对从训练集中随机采样的每批数据应用不同的变换,如旋转、翻转、缩放、平移等,并将其提供给模型,以使其对变换更加鲁棒并提高性能。

图 6: 所示的数据增强技术
结论:
卷积神经网络正在解决以前被认为无法解决的各种挑战,并且在大多数情况下,击败了人类水平的性能,正如在 ImageNet challenge 中看到的那样,Resnet 比人类表现得更好。使 CNN 如此伟大的概念并不复杂,但非常直观,有逻辑性,易于理解。
我希望你喜欢这篇文章,如果你有任何疑问、建议或要求,请在下面留下你的评论,或者通过 twitter 或 LinkedIn 与我联系。
参考资料:
- 伊恩·古德菲勒、约舒阿·本吉奥和亚伦·库维尔的深度学习书籍。
- 人类视觉系统架构图 2 取自 knowingneurons.com。
- 为什么等方差优于过早不变性 Geoffrey Hinton(图 4)。
- 图 6 来自 itutorials.com。
新冠肺炎投影模型的透明度、再现性和有效性
没有停滞迹象的模型的指数增长
预测传染病传播的模型在为全世界的公共卫生政策提供信息方面一直发挥着重要作用。但是在历史上,公众和媒体从来没有像新冠肺炎·疫情事件那样敏锐地意识到这些模型的存在和重要性。研究人员和决策者正依靠这些模型在国家和地方层面进行规划。此外,公众正在敏锐地跟踪趋势,希望这些模型可以指导他们何时生活将恢复正常,无论这意味着回到工作和学校,计划下一个假期,还是只是剪头发。在疫情第一阶段过后不久,需要进行快速预测,现成的模型受到了媒体和政府官员的关注。现在越来越清楚的是,这种疫情将在未来几个月甚至几年继续扰乱生活,关于锁定、社交距离和资源分配的关键决策将继续严重依赖其中一些模型。新的模式不断涌现,各种统计数据和预测在社交媒体平台上迅速传播,一些人将此称为“流行病”本身(见卫报文章)。很难知道这些预测中哪些是可靠的,而且来自不同模型的信息经常相互矛盾(见《洛杉矶时报》文章),在危机时期制造了一种进一步的不确定感。
我们相信,在未来,如果可用的预测种类更少,社会将得到更好的服务,但可用的预测将因其可靠性而受到更多审查。这促使我们探索一些可用的模型,包括两个最有影响力的模型,使用三个标准 (1)透明度(2)再现性和(3)有效性 。接下来,我们描述我们在这方面的经验和教训,希望在未来,它可以开始建立一个更好的评估未来模型的框架。我们使用了来自意大利和纽约的数据,这些数据可以在 Github CSSEGISandData 新冠肺炎和 Github NYTimes 新冠肺炎-data 网站上获得,这两个地方的疫情疫情严重,但每天的死亡人数已经达到峰值。我们使用不同数量的数据追溯到峰值,然后检查该模型在预测未来短期趋势方面的表现,并与截至 2020 年 5 月 1 日的观察模式进行比较。
两种有影响力的死亡预测模型的比较评价
可以说有两种模型,一种是由伦敦帝国理工学院(伦敦)【ICL】开发的,另一种是由美国、英国和欧洲最有影响力的政策制定机构(IHME)华盛顿大学(西雅图)开发的。特别是,在三月中旬左右,帝国理工学院的模型预测,由于新冠肺炎没有采取任何缓解措施,英国将有 50 万人死亡,美国将有 200 万人死亡。这些预测引起了决策者对实施严格封锁和社会距离措施的紧迫性的关注。IHME 模型从 3 月下旬开始在美国受到关注,尤其是在白宫每日新闻简报开始引用其结果之后。除了死亡之外,该模型还可以预测住院和 ICU 入院,因此很可能已经用于州级资源规划。这一模式最近受到了传染病专家的批评,但它仍然被政府和媒体人员广泛引用。**
透明度和再现性
帝国理工学院模型的源代码通过 GitHub 公开。在 11 个欧洲国家应用该模型的代码是可用的,发布的结果是可复制的。然而,对在其他数据集上应用该方法感兴趣的用户将需要更多关于数据处理细节和参数设置的指导性指导。一份可用的报告以透明的方式描述了基础方法,并为各种模型选择和假设提供了充分的理由。该方法使用贝叶斯框架,该框架建立在潜在感染病例的传播模型(见下文)上,并通过借用各国的信息来指定感染病例中的死亡分布。该报告还描述了对各种模型选择和假设的预测的各种敏感性分析。
IHME 模型的源代码也可以通过 GitHub 获得。IHME 模型预测可用于不同的地点,包括美国各州和欧洲经济区国家的网站上。然而,重现这些结果所需的方法和参数的准确规格是不透明的,因此结果是不可重现的。报告提供了对基本方法的简要描述,该方法依赖于将一个相当严格的参数模型与观察到的数据进行拟合。然而,该报告对模型选择和假设给出了有限的理由。特别是,很难跟踪预测中的不确定性是如何量化的,以及社会距离协变量是如何定义的。
在意大利和纽约州进行回顾性验证
图 1 显示,IHME 模型使用高峰前 7 天的数据,对高峰时的死亡时间和强度做出了较好的估计。但是当数据在峰值前 14 天使用时,峰值预测并不可靠。此外,该模型预测死亡人数将在高峰后快速下降。然而,目前的趋势表明,峰值后的下降趋势要温和得多。事实上,观察到的累积死亡人数经常超过置信区间上限。ICL 模型高估了意大利每日死亡人数,尽管观察到的趋势在提供的置信带(阴影区域)内。就纽约而言,预测和观察到的趋势非常相似,但当使用峰值前一两周的数据建立模型时,预测趋势的置信界限非常宽。


****
****图一。验证 IHME 和 ICL 模型。使用高峰前 14 天或 7 天的“训练”数据(棕色线)或高峰前的所有数据建立模型。在意大利和纽约,观察到的每日死亡高峰分别是 3 月 27 日和 4 月 7 日。训练期后的预测趋势(橙色线)与观察到的趋势(蓝色线)进行比较。阴影区域显示预测趋势周围 95%的置信界限。对于 IHME 模型,置信区域不可用于每日死亡,而是显示累积死亡。数据从累计死亡人数达到百万分之 0.31(IHME 模型拟合使用的标准)的那一天开始显示。
评估用于预测感染病例的传播模型
预测感染病例的模型,而不是包含易感、暴露、感染和清除(SEIR)之间不同过渡状态的死亡模型,早在中国武汉省发生疫情后就已使用[ Wu 等人,2020 年。我们找不到 SEIR 模型的开源代码,这些代码很容易操作来拟合其他国家的数据。相反,我们使用了一个稍微简化的 SIR 模型[ Song et al. 2020 ],这是密歇根大学的研究人员最近开发的。
透明度和再现性
这个 eSIR 模型的源代码可以通过 Github 获得。该模型仅适用于武汉数据集,使用现有代码可再现结果。随附报告[ 宋等人 2020 ]中描述的基本方法易于遵循且合理。
该模型的一个局限性是,它需要指定表示封锁期内疾病动态的未知参数值。但是因为代码很容易访问,我们可以重写一部分代码,使用上周的训练数据对未知参数进行进一步的“调整”。具体来说,我们使用指数衰减函数来模拟首次干预后的传播率下降,其中我们假设在达到阈值后出现衰减平稳期。我们将衰减率和阈值水平视为“调谐参数”。我们拟合原始模型,删除最后一周的训练数据,然后搜索两个参数的组合,该组合给出最后一周内观察到的情况的预测趋势的最佳拟合。
验证
图 2 显示,对参数进行额外调整后的 eSIR 模型可以合理准确地预测报告案例的未来趋势。然而,这种特殊的模型产生了非常大范围的不确定性,并且该范围通常包括长时间内的零个新病例。
****
****图二。eSIR 模型的验证。使用峰值前 7 天的“训练”数据(棕色线)或峰值前的所有数据建立模型。观察到的每日病例高峰分别是意大利和纽约的 3 月 21 日和 4 月 4 日。训练数据被进一步分割,最后 7 天用于调整模型的锁定后参数。训练期后的预测趋势(橙色线)与观察到的趋势(蓝色线)进行比较。阴影区域显示预测趋势周围 95%的置信界限。数据显示,从当天开始累计死亡人数超过 6 人。
总结发现和注意事项
在两个流行的模型中,我们发现伦敦帝国理工学院(ICL)的模型比 IHME 的模型更透明和可复制。前一个模型有时高估了未来的死亡人数,而后一个模型显然低估了高峰后的死亡人数。两个模型都使用一周前的数据合理地预测了峰值的时间。ICL 模型对纽约州产生了更大范围的不确定性,这可能是因为该模式不符合他们从欧洲国家使用的内部培训数据。eSIR 模型是透明的,我们可以修改他们的代码来提出我们自己的策略,通过数据分割来了解一些锁定后的参数。然而,目前实施的模型产生了非常大的不确定性,以至于置信下限提供的最佳情况可能没有意义。
更强调不确定性
我们主张更加重视围绕预测的不确定性的沟通。ICL 和 eSIR 模型都包含一个灵活的贝叶斯建模框架,可以围绕预测趋势产生非常宽的不确定性范围。IHME 模型产生了较窄的不确定性界限,但由于使用了无法解释观察到的趋势的非常严格的模型,它很可能低估了不确定性。在未来,建模者需要更有力地交流不确定性的水平和来源,并意识到,有时,主要的发现可能是不确定性本身的水平。媒体和政府人员也需要更多地关注不确定性的范围,并对宣传周围有太多不确定性的统计数据持谨慎态度。最佳做法可能是强调更短期的预测,并意识到长期趋势可能出现的最坏情况,正如一些最可靠预测的置信上限所表明的那样。
不要过度使用模特
我们还提倡在使用这些模型评估干预措施的有效性时要更加谨慎,因为估计中存在很大的不确定性。例如,在 COV-IND-19 研究小组的媒体文章中,研究人员使用我们使用的相同 eSIR 模型评估了 3 月 25 日印度封锁的影响。他们预计,在没有封锁的情况下,到 4 月 30 日,病例数将在 35,000 例左右,如果封锁有效,病例数可能低至近 4000 例。事实上,在这篇文章发表的当天(4 月 3 日),印度的病例数为 2567 例,到 4 月 30 日,报告的病例数接近 35000 例。值得称赞的是,尽管作者承认不确定性的范围很大,但决策者和公众并不容易理解不确定性的概念,这种分析可能会产生一种意想不到的印象,即封锁可能并不有效。提升封锁效果的最佳方法是以透明的方式报告经验数据,如报告病例和死亡的增长率,这些数据清楚地表明封锁在印度效果良好,就像在许多其他国家一样(见图 3)。

****图三。病例/死亡日增长率。显示自病例/死亡数超过 100/50 后的趋势。
我们的分析也有局限性
我们的分析结果也应该谨慎解读。我们只使用了两个高密度区域的数据来检验模型预测的有效性。在强度较低的国家/地区,模式可能有所不同,我们计划在以后的研究出版物中使用额外的数据进行更广泛的分析。我们已经尝试基于可用的文档尽可能最好地实现不同的模型。有可能我们没有在建模者想要的理想环境中实现其中的一些。此外,在我们完成分析后,ICL 和 IHME 模型最近都进行了更新,这些新版本的模型可能提高了有效性。我们也承认,在疫情这样一个非常动态的情况下,模型验证的意义可能存在模糊性,在这种情况下,与预测值相比,干预措施的实施和取消预计会改变观察到的趋势(参见讨论)。尽管如此,我们相信,随着在疫情第一阶段的整个周期中,以及在不同类型的干预措施下,越来越多的数据可用,恰当地定义和展示模型验证将越来越有可能。
需要缓解需要缓解**
展望未来,显然需要付出更多努力,遵循透明、可复制和有效的原则,更好地审查模型。不附带其他定量研究人员可以访问、操作和试验的开源代码的模型是不可靠的。使用这些模型的具体预测还应该附带额外的开源代码、规范和数据,以便其他研究人员可以准确地复制结果。此外,随着越来越多的经验数据可用于流行病第一阶段的整个周期,建模者应考虑使用回顾性数据来验证他们自己的预测。然而,当我们应用这些原则时,重要的是要记住,所有与疫情相关的事情都需要在有限的时间和资源内紧急完成。因此,我们呼吁热衷于对该领域做出贡献的定量研究人员,将他们的努力导向评估和可能调整现有模型。
显然,在未来,如果预测模型要更加精确,它需要纳入除疫情本身的简单时间序列数据之外的其他类型的数据。就像股票市场的预测将具有有限的价值,除非有关住房和劳动力市场、行业前景和消费者行为的信息被纳入模型,我们不能指望新冠肺炎预测模型有更高的精确度,除非其他类型的数据,如移动和基于 GPS 的移动信息或不同类型干预下的公众行为调查数据被纳入。
密码
本文中所有分析使用的代码可以在这里访问
贡献者
这篇文章是合作努力的结果
约翰·霍普金斯大学博士后金
Neha Agarwala,马里兰大学博士生
约翰·霍普金斯大学即将毕业的博士生 Prosenjit Kundu
约翰·霍普金斯大学博士生王怡
赵,约翰·霍普金斯大学博士生
约翰·霍普金斯大学彭博杰出教授 Nilanjan Chatterjee(电子邮件:nilanjan10c@gmail.com)
确认
这篇文章从我们的同事和约翰霍普金斯大学生物统计系的导师那里得到了实质性的改进,包括 Jeff Leek、Thomas Louis、Karen Bandeen-Roche 和 Elizabeth Stuart 教授。
转置卷积去神秘化
转置卷积对于图像分割、超分辨率等应用来说是一个革命性的概念,但有时它会变得有点难以理解。在这篇文章中,我将试图揭开这个概念的神秘面纱,让它更容易理解。

(来源)
介绍
自从卷积神经网络(CNN)流行以来,计算机视觉领域正在经历一个过渡阶段。这场革命始于 Alexnet 在 2012 年赢得 ImageNet 挑战赛,自那以来,CNN 一直统治着图像分类、对象检测、图像分割和许多其他图像/视频相关任务的领域。
随着我们深入网络,卷积运算降低了空间维度,并创建了输入图像的抽象表示。CNN 的这一特性对于图像分类等任务非常有用,在这些任务中,您只需预测特定对象是否出现在输入图像中。但是该特征可能会给诸如目标定位、分割之类的任务带来问题,其中原始图像中的目标的空间维度是预测输出边界框或分割目标所必需的。
为了解决这个问题,使用了各种技术,例如全卷积神经网络,其中我们使用“相同”填充来保留输入维度。虽然这种技术在很大程度上解决了这个问题,但是它也增加了计算成本,因为现在卷积运算必须应用于整个网络的原始输入维度。

图一。全卷积神经网络(来源
用于图像分割的另一种方法是将网络分成两部分,即下采样网络和上采样网络。
在下采样网络中,使用简单的 CNN 架构,并产生输入图像的抽象表示。
在上采样网络中,使用各种技术对抽象图像表示进行上采样,以使其空间维度等于输入图像。这种架构以编码器-解码器网络而闻名。

图二。用于图像分割的下采样和上采样网络(来源)。
上采样技术
下采样网络直观且众所周知,但很少讨论用于上采样的各种技术。
编码器-解码器网络中最广泛使用的上采样技术是 :
- 最近邻居:顾名思义,在最近邻居中,我们取一个输入像素值,并将其复制到 K 个最近邻居,其中 K 取决于预期输出。

图三。最近邻向上采样
2.双线性插值:在双线性插值中,我们取输入像素的 4 个最近像素值,并根据 4 个最近像元的距离进行加权平均,平滑输出。

图 4。双线性插值
3.钉床:在钉床中,我们复制输出图像中相应位置的输入像素值,并在其余位置填充零。

图 5。甲床上采样
4.Max-un Pooling:CNN 中的 Max-Pooling 层取内核中所有值中的最大值。要执行 max-unpooling,首先,在编码步骤中为每个 max-pooling 层保存最大值的索引。然后在解码步骤中使用保存的索引,其中输入像素被映射到保存的索引,在其他地方填充零。

图 6。最大-取消上采样
所有上述技术都是预定义的,并且不依赖于数据,这使得它们是特定于任务的。它们不从数据中学习,因此不是一种通用的技术。
转置卷积
转置卷积用于使用一些可学习的参数将输入特征映射上采样为期望的输出特征映射。
转置卷积的基本操作解释如下:
1。考虑一个 2×2 编码的特征图,它需要被上采样为 3×3 的特征图。

图 7。输入特征地图

图 8。输出特征地图
2.我们取一个大小为 2x2 的核,单位步幅,零填充。

图九。大小为 2x2 的果仁
3.现在我们取输入特征图的左上元素,并将其与内核的每个元素相乘,如图 10 所示。

图 10。
4.类似地,我们对输入特征图的所有剩余元素都这样做,如图 11 所示。

图 11。
5.如您所见,上采样后的要素地图中的某些元素相互重叠。为了解决这个问题,我们简单地添加重叠位置的元素。

图 12。完整的转置卷积运算
6.最终输出将是最终的上采样特征地图,具有所需的 3×3 空间维度。
转置卷积也称为反卷积,这是不恰当的,因为反卷积意味着消除卷积的影响,这不是我们的目标。
它也被称为上采样卷积,对于它用来执行的任务是直观的,即上采样输入特征图。
它也被称为分数步长卷积 due,因为输出的步长相当于输入的分数步长。例如,输出上的步长 2 是输入上的 1/2。
最后,它也被称为后向步长卷积,因为转置卷积中的前向传递相当于正常卷积的后向传递。
转置卷积的问题:
如下所示,转置卷积会受到棋盘效应的影响。

图 13。方格状文物(来源
造成这种情况的主要原因是在图像的某些部分出现不均匀的重叠,从而导致伪像。这可以通过使用可被步幅整除的内核大小来固定或减少,例如,当步幅为 2 时,采用 2×2 或 4×4 的内核大小。
转置卷积的应用;
- 超分辨率:

图十四。使用转置卷积的超分辨率(信号源)
2.语义分割:

图 15。使用转置卷积实现语义分割(来源)
结论:
转置卷积是现代分割和超分辨率算法的支柱。它们提供了抽象表示的最佳和最一般化的上采样。在这篇文章中,我们探讨了所使用的各种上采样技术,然后试图深入了解转置卷积的直观理解。
我希望你喜欢这篇文章,如果你有任何疑问、疑问或评论,请随时通过 Twitter 或 Linkedin 与我联系。
参考文献:
3.深入钻研深度学习
基于用户搜索行为的旅游细分

图片来源:Pixabay
旅游意向,旅游个性化,K-Modes 聚类
在旅行和旅游业中,细分是针对具有不同旅行意图和动机的不同群体开发行程和营销材料的重要策略。它有助于企业了解组成受众的子群体,以便企业能够更好地定制产品和信息。
旅游业的一个警告是,与网上购物不同,休闲旅游是一种不频繁的购买,大多数休闲旅行者每年只旅行一次或两次,活跃的客户要么不常见,要么预订非常慢。
但好的一面是,人们喜欢旅行,我们中的许多人甚至花更多的时间计划假期,而不是理财。因此,如果你是任何在线旅行社的经常用户,你已经将你的数据交给了该公司,以换取该公司向你提供价值。你已经告诉他们关于你自己,你的旅行意图和动机。你期待对你来说更个性化的旅行选择,而不是你今天得到的一长串选择。
在这个细分模型的原型中,我们试图理解用户行为和意图的细微差别,通过旅行对用户的搜索进行细分,即根据共同特征将旅行分成组的过程。
由于各种原因,我不能分享数据集,但请随意使用您自己的适合这个原型的数据集。或者欢迎你查看 Jupyter 笔记本。
数据预处理和特征工程
在数据处理和特征工程阶段,我做了以下决定:
- 我们将按行程对这些搜索进行细分,因此我删除了重复的 trip _ ids。
- 有超过 31%的搜索用户添加了“watch”,我会使用带有“watch_added_dt”的数据,因为:1)。我们对用户了解得越多,我们就能更好地根据他们的兴趣个性化信息。2).当用户添加“观察”时,这表明他(或她)对这次旅行感兴趣,并且希望接收关于航班票价更新的通知。
- 超过 77%的搜索来自美国,我认为首先对这些旅行(来自美国)进行分类是有意义的。因为:1)。我想把美国公共假日作为一个特色。2).来自不同国家/地区的旅行者的旅行行为可能不同。
- 用中间值填充缺失的“停留”。
- 删除缺少“最低价格”的行,因为我认为价格是旅行细分中最重要的元素之一。
- 增加一个新功能——“预订 _ 窗口”,这是“第一次搜索 _dt”和“出发 _ 日期”的区别。
- 增加一个新功能——“路线”,就是“始发地”到“目的地”。
- 增加一个新功能——“假日”,是由“出发 _ 日期”衍生而来的美国公共假日。

表 1
经过处理后,我们要处理的数据包含 269,203 次旅行,有 25,696 条不同的飞行路线和 773 个不同的出发日期,目的地有 178 个国家,如表 1 所示。
观察:
在数据中,第一价格和最低价格相当接近。
大约。其中 0.3%的行程是在同一天出发和返回(即“停留”= 0)。
另外 0.1%的旅行希望尽快离开(即“第一次搜索日期”和“出发日期”是同一天)。
探索性数据分析
描述一次“旅行”的重要因素是什么?让我们重点关注以下特性。
- 周末或工作日出发

76%的搜索行程是在工作日出发。
- 目的地

图 1
该数据集主要由美国境内的旅行目的地决定(超过 68%)。
- 飞行路线

图 2
观察:
·前 20 条搜索路线中有 19 条是在美国境内行驶的。
:前 20 条航线中有 12 条是从纽约到某地,大多是到国内某个温暖的地方,只有一条是从纽约到巴黎。
- 出发那天是不是假期,如果是,什么假期。

图 3
我有一种感觉,用户不太可能在繁忙的假日旅游季节出行,这可能与旺季价格有关。
- 最低价格。当一个用户开始观察一个价格时,他(或她)应该总是以等待最低价为目标,所以我决定使用最低价而不是第一个价格。

图 4
我们可以看到 73%的旅行价格在 100 到 500 美元之间。
- 他们呆多久。

图 5
其中 73%的旅行停留时间在 0 到 7 天之间。
- 预订窗口

图 6
这些旅行中有 55%在 0 到 90 天的预订窗口范围内。
k 模式聚类
标准的 k-means 算法并不直接适用于像这样的分类数据。因此,我将使用 k-modes 算法,该算法使用简单的匹配相异度来处理分类对象。它根据数据点之间匹配类别的数量来定义分类。
为了使用 k-mode,我将三个数字特征转换为分类特征,即:“最低价格”到“最低价格 _bin”,“停留”到“停留 _bin”,“预订 _ 窗口”到“预订 _ 窗口 _bin”,根据上面三个饼状图中的阈值。

图 7
根据图 7,我应该选择 k=3,也就是三个预测段。
识别细分
- 按目的地地区划分行程。

图 8
- 按飞行路线划分行程。

图 9
- 按美国公共假日划分旅行。

图 10
- 按周末和工作日细分。

图 11
- 按部门划分的最低价格分布。

图 12
- 按航段划分的停留时间分布。

图 13
- 按航段划分的订票窗口分布。

图 14
解释结果
段 0:
- 包含 178,519 次出行的最大路段。
- 它们大多是美国国内的航线,还有一些从美国到墨西哥或安的列斯的航线。
- 这些路线中的大多数可能是从纽约或芝加哥到温暖的地方,如加利福尼亚、佛罗里达或内华达。
- 这些旅行可能在工作日出发。
- 平均价格为 310 美元,平均停留时间约为 6 天,用户的平均预订窗口不到 3 个月。
因此,第 0 段中的旅行听起来像是从东部到阳光明媚的目的地的为期一周的假期。
第 1 段 :
- 该航段包含 48,101 次出行。
- 这一部分的旅行主要是国内旅行。
- 这些旅行很可能在周末出发,包括阵亡将士纪念日周末和劳动节周末;他们周末去度假大约 3 天。
- 因为这是一次周末度假,在美国境内的飞行距离可能很短。
- 平均价格是 319 美元。
因此,航段 1 中的旅行大多是相对短途的快速周末度假。他们可以是廉价的,浪漫的或最后一分钟的,或者只是从城市生活的喧嚣中充电。
第二段 :
- 该航段包含 42,583 次出行。
- 该细分市场中的旅行可能是国际旅行,例如从美国到欧洲或亚洲的旅行。它还包括国内旅行,但旅行到夏威夷很远。
- 对于这种类型的旅行,用户倾向于在工作日搜索出发。
- 平均价格为 743 美元,平均停留时间为 14 天左右。
- 用户倾向于更早地计划长途旅行,平均预订时间大约为 4-5 个月。
我们如何使用这些信息?
通过使用上述知识来创建更有效的内容或推荐,企业将能够基于用户的意图为其提供最佳体验。
例如,一旦用户搜索并观看段 1 中的旅行,在线旅行社将知道该用户可能寻找周末度假,在线旅行社然后可以给出关于周末度假的旅行建议或推荐当前在航班或酒店上有促销的其他类似的周末度假目的地。
Jupyter 笔记本可以在 Github 上找到,好好享受这周剩下的时光吧!
参考资料:
http://citeseerx.ist.psu.edu/viewdoc/download doi = 10 . 1 . 1 . 932 . 2724&rep = rep 1&type = pdf
begingroup$ K-Means 的目标是减少类内方差,因为它计算质心作为均值…
datascience.stackexchange.com](https://datascience.stackexchange.com/questions/22/k-means-clustering-for-mixed-numeric-andcategorical-data) [## nicodv/kmodes
k-modes 和 k-prototypes 聚类算法的 Python 实现。依赖 numpy 完成大量繁重的…
github.com](https://github.com/nicodv/kmodes)
https://www . ka ggle . com/ashydv/bank-customer-clustering-k-modes-clustering
第 1 部分:用 Neosemantics 库将 WikiData 导入 Neo4j
旅游的游客
从 WikiData 导入数据并打开街道地图 API,以在 Neo4j 中创建知识图表
在短暂的暑假之后,我准备了一个新的博客系列。在这第一部分,我们将构建一个位于西班牙的古迹知识图表。你可能知道,我最近对通过维基数据 API 获得的知识财富产生了浓厚的兴趣和敬意。我们将继续完善我们的 SPARQL 语法知识,并从 WikiData API 中获取有关西班牙古迹的信息。我之前并没有意识到这一点,但是收集网上可用的 RDF 数据并将其导入 Neo4j 是一个如此受欢迎的话题,以至于Jesus Barrasa博士开发了一个 Neosemantics library 来帮助我们完成这个过程。
在本系列的下一部分,我们将看看 Neo4j 图形数据科学库中可用的寻路算法。
议程
- 安装新语义库
- 图形模型
- 构造 WikiData SPARQL 查询
- 导入 RDF 图
- 使用 OSM API 进行反向地理编码
- 验证数据
安装新语义库
在这个博客系列中,我们将使用标准的 APOC 和 GDS 库,我们只需在 Neo4j 桌面应用程序中单击一下就可以安装它们。最重要的是,我们将把新语义库添加到我们的堆栈中。它用于在 Neo4j 环境中与 RDF 数据进行交互。我们可以将 RDF 数据导入 Neo4j,或者以 RDF 格式导出属性图模型。
为了安装 Neosemantics 库,我们下载了最新版本并将其保存到 Neo4j 插件文件夹中。我们还需要在 Neo4j 配置文件中添加下面一行。
dbms.unmanaged_extension_classes=n10s.endpoint=/rdf
我们现在准备启动我们的 Neo4j 实例。首先,我们需要用下面的 cypher 过程启动新语义配置。
CALL n10s.graphconfig.init({handleVocabUris: "IGNORE"})
查看一下文档中关于配置选项的信息。
图形模型
纪念碑在我们图表的中心。我们将它们的名称和图像的 URL 存储为节点属性。这些纪念碑受到了各种建筑风格的影响,我们将其表示为与建筑节点的关系。我们将把城市和州的纪念碑保存为一个两级分层位置树。

用 draw.io 创建的图形模型
Neosemantics 库需要对标记为 Resource 的节点的属性“uri”进行唯一约束。我们还将为州和城市节点添加索引。apoc.schema.assert过程允许我们通过一次调用定义许多索引和唯一约束。
CALL apoc.schema.assert(
{State:['id'], City:['id']},
{Resource:['uri']})
构造 WikiData SPARQL 查询
对我来说,构建新的 SPARQL 查询最简单的方法是使用 WikiData 查询编辑器。它有一个可爱的自动完成功能。它还有助于查询调试。
我们想要检索位于西班牙的古迹的所有实例。我发现在维基数据上找到各种实体最简单的方法就是简单地使用谷歌搜索。然后,您可以在网站上检查该实体的所有可用属性。我第一次看到的 SPARQL 查询如下所示:
SELECT *
WHERE { ?item wdt:P31 wd:Q4989906 .
?item wdt:P17 wd:Q29 .
?item rdfs:label ?monumentName .
filter(lang(?monumentName) = "en")
?item wdt:P625 ?location .
?item wdt:P149 ?architecture .
?architecture rdfs:label ?architectureName .
filter(lang(?architectureName) = "en")
?item wdt:P18 ?image }
WHERE子句中的前两行定义了我们要寻找的实体:
// Entity is an instance of monument entity
?item wdt:P31 wd:Q4989906 .
// Entity is located in Spain
?item wdt:P17 wd:Q29 .
接下来,我们还确定我们感兴趣的实体的属性。在我们的例子中,我们希望检索纪念碑的名称、图像、位置和建筑风格。如果我们在查询编辑器中运行这个查询,我们会得到以下结果。

我们已经在 SPARQL 查询的WHERE子句中定义了想要检索的信息。在用新语义导入数据之前,我们需要对数据格式进行一些调整。第一个也是最关键的是将SELECT条款改为CONSTRUCT。这样,我们将得到返回的 RDF 三元组,而不是信息表。你可以在这篇由马克·李约瑟写的博客中读到更多关于SELECT和CONSTRUCT的区别。
有了 Neosemantics 库,我们可以预览使用n10s.rdf.preview.fetch过程后我们存储的图模型会是什么样子。我们将从检查一个空的CONSTRUCT语句的图表模式开始。
WITH '
CONSTRUCT
WHERE { ?item wdt:P31 wd:Q4989906 .
?item wdt:P17 wd:Q29 .
?item rdfs:label ?monumentName .
?item wdt:P625 ?location .
?item wdt:P149 ?architecture .
?architecture rdfs:label
?architectureName .
?item wdt:P18 ?image} limit 10 ' AS sparql
CALL n10s.rdf.preview.fetch(
"https://query.wikidata.org/sparql?query=" +
apoc.text.urlencode(sparql),"JSON-LD",
{ headerParams: { Accept: "application/ld+json"} ,
handleVocabUris: "IGNORE"})
YIELD nodes, relationships
RETURN nodes, relationships
结果

一个问题是节点没有标签。您可能还会注意到,关系类型提供的信息并不多,因为如果您不知道 WikiData 属性映射,P149 或 P31 没有多大意义。另一件事情是,图像的 URL 被存储为一个单独的节点,这一点在这个可视化中并不明显。如果您还记得之前的图形模型,我们决定将图像 URL 保存为 monument 节点的属性。
我不会讲太多细节,但是在CONSTRUCT子句中,我们可以定义我们的图表模式在 Neo4j 中应该是什么样子。我们还使用以下语法定义了要将图像的 URL 保存为纪念碑节点的属性,而不是单独的节点:
?item wdt:P18 ?image .
bind(str(?image) as ?imageAsStr)
我们现在可以用更新后的CONSTRUCT语句预览新的查询。
WITH ' PREFIX sch: <http://schema.org/>
CONSTRUCT{ ?item a sch:Monument;
sch:name ?monumentName;
sch:location ?location;
sch:img ?imageAsStr;
sch:ARCHITECTURE ?architecture.
?architecture a sch:Architecture;
sch:name ?architectureName }
WHERE { ?item wdt:P31 wd:Q4989906 .
?item wdt:P17 wd:Q29 .
?item rdfs:label ?monumentName .
filter(lang(?monumentName) = "en")
?item wdt:P625 ?location .
?item wdt:P149 ?architecture .
?architecture rdfs:label ?architectureName .
filter(lang(?architectureName) = "en")
?item wdt:P18 ?image .
bind(str(?image) as ?imageAsStr) } limit 100 ' AS sparql CALL n10s.rdf.preview.fetch(
"https://query.wikidata.org/sparql?query=" +
apoc.text.urlencode(sparql),"JSON-LD",
{ headerParams: { Accept: "application/ld+json"} ,
handleVocabUris: "IGNORE"})
YIELD nodes, relationships
RETURN nodes, relationships
结果

导入 RDF 图
现在,我们可以将图表导入 Neo4j。我们使用n10s.rdf.import.fetch而不是n10s.rdf.preview.fetch过程,并保持查询的其余部分不变。
WITH 'PREFIX sch: <http://schema.org/>
CONSTRUCT{ ?item a sch:Monument;
sch:name ?monumentName;
sch:location ?location;
sch:img ?imageAsStr;
sch:ARCHITECTURE ?architecture.
?architecture a sch:Architecture;
sch:name ?architectureName }
WHERE { ?item wdt:P31 wd:Q4989906 .
?item wdt:P17 wd:Q29 .
?item rdfs:label ?monumentName .
filter(lang(?monumentName) = "en")
?item wdt:P625 ?location .
?item wdt:P149 ?architecture .
?architecture rdfs:label ?architectureName .
filter(lang(?architectureName) = "en")
?item wdt:P18 ?image .
bind(str(?image) as ?imageAsStr) }' AS sparql
CALL n10s.rdf.import.fetch(
"https://query.wikidata.org/sparql?query=" +
apoc.text.urlencode(sparql),"JSON-LD",
{ headerParams: { Accept: "application/ld+json"} ,
handleVocabUris: "IGNORE"})
YIELD terminationStatus, triplesLoaded
RETURN terminationStatus, triplesLoaded
让我们从一些探索性的图查询开始。我们将首先计算图表中纪念碑的数量。
MATCH (n:Monument)
RETURN count(*)
我们已经将 1401 个纪念碑导入到我们的图表中。我们将继续计算按建筑风格分组的纪念碑的数量。
MATCH (n:Architecture)
RETURN n.name as monument,
size((n)<--()) as count
ORDER BY count DESC
LIMIT 5
结果
罗马式和哥特式建筑风格影响了大多数纪念碑。当我探索 WikiData 时,我注意到一种架构风格可以是其他架构风格的子类。作为练习,我们将把架构层次关系导入到我们的图表中。在我们的查询中,我们将迭代存储在图中的所有架构风格,并从 WikiData 中获取任何父架构风格,并将其保存回我们的图中。
MATCH (a:Architecture)
WITH ' PREFIX sch: <http://schema.org/>
CONSTRUCT { ?item a sch:Architecture;
sch:SUBCLASS_OF ?style.
?style a sch:Architecture;
sch:name ?styleName;}
WHERE { filter (?item = <' + a.uri + '>)
?item wdt:P279 ?style .
?style rdfs:label ?styleName
filter(lang(?styleName) = "en") } ' AS sparql
CALL n10s.rdf.import.fetch(
"https://query.wikidata.org/sparql?query=" +
apoc.text.urlencode(sparql),"JSON-LD",
{ headerParams: { Accept: "application/ld+json"} ,
handleVocabUris: "IGNORE"})
YIELD terminationStatus, triplesLoaded
RETURN terminationStatus, triplesLoaded
我们现在可以看一些架构层次的例子。
MATCH (a:Architecture)-[:SUBCLASS_OF]->(b:Architecture)
RETURN a.name as child_architecture,
b.name as parent_architecture
LIMIT 5
结果
似乎现代主义是新艺术的子范畴,新艺术是装饰艺术的子范畴。
空间丰富
起初,我想在维基数据中包含一些市政信息,但是事实证明,这些信息相对较少。不过不用担心,我后来意识到我们可以使用反向地理编码 API 来检索这些信息。APOC 有专门的反向地理编码程序。默认情况下,它使用开放的街道地图 API,但我们也可以定制它来与其他提供商合作。查看文档了解更多信息。
首先,我们必须将位置信息转换为空间点数据类型。
MATCH (m:Monument)
WITH m,
split(substring(m.location, 6, size(m.location) - 7)," ") as point
SET m.location_point = point(
{latitude: toFloat(point[1]),
longitude: toFloat(point[0])})
查看 OSM 反向地理编码 API 的示例响应。
MATCH (m:Monument)
WITH m LIMIT 1
CALL apoc.spatial.reverseGeocode(
m.location_point.latitude,
m.location_point.longitude)
YIELD data
RETURN data
结果
{
"country": "España",
"country_code": "es",
"isolated_dwelling": "La Castanya",
"historic": "Monument als caiguts en atac Carlista 1874",
"road": "Camí de Collformic a Matagalls",
"city": "el Brull",
"municipality": "Osona",
"county": "Barcelona",
"postcode": "08559",
"state": "Catalunya"
}
开放街道地图 API 有点有趣,因为它在城市、城镇和村庄之间有所不同。此外,位于加那利群岛的纪念碑没有国家可用,但却是加那利群岛的一部分。我们将把群岛作为一个州,把城市、城镇和村庄放在一个标签下。出于批处理的目的,我们将使用apoc.periodic.iterate程序。
CALL apoc.periodic.iterate(
'MATCH (m:Monument) RETURN m',
'WITH m
CALL apoc.spatial.reverseGeocode(
m.location_point.latitude,m.location_point.longitude)
YIELD data
WHERE (data.state IS NOT NULL OR
data.archipelago IS NOT NULL)
MERGE (s:State{id:coalesce(data.state, data.archipelago)})
MERGE (c:City{id:coalesce(data.city, data.town,
data.village, data.county)})
MERGE (c)-[:IS_IN]->(s)
MERGE (m)-[:IS_IN]->(c)',
{batchSize:10})
该查询将花费很长时间,因为默认的限制延迟设置是 5 秒。如果您没有时间等待,我已经将空间结果保存到 GitHub 中,您可以在不到五秒钟的时间内使用以下查询轻松导入它们。
LOAD CSV WITH HEADERS FROM "https://raw.githubusercontent.com/tomasonjo/blogs/master/Traveling_tourist/traveling_tourist_cities.csv" as row
MATCH (m:Monument{uri:row.uri})
MERGE (c:City{id:row.city})
MERGE (s:State{id:row.state})
MERGE (m)-[:IS_IN]->(c)
MERGE (c)-[:IS_IN]->(s);
我们将首先检查是否有任何遗迹的空间价值缺失。
MATCH (m:Monument)
RETURN exists ((m)-[:IS_IN]->()) as has_location,
count(*) as count
结果
我们已经检索了几乎所有遗迹的空间信息。创建位置层次结构树时需要注意的是,树中的每个节点与其父节点只有一个传出关系。如果这个规则被打破,位置树的结构完整性将丢失,因为一些实体将具有不止一个位置。查看我的位置树帖子获取更多关于如何绕过这个问题的信息。
MATCH (c:City)
WHERE size((c)-[:IS_IN]->()) > 1
RETURN c
幸运的是,我们在这里没有遇到这个问题。我们现在可以探索我们的空间丰富的结果。我们将看看位于加泰罗尼亚的按照建筑风格分组的古迹数量。
MATCH (s:State{id:'Catalunya'})<-[:IS_IN*2..2]-(:Monument)-[:ARCHITECTURE]->(architecture)
RETURN architecture.name as architecture,
count(*) as count
ORDER BY count DESC
LIMIT 5
结果
让我们快速看一下维基百科对教育目的的乡土建筑的定义。
乡土建筑是以使用当地材料和知识为特征的建筑,通常没有专业建筑师的监督。
我们还可以看看各州最常见的纪念碑建筑风格。我们将使用 Neo4j 4.0 中引入的子查询语法。
MATCH (s:State)
CALL {
WITH s
MATCH (s)<-[:IS_IN*2..2]-()-[:ARCHITECTURE]->(a)
RETURN a.name as architecture, count(*) as count
ORDER BY count DESC LIMIT 1
}
RETURN s.id as state, architecture, count
ORDER BY count DESC
LIMIT 5
结果
结论

如果你遵循了这篇文章中的步骤,你的图表应该看起来像上面的图片。我总是对只用 cypher 从各种 API 获取数据是如此容易印象深刻。如果您想调用任何自定义端点,您仍然可以使用apoc.load.json过程。
在下一部分,我们将深入研究寻路算法。与此同时,试试 Neo4j ,加入 Twin4j 简讯。
和往常一样,代码可以在 GitHub 上获得。
将线性回归视为不仅仅是一个黑盒
超越线性回归()。拟合(x,y)

线性回归是最基本的机器学习模型之一,但它的应用无处不在:从预测房价到预测电影收视率。即使当你坐出租车去上班时,你也有一个线性回归问题要处理。
简单来说,线性回归是指预测值和特征值之间的线性关系。线性回归的数学方程如下:

其中 Y 为预测值, θ₀ 为偏差参数, θ₁ 为参数值, x 为特征值。
为了说明这些参数的含义,假设您乘坐出租车去上班。你进入出租车后不久,出租车费就不会从 0 开始了。而是从一些值开始,比如说 5 美元。这 5 美元代表回归方程中的偏差项( θ₀ )。预测值 Y 是你应该支付的价格,特征值 x 是行驶距离,【θ₁】是随着行驶距离的增加你需要多支付多少的斜率。
让我们在出租车费用和出行距离之间生成一个随机的线性外观数据,以形象化上面的图示。
import numpy as npnp.random.seed(1)
x = 2 * np.random.rand(100,1)
y = 4+ 2 * x + np.random.rand(100,1)

出租车费和旅行距离之间有一个线性趋势。因此,我们可以将出租车费和出行距离作为线性函数进行关联:

现在的问题是我们如何获得【θ₀】【θ₁】的最优值,使得我们的线性回归模型表现最佳?为此我们可以使用 Scikit-learn。
用 Scikit-learn 构建线性回归模型
如果您有一个线性回归问题要解决,就像上面的研究案例一样,找到 θ₀ 和 θ₁ 的最优值的最简单方法是通过调用 Scikit-learn 库中的LinearRegression函数。这个问题可以用四行代码解决,如下所示:
from sklearn.linear_model import LinearRegressionlm = LinearRegression()#train the model
lm.fit(x,y)print(lm.intercept_, lm.coef_)## Output: [4.49781698] [[1.98664487]]
由此我们可以得出出租车费和出行距离之间的关系如下:

有了这个关系,现在你可以估算出你的位置和目的地之间的距离,现在你可以预测你应该付多少钱的出租车费。
线性回归不仅仅是一个黑箱
机器学习库可以让我们的生活更容易解决不同的问题,这令人惊叹。仅用四行代码,您就已经优化了一个线性回归模型,而无需了解任何内幕。
虽然这些机器学习库令人惊叹,但它们肯定为你隐藏了一些实现细节,例如,我们将线性回归问题视为黑盒。
知道引擎盖下是什么以及 Scikit-learn 如何提出解决方案taxi _ fares = 4.49+1.98 * travel _ distance 岂不是太棒了?他们怎么知道 4.49 是偏差项的最佳值,1.98 是斜率的最佳值?
如果我们知道在引擎盖下发生了什么,我们将对在任何给定问题中使用的适当模型有深刻的理解,并且我们可以更有效地调试错误。另外,对回归问题如何工作有一个很好的理解将是我们理解神经网络算法背后的数学和直觉的一个很好的基础。
要找到线性回归问题的最佳模型,通常有两种方法:
- 使用称为梯度下降的迭代优化方法。
- 使用“封闭形式”的方程称为正规方程。
线性回归的梯度下降
简而言之,梯度下降算法通过调整模型参数 θ (偏差项和斜率)来迭代地最小化关于训练数据的误差或成本函数,从而找到线性回归问题的最优解。
让我们形象化地描述一下梯度下降是如何工作的,以使其更加清晰。

梯度下降从随机初始化参数值 θ 的初始值开始。然后,这个 θ 值将在每次迭代中被调整,使得误差或成本函数被最小化。一旦梯度下降算法找到 θ 最小值,代价函数就不会再变小。这意味着算法已经收敛。
需要注意的是,并非所有的成本函数都有一个完美的碗形,如上图所示。大多数情况下,成本函数有各种不规则的形状和平台,如下所示:

成本函数的形状不规则性使得梯度下降算法很难收敛,因为该算法很有可能会陷入局部最优而无法找到全局最优。
幸运的是,线性回归问题中的成本函数总是凸的,这意味着没有局部最优解,只有一个全局最优解。因此,无论你如何初始化你的初始值,几乎可以保证梯度下降算法将在全局最小解中结束。
但问题是,如果没有局部最优解,为什么“几乎”可以保证梯度下降最终会达到全局最小?
因为梯度下降中有一个重要的参数你需要提前定义,就是学习率。
学习率决定了算法在每次迭代中的步长。如果学习率太小,很有可能在迭代结束时达不到全局最小值。如果学习率太高,算法将在每次迭代中围绕谷值跳跃,并且算法将发散。

学习率过低(左)和过高(右)
梯度下降的实现
在所有关于梯度下降的解释之后,让我们使用研究案例“出租车费 vs 旅行距离”来实现它。
对于线性回归问题,我们应该最小化梯度下降算法的代价函数是均方误差(MSE)。

其中:

在上面的数学符号中,是代价函数, m 是观测值或数据的个数, hθ 是来自线性回归的预测值, θ₀ 是偏差项, θ₁ 是斜率, x 是特征值我们可以用代码实现上述等式:
*def computeCostFunction(x, y, theta, num_observations):
linear_function= np.matmul(x,theta)
error = np.subtract(linear_function,y)
error_squared = error.transpose()**2
cost_function = (0.5/num_observations)*(np.sum(error_squared))
return cost_function*
下一个重要步骤是计算每个模型参数的成本函数的梯度。这种梯度背后的直觉是,如果你调整参数【θⱼ】的值,成本函数将改变多少。**
对于均方误差,其偏导数有一个封闭形式的解。所以,如果你不是微积分专家,这不是问题。

一旦你知道了每个参数 θⱼ 的梯度,现在你可以更新每个参数 θⱼ 进行一次迭代。这就是学习率 α 在梯度下降算法中发挥作用的地方。

最后,我们可以用代码实现上述等式:
**intercept_x = np.ones((len(x),1)) #add bias term
x = np.column_stack((intercept_x,x))alpha = 0.05 #learning rate
iterations = 25000
theta = np.random.randn(2,1) #random initialization of parameters
num_observations = 1000def gradientDescent(x, y, theta, alpha, num_observations, iterations):
cost_history = np.zeros((iterations,1))
for i in range (iterations):
linear_function= np.matmul(x,theta)
error = np.subtract(linear_function,y) gradient = np.matmul(x.transpose(),error)
theta = theta - (alpha * (1/num_observations) * gradient)
cost_history[i] = computeCostFunction(x, y, theta, num_observations)
return theta, cost_historytheta, cost_history = gradientDescent(x, y, theta, alpha, num_observations, iterations)print(theta)#Output: array([[4.49781697],[1.98664487]])**
梯度下降的输出看起来与用 Scikit-learn 获得的输出相同。不同的是,你现在知道什么是引擎盖下。
有一种更直接的方法可以找到线性回归问题的最优解,那就是正规方程。
正规方程
在线性回归中,正规方程是找到参数最佳值的封闭解。使用这种方法,没有迭代方法,没有偏导数,也没有参数更新。一旦你计算了正规方程,你就完成了,你得到了线性回归模型的最优解。

上式中, θ 为回归参数的最优值, x 为特征, Y 为预测值。我们可以用代码实现上面的等式:
**def normalEquation(x, y):
x_transpose = x.transpose()
x = np.matmul(x_transpose, x)
x_inverse = np.linalg.inv(x)
x_final = np.matmul(x_inverse,x_transpose)
theta = np.matmul(x_final,y)
return thetatheta = normalEquation(x, y)print(theta)#Output: array([[4.49781698],[1.98664487]])**

同样,结果与我们从 Scikit-learn 库和梯度下降优化中得到的结果相匹配。现在你可能会问一个问题:如果法方程非常简单,那么我们为什么不应该用它来代替梯度下降呢?让我们讨论一下。
正规方程还是梯度下降?
在解决线性回归问题时,正规方程和梯度下降都有各自的优点和缺点。
利用正规方程,可以直接求解线性回归问题,不需要初始化初始参数值 θⱼ ,也不需要计算偏导数。所有这些都可以通过矩阵矢量化来完成。这使得一切都很快,即使观察或训练数据的数量很大。此外,您不需要对法线方程的每个参数进行归一化来得出最优解。
使用正规方程的主要缺点是它的计算复杂性。这是因为我们需要用这种方法求矩阵的逆矩阵。如果你有一个多元线性回归问题,有大量的特征可供选择,法方程的计算成本将非常昂贵。
使用梯度下降,如果在多元线性回归问题中有大量的特征选择,计算复杂性就不是问题。
然而,如果你有大量的观察或训练数据,梯度下降会变得很慢。这是因为在每次迭代中,梯度下降使用所有的训练数据来计算成本函数。
此外,您始终需要归一化或缩放每个要素,以使梯度下降收敛得更快。下图显示了归一化特征和忘记归一化特征的情况。

线性回归中的归一化要素(左)和非归一化要素(右)
如果对要素进行归一化,线性回归中的成本函数会形成一个完美的碗形,从而加快算法的收敛速度。同时,如果您忘记对要素进行归一化或它们不共享相同的比例,则会形成拉长的碗形。该算法最终仍会以全局最小解结束,但需要更长的时间来收敛。
现在你知道使用法方程和梯度下降算法的优缺点了。作为提示,下表总结了这两种算法的性能。

如何提高梯度下降的性能?
梯度下降的主要缺点是,它使用全部训练数据来计算每次迭代的成本函数。如果我们有大量的观察或训练数据,这将是有问题的。为了解决这个问题,通常采用随机梯度下降(SGD)或小批量梯度下降。
在 SGD 算法中,该算法不是使用整个训练样本,而是在每一步的训练样本中随机选取一个实例。然后,将基于这个随机实例计算梯度。这使得计算时间更快,成本更低,因为在每一步中只有一个实例需要在内存中。
在小批量梯度下降中,不是使用整个训练示例(如梯度下降)或使用随机实例(如 SGD ),而是基于称为小批量的随机实例的小子集来计算梯度。将整个训练数据分成几个小的小批量使得矩阵运算和计算过程更快。
对于简单或多元线性回归问题,通常使用梯度下降就足够了。然而,对于更复杂的应用,如神经网络算法,小批量梯度下降将是首选。
A/B 测试中的处理分配策略
A/B 测试中常见处理分配策略的问题及克服方法概述。
在我之前的帖子中,我给出了规划 A/B 测试的一步一步的过程,但是没有涉及实现部分。本周,我将重点关注 A/B 测试实现的一个组成部分——治疗分配。
有效的随机对照实验的要求之一是随机处理分配。尽管它很重要,但不幸的是,这也是大多数人似乎不太关心的部分。也许因为随机的概念看起来很简单,我只是随机地将我的实验对象分配到控制组和治疗组,就这样,对吗?在商业环境中,实际情况远非如此,因为有些需求使得随机性难以实现。
在这篇博文中,我将试图强调其中一些需求,人们用来满足这些需求的常用策略,这些策略带来的问题,以及最后一个满足所有需求的简单而优雅的解决方案。
一致的治疗分配
给予实验对象的治疗每次都必须是一致的。
我在这里举几个例子:
- 如果网飞想要衡量其新推荐引擎的效果,那么每个用户(实验对象)都应该像以前一样从相同的推荐引擎接收推荐。
- 如果我想测试一个新的移动应用设计是否有更快的加载时间,那么每个设备(实验对象)每次都需要加载相同的移动应用设计。
如果我们在每次实验对象暴露时随机分配治疗,那么我们将无法保证这一点。(例如,用户 A 可能每次都登录并看到不同的处理。)
为了便于解释,并且考虑到大多数实验都是以用户为实验对象的事实,从今以后,我将把用户视为实验对象。
可量测性
满足第一个要求是相当容易的,我们只需记录下哪个用户第一次接触实验时接受了哪种治疗。在后续的登录或者后续的曝光中,我们只是查找这个用户过去的待遇,分配相同的待遇。
这对于一个小公司来说可能是可行的,但是随着你规模的扩大,你会发现这种方法根本不具备很强的可扩展性。我们可以想象,在我们有几千万用户,几百次实验的情况下,要做到这一点几乎是不可能的。
为了做到真正的随机,跨实验的分配应该是独立的
为了解决上述两个问题,公司常用的策略之一是利用用户 ID 的最后一个字符或数字。在这种情况下,治疗将是一致的和完全可扩展的,因为治疗仅依赖于固定的用户 ID。
然而,这带来了另一个问题。这个实验本身确实是随机的。但是随着我们做越来越多的实验,我们会发现不同实验的治疗任务现在是相互关联的。
这里举个例子。
+-------------------+-----------------+-------------------+-------+
| | **Exp 1 : Control** | **Exp 1 : Treatment** | **Total** |
+-------------------+-----------------+-------------------+-------+
| **Exp 2 : Control** | 2107 | 2929 | **5036** |
| **Exp 2 : Treatment** | 2916 | 2048 | **4964** |
| **Total** | **5023** | **4977** | **10000** |
+-------------------+-----------------+-------------------+-------+
想象一下,如果我们有 10,000 个用户,我们做了两个实验。在第一个实验(实验 1)中,我们测试了订阅费的减少是否会增加付费会员的转化率,并发现了一个显著的结果。在第二个实验(实验 2)中,我们测试了一个额外的特性是否会增加用户的购买次数,结果发现这种影响在统计上并不显著。
以下是可能出现这种情况的原因:
- 付费用户会因为打折而购买更多的东西。
- 实验 1 中处理组的更多用户被分配到实验 2 中的控制组,这意味着实验 2 控制组有更多的付费用户。
- 如果两组都服用安慰剂,实验 2 对照组的用户自然会比治疗组购买更多的东西。
- 因此,即使实验 2 中的额外特征确实增加了购买的数量,我们也不能准确地测量效果,因为实验 2 中的处理分配与实验 1 中的处理分配相关。
为了真正随机,在不同实验中的处理分配之间不应该有相关性。
这种相关性多久发生一次?
为了检验这一点,我执行了以下模拟。
- 生成 10,000 个用户 id,其中每个 id 是长度为 10 的十六进制字符串(例如: d3ef2942d7 )。
import pandas as pd
import randomexp_df = pd.DataFrame(['%010x' % random.randrange(16**10) for x in range(10000)], columns=['user_id'])
2.创建 20 个实验。对于每个实验,从 16 个可能值(0 - 9,a - f)中随机选择 8 个作为对照。如果用户 ID 的最后一个字符在控制中,则将他们分配到控制组,否则,将他们分配到处理组。
import numpy as np# number of experiments
j = 20# get all 16 possible values
random_list = exp_df['user_id'].apply(lambda x: x[-1]).unique()control_list = [set(np.random.choice(random_list, 8, replace=False)) for x in range(j)]
treatment_list = [set(random_list) - x for x in control_list]
for k in range(j):
exp_df[f'exp_{k}'] = exp_df['user_id'].apply(lambda x: x[-1]).isin(control_list[k]).map({True:'control', False: 'treatment'})

结果数据帧的外观示例。
3.对于每一对实验,生成如上例所示的列联表,并进行卡方检验。
from scipy import stats# initialize list to store chi-square results all experiment pairs
chi2_res = []for cols in combinations(exp_df.columns[1:], 2): target_cols = list(cols)# generate contingency table
aggregate_df = exp_df[target_cols]\
.groupby(target_cols)\
.size()\
.to_frame()\
.reset_index()\
.pivot(index=target_cols[1],
columns=target_cols[0])# store chi-square test result
chi2_res.append(stats.chi2_contingency(aggregate_df)[1])# number of pairs that fail the chi-square test at alpha = 0.01
print((np.array(chi2_res) < 0.01).sum())
直观地说,如果处理分配是真正独立的,我们应该期望看到以下情况(假设 50%的样本分配给对照,50%分配给处理)。
+-------------------+-----------------+-------------------+-------+
| | **Exp 1 : Control** | **Exp 1 : Treatment** | **Total** |
+-------------------+-----------------+-------------------+-------+
| **Exp 2 : Control** | 2500 | 2500 | **5000** |
| **Exp 2 : Treatment** | 2500 | 2500 | **5000** |
| **Total** | **5000** | **5000** | **10000** |
+-------------------+-----------------+-------------------+-------+
卡方检验告诉我们实际计数离独立案例有多远。距离越远,卡方检验统计值越高,因此有更强的证据表明两个实验的处理分配之间存在某种关联。
我将上面的模拟运行了 100 次,看看在每个模拟中有多少对实验没有通过卡方检验,得到了平均值62%(190 对中的 118 对)!换句话说,如果我们使用这种方法,平均来说,我们将观察到 62%的实验对没有通过卡方检验。我还尝试了 5 次或 10 次实验(而不是最初的 20 次),得到了类似的结果(约 60%)。

在 100 次模拟中未通过卡方检验的实验对数量的分布。
奋力营救
满足上述所有要求的解决方案实际上非常简单。我们所要做的就是使用一个散列函数。散列函数是可以用来将任意大小的数据映射到固定大小的值的任何函数。该属性确保满足前两个要求(一致性和可伸缩性)。
为了满足最终的需求,我们只需要散列用户 ID 和实验 ID 的连接,而不是只散列用户 ID。
下面是如何使用 python 实现这一点的示例。
import mmh3def treatment_assignment(user_id, exp_id, control_bucket):
# calculates the number of buckets
num_buckets = len(control_bucket) * 2
# this generates a 32 bit integer
hash_int = mmh3.hash(user_id + exp_id)
# get the mod of the 32 bit integer
mod = hash_int % num_buckets
if mod in control_bucket:
return 'control'
else:
return 'treatment'
# create 50 random integer as control group
control_bucket = np.random.choice(np.arange(0,100,1), 50, replace=False)
treatment_assignment('d3ef2942d7', 'exp_1', control_bucket)
根据您的控制桶和实验 ID,上述函数将返回treatment或control。我们也可以把它扩展到两个以上变量的实验。
我用这种方法再次运行了上面的模拟,下面是结果。

模拟 190 个实验对中未通过卡方检验(α= 0.01)的实验对的数量。
我们可以看到,100 次模拟中的 73 次导致 2 对或更少的实验(190 对中)无法通过卡方检验,这证明这是一种分配治疗的健壮方法,而不违反任何要求。
结论
在这篇博文中,我详细概述了实验中处理分配的要求。我列出了我见过的一些常见的实践,并陈述了它们为什么违反了一些需求。违反任何要求都意味着实验结果不再有效,这就是为什么我们应该开始更多地关注治疗方案。
最后,我给出了一个简单的解决方案,它满足了适用于两个以上变量实验的所有要求。
参考
[1]好哈希难求(offer up)
https://blog . offer up . com/A-good-hash-is-hard-to-find-60 E8 A 201 E8 ce
树算法讲解:球树算法 vs. KD 树 vs .蛮力
了解最近邻搜索的结构化数据算法背后的内容

来源: pixabay 。
这三种算法都用于最近邻搜索。球树和 KD 树算法是用于数据点的空间划分和它们到特定区域的分配的树算法。换句话说,它们用于在多维空间中构建数据。
不过先从底层说起: 为什么叫树算法?什么是树?— 跳过,如果你已经知道了!
一棵树是一种结构化数据的分层方式。由于存在诸如队列的线性数据结构,其中数据被一个接一个地分配,所以树是一种常见的数据结构。树被应用于计算机科学的许多不同领域,从图形、数据库到操作系统。它们不仅与自然界中的植物学朋友有共同的名称,而且还有一些特征。作为植物树,计算机科学中的树有根、叶和分支。然而,与普通树相比,这些部分的分配是自底向上的。根在树顶,叶在下端。

树的结构。
父节点= 是另一个节点之上的节点,例如,根节点是其下内部节点的父节点
子节点 =顾名思义,父节点的子节点,以及父节点下的子节点。子节点可以再次成为下面节点的父节点。
根节点 =最上面的节点,树的原点
叶节点 =也称为外部节点,可以看作是一个“死胡同”,它是最后一个节点,下面没有子节点
内部节点 =也称为内部节点或分支节点。它是一个上下都有连接的节点(子节点和父节点)
你还没上媒体?每月仅需 4.16 美元。
开始使用
为了更好地理解这在计算机科学话题中的表现,你可以在下面找到一段 HTML 代码。树有助于构建网站,网站通常可以用树来描述。
**<html>
<head>**
**<meta charset=utf-8" />**
**<title>**Ball Tree vs. KD Tree**</title>**
**<nav>**
**<a href="/r/">**R**</a>**
**<a href="/js/">**JavaScript**</a>**
**<a href="/python/">**Python**</a>**
**</nav>**
**</head>****<body>**
**<h1>**What is a tree?**</h1>**
**<ul>**
**<li>**List item one**</li>**
**<li>**List item two**</li>**
**</ul>**
**<h2>**How does a tree look like?**</h2>**
**</body>**
**</html>**

html 代码的树形可视化。
KD-Tree 算法和 Ball 算法都是构建这种树的二进制算法。在这个上下文中,二进制意味着每个父节点只有两个子节点。典型地,该算法应用于最近邻搜索。
球树算法
球树算法可以被视为一个度量树。度量树考虑数据点所在的度量空间来组织和构造数据点。使用度量标准,点不必是有限维的或在向量中(Kumar,Zhang & Nayar,2008)。
该算法将数据点分成两个聚类。每个集群被一个圆(2D)或一个球(3D)包围。这个球通常被称为超球。
“一个超球是距离称为其中心的给定点恒定距离的点集。”— 维基百科
从簇的球体形式,衍生出名称 球树算法 。每个簇代表树的一个节点。让我们看看算法是如何执行的。
选择的子节点之间的距离最大,通常在树的每一层使用以下结构。
首先,设置整个数据点云的质心。与质心距离最大的点被选为第一个群集和子节点的中心。离第一聚类中心最远的点被选为第二聚类的中心点。然后,将所有其他数据点分配给距离中心最近的节点和聚类,可以是聚类 1 或聚类 2。任何点只能是一个集群的成员。球体线可以彼此相交,但是这些点必须明确地分配给一个簇。如果一个点正好在两个中心的中间,并且随后到两边的距离相同,则该点必须被分配到一个聚类中。集群可能不平衡。这基本上是球树算法背后的概念。将数据点分成两个群/球的过程在每个群内重复,直到达到定义的深度。这导致嵌套集群包含越来越多的圆。

球树算法的可视化。
如上图所示,树的深度为 2。质心 1 是算法的开始。一个球体(2D)被放置在所有数据点(灰色)周围。从中心开始,选择聚类的最远点,这里是数字 3 或数字 9。这是星团 1 的新中心,这里是紫色星团的 3 号。离第三个点最远的点是聚类 2 的中心。这是橙色星团的 9 号。然后,包括紫色球体的所有数据点被考虑用于新质心 2 的计算。对位于橙色球体中的所有数据点进行同样的操作,得到质心 3。最远的点再次成为新群的中心。数据点编号 3 是距离质心 2 和新簇中心最远的点。
在质心 2 的另一侧,它和数据点 1 之间的最大距离 ist。它是第二个集群的中心。然后对橙色侧也执行该步骤,再次产生两个集群。然而,橙色的一面是不平衡的。
生成的树如下所示(M 是质心为 1 的球体和包含所有数据点的起始球体)。从那里,集群被划分为深度为 2:

产生的球树。
KD 树算法
KD 树算法是最常用的最近邻算法之一。数据点在每个节点被分成两组。像前面的算法一样,KD 树也是一个二叉树算法,总是以最多两个节点结束。选择的分割标准通常是中间值。在下图的右侧,您可以看到数据点的确切位置,左侧是它们的空间位置。

数据点及其在坐标系中的位置。
KD 树算法首先使用第一轴的中值,然后在第二层中使用第二轴的中值。我们从 x 轴开始,
按升序排序的 x 值是:1,2,3,4,4,6,7,8,9,9。接下来,中位数是 6。
数据点然后被分成较小的和较大的,等于 6。这导致(1,2) (2,3) (3,4) (4,5) (4,6)在左侧,而(6,5) (7,9) (8,7) (9,6) (9,1)在另一侧的簇上。在坐标系中画出 6 的中值,就可以看到这两个可视化的集群。

将 6 的 x 中值绘制到坐标系中。
现在我们要用 Y 轴。我们已经有了两个集群,所以我们需要分别查看它们。在左边,我们得到了排序后的 y 值:2,3,4,5,6。中位数是 4。这会在值 4 处产生一条分隔线,坐标系如下所示:

中间值 4 分隔 X 中间值(=6)左侧的数据点。
这些值以 4 分隔,第一个分类包含(2,3) (1,2)。第二组包含点(4,6) (3,4) (4,5)。
在 x-median 的另一边,4 是目前的 5 个点(包括点(5,6))。按照排序顺序,y 值为 6,7,8,9,9。这导致中值为 8,并且第一个聚类包含(9,1)和(6,5)。第二个聚类包含(8,7)(7,9)(9,6)。
最终的坐标系如下所示。数据点被分成深度为 2 (X 和 Y)的 4 个聚类。

最终的空间分离。
但是,这棵树看起来像什么?让我们看看结果树是如何划分的。

KD 树。
比较和总结
由于考虑了所有数据点,暴力可能是最准确的方法。因此,没有数据点被分配给错误的聚类。对于小数据集,蛮力是合理的,然而,对于增加的数据,KD 或球树是更好的选择,因为它们的速度和效率。
KD 树及其变体可以被称为“投影树”,这意味着它们基于点在一些低维空间中的投影来对点进行分类。(库马尔、张和纳亚尔,2008 年)
对于低维数据,KD 树算法可能是最好的解决方案。如上所述,KD 树的节点划分是轴对齐的,不能采取不同的形状。所以分布可能没有正确映射,导致性能下降。
对于高维空间,球树算法可能是最好的解决方案。它的性能取决于训练数据的数量、维度和数据的结构。由于没有清晰的结构,具有许多噪声数据点也可能导致糟糕的性能。
感谢您的阅读,希望您喜欢!
请继续关注 Marius Hucker 的新文章。如果您还没有注册,您将创建一个中型帐户…
medium.com](https://medium.com/subscribe/@hucker.marius)
参考文献
k 近邻(KNN)是一个简单的机器学习算法,可用于分类和…
github.com](https://github.com/JKnighten/k-nearest-neighbors/wiki/KNN-and-BallTree-Overview) [## 球树深入浅出的解释——Linux 大叔
2015 年 1 月 20 日在这篇博客和接下来的几篇博客中,我将讨论 k-近邻(k-NN)算法的实现…
ashokharnal.wordpress.com](https://ashokharnal.wordpress.com/tag/ball-tree-explained-in-simple-manner/) [## Python 中最近邻搜索的基准测试
我最近提交了一个 scikit-learn pull 请求,其中包含一个全新的球树和快速最近邻的 kd 树…
jakevdp.github.io](https://jakevdp.github.io/blog/2013/04/29/benchmarking-nearest-neighbor-searches-in-python/)
树增强混合效应模型
GPBoost:结合树提升和混合效果模型
本文展示了如何使用 GPBoost 算法将树提升(有时也称为“梯度树提升”)与混合效果模型结合起来。提供了方法论以及如何使用 Python 应用 GPBoost 库的背景知识。我们展示了如何(I)训练模型,(ii)调整参数,(iii)解释模型,以及(iv)进行预测。此外,我们还比较了几种可供选择的方法。

介绍
树提升凭借其众所周知的实现,如 XGBoost、LightGBM 和 CatBoost,被广泛用于应用数据科学。除了最先进的预测准确性之外,树提升还具有以下优势:
- 非线性、不连续性和复杂高阶相互作用的自动建模
- 对预测变量中的异常值和多重共线性具有稳健性
- 预测变量单调变换的尺度不变性
- 预测变量中缺失值的自动处理
混合效应模型是一种用于聚类、分组、纵向或面板数据的建模方法。其中,它们的优势在于,它们允许更有效地学习为回归函数选择的模型(例如,线性模型或树集合)。
如 Sigrist (2020)所述, 组合的梯度树提升和混合效果模型通常比(I)普通梯度提升、(ii)标准线性混合效果模型和(iii)将机器学习或统计模型与混合效果模型相结合的替代方法表现更好。
建模分组数据
分组数据(也称为聚类数据、纵向数据、面板数据)在许多应用中,当对一个感兴趣变量的不同单位进行多次测量时,会自然出现分组数据。例子包括:
- 人们希望研究某些因素的影响(例如,学习技巧、营养、睡眠等。)对学生的考试成绩,每个学生做几个测试。在这种情况下,单元(即分组变量)是学生,感兴趣的变量是考试分数。
- 一家公司收集其客户的交易数据。对于每个客户,都有几笔交易。这些单元就是客户,而感兴趣的变量可以是交易的任何属性,比如价格。
基本上,这种分组数据可以使用四种不同的方法建模:
- 忽略分组结构。这很少是一个好主意,因为重要的信息被忽略了。
- 分别为每个小组(即每个学生或每个顾客)建模。这也不是一个好主意,因为相对于不同组的数量,每组的测量数量通常很小。
- 在您选择的模型中包含分组变量(如学生或客户 ID ),并将其视为分类变量。虽然这是一种可行的方法,但它有以下缺点。通常,每组的测量数量(例如,每个学生的测试数量、每个客户的交易数量)相对较少,而不同组的数量(例如,学生数量、客户数量等)相对较多。).在这种情况下,该模型需要基于相对少的数据学习许多参数(每个组一个),这使得学习效率低下。此外,对于树,高基数分类变量可能会有问题。
- 在混合效果模型中使用所谓的随机效果对分组变量进行建模。这通常是两种方法之间明智的妥协。第三。以上。具体而言,如下文和SIG rist(2020)所示,在树提升的情况下,与其他方法相比,这是有益的。
方法论背景
对于 GPBoost 算法,假设响应变量 y 是潜在的非线性均值函数 F(X)和所谓的随机效应 Zb 的和:
y = F(X) + Zb + e
在哪里
- y 是响应变量(又名标签)
- x 包含预测变量(即特征), F()是一个潜在的非线性函数。在线性混合效应模型中,这只是一个线性函数。在 GPBoost 算法中,这是树的集合。
- Zb 是假设遵循多元正态分布的随机效应
- e 是一个误差项
使用 GPBoost 算法训练该模型,其中训练意味着使用树集合学习随机效应的(共)方差参数(又名超参数) 和回归函数 F(X】。在模型被学习之后,随机效应 Zb 可以被估计(或预测,因为它经常被称为)。简而言之,GPBoost 算法是一种 boosting 算法,它迭代地学习(共)方差参数,并使用梯度和/或牛顿 boosting 步骤向树集合添加一棵树。与现有 boosting 算法的主要区别在于,首先,它考虑了由于聚类而导致的数据之间的依赖性,其次,它学习随机效应的(共)方差参数。有关该方法的更多详细信息,请参见 Sigrist (2020) 。在 GPBoost 库中,可以使用(加速)梯度下降或 Fisher 评分来学习(共)方差参数,使用 LightGBM 库来学习树。特别是,这意味着 LightGBM 的全部功能都是可用的。
如何在 Python 中使用 GPBoost 库
在下文中,我们将展示如何使用 Python 中的 GPBoost 库来应用组合的树增强和混合效果模型。本文中使用的完整代码可以在这里作为 Python 脚本找到。注意,还有一个等价的 R 包。更多相关信息可在这里找到。
装置
pip install gpboost -U
模拟数据
我们在这里使用模拟数据。我们采用众所周知的非线性函数 F(X) 。为了简单起见,我们使用一个分组变量。但是同样可以使用几种随机效应,包括分层嵌套效应、交叉效应或随机斜率效应。样本的数量是 5000,不同组或聚类的数量是 500。我们还生成测试数据来评估预测的准确性。对于测试数据,我们既包括已知的观察组,也包括新的未观察组。
import gpboost as gpb
import numpy as np
import sklearn.datasets as datasets
import time
import pandas as pd# Simulate data
ntrain = 5000 # number of samples for training
n = 2 * ntrain # combined number of training and test data
m = 500 # number of categories / levels for grouping variable
sigma2_1 = 1 # random effect variance
sigma2 = 1 ** 2 # error variance
# Simulate non-linear mean function
np.random.seed(1)
X, F = datasets.make_friedman3(n_samples=n)
X = pd.DataFrame(X,columns=['variable_1','variable_2','variable_3','variable_4'])
F = F * 10**0.5 # with this choice, the fixed-effects regression function has the same variance as the random effects
# Simulate random effects
group_train = np.arange(ntrain) # grouping variable
for i in range(m):
group_train[int(i * ntrain / m):int((i + 1) * ntrain / m)] = i
group_test = np.arange(ntrain) # grouping variable for test data. Some existing and some new groups
m_test = 2 * m
for i in range(m_test):
group_test[int(i * ntrain / m_test):int((i + 1) * ntrain / m_test)] = i
group = np.concatenate((group_train,group_test))
b = np.sqrt(sigma2_1) * np.random.normal(size=m_test) # simulate random effects
Zb = b[group]
# Put everything together
xi = np.sqrt(sigma2) * np.random.normal(size=n) # simulate error term
y = F + Zb + xi # observed data
# split train and test data
y_train = y[0:ntrain]
y_test = y[ntrain:n]
X_train = X.iloc[0:ntrain,]
X_test = X.iloc[ntrain:n,]
学习和预测
下面的代码显示了如何训练模型并进行预测。如下所示,学习的方差参数接近真实值。注意,当进行预测时,可以对均值函数 F(X)和随机效应 Zb 进行单独的预测。
# Define and train GPModel
gp_model = gpb.GPModel(group_data=group_train)
# create dataset for gpb.train function
data_train = gpb.Dataset(X_train, y_train)
# specify tree-boosting parameters as a dict
params = { 'objective': 'regression_l2', 'learning_rate': 0.1,
'max_depth': 6, 'min_data_in_leaf': 5, 'verbose': 0 }
# train model
bst = gpb.train(params=params, train_set=data_train, gp_model=gp_model, num_boost_round=31)
gp_model.summary() # estimated covariance parameters
#Covariance parameters:
# Error_term Group_1
#Param. 0.92534 1.016069
# Make predictions
pred = bst.predict(data=X_test, group_data_pred=group_test)
y_pred = pred['response_mean']
np.sqrt(np.mean((y_test - y_pred) ** 2)) # root mean square error (RMSE) on test data. Approx. = 1.26
参数调谐
仔细选择调整参数对于所有升压算法都很重要。可以说,最重要的调优参数是提升迭代的次数。太大的数值通常会导致回归问题中的过度拟合,而太小的数值则会导致“欠拟合”。在下文中,我们展示了如何使用交叉验证来选择提升迭代的次数。其他重要的调整参数包括学习速率、树深度和每片叶子的最小样本数。为了简单起见,我们不在这里对它们进行调优,而是使用一些默认值。
# Parameter tuning using cross-validation (only number of boosting iterations)
gp_model = gpb.GPModel(group_data=group_train)
cvbst = gpb.cv(params=params, train_set=data_train,
gp_model=gp_model, use_gp_model_for_validation=False,
num_boost_round=100, early_stopping_rounds=5,
nfold=4, verbose_eval=True, show_stdv=False, seed=1)
best_iter = np.argmin(cvbst['l2-mean'])
print("Best number of iterations: " + str(best_iter))
# Best number of iterations: 31
更新 :从版本 0.4.3 开始,GPBoost 现在有了一个函数(grid_search_tune_parameters),可以使用随机或确定性网格搜索进行参数调整。详见本 Python 参数调优演示 。
特征重要性和部分相关性图
特征重要性图和部分相关性图是用于解释机器学习模型的工具。这些可以如下使用。
# Plotting feature importances
gpb.plot_importance(bst)

特征重要性图
单变量部分相关图
from pdpbox import pdp
# Single variable plots (takes a few seconds to compute)
pdp_dist = pdp.pdp_isolate(model=bst, dataset=X_train,
model_features=X_train.columns,
feature='variable_2',
num_grid_points=50,
predict_kwds={"ignore_gp_model": True})
pdp.pdp_plot(pdp_dist, 'variable_2', plot_lines=True)

多元部分相关图
# Two variable interaction plot
inter_rf = pdp.pdp_interact(model=bst, dataset=X_train,
model_features=X_train.columns,
features=['variable_1','variable_2'],
predict_kwds={"ignore_gp_model": True})
pdp.pdp_interact_plot(inter_rf, ['variable_1','variable_2'], x_quantile=True, plot_type='contour', plot_pdp=True)
# ignore any error message

用于可视化交互的二维部分相关图
SHAP 价值观
SHAP 值和依赖图是模型解释的另一个重要工具。这些可以按如下方式创建。注意:为此需要 shap 版本> =0.36.0。
import shap
shap_values = shap.TreeExplainer(bst).shap_values(X_test)
shap.summary_plot(shap_values, X_test)
shap.dependence_plot("variable_2", shap_values, X_test)

SHAP 价值观

变量 2 的 SHAP 相关图
与替代方法的比较
在下文中,我们使用上述模拟数据将 GPBoost 算法与几种现有方法进行比较。我们考虑以下替代方法:
- 线性混合效应模型(‘Linear _ ME’)其中 F(X)是线性函数
- 忽略分组结构的标准梯度树提升(‘Boosting _ Ign’)
- 标准梯度树提升,包括作为分类变量的分组变量(‘Boosting _ Cat’)
- 混合效果随机森林(‘MERF’)(详见此处和 Hajjem et al. (2014) )
我们根据使用均方根误差(RMSE)和计算时间(以秒为单位的时钟时间)测量的预测准确性来比较这些算法。结果如下表所示。产生这些结果的代码可以在下面的附录中找到。

GPBoost 和替代方法的比较。
我们看到 GPBoost 和 MERF 在预测准确性方面表现最好(几乎一样好)。此外,GPBoost 算法比 MERF 算法快大约 1000 倍。线性混合效应模型(' Linear_ME ')和忽略分组变量的树提升(' Boosting_Ign ')的预测精度明显较低。将分组变量作为分类变量(“Boosting_Cat”)的树提升也显示出比 GPBoost 或 MERF 更低的预测准确性。注意,在这个例子中,测试数据包含已经在训练数据中观察到的现有组和没有在训练数据中观察到的新组(各 50%)。如果测试数据仅包含现有组,Boosting_Cat 和 GPBoost / MERF 之间的差异更大;参见 中的实验 Sigrist (2020) 。
注意,为简单起见,我们只进行一次模拟运行(更详细的比较见【SIG rist(2020))。除了 MERF,所有计算都是使用 gpboost Python 包 0.7.6 版完成的。此外,我们使用 MERF Python 包版本 0.3。
结论
GPBoost 允许组合混合效果模型和树提升。如果应用线性混合效应模型,应调查线性假设是否确实合适。GPBoost 模型允许放宽这一假设。它可以帮助您找到非线性和相互作用,并实现更高的预测准确性。如果您是 XGBoost 和 LightGBM 等 boosting 算法的频繁用户,并且您有潜在高基数的分类变量,GPBoost(它扩展了 LightGBM)可以使学习更加高效,并产生更高的预测准确性。
据我们所知,GPBoost 库在计算速度和预测准确性方面目前是无与伦比的。其他优势是 GPBoost 支持一系列模型解释工具(可变重要性值、部分相关图、SHAP 值等)。).此外,除了分组或聚类随机效果外,还支持其他类型的随机效果,如高斯过程。
希望这篇文章对你有用。关于 GPBoost 的更多信息可以在配套文章 Sigrist (2020) 和 github 上找到。
参考
Hajjem、f . Bella vance 和 d . la rocque(2014 年)。聚类数据的混合效果随机森林。统计计算与模拟杂志, 84 (6),1313–1328。
柯国光,孟,陈,王,陈,马,刘天元(2017)。Lightgbm:一种高效的梯度推进决策树。在神经信息处理系统的进展(第 3146–3154 页)。
皮涅罗和贝茨博士(2006 年)。S 和 S-PLUS 中的混合效果车型。斯普林格科学&商业媒体。
西格里斯特,F. (2020)。高斯过程增强。arXiv 预印本 arXiv:2004.02653 。
附录
替代方法比较代码
results = pd.DataFrame(columns = ["RMSE","Time"],
index = ["GPBoost", "Linear_ME","Boosting_Ign","Boosting_Cat","MERF"])
# 1\. GPBoost
gp_model = gpb.GPModel(group_data=group_train)
start_time = time.time() # measure time
bst = gpb.train(params=params, train_set=data_train, gp_model=gp_model, num_boost_round=best_iter)
results.loc["GPBoost","Time"] = time.time() - start_time
pred = bst.predict(data=X_test, group_data_pred=group_test)
y_pred = pred['response_mean']
results.loc["GPBoost","RMSE"] = np.sqrt(np.mean((y_test - y_pred) ** 2))
# 2\. Linear mixed effects model ('Linear_ME')
gp_model = gpb.GPModel(group_data=group_train)
X_train_linear = np.column_stack((np.ones(ntrain),X_train))
X_test_linear = np.column_stack((np.ones(ntrain),X_test))
start_time = time.time() # measure time
gp_model.fit(y=y_train, X=X_train_linear) # add a column of 1's for intercept
results.loc["Linear_ME","Time"] = time.time() - start_time
y_pred = gp_model.predict(group_data_pred=group_test, X_pred=X_test_linear)
results.loc["Linear_ME","RMSE"] = np.sqrt(np.mean((y_test - y_pred['mu']) ** 2))
# 3\. Gradient tree-boosting ignoring the grouping variable ('Boosting_Ign')
cvbst = gpb.cv(params=params, train_set=data_train,
num_boost_round=100, early_stopping_rounds=5,
nfold=4, verbose_eval=True, show_stdv=False, seed=1)
best_iter = np.argmin(cvbst['l2-mean'])
print("Best number of iterations: " + str(best_iter))
start_time = time.time() # measure time
bst = gpb.train(params=params, train_set=data_train, num_boost_round=best_iter)
results.loc["Boosting_Ign","Time"] = time.time() - start_time
y_pred = bst.predict(data=X_test)
results.loc["Boosting_Ign","RMSE"] = np.sqrt(np.mean((y_test - y_pred) ** 2))
# 4\. Gradient tree-boosting including the grouping variable as a categorical variable ('Boosting_Cat')
X_train_cat = np.column_stack((group_train,X_train))
X_test_cat = np.column_stack((group_test,X_test))
data_train_cat = gpb.Dataset(X_train_cat, y_train, categorical_feature=[0])
cvbst = gpb.cv(params=params, train_set=data_train_cat,
num_boost_round=1000, early_stopping_rounds=5,
nfold=4, verbose_eval=True, show_stdv=False, seed=1)
best_iter = np.argmin(cvbst['l2-mean'])
print("Best number of iterations: " + str(best_iter))
start_time = time.time() # measure time
bst = gpb.train(params=params, train_set=data_train_cat, num_boost_round=best_iter)
results.loc["Boosting_Cat","Time"] = time.time() - start_time
y_pred = bst.predict(data=X_test_cat)
results.loc["Boosting_Cat","RMSE"] = np.sqrt(np.mean((y_test - y_pred) ** 2))
# 5\. Mixed-effects random forest ('MERF')
from merf import MERF
rf_params={'max_depth': 6, 'n_estimators': 300}
merf_model = MERF(max_iterations=100, rf_params=rf_params)
print("Warning: the following takes a lot of time")
start_time = time.time() # measure time
merf_model.fit(pd.DataFrame(X_train), np.ones(shape=(ntrain,1)), pd.Series(group_train), y_train)
results.loc["MERF","Time"] = time.time() - start_time
y_pred = merf_model.predict(pd.DataFrame(X_test), np.ones(shape=(ntrain,1)), pd.Series(group_test))
results.loc["MERF","RMSE"] = np.sqrt(np.mean((y_test - y_pred) ** 2))
print(results.apply(pd.to_numeric).round(3))
Python 的树形图基础
一种非常简单的展示构图的方式
不缺乏可视化来显示单个值如何组成一个整体。有些可能非常复杂和具体;其他的,比如我们今天要探索的,非常简单易用。
从馅饼到华夫饼图表,展示整体比例的想法通常是讲故事的良好开端。您可以从构成开始,然后逐个探索不同的值,或者比较不同实体或不同时期的不同结构。

树形图
美国计算机科学家、马里兰大学教授本·施奈德曼在 20 世纪 90 年代首次使用了这种可视化技术。

Ben Shneiderman 和他的两个树状图艺术项目作品
他的可视化变得非常流行,你可以在很多工具和语言中找到不同的实现和不同的算法来创建它们,比如 Tableau、PowerBi、Python、R 等等。
方形的
在接下来的例子中,我们将使用 Python 和 Matplotlib、Squarify 和 Pandas 来创建和设计我们的树形图。稍后我们将快速了解 Plotly 能做什么。
import matplotlib.pyplot as plt
import squarify # pip install squarify
import pandas as pd
Squarify 没有什么复杂的,我们可以简单地向它传递一个数字列表,它就会计算出布局。
sizes = [50, 25, 12, 6]squarify.plot(sizes)
plt.show()

小菜一碟。
现在,你不能从仅仅看这些随机的方块中得到太多的信息。也许你可以对整体构成有一些见解,但除非你熟悉数据本身,否则你无法识别那些方块。
让我们更具体一些,给我们的树形图添加一些标签。我们也可以去掉轴,让颜色柔和一点。
sizes=[50, 25, 12, 6]
label=["50", "25", "12", "6"]squarify.plot(sizes=sizes, label=label, alpha=0.6 )
plt.axis('off')plt.show()

旗帜
酷!你注意到颜色是如何变化的吗?我们降低了α,这就是为什么它们不那么亮,但不仅如此,它们还改变了位置。
将颜色分配给方块的方式有一些随机性——别问我为什么。
尽管随机性很有用,会带来一些意外的惊喜,但它并不总能让你得到一个有意义的配色方案。为了避免这种情况,我们可以定义我们的颜色列表。
sizes=[50, 25, 12, 6]
label=["50", "25", "12", "6"]
color=['red','blue','green','grey']squarify.plot(sizes=sizes, label=label, color=color, alpha=0.6 )
plt.axis('off')plt.show()

定义我们的颜色以获得更清晰的图像只是其中的一个方面。
颜色可以添加另一种编码,例如,如果我们在不同的地方有一家公司,我们可能希望看到每个公司对我们的总资产有多少贡献,这将是我们的树形图中方块的大小。
然后,我们可以根据利润、生产数量、成本或任何我们想要的东西来绘制这些地点的彩色地图。
让我们举一个更容易理解的例子来说明这个想法,假设我们在两个地方有四家公司。我们可以在不同的地方使用不同的颜色,在同一个地方使用相似的颜色。
sizes=[50, 25, 12, 6]
label=["BC 1", "OT 1", "OT 2", "OT 3"]
color=['red','#1C9FB0','#32A0CE','#1C51B0']squarify.plot(sizes=sizes, label=label, color=color, alpha=0.6 )
plt.axis('off')plt.show()

现在我们可以知道每个人贡献了多少,我们也可以很容易地区分 BC 和 OT。
可量测性
我们可以使用 Pandas 数据框而不是列表,这为我们可以在数据中执行的各种操作打开了大门,并大大增加了可伸缩性。
让我们看看这种可视化可以扩展到什么程度。我将使用英国按品种分类的农场动物数量数据集。
df = pd.read_csv('data/animal-population-by-breed-on_1-march-2010.csv')# convert to numeric and drop na
df['Number of Animals'] = pd.to_numeric(df['Number of Animals'], errors='coerce')
df.dropna(inplace=True)df.head()

现在,我们可以将“动物数量”作为我们的尺寸,将“品种”作为我们的标签。
fig, ax = plt.subplots(1, figsize = (12,12))squarify.plot(sizes=df['Number of Animals'],
label=df['Breed'],
alpha=.8 )plt.axis('off')
plt.show()

呃,这可不好。标签太多!
让我们尝试对数据框进行排序,并标记最大的方块。
df.sort_values('Number of Animals', ascending=False, inplace=True)
df

fig, ax = plt.subplots(1, figsize = (12,12))squarify.plot(sizes=df['Number of Animals'],
label=df['Breed']**[:5]**,
alpha=.8 )plt.axis('off')
plt.show()

厉害!有了最重要的人群的分类数据和标签,我们可以得到一些见解。
Plotly
有了 Plotly,我们可以在树形图上更进一步,分组数据,添加工具提示,颜色图非常简单。
让我们快速地看一个例子,只是为了说明可能性。我将使用一个视频游戏销售数据集和 Plotly Express。
首先,让我们加载数据并删除空行。
df = pd.read_csv('data/vgsales.csv')
df.dropna(inplace=True)
df

现在我们来绘制它!我们将传递数据框、包含类别的字段的路径、方块的大小和颜色的强度。
Plotly 将使用颜色图绘制我们的绘图,添加颜色条、工具提示,并根据方块的大小调整标签的大小。
import plotly.express as pxfig = px.treemap(df,
path=['Platform', 'Genre'],
values='Global_Sales',
color='NA_Sales'
)fig.show()

好的,标签不是每个方块都可读的,但是我们确实有工具提示来帮助我们找出那些我们不能读的,并且我们在这里添加了比以前更多的信息。
我个人并不热衷于互动;老实说,我很难想出如何将它添加到故事中——我的意思是,我们开始探索树状图,作为讲故事的开始和简化构图的方式,但这种可视化并不那么友好。
随着我们的视觉化增加了交互性和复杂性,我会提出相反的建议。通过显示单个方块或组,从您发现的具体见解开始,然后在最后,展示您的完整树形图,让用户自己探索它。
看看 Nadieh Bremer 的用和弦图讲故事。她的最终视觉化相当复杂。尽管如此,她在向你展示大图之前处理细节的方式非常棒,让数据更容易理解。
这里有一个我们创建的交互式 viz 的链接,在这里你可以找到代码。
感谢阅读我的文章。我希望你喜欢它。
资源: Treemaps 马里兰大学;
Python 图库—Treemap;
Datacadamia—Treemaps;
plottly—Treemaps;
Plotly — HTML 导出;
Github—Squarify;
树状图,为什么和如何
用树形图讲故事

图片由 Pixabay 提供
渐渐地,硬盘上的文件数量从几十个增加到几百个,从几百个增加到几千个。同样,文件夹和目录的数量几乎呈指数级增长。有必要找到一些图表,让相对简单的方式来可视化新的 利维坦的内容。
这个解决方案来自于 Ben Shneiderman ,马里兰大学的计算机科学教授和人机交互实验室 (HCIL)的创建者。他在寻找“目录树结构的紧凑可视化”(#1)。很明显,目录的结构对应于一个分层的树形结构,但是现有的图非常庞大,因此效率很低。用他自己的话来说:“树形结构的节点连接图变得太大而无用,所以我探索了在空间受限的布局中显示树的方法”(#2)。
最终结果是一个名为 Treemap 的图表。令人意想不到的是,它目前的应用大大超出了最初的目的。
为什么:树形图是一个基于矩形的可视化,它允许你表示一个层次有序(树形结构)的数据集。概念上的想法是在一个物理限制的空间中比较数量和显示一些层次结构的模式。为此,不同大小和颜色的矩形用于从不同角度显示数据集。目标不是指出精确的数值,而是将数据集“分解”成其组成部分,并快速识别其较大和较小的组成部分。

图 1:用 Plotly 创建的树形图。
它们还用于表示等级、各部分之间的差异以及非等级“扁平”结构中的相对比例。从这个意义上说,它们被认为是传统饼图的替代品,传统饼图通常用于可视化“部分到整体”的关系。与饼状图最大的不同在于,树形图允许你在一个相对较小的空间内比较组成 嵌套矩形的方案中整体的各个部分。与饼状图相比,饼状图的一个优势在于,它们可以在一个结构中包含数十或数百个部分,这个结构可能是分层的,也可能不是。
物理空间的有效利用和智能色彩管理使它们成为在各种商业分析应用以及金融、商业、政府、选举和类似领域中可视化大量信息的出色工具:标准普尔 500 指数;Merval(布宜诺斯艾利斯证券交易所);各省或各州的选举结果;按地区、国家或部门分列的出口;按产品划分的销售额;等等。以下树形图显示了 2017 年牙买加按行业(农业、石油、化工、矿产等)划分的出口情况。) (#3).

图 2:2017 年牙买加出口。资料来源:commons.wikimedia.org(# 3)。
由于前面提到的原因,树图是商业智能(BI)表示中最常用的可视化元素之一,尽管当可视化的目标是在数据集的组件之间进行精确比较时,不应使用树图。
如何实现:它们由一系列嵌套的矩形(瓷砖)组成,其面积与它们所代表的数据成比例。较大的矩形(层次的较高层)指示数据树的根或主分支,并被细分成较小的矩形,指示树的每个分支所具有的节点(层次的较低层)的大小。数据集的数值应该是正的,并且对应于矩形的面积。
色彩的巧妙运用让新的维度融入到图表中。通常的程序是在不同的矩形中使用颜色来表示第二个分类或定量值。因此,通过单一颜色的强度变化来表示等级;通过两种颜色强度的变化来显示不同的正负定量值。下图显示了高于和低于阈值的值,这些值以两种颜色的不同强度排列。

图 3:用 Squarify 创建的树形图。
从上面可以推断出,恰当地使用颜色可以让我们使用树形图来表示损失、销售额下降或其他非正值。总是指不是由矩形面积表示的第二定量值。
下面的树状图显示了标准普尔 500 (#4)。它是纽约证券交易所(NYSE)和纳斯达克(NASDAQ)500 只广泛持有的股票的指数或集合。矩形的大小表示指数中每家公司的相对规模。不同的颜色和不同的颜色强度显示了特定一天的阳性、阴性和“中性”结果的刻度:非常亮的红色表示向下的大偏移,非常亮的绿色表示向上的大偏移。毫无疑问,树状图是在相对较小的空间内显示大量信息的最合适的可视化方式。

图 4:标准普尔 500(排名第四)。
几个算法允许你通过彩色矩形形状的嵌套图形显示分层数据:方形、切片和方块,以及条状。 Squarified 是商业数据可视化工具实现最多的算法。它基于一种策略,即设法使每个区块尽可能地呈正方形,以便于它们之间的比较。它还试图从图的左上到右下以递减的方式排列数据集的连续成员(块、切片)。最后,该算法在台式机、笔记本电脑和手机上有很好的视觉呈现。
用树状图讲述故事时需要考虑的一些注意事项:
- 请记住,树状图很难阅读,因为它们依赖于观众通过比较区域来解码数字信息的能力。不要忘记,人类擅长估算距离,却不擅长计算面积。由于在树形图中显示了大量的区域(矩形、瓦片),这个缺点在树形图中被放大了。在使用树形图之前,有必要评估条形图或饼状图是否没有以较低的视觉强度讲述相同的故事。
- 当真正重要的信息有相似的数值时,这些图表是不可取的。在这种情况下,算法会生成非常相似的区域块,很难在仪表板中进行比较。
- 树形图不能用于显示可能取负值(如损失)的数值变量。请记住,这种类型的限制是所有那些使用区域来编码数量变量的图表所共有的。
- 如果你想在交互式演示中使用它们,要非常小心。请记住,如果比较区域相对困难,那么比较交互变化的区域就更加复杂。
- 另一个很难进行好的比较的问题是缺少一个共同的基线,这是条形图所没有的缺点。
- 当大块和小块之间的尺寸差异非常大时,通常不可能在较小的矩形中包含文本。标准的解决方案是使用工具提示在用户悬停在矩形上时提供与这些矩形相关的信息。
当然,了解 Stephen first 对这个问题的看法是很重要的:“当传统的图形(如条形图)无法使用时,因为在单个图形甚至单个屏幕上的一系列图形中有太多的项目要用条形来表示,树形图通过优化使用屏幕空间来解决这个问题。因为它们依赖于预先注意的属性来编码我们无法精确比较的值(面积和颜色),所以我们保留这种方法用于无法使用其他更精确的可视化或没有必要精确的情况。”(#5).
用 Python 绘制树状图
正如的 Python 图库 (#6)所指出的,“dataviz 的可能性相当差”。要用 Python 绘制树形图,必须导入 Squarify 库 ( pip install squarify )并使用 squarify.plot 函数,通过 alpha 属性指示值、标签、颜色和透明度。与一些商业工具生成的图相比,生成的图非常有限。
一个更好的替代方法是通过 treemap 指令使用 Plotly ,其语法远优于其 Squarify 等价指令。除了指示值、标签和颜色之外,在 plotly.express 中的 treemap 语法还包括 path 参数,它允许你定义从根到分支(#7)的扇区层次。
plotly.express.treemap(*data_frame=None*, *names=None*, *values=None*, *parents=None*, *ids=None*, *path=None*, *color=None*, *color_continuous_scale=None*, *range_color=None*, *color_continuous_midpoint=None*, *color_discrete_sequence=None*, *color_discrete_map={}*, *hover_name=None*, *hover_data=None*, *custom_data=None*, *labels={}*, *title=None*, *template=None*, *width=None*, *height=None*, *branchvalues=None*, *maxdepth=None*)
结论
当数据集以具有根、分支和节点的树形布局的分层顺序构造时,树形图是一种合适的可视化类型。它允许我们在有限的空间内以非常有效的方式显示大量数据的信息。可视化的主要目的是在不同层级之间进行不精确的比较。它们也用于对非层次数据进行“整体的一部分”分析。它们不应该用于得出准确的结论,带有负面的定量数据,或者当数字差异不允许进行适当的比较时。

图片由来自 Unsplash 的 David Kovalenko 提供
如果你对这篇文章感兴趣,请阅读我以前的(https://medium.com/@dar.wtz):
Mekko 图表,为什么&如何
为什么和如何
towardsdatascience.com](/mekko-charts-f38311c576e2)
仪表图和项目符号图,为什么和如何,用仪表讲故事
为什么&如何,用量表讲故事
towardsdatascience.com](/gauge-bullet-charts-cfe171ca3094)
参考文献
1.本·施奈德曼(1992 年)。“用树状图显示树木:二维空间填充方法”。美国计算机学会图形汇刊。11: 92–99.doi:10.1145/102377.115768。
2.— Ben Shneiderman,2006 年 4 月 11 日,使用树形图可视化发现商业智能,http://www.perceptualedge.com/articles/b-eye/treemaps.pdf
3.—https://commons . wikimedia . org/wiki/File:Jamaica _ Export _ treemap . jpg
4.—https://www.mergersandinquisitions.com/what-is-the-sp-500/
5.—Stephen first,2009,《现在你看到了:定量分析的简单可视化技术》,分析出版社;第一版(2009 年 4 月 1 日)
6.——【https://python-graph-gallery.com/treemap/
7.—https://plotly . com/python-API-reference/generated/plotly . express . treemap . html
2020 年及以后人工智能的趋势

在企业中实现真实、不可见和增强的三种方式
无论是机器撰写的新闻文章,人工智能主导的网络安全还是情绪检测的关键发展,2019 年无疑为人工智能(AI)世界带来了前沿成就。展望 2020 年代,我们能期待什么?
对企业和消费者来说,人工智能肯定会继续以充满希望的方式发展。迄今为止围绕人工智能的宣传正迅速被更切实的现实所取代,为组织提供了以结果驱动的方式收获人工智能回报的机会。
以下是人工智能将如何帮助商业领袖和决策者拓展其企业的边界,以提高业务绩效:
人工智能:不再只是一句流行语

如今,AI 对大多数工作场所来说并不陌生。根据 Forrester 的研究,53%的全球数据和分析决策者表示,他们已经实施、正在实施或正在扩展或升级某种形式的人工智能的实施。
2019 年,我们看到围绕人工智能的对话发生了转变。组织开始关注从人工智能中获得的商业价值,供应商就人工智能成熟度教育客户。与此同时,公司开始寻找数据科学家以外的角色,以支持他们的人工智能之旅。
今年,我们将继续关注用人工智能解决“真正的”挑战,而不是大肆宣传变得模糊不清的“人工智能”将会有更多关于你可以从哪里开始使用人工智能以及将人工智能应用于商业需要哪些团队技能的讨论。您将听到更多关于如何通过采用实现业务投资回报的信息。
更多的企业将意识到,让合适的人负责他们的人工智能项目将产生更大的效益。例如,与没有首席数据官的公司相比,拥有专门首席数据官(CDO)或首席分析官(CAO)的公司使用人工智能、机器学习(ML)和/或深度学习的可能性已经增加了大约 1.5 倍。
通过在工作场所更加强调人工智能,每个部门和角色的员工都会注意到人工智能对企业越来越重要。
数据科学团队将继续实现欢迎具有跨学科经验的成员的好处,这将进一步扩大非技术人员的数量,在人工智能的使用中发挥更大的作用——这是由更容易获得 ML 工具包和 AutoML 功能推动的。
随着人工智能的采用越来越多,越来越多的员工实际体验到人工智能如何增强、改善甚至从根本上转变他们的战略和工作角色,人工智能将不再被归入企业的 R&D 翼。它将触及未来公司的几乎每一个部分。
隐形 AI 实现

2019 年,我们看到了狭义 AI 的巨大进步,包括卫星图像、自然语言处理(NLP)和计算机视觉等领域。展望未来,期待看到更多人工智能研究的突破,以及人工智能模型的商品化。
人工智能将越来越多地嵌入到领域驱动的解决方案中,使其变得无形而无所不在。人工智能在边缘计算中的趋势性使用就是一个例子。在更有效地利用资源的推动下,信息处理和内容收集将更接近信息源。
边缘人工智能还可以降低延迟,提高数据隐私性。人工智能驱动的推理、模式匹配和预测将融入边缘的领域应用,使用户界面更加智能。
IDC 预测,到 2022 年,75%的企业将在技术和流程开发中嵌入智能自动化。同一份报告预测,到 2024 年,人工智能将成为新的用户界面,50%的用户触摸将通过计算机视觉、自然语言、增强现实和虚拟现实来增强。人工智能将无处不在,但从未见过。
增强人工智能:人类驱动的未来

今年,你将看到更多人类与人工智能的合作。人工智能将越来越多地以人为本的方式设计,允许增强能力。
人在回路不仅仅是紧急情况下的自动防故障装置;这就像今天的自动驾驶汽车背后有一个人类司机。然而,人类在人工智能系统中的作用远不止是在出现问题时待命。
人类的输入是必要的,以减轻诸如人工智能偏差等问题,并提高模型日常决策的质量。组织已经开始关注这种需求:根据 IDC 的数据,在未来四年,我们将看到 75%的企业重新培训和发展员工,以解决新的技能集,优化人与人工智能的交互。
然而,权力越大,责任越大。Forrester 警告今年有三个公关灾难可能会“撼动科技界的声誉”:深度假货的兴起、面部识别的不当使用和过度个性化。有了人在回路系统,组织可以确定这种人工智能技术可能发生不道德转向的点,并将其引导回正确的道路上。
很明显,2020 年为人工智能在企业中的应用带来了巨大的希望。然而,企业和消费者必须明白,快速涌现的人工智能能力不是可以盲目信任的解决所有问题的黑匣子。
纠正这些错误观念对公司来说至关重要,因为他们将继续利用技术的力量。借助为持续人工输入而设计的系统,企业可以使用人工智能来解决障碍,并在 2020 年及以后创造价值。
这篇文章最初是 发表在《分析》杂志的 上。增加了插图。标题照片由 在上画出。
自然语言理解的模型预训练趋势
令牌预测的不确定未来

帕特里克·托马索在 Unsplash 上的照片
预训练现在在自然语言理解中无处不在(NLU)。不管目标应用程序是什么(例如,情感分析、问题回答或机器翻译),模型首先会在大量自由格式文本(通常有数百千兆字节)上进行预训练。目的是用一般的语言学知识初始化模型,以便以后在多种上下文中使用。一个预先训练好的精通语言学的模型可以在一个更小的数据集上微调来执行目标应用。
虽然我们已经着重确定了将模型暴露给无止境的互联网空谈者的有用性,但是模型应该如何与之交互仍然不明显。关于这种互动有两个要求。首先,需要将数据游戏化到一个任务中:在每个训练步骤中,模型试图解决该任务,接收对其性能的反馈,然后相应地调整其参数。第二,由于数据量大,任务需要无人监管:正确的预测应该已经存在于原始数据中,不需要人工注释。
传统上,预训练任务围绕着预测从文本文档中人工移除的标记。尽管它们很简单(或者正因为如此),这些技术从预训练开始就一直统治着这个领域,取得了真正显著的成果。然而,我们可能只是触及了表面。数据集中一定有很多释放的潜力,比我们童年时接触到的令牌数量多几个数量级。在最近的研究中,创新的想法已经萌芽,提出了更精细的训练前任务,如文献检索和释义。
过去:单向语言建模
一种简单而有效的技术是下一个标记预测:给定一个文本文档,训练一个模型从左到右遍历它,并根据它目前已经读取的内容预测沿途的每个标记。这项任务也被称为语言建模(LM)。语言建模的香草单向公式被现在著名的 OpenAI 的 GPT 模型采用,其巨大的计算规模弥补了训练目标的简单性。GPT-3 模型[1]在 4000 亿个令牌上训练了 1750 亿个参数,记录了前所未有的少量性能:为了解决现实世界的任务,在预训练后不需要或很少需要微调。
在预训练+微调范式开始统治 NLU 之前,伪双向语言模型有过辉煌的时刻;他们将遍历输入文本两次(从左到右和从右到左),而不是一次通过,以给出双向处理的假象。例如,为布偶系列设定趋势的 ELMo [2]使用这种技术来产生连续的输入表示,这些表示稍后将被馈送到最终任务模型中(换句话说,只有输入嵌入被预训练而不是整个网络栈)。尽管当时很流行,但伪双向 LMs 从未在预训练+微调的背景下复兴。
现在:蒙面语言建模
在过去的两年里,NLU 事实上的基石是谷歌的 BERT [3],它被预先训练了两个目标:屏蔽语言建模 (MLM)和下一句话预测。在 MLM 训练期间,模型被暴露于文本文档,其中 15%的标记被替换为特殊的[掩码]标记;它的任务是恢复这些省略。访问掩码标记两侧的上下文有助于模型双向处理文本。假设 MLM 鼓励模型比单向和伪双向 LMs 更接近地模仿人类推理。
MLM 相对于它的下一个单词预测前身的主要缺点是降低了采样效率,因为只有 15%的标记被预测。此外,[MASK]标记在预训练和微调阶段观察到的输入之间引入了差异,因为下游任务不会屏蔽它们的输入。XLNet [4]提出了解决这些问题的 MLM 的变体,但是与 BERT 相比,它的采用仍然相对有限。
未来:超越象征性预测
尽管取得了成功,令牌预测的目标并非完美无缺。他们的主要批评是,他们只关注语言的形式:模型学习连贯语言的特征,而不一定将意义与它联系起来。像 GPT-2 [5]这样的生成模型是众所周知的幻觉——也就是说,产生令人信服的看起来真实的文本,而不是锚定在现实中。这或许就是 OpenAI 不愿意开源他们模型的原因。
最近的工作在将自然语言融入现实世界的方面取得了进展。诸如 REALM (检索-增强语言模型预训练)[6]和 MARGE (检索和生成的多语言自动编码器)[7]之类的研究项目引入了超越简单令牌预测的更精细的预训练技术。
领域(检索-增强语言模型预训练)
REALM 专注于开放领域问答(open-QA)的具体应用:给定一个问题和一个文档数据库,任务是从其中一个文档中提取正确答案。遵循标准实践,在自由形式文本的大型语料库上执行预训练。这项创新是对经典 MLM 任务的调整:在预测屏蔽令牌之前,模型被训练为首先检索有助于填补空白的文档。

通过屏蔽语言建模和文档检索进行领域预训练[6]
这种技术有两个主要优点。首先,它鼓励基于证据的预测,而不是听起来很好的猜测(它也有助于在像“1696 年 7 月”这样的突出时间跨度上应用掩码,而不是任意地)。其次,它方便地为端到端的开放式质量保证微调奠定了基础,如下图所示。注意,训练数据没有明确地将问答配对链接到相关文档。但是,由于模型在预训练期间获得了一些文档相关性的概念,因此缺少这种明确的信号不太具有破坏性。主要的缺点是在每个训练步骤中检索一个文档背后的工程复杂性,同时确保这个操作(在一个潜在的大集合上)保持可区分性。

面向开放领域问答的领域微调[6]
您可能会认为在预训练期间包含一个检索步骤会降低预训练模型的通用性(毕竟,REALM 只适用于 open-QA)。但是 MARGE 和表明事实并非如此。
MARGE (检索和生成的多语言自动编码器)
上面所有的方法都建议在输入被改变后对其进行某种类型的重建。从左到右 LMs 删除被预测标记右侧的所有文本,而 MLMs 从输入文本中删除任意标记。MARGE pre-training 将这一挑战提升到了一个新的水平,并要求模型完成看似不可能的任务:重建一个它从未见过的“目标”文档,甚至没有因截断或省略而受损。取而代之的是,向模型显示与输入相关的其他“证据”文档(例如,解释或甚至将其翻译成另一种语言),并要求其重新生成原始文本。下图显示了一个示例。

用于 MARGE 预培训的目标和证据文件(改编自[7])
MARGE 和 REALM 之间的一个主要区别是,前者是一个序列到序列模型(由一个编码器和一个解码器组成),而后者只是一个解码器。这使得 MARGE 能够在广泛的下游任务上进行微调,包括辨别(例如,分类或提取问题回答)和生成任务(例如,机器翻译、摘要或释义)。MARGE 提出了一个有趣的观察,即预训练+微调范式甚至在检索仅在预训练期间执行时也成立(记住 REALM 在两个阶段都使用了它的检索器)。一个真正显著的成果是 MARGE 可以执行体面的零镜头机器翻译——也就是说,不需要对并行数据进行任何微调!
结论
增加训练数据的数量仍然是提高模型质量的可靠方法,即使存在数千亿个令牌,这种趋势似乎也不会放缓。但是,尽管接触到的文本比人类一生中处理的还要多,但机器的表现仍然不如我们,特别是在本质上具有生成性或需要复杂推理的任务中。也就是说,模型与数据交互的方式非常低效。研究团体已经开始脱离单纯依赖语言形式的预训练任务,并纳入鼓励在现实世界中锚定语言理解的目标。
参考
- 布朗等人,语言模型是少量学习者 (2020)
- 彼得斯等人,深度语境化的词语表征 (2018)
- Devlin 等人, BERT:用于语言理解的深度双向转换器的预训练 (2018)
- 杨等, XLNet:面向语言理解的广义自回归预训练 (2019)
- 拉德福德等人,语言模型和无监督多任务学习者 (2019)
- Guu 等人,领域:检索-增强语言模型预训练 (2020)
- 刘易斯等人,通过转述进行预训练 (2020 年)
考验和磨难:在科拉布和 TPU 上使用 Keras

Max 陈在 Unsplash 上的照片
延续 之前的帖子 关于在 Colab 和 TPU 上试验 Keras,同时尝试一些旧的 NLP 教程。在这篇文章中,我试图帮助我的业余爱好者节省一些时间和挫折。
根据上一篇文章中概述的设置,在 160 万条推文中训练情感分类器的速度非常快(每个时期大约 500 秒,而 1.5 小时)。然而,完整的训练仍然需要一个多小时。对于这一轮来说,81%的准确度就足够了。因此,我决定现在停止花时间在训练上,这样我就可以专注于进一步的实验。
为此,我想将模型保存到磁盘(即映射的 Google Drive),这样每当我重新打开 Colab 时,我就可以加载模型并从我离开的地方继续。这比预期的要困难得多。我不得不尝试各种组合,然后才能满足于一个折衷的解决方案(使用 h5 格式保存到 Google Drive )。
- 首先,我尝试在映射的 Google Drive 文件夹中使用 model.save() 。出现以下错误:
*UnimplementedError: File system scheme ‘[local]’ not implemented.*
2.显然,TPU 训练过的 SavedModel 模型只能保存到谷歌云存储中。这让我开始了在 google.cloud.storage API 和各种 gsutil 命令中尝试 blobs 的徒劳之旅。用这些工具将模型(二进制格式)保存到 GCS 似乎是不可能的。
最后,我想到了最简单的解决方案——简单地使用GS://
3.我在运行 load_model 时不断遇到这个错误:
*NotFoundError: Unsuccessful TensorSliceReader constructor*
显然,它无法在变量/变量文件夹中找到一些匹配的文件。这是真的,因为那个文件夹是空的,我不知道为什么 tensorflow 没有填充那个文件夹。那不是我能解决的问题。
假设这里引用的变量是优化器变量,我在保存时尝试了include _ optimizer = False。对错误没有影响。
4.接下来,我尝试了 h5 格式,理由是它是一个单独的文件,所以不需要处理丢失的文件夹和文件。这次尝试失败得很惨,因为这个错误导致我甚至无法保存文件:
*OSError: Unable to create file (unable to open file: name = ‘gs:<savepath>’, errno = 2, error message = ‘No such file or directory’, flags = 13, o_flags = 242)*
保存路径的目录存在,正在运行!gsutil ls 命令T3 确认了这一点。该文件显然不存在,因为 tensorflow 尚未保存它。
作为一个黑客实验,我复制了一个虚拟文件到那个位置,tensorflow 可以覆盖它。没什么区别。
5.接下来尝试将 h5 保存到本地映射的 Google Drive,并加载回来,它工作得非常好。虽然我仍然在检查使用“旧的”h5 格式会丢失什么,但这将是我目前默认的保存和加载方法。
我没有尝试的一个选项是只使用 save_weights() 调用,因为我想保存整个模型。
再说一次,虽然训练中表现出色,使用 TPU 的愿望非常强烈,但实现这一愿望的途径却是一个绝对的雷区。讨论这些问题的一些线程已经运行了多年。我相信在某个地方会有解决办法。但我希望谷歌团队能尽快坐下来,写一本合适的剧本,帮助我们解决这些问题。
部落知识不是你的朋友
保守一个组织的秘密不利于你的职业健康
如果你正在做一个与 IT 服务台有关的项目,你将需要 Ted。泰德从一开始就在这里,没有人比他更熟悉所有“尸体埋在哪里”。Ted 用特定于组织的首字母来说话,并能令人作呕地解释每一个数据差异的历史背景。没有 Ted,IT 帮助台肯定会爆炸成一堆燃烧的瓦砾。
我们都知道至少有一个 Ted 拥有超本地公司秘密网络的组织支柱。他们保护的信息通常被称为部落知识:关于组织的流程、技术和运营的详细信息,这些信息非常不直观、非常复杂、很少记录,并且对业务的影响不成比例。部落知识是实例知识的一种形式,这种洞察力在公司之外没有什么价值,但在公司内部却显得至关重要。个体间这种部落知识的密度通常被称为 总线因子;即“如果泰德被公交车撞了,我们会有多惨?”虽然人们普遍认为,从组织的角度来看,部落知识对业务不利,但很少有人讨论高总线因素对负责人有多有害。

希望不是 Ted 在人行横道上,否则我们都完了。丘特尔斯纳普在 Unsplash 上拍照
什么使知识部落化?
在深入研究部落知识如何污染个人技能池之前,先定义一下部落知识与其他类型知识的区别会有所帮助。在这种情况下,与部落知识截然相反的是技能知识——也就是说,增加个人职业价值和增加组织价值的能力和专业知识。技能知识在几个方面不同于部落知识:
- 部落知识是领域实例特有的。 技能知识和部落知识的关键区别之一是范围。给定一个像 Web 开发这样的领域,Ruby on Rails 框架的知识是一个更大的领域技能,因为成千上万的网站都在使用这个框架。了解公司代码库中的
User类、UserLegacy类和DefaultUser类之间的区别是 Ruby on Rails 的实例所特有的知识。世界上只有在一个单一的 Rails 代码库中,这些信息才是有价值的或有效的。 - 部落知识是语境依赖的。 当组织知识需要剧情阐述才能被理解和有用时,很可能是部落知识。"几年前,我们只能从单一供应商处获得任何特定产品。当我们开始为一个产品使用多个供应商时,我们必须在系统中添加产品名称的后缀,然后在网站上合并库存。现在,系统可以处理多个供应商,但一些团队在输入新产品时仍然会加上后缀。“这是组织上的绝学;讲故事,解释为什么奇怪的行为或复杂的过程是(或曾经是)必要的。没有上下文,部落知识有时产品应用了无意义的后缀既无帮助也不可理解。
- 部落知识与当前状态紧密相连。如果组织过程或技术的变化使得某些知识变得毫无价值,那么这些知识很可能是部落的。采用专业的电子邮件管理系统后,使用一系列自制脚本生成复杂的电子邮件列表和交错发送的能力立即变得过时——这是部落知识。管理电子邮件列表和确定最佳发送时间所涉及的技能超越了当前状态,适用于任何电子邮件解决方案——这种知识是有技巧的。
- 部落知识往往涉及一种情感依恋。部落知识给它的拥有者提供了一种权力和重要性的感觉,这种感觉可以发展成一种相互依赖;对这种知识的任何威胁(比如替换一个过于复杂的系统或交叉训练更大的团队)都被解释为对部落首领主权的威胁,并遭到轻蔑和/或防御性的回应。
对于知识是否是部落的,有一个简单的试金石:对于一个给定的角色,一个有更多领域经验的人能在没有被提供这种知识的情况下轻易地接管这个角色吗?如果可以,这些知识很可能是领域技能知识。如果他们不能,这种知识可能是部落的。
那么,为什么要首先考虑避免部落知识呢?对许多人来说,这种想法坚持认为,拥有堆积如山的组织秘密等同于作为团队成员的持续价值。这里有一些非常有说服力的理由,可能会让你重新考虑。
部落知识已经失去了保质期
微服务的发展在商业中无处不在。现代系统和流程被设计成尽可能的模块化和组件化,使得曾经不确定的业务元素突然变得可任意处理。

现代企业都是模块化的,这给部落知识依赖带来了麻烦。凯利·西克玛在 Unsplash 上的照片
考虑一家使用多家运输公司的零售商。曾经,每个运营商都需要与零售商的软件、会计流程和供应商关系进行独特的集成。这种在许多载体上成倍增加的开销将不可避免地导致代码、过程和实践的混乱。它也可能创造至少一个部落知识大师来驾驭这一切。今天,有许多 SAAS 供应商通过一个集成连接到数百家运输公司。过去复杂的芭蕾被资产负债表上的几行代码和一个供应商所取代——所有的部落知识立刻变得毫无用处。在一个模块化设计的世界里,部落知识大师会发现自己随时会被淘汰,而且没有任何警告。
部落知识模糊了你的视野
当我年轻的时候还在卖摩托车的时候,第一辆高性能的四冲程越野摩托车上市了。这些机器的第一次运行几乎不可能启动;制造商很快推出了第二代自行车,开始时没有那么高调,但如果你设法购买了第一批自行车中的一辆,就需要特殊的技能来启动马达。启动这些自行车是如此困难,以至于我们摩托车店只有一个人能够启动它们。他是我们第一代四冲程部落专家。
当我们以高价购买第一代自行车时,你可以打赌他就是买家。每当他从摩托车拍卖会上回来,卡车后面肯定至少会有几辆这样的豪车。尽管对自行车的需求很少(谁想要一台你不能启动的机器?)众所周知,他对这些东西情有独钟,这影响了他的判断。
部落知识有办法扭曲我们评估价值的方式。当利基实例知识在特定的过程或工具方面帮助我们时,我们更有可能倾向于那个过程/工具,即使另一个选择客观上更好。这种短视对于部落知识专家来说是危险的,因为它分散了我们对真实职业轨迹和更大成功的注意力。透过部落知识的迷雾,宗师可能很难认识到做好工作——在这种情况下,转售摩托车以获得可观的利润——是持久职业成功的关键。
部落知识让你无法胜任工作
编程中的一个常用短语是“把那个问题留给未来的我们”一般来说,我们只做我们知道现在绝对需要的工作;这留下了时间来观察事情在实践中如何发展,并根据需要进行调整。
任何富含部落知识的复杂系统的问题在于,它开始限制“未来的我们”所指的群体的规模。工作需要的部落知识越多,能够做的人就越少。对下一个接手这项工作的人的同情往往会激励我们尽可能多地清除诱杀装置和雷区。如果同情失败了,至少我们的骄傲通常会确保我们把东西收拾得足够整洁,以免被人评判。但是,当部落知识确保了你作为下一个人的角色时,做我们最糟糕的工作就变得容易了。雪上加霜的是,随着时间的推移,人类往往会高估自己记忆信息的能力。这可能导致在文档中偷工减料,让未来的我们没有路线图。
部落知识降低了你的市场技能
随着普通美国工人在成年后至少更换十几次雇主,长期技能是维持可持续职业生涯的重要组成部分。将自己培养成部落知识领袖或专注于技能的专家都需要大量的时间和汗水投资,但它们的回报非常不同。考虑一下 IT 支持主管如何选择投资他们的未来:
作为一名部落知识专家,这位主管可能会付出非凡的努力,让自己参与到所有与 IT 支持部门无关的事务中。了解电话系统每天汇总日志文件以及不同日志文件前缀代码的含义、了解营销部门如何使用 6 个定向电话号码来表示营销活动来源渠道、找到帮助热线软件数据库为何有 2 个不同用户表的背景资料等非常重要。
要成为一名技能专家,主管可能会花时间搜索有关技术团队管理、管理心理学和客户服务策略的文献、课程作业和案例研究。它还会定期参加行业交易会和供应商开放日,接触各种呼叫中心电话系统、支持分析和团队管理工具。
在会议中,部落知识专家将成为当地的英雄,对一些很平常的问题做出大量的快速回答,例如“这个电子表格看起来很糟糕,因为 xyz 表直到周二下午 3 点才刷新,这是因为我们与 abc 供应商在 9 年前签订了合同协议。”部落知识专家会尽情享受对首席执行官脱口而出的每一个即兴琐碎问题作出回应的即时满足感,并在每次会议结束时都沉浸在一片多巴胺云中,这种多巴胺来自于“如果没有你,我们会做什么”的轻拍。
在同一次会议上,技能专家可能无法回答电子表格的问题,并且需要调查它是否真的重要(提示:它很少重要)。技能专家更愿意考虑的是帮助台软件供应商的变化、团队新的远程工作政策,或者与主要竞争对手合并对工作流程的影响。
当面试新公司的下一个职位时,技能专家将能够明智地谈论行业的最新趋势,就企业需要做出的即将到来的选择提出明智的意见,并带领面试者完成构成其职业生涯的极具影响力的工作。另一方面,部落知识专家会发现自己迷失在组织领域的安全之外。他们丰富的知识详述了为什么前雇主的 TPS 报告只能在周二下午 4 点后发布,这些知识在面试中几乎没有用处,更大行业的对话将遭遇一场令人痛苦的心理斗争,专家寻找的答案根本不会出现。

面试并不是保持行业相关性的最佳时机。约翰娜·布格特在 Unsplash 上的照片
我们的学习资源确实是有限的,关于我们如何投资技能的每一个选择都会影响我们的市场竞争力。
如何挣脱自己的部落知识
如果你发现自己是一名部落知识大师(或者正在成为一名部落知识大师的路上),有一些技巧可以帮助你改变道路。在此,按照应用顺序:

- 简化:首先,部落知识需要存在吗?通常,对为什么一件事情如此复杂和神秘进行一点调查就会得到令人惊讶的结果。我曾经做过一个项目,由于需要近乎实时地生成 PDF 报告并通过电子邮件发送,这个项目变得异常复杂。经过更仔细的检查,结果发现没有人真正使用这些报告——事实上,每个收件人早就把它们标记为垃圾邮件了。除了消除不必要的复杂性,调查支持边缘案例的 ROI 也是明智的。通常你会发现一个非常简单的系统,除了一个导致它螺旋成一个部落知识的蜘蛛网的边缘情况。如果这些边缘案例没有为企业的整体运营增加显著的价值,那么完全牺牲它们可能是有意义的。
- 标准化:有些复杂性总是不可避免的。当你完成了简化,下一步就是寻找和实现被广泛接受的最佳实践。例如,使用广泛采用的平台如 WordPress 或 Ruby on Rails 来显示一个网站,远比创建一个用 Rust 编写的定制内容管理系统缺乏部落知识。即使是最简单的过程也有最佳实践标准:如果你去一家使用笼式洗碗机的餐厅,你会注意到银器都是朝下放置的。这确保了尖齿被清洁,也防止了偏离的刀片向上的刀割伤想要抓住手柄的人。尽可能寻找和实现最佳实践将有助于减少您的部落知识的部落性。
经常有一种观点认为,组织面临的挑战是 如此 独特,与其他业务如此不同,以至于唯一可能的策略是完全定制的解决方案;这通常是狂妄自大。每个公司都有难缠的供应商、粗糙的数据结构和不可预测的需求周期。在这种情况下,提出以下问题可能会有所帮助:其他组织是如何解决这些问题的?是什么让我们的解决方案独一无二?我们解决方案的优势是否足以抵消增加的复杂性?您解决供应商发票或数据库分发的策略很少是组织的市场差异化因素,定制解决方案不是核心业务能力的一部分,只会分散注意力。 - 当需要变得复杂时,文档往往是我们最后的希望。记录复杂性本身就是一门艺术。文档需要是可访问的——如果部落知识专家是唯一可以阅读文档的人,那就没什么用了。编写文档的最佳人选不是部落知识专家,而是能够咨询他们并避免熟悉性可能导致的盲点的其他人。为了保持有用,文档必须定期更新(这就是为什么简化总是更好的原因)。
- 推卸责任:部落知识有点像 魔戒 在把魔戒给别人的同时也可以逃脱诅咒。如果你选择向一个同胞灌输组织的黑暗秘密,你至少会卸下自己的负担。这种策略有点不道德,因为你的受害者现在将遭受部落知识的寄生效应,而你可以专注于职业建设技能,所以要小心使用。
如何帮助你的团队摆脱困境
组织通常认识到高“公共汽车因素”角色的负面影响,然而,导致部落知识的行为的偶像化仍然深深植根于许多商业文化中。摆脱部落知识依赖的重负始于攻击允许这种行为盛行的文化根源。
- 让部落知识大师负起责任:通常,拥有最多部落知识的人也最渴望在他们的角色中脱颖而出。通过清楚地定义一个小的个人总线因素作为他们成功的度量(隐含地定义一个大的总线因素作为失败的标志),你为这些团队成员提供了一个框架来消除他们领域中的部落知识依赖。当团队成员抓住错误的权力不放,抵制简化、标准化和文档化的努力时,让他们承担责任会让他们的心态暴露为一种负担。
- 更新价值叙述:随着现代组织努力衡量知识工作者的价值,不幸的是,将贡献者的价值与他们的个人业务因素联系起来是非常普遍的。公司通常会鼓励交叉培训,并为采用最佳实践而欢呼,同时尊重团队成员对嵌入的部落知识的掌握。为了改变这种模式,组织需要改变措辞。
鼓励部落知识:
--“没有你我们无法管理这个地方,莎拉,你比任何活着的人都更了解 TPS 系统!”
--“你需要确保 Ted 参加会议,否则我们无法讨论 IT 实施。”
--“我们的系统太复杂、太独特,我们无法考虑聘请外部顾问来帮助我们,他们不会理解我们在这里做什么。”鼓励技能知识:
--“Alex,你在 TPS 系统上的工作太棒了,我们现在可以在不到一周的时间内让新操作员上岗了!”
--“摩根是我们最好的项目领导之一,由于摩根管理部门的方式,该团队的任何成员都有资格列席会议。”
——“我们的系统设计得非常智能,我们可以很容易地用合格的开发人员来扩充我们的资源,并在我们看到商机时迅速添加功能。”
抓住每一个机会,用灵活的、以技能为中心的解决方案来定义成功,这将大大减少整个组织的团队知识。
最后
维系一个部落知识的魂器对持有者和整个组织都是有害的。学会抛弃领域实例知识的虚假权威,将为真正的技能增长创造机会,提高职业市场竞争力,并提高你所做工作的质量标准。
我在一次著名的纸牌游戏比赛中成功使用的技巧
使用创造性特征工程和高级回归技术预测房价|前 3 %

汤姆·朗布尔在 Unsplash 上拍照
作为一名新的数据科学家,在我攻读数据分析硕士学位期间,我积极参加了许多 Kaggle 比赛,以便将我在学习期间学到的所有知识投入到工作中。

我就说说我的第一场比赛,房价:高级回归技巧 。对于数据科学初学者或通过了机器学习课程并希望扩展其技能组合的学生来说,这是一场完美的比赛。本次竞赛的目标是预测样本房屋的销售价格。根据预测值的对数和观察到的销售价格的对数之间的 均方根误差(RMSE) 对所有提交进行评估。
有几个因素会影响房子的整体价格,其中一些因素更为有形,如房子的质量或房子的整体大小(面积),其他因素更为内在,如经济表现。建立一个准确的模型来预测实际价值是一项艰巨的任务,因为有内部和外部因素会影响每栋房子的价格。
在这里我将为你描述我是如何从第 2800 名左右进入前 150 名的。你可以在我的 GitHub 个人资料上找到详细的笔记本:https://github.com/chouhbik/Kaggle-House-Prices
一开始,我从构建不同的模型开始,并让它们根据我从 Kaggle 下载的原始数据进行训练。随机森林表现良好,得分为 0.13825,足以让我在排行榜上垫底。
之后才知道 EDA(探索性数据分析)****特征工程的前期步骤是如此重要。接下来,我将向您展示我是如何改进的…
第一步:因变量的对数变换
有两个原因需要预测销售记录,而不是销售额:首先,本次竞赛中使用的评估指标取决于销售记录,其次,许多货币实体的分布接近对数正态分布,即重尾分布。通常,预测不具有重尾性的事物会更好,并且大多数机器学习模型在正态分布的数据上表现良好。
(np.log(df_train["SalePrice"])).hist(bins = 40)

直方图:销售记录
步骤 2:重新编码预测值/缺失值/异常值
一些顺序的预测值(即它们的值有自然顺序)最初被存储为因子。我以一种明智的方式对它们进行了重新编码(这种预测器的例子有ext equal和 BsmtCond )。显然,我必须填写缺失的值:
- 数字预测中的缺失:当有意义时,我用零填充(例如,如果地下室没有浴室,BsmtFullBath 应该等于零),
- 分类预测中的缺失:我用中位数或其他典型值(均值或众数)填充。最后,
- 此外,去除预测值中的一些异常值也是值得的,如总体质量和 GrLivArea 。
第三步:处理非线性
由于我计划使用一些线性方法(拉索,山脊,SVM),我用它们的对数替换了所有的重尾预测值,并为一些预测值添加了它们的平方(即我们有预测值 X,我们添加了预测值 X)。用对数代替重尾预测值的动机是:
- 线性方法可能用非常小的权重来拟合这种预测值,并且值中包含的大部分信息可能会丢失
- 当这样的预测值取非常高的值时,预测值也可能非常高或误导。添加方块是由散点图“预测值与销售记录”中的非线性引起的
步骤 4:添加新的预测值
我决定增加一些指标变量,如果相应的预测因子取某个值,它们就等于某个值。
例如:
*# feture engineering a new feature "TotalSF"*
all_data['TotalSF'] = all_data['TotalBsmtSF'] + all_data['1stFlrSF'] + all_data['2ndFlrSF']all_data['YrBltAndRemod']=all_data['YearBuilt']+all_data['YearRemodAdd']
all_data['Total_sqr_footage'] = (all_data['BsmtFinSF1'] + all_data['BsmtFinSF2'] +all_data['1stFlrSF'] + all_data['2ndFlrSF'])
all_data['Total_Bathrooms'] = (all_data['FullBath'] + (0.5 * all_data['HalfBath']) + all_data['BsmtFullBath'] + (0.5 * all_data['BsmtHalfBath']))
all_data['Total_porch_sf'] = (all_data['OpenPorchSF'] + all_data['3SsnPorch'] + all_data['EnclosedPorch'] + all_data['ScreenPorch'] +all_data['WoodDeckSF'])all_data['haspool'] = all_data['PoolArea'].apply(lambda x: 1 if x > 0 else 0)
all_data['has2ndfloor'] = all_data['2ndFlrSF'].apply(lambda x: 1 if x > 0 else 0)
all_data['hasgarage'] = all_data['GarageArea'].apply(lambda x: 1 if x > 0 else 0)
all_data['hasbsmt'] = all_data['TotalBsmtSF'].apply(lambda x: 1 if x > 0 else 0)
all_data['hasfireplace'] = all_data['Fireplaces'].apply(lambda x: 1 if x > 0 else 0)
步骤 5:堆叠模型
下一步,我使用了 10 层交叉验证和堆叠:对于每次“运行”的交叉验证,我试图在 10 层(套索、脊、弹性网、GBM 和 XGBoost) 中的 9 层上拟合 5 个模型,对遗漏的层进行预测,并使用这五组预测作为另一个套索模型的输入,以预测遗漏层的销售记录(这种套索模型被称为我们总共有 610=60 款 (10 套 6 款)。我使用所有这些模型进行最终预测:我采用测试数据集,使用 5 个子模型进行预测,然后使用这些模型的输出作为各自元模型的输入,以获得给定模型集的预测集。我重复这个过程 10 次,得到 10 组预测,然后使用算术平均值*对它们进行平均,得到用于提交的数据。**
第六步:调整参数
对于堆叠,我使用了 6 个不同的模型,每个模型都需要调整(对于 交叉验证 的每次“运行”,我用始终相同的参数拟合 6 个模型。我花了很多时间和提交来微调参数(最好的改进是通过为GradientBoostingRegressor调整min _ samples _ leaf和min _ samples _ split)。最终,我得到了 0.1167 左右的 smt,并且确信我无法通过调优来提高它。
第 7 步:更多缺失值
接下来,我尝试了不同的填充缺失值的策略(模式/方法/中间值等)。).在某种程度上,最有效的东西是出乎意料的:它基于 R 中的 mice 包,在这里描述为。我使用 鼠标 包来填充数值变量 NA 使用随机森林。
Mice package 在填充 NAs 时非常方便。结果不是最好的,但比用每个变量的中间值来填补缺口要好。更重要的是,它很好用。
使用 R/Mice/SVM 和 python/stacking 并取这些预测的几何平均值,我得到了 0.1131,这已经很好了。令人意想不到的是,这种简单的、非业务驱动的填补异常值的方法起了作用。注意,老鼠可能会在数值预测器中填入不等于零的东西,而它们应该是零。我认为这种方法帮助了我,因为它与我在 python 中得到的完全不同。
第八步:野蛮武力
我对 0.1131 已经很满意了,但是我最不想尝试的就是这个。回归通常不适用于边缘情况,不适用于预测值的大小。我接受了培训,运行了 R/mice/svm、python/stacking,使用几何平均值对结果进行了平均,得到了销售记录的最终预测,并将它们与真实值的记录进行了对比:

从图中可以明显看出,对于小的最终预测,我们高估了销售记录,对于大的预测值,我们低估了。
最后,我尝试了一个相当残酷的方法:我取预测销售额(不是对数,而是实际销售额),取 3 个百分点,手动增加/减少预测。
我用不同的百分位值重复了 8 次,直到最后得到 0.10985。值得注意的是,这种强力方法提高了对少量观察值的预测,但似乎足以提高排行榜上的分数。
**q1 = final_sub['SalePrice'].quantile(0.0025)
q2 = final_sub['SalePrice'].quantile(0.0045)
q3 = final_sub['SalePrice'].quantile(0.99)
final_sub['SalePrice'] = final_sub['SalePrice'].apply(lambda x: x if x > q1 else x*0.79)
final_sub['SalePrice'] = final_sub['SalePrice'].apply(lambda x: x if x > q2 else x*0.89)
final_sub['SalePrice'] = final_sub['SalePrice'].apply(lambda x: x if x < q3 else x*1.0)**
总结:
最后,我还尝试了一些其他的方法,但是没有用:
- 主成分分析
- 添加越来越多的预测器
- 将其他模型添加到堆叠中
- 在张量流和标度上使用 keras,具有密集的神经网络。
我想说的是,产品化版本的评估指标大约为 0.12,因为可以构建简单的模型来提供相同的结果。有帮助的有趣的事情:添加一些预测器的方块,在原始数据上使用鼠标& SVM** 和 R 并使用边缘预测(非常低或非常高的预测)。**
希望你喜欢这个项目的分析和预测模型,这些预测模型曾经接近准确的预测。
TriGraph:如何使用图表分析铁人三项项目

ID 21137693 Patrimonio 设计有限公司| Dreamstime.com
图形直观。图为强大的。而且图形可以在广泛的应用中使用。本文的目标是展示在我们的例子中,尤其是在铁人三项比赛中,图表如何简化定义良好的数据模型之上的数据分析。为了实现这一点,我们使用 Neo4j 作为图形数据库,并使用 Ironman Vega 世界锦标赛 2019 的结果进行分析。我们想要回答的问题是:
- 所有首发球员的国家分布如何?
- 在 10 小时内完成比赛的所有运动员的年龄组分布如何?
正如我们将看到的,定义数据模型对于后面的分析至关重要。让我们直接开始吧。
数据模型
数据模型是后面分析的关键。它提供了一个抽象,将数据源定义的初始原始数据映射成信息(进一步阅读)。在这些信息的基础上,您可以定义您的查询并进行分析。在我们的例子中,一切都从运动员开始,他的名字和姓氏被识别出来。一名运动员属于一个由名字定义的队。此外,运动员来自一个国家,目前由国家简称定义。最重要的是,运动员在某个比赛和某个比赛中取得了和的成绩。一场比赛中所有的分割都是最终结果的一部分。产生的模式如下所示:**

Neo4j 生成的图形数据库的结果模式
这个例子表明,建立一个图表是多么直观和容易理解。正如我们所看到的,产生的模式非常接近我们如何定义和看待现实世界。
一旦定义了数据模型,就该用结果填充数据库了。当我们使用 Neo4j 时,填充图形数据库是通过名为‘cypher’的图形查询语言完成的。让我们来看一个例子,让你感受一下这是怎么做到的:
create (:Athlete {firstName: “testFN”, name: “testLN” })
现在,我们创建一个国家,然后让我们添加一名运动员来起源于它:
create (:Country{shortCode:”USA”});
match (a:Athlete),(c:Country)
create (a)-[:originatesFrom]->( c )
return a,c;

结果图看起来像预期的那样
一旦有了模式并获得了查询语言的逻辑,就很容易将原始数据映射到数据模型并导入它。为此,我将 python 与 jupyter 笔记本和 py2neo 一起使用。
回答问题
有了数据模型和原始数据的导入,艰苦的工作已经完成,我们可以享受图形的可视化。所以,现在是时候回答我们上面提出的问题了,从第一个开始。这两个问题有一个共同点,即信息在图中被明确地陈述,这使得它容易得多。
所有首发球员的国家分布如何?由于在数据模型中运动员和国家之间有直接关系,我们唯一要做的就是匹配它并返回节点,如下图所示。
match (a:Athlete)-[:originatesFrom]->(c:Country)
return a,c

国家分布
在 10 小时内完成比赛的所有运动员的年龄组和国家分布情况如何?这个问题有点难回答,因为它有一个条件。分割时间将总时间保存为 Neo4j 中的持续时间。现在我们可以过滤时间,得到相应的结果以及与比赛(=年龄组)和运动员的关系。最后,所有三个都被返回,结果图如下所示。
match (s:Split)
where s.name = “FINISH” and s.total.minutes < 600
with s
match (a:Athlete)-[:achieved]->(r:Result)
where s.bib = r.bib
with a,r
match (r)-[:achievedIn]->(c:competition)
return a,r,c

完成 10 小时以下比赛的所有运动员的年龄组分布
摘要
我想展示的是,一旦数据模型就位,使用简单易懂的查询就可以获得非常好的结果。非常重要的一点是,数据模型可以很容易地用新的特性和信息进行扩展。此外,可以导入新的源,使用完全相同的查询可以获得相同的结果。但这只是开始。查询非常简单,结果并不令人惊讶,也可以通过其他技术实现。但是这张图表是下一步的基础。这个想法是为了回答关于铁人三项比赛和训练策略的更复杂的问题。我接下来想研究的是团队比赛如何影响不同比赛的最终结果,并拥有一套可以自动进行和共享的标准分析。
OpenShift 集群和工作负载故障排除
每个 OpenShift 用户都应该知道的命令集合
如果您是集群管理员、集群操作员或团队中唯一真正知道 OpenShift 集群中发生了什么的开发人员,那么您会知道,一些可怕的事情会不时发生。这是不可避免的,最好为倒楣的时刻做好准备。因此,当涉及到调试中断的部署、资源消耗、缺少权限、无法实现的工作负载等时,这是应该成为您的武器库的一部分的命令集合…

不要使用 Web 控制台
是的,它看起来不错,易于导航,便于执行一些任务…如果路由器出现问题,或者部署失去其端点或者运营商出现一些问题,它也会变得不可达…
如果您不太习惯使用 web 控制台做任何事情,那么在每一分钟都很重要的时候,您可能最终无法使用 CLI 解决问题。因此,我的建议是熟悉oc工具,至少在这篇文章期间,忘记 web 控制台的存在。
监控节点资源
不时地检查工作节点上的可用内存和 CPU 是很好的,尤其是如果您的 pod 停留在Pending状态或正在OOMKilled状态。您可以通过以下方式做到这一点:
其中显示了节点的 CPU 和内存状态。如果你想过滤掉主节点,只看到工人,那么你可以使用-l node-role.kubenetes.io/worker,就像这样:
排除节点设备故障
如果您正在运行裸机集群,那么很有可能您最终会遇到与运行在节点本身上的东西相关的问题。要查看在 worker 节点上运行的特定systemd单元(例如 crio 或 kubelet )发生了什么,可以使用:
该命令从特定的unit检索日志。因此,使用-u crio运行会给我们带来以下结果:
当日志不够好,并且您需要在 worker 节点内部真正探索时,您可以运行oc debug nodes/node-name。这将通过在节点上创建特权 pod,为您提供这个特定节点内的 shell。这种交互式会话的示例:
在上面的会话中,我们使用crictl来检查直接在 worker 节点上运行的容器。如果需要,我们可以在这里启动/重启/删除一些容器或系统服务。不用说,在接触运行在节点本身上的东西时要非常小心。
顺便提一下,如果您在某个托管公共云上运行您的集群,那么您很可能没有直接访问节点的权限,因为这将是安全问题,所以最后一个命令可能会在您身上失败。
监控集群更新
当您决定是时候将您的集群更新到新版本时,您可能需要监控进度。或者,如果一些运营商在没有任何明确原因的情况下中断,您可能还想检查您的clusterversion运营商,看看它是否正在向新版本发展,这可能是暂时服务降级的原因:
上面的两个命令都检索版本和集群当前是否正在升级的信息,或者集群操作符的总体状态。
调试 pod 的所有方法
最容易出问题的当然是 pod 和/或部署( DeploymentConfig )。有很多命令可以让您了解应用程序的问题所在,所以从最高级的命令开始:
oc status是获得项目中部署的资源概况的最简单方法,包括它们的关系和状态,如上所示。
下一个,你已经知道了——oc describe。我提到它的原因是底部的Events:部分,它只显示与这个特定资源相关的事件,这比试图在oc events的输出中找到任何有用的东西要好得多。
另一个常用命令是oc logs。你可能不知道的一件事是,它可以使用-c参数针对 pod 的特定容器。例如:
如果您在多容器窗格中调试单个容器,并且希望只过滤相关的日志,这可能会很方便。
现在,对于鲜为人知的命令。oc debug已经在关于调试节点的章节中展示了,但是它也可以用于调试部署或 pod:
与带有节点的示例不同,这不会为正在运行的 pod 提供 shell,而是在调试模式下创建现有 pod 的精确副本。也就是说,标签将被去除,命令更改为/bin/sh。
您可能需要在 OpenShift 中调试 pod 的一个原因是安全策略问题。在这种情况下,您可以将--as-root添加到命令中,以防止它在启动时崩溃。
这个命令的好处在于它可以与任何创建 pod 的 OpenShift 资源一起使用,例如部署、作业、图像流标签等。
在窗格和容器中运行临时命令
尽管创建调试窗格非常方便,但有时您只需要在实际的窗格中摸索。你可以用oc exec来表示。这些是您可以利用的变体:
上面的第一个命令在podname中一次性运行command,如果必要的话还会有额外的选项。第二个命令将让您将 shell 放入 pod 中的特定容器,尽管您可能会使用简写- oc rsh。
您可以使用的一个用于故障排除的命令是oc cp,但是不应该在生产环境中使用,它将文件复制到 pod 或从 pod 复制文件。如果您需要从容器中取出一些文件以便进一步分析,这个命令会很有用。另一个用例是将文件复制到 pod(容器)中,以便在测试期间快速修复一些问题,然后在 Docker 镜像(Dockerfile)或源代码中正确修复。
检查损坏的图像
我认为这对于调试 pod 和容器已经足够了,但是调试应用程序映像呢?为此你应该求助于skopeo:
第一个命令可以检查图像存储库,这在无法提取图像的情况下非常有用,如果标签不存在或者图像名称拼写错误,就可能会发生这种情况。第二个只是给你可用的标签列表,而不需要你打开注册网站,这是非常方便的。
收集所有可用的信息
当其他一切都失败时,您可以尝试运行oc adm must-gather来从集群中获取所有可用的信息,这对调试很有用。该命令生成的文件可用于您自己的调试,或在您需要帮助时发送给 Red Hat 支持。
调试无法访问的应用程序
应用程序/部署看似运行良好,但却无法相互联系,这种情况并不少见(至少对我来说是这样)。这可能有几个原因。让我们看下面的场景—您有一个应用程序和数据库。两者都运行良好,但由于某种原因,您的应用程序无法与数据库通信。有一种方法可以解决这个问题:
上面的代码片段假设我们已经运行了应用程序和数据库,以及它们各自的服务。我们可以通过尝试从应用程序访问数据库来开始调试。不过,我们首先需要的是数据库的 IP,我们使用第一个命令锁定它。接下来,我们使用oc debug创建应用程序的副本,并尝试使用curl访问数据库 pod,这是成功的。
之后,我们以相反的方式重复测试,我们可以看到curl超时,这意味着数据库无法到达应用程序 IP。然后我们检查先前创建的服务,没有什么奇怪的。最后,我们检查端点,我们可以看到应用程序窗格没有端点。这很可能是由相应的服务的错误配置引起的,如最后一个命令所示,这里我们显然有错误的selector。(用oc edit svc/...)修复这个错误后,端点被自动创建,应用程序可访问。
修复缺失的安全上下文约束
如果您的 pod 由于任何与复制/访问文件、运行二进制文件、修改节点上的资源等相关的问题而失败,那么很可能是与安全上下文约束有关的问题。根据您得到的具体错误,您应该能够为您的 pod 确定正确的 SCC。如果还不清楚,那么有一些提示可以帮助你做出决定:
如果您的 pod 由于其使用的 UID/GID 而无法运行,那么您可以检查每个 SCC 的 UID 和 GID 范围:
如果这些字段被设置为<none>,你应该去看看项目注释:
这些注释告诉您,pod 的有效 UID 将在 1001490000 +/- 10000 范围内。如果这不能满足您的需求,您必须设置spec.securityContext.runAsUser: SOME_UID来强制特定的 UID。如果您的 pod 在这些更改后失败,那么您必须切换 SCC 或修改它以具有不同的 UID 范围。
确定服务帐户需要哪个 SCC 来运行 pod 的一个巧妙方法是使用oc adm policy scc-subject-review命令:
该命令的作用是检查用户或服务帐户是否可以创建使用 YAML 表示传入的 pod。当该命令的输出显示<none>时,则意味着不允许资源。如果显示的是 SCC 的任何名称,例如上面的anyuid,那么这意味着这个资源可以通过这个 SCC 来创建。
要将此命令用于某个服务帐户而非用户,请添加-z参数,例如- oc adm policy scc-subject-review -z builder。
当这个命令的输出显示除了<none>之外的任何东西时,那么您就知道可以开始了。
结论
从这篇文章中最大的收获应该是,如果您的 OpenShift 集群中有什么东西不工作,那么它可能是 RBAC,如果不是,那么它是 SCC。如果也不是这样,那就是联网(DNS)。说真,我希望下次您需要在 OpenShift 中进行故障诊断时,这些命令中至少有一些能为您节省一些时间。此外,最好能记住那些更常见的,因为你永远不知道什么时候你真的会需要它。😉
本文最初发布于martinheinz . dev
[## 将任何 Python 项目部署到 Kubernetes
是时候深入 Kubernetes,使用这个成熟的项目模板将您的 Python 项目带到云中了!
towardsdatascience.com](/deploy-any-python-project-to-kubernetes-2c6ad4d41f14) [## 从死亡中拯救你的 Linux 机器
恢复您的根密码和更多
medium.com](https://medium.com/better-programming/save-your-linux-machine-from-certain-death-24ced335d969) [## 分析 Docker 图像安全性
码头集装箱远没有你想象的那么安全…
towardsdatascience.com](/analyzing-docker-image-security-ed5cf7e93751)
使用 React、Angular 或任何 web 框架真正定制 Power BI
通过开发“定制视觉”

凯利·西克玛在 Unsplash 上的照片
随着组织中可用数据量的增长,以清晰直接的方式呈现数据变得越来越重要。在这种背景下,Power BI——微软的商业分析工具——获得了突出地位。
即使依靠足以满足大多数常规企业需求的集成组件和导航机制,该平台仍因其定制可能性而脱颖而出。
除了能够定制平台的内置组件之外,利用一些前端工程技术,还可以从头开始开发新的组件。
开发 Power BI 定制视觉效果
使用 PowerBI 可视化工具包,或 pbiviz ,通过自定义可视化工具的编程来进行新组件的开发过程,可以使用节点包管理器 — NPM 来安装。

Pbiviz 命令行界面
定制可视化的开发只需要传统 web 技术的知识,如 Typescript 、HTML 和 CSS,并且可以通过使用框架来增强,如 React 、 Angular 或 D3.js 。
自定义视觉效果的设计是通过 CLI 工具生成的,该工具由上述 NPM 软件包提供。

基本上,web 开发人员只需要编写很少的代码——只有两个方法:构造函数和实现IVisual的类的更新。此外,同样由 CLI 工具生成的文件capabilities.json允许用户定义属性,例如颜色和字体,最终用户可以在 Power BI 中使用这些属性进行定制。
export class Visual implements IVisual {
constructor(options: VisualConstructorOptions) {
// code here
}
public update(options: VisualUpdateOptions) {
// ... and here
}
}
由 pbiviz 提供的工具允许 web 开发人员对他的工作进行即时反馈,并通过热重新加载进行更新。全部由 Power BI 服务提供支持。

pbviz 客户端处理热重载并与 Power BI 服务轻松集成——不,WebUI 不是在 localhost 上运行的!
除了能够从您的报告和仪表板上所做的工作中受益,还可以在应用程序源 —微软公司的应用程序商店中共享创作。在撰写本文时,已经有超过 260 种免费的自定义视觉效果可供使用。
我希望你喜欢阅读这篇文章。
如果你有,可以考虑在Twitter上关注我。
谢谢你的时间。
保重,继续编码!
川普 VS 特鲁多:新冠肺炎危机期间谁更好地利用了推特
在新冠肺炎疫情期间,人们将他们的担忧、担忧、沮丧和爱带到社交媒体上与世界其他地方分享。Twitter 已经成为世界领导人与其支持者和追随者交流的官方渠道之一。
为了了解他们在忙什么,我们摘录了两位世界领导人唐纳德·川普(美国总统)和贾斯廷·特鲁多(加拿大总理)的推特。通过应用自然语言处理技术和潜在狄利克雷分配(LDA)算法,可以学习他们推文的主题。因此,我们可以看到危机期间他们在想什么。

我们使用 Python 3.6 和以下软件包:
- TwitterScraper ,一个抓取推文的 Python 脚本
- NLTK(自然语言工具包),一个用于文本处理的 NLP 包,例如停用词、标点符号、标记化、词条化等。
- Gensim ,“生成相似”,一个流行的用于主题建模的 NLP 包
- 潜在狄利克雷分配 (LDA),一种用于主题聚类/建模的生成式概率模型
- pyLDAvis ,一个交互式 LDA 可视化软件包,旨在帮助解释在文本数据语料库上训练的主题模型中的主题
数据采集
我们使用 TwitterScraper 从 Twitter handle @realDonaldTrump 和@JustineTrudeau 收集推文。仅收集 2020 年 3 月 1 日至 4 月 27 日发布的原创推文,不转发他人推文。只有英语。
**from** **twitterscraper** **import** query_tweets
**import** **datetime**
**import** **pandas** **as** **pd**
**import** **re**, **pickle**, **os**
**import** **matplotlib.pyplot** **as** **plt**KEYWORD = 'realDonaldTrump' *#Twitter handle*
BEGINDATE = datetime.date(2020, 3, 1)
ENDDATE = datetime.date(2020, 4, 27)
LANG = 'en'
TWEET_QUERY = 'from:'+ KEYWORD
ORIG_TWEET_FILE = r'./data/'+ KEYWORD+'/' + 'all_tweets'**if** os.path.isfile(ORIG_TWEET_FILE):
**with** open (ORIG_TWEET_FILE, 'rb') **as** fp:
all_tweets_df = pickle.load(fp)
print('Loaded tweet extracts from file**\n**')
**else**:
print('Start scraping tweets from twitter.com...**\n**')
*# https://twitter.com/search-advanced*
list_of_tweets = query_tweets(TWEET_QUERY,
begindate=BEGINDATE,
enddate=ENDDATE,
lang=LANG)
*# Convert list of tweets to DataFrame*
all_tweets_df = pd.DataFrame([vars(x) **for** x **in** list_of_tweets])
all_tweets_df.drop_duplicates(subset=['id'], inplace=**True**)
all_tweets_df.reset_index(drop=**True**, inplace=**True**)
*# Save tweet extracts to file*
save_data_to_pickle(ORIG_TWEET_FILE, all_tweets_df)
print ('Tweet extracts saved**\n**')
按周、日和小时统计的推文数量


似乎特朗普喜欢在下午 1 点到 4 点发推文,而特鲁多喜欢在下午 3 点左右发推文。


特朗普和特鲁多都在一周内定期发推特。特朗普似乎更喜欢在周日发推特!
推文长度
2020 年 3 月 1 日至 4 月 27 日,特朗普发了 673 条推文,平均一条推文 27 个字,特鲁多发了 386 条推文,平均一条推文 41 个字。特朗普有许多短推文(不到 10 个字),也有一些长推文(超过 40 个字)。特鲁多的推文最多,有 40 到 50 个单词。


数据预处理
文本预处理是将文本从人类语言转换成机器可读格式以便进一步处理的过程。以下预处理步骤适用于我们的 Twitter 文本。
- 将所有单词转换成小写
- 删除非字母字符
- 删除短单词(长度小于 3)
- 标记化:将句子分解成单词
- 词性标注:将单词按其语法类别分类的过程,目的是理解它们在句子中的作用,如动词、名词、形容词等。词性标注为词汇化提供了语法环境。
- 词汇化:将一个单词转换成它的基本形式,例如
car, cars, car’s到car - 去掉常见的英语单词,如 a,the,of 等。,并删除对我们的分析没有什么价值的常用词,如 com、twitter、pic 等。
**import** **nltk**
**from** **nltk.corpus** **import** stopwords, wordnet
**from** **nltk.stem** **import** WordNetLemmatizer
*# Additional stop words to be removed from text*
additional_stop_words=['twitter','com','pic','rt','via']
**def** get_wordnet_pos(word):
*"""*
*Map POS tag to first character lemmatize() accepts*
*"""*
tag = nltk.pos_tag([word])[0][1][0].upper()
tag_dict = {"J": wordnet.ADJ,
"N": wordnet.NOUN,
"V": wordnet.VERB,
"R": wordnet.ADV}
**return** tag_dict.get(tag, wordnet.NOUN)
**def** text_cleanup(text):
*# Convert to lowercase*
text_clean = text.lower()
*# Remove non-alphabet*
text_clean = re.sub(r'[^a-zA-Z]|(\w+:\/\/\S+)',' ', text_clean).split()
*# Remove short words (length < 3)*
text_clean = [w **for** w **in** text_clean **if** len(w)>2]
*# Lemmatize text with the appropriate POS tag*
lemmatizer = WordNetLemmatizer()
text_clean = [lemmatizer.lemmatize(w, get_wordnet_pos(w)) **for** w **in** text_clean]
*# Filter out stop words in English*
stops = set(stopwords.words('english')).union(additional_stop_words)
text_clean = [w **for** w **in** text_clean **if** w **not** **in** stops]
**return** text_clean
cleaned_tweets_df = all_tweets_df.copy(deep=**True**)
*# parsing tweets*
cleaned_tweets_df['token'] = [text_cleanup(x) **for** x **in** all_tweets_df['text']]
print ('Tweets cleanup done')
我们还从文本中提取二元模型(成对的连续单词)。
**from** **nltk.util** **import** ngrams
**def** word_grams(words, min=1, max=2):
word_list = []
**for** n **in** range(min, max):
**for** ngram **in** ngrams(words, n):
word_list.append(' '.join(str(i) **for** i **in** ngram))
**return** word_list
*# Generate bigram tokens*
cleaned_tweets_df['bigram_token'] = [word_grams(x, min=2, max=3) **for**
x **in** cleaned_tweets_df['token']]
经过预处理后,我们的推文看起来像这样:

单词计数和单词云
我们使用二元模型进行单词计数和单词云,因为二元模型比单个单词提供了更有意义的见解。
# Convert series to list for word count
tweets_text = [word for one_tweet in cleaned_tweets_df['token'] for word in one_tweet]from collections import Countern_grams = list(ngrams(tweets_text, 2))
common_words = Counter(n_grams).most_common()
word_count = pd.DataFrame(data = common_words, columns=['word','frequency'])
# Convert list to string
word_count['word'] = word_count['word'].apply(' '.join)# Plot word count graph
title = "Word Frequency: Twitter @{} {} - {}".format(KEYWORD, BEGINDATE, ENDDATE)
word_count.head(20).sort_values('frequency').plot.barh(x='word', y='frequency', title=title,figsize=(16,10), fontsize=16)


word_count.head(20)

特朗普推文中最常见的前 5 个词是:
**fake news white house united state news conference mini mik**e

特鲁多推文中最常见的前 5 个词是:
**make sure across country keep safe canada emergency health car**e
以下是特朗普推文的词云:

以下是特鲁多推文的词云:

主题建模
主题建模是一种无监督的机器学习技术,广泛用于发现文档集合中的抽象主题。它认为每个文档由几个主题表示,每个主题由一组频繁出现的单词表示。例如,对于cloud, rain, wind的群集,我们可以知道相关联的主题可能与weather相关。
对于主题建模,我们使用 LDA 算法,使用从我们的预处理中获得的 unigrams 为每个 tweet 创建 TF-IDF 向量。
如何知道我们所学的题目是否最能代表原文?我们计算和测量一致性分数。
话题连贯性——意思是语义连贯性——是一种人类判断的质量,它取决于单词的语义。[ 用正则化的话题模型提高话题连贯性
连贯性评分根据主题本身内高评分单词之间的语义相似度来衡量主题的可解释性。为了找出我们推文中的最佳主题数量,我们计算了不同数量主题的一致性分数。分数越高,主题的数量越符合文本。
选择大量能产生较高连贯分数的话题可以提供有意义和可解释的话题。更多的主题通常会提供更高的连贯分数,但意义却相当零散。

对于特朗普的推文,具有 8 个主题的 LDA 模型产生最高的一致性值。

对于特鲁多的推文,具有 6 个主题的 LDA 模型产生最高的一致性值。
话题生成
top_n_keywords = 4
topics_pd = pd.DataFrame()
# Label topics using top 4 keywords of the topic
for idx, topic in lda_model.print_topics():
topic_desc = ‘-’.join([x[0] for x in lda_model.show_topic(idx,top_n_keywords)])
topics_pd = topics_pd.append(pd.Series([idx,topic_desc, topic]), ignore_index=True)
topics_pd.columns = [‘topic_num’,’topic_desc’,’topic_keywords’]
topics_pd
我们为特朗普的推文生成以下 8 个主题。topic_keywords显示热门关键词及其对主题的重要性(权重)值。我们将前 4 个单词连接起来作为主题的标签,希望它能代表主题的含义。例如,conference-white-news-house可以解释为与白宫新闻发布会相关的话题。thank-deal-great-call可能与向某人道谢、打了一个很棒的电话或做了一笔大生意有关。
topic_num topic_desc topic_keywords0.0 federal-government-full-test 0.066*"federal" + 0.059*"government" + 0.037*"full" + 0.033*"test" + 0.030*"general" + 0.026*"bill" + 0.023*"hospital" + 0.022*"low" + 0.022*"know" + 0.021*"continue"
1.0 conference-white-news-house 0.089*"conference" + 0.085*"white" + 0.082*"news" + 0.081*"house" + 0.072*"eastern" + 0.056*"today" + 0.039*"press" + 0.036*"million" + 0.032*"thank" + 0.031*"world"
2.0 thank-deal-great-call 0.285*"thank" + 0.053*"deal" + 0.052*"great" + 0.038*"call" + 0.035*"company" + 0.026*"act" + 0.025*"leader" + 0.024*"together" + 0.021*"go" + 0.021*"work"
3.0 joe-bernie-mike-sleepy 0.060*"joe" + 0.057*"bernie" + 0.056*"mike" + 0.053*"sleepy" + 0.049*"mini" + 0.036*"biden" + 0.033*"long" + 0.032*"democrat" + 0.030*"foxnews" + 0.027*"also"
4.0 fake-news-state-people 0.022*"fake" + 0.020*"news" + 0.017*"state" + 0.017*"people" + 0.017*"get" + 0.017*"country" + 0.017*"united" + 0.017*"say" + 0.016*"time" + 0.015*"medium"
5.0 keep-total-complete-safe 0.035*"keep" + 0.034*"total" + 0.034*"complete" + 0.031*"safe" + 0.029*"endorsement" + 0.029*"small" + 0.026*"strong" + 0.026*"business" + 0.025*"great" + 0.024*"amendment" 6.0 great-day-book-history 0.107*"great" + 0.043*"day" + 0.039*"book" + 0.038*"history" + 0.032*"wonderful" + 0.031*"hard" + 0.031*"end" + 0.029*"national" + 0.028*"american" + 0.027*"situation"
7.0 kag-thank-ventilator-need 0.162*"kag" + 0.093*"thank" + 0.067*"ventilator" + 0.042*"need" + 0.038*"spoke" + 0.032*"good" + 0.032*"help" + 0.028*"every" + 0.022*"deliver" + 0.018*"work"
以下是从特鲁多的推文中了解到的 6 个话题。例如,business-small-help-owner很可能与向小企业主提供帮助有关。
topic_num topic_desc topic_keywords0 0.0 business-small-help-owner 0.054*"business" + 0.034*"small" + 0.026*"help" + 0.025*"owner" + 0.024*"support" + 0.023*"announce" + 0.020*"non" + 0.020*"detail" + 0.019*"announcement" + 0.019*"emergency"1 1.0 spoke-international-talk-spread 0.040*"spoke" + 0.034*"international" + 0.031*"talk" + 0.030*"spread" + 0.029*"call" + 0.029*"impact" + 0.029*"today" + 0.028*"covid" + 0.026*"leader" + 0.025*"economy"2 2.0 benefit-test-lose-apply 0.051*"benefit" + 0.036*"test" + 0.034*"lose" + 0.033*"apply" + 0.033*"emergency" + 0.029*"month" + 0.028*"receive" + 0.025*"response" + 0.025*"year" + 0.023*"invest"3 3.0 make-work-need-sure 0.024*"make" + 0.022*"work" + 0.020*"need" + 0.018*"sure" + 0.018*"continue" + 0.018*"country" + 0.018*"keep" + 0.017*"health" + 0.016*"home" + 0.016*"safe"4 4.0 celebrate-hope-life-around 0.053*"celebrate" + 0.042*"hope" + 0.038*"life" + 0.037*"around" + 0.036*"clock" + 0.031*"please" + 0.030*"full" + 0.026*"year" + 0.022*"late" + 0.022*"world"5 5.0 one-update-watch-family 0.026*"one" + 0.024*"update" + 0.023*"watch" + 0.023*"family" + 0.022*"time" + 0.021*"friend" + 0.021*"kid" + 0.019*"give" + 0.019*"great" + 0.018*"live"
我们将这些主题应用到推文中,并分配概率最高的主题。以下是特朗普的一些可能的话题。
他们正在对伯尼发动政变!
Topic: joe-bernie-mike-sleepy
Probability: 0.544840
Token: [stag, coup, bernie]
米歇尔正在明尼苏达州竞选国会议员。米歇尔将保护未出生的孩子,坚决打击犯罪&边境,减税,你的#2A,热爱我们的军队,退伍军人,&将支持我们伟大的农民。米歇尔得到了我的全力支持!https://secure . winred . com/MichelleFischbach/website-捐赠 …
Topic: keep-total-complete-safe
Probability: 0.808480
Token: [michelle, fischbachmn, run, congress, minnesota, michelle, protect, unborn, strong, crime, border, cut, tax, love, military, vet, stand, great, farmer, michelle, complete, total, endorsement]
正文: @FoxNews 正在努力推动激进左派,无为民主党。不像他们的竞争对手, @CNN & MSDNC(康卡斯特),公平&平衡。他们什么时候才能明白?激进左派甚至从未允许@FoxNews 参与他们的低收视率辩论!
Topic: fake-news-state-people
Probability: 0.802032
Token: [foxnews, work, hard, push, radical, left, nothing, democrat, want, unlike, competitor, cnn, msdnc, comcast, fair, balance, ever, learn, radical, left, never, even, give, foxnews, permission, partake, low, rat, debate]
以下是特鲁多的一些可能的话题。
长期以来,矿业一直是加拿大经济的基石。现在,在我们向更清洁的未来过渡的过程中,它比以往任何时候都更加重要。今天在#PDAC2020 上,我们谈到了加拿大成为世界上最清洁的金属和 minerals.pic.twitter.com/cs27PXMMmD 供应商的机会
Topic: make-work-need-sure
Probability: 0.876077
Token: [mining, long, building, block, canadian, economy, ever, important, role, play, transition, cleaner, future, today, pdac, spoke, opportunity, canada, world, cleanest, supplier, metal, mineral, pxmmmd]
哈维尔·佩雷斯·德奎利亚尔一生致力于促进普遍人权&建设一个更加和平的世界,他的遗产将代代相传。我向他的家人和所有那些其生活受到他杰出工作影响的人致以最深切的慰问。
Topic: celebrate-hope-life-around
Probability: 0.791011
Token: [javier, rez, llar, dedicate, life, promote, universal, human, right, building, peaceful, world, legacy, live, generation, deepest, condolence, family, whose, life, touch, remarkable, work]
我们的首要任务是保证加拿大人的安全。随着新冠肺炎病毒在世界各地的传播,我们正在建立一个新的内阁委员会,以我们一直在做的工作为基础,预防和限制病毒在加拿大的传播,并监测其经济影响。https://pm . GC . ca/en/news/news-releases/2020/03/04/prime-minister-creates-Committee-新冠肺炎…
Topic: spoke-international-talk-spread
Probability: 0.612871
Token: [top, priority, keep, canadian, safe, covid, spread, around, world, create, new, cabinet, committee, build, work, prevent, limit, spread, virus, canada, monitor, economic, impact]
下图是 2020 年 3 月 1 日至 4 月 27 日,特朗普不同话题的推文比例。话题fake-news-state-people的推文占 46%。


下图显示了特鲁多不同话题的推文比例。话题make-work-need-sure的推文占 38%。


主题可视化
我们使用 pyLDAvis ,一个交互式 LDA 可视化包,来绘制所有生成的主题及其关键字。这里是特朗普推文话题的链接,以及特鲁多推文话题的链接。
左边的每个气泡代表一个主题。气泡的大小代表主题的流行程度。气泡之间的距离反映了主题之间的相似性。两个圈越近,话题越相似。

结束语
为了了解我们的世界领导人如何处理新冠肺炎危机,并与他们的追随者沟通,我们转向 Twitter,搜集了过去两个月唐纳德·特朗普和贾斯廷·特鲁多的推文。我们生成了词云来显示出现最多的词,通过开发不同主题数量的 LDA 模型来学习他们推文的主题。
在危机时刻,你认为谁能更好地利用 Twitter 并与他的人民沟通?
所有代码都可以在 GitHub 上找到。
感谢阅读。如果您有任何反馈,请在下面留言,通过我的博客【https://ai-journey.com/联系我,或者通过 LinkedIn 给我发消息。
信任:为什么人工智能伦理和 MLOps 齐头并进
对你的人工智能努力来说,可能发生的最糟糕的事情是,你最终通过良好的意图破坏了人们的生活。

劳伦·露露·泰勒在 Unsplash 上的照片
对你的人工智能努力来说,可能发生的最糟糕的事情是,你最终通过良好的意图破坏了人们的生活。
如果没有适当的人工智能道德实践,你聪明的人工智能应用程序可能会根据性别从工作申请中筛选最佳候选人,你可能会阻止好公民获得贷款资格,或者你的人工智能应用程序甚至可能导致孩子被不公平地从父母身边带走。
这些例子听起来有些极端,但它们都发生过,以非常糟糕的方式影响着人们的生活。然而,如果这些应用程序的创作者遵循了人工智能道德最佳实践,这些事件本来是可以避免的。
人工智能伦理有许多方面,包括:
- 管理层支持和监督
- 创造和实施最佳实践的人工智能伦理委员会
- 接受人工智能伦理培训的人工智能和商业专业人士
- 人工智能平台和工具可以自动检测和修复偏见,并向人类解释基于人工智能的建议背后的原因
- 执行偏差检测和可解释性的人工智能方法——这就是 MLOps 的用武之地
MLOps 通常被描述为结合数据科学家和运营团队的工作来管理 ML 生命周期。
我会更进一步:MLOps 结合人工智能道德最佳实践,将数据科学家和运营团队的工作结合起来,以大规模提供道德人工智能。
MLOps 涉及通过实施持续集成和持续交付(CI/CD)管道,将数据科学概念验证转移到生产前和生产中的最佳实践。你如何让这些管道合乎道德?确保您的 MLOps 最佳实践包括:
- 偏差监控
- 偏差的自动校正
- 公平性检测
- 漂移检测
- 模型可解释性
这就是 MLOps 和人工智能伦理如何结合起来,使人工智能的良好意图成为现实。
阅读更多关于信任与人工智能的内容:https://towards data science . com/how-to-不仅生存,而且领导人工智能革命-981d249f1bc7
你认为——请在评论中写下你需要信任人工智能做什么最重要的决定?
信任欧盟始于国内
建立分类模型预测哪些选民信任欧盟议会

马库斯·斯皮斯克在 Unsplash 上的照片
介绍
在这篇文章中,我将使用 2019 年欧洲选举研究(EES)选民研究数据和调查是否有可能建立一个可以准确预测一个选民是否信任欧盟议会的机器学习模型。
我的分析将按以下方式组织。首先,我将介绍我的数据来源——EES 2019 选民研究。接下来,我将描述在给定信息的情况下,问题可以被公式化的各种方式。选择了最合适的策略后,我将讨论优化的 XGBoost 模型预测结果的能力。为了理解是哪些特征在驱动它的决策,我将应用 TreeSHAP。最后,我将执行特征选择和规则提取,以得到一个更简单,但仍然大致准确的框架。
主要亮点
如果我们把选民分成两类:
- 推动者(当被问及对欧盟议会的信任程度时,回答“信任”或“非常信任”的人;第 1 组)
- 怀疑者(回答“既不信任也不不信任”、“不信任”、“不信任很多”的人);第 0 组)
可以得出以下结论:
- 一个经过训练和调整的 XGBoost 算法可以以 83%的准确率将投票者正确分组。
- 驱动模型决策的最重要因素是选民是否信任他们自己的国会。这甚至比受访者对欧洲一体化的看法或他们对自己国家成为欧盟成员的好处的看法更重要。
- 一条规则规定如果有人公开 不信任他们的国民议会,他们应该被归类为怀疑者,在 76% 的情况下都是对的。
数据
EES 2019 年选民研究(Schmitt 等人,2019 年)是一项大规模调查,该调查在 2019 年 5 月询问了所有 27 个欧盟成员国以及英国的公民关于他们的政治偏好。该调查包括诸如“在欧洲议会选举中,你投了哪个政党的票?”或者“你怎么看待国家对经济的调控?”混合了人口统计信息。总的来说,我用 Python 清理和处理的原始 Stata 数据文件包含 115 列和 26000 多行。
该表是一个非常丰富的资源,但是,对于建模来说,并不容易使用。它缺乏详细的数据字典,包含希腊语或俄语等语言所需的利基编码,以及以大多数机器学习方法难以使用的形式保存一些信息。
例如,关于最后一点,在同一个单列中,人们可以找到德国、荷兰和意大利选民对本国政党的评分。这是一个主要障碍,因为将这种数据输入基于树的模型或逻辑回归会导致模型隐含地认为与法国人关联的值 3 与西班牙人分配的值 3 相同,而实际上它们指的是不同的当事人。
为了解决我的研究问题,接下来的问题被选为我的目标变量——“你在多大程度上信任欧洲议会?”。它允许受访者从 1 到 5 中选择数字作为答案,其中 1 代表“是的,完全”,5 表示“不,一点也不”。选民可以选择“我不知道”,这也反映在数据中。
关于数据管理的更多细节,请查看我在 GitHub 上的代码,链接在文章底部。这里值得一提的最后一点是,在这个过程中,任何丢失的值都不会被修改,除非下面另有说明。
建模方法
我决定对数据应用 XGBoost,这是一种最先进的基于树的算法,被从业者和数据科学爱好者广泛使用。我的结果是在谷歌实验室用 Python 生成的。
作为研究的基础,我训练了 4 个不同的模型。
- 模型 A: 5 点量表结果——多类分类(将目标变量作为给定变量)
- 模型 B: 3 点量表结果——多类分类(“我不信任”和“我根本不信任”回答被合并到一个桶中;“我信任”和“我非常信任”回答也成为一个类别)
- 模型 C:二元分类——“我相信”和“我非常相信”的回答成为一类,其余的回答被分配到另一类
- 模型 D:二元分类——“我不信任”和“我根本不信任”的回答被视为一类,其余的回答被视为另一组
这些分类器的混淆矩阵如下。

混淆矩阵:模型 A(左上)、模型 B(右上)、模型 C(左下)、模型 D(右下)
首先,正如人们注意到的,A 和 B 必须处理非二进制结果,结果表现不佳。例如,模型 B 试图分配“既不信任,也不不信任”的条目比随机投掷硬币更糟糕。此外,只有 37%的 1s 被模型 a 正确预测。
鉴于这样的结果,我决定放弃这些,把注意力放在模型 C 和 d 上。对它们来说,结果看起来很有希望。再次考虑了问题陈述的核心,我最终选择了模型 C 作为合适的方法。
这一模型让我们能够区分两个群体,我将称之为倡导者(1 类)和怀疑者(0 类),后者包括那些“既不信任也不不信任”欧盟议会的人。由于这一点,我们可以将分类定义为在那些公开、明确信任泛欧机构的人和那些不信任泛欧机构的人之间划定界限,这比模型 d 更好地反映了问题陈述的意图。
此外,我还调查了模型 c 的精度、召回率或准确度等其他指标。如下图所示,它们看起来也不错。例如,如果算法预测 a 投票是发起人,它将在四分之三的情况下是正确的。总体绩效指标,如宏观 F1 分数接近 80%,显示了更好的前景。

型号 C 的分类报告
下一步,我使用 hyperopt 对模型 C 进行了调优,hyperopt 是一个 Python 库,可以用来帮助选择最佳的超参数。这导致了接下来两张图中所显示的性能。

模型 C 调优后的混淆矩阵

C 型调优后的分类报告
总结我这部分的分析,确实有可能建立一个好的预测模型,能够正确地发现信任欧盟议会的选民。该分类器对怀疑者的准确率和召回率都可以达到 85%以上,对促进者的准确率和召回率可以达到 73%以上。
模型解释
作为下一步,有人可能会问——“那么,哪些因素对我们的模型最重要?”。我们可以使用 TreeSHAP (Lundberg 等人,2020 年)来发现这一点,这是一种解释基于树的模型的博弈论方法。接下来的两张图表将为我们解答这个问题。


左图:模型所用要素的平均重要性,右图:SHAP 值在整个数据集中的分布
根据左图,我们可以清楚地看到,有 4 个特征主要驱动模型的预测。它们是 Q18_1、Q4、Q22 和 Q23,其含义如下。
- Q18_1 —“你信任[国家议会]吗?” (5 分制,从“是,完全— 1”到“不是,完全不是—5”)
- 问题 4——“您对欧盟的民主运作方式非常满意、相当满意、不太满意还是完全不满意?”(非常/一般/不太/完全不知道)
- 问题 22——“一般而言,您认为[国家]欧盟成员资格..?"(好/坏/都不是/不知道)
- 问题 23——“有人说欧洲统一应该进一步推进。其他人说它已经走得太远了。你有什么看法?” (11 分制,从“统一已经走得太远— 0”到“统一应该再推进一步—10”)
如果我们看看上面给出的 SHAP 值,看右边的图表,很有趣地看到,对于上面提到的前 3 个变量,两组之间的差异如此明显。那些不信任本国议会、对欧盟的民主不满意、并声称本国的欧盟成员资格对自己有负面影响的人,属于怀疑者之列。促销员则相反。关于欧洲统一的最后一个问题解释力较弱,集群也不那么明显。
虽然 Q4、Q22、Q23 和结果之间的关系似乎很直观,但对国家机构的信任程度是关键预测因素的事实是一个相当令人惊讶的见解。它强调了信任地方政府和中央政府之间的紧密联系。可以假设,对欧盟议会缺乏信心的问题是某些人对政治和公共部门普遍缺乏信心的结果,而不是一个独立的问题,因为问题源于欧盟的性质或其政策。
除此之外,如下图所示,Q18_1 和其他变量之间的相互作用似乎很小。例如,SHAP 值的大小表明了模型的输出是如何被推动的,并没有揭示出对国家机构缺乏信心会影响“脱离者”/“留在者”与信任欧盟议会之间的联系。




突出顶部特征之间关系的 SHAP 依赖图
XGBoost 提供的基尼系数重要性细节通常被用作衡量不同重要性的指标,也与 SHAP 产出大体一致。

与我们的模型处理的不同特征相关的增益
类似地,我们可以用 scikit-learn 的部分相关性图(PDP)验证 SHAP 相关性图显示的关系。比较两者,我们看不出有任何重大分歧。

3 个最重要功能的 PDP
当地的解释怎么样?总体重要性分数告诉我们的故事和我们在单个预测中看到的故事是一样的吗?为了仔细研究这一点,让我们来看看 3 个任意挑选的选民,以及专门用 SHAP 为他们做出的预测。



针对个人选民的 SHAP 势力阴谋
如我们所见,对于所有 3 个人,Q18_1、Q4、Q22 和 Q23 是最有影响力的因素。例如,在第一个图中,回答者对 Q18_1 的回答(在他们的例子中,“既不信任也不不不信任”)使模型的输出下降最多,而对欧盟的民主状况相当满意使其上升。给定这些信息,以及我们所知道的关于投票者的其他一切,该模型的输出值(以对数优势为单位)为-1.13,这可以转换为成为推动者的大约概率为 24%。
模型简化
作为我分析的最后一步,我将尝试制作一个更简单的模型,它只需要几个独立变量就可以提供令人满意的性能。
让我们假设我们的目标是得到一个达到 80%准确率的东西。
在进行特征选择之后,事实证明,仅考虑前 4 个特征(即 Q18_1、Q4、Q22、Q23)的模型可以表现得非常好!它尤其擅长预测持怀疑态度的选民,详情如下。

简单模型的混淆矩阵

简单模型的分类报告
我们能再简化一下吗?应用一种称为 defragTrees (Hara 和 Hayashito,2018)的规则提取方法,我们的模型揭示了我们确实可以进一步提取它!

运行可解释性算法产生了大量的规则,如左图所示,这些规则可以总结如下。
- 如果被调查者在 Q23 中的得分大于 1,在 Q18_1 中的得分小于 4,他们就是推广者。
- 否则,他们就是怀疑者。
如果这个系统被应用到整个测试集,缺失值被特征的中间值代替,我们将在 77%的情况下是正确的。
此外,额外的分析表明,在整个数据集中,将 Q18_1 的答案小于 4 的每个人分类为促进者,而将其余的回答者分类为怀疑者,会导致惊人准确的结果。使用这个基于信任级别的简单规则,我们可以正确地检测 76%的启动子。此外,当这个原则预测到一个怀疑者时,它有 79%的几率是正确的。
限制
和所有数据驱动的研究一样,我们需要非常小心我们的方法建立在什么样的假设之上。
首先,正如 Gosiewska 和 Biecek (2019)所解释的,使用 SHAP 评估全球重要性可能会导致虚假的结果,因为分析中完全忽略了相互作用的影响。换句话说,我们可能高估了一些特征的重要性,因为它们与真正重要的特征相关。
其次,考虑到用于模型估计和测试预测的数据比例,精确的性能数据可能会略有变化。所报告的精度或召回分数应该被视为在数据集的上下文中保持的相当准确但不完美的估计。
第三,我们必须记住,相关性并不意味着因果关系。揭示驱动我们模型的关系可以带来有趣的见解。然而,这些只能在预测的背景下理解,而不是黄金标准的统计推断。
最后,我对“我不知道”答案的编码处理只是在调查数据中处理这种回答的一种方法。更有力的研究将考虑其他方法的利弊,选择最合适的方法。
参考
南 Hara,K. Hayashi,使树集合可解释:贝叶斯模型选择方法。《第 21 届人工智能与统计国际会议论文集》(AISTATS'18),第 77–85 页,2018 年。
Gosiewska,a .和 Biecek,p .,不要相信加法解释,arXiv,2019。
Lundberg,S.M .,Erion,g .,Chen,h .等【从局部解释到全球理解用可解释的人工智能研究树木】。自然机器智能 2,56–67(2020)。https://doi.org/10.1038/s42256-019-0138-9
施米特、赫尔曼、霍波特、萨拉 B、范德布鲁格、沃特和波帕、塞巴斯蒂安 A (2019) 欧洲议会选举研究 2019,选民研究 。
GitHub 链接—代码和更多解释:
https://github.com/DawidGalarowicz/EUParlTrust
试试 TextHero:清理和分析 Pandas 中文本的绝对最简单的方法
自然语言处理(NLP)让 Python 变得简单

加布里埃尔·巴西诺在 Unsplash 上的照片
简化的自然语言处理
我一直在寻找新的工具来帮助我简化自然语言处理管道,所以当我偶然发现一个展示 Texthero 功能的视频短片时,我知道我必须马上尝试一下。Texthero 被设计成一个 Pandas 包装器,因此它使得预处理和分析基于文本的 Pandas 系列变得前所未有的容易。我立即调出文档,打开笔记本,下载了几千条 Reddit 线程进行分析,以测试新的 lib。
注:text hero 库还在测试中!可能会有错误,管道可能会改变。我发现了 wordcloud 功能中的一个 bug,并报告了它。它应该在即将到来的更新中得到修复!
Texthero 概述
在处理代码时,我会更仔细地研究这些,但为了快速概述,Texthero 分为四个功能模块:
预处理
预处理模块是关于高效清理基于文本的熊猫系列。它主要是在幕后使用正则表达式(regex) 。
NLP
NLP 模块包含一些常见的 NLP 任务,比如命名实体识别和名词块。它正在使用引擎盖下的空间。
代表权
表示模块用于使用不同的算法创建单词向量。它包括像主成分分析和 kMeans 这样的东西。它使用 scikit-learn for TF-IDF 和 Count ,并从语言模型预先计算加载嵌入。
可视化
可视化模块用于可视化散点图或生成文字云。这个模块目前只有几个功能,并在引擎盖下使用了 Plotly 和 WordCloud。
查看文档以获得完整的特性列表!
[## Texthero 文本预处理,从零到英雄的表示和可视化。
从零到英雄的文本预处理、表示和可视化。
从零到 hero.texthero.org 的文本预处理、表示和可视化](https://texthero.org/)
依赖性和数据
尽管使用 pip 安装很容易,但由于熊猫版本的问题,当我试图在我的有 Apache Airflow 的环境中安装它时,我遇到了一个冲突。此外,它需要一段时间来安装在一个新的环境中,因为它在后端使用了如此多的其他库。第一次导入后,它还会下载一些额外的东西。
对于数据集,我使用 PRAW 从 Reddit 中提取数据。检查这篇文章,如果你需要一个 PRAW 复习。
[## 用 Python 收集自然语言处理(NLP)文本的入门指南
通过 API 和 Web 抓取收集文本
towardsdatascience.com](/ultimate-beginners-guide-to-collecting-text-for-natural-language-processing-nlp-with-python-256d113e6184)
!pip install textheroimport praw
import pandas as pd
import texthero as herofrom config import cid, csec, ua #PRAW credentials
注意当你第一次导入 Texthero 时,你会看到它从 NLTK 和 Spacy 下载了一些东西:

初次导入时下载 Texthero
获取一些数据
我正在从教学子编辑中提取数据,看看我们是否能找出任何与 COVID heavy America 秋季开学有关的主题。
#create a reddit connection
reddit = praw.Reddit(client_id= cid,
client_secret= csec,
user_agent= ua)#list for df conversion
posts = []#return 1000 new posts from teaching
new = reddit.subreddit('teaching').new(limit=1000)#return the important attributes
for post in new:
posts.append([post.title, post.score, post.num_comments, post.selftext, post.created, post.pinned, post.total_awards_received])#create a dataframe
df = pd.DataFrame(posts,columns=['title', 'score', 'comments', 'post', 'created', 'pinned', 'total awards'])#return top 3 df rows
df.head(3)
PRAW 使得从 Reddit 提取数据并将其加载到熊猫数据框架中变得非常容易。
注意我从教学拉了 1000 个新岗位。使用 df.head(3) ,dataframe 的输出看起来会像这样:

数据帧的前 3 行
用 Texthero 预处理
texthero 库的真正卖点是它简化的预处理管道。记不住正则表达式语法?Texthero 为您报道!只需调用。 clean() 方法并通过 dataframe 系列:
df['clean_title'] = hero.clean(df['title'])
使用 clean()时,它默认运行以下七个函数
fillna(s)用空格替换未赋值的值。lowercase(s)小写所有文本。remove_digits()删除所有数字块。remove_punctuation()删除所有字符串.标点(!" #$% & '()*+,-。/:;< = >?@[]^_`{|}~).remove_diacritics()去除琴弦上的所有重音。remove_stopwords()删除所有停用词。remove_whitespace()去掉单词之间的所有空格。
定制清洗
如果缺省设置不能满足需要,那么创建一个定制的清理管道就非常简单了。例如,如果我想保留停用词并阻止包含的词,我可以注释掉 remove_stopwords 并将text hero . preprocessing . stem()添加到管道中:
from texthero import preprocessing#create a custom cleaning pipeline
custom_pipeline = [preprocessing.fillna
, preprocessing.lowercase
, preprocessing.remove_digits
, preprocessing.remove_punctuation
, preprocessing.remove_diacritics
#, preprocessing.remove_stopwords
, preprocessing.remove_whitespace
, **preprocessing.stem**]#pass the custom_pipeline to the pipeline argument
df['clean_title'] = hero.clean(df['title'], pipeline = custom_pipeline)df.head()
注意 custom_pipeline 是一个预处理函数列表。查看文档以获得预处理特性的完整列表!

Clean_title 添加到数据帧
检查热门词汇
检查顶部单词只有一行代码,这是我喜欢做的事情,看看是否有额外的单词我应该考虑添加到停用单词列表中。Texthero 还没有内置条形图,它只有散点图,所以我将使用Plotly express来可视化条形图中的顶部单词。
tw = hero.visualization.top_words(df['clean_title']).head(10)import plotly.express as pxfig = px.bar(tw)
fig.show()
tw.head()

热门词汇图
添加新的停用字词
词干 这些词加上“’”(老师和学生之间注意一下)这些停用词应该给我更多独特的词。词干已经添加到自定义管道中,但需要添加停止词。使用两个列表上的联合可将停用词添加到停用词列表中:
from texthero import stopwordsdefault_stopwords = stopwords.DEFAULT#add a list of stopwords to the stopwords
custom_stopwords = default_stopwords.union(set(["'"]))#Call remove_stopwords and pass the custom_stopwords list
df['clean_title'] = hero.remove_stopwords(df['clean_title'], custom_stopwords)
注意,custom_stopwords 列表被传递到 hero.remove_stopwords()中。我将重新可视化它并检查结果!

阻止结果
在使用词干和额外的停用词后,结果看起来好一点了!
建造管道
感谢熊猫的。pipe() ,把 Texthero 模块组件链接在一起超级简单。为了形象化题目,我准备用 主成分分析 来压缩向量空间。我也准备运行 K-means 聚类来增色。记住,Texthero 将一个系列作为输入,将系列作为输出,这样我可以将输出设置为 dataframe 中的一个新列。
#Add pca value to dataframe to use as visualization coordinates
df['pca'] = (
df['clean_title']
.pipe(hero.tfidf)
.pipe(hero.pca)
)#Add k-means cluster to dataframe
df['kmeans'] = (
df['clean_title']
.pipe(hero.tfidf)
.pipe(hero.kmeans)
)df.head()

只用几行代码就应用了 PCA 和 K-means 聚类!现在可以使用 hero.scatterplot() 将数据可视化
#generate scatter plot
hero.scatterplot(df, 'pca', color = 'kmeans', hover_data=['title'] )
因为它在引擎盖下使用了 Plotly,散点图就像你所期望的那样具有交互性!可以根据需要放大。现在是时候探索视觉化的结果,看看可以获得什么样的洞见了!

标题散点图
最后的想法
虽然该库仍处于测试阶段,但我看到了 Texthero 充满希望的未来,并希望它得到应有的爱。它使得清理和准备熊猫数据框中的文本变得轻而易举。我希望增加一些可视化选项,但是散点图是一个很好的开始。如果您有兴趣了解更多关于自然语言处理的知识,请查看我的其他文章,这些文章涵盖了一些基础和高级主题。
[## 用 Python 实现自然语言处理中单词嵌入的简单方法
使用 Gensim 和 Plotly 探索单词嵌入
towardsdatascience.com](/the-simple-approach-to-word-embedding-for-natural-language-processing-using-python-ae028c8dbfd2) [## 使用 Python 学习自然语言处理的 3 个超级简单的项目
单词云、垃圾邮件检测和情感分析的简单代码示例
towardsdatascience.com](/3-super-simple-projects-to-learn-natural-language-processing-using-python-8ef74c757cd9) [## 用 Python 分析《黑色大丽花谋杀案》的残暴歌词用 Genius API,SpaCy…
利用自然语言处理和词性标注发现《死亡金属》中的主题
towardsdatascience.com](/using-python-to-analyze-the-brutal-lyrics-of-the-black-dahlia-murder-with-genius-api-spacy-bfc7e0e8577f)
完全码
感谢阅读。以下是完整的代码:
#create a reddit connection
reddit = praw.Reddit(client_id= cid,
client_secret= csec,
user_agent= ua)
#list for df conversion
posts = []#return 1000 new posts from teaching
new = reddit.subreddit('teaching').new(limit=1000)#return the important attributes
for post in new:
posts.append([post.title, post.score, post.num_comments, post.selftext, post.created, post.pinned, post.total_awards_received])#create a dataframe
df = pd.DataFrame(posts,columns=['title', 'score', 'comments', 'post', 'created', 'pinned', 'total awards'])#return top 3 df rows
df.head(3)from texthero import preprocessingcustom_pipeline = [preprocessing.fillna
, preprocessing.lowercase
, preprocessing.remove_digits
, preprocessing.remove_punctuation
, preprocessing.remove_diacritics
, preprocessing.remove_stopwords
, preprocessing.remove_whitespace
, preprocessing.stem]df['clean_title'] = hero.clean(df['title'], pipeline = custom_pipeline)
df.head()from texthero import stopwordsdefault_stopwords = stopwords.DEFAULT
custom_stopwords = default_stopwords.union(set(["'"]))df['clean_title'] = hero.remove_stopwords(df['clean_title'], custom_stopwords)hero.visualization.top_words(df['clean_title'])tw = hero.visualization.top_words(df['clean_title']).head(10)import plotly.express as pxfig = px.bar(tw)
fig.show()df['pca'] = (
df['clean_title']
.pipe(hero.tfidf)
.pipe(hero.pca)
)df['kmeans'] = (
df['clean_title']
.pipe(hero.tfidf)
.pipe(hero.kmeans)
)hero.scatterplot(df, 'pca', color = 'kmeans', hover_data=['title'] )
谢谢大家!
在 Pixel 4 XL 上尝试 Android 的 NNAPI ML 加速器和对象检测
测试和比较 Android 的 ML 加速和 MobileDet 模型
随着对更私密、更快速、低延迟的机器学习的需求增加,对能够在所谓的“边缘”上良好运行的更易访问的设备上解决方案的需求也在增加其中两个解决方案是目前在谷歌 Pixel 4 手机上可用的 Pixel 神经核心 (PNC)硬件及其边缘 TPU 架构,以及Android 神经网络 API (NNAPI),这是一个为在 Android 设备上执行机器学习操作而设计的 API。
在本文中,我将展示我如何修改面向 Android 的 TensorFlow Lite 对象检测演示,以使用在像素 4 XL 上的 NNAPI 下运行的边缘 TPU 优化模型。此外,我想展示我为记录预测延迟所做的更改,并比较使用默认 TensorFlow Lite API 和 NNAPI 所做的更改。但在此之前,让我简要概述一下我到目前为止介绍的术语。

应用程序检测我最喜欢的书之一。Pic by me 。
像素神经核心、边缘 TPU 和 NNAPI
Pixel 神经核心是之前 Pixel 视觉核心的继任者,是 Pixel 4 硬件的一部分的特定领域芯片。它的架构遵循边缘 TPU(张量处理单元)的架构,这是谷歌用于边缘计算设备的机器学习加速器。作为一款专为“边缘”设计的芯片,意味着它比谷歌云平台上的大型芯片更小、更节能(它每秒可以执行 4 万亿次运算,而只消耗 2W )。
然而,边缘 TPU 并不是所有机器学习的全面加速器。该硬件旨在改进正向传递操作,这意味着它擅长作为推理引擎,而不是作为训练工具。这就是为什么你会发现在设备上使用的模型在其他地方被训练的应用。
在软件方面,我们有 NNAPI。这个用 C 编写的 Android API 在采用硬件加速器(如 Pixel Visual Core 和 GPU)的设备上为 TensorFlow Lite 模型提供加速。用于 Android 的 TensorFlow Lite 框架包括一个 NNAPI 委托,所以不用担心,我们不会编写任何 C 代码。

图一。Android 神经网络 API 的系统架构。来源:https://developer.android.com/ndk/guides/neuralnetworks
模型
我们将在这个项目中使用的模型是 MobileDet 对象检测模型的 float32 版本,该模型针对边缘 TPU 进行了优化,并在 COCO 数据集上进行了训练(链接)。让我快速解释一下这些术语的意思。 MobileDet (熊等)是一个最新的轻量级对象检测模型系列,适用于手机等低计算能力设备。这种 float32 变体意味着它不是一个量化的模型,一个经过转换以降低模型精度为代价的模型。另一方面,完全量化的模型使用基于 8 位整数的小权重(源)。然后,我们有了 COCO 数据集,它是“上下文中的公共对象”的缩写( Lin et al. )。这个图片集有超过 200,000 张图片,分为 90 类,包括“鸟”、“猫”、“人”和“车”。"
现在,在那一点理论之后,让我们看看应用程序。
该应用程序
我使用的应用程序基于 TensorFlow 存储库中提供的 Android 对象检测示例应用程序。但是,我对它进行了修改,使用了 NNAPI 并将推理时间记录到一个文件中,这些数据是我用来比较 NNAPI 和默认 TFLITE API 的预测时间的。下面是DetectorActivity.java文件,负责生成检测结果——完整的源代码在我的 GitHub 上;我只是显示这个文件,因为它有最多的变化。在这个文件中,我更改了模型的名称(在将 MobileDet 模型添加到 assets 目录之后),更改了变量TF_OD_API_INPUT_SIZE以反映 MobileDet 的输入大小,并将TF_OD_API_IS_QUANTIZED设置为false,因为模型没有被量化。除此之外,我添加了两个列表来收集预测的推断时间(每个 API 一个列表),并添加了一个 override onStop方法来在用户关闭应用程序时将列表转储到一个文件中。我必须做的其他小改动是将 TFLiteObjectDetectionAPIModel.java 的 T4 从 10 改为 100,并在 Android 清单中添加 T5 权限,这样应用程序就可以将文件写入文档目录。
我做的另一个重要改变是注释掉——从而启用——允许我们使用 NNAPI 运行应用程序的切换按钮。默认情况下,那部分代码是有注释的,所以不可能从应用内部激活 API 不过,我可能错了(如果你发现其他情况,请纠正我)。
你可以在 https://github.com/juandes/mobiledet-tflite-nnapi 的我的 GitHub repo 中找到这个应用的完整源代码。要运行它,请打开 Android Studio,选择“打开现有的 Android Studio 项目”,然后选择项目的根目录。项目打开后,单击绿色小锤子图标构建它。然后,单击播放图标,在虚拟设备或实际设备(如果已连接)上运行它。如果可能的话,使用真实的设备。
测量延迟
那么,这款应用检测物体的速度有多快呢?为了测量延迟,我添加了一个小功能,将使用普通 TFLITE 和 NNAPI 做出的推断的预测时间(以毫秒为单位)写入文件。之后,我拿起文件,在 R 中执行了一点分析,以从数据中获得洞察力。下面是结果。

图 2:用 TFLITE API 完成预测的推断时间

图 3:用 TFLITE API 完成预测的推断时间
图 2 和图 3 是每个 API 下推理时间的直方图。第一个(n=909)对应于默认的 TFLITE API,在 100(毫秒)左右有一个峰值,在可视化的高端有几个极端的异常值。图 3 (n=1169)对应于使用 NNAPI 完成的预测,其峰值约为 50 毫秒。然而,这些极端异常值会使平均值和分布向右移动。因此,为了更好地可视化时间,我删除了这些值,并绘制了没有它们的相同的可视化效果。现在,它们看起来如下:

图 4:用 TFLITE API 完成的预测的推断时间(没有异常值)

图 5:用 NNAPI 完成的预测的推断时间(没有异常值)
更好,对吗?两个图上的黑色垂直线表示平均值。对于 TFLITE 图,平均推断时间为 103 毫秒,中位数为 100 毫秒。在 NNAPI 方面,平均预测时间为 55 毫秒,中位数为 54 毫秒。几乎快了一倍。
下面的视频展示了应用程序的运行。在这里,我只是将手机指向我的电脑,从视频中检测对象:
总结和结论
机器学习和人工智能的进步确实令人着迷。首先,他们接管了我们的电脑和云,现在他们正在向我们的移动设备进军。然而,后者和我们传统上用来部署机器学习平台的平台之间有很大的区别。更小的处理器和电池依赖性就是其中的一些。因此,专门针对这类设备的框架和硬件的开发正在迅速增加。
这些工具中的几个是像素神经核心,边缘 TPU 和 NNAPI。这种硬件和软件的结合旨在为我们的移动设备带来高效准确的人工智能。在本文中,我对这些进行了概述。然后,我展示了如何更新用于 Android 的 TensorFlow Lite 对象检测示例,以启用 NNAPI 并写入推理时间文件。使用这些文件,我用 R 做了一个小分析,将它们可视化,发现用 NNAPI 做的预测比用默认 API 做的预测花费了大约一半的时间。
感谢阅读:)
[## juandes/mobiledet-tflite-nnapi
注意:该项目最初取自(并修改)官方 TensorFlow 示例库,网址为…
github.com](https://github.com/juandes/mobiledet-tflite-nnapi)
生成合成时间序列数据的 Python 库
tsBNgen:一个 Python 库,用于从任意动态贝叶斯网络结构中生成时间序列数据

由 Unsplash 上的 Behzad Ghaffarian 拍摄的照片
合成数据广泛应用于各个领域。这是因为许多现代算法需要大量数据来进行有效的训练,而数据收集和标记通常是一个耗时的过程,并且容易出错。此外,一些真实世界的数据,由于其性质,是保密的,不能共享。
提出了生成对抗网络等方法来生成时间序列数据。然而,GAN 难以训练并且可能不稳定;此外,它需要大量的数据来进行有效的训练。
本文将介绍一个 python 库 tsBNgen ,它基于任意的动态贝叶斯网络结构生成合成时间序列数据。
虽然 tsBNgen 主要用于生成时间序列,但是通过将时间序列的长度设置为 1,它也可以生成横截面数据。
以下是本文中讨论的主题列表。
- 简介
- 特性
- 指令
- 示例
- 结论

简介
tsBNgen 是在 MIT 许可下发布的 python 包,用于从任意贝叶斯网络结构中生成时间序列数据。贝叶斯网络是一种广泛用于对现实世界过程中的不确定性建模的概率图形模型。动态贝叶斯网络(DBNs)是一类特殊的贝叶斯网络,用于对时间和时间序列数据进行建模。
贝叶斯网络在教育和医学等领域受到了广泛的关注。例如,在中,作者使用 HMM(DBN 的一种变体)来预测学生在教育视频游戏中的表现。有向图模型(贝叶斯网络)的一个显著优点是,它们可以表示图中节点之间的因果关系;因此,它们提供了一种直观的方法来模拟现实世界的过程。一旦专家确定了图形结构,这种说法使得 tsBNgen 成为生成数据的非常有用的软件。从某种意义上说,与 GAN 等数据驱动的方法不同,tsBNgen 是一种基于模型的方法。
要了解关于这个包、文档和例子的更多信息,请访问下面的 GitHub 资源库。
描述说明 tsBNgen 是一个 Python 包,基于任意贝叶斯网络生成时间序列数据…
github.com](https://github.com/manitadayon/tsBNgen)
我最近在 YouTube 上制作了一系列视频来使用这个包。这些视频也在我的 GitHub 页面上,你可以在这里观看:
我尝试详细检查贝叶斯网络和 tsBNgen 包。如果你有任何问题或意见,你可以在我的 YouTube 频道问我。
特征
以下是 tsBNgen 支持的特性和功能列表:
- 简单易用的界面。
- 支持离散、连续和混合网络(离散和连续节点的混合)。
- 对连续节点使用多项式分布和高斯分布支持离散节点。
- 支持任意环回(时间连接)值的时间依赖性。
- 易于修改和扩展代码以支持新的结构。
- 基于模型的方法,一旦知道了因果结构,就可以生成综合数据。
指令
要使用 tsBNgen ,要么克隆上述存储库,要么使用以下命令安装软件:
pip install tsBNgen
软件成功安装后,发出以下命令导入所有函数和变量。
from tsBNgen import *
from tsBNgen.tsBNgen import *
这就是你利用软件中存在的所有功能所需要的。
例子
在查看一些示例之前,让我定义以下参数,这些参数将在本节中使用。
注:以下描述、表格(作为图像的一种形式)、图像均由作者从本文中获得。

例 1
假设您想要为图 1 中的以下架构生成数据,这是一个 HMM 结构。

图一。(作者供图)
顶层节点被称为状态,下面的节点被称为观察。在 HMM 中,状态是离散的,而观察值可以是连续的,也可以是离散的。下表总结了图 1 的参数设置和概率分布。


t 时刻所有节点的条件概率分布(作者供图)
在表 1 中,T 是指时间序列的长度,N 是指样本数,loopback 决定了时间连接的长度。例如,loopback 值为 1 意味着一个节点在以前连接到一些其他节点。
注 : tsBNgen 可以通过设置 T=1 来模拟标准贝叶斯网络(横截面数据)。
具有上述 CPD 和参数的架构 1 可以轻松实现如下:
import time
START=time.time()
T=20
N=1000
N_level=[4]
Mat=pd.DataFrame(np.array(([0,1],[0,0]))) # HMM
Node_Type=['D','C']CPD={'0':[0.25,0.25,0.25,0.25],'01':
{'mu0':20,'sigma0':5,'mu1':40,'sigma1':5,
'mu2':60,'sigma2':5,'mu3':80,'sigma3':5}}Parent={'0':[],'1':[0]}CPD2={'00':[[0.6,0.3,0.05,0.05],[0.25,0.4,0.25,0.1],[0.1,0.3,0.4,0.2],
[0.05,0.05,0.4,0.5]],'01':{'mu0':20,'sigma0':5,'mu1':40,'sigma1':5,
'mu2':60,'sigma2':5,'mu3':80,'sigma3':5
}}loopbacks={'00':[1]}
Parent2={'0':[0],'1':[0]}
Time_series1=tsBNgen(T,N,N_level,Mat,Node_Type,CPD,Parent,CPD2,Parent2,loopbacks)
Time_series1.BN_data_gen()
FINISH=time.time()
print('Total Time is',FINISH-START)
上面的代码生成 1000 个长度为 20 的时间序列,对应于状态和观察值。观察值正态分布,具有特定的平均值和标准偏差。这些状态是离散的(因此是“D”),并采用由 N_level 变量确定的四个可能的级别。 loopbacks 是一个字典,其中每个键都有以下形式:node+它的 parent。因为在架构 1 中,只有状态,即节点 0(根据图的拓扑排序)跨时间连接,并且在时间 t 时节点 0 的父节点是在时间 t-1 时的节点 0;因此,回环的键值是‘00 ’,并且由于时间连接仅跨越一个时间单位,所以其值是 1。Node_Type 确定图中节点的类别。例如,在本例中,第一个节点是离散的(“D”),第二个节点是连续的(“C”)。Mat 表示网络的邻接矩阵。
生成上述数据的总时间为 2.06 秒,通过 HMM 算法运行该模型,即使是五个样本,我们的准确率也超过 93.00 %。
现在让我们看一个更复杂的例子。从现在开始,为了节省空间,我避免显示 CPD 表,只显示用于生成数据的架构和 python 代码。
例 2
示例 2 参考图 2 中的架构,其中前两层中的节点是离散的,而最后一层 nodes(u₂是连续的。

图 2(作者照片)
根据图的拓扑排序,可以将每个时间点的节点命名为 0、1 和 2。假设您希望在以下情况下生成数据:节点 0(顶部节点)取两个可能值(二进制),节点 1(中间节点)取四个可能值,最后一个节点是连续的,并且对于其父节点的每个可能值将根据高斯分布进行分布。以下 python 代码模拟了 2000 个样本的场景,每个样本的长度为 20。
T=20
N=2000
N_level=[2,4]
Mat=pd.DataFrame(np.array(([0,1,1],[0,0,1],[0,0,0])))
Node_Type=['D','D','C']CPD={'0':[0.6,0.4],'01':[[0.5,0.3,0.15,0.05],[0.1,0.15,0.3,0.45]],'012':{'mu0':10,'sigma0':2,'mu1':30,'sigma1':5,
'mu2':50,'sigma2':5,'mu3':70,'sigma3':5,'mu4':15,'sigma4':5,'mu5':50,'sigma5':5,'mu6':70,'sigma6':5,'mu7':90,'sigma7':3
}}Parent={'0':[],'1':[0],'2':[0,1]}CPD2={'00':[[0.7,0.3],[0.2,0.8]],'011':[[0.7,0.2,0.1,0],[0.6,0.3,0.05,0.05],[0.35,0.5,0.15,0],
[0.2,0.3,0.4,0.1],[0.3,0.3,0.2,0.2],[0.1,0.2,0.3,0.4],[0.05,0.15,0.3,0.5],[0,0.05,0.25,0.7]],'012':{'mu0':10,'sigma0':2,'mu1':30,'sigma1':5,
'mu2':50,'sigma2':5,'mu3':70,'sigma3':5,'mu4':15,'sigma4':5,'mu5':50,'sigma5':5,'mu6':70,'sigma6':5,'mu7':90,'sigma7':3
}}
Parent2={'0':[0],'1':[0,1],'2':[0,1]}
loopbacks={'00':[1],'11':[1]}
Time_series2=tsBNgen(T,N,N_level,Mat,Node_Type,CPD,Parent,CPD2,Parent2,loopbacks)
Time_series2.BN_data_gen()
如上面的代码所示,节点 0(顶部节点)在第一个时间步中没有父节点(这是变量 parent 所代表的)。在因果或贝叶斯网络中,这有时被称为根或外生变量。节点 1 连接到节点 0,节点 2 连接到节点 0 和 1。为了表示时间 0 之后的其他时间步长的结构,使用变量 Parent2。这表示节点 0 跨时间连接到自身(因为在环回中‘00’是[1],所以时间 t 仅连接到 t-1)。节点 1 同时连接到节点 0,并在之前连接到节点 1(这也可以从 loopback 变量中看出)。
由于 tsBNgen 是基于模型的数据生成,因此您需要提供每个节点的分布(对于外部节点)或条件分布。如果您想要生成与任意分布的架构相对应的合成数据,那么您可以选择 CPD 和 CPD2 为您喜欢的任何值,只要每个离散分布的条目之和为 1。例如,节点 0 的 CPD 为[0.6,0.4]。你可以把这些值改成你喜欢的任何值,只要它们加到 1 上。
例 3
示例 3 参考图 3 中的架构,其中前两层中的节点是离散的,而最后一层 nodes(u₂是连续的。

图 3(作者照片)
假设您希望在以下情况下生成数据:节点 0(顶部节点)是二进制的,节点 1(中间节点)取四个可能值,节点 2 是连续的,并且将根据其父节点的每个可能值的高斯分布进行分布。以下 python 代码模拟了 1000 个样本的场景,每个样本的长度为 10。
T=10
N=1000
N_level=[2,4]
Mat=pd.DataFrame(np.array(([0,1,0],[0,0,1],[0,0,0])))
Node_Type=['D','D','C']
CPD={'0':[0.5,0.5],'01':[[0.6,0.3,0.05,0.05],[0.1,0.2,0.3,0.4]],'12':{'mu0':10,'sigma0':5,'mu1':30,'sigma1':5,
'mu2':60,'sigma2':5,'mu3':80,'sigma3':5}}
Parent={'0':[],'1':[0],'2':[1]}CPD2={'00':[[0.7,0.3],[0.3,0.7]],'0011':[[0.7,0.2,0.1,0],[0.5,0.4,0.1,0],[0.45,0.45,0.1,0],
[0.3,0.4,0.2,0.1],[0.4,0.4,0.1,0.1],[0.2,0.3,0.3,0.2],[0.2,0.3,0.3,0.2],[0.1,0.2,0.3,0.4],[0.3,0.4,0.2,0.1],[0.2,0.2,0.4,0.2],
[0.2,0.1,0.4,0.3],[0.05,0.15,0.3,0.5],[0.1,0.3,0.3,0.3],[0,0.1,0.3,0.6],[0,0.1,0.2,0.7],[0,0,0.3,0.7]],'112':{'mu0':10,'sigma0':2,'mu1':30,'sigma1':2,
'mu2':50,'sigma2':2,'mu3':60,'sigma3':5,'mu4':20,'sigma4':2,'mu5':25,'sigma5':5,'mu6':50,'sigma6':5,'mu7':60,'sigma7':5,
'mu8':40,'sigma8':5,'mu9':50,'sigma9':5,'mu10':70,'sigma10':5,'mu11':85,'sigma11':2,'mu12':60,'sigma12':5,
'mu13':60,'sigma13':5,'mu14':80,'sigma14':3,'mu15':90,'sigma15':3}}Parent2={'0':[0],'1':[0,0,1],'2':[1,1]}
loopbacks={'00':[1], '01':[1],'11':[1],'12':[1]}Time_series2=tsBNgen(T,N,N_level,Mat,Node_Type,CPD,Parent,CPD2,Parent2,loopbacks)
Time_series2.BN_data_gen()
同样,您可以为任何想要的图形模型生成时间序列数据。这是一个非常好的工具,因为许多现实世界的问题都可以建模为贝叶斯和因果网络。
有关更多示例和最新文档,请访问下面的 GitHub 页面。
描述说明 tsBNgen 是一个 Python 包,基于任意贝叶斯网络生成时间序列数据…
github.com](https://github.com/manitadayon/tsBNgen)
如果你想了解更多,请务必在我的 YouTube 频道上观看我最近的视频。
奖励:如果你想看 HMM 等图形建模算法和 LSTM 等深度学习方法在合成生成的时间序列上的对比分析,请看看这个 paper⁴.
结论
在本文中,我介绍了 tsBNgen ,这是一个从任意 BN 生成合成数据的 python 库。使用两个示例解释了该软件的特性和功能。有关该软件的更多最新信息,请访问上面提到的 GitHub 页面。
参考
[1] M. Frid-Adar,E. Klangand,M. Amitai,J. Goldberger,H. Greenspan,使用 gan 的合成数据增强用于改进肝脏病变分类(2018),IEEE 2018 第 15 届生物医学成像国际研讨会
。
[2] M. Tadayon,G. Pottie,利用隐马尔可夫模型预测教育游戏中的学生表现 (2020),IEEE 2020 IEEE 教育汇刊。
[3] M. Tadayon,G. Pottie, tsBNgen:一个从任意动态贝叶斯网络结构生成时间序列数据的 Python 库 (2020),arXiv 2020,arXiv 预印本 arXiv:2009.04595。
[4] M. Tadayon,G. Pottie,隐马尔可夫模型与 LSTM 的比较分析:一种模拟方法 (2020),arXiv 2020,arXiv 预印本 arXiv:2008.03825。
三氯甲烷降解为五氯苯甲醚
生命科学的数理统计和机器学习
茫然不知所措

这是来自专栏 生命科学的数理统计和机器学习 的第十六篇文章,在这里我试图以简单的方式解释生物信息学和计算生物学中使用的一些神秘的分析技术。在我的上一篇文章中,tSNE vs. UMAP:全局结构,我触及了大困惑的极限,作为 tSNE 保留更多全局数据结构的一种潜在方式,当试图使用 tSNE 超越可视化来处理数据点簇之间的层次关系(聚类)时,这变得很重要。在这里,我将更详细地描述在使用 tSNE 时出现的一些陷阱,比如将非线性 tSNE 转换为线性 PCA 降维。
为什么使用 tSNE 有很大的困惑
随着数据集大小的增长,例如在 scRNAseq 字段中,处理数十万甚至数百万个单元几乎是一种惯例,在大型数据集上使用具有超过推荐值(5 到 50) 的巨大复杂性的 tSNE 变得越来越常见,以实现数据结构的稳健发现。事实上,在大型数据集上使用默认困惑可能没有什么意义,因为它经常导致数据点的不太明显的聚类,就像在几篇出版物中显示的那样,例如在这篇伟大的文章中,也可以在这里看到很好的讨论。

小的困惑值不允许解析数据结构,图像源
即使对于一个表现良好的 MNIST 基准数据集,大小困惑之间的差异也是显而易见的:更大的困惑导致 tSNE 降维图中更明显的聚类。

当增加 tSNE 混淆值时,MNIST 手写数字图像簇变得更加清晰
随着数据集规模的增长,这变得越来越重要。你可能想要在大型数据集上使用 tSNE 来增加困惑值的第二个原因是一个长期存在的假设(例如参见这里的这里的和这里的这里的),即 tSNE 可以在大困惑时保留更多的全局数据结构,在这方面与 UMAP 相当。为了证明这一点,让我们模拟 2D 数据点的 3 个高斯斑点(每个 1000 个点):两个斑点彼此靠近并且远离第三个斑点。

现在我们将尝试使用不同的困惑值来重建运行 tSNE 的原始数据:10、30、100 和 1000。

这里我们可以做两个重要的观察。首先,在低困惑值 10 甚至 30 时,数据结构不明显。事实上,如果我不对这些点着色,甚至很难猜测我们在困惑 10 和 30 的 tSNE 图中看到多少斑点,因为数据点似乎形成了某种程度上的哑铃簇。因此,为了解析所有三个聚类,我们需要将困惑值增加到 100。第二,注意,在困惑度等于 10、30 和 100 时,三个集群似乎是等距的,尽管我们知道它们中的两个应该位于一起并且与第三个分开。正确的群集(全局数据结构)的相对位置只有在我们将困惑度增加到 1000 时才能实现,即远远超过 5 到 50 之间的推荐值。因此,大的困惑极限对于从 scRNAseq 数据的 tSNE 图中得到的细胞群体的接近度的生物学解释变得至关重要。
tSNE 类似于五氯苯甲醚,令人困惑
现在,在我们认识到大困惑极限对于 tSNE 的重要性之后,让我们在我在上一篇中介绍的世界地图数据集(3023 点)的2DT2【线性流形】T3 上评估 tSNE 的大困惑。生成世界地图数据集的完整代码可以在我的 github 上找到。

用 tSNE 和大困惑值重建的 2D 世界地图数据集
在这里,我们可以观察到 tSNE 错误地将南美洲放置在困惑 500 和 1000 处的非洲和北美洲之间。然而,当困惑度达到 2000 时,我们得到了原始世界地图数据集的几乎完美的重建,其中各大洲之间的所有相对距离似乎都保留得很好。这似乎令人放心,并表明如果想用 tSNE 在数据中保留更多的全局结构,只需增加困惑值就足够了。然而,如果我们将原始的线性 2D 世界地图数据集投影到非线性流形(如球体)上,就可以清楚地看到捕捉,以这种方式,我们模拟了具有映射到球体表面的大陆的 3D 球体。让我们在非线性 3D globe /sphere 数据集上运行具有大困惑值的 tSNE,并将其输出与 PCA 和 UMAP 进行比较。此外,我们还会将 2D 数据集投影到其他著名的非线性流形上,如 S 形曲面和 瑞士卷 上,并运行 PCA 和 tSNE / UMAP 大困惑/ n_neighbor。注意,tSNE 和 UMAP 都用 PCA 进行了相同的初始化。

PCA,tSNE / UMAP 逍遥法外困惑/世界地图上的 n_neighbor 投影到球面,S 形和瑞士卷
这里我们观察到 PCA 和 tSNE 图之间有显著的相似性。发生了什么事?由于 3D 非线性流形(球体、S 形和瑞士卷)的内部维数仍然是两个,人们会期望适当的降维方法可以恢复原始的 2D 世界地图数据。PCA 未能实现的原因是 PCA 通过仿射变换(如旋转、翻转、平移、拉伸等)来搜索数据中变化最大的轴。然而,通过旋转非线性流形,人们将找不到恰当地捕捉到感兴趣的变化的轴,即世界地图中各大洲的位置。相反,主成分分析揭示的螺旋、S 曲线和圆确实捕捉到了通过线性变换可以检测到的大部分变化。PCA 在非线性流形上失败是可以理解的,但是为什么 tSNE 没有表现得更好呢?这对于嵌入到非线性 3D 流形中的世界地图来说不是唯一的,但是甚至可以在表现良好的基准 MNIST 数据集上检测到,即对于等于 500 和 3000 的大困惑值的 tSNE 图类似于 PCA 图。

在非常大的 tSNE 困惑值下,MNIST 手写数字的簇变得不太明显
这是怎么回事?难道 tSNE at launch performance 不应该用正确保留的全局结构来重建原始数据吗,就像本节开始时显示的 2D 线性世界地图 performance = 2000 那样?为了回答这个问题,我们需要研究 tSNE 算法,并认识到由于梯度下降中的梯度消失,tSNE 退化为 PCA。
检查 tSNE 梯度的大困惑
为了理解 tSNE 在自由困惑时会发生什么,我们需要回忆一下,tSNE 是基于以下四个等式的,参见我的帖子以了解更多关于 tSNE 的优点和缺点的信息:

并检查一些重要的概念,如数据的成对欧几里得距离的分布、西格玛值的分布(等式中高斯核的带宽。1),高维概率的分布以在一定距离上找到数据点(等式。1)、低维概率的分布(等式。3),最后是 KL 散度梯度值的分布(等式。4).可悲的是,如果不破解代码,所有这些指标都不容易从 tSNE 的当前实现中提取(例如 scikitlearn 或 Rtsne ) 。特别是,我们需要检查 KL-散度的梯度是否在大混乱时消失,因为这将意味着 tSNE 内的梯度下降在用例如 PCA 初始化后从未正确开始。

原则上,查看 scikitlearn 的 tSNE 代码,我们可以找到函数 _joint_probabilities 和 _kl_divergence ,它们可以提供以下信息:

这里我们可以看到,随着困惑参数的增长,KL 梯度下降到几乎为零。对于未来更全面的信息,从头开始实施 tSNE是有意义的。下面的代码以数字形式再现了 scikitlearn tSNE 实现的输出,但是更加简洁,希望更容易理解。


从头开始编程 tSNE 的优点是,我们可以提取我们想要的任何信息,例如在不同的困惑值下 KL 梯度的演变。我们可以立即看到,KL-gradient very 很快将下降到零,因此嵌入的 的坐标停止更新,并且我们最终只有初始化时的坐标。

因此,如果我们用 PCA 初始化 tSNE 并增加困惑,我们就有风险以 PCA 图而不是 tSNE 结束。注意,为简单起见,我使用术语 PCA,尽管更正确的说法是 tSNE 退化到多维标度(MDS) 图。PCA 和 MDS 在很多方面是相似的,我指的是 PCA(不是 MDS)作为一种更流行和已知的技术。
但是为什么 KL 梯度在大混乱中消失了呢?仔细观察会发现 P 和 Q 之间有趣的相互作用,即在特定距离观察数据点的高维和低维概率。两个概率在大混乱时变得可比较,即 P/Q 接近 1,并且从等式 4 得出,KL 梯度消失并且 tSNE 恶化。

在草率的符号中,回顾 tSNE 有一个重要的超参数,早期的夸张,它是高维概率 P 的倍增因子,可以将 tSNE 恶化条件写为:

其中α是早期夸张,N 是样本大小,σ与困惑度相关,X 和 Y 是高维度和低维度中数据点之间的平均欧几里德距离。因此,上面的等式连接了 tSNE 的最重要的超参数,除了当 P = Q 条件在很大的困惑下被满足时无关紧要的学习速率。
摘要
在这篇文章中,我们了解到需要在大型数据集上使用 tSNE 来增加困惑值,以便获得更多不同的聚类并在数据中保留更多的全局结构,这对于不同聚类之间距离的生物学解释变得非常重要。然而,当 KL 梯度消失时,增加的复杂度可能导致非线性 tSNE 的退化到线性 PCA / MDS 维数减少,因此算法永远不会正确开始。如果对线性数据运行 tSNE ,这有时会被错误地解释为是“原始数据的完美重建”,但是当对非线性数据运行 tSNE 时,这显然是错误的。
在下面的评论中,让我知道哪些来自生命科学的分析技术对你来说似乎是特别神秘的,我会在以后的文章中介绍。在我的 Github 上查看帖子中的代码。在 Medium 关注我,在 Twitter @NikolayOskolkov 关注我,在 Linkedin 关注我。下一次我将转向进化生物学,并解释如何利用古代 DNA 来估算种群数量,敬请关注。
tSNE 简化版
使用 tSNE 减少尺寸
伙计们,说实话当我听到 tSNE 这个名字和完整形式是 t 分布随机邻域嵌入时,我很害怕。渐渐地,我可以在报纸、各种博客中找到自己的路,并意识到它并不像名字所暗示的那样不祥。我只是想分享一下我收集的直觉。让我把它写在一个问题里,就像我想到的那样回答。
什么是降维?
属性数量越少,管理就越简单。模型易于训练、存储,也易于可视化。从技术上来说,你可以消除一些原有的功能或属性。这被称为特征选择。还有另一类技术,其中从原始特征集创建新特征。举个典型的例子,有一群顾客,他们的身高和体重被收集起来。如果我们想要创建一个能够代表这两种属性的特征,我们可以使用身体质量指数(身体质量指数),它是以千克为单位的体重和以平方米为单位的身高来计算的。这就是降维的简单动机。
从技术角度来看,我们构建新特征的第二套方法被称为降维技术。
这可以任意实现吗?还是总能给出有意义的真实世界特征?
当然不是任意的。像任何最大似然算法一样,必须有一个目标函数。让我们将原始数据集称为 D,将具有缩减的或新的特征集的数据集称为 r。让有一个函数‘f’来度量包含在任何数据集中的信息。损失函数/目标函数会在一个抽象的层次上,f(D) — f(R),我们姑且称之为 J(D,R)。我们应该尝试最小化这个 j。如果有几种可能的减少,即 R1,R2,Rn,我们应该选择那个版本的新构建的功能最小化 j
大多数时候,没有像身体质量指数那样的真实世界即领域含义可以附加到新构造的特征上,这是这些方法的弱点。
为什么需要降维?
- 这样我们就可以形象化(为此我们需要减少到二维或三维)并且它是可解释的
- 模型轻便高效,训练和应用速度更快
现在什么是 tSNE?
tSNE 是 t-分布随机邻域嵌入
让我们从随机邻域嵌入开始。嗯,一个城市阿格拉可以用它的‘纬度、经度’来表示,或者,它可以用它离新德里的距离来表示。
这类似于单词嵌入的概念,一个单词由周围的单词表示。一个载体被它所保持的公司所知。

图 1:邻居代表(来源:作者)
在上图中,有 9 个点,有两个属性 x1 和 x2。用绿色标记的点可以用 x1 和 x2 这两个值来表示。
或者,它可以用它与 k 个最近邻(k = 4)的距离或相似性来表示,这些最近邻用黑色标记。相似度与距离成反比。为了标准化,我们可以用 0 到 1 之间的数字来衡量这些相似性。
如果我们以稍微不同的方式思考,我们可以认为这是选择邻居的概率,假设已经选择了绿点。让我们用下面的简单例子来理解这一点。

图 2:点的邻域(来源:作者)
直觉很简单,
- 邻居越近,概率越高
- 所有四个邻居的概率之和是 1。
任何时候,当我们处理概率时,我们都在处理随机性,在更正式的术语中,随机性被称为随机的。
所以不用坐标来表示绿点,我们可以表示为(0.91,0.045,0.045,0.01)。这被称为随机邻域嵌入。
这还没做到,你的问题当然会是,维度是怎么降的?现在让我们把原始空间中的概率分布称为 p,让我们假设原始向量没有两个特征,而是总共有 50 个特征。
让我们进一步假设,通过一些魔法,我们已经将这些向量转换成二维向量,然后我们再次通过它的随机邻域嵌入来表示这些点,这只不过是一个概率分布 q。当我们计算概率时,假设像正态分布这样的标准概率密度函数。
我们来重述一下,所以同一个点用原始维度上一个叫做 p 的概率分布和另一个缩减空间上的概率分布 q 来表示。不需要太多思考,我们就可以得出结论,p 和 q 应该尽可能相似。
我们如何度量两个概率分布之间的相似性?我们通过使用 KL 散度的来实现。

等式 1:单点的 KL 散度

等式 2:所有点的 KL 散度
我们所说的 J 就是上面的等式。
KL 发散的直觉
假设原始分布如下所示

图 3:嵌入原始特征空间(p)(图片来源:作者)
现在让我们使用 KL 散度的概念,它是 p 和 q 之比的函数。让我们在约化空间中评估以下两组分布。

图 4:两个目标概率分布 q1 和 q2
从视觉上,我们可以看到第二个更类似于图 3 中的原始分布,当我们计算每个点的 p/q 并求和时,我们确实看到第二个备选方案的值更小(4.08 而不是 5)。
t 分布呢?
同样,我们不会在这里处理数学,我们的想法是当我们嵌入或转换到较低的维度时,我们试图保持邻域(通常称为局部结构)。在第一篇论文中,称为随机邻域嵌入,这种邻域保持足够好。在改进中,作者希望原始空间中的远点在缩减空间中进一步移动。
比方说,我们知道某个特定邻居的概率是 0.05,使用标准正态表,我们可以说它离平均值(参考点)的距离是 1.65。如果我们使用 t 分布(自由度为 20)转换 0.05 的相同概率,则为 1.725。底线是,对于相同的概率,t 分布将较低维度中的邻居推离参考点更远。
这就是 t 分布随机邻域嵌入的故事。
tSNE 的参数是什么?
最简单的参数是在低维空间中我们需要多少特征。这通常是两个。
另一个重要的参数是困惑度,它决定了当我们进行随机嵌入时,需要考虑多少个邻居。如果我们保持一个高的困惑值,它会考虑所有的点(全局),如果我们保持一个小的值,它会考虑几个邻居(局部)。同心圆的例子说明了这一点。

图 5:原始特征空间散点图中的同心圆(来源:作者的笔记本)

图 tSNE 空间的散点图,同心圆的不同困惑度(来源:作者笔记本)
图 5 是原始数据。在这里,我们不是在减少维度,而是在说明困惑的影响
很明显,随着复杂度的降低,红点仍然彼此接近,但是全局结构丢失了。在论文中,作者建议值在 5 到 50 之间。
为什么不是 PCA?
说到降维,主成分分析的回忆最多。PCA 通过保持数据的可变性来工作。对可变性贡献最大的点是离中心最远的点。因此,PCA 更多地受到这些点的影响,这给出了数据的一种全局结构。此外,PCA 有一个线性假设,而 tSNE 没有。

图 7:MNIST 数据集的 2D 表示(作者笔记本)
我敢肯定,你知道 MNIST 数据集有 10 个类和 784 个特征。在上面的散点图中,使用 tSNE 和 PCA 构建了 2 个新特征,并且在图 7 中每个类别由不同的颜色表示。这是非常明显的,在 tSNE 减少的空间,类更好地分开。请记住,这两个都是无监督的方法,因此不使用类信息。
PCA,tSNE 对峙
- PCA 关注全局结构而非局部,tSNE 关注局部
- PCA 是线性的,tSNE 是非线性的
- PCA 直觉比 tSNE 更简单,参数更少,因此具有更广泛的适用性
- tSNE 在计算上比 PCA 更昂贵
请查看笔记本和视频了解更多详情
我们的资源来自加尔各答大学(CU)数据科学实验室(A.K.Chouhdury 学院 IT 和统计系)
动手:【https://www.youtube.com/watch?v=su6amJTXnto】T4
理论:https://youtu.be/KvbuJ0daXd4
Kaggle 笔记本:https://www.kaggle.com/saptarsi/tsne-with-python
结论:
如果原始特征很重要,就不能使用降维。PCA 可以是一个很好的起点,如果我们没有得到一个好的结果,我们可以选择 tSNE。如果在整个数据集上运行的成本很高,可以采用一些采样方法。
参考文献:
[1] Hinton GE,Roweis ST .随机邻域嵌入。神经信息处理系统进展 2003(857-864 页)。
[2] Géron A .使用 Scikit-Learn、Keras 和 TensorFlow 进行机器学习:构建智能系统的概念、工具和技术。奥莱利媒体;2019 年 9 月 5 日。
[3]https://towards data science . com/an-introduction-to-t-SNE-with-python-example-5a3a 293108 D1
[4] YouTube。(2013 年 11 月 6 日)。使用 t-SNE[视频文件]可视化数据。从 https://www.youtube.com/watch?v=RJVL80Gg3lA 取回
多伦多证交所与 UMAP:全球结构
生命科学的数理统计和机器学习
为什么保护全球结构很重要

这是来自专栏 生命科学的数理统计和机器学习 的第十五篇文章,我试图以简单的方式解释生物信息学和计算生物学中使用的一些神秘的分析技术。诸如 tSNE 和UMAP之类的降维技术对于许多类型的数据分析来说绝对是核心,然而令人惊讶的是,人们对它们到底是如何工作的却知之甚少。之前,我开始在我的文章中比较 tSNE 和 UMAP,UMAP 到底是如何工作的,如何从零开始为 UMAP 编程以及为什么 UMAP 优于 tSNE 。今天,我将分享我对 tSNE 和 UMAP 在多大程度上能够保持你的数据中的全局结构的看法,以及为什么它很重要。我将尝试使用真实世界的 scRNAseq 数据以及已知基本事实的合成数据,展示 UMAP 更好地保存全球结构的 T28 数学原因。我将特别提出大困惑/ n_neighbors 的限制,其中两种算法大概都可以保留全局结构信息。
UMAP 组件上的聚类
如果你只把 tSNE 和 UMAP 用于高维数据的可视化,你可能从来没有想过它们能保留多少全局结构。事实上,tSNE 和 UMAP 都被设计为主要保留局部结构,即将相邻的数据点组合在一起,这确实为数据中的异质性提供了非常丰富的可视化信息。数据可视化的质量,即保留局部结构,在 tSNE 和 UMAP 之间是可比的,前提是您正确地 调整了它们的超参数 。

tSNE 和 UMAP 同样很好地可视化细胞群体(保留局部结构),图像来源
然而,如果您想进行下一步并了解数据点聚类之间的相关性,这可能会很麻烦,因为 tSNE 和 UMAP 作为邻居图算法不能保证聚类间的距离被正确保留。评估聚类之间的相关性实质上意味着在聚类之间建立层次和边界,以便查看聚类的起点和终点。因此,我们到达了集群问题。由于维数灾难和选择适当的距离度量,在原始数据上运行聚类不是一个好主意。取而代之的是在降维上的聚类 (使用 PCA、tSNE 或 UMAP) 可以更健壮。降低聚类的维度正是你开始看到 tSNE 和 UMAP 之间差异的地方。
为了证明这一点,我使用来自bjrklund 等人的 scRNAseq 数据,并在 1)原始表达数据、2)显著的 30 个 PCA 主成分(PCs)、3) tSNE 2D 投影和 4) 30 个 UMAP 成分上比较 K 均值聚类。

如我们所见,由于 scRNAseq 数据的维数灾难和非线性,当在原始表达数据(~15 000 个基因)上进行聚类时,K-means 与降维图像不一致,并且 30 个 PC 显然也是 K-means 难以成功的高维空间。相反,仅在 2 个 tSNE 分量和 30 个 UMAP 分量上的聚类导致聚类和 tSNE 降维图像之间几乎完美的一致。tSNE 上的聚类应该与 tSNE 图一致,这并不奇怪,但是 2D tSNE 表示可能没有捕捉到数据中的大量变化,因此 tSNE 上的聚类被广泛认为是一个糟糕的想法。然而,由于算法的限制,增加 tSNE 组件的数量几乎是不可能的。相比之下,UMAP 提供 2 个以上的分量,例如,在我们的例子中是 30 个,与 30 个 PCA 分量相比,它可以捕获数据中更多的变化。总之,虽然使用 tSNE 的 2D 降维过于极端,但使用 PCA 的 30D 降维可能无法捕捉到数据中的足够多的变化,因此在数量为 的 UMAP 组件上的聚类在极端降维的聚类(例如 2D tSNE)和使用 PCA 的非最优降维(过于线性或仍然过于高维)的聚类之间给出了一个很好的折衷。
30 个 UMAP 分量比 30 个 PCA 分量在数据中保留更多的变化,因此更适于聚类
量化全局结构保存
在上一节中,我解释了 UMAP 组件上的集群如何比 tSNE 或 PCA 组件上的集群更有益。然而,如果我们决定在 UMAP 组件上进行聚类,我们需要确保 UMAP 正确地保留了聚类内(局部结构)和聚类之间(全局结构)的数据点之间的距离,因此我们将花费一些时间来尝试理解如何通过 PCA、tSNE 和 UMAP 来观察和量化全局结构保留。通过使用真实世界的 scRNAseq 数据集的算法来量化全局结构保留可能不太容易,因为一般来说基本事实是未知的。这就是具有已知原始结构的合成数据 非常方便的地方,这里我们将使用具有 5 个集群/大陆(不包括南极洲)的点的 2D 世界地图集合,有关如何从头构建数据集的详细信息,请参见我的 github 上的完整笔记本。

合成 2D 数据集(世界地图),包含 5 个集群/大洲
由于我们对大陆之间的距离以及它们的 T2 形状有一些感觉,这就是我们可以直观地称之为“全球结构”,我们可以通过运行 PCA、tSNE 和 UMAP 降维算法来尝试重建原始数据。由于原始数据集是线性/平面流形,我们可以预期线性降维比非线性降维做得更好。

用 PCA、tSNE(困惑= 500)和 UMAP (n_neighbor = 500)重建世界地图
不出所料, PCA 能够完美地重建原始数据集,除了翻转极点,这很好,因为 PCA 对数据的仿射变换是不变的,如翻转、旋转、平移等。tSNE 和 UMAP 的表现比 PCA 差,但方式不同,尽管我对它们使用了相同的 PCA 初始化。我们可以立即观察到,tSNE 将南美洲置于非洲和北美洲之间,而当 UMAP 重建时,它或多或少地保持了原来的距离。为了量化所有点对之间的原始距离保持得有多好,我们可以计算原始和重建的成对欧几里德距离之间的 斯皮尔曼相关系数 。

通过降维算法量化成对距离保持
执行 Mann-Whitney U 检验,我们可以得出结论 UMAP 比 tSNE 更好地保持了成对欧几里德距离(p 值= 0.001)。箱线图中的置信区间是通过自举程序建立的,详见我的 Github 上的代码。现在让我们计算原始的和重建的大陆间距离之间的 Spearman 相关性。测量聚类的质心之间的距离,即忽略数据中的变化,可能不是一个很好的主意,因为聚类被拉长了。因此,一个更好的想法是计算所有聚类对之间的马氏距离。 Mahalanobis 距离首先计算一个聚类的每个点到第二个聚类的质心之间的距离,并通过第二个聚类中变化的“厚度”(假设该聚类具有椭球对称性)来归一化这些距离。

世界地图中各大洲/星团之间成对马氏距离的保持
在这里,我们观察到,虽然对于南美、澳洲和非洲不清楚,至少对于北美和欧亚大陆,UMAP 比 tSNE 更好地保留了这些集群和其他集群之间的原始 Mahalanobis 距离。对所有聚类进行平均(上图中的最后一个图)并执行 Mann-Whitney U 检验,我们证明了 UMAP 确实更好地保留了大陆/聚类之间的原始马氏距离 (p 值= 0.034)。最后,让我们将聚类的形状的保持作为数据的全局结构的另一种度量。为此,我们将使用聚类周围边界框的大小。

环绕世界地图各大洲的边界框作为聚类形状的度量
再次计算原始大陆和重建大陆周围的边界框大小(高度和宽度)之间的 Spearman 相关系数,我们可以观察到,尽管有很多变化,UMAP 明显比 tSNE(Mann-Whitney U 检验 p 值= 0.021)更好地保留了集群的形状,见下图。

世界地图各大陆原始形状和重建形状之间的相关性
这里我特意省略了自举过程,以表明 tSNE 著名的随机性仅仅来源于默认随机初始化的 。用 PCA 或图拉普拉斯算子初始化,tSNE 成为一个确定性方法。相比之下, UMAP 由于通过随机梯度下降(SGD) 对其代价函数(交叉熵)进行优化,即使用 PCA 或图拉普拉斯算子进行非随机初始化也能保持其随机性。绝对清楚的是,上面所有的 tSNE 与 UMAP 的定量比较都是用相同的 PCA 初始化对两种方法进行的,因此初始化不能成为解释它们在全局结构保留方面差异的因素。****
初始化与成本函数
为 tSNE 和 UMAP 指定相同的 PCA 初始化,我们避免了文献中关于仅由不同初始化场景驱动的 tSNE 与 UMAP 的比较的混淆。记住,两种算法都利用梯度下降来计算最佳嵌入。根据定义,梯度下降规则从一些随机或非随机(PCA)初始化开始,并通过计算成本函数的梯度来优化成本函数。

梯度下降规则:最终嵌入依赖于初始化和成本函数
假设 tSNE 和 UMAP 都已经用 PCA 进行了非随机初始化,我们观察和量化的输出中的所有差异都源于内在的算法特性,例如成本函数。我在这里和展示了 UMAP 对成本函数的更明智选择促进了更好的全局结构保存。
非线性流形的 tSNE 与 UMAP
以前,我们在线性平面(世界地图)上使用合成的 2D 数据点集合。现在让我们将 2D 数据点嵌入到 3D 非线性流形中。例如,这可以是球体/地球仪,然而,事实证明,这种嵌入不是无足轻重的,因为它导致数据的严重 失真。因此,我们将把世界地图嵌入到经典的瑞士卷非线性流形中,它代表了 3D 空间中的一种阿基米德螺旋。我们将再次运行 PCA,tSNE 和 UMAP 降维,试图重建原始的世界地图。请注意,数据的固有维度仍然是 2D,因此良好的降维应该展开瑞士卷。

用 PCA、tSNE(困惑=50)和 UMAP (n_neighbor=50)重建投影在三维瑞士卷上的世界地图
我们首先看到的是 PCA 未能解开瑞士卷,即重建原始的 2D 世界地图。这是因为 PCA 作为线性算法为所有成对距离分配相等的权重,而相比之下,非线性流形学习器如 tSNE 和 UMAP 优先考虑邻居之间的距离,这种策略允许他们计算出数据的内在 2D 维度。

通过非线性流形上的 PCA、tSNE 和 UMAP 保持成对距离,图像源
此外,查看 tSNE 世界地图重建,我们可以立即注意到,如果一个人沿着从南美洲到非洲的路线,他会经过欧亚大陆,因为某种原因 tSNE 将它放置在南美洲和非洲之间。相比之下, UMAP 正确地将非洲置于南美洲和欧亚大陆之间。
有一个 假设tSNE 和 UMAP 能够在非常大的困惑/ n 邻居超参数值下重建原始数据。让我们用嵌入到 3D 中的世界地图来检验它是否正确。

tSNE 在瑞士卷上投影世界地图的大困惑极限
这里我们观察到一个有趣的效应。在较大的困惑值下,tSNE 的数据重建质量没有提高而是下降。困惑= 2000 的 tSNE 可视化没有提醒你什么吗?正确,这看起来像上面的 PCA 重建。增加学习率和迭代次数并不能解决问题,欢迎大家来查。但是发生了什么?显然,tSNE 在大的困惑值下存在收敛问题,查看轴。仔细观察 tSNE 在大困惑值时的梯度,发现非常接近 0 。

tSNE 梯度在大的困惑值处消失
根据上面的梯度下降规则,当成本函数的梯度接近零时, tSNE 不更新初始 PCA 初始化。因此它看起来像
如果您用 PCA 初始化 tSNE 并增加困惑值,您将面临最终只使用 PCA 的风险
在下一节中,我们将展示这个结论是如何从 tSNE 的梯度方程中得出的。到目前为止,让我们看看UMAP 在 n_neighbor 的表现,这是 UMAP 对 tSNE 困惑的模拟。

瑞士卷上世界地图投影的 UMAP 大近邻限制
这非常有趣, UMAP 似乎对 n_neighbor 超参数不太敏感。在下一节中,我们将再次理解这种行为背后的数学原理。
全局结构保存背后的数学
假设 tSNE 和 UMAP 都用 PCA 相同地初始化,UMAP 保留更多全局结构的一个原因是成本函数 的 更好选择。然而,在这里,我将试图从不同的角度来看 UMAP 的更好的全球结构保存,暂时忽略 tSNE 和 UMAP 之间的成本函数的差异。tSNE 和 UMAP 都定义了高维度的概率,以观察某个距离处的点,这些点属于以下指数族:

这里 σ 是一个参数,负责细胞/样品能够在多大程度上相互“感觉”。由于 σ 是一个有限值,即不趋向于无穷大,每个数据点只能“感觉”到其最近邻居的存在,而不能感觉到远点的存在,因此 tSNE 和 UMAP 都是邻居图算法,因此保留了数据的局部结构。然而,在极限 σ →∞中,每个点都有机会“记住”其他每个点,因此在这个极限中,理论上 tSNE 和 UMAP 都可以保留全局结构。然而,不是 σ 本身才是 tSNE 和 UMAP 的实际超参数,而是分别为困惑度和最近邻数 n_neigbor 。让我们检查什么样的困惑和 n_neighbors 值导致极限 σ →∞。为此,我们将采用真实世界的 scRNAseq 数据集,即癌症相关成纤维细胞(CAFs) 基因表达,并计算均值 σ 如何依赖于困惑和 n_neighbor 超参数。

对于 tSNE / UMAP,作为困惑度/ n_neighbor 的函数的平均 sigma 的行为
事实证明,当增加 n_neighbor 超参数时, UMAP 的均值 sigma 非常快地达到平稳状态,而 tSNE 似乎对困惑更敏感,因为 tSNE 的均值 sigma 在接近数据集大小的困惑处双曲线发散。请查看我的 github 上的笔记本了解计算的细节。请注意,UMAP 平均西格玛(平台水平)的绝对值可以由两个附加的 UMAP 超参数调节:带宽和 local_connectivity ,然而,相关性平均西格玛对 n_neighbor 将具有相同的缓慢对数增长。因此,我们证实了我们之前的经验观察,即 tSNE 对困惑比 UMAP 对 n_neighbor 更敏感。tSNE 的均值 sigma 在大范围内的近似双曲线散度对 tSNE 成本函数(KL-散度)的梯度有的显著影响。在极限 σ →∞中,上述方程中的高维概率变为 1,这导致 KL-散度梯度的退化。

这就是为什么对于大困惑,我们有一个 tSNE 嵌入,看起来类似于瑞士卷的 PCA 图像。因此,当在大的困惑极限中使用 tSNE 时需要小心,并且监控 KL-散度的梯度,以便 tSNE 不会退化到类似 PCA / MDS 的线性。最后,请注意,合成世界地图 2D 和 3D 数据集的相关性均值 sigma 与困惑度/ n_neighbor 看起来非常类似于上面为 CAFs scRNAseq 数据计算的值,可以在我的 github 上的 Jupyter 笔记本中找到。
摘要
在这篇文章中,我们已经了解到当使用 tSNE 和 UMAP 超越数据可视化时,全局结构保持是非常重要的。使用线性和非线性流形,我们从不同的角度展示了 UMAP 保存了更多的数据全局结构。这与成本函数的选择和相对于 n_neighbor UMAP 超参数的 均值 sigma 的 非常慢的对数 依赖性有关。相比之下,tSNE 在较大的困惑值下显示出平均 sigma 的近似双曲线发散,这可能会导致 tSNE 成本函数的梯度消失以及算法收敛的严重问题。
在下面的评论中让我知道生命科学中的哪些分析技术对你来说似乎特别神秘,我会在以后的文章中尽量介绍它们。在我的 Github 上查看帖子中的代码。在媒体关注我,在 Twitter @NikolayOskolkov 关注我,在 Linkedin 关注我。下一次,我将转向进化生物学,并展示如何利用古代 DNA 来估算种群数量,敬请关注。
使用 Keras 和 Talos 在 Python 中调整深度学习网络的超参数
使用 Talos 在 CNN 中网格搜索超参数,例如狗-猫 CNN 分类器

马里奥·高在 Unsplash 上拍摄的照片
随着深度学习框架的发展,许多人设计人工神经网络的架构变得更加方便和容易。Tensorflow、Keras 和 Pytorch 这 3 个最流行的框架使用得更频繁。为了改善我们的神经网络的性能,有许多方法,例如,使用数据扩充来改善数据质量。然而,数据质量是数据科学的源泉。为了获得更好的数据质量,通常需要额外的费用、时间和人力资源。因此,我们更喜欢处理模型的超参数🏄🏼。
开始吧!
1.参数或超参数
模型参数是模型内部的配置变量。它依赖于模型的训练数据。模型的参数可以通过给定数据与模型拟合来估计。
模型超参数是模型外部的配置。超参数是为了帮助我们找到不依赖于训练数据的模型参数。
相反,我们通过适当设置一系列超参数来寻找模型的参数,优化了训练过程,充分利用了数据。这意味着我们可以手动给定超参数值,并且它们不能在训练期间更新。但是参数是模型的本能,在训练过程中不断更新。
一个不恰当的比喻,如果我们把一个学生看作一个模型,他的知识、性格、技能更像是模型的参数。我们训练他获得这些能力和特征的方式可以被视为超参数。
由于超参数是模型参数的关键,我们应该对它们给予足够的重视。如何选择模型的超参数?处理这个问题需要足够的知识和耐心。
2.调整超参数的策略
通常有 5 种不同的优化技术:
- 手动搜索:我们根据自己的判断/经验选择一些模型超参数。然后,我们训练该模型,评估其准确性,并再次开始该过程。重复这一循环,直到达到令人满意的精确度。
- 网格搜索:超参数网格,在训练算法的超参数空间的给定子集上,在每个可能的组合上训练/测试我们的模型。这是超参数优化的传统方法。
- 随机搜索:通过随机选择覆盖所有组合的全部选择。因此,它可以通过选择这些超参数的一些随机组合来减少搜索迭代的次数。
- 贝叶斯优化:是一种针对黑盒功能全局优化的顺序设计策略。它通过牢记过去的结果来选择输入值,从而减少了搜索迭代的次数。
- 进化算法:用一些预定义的超参数创建 N 个机器学习模型的群体。它生成一些子代,这些子代具有与最佳模型相似的超参数,从而再次得到 N 个模型的群体。通过使用类似于生物进化的机制顺序地选择、组合和改变参数,只有最好的模型会在过程结束时存活下来。它模拟了自然选择的过程,这意味着那些能够适应环境变化的物种能够生存和繁殖,并延续到下一代。
3.我们自己的方法:网格搜索
在我们的工作中,我们经常使用网格搜索。网格搜索受到高维空间的影响,但通常可以很容易地并行化,因为算法使用的超参数值通常是相互独立的。此外,我们在 Colab 平台上编写代码,这允许我们在您的浏览器中编写和执行 Python:
- 不需要配置
- 免费访问 GPU
- 轻松分享
4.喀拉斯和塔罗斯
如果你想用最少的代码快速构建和测试一个神经网络,那么 Keras 就是你需要的。Keras 是一个用 Python 编写的开源神经网络库,是一个为人类而不是机器设计的 API。由于 Tensorflow 2 提供了 Keras 和直观的高级 API tf 的紧密集成。keras,有 2 种方式使用 Keras,要么直接导入 Keras,要么从 tf 导入 Keras。
Talos 于 2018 年 5 月 11 日发布,此后进行了七次升级。当在 scan-command 中使用 Talos 运行代码时,所有可能的组合都会在实验中进行测试。
重要提示: Talos 通过完全自动化超参数调整和模型评估,彻底改变了普通的 Keras 工作流程。Talos 完全公开了 Keras 功能,不需要学习新的语法或模板。
我们可以用一行命令行安装 talos:
pip install talos
5.CNN 的狗与猫分类器
为了使我们的结果可见和直观,我们用一个简单的例子来分类图像是包含一只狗还是一只猫,这是计算机视觉中的一个古老问题😆。我从 Kaggle 下载了图像数据集。从链接下载数据集后,它是 ZIP 文件格式。


Colab 中解压缩的数据集
from google.colab import drivedrive.mount('/content/gdrive/')!mkdir -p dataset!unzip /content/gdrive/My\ Drive/Colab\ Notebooks/blogs_medium/cat_dog.zip -d dataset/
我们可以用几行代码直接在 Google Colab 中解压文件。
这里我们使用 LeNet-5,这是一个 22 岁的神经网络,通常作为教学样本。

现在我们开始用 Keras 构建 LeNet-5 的代码。要在 Keras 中获得可重复的结果,设置随机种子是必要的。
import osimport tensorflow as tfimport numpy as npimport random as python_randomnp.random.seed(42)python_random.seed(42)tf.random.set_random_seed(42)
然后我们可以专注于图像数据。我们需要用keras . preparation . image将它们读入训练和验证数组,稍后流入 CNN 进行训练和验证。所有图片必须有统一的大小,例如(100,100,3)。虽然数据集中的狗或猫的图像大小不同,有些大,有些小,但我们可以通过调整大小使它们大小相等。
import kerasimport globimport osfrom keras.preprocessing.image import ImageDataGenerator,load_img,img_to_array, array_to_imgfrom keras.layers import Dense, Conv2D, MaxPool2D, Flatten,Dropoutfrom keras import optimizersfrom keras.models import Sequentialimport numpy as npimage_size=(100,100)train_cats = glob.glob('dataset/training_set/training_set/cats/*.jpg')train_dogs = glob.glob('dataset/training_set/training_set/dogs/*.jpg')train_files = [fn for fn in train_cats]+[fn for fn in train_dogs]print(len(train_files))train_imgs = [img_to_array(load_img(img, target_size=image_size)) for img in train_files]train_imgs = np.array(train_imgs)print(train_imgs.shape)train_labels= [0 for i in range(len(train_cats))]+[1 for i in range(len(train_dogs))]val_cats = glob.glob('dataset/test_set/test_set/cats/*.jpg')val_dogs = glob.glob('dataset/test_set/test_set/dogs/*.jpg')val_files = [fn for fn in val_cats]+[fn for fn in val_dogs]val_imgs = [img_to_array(load_img(img, target_size=image_size)) for img in val_files]val_imgs = np.array(val_imgs)print(val_imgs.shape)val_labels= [0 for i in range(len(val_cats))]+[1 for i in range(len(val_dogs))]
在上面的代码中,所有的“狗”和“猫”都在数组中,要么是训练集,要么是验证集。此外,我们用数字 1 标记狗,用数字 0 标记猫。
接下来,我们使用一次热编码对分类整数特征 0 和 1 进行编码。
num_classes = 2epochs = 10input_shape = (100,100,3)# encode text category labelsfrom sklearn.preprocessing import OneHotEncoder, LabelEncodertrain_labels_array = np.array(train_labels)le = LabelEncoder()train_integer_encoded = le.fit_transform(train_labels_array)ohe = OneHotEncoder(sparse=False)train_integer_encoded = train_integer_encoded.reshape(len(train_integer_encoded), 1)train_labels_ohe = ohe.fit_transform(train_integer_encoded)validation_labels_array = np.array(val_labels)validation_integer_encoded = le.fit_transform(validation_labels_array)ohe = OneHotEncoder(sparse=False)validation_integer_encoded = validation_integer_encoded.reshape(len(validation_integer_encoded), 1)validation_labels_ohe = ohe.fit_transform(validation_integer_encoded)
数据必须标准化,以便模型可以更快地收敛。
train_imgs_scaled = train_imgs.astype('float32')validation_imgs_scaled = val_imgs.astype('float32')train_imgs_scaled /= 255validation_imgs_scaled /= 255
然后建立一个模型结构
from keras import layersfrom keras.layers import Conv2D, MaxPooling2D, Flatten, Dense, Dropoutfrom keras.models import Modelfrom keras import optimizersdef lenet_5(in_shape=(100,100,3), n_classes=2):in_layer = layers.Input(in_shape)conv1 = layers.Conv2D(filters=20, kernel_size=5,padding='same', activation='relu')(in_layer)pool1 = layers.MaxPool2D()(conv1)conv2 = layers.Conv2D(filters=50, kernel_size=5,padding='same', activation='relu')(pool1)pool2 = layers.MaxPool2D()(conv2)flatten = layers.Flatten()(pool2)dense1 = layers.Dense(500, activation='relu',kernel_initializer='glorot_uniform')(flatten)preds = layers.Dense(2, activation='softmax',kernel_initializer='glorot_uniform')(dense1)opt = keras.optimizers.Adam(lr=0.001, beta_1=0.9, beta_2=0.999, amsgrad=False)model = Model(in_layer, preds)model.compile(loss="categorical_crossentropy", optimizer=opt, metrics=["accuracy"])return modelif __name__ == '__main__':model = lenet_5()print(model.summary())

模型摘要
在这里,我们为 10 个时期训练模型,并将 batch_size 定义为 200。
from keras.callbacks import ModelCheckpointcheckpoint = ModelCheckpoint("lenet.h5",monitor='val_acc',verbose=1,save_best_only=True, save_weights_only= False, mode ='auto',period=1)history = model.fit(x=train_imgs_scaled, y=train_labels_ohe, validation_data=(validation_imgs_scaled, validation_labels_ohe), batch_size=200, epochs=10, callbacks=[checkpoint], shuffle=True)
经过长时间的等待,我们可以得到一个训练/验证图。

10 个时期后,val_acc 为 0.7207,val_loss 为 0.5841

模型 acc 和 loss
上图显示,经过 5 个时期后,模型改善不多。但它并没有过度适应。因此,我们仍然可以使用获得的模型。
我们想做更多的努力来为我们的狗-猫分类器训练一个更好的 LeNet-5 模型,所以我们专注于模型的超参数来改进模型🎑。
6.LetNet-5 中的 Talos,带代码
这里我们定义了一个新的函数,它具有与 LeNet-5 相同的结构,但是模型中的一些超参数是可变的。我们将这些可变超参数保存在字典“p”中。
p = {'first_hidden_layer': [500],'opt': [Adam, sgd],'dropout': [0,0.5],'weight_regulizer':[None],'lr': [1],'emb_output_dims': [None],'kernel_initializer':["glorot_uniform"]}
为了减少计算机计算和程序运行时间,在字典中我们只设置了' opt '和' dropout '变量,optimizer 有两个选项(Adam 或 SGD ), dropout 有两个可能的值。总共有 4 种组合。
from keras.optimizers import Adam,sgd
from keras.models import load_modelfrom keras.utils import CustomObjectScopefrom keras.initializers import glorot_uniformimport talosfrom talos.model.normalizers import lr_normalizerdef lenet_model(x_train, y_train,x_val, y_val, params):in_layer = layers.Input((100,100,3))conv1 = layers.Conv2D(filters=20, kernel_size=5,padding='same', activation='relu')(in_layer)pool1 = layers.MaxPool2D()(conv1)conv2 = layers.Conv2D(filters=50, kernel_size=5,padding='same', activation='relu')(pool1)pool2 = layers.MaxPool2D()(conv2)flatten = layers.Flatten()(pool2)dense1 = layers.Dense(params['first_hidden_layer'], activation='relu')(flatten)dropout1 = layers.Dropout(params['dropout'])(dense1)preds = layers.Dense(2, activation='softmax')(dropout1)model = Model(in_layer, preds)model.compile(loss="categorical_crossentropy", optimizer=params['opt'](lr=lr_normalizer(params['lr'],params['opt'])), metrics=["acc"])steps_per_epoch = int(np.ceil(train_imgs.shape[0] / 20)) - 1history = model.fit(x=train_imgs_scaled, y=train_labels_ohe, validation_data=(validation_imgs_scaled, validation_labels_ohe), batch_size=200, epochs=10, callbacks=[talos.utils.ExperimentLogCallback('kgt', params)], verbose=1)return history, modelt = talos.Scan(x=train_imgs_scaled, y=train_labels_ohe, model=lenet_model, experiment_name= 'kgt', params=p)
借助于扫描命令(talos。扫描),我们开始配置实验。它将比训练最后一个基本 LeNet-5 模型持续更长的时间。

训练实验过程中的进度条
实验报告以 csv 格式保存。文件格式。我们可以阅读 csv。文件在表中显示结果。

表中的实验报告

上图:val_acc 图。下图:val_loss 图
通过绘制 validation_accuracy(上图)和 validation_loss(下图),我们可以得出结论,第 0 次和第 3 次实验的训练模型比第 2 次和第 4 次实验的模型好得多。对比实验参数信息,我们发现 adam 模型具有更好的性能。辍学方法在培训 LeNet-5 中发挥了一点作用。
综合考虑,模型 0 的性能最好,它使用了 Adam,但没有辍学。
7.结论
在这个故事中,我们介绍了如何使用 talos 通过 Keras 构建的 CNN 来调整 a 的超参数。首先,有一些参数和超参数的基础知识,并回顾了优化超参数的常用方法。在故事的其余部分,我们构建了一个基于 LeNet-5 的猫狗分类器,并扫描了所有感兴趣的超参数组合。通过观察验证的度量,我们可以知道哪个超参数影响最大,哪个组合给出了最好的结果🏁。
代码在我的 GitHub 里😬
https://github . com/Kopfgeldjaeger/Medium _ blogs _ code/tree/master/2 _ talos _ grid _ search
8.参考
Liashchynskyi,p .,& Liashchynskyi,P. (2019)。网格搜索、随机搜索、遗传算法:NAS 的一大对比。 arXiv 预印本 arXiv:1912.06059
范里恩,J. N .,&胡特,F. (2018,7 月)。跨数据集的超参数重要性。在第 24 届 ACM SIGKDD 知识发现国际会议论文集&数据挖掘(第 2367–2376 页)。
f .哈特、j .吕克和 l .施密特-蒂梅(2015 年)。超越超参数的手动调整。 KI-Künstliche Intelligenz , 29 (4),329–337。
欢迎来到塔罗斯!您可以使用 Talos 对 Keras 模型进行超参数优化。Talos 允许您使用 Keras…
autonomio.github.io](https://autonomio.github.io/docs_talos) [## keras:Python 深度学习 API
Keras 是为人类设计的 API,不是为机器设计的。Keras 遵循减少认知负荷的最佳实践:it…
keras.io](https://keras.io/)
在 Google AI 平台上用贝叶斯优化调整模型

伊利亚·巴甫洛夫在 Unsplash 上的照片
谷歌 ML 教程
如何使用 Google 云服务为您的机器学习模型找到最佳超参数
在 Google ML 教程系列的这篇文章中,我们将讨论如何使用 AI 平台内置工具来调优你的机器学习模型的超参数!我们将使用一种叫做贝叶斯优化的方法来导航超参数空间,然后找到一个比缺省值更好的集合。
在上一篇文章中,我们在一个银行营销数据集上训练了一个RandomForestClassifier 。我们使用了算法的默认超参数,取得了很好的效果。但是,如果我想调整这个模型,试图找到一个更好的超参数集呢?例如,我想调整:
max_depth:森林中每棵树的最大深度min_samples_split:拆分树节点的最小样本数(或分数)max_features:用于每棵树的
训练的输入特征的数量(或分数)max_samples:与max_features相同,但针对行
搜索最佳超参数的最常见方法是网格搜索和随机搜索方法。
- 在网格搜索中,该算法为给定超参数的每个单一组合训练一个模型,然后返回具有最佳性能的集合。这种方法非常耗时,尤其是当您希望一次调整 2–3 个以上的超参数时,因为要训练的模型数量呈指数级增长。
- 在随机搜索中,该算法改为随机挑选超参数的 n 个组合,并为每个组合训练一个模型。这里问题出在随机字上:算法可能会跳过最有效的超参数集,尤其是当我们设置一个低的 n 时。
在本教程中,我们将在 Google AI 平台的一点帮助下使用贝叶斯优化方法!但首先,什么是贝叶斯优化?
尽管在本文中,我们将更多地关注代码部分,而不是方法背后的理论,但我将尝试给出一个快速的概述。为了更全面和完整的介绍,我建议看看这些文章( 1
和 2 )。
在某种程度上,贝叶斯优化从上述两种
方法中获益:它确实从所有可能的超参数组合中挑选了一个子样本,但是挑选是以一种更明智的方式进行的。该算法用代理函数模拟目标函数的分布(比如我们模型的平均精度);这个函数的定义域是给定的超参数空间。然后,它探索这种分布,尝试不同的超参数集。在每一次尝试中,它获得了更多关于目标函数实际分布的信息(以 Bayes 方式),因此它可以移动到更“有希望”的域空间子集。
出于这个特定的原因,请记住,我们不能完全并行化贝叶斯优化的过程(与网格和随机搜索相反),因为每次迭代都会从上一次迭代中学习。
现在我们来训练一些模型!对于本教程,我们将遵循与
培训教程相同的步骤:
- 将数据存储在谷歌存储器上
- 编写一个 Python 应用程序来训练模型
- 在人工智能平台上开展培训工作
最大的区别在于 Python 应用程序本身:我们需要添加一个框架来将模型的性能结果与贝叶斯优化联系起来。这个框架叫做 Hypertune :用pip install cloudml-hypertune简单安装即可。
更改 Python 应用程序
首先要做的是定义我们想要优化的超参数列表。
我们必须训练这样的管道
pipeline = Pipeline([
('data_prep',
ColumnTransformer([
('num_prep', StandardScaler(), num_features),
('cat_prep', OneHotEncoder(handle_unknown='ignore'),
cat_features)
])),
# ML model
('model',
RandomForestClassifier(
random_state=42,
n_estimators=500,
class_weight='balanced'
))
])
为了将这些超参数传递给应用程序(和管道),我们必须用argparse库定义一个参数列表,如下所示
import argparse…# Instantiate an argument parser
parser = argparse.ArgumentParser()# Define the list of input arguments
parser.add_argument('--max-depth', type=int, default=10,
help='Maximum depth of each tree in Random
Forest model'
' (integer, default 10)')
然后我们解析参数,并将它们输入管道
# Parse arguments
args = parser.parse_args()…pipeline = Pipeline([
('data_prep',
ColumnTransformer([
('num_prep', StandardScaler(), num_features),
('cat_prep', OneHotEncoder(handle_unknown='ignore'),
cat_features)
])),
# ML model
('model',
RandomForestClassifier(
random_state=42,
n_jobs=args.n_jobs,
n_estimators=args.n_estimators,
max_depth=args.max_depth,
max_features=args.max_features,
min_samples_split=args.min_samples_split,
class_weight='balanced',
max_samples=args.max_samples
))
])
之后,我们需要一个策略来评估每个给定超参数集的性能。我们使用交叉验证方法:
1。你把你的数据分成 n 份
2 份。选择一个分割作为验证3。连接剩余的 n-1 个分割,并在这个新的
数据集
4 上训练模型。计算保持分离
5 的性能。每次分割重复 2-4 次
如果你想稳健地评估一个模型,这种方法是合适的,因为你在 n 潜在不同的场景中训练和验证它。
我们可以使用 scikit-learn 中预置的cross_validate 函数。
from sklearn.model_selection import cross_validate…scores = cross_validate(pipeline, train, y_train,
scoring=['accuracy', 'precision', 'recall',
'f1'],
cv=5)
我们提供:
- 一个有效的分类器(我们可以使用像
GradientBoostingClassifier这样的模型,或者像我们的例子中的
整个管道) - 输入数据和目标
- 要计算的一个或多个度量(如准确度和精确度);这里是
可用指标的完整列表 - n(
cv参数)的一个值
scores结果是一个字典,其中包含每个给定指标的条目。例如,scores['test_accuracy']将是一个在 5 次迭代中具有 5 个计算精度的向量。
最后,我们必须使用hyperopt框架。由于整个优化基于单个值,我们必须选择一个特定的指标( F1-score )并计算平均值。
# Instantiate a hypertune object
hpt = hypertune.HyperTune()# Compute the average metric
avg_f1 = scores[‘test_f1’].mean()# Pass the value to hyperopt
hpt.report_hyperparameter_tuning_metric(
hyperparameter_metric_tag='F1',
metric_value=avg_f1,
global_step=1
)
这就是 Python 应用程序的全部内容!你可以在我的 Github repo 上找到整个应用程序
但是嘿!我们已经定义了要优化的超参数,但没有定义应用程序必须尝试使用贝叶斯优化的值(或值的范围)。我们如何做到这一点?
指定超参数:配置文件
我们创建一个新的配置文件,其中包含要优化的超参数列表。对于其中的每一个,我们指定:
- 类型,如分类或整数
- 如果是绝对的,它可以作为值的类别
- 如果是数字,则为要应用的数字范围和缩放类型
例如,对于最大深度超参数,我们指定:
- 整数类型
- 介于 4 和 20 之间的值
- 线性缩放,因此可能值的空间线性缩放为(0,1)
对于最小样本分割,我们可以指定:
- 浮动(双)型
- 介于 0.001 和 0.1 之间的值
- 因为我们想更多地取最左边的值而不是最右边的值,所以我们使用了一个对数标度
所有这些定义都放在一个config.yaml文件中:
trainingInput:
hyperparameters:
goal: MAXIMIZE
hyperparameterMetricTag: F1
maxTrials: 20
maxParallelTrials: 2
params:
- parameterName: max-depth
type: INTEGER
minValue: 4
maxValue: 20
scaleType: UNIT_LINEAR_SCALE
- parameterName: min-samples-split
type: DOUBLE
minValue: 0.001
maxValue: 0.1
scaleType: UNIT_LOG_SCALE
- parameterName: max-features
type: DOUBLE
minValue: 0.1
maxValue: 0.9
scaleType: UNIT_LINEAR_SCALE
- parameterName: max-samples
type: DOUBLE
minValue: 0.1
maxValue: 0.9
scaleType: UNIT_LINEAR_SCALE
让我们看看这个文件。
- 在顶部,我们确定哪个是调优作业的
goal。我们想用标签F1来MAXIMIZE一个度量。请记住,这个标签应该与 Python 应用程序中的hpt.report_hyperparameter_tuning_metric相同。 - 然后,我们定义调优工作必须进行的试验(轮次)。我们说
“总共 20 次试验,每次平行 2 次”。在时间和性能之间有一个折衷:我们指定的并行作业越多,花费的时间就越少。
但是请记住,贝叶斯过程从前面的步骤中学习:学习步骤是total trials / number of parallel trials(在我们的例子中,是 10),所以如果我们“过多地”并行化,该过程将没有多少步骤要学习。 - 最后,我们逐一列出具有上述特征的超参数。再次提醒,记住
parameterName必须与 Python 应用程序的argparse部分中定义的
相同。在这里您可以找到超参数定义的完整文档。
在 AI 平台上运行调优作业
我们将使用应用于培训的相同命令,添加config文件的规范:
gcloud ai-platform jobs submit training "$JOB_NAME" \
--module-name=src.tune \
--package-path=./src \
--job-dir=gs://bank-marketing-model/"$JOB_NAME" \
--region=$REGION \
--scale-tier=CUSTOM \
--master-machine-type=n1-standard-8 \
--python-version=3.7 \
--runtime-version=2.2 \
--config=./config.yaml \
-- \
--n-jobs=8
当你提交代码时,一个新条目将出现在你的谷歌人工智能平台作业控制台中:

作者图片
你可以弹出这个条目,查看 20 个试验中的每一个:一些将会完成,
其他的将会运行。对于每个试验,都有所选择的超参数列表和 F1 指标的最终值。
当所有的试验都完成后,如果我们点击“母亲”工作,我们会看到一个从最好到最差排序的试验结果的
摘要。

作者图片
在本例中,我们可以看到最大深度和最小样本分割超参数非常稳定,最佳性能值分别为 20 和 0.001。其他两个超参数尚未确定,但我们可以看到一个向区间的上半部分发展的趋势。
为了证明过程已经及时学习的事实,我们计算试验的次数和“到达”顺序之间的相关性。我们发现了一个
-0.71 的相关性!这意味着更高的试验编号(因此试验具有更多的
“学习”过程)具有更低的到达点。
当我们在相同的数据上使用默认的超参数训练相同的模型时,我们在训练中达到了 52.44%的 F1,在测试中达到了 51.22%的 F1。现在交叉验证+贝叶斯优化,我们达到了 64.46%!接下来的步骤可能是:
- 运行另一个优化作业,修复最大深度和最小样本分割,并关注其他超参数
- 调整其他超参数,如
criterion或min_impurity_split - 使用找到的最佳超参数集运行训练作业,以
部署模型
再次感谢您阅读这篇文章!我真的希望你能设法使用这个强大的工具来增强你的机器学习模型。请留下您的经验或反馈的评论!
调整模型的超参数,并根据业务场景瞄准特定指标
支付卡欺诈模型调整—准确性、召回率和精确度

来源:www.dqindia.com
您知道如何根据特定的产品需求和目标选择正确的误差指标吗?评估一个分类问题可能看起来有点棘手,但是由于商业决策对它的高度影响,它是极其重要的。此外,您处理模型的方式在不同的场景中可能会有所不同。
在本帖中,我们将在两个不同的场景中讨论信用卡欺诈检测分类模型的模型调整。我们将决定是选择召回还是精确作为衡量标准,并观察其影响。
值得一提的是,这篇文章将基于我为 Udacity 的机器学习工程师项目完成的一个项目,鼓励你在我的 GitHub 网页上查看。
问题:
- 让我们假设您在一家银行工作,您的任务是创建一个模型,在两种不同的情况下识别信用卡交易是欺诈还是有效
场景一:
- 您的经理要求您建立一个模型,以大约 85%的准确率检测欺诈案件。
场景二:
- 您的经理希望改善客户体验,决定用户的有效交易中最多只能有 15%被归类为欺诈交易。
总结:
- 对混淆矩阵的简单理解
- 对准确性的简单理解
- 对回忆的简单理解
- 对精度的简单理解
- 场景 1 的模型调整
- 场景 2 的模型调整
混淆矩阵:
为了理解这些指标,从而为我们的每个业务场景选择正确的指标,我们必须知道如何解释混淆矩阵及其术语。
混淆矩阵是用来衡量分类模型性能的表格。它包含具有实际分类的列和表示预测的行(反之亦然),如下所示:

来源:走向数据科学。com
其中:
- 真阳性(TP) :正确分类的阳性类别
- 假阳性(FP): 阳性分类错误
- 假阴性(FN): 阴性分类错误
- 真阴性(TN): 阴性分类正确
我想看下面的图片,你可能会对混淆矩阵有更好的理解。

准确度—召回率—精确度
给你两个场景,你会如何评价你的模型?你知道如何进行吗?为了决定我们应该追求的指标,我们必须对指标有深刻的理解。所以让我们直接用例子和场景来解释吧!

来源:向数据科学。com
精度:
准确性是一个简单的衡量标准。它基本上是正确预测与总预测的比率。你可以看到下面的公式:

资料来源:towardsdatascience.com
其中 TP =真阳性,TN =真阴性,FP =假阳性,FN =假阴性代表预测。
现在,让我们假设你的第一个模型给你 99%的准确率,比如下面的 LinearLearner 模型(一个 SaeMaker 的内置模型)。这意味着您的欺诈检测模型在标记欺诈交易和合法交易方面做得非常好,对吗?

实际上,这个问题的答案是:视情况而定!让我们更仔细地分析一下积极和消极的分类,以便更好地理解模型性能。
上述模型标记了超过 30 FN(标签不正确的欺诈性交易)和略高于 30 FP 的交易(标签不正确的有效交易)。考虑到用户的观点,我们可以声明他们不希望任何有效的交易被归类为欺诈。在这种情况下,我们必须意识到这样一个事实,即我们的模型应该有尽可能少的误报(错误标记,欺诈交易)。那样的话,我们就不能仅仅基于其准确性来调整模型以完成业务需求。为了有效地做到这一点,我们必须进行模型调整,分析一个指标,这可以帮助我们减少 FP 的数量,这与准确性无关。
回忆:

考虑到我们的欺诈检测项目,我们可以说召回率是正确标记的欺诈总数与欺诈示例总数(标记为欺诈的欺诈和标记为有效交易的欺诈)的比率。高召回率表明我们的类被正确识别(TP 数量高,FN 数量少)。
我们思考回忆的方式如下:
在阳性样本中,我的模型正确预测了多少?
精度:

来源:owardsdatascience.com t
精确度是我们必须涵盖的第三个指标,这样我们才能解决我们的业务场景问题。它基本上是正确分类的类别数除以这些类别被预测的总时间。在我们的例子中,精度是被正确分类为欺诈的欺诈交易的数量除以被正确分类为欺诈的欺诈交易的数量,加上被标记为欺诈的有效交易的数量。
我们能想到的精度是:
在我的模型标记为正面的所有预测中,有多少实际上是正面的?
管理阶层失衡
每当我们想要进行模型调优时,我们必须牢记不平衡的数据可能会使模型偏向于预测具有更多数据点的类。因此,我们必须考虑二元分类器的类不平衡训练。
在这个项目中,我使用了 LinearLearner 模型提供的一个超参数来处理不平衡的数据集。这个超参数(positive _ example _ weight _ mult)基本上就是训练一个二元分类器的时候分配给正类(1,欺诈)的权重。有效交易的权重固定为 1。
场景 1

现在让我们考虑一下我们的业务案例。根据经理的要求,我们可以说我们必须建立一个模型,这个模型有尽可能多的真阳性和尽可能少的假阴性。这意味着我们必须选择召回指标来调整我们的欺诈检测模型。

因此,为了达到特定的召回值,我使用了二元 _ 分类器 _ 模型 _ 选择 _ 标准 超参数,这是训练数据集的模型评估标准。通过使用这个度量,我们能够为我们的参数指定我们想要达到的精确值。
假设训练集的性能在测试集的性能的 5%以内,我们应该以 90%的召回率为目标。

这样,我们最终得到以下指标:

尽管我们的目标是 90%的召回率,但是我们可以看到这个模型给出了大约 91%的召回率。当应用于测试数据时,该值可能会有所不同。我们还应该看到,我们有更少的假阴性,这正是我们想要的。
场景 2:

在这种情况下,业务需求是模型只将 15%的有效交易标记为欺诈交易。这意味着我们希望尽可能少的误报(0 归类为 1)。因此,我们可以使用精度来进行模型调整。

基于这个要求,我们可以计算出我们想要的精度的近似值:85/(85+15)。同样,让我们假设与测试相比,我们在培训中的表现会高出 5%。因此,我们的目标是在测试集上达到 80–85%的精确度。

值得一提的是,该模型训练的固定精度为 90%,并试图获得尽可能高的召回率。考虑到这一点,我们可以看到作为优化模型结果的指标。

我们可以看到,该模型仍然对 26 个有效交易进行了错误分类,因此我们可能希望返回并提高我们的目标——精确度。我们还可以注意到,如果我们考虑精度的权衡,召回率和准确率都还可以。
结论:
在现实世界的项目中,产品的成功通常与调整特定的指标相关联。为了满足业务需求,了解您将选择哪种指标非常重要。我希望这篇文章能让您更好地理解如何根据业务决策和需求调整您的模型。

调整参数。以下是方法。
对你的机器学习模型有益的常见和不太常见的参数。

Gabriel Gurrola 在Unsplash【1】上的照片。
目录
- 介绍
- 因素
- 例子
- 密码
- 摘要
- 参考
介绍
参数可能令人望而生畏,令人困惑,令人不知所措。本文将概述常用机器学习算法中使用的关键参数,包括:随机森林、多项式朴素贝叶斯、逻辑回归、支持向量机和 K 近邻。还有称为超参数的特定参数,我们将在后面讨论。参数调整有利于提高模型精度,减少模型运行时间,并最终减少模型的货币支出。
因素
来自sk learn【2】和其他库的每种型号都有不同的参数;但是,这些常用算法之间有相当多的重叠。开发模型时,您将拥有默认参数。调整这些有助于提高您的整体准确性。这些参数将在分类代码中找到(我将展示 Python 中的例子),例如,假设您使用 clf 作为分类器的别名,您可以输入这些参数并在那里更改或调整它们的值,最终更改您的模型的输出。
最有效的调优方法之一是网格搜索[3]。这些参数是不同的,因为它们被认为是超参数,并且不是在估计器本身中直接学习的。在建立了上述分类器之后,您将创建一个参数网格,该网格将进行搜索以找到超参数的最大优化。请记住,这个调优过程计算量很大,如果您添加几个超参数的话,可能会非常昂贵。该模型将引用您列出的所有网格项目的组合,因此,几乎就好像您有几个模型在运行,而不是一个。
例子

照片由 Fabian Grohs 在Unsplash【4】上拍摄。
下面,我会把我在整个数据科学硕士期间所学的常用机器学习算法,以及我的职业生涯包括在内。虽然这些都是需要学习和练习的重要方法,但还有无数其他方法可以帮助你提高准确度。
这些参数不仅有助于提高准确性,而且有助于降低计算机的计算时间和工作量,因为您将降低随机森林的数量,从而降低树的最大深度,最终也将节省资金。
下面列出的是来自 sklearn 的常见机器学习算法,其中包括几个可编辑的参数。以下是所有记录的参数及其各自的机器学习算法的链接:
下面代码片段中的默认参数将在括号内——我还将给出我发现在过去有用的建议。
随机森林
**n_estimators**: number of trees in your forest (100)**max_depth**: maximum depth of your tree (None) - recommendation, change this parameter to be an actual number because this parameter could cause overfitting from learning your traning data too well**min_samples_split**: minimum samples required to split your node (2)**min_samples_leaf**: mimimum number of samples to be at your leaf node (1)**max_features**: number of features used for the best split ("auto")**boostrap**: if you want to use boostrapped samples (True)**n_jobs**: number of jobs in parallel run (None) - for using all processors, put -1**random_state**: for reproducibility in controlling randomness of samples (None)**verbose**: text output of model in process (None)**class_weight**: balancing weights of features, n_samples / (n_classes * np.bincount(y)) (None) - recommendation, use 'balanced' for labels that are unbalanced
多项式朴素贝叶斯
parameters - **alpha**: a paramter for smoothing (1.0)**class_prior**: looking at the previous class probability (None) attributes -**feature_count**: number of samples for each class or feature (number of classes, number of features)**n_features**: number of features for sample
逻辑回归
**penalty**: l1 or l2 (lasso or ridge), the normality of penalization (l2)**multi_class**: binary versus multiclass label data ('auto')
支持向量机
parameter **- decision_function_shape**: 'ovr' or 'one-versus-rest' approach
k-最近邻
parameter - **n_neighbors**: number of neighbors (5)
密码
下面是一些有用的代码,可以帮助您开始参数调整。有几个模型可以从调优中受益,业务和团队也可以从调优带来的效率中受益。下面,是模型代码,以及可用于强大优化的网格搜索代码。
调谐参数的位置。作者代码[5]。
网格搜索示例。作者代码[6]。
摘要

照片由 ThisisEngineering RAEng 在Unsplash【7】上拍摄。
现在,希望您和您的团队能够从应用当前或下一个机器学习模型的参数更改中受益。从默认参数开始,确保在开始调优之前了解每个模型的参数,以及关于这些参数定义的官方文档。虽然它们可以改善你的模型,但是参数也可以以降低你的准确性或者过度拟合你的模型的方式被调整。小心谨慎,你会发现自己拥有一个成功的、复杂的数据科学模型。
我希望你觉得这篇文章有趣且有用。谢谢大家!
如果你想了解更多关于这些特定的机器学习算法,我写了另一篇文章,你可以在这里找到[8]:
通用算法的端到端运行;包括随机森林,多项式朴素贝叶斯,逻辑回归…
towardsdatascience.com](/machine-learning-algorithms-heres-the-end-to-end-a5f2f479d1ef)
参考
[1]照片由 Gabriel Gurrola 在Unsplash(2016)上拍摄
[2] sklearn, sklearn.linear_model。后勤回归(2007 年至 2019 年)
[3] sklearn, 3.2。调整估计器的超参数(2007–2019)
[4]照片由 Fabian Grohs 在 Unsplash 上拍摄,(2018)
[5] M.Przybyla,要点—模型,(2020)
[6] M.Przybyla,要点——网格搜索,(2020)
[7]照片由this engineering RAEng在Unsplash(2020)上拍摄
[8] M.Przybyla,机器学习算法。这里是端到端的。, (2020)
加速您的空间 NLP 管道
使用自定义管道和 joblib 显著加快 spaCy 中文本预处理的技巧和诀窍

来源: Pixabay
假设您有一个大型文本数据集,您希望对其应用一些非平凡的 NLP 转换,例如去除停用词,然后对文本中的词进行词汇化(即,将它们简化为词根形式)。spaCy 是一个工业级的 NLP 库,就是为这样的任务而设计的。
在本文中,纽约时报数据集用于展示如何显著加快 spaCy NLP 管道。目标是接受一篇文章的文本,并快速返回一个词条列表以及不必要的单词,即删除的停用词。
Pandas 数据框架提供了一个方便的界面来处理这种性质的表格数据 spaCy NLP 方法可以方便地直接应用于数据框架的相关列。首先通过运行预处理笔记本 ( ./data/preprocessing.ipynb)获取新闻数据,该笔记本处理从 Kaggle 下载的 raw 文本文件,并对其进行一些基本的清理。该步骤生成一个包含表格数据的文件(存储为nytimes.tsv)。在同一目录中还提供了一个精选的停用词文件。
负载空间模型
由于在本练习中我们不会执行任何专门的任务,比如依赖关系解析和命名实体识别,所以在加载 spaCy 模型时这些组件是禁用的。
提示: spaCy 有一个
sentencizer组件,可以插入空白管道。
句子分析器管道简单地执行标记化和句子边界检测,随后可以将词条提取为标记属性。
定义了一个方法,从文本文件中读入停用词,并将其转换为 Python 中的集合(为了高效查找)。
在纽约时报数据集中阅读
NYT 新闻数据集的预处理版本作为熊猫数据帧读入。这些列被命名为date、headline和content——内容列中显示的文本将被预处理以删除停用词并生成标记词条。

定义文本清理器
由于新闻文章数据来自原始 HTML 转储,因此非常混乱,包含大量不必要的符号、社交媒体句柄、URL 和其他工件。清理它的一个简单方法是使用正则表达式,它只解析给定长度(3 到 50)之间的字母数字字符串和连字符(以便包含带连字符的单词)。这会将每个文档过滤成对 lemmatizer 有意义的文本。

选项 1:顺序处理数据帧列
现在我们只剩下了干净的字母数字标记,在继续词汇化之前,可以通过删除停用词来进一步清理这些标记。
处理该文本的直接方法是使用一个现有的方法,在本例中是下面显示的lemmatize方法,并使用pandas.Series.apply将其应用到 DataFrame 的[clean](https://prrao87.github.io/blog/images/copied_from_nb/clean)列。使用每个令牌的底层[Doc](https://spacy.io/usage/spacy-101#annotations) 表示来完成词汇化,其中包含一个lemma_属性。停用词在词汇化过程中同时被删除,因为这些步骤中的每一步都涉及到遍历相同的标记列表。
得到的词条作为一个列表存储在一个单独的列preproc中,如下所示。
%%time
df_preproc['preproc'] = df_preproc['clean'].apply(lemmatize)
df_preproc[['date', 'content', 'preproc']].head(3)CPU times: user 48.5 s, sys: 146 ms, total: 48.6 s Wall time: 48.6 s
将这种方法应用到 DataFrame 的[clean](https://prrao87.github.io/blog/images/copied_from_nb/clean)列并计时,它显示在 8,800 篇新闻文章上运行几乎需要一分钟。
选项 2:使用nlp.pipe
我们能做得更好吗?在 spaCy 文档中,指出“将文本作为流处理通常比逐个处理更有效”。这是通过调用语言管道来实现的,语言管道在内部将数据分成几批,以减少纯 Python 函数调用的数量。这意味着数据越大,nlp.pipe所能获得的性能增益就越好。
为了使用语言管道来传输文本,定义了一个新的 lemmatizer 方法,它直接作用于 spaCy Doc对象。然后,该方法被批量调用,以处理通过管道传输的Doc对象的序列,如下所示。
和以前一样,通过传递来自现有 DataFrame 的[clean](https://prrao87.github.io/blog/images/copied_from_nb/clean)列的数据来创建一个新列。注意,与上面的工作流#1 不同,我们在这里不使用apply方法——相反,数据列(一个 iterable)作为参数直接传递给预处理管道方法。
%%time
df_preproc['preproc_pipe'] = preprocess_pipe(df_preproc['clean'])
df_preproc[['date', 'content', 'preproc_pipe']].head(3)CPU times: user 51.6 s, sys: 144 ms, total: 51.8 s Wall time: 51.8 s

对这个工作流进行计时似乎并没有显示出比之前的工作流有所改进,但是根据 spaCy 文档,随着我们处理越来越大的数据集,这种方法应该会显示出一些计时改进(平均而言)。
选项 3:使用 joblib 并行化工作
我们可以做得更好!以前的工作流程依次处理每个新闻文档,生成词条列表,然后作为新列附加到数据帧。因为每一行的输出都完全独立于另一行,这是一个令人尴尬的并行问题,非常适合使用多个内核。
spaCy 推荐使用joblib库来并行处理 NLP 流水线的块。确保您在运行以下部分之前pip install joblib。
为了并行化工作流,必须定义更多的助手方法。
- 分块:新闻文章内容是一个(长)字符串列表,其中每个文档代表一篇文章的文本。这些数据必须以“块”的形式提供给由
joblib启动的每个工作进程。每次调用chunker方法都会返回一个生成器,该生成器只包含特定块的文本作为字符串列表。在词汇化过程中,基于迭代器索引检索每个新块(前面的块被“遗忘”)。 - 扁平化:一旦 joblib 创建了一组在每个块上工作的 worker 进程,每个 worker 返回一个包含每个文档的 lemmas 的“列表列表”。然后,这些列表由执行者组合,以提供一个 3 级嵌套的最终“列表列表列表”。为了确保 executor 输出的长度与文章的实际数量相同,定义了一个“flatten”方法来将结果合并到一个包含词条的列表中。例如,两个并行的执行器将返回一个最终的嵌套列表:
[[[a, b, c], [d, e, f]], [[g, h, i], [j, k, l]]],其中[[a, b, c], [d, e, f]]和[[g, h, i], [j, k, l]]是指每个执行器的输出(最终的输出由 joblib 连接成一个列表)。这个结果的展平版本是[[a, b, c], [d, e, f], [g, h, i], [j, k, l]],即去掉了一层嵌套。
除了上面的方法,一个类似的nlp.pipe方法被用在工作流#2 中,在每个文本块上。这些方法中的每一个都被封装到一个preprocess_parallel方法中,该方法定义了要使用的工作进程的数量(在本例中为 7 个),将输入数据分成块并返回一个扁平的结果,然后可以将该结果附加到 DataFrame 中。对于具有更多物理内核的机器,工作进程的数量可以进一步增加。
使用 joblib 的并行工作流如下所示。
%%time
df_preproc['preproc_parallel'] = preprocess_parallel(df_preproc['clean'], chunksize=1000)CPU times: user 683 ms, sys: 248 ms, total: 932 ms Wall time: 17.2 s

对这种并行工作流进行计时显示出显著的性能提升(运行时间几乎减少了3 倍)!随着文档数量的增加,使用 joblib 启动多个工作进程的额外开销很快就会得到补偿,并且这种方法可以显著优于顺序方法。
块大小和批量大小的影响
请注意,在并行化的工作流中,需要指定两个参数—最佳数量可能因数据集而异。chunksize控制每个进程处理的每个块的大小。在本例中,对于 8,800 个文档,使用的块大小为 1000。太小的块大小意味着会产生大量的工作线程来处理大量的块,这会降低执行速度。通常,几百到几千个文档的块大小是一个很好的起点(当然,这取决于数据中每个文档的大小,以便块可以放入内存)。
批量大小是特定于nlp.pipe的参数,同样,一个好的值取决于正在处理的数据。对于相当长的文本,比如新闻文章,保持批量适当小是有意义的(这样每一批就不会包含真正的长文本,所以在这种情况下选择 20 作为批量大小。对于其他情况(例如 Tweets ),如果每个文档的长度非常短,可以使用更大的批量。
建议试验任一参数,看看哪种组合能产生最佳性能。
集合与列表
重要提示:尽可能使用集合而不是列表进行查找。
注意,在前面定义的get_stopwords()方法中,从停用词文件中读入的停用词列表在 lemmatizer 方法中通过查找移除停用词之前被转换为一个集合。一般来说,这是一个非常有用的技巧,但是特别是对于停用词移除,集合的使用变得更加重要。为什么?
在任何现实的停用词表中,比如这个新闻数据集的停用词表,有理由期待几百个停用词。这是因为对于主题建模或情感分析等下游任务,有许多特定于领域的单词需要删除(非常常见的动词,无用的缩写,如时区、星期几等。).每一个文档中的每个单词都需要与停用词表中的每个单词进行比较,这对于成千上万的文档来说是一项昂贵的操作。
众所周知,集合的查找时间为 O(1)(即常数),而列表的查找时间为 O(n)。在lemmatize()方法中,由于我们在停用词集中检查每个词的成员资格,我们期望集合比列表好得多。为了测试这一点,我们可以重新运行工作流#1,但是这一次,使用一个停用词列表来代替。
stopwords = list(stopwords)%%time
df_preproc['preproc_stopword_list'] = df_preproc['clean'].apply(lemmatize)
df_preproc[['date', 'content', 'preproc_stopword_list']].head(3)CPU times: user 1min 17s, sys: 108 ms, total: 1min 18s Wall time: 1min 18s

有了停用词表,现在产生相同的结果比以前(有了集合)多花了 50%的时间,也就是说运行时间增加了 1.5 倍!这是有意义的,因为在这种情况下,停用词列表大约有 500 个单词长,并且需要检查语料库中的每个单词是否属于这个合理大小的列表。
结论
在本练习中,使用 spaCy 管道处理了一个新闻文章数据集(NY Times ),以输出表示每篇文章内容中有用标记的词条列表。因为真实世界的新闻数据集几乎肯定比这个大,并且大小可以是无限的,所以需要快速、高效的 NLP 管道来对数据执行任何有意义的分析。以下步骤对于加速空间管道非常有用。
禁用 spaCy 模型中不必要的组件:标准 spaCy 模型的流水线包含 tagger(分配词性标签)、parser(生成依赖解析)和命名实体识别组件。如果需要这些动作中的任何一个或者都不需要,这些组件必须在加载模型后立即被禁用(如上所示)。
使用集合而不是列表进行查找:当执行查找以将一组标记与另一组标记进行比较时,总是使用集合来执行成员资格检查——列表的查找速度要慢得多!停用词的列表/集合越大,使用集合时看到的性能增益就越大。
尽可能使用定制语言管道:使用nlp.pipe设置语言管道是处理大块文本的一种非常灵活有效的方式。更好的是,spaCy 允许您单独禁用每个特定子任务的组件,例如,当您需要单独执行词性标记和命名实体识别(NER)时。有关如何在模型加载、处理或处理定制块期间禁用管道组件的示例,请参见 spaCy 文档。
尽可能使用多个内核:当处理彼此完全独立的单个文档时,请考虑通过在多个内核之间分配计算来并行化工作流。随着文档数量变得越来越大,性能提升可能是巨大的。人们只需要确保文档被分成块,所有这些块在任何给定的时间都必须适合内存。
我希望这是有用的——祝你在下一个 NLP 项目中测试它们时愉快!
原载于 2020 年 5 月 2 日https://prrao 87 . github . io。
动荡和金融市场?
金融市场动力学与湍流流动的比较

图片来自 Pixabay
动荡的流体行为和金融市场之间有着有趣的相似之处。在这两种情况下,人们可以发现大规模的扰动被转移到连续的较小规模(见斯坦利和曼特尼亚)。在液体的情况下,通过搅动它,人们可以观察到输入系统的能量被转移到越来越小的尺度。在金融市场中,大规模“注入”的不是能量,而是信息,人们可以观察到反应向较小规模(在这种情况下是个人投资者)的传递。两者都非常难以建模,因为它们的部件之间或者它们与环境之间存在多种类型的交互作用
在这里,根据 Stanley 和 Mantegna 的详细统计分析,可以看出,尽管金融市场和动荡的流体在质量上有相似之处,但在数量上,这种对应是有限的。
图 1:湍流射流的流动呈现出典型的湍流行为的宽范围长度尺度(来源)。
什么是湍流?
让我们考虑在管道中流动的流体,具有以下参数:

图 2:右边液体粘度较高(来源)。
在流体力学中,所谓的雷诺数或 Re 是一个(无量纲)量,有助于预测流型。具有上述参数的流动流体的 Re 由下式给出:

等式 1:在直径为 l 的管道中,运动粘度为ν、速度为 V 的流体的雷诺数。Re 值表示流体的复杂程度。
图 3:直径为 l 的管道中运动粘度为ν、速度为 V 的湍流流体。雷诺数 re 表示流体的复杂程度(来源的修改版本)。*
雷诺数 Re 是对流体复杂性的一种度量。根据 Re 值,流体要么是湍流(高度复杂),要么是层流(低复杂性)。层流变成湍流的过程称为层流-湍流转捩。
图 4:层流。速度曲线“看起来像一副纸牌”。流体在相互滑动的层中起作用(来源)。*
描述不可压缩流体动力学的著名的纳维尔-斯托克斯方程由下式给出:

等式 2:纳维尔-斯托克斯方程,其中 V ( r ,t)是速度矢量场,P 是压力。
其中 V ( r , t )为 r 处的速度矢量,时间 t 和 P 为压力。情商。2 描述了具有非常高 Re 的湍流状态(完全发展的湍流)。
关于能量级联的一个旁白
在具有非线性动力学的系统中,例如具有充分发展的湍流的流体,直接(反向)能量级联涉及能量从大(小)尺度运动到小(大)尺度的转移。如果有中间刻度,这个中间范围叫做惯性范围或惯性子范围。

图 5:该图显示了湍流能谱中的产生、能量级联和耗散(来源)。
惯性范围内的能谱包括从低到高波数的能量转移(能量级联),并具有以下幂律形式:

等式 3:惯性范围内的能谱具有这种幂律形式。
Kolmogorov 1941 年的理论
著名的苏联数学家安德雷·柯尔莫哥洛夫在两篇论文(均来自 1941 年)中表明,对于完全发展湍流的流体(在 Re → ∞极限内的流体),会发生以下行为

方程 4:Re→∞时,均方速度增量的行为,由苏联数学家安德雷·柯尔莫哥洛夫在 1941 年发现。
图 6:苏联数学家安德雷·柯尔莫哥洛夫(来源)。*
在惯性范围内。在方程 4 中, l 对应的距离(见 Stanley 和 Mantegna )小于发生湍流行为的维度,大于动能消散为热量的长度。
然而,Kolmogorov 的理论未能解释速度变化U(t)=δV(t)的间歇行为(活动在时间演化中突然变化的发生)以及随之而来的 U ( t )的概率分布的轻子态,
比较金融市场和动荡动态
为了比较金融市场和湍流的时间行为, Stanley 和 Mantegna 分析了两个量,即:
- 1984-1989 年期间标准普尔 500 指数的动态
- 非常高 Re 的三维完全湍流流体的速度V(t)(更具体地说他们认为“在康涅狄格农业研究站树冠以上约 6 米的大气表层的风速”)。
在短时间内,这两个过程都是非平稳、非高斯和间歇的。然而,在长时间内,这两个过程都是渐近稳定的。
Stanley 和 Mantegna 对标准普尔 500 指数和流体速度进行了四次比较,即:
- 在它们的时间演变 Y ( t )和 V ( t )之间
- 它们的变化量Z(t)=δY(t)和U(t)=δV(t)
- 作为 Z ( t )和 U ( t )的δt的函数的标准偏差σ(δt)之间
- 在 Y ( t )和 V ( t )的功率谱 S ( f )之间
标准普尔 500 指数和流体速度的时间演变
下面的图 7 比较了在间隔δt= 1 小时采样的标准普尔 500 指数的时间演变和完全发展湍流中的大气风速(在非常高的雷诺数 Re 下)。

图 7:以一小时为间隔采样的标准普尔 500(上图)。充分发展湍流中的大气风速(具有很高的 Re)(下图)(来源)。
标准普尔 500 指数和流体速度的变化
图 8 在顶部显示了 S & P 500 指数的δt= 1 小时的间隔的变化,在底部显示了流体速度的变化(在更高的采样率下)。我们看到,对于湍流来说,关于 x 轴是对称的,这在金融数据中是不存在的。这种差异将在下面通过指标Z(t)=δY(t)和流体速度增量U(t)=δV(t)的标准偏差的行为来确认(见图 9 及其上面的讨论)。

图 8:标普 500 指数δt= 1 小时的区间变化(上)。流体速度的变化(以更高的采样率) (底部)(来源)。
标准普尔 500 指数和流体速度增量的标准差
Stanley 和 Mantegna 还针对流程 Z ( t )和 U ( t )研究了波动率σ(δt)作为δt的函数,如图 8 所示。在这两种情况下,幂律

等式 5:增量 Z( t )的概率分布的波动性和速度增量 V(t+δt)-V(t)作为δt 的函数都呈现幂律行为,指数分别为ν=0.53 和ν=0.33。**
虽然σ(δt)在两个过程中都表现出幂律行为,但它们之间的时间相关性却有很大不同。更具体地说,我们有:
- 概率分布P(Z(t)的波动率σ(δt)有一个超扩散行为的初始区间,之后是扩散行为,典型的增量不相关的随机过程(上图)。**
- 概率分布的标准差 P ( U )其中 U 为速度增量U(t)=V(t+δt)-V(t)作为δt的函数

图 9:S&p500 指数时间序列(上)的增量 Z( t )的概率分布 P ( Z )的波动率σ(δt)作为δt的函数。概率分布的标准偏差 P ( U )其中 U 为速度增量U(t)= V(t+δt)-V(t)作为湍流(底部)δt的函数(来源【来源】
光谱密度
平稳随机过程的功率谱是其自相关函数的傅立叶变换:

等式 6:平稳随机过程的功率谱。
随机游走的 S ( f )具有以下函数形式:

方程式 7:随机漫步的光谱密度。
人们可以用它们的功率谱来比较这两个过程。两者都服从函数形式:

等式 8:两个过程的功率谱都具有这种形式,但是η非常不同。在惯性和耗散范围内,标准普尔 500 指数η=1.98,而速度时间序列η=5/3 和η=2。
两个过程都有形式为 Eq 的 S ( f )。8,但指数差别很大。对于 S & P 500 指数,我们得到 η =1.98,这非常接近与随机游走相关的指数。速度时间序列在低频和高频分别有 η =5/3 和 η =2。
异同
对高度湍流流体的动力学和标准普尔 500 指数的比较分析表明,同样的方法可以应用于检查具有已知但(分析上)不可解的运动方程的不同系统。
相似性包括间歇性行为的存在,非高斯概率分布逐渐收敛到高斯吸引子。差异包括两个系统中概率分布的形状,以及速度波动与标准普尔 500 指数波动反相关的事实,标准普尔 500 指数波动是不相关的。
这篇文章是基于关于金融市场动态和动荡行为之间关系的讨论,这些讨论见于自然杂志的文章和斯坦利阁下和 R.N .曼特尼亚的教科书。
我的 Github 和个人网站 www.marcotavora.me 有一些关于金融和其他主题的有趣资料,如数学、数据科学和物理。看看他们!
土耳其语音学:快速介绍
实用土耳其发音语音学介绍
在本文中,我们将探讨土耳其语语音系统的一些细节,包括更高质量的土耳其 ASR 开发技巧😉
在转向对话式人工智能之前,我在一个演讲团队工作。在这篇文章中,我将分享我在开发土耳其语语音识别系统时发现的技巧。作为一个无限的语料库来源😄,我用自己的声音生成例子。为了让一些语音事件更容易理解,我对比了土耳其语和英语的发音。
土耳其语几乎是一种语音语言,大多数单词的发音与书写一致。字母表由 29 个字母、8 个元音和 21 个辅音组成:a b c ç d e f g ğ h ı i j k l m n o ö p r s ş t u ü v y z。开发一个土耳其 g2p 相对容易,你可以选择一个基于规则的系统。元音容易,辅音容易。单词级重音模式是基于规则的,也不是很复杂。土耳其语的语音系统的确很确定。
我用土耳其 SAMPA 耳机做我的 Kaldi 实验,你可以从这里使用它。对应的语音词库是这里是。在生成该词典的过程中会用到以下所有技巧😉。

剧透:土耳其语听起来/感觉如何
让我们开始探索:
元音
我们的正字法有 8 个元音,没有双元音,没有辅音群,也没有什么新奇的东西。元音分为后/前、圆和高/低。
土耳其语音节模式比较容易,元音和音节之间存在一一对应关系,one vowel = one syllable。因此,土耳其音节化算法是相当容易的。
kazak ka-zak
yorgan yor-gan
kestirmek kes-tir-mek
getirmek ge-tir-mek
元音就是这样,真的。在接下来的两节中,我将描述关于元音的两点,为了一个好的 g2p,你应该睁大你的眼睛/耳朵。
短/长元音
土耳其语有许多阿拉伯语/波斯语借词。在文字革命之前,土耳其语确实是波斯语、阿拉伯语和土耳其语的混合。今天,现代土耳其语主要由土耳其语单词组成,但仍受到阿拉伯语和波斯语相当大的影响。
这些词的问题是,通常有土耳其同行写的相同但元音确实不同。对比
kâr(利)kar(雪)
hâla(尚)vs hala(姨妈)
Kâsım(专有名词,一个男性的名字)vs Kasım(十一月)
【屋顶痕】,^被排除在 90 年代的书面语之外。因此,这些单词在正字法中变得不可区分,并给 SMT、ASR 和许多其他统计系统带来麻烦。让我们听单词并比较元音长度:

kar(斯诺),同“短一”。
对比一下kâr,下面加个long a:
1。查看原始信号
2 的元音长度差异。参见频谱图
3 中两个a共振峰的相似性。也听到看到k s 的不同,这里k s 确实不同。k有两个音位变体,/c/和/k/;腭化和非腭化音位变体。长元音也有使同一个音节中的辅音腭化的效果。这里,kâr中的k确实是/c/,其中kar中的k是/k/。从光谱图中可以看出/k/和/c/的不同特征。土耳其 SAMPA 区分/c/和/k/,显然它们是不同的音位,从频谱特征可以看出:

带“long a”的“kar”(利润)
摘要:虽然写法相同,但是从声谱图上看,kar和kâr在声学上非常不同。
如何处理:正如我之前说过的,如果一个旧外来词有一个母语为土耳其语的孪生词,就会出现这种情况。这样的单词的数量是有限的,当做土耳其 g2p 时,人们通常从这样的单词的字典中得到帮助。很可能你想在你的语音词典中包括这两种发音,就像这样:
kar: k a r
kar: c a: r
插字
从外来词来说,元音增音在西方外来词中经常发生。我在元音部分介绍中提到过,土耳其语没有声母簇;每个音节有一个元音,每个元音就是一个音节;元音-音节是一一对应的。因此,难怪我们在第一个音节插入一个元音来分割潜在的声母簇。查看发音中的额外元音:
kral k 1 r a 5
tren t i r e n
Brüksel b y r y c s e l
Twitter t i v i t 1 r
我的同事兼朋友,资深语言学家阿萨夫用他受过良好教育的耳朵注意到了这个语音事件。他说我插入了一个额外的元音,我告诉他这在以土耳其语为母语的人中很常见。
让我们听听我口中的Brüksel。注意频谱图中两个不同的相同元音:

“Brüksel”,发音有两个元音:一个隐藏,一个书写
Ben 是我的同事,一位才华横溢的程序员,母语是英语,来自美国。因此,他的声道肯定知道如何发出我不能发出的辅音串。听他讲Brussels,从频谱图看发病簇:

“布鲁塞尔”,由一个美国土著人发音。
小结:元音增音与西方借词一起发生。
如何处理:这几个字很好区分,如果一个字以两个辅音开头;那么就属于这一类。找到第一个拼写元音,然后插入一个合适的音标元音。增音元音应该是高元音([iiuü])。
为什么我们不能生产双元音
我们也不能生产双元音,没错😄。我试着发low的音,但失败了,而本成功了😄
让我们看看实际情况:

“低”,由土耳其母语者发音。与原发音不太相关
与英语不同,土耳其语元音的 back 和 roundedness 是不相关的。我们有前圆元音(ö、ü)和后圆元音(o、u)。如果我们观察频谱图的共振峰,我们会注意到 F2 值较低,表明存在后向性。相对较高的 F1 变得较低,F3 以及 F1 和 F2 之间的差距暗示着圆整...这个元音是/o/。所以,我不幸地发了一个单元音。还要注意,语音信号只有一次上升,持续时间相当长。本再次出手相救:

“low”,由美国本土人发音。这个双元音发音完美。
注意频谱图中从一个元音到另一个元音的过渡。然而,在语音信号中只有一个上升;技术上有一个音节;而是频谱图中的两个元音。作为一个美国人,本的声道完美地完成了滑行,而我只能发出“Lovvvvvvvv”很不幸😄
软 G!
软 G 是一个有争议的声音。在土耳其正字法中,它被标记为辅音,但它确实不是一个真正的声音。在某些情况下,它会拉长前一个元音,在某些情况下,它会完全消失。以下是一些观察结果:
- 当它在字尾或音节尾位置时,它拉长前面的后元音,如
dağdan/d a: d a n/和dağ/d a:/ - 相同的后元音之间是听不见的,例如
uğur /u: r/、ağarmak、/a: r m a k/和sığır、/s ı: r/。 - 在相同的前元音之间,要么听不见,如
bildiğim/b i l d i: m/,要么听起来像腭滑音,如düğün /d y j y n/。 - 当它出现在 e 和 I 之间时,要么听不见,要么发音为腭滑
/j/。单词değil常被听成/d e j i l/和/d i: l/。 - 在
i和e之间出现时,软 g 多听为腭滑/j/:diğer常读作/d i j e r/,有时误写成diyer - 在圆元音之间几乎听不见,例如
soğuk/s o u k/。 - 在圆元音和非圆元音之间,大部分是听不见的,例如
doğan/d o a n/。 a+ğ+ı序列可能听起来像是/a/后跟/1/的序列,或者像是两个/a/元音的序列:ağır为/a 1 r/或/a: r/。ı+ğ+a序列发音为/a/后接/1/:sığan/s 1 a n/
动作上看软 G。你会听到单词dağ (mountain),这里软 G 位于音节末尾,因此他会拉长音节元音a。查看语音信号中元音的持续时间,并将其与另一个单音节单词kar进行比较,这是笔记本的第一个单词:

压力,压力,压力
土耳其应力模式高度依赖于形态学。大多数原生词的最后一个音节带有重音,后缀通常将重音“移向”词尾。对于大多数单词来说,词尾重音或多或少是标准的,但旧的阿拉伯语/波斯语借词例外。
我们来看一个例子。koyun既可以解释为
koyun koy+un <imper><2per> koyun koyun (noun)
命令式后缀将重音带到前一个音节,因此
koyun k o+ j u n <imp><2per>
koyun k o j u+ n (noun)
从我与 Kaldi 的实验中,我注意到神经网络无论如何都学习土耳其重音模式,不需要输入重音位置。的确,还不如根本不标注重音位置,让神经网络去学习。如果你不小心做错了标记,你可能会让 WER 的情况更糟(我就是这么做的😂).
在探讨了咬字之后,我们再来玩一些连续的语音数据,探讨一下土耳其语的声学模型。下面我用的是 Mozilla Common Voice 土耳其语语音语料库。该数据集包含 13725 个话语,总共 14 个小时的讲话。
就像在任何语音任务中一样,首先我提取声学特征向量,我喜欢使用有 40 个特征的mfcc 。代码如下:
import librosafiles = glob.glob("../tr/clips/*.mp3")datas = []for fn in files:
X, sample_rate = librosa.load(fn)
print(fn + ":" + str(sample_rate) + "Hz")
fs=40
mfcc_feature = librosa.feature.mfcc(X,n_mfcc=fs)
datas.append(mfcc_feature)
结果是一个(13725, 40)数据矩阵。(13725 个实例=话语,40 =声音特征的数量)
然后我用t-SNE把提取的 mfcc 向量分别投影到二维和三维空间。我用了sklearn的t-SNE函数:
嵌入二维空间的 mfcc 特征
嵌入三维空间的 mfcc 特征
mfcc单独只是一种特征化方法,它不包括任何上下文/顺序信息。它没有提供任何关于哪些音素以何种密度一起出现,哪些音素是相似的,哪些音素在相似的语音环境中一起出现的信息…正如我所说的没有顺序信息。语言和言语是关于语境的,即本质上是连续的。因此我决定做一个更有语境的特征化,我在提取的mfcc s 上做了一个autoencoder,它实际上是一个基本的seq2seq,没什么特别的:
model_inputs = Input(shape=(40,))
inputs = Lambda(lambda x: K.expand_dims(x, -1))(model_inputs)
encoded = LSTM(5, return_sequences=False)(inputs)decoded = RepeatVector(40)(encoded)
decoded = LSTM(1, return_sequences=True)(decoded)
decoded = Lambda(lambda x: K.squeeze(x, -1))(decoded)autoencoder = Model(model_inputs, decoded)
autoencoder.compile(loss='mse', optimizer='adam')
autoencoder.fit(datas, datas, epochs=10)
我用了两个LSTM,一个用于编码器,一个用于解码器。RepeatVector 层用于构建多对多架构。最终的架构看起来像:

自动编码器架构,基本 seq2seq 模型
好了,接下来是精彩的部分:我提取了编码器 LSTM 的输出,并通过t-SNE将其投影到三维空间:

编码器 LSTM 输出的三维 t-SNE
这是土耳其语语音编码后的样子,结果非常漂亮。难怪是这样,土耳其人本身就很美。
亲爱的读者,我们已经到了这篇文章的结尾。欣赏美很重要,更重要的是一起欣赏美😄想了解我的更多信息,请关注我的博客,或者随时通过我的 Linkedin 页面联系我。在那之前,请保持快乐、安全和无病毒。干杯!
用函数式语言把 Julia 变成 Python 和 Duck-Type
通过结合 Julia 富有表现力的语法和令人敬畏的调度,将 Julia 变成 Python。

( python logo src , julia logo src ,史酷比是汉娜芭芭拉的注册商标,我不拥有史酷比)
(有个视频可以用这个看!希望你喜欢!)
介绍
Julia 在很多方面都是一种不可思议的语言,作为一种函数式编程语言,它对可变性的支持令人惊讶。此外,Julia 包含了其开箱即用的调度,这是一个简单有效的多态工具。大多数 Julia 用户都很清楚 Julia 的调度,但是他们可能不知道 Julia 有操纵语言进行鸭式打字的能力。使用这种方法,当使用我们的类型和它们各自的方法时,我们可以使 Julia 看起来和感觉上与 Python 非常相似。
派遣
如果你和朱莉娅呆过一段时间,你很可能对朱莉娅的派遣很熟悉。Julia 的调度可用于应用不同的方法来处理不同类型的参数多态性。尽管 Julia 在这方面非常动态,尽管如此(以及可变声明),重要的是要记住我们仍然保留了 Julia 作为函数式编程语言的好处。考虑前两个示例函数:
function _handleArray(array)
[println(i) for i in array]
endfunction _handleDataFrame(df)
for column in eachcol(df)
[println(i) for i in column]
end
end
DataFrame 是一种不能像我们通常使用数组那样逐元素迭代的类型。为了迭代一个数据帧,我们需要使用 eachcol()方法。我们可以使用两个完全独立的函数来处理这两种类型,但是另一种更明智的方法是,我们可以使用 Julia 的调度程序用相同的方法来处理这两种类型。

现在,每当数据帧通过 handle()方法传递时,我们都会得到预期的返回:

同样,当我们传递一个数组时,我们得到完全相同的结果:

不过,我们确实得到了一个有趣的监督阵列。Julia 这样做是因为每当数组像这样循环时,它都会给出一个返回:
[println(i) for i in array]
但是当然,println()的每次调用都不修改现有值或提供返回,所以 Julia 什么也不返回,因为什么也没有返回。这是一个有趣的和有点滑稽的疏忽,我以前从来没有注意到,因为当然
我不经常按元素打印数组。
鸭子打字
在 Julia 中使用一些有趣的类型构造技巧,我们可以像在 Python 和 C++这样的面向对象编程语言中一样,在 Julia 中有效地“鸭式”。这对于创建具有可变数据的对象和操纵该数据的子方法非常有用。这样做的另一个巨大好处是,我们可以有一个初始化函数,每当我们的类型被创建时,这个函数就会运行。当然,这是 Python 的一个主要特点,例如,几乎每次创建新类型时都会调用 init 函数。考虑这个函数:
function our_type(number)
add5() = number += 5
double() = number *= 2
get_number() = number
(var) -> (add5;double;number)
end
结尾的语句,
(var) -> (add5;double;number)
创建一个新的类型,包含我们在创建该类型时使用的所有数据和函数。这可以是数据;在 Int64 例子中的数字,也可以是函数;在 double()或 add5()的例子中。现在,无论何时创建这种类型,我们都可以像调用孩子一样调用函数。
d = our_type(5)

然而,使用这种方法有一些问题。您可能遇到的第一个问题是,当使用此方法时,类型将没有值。例如,如果我们试图调用 typeof ():

这种方法的另一个缺点是访问我们断言为新类型的子类型的数据,在本例中,该数据是 int 64“number”每当我们调用这些数据时,都会得到一种类型的核心。方框:

如果我们试图把这个整数和另一个整数相加,那是行不通的!

然而,我们可以通过添加一个返回数据“数字”的新方法来减轻这种情况考虑这个例子:

然后我们可以像使用普通的 Int64 一样使用返回的数字。正如您在 Python 中所期望的那样,当使用我们的函数在我们的类型内部操作数据时,它实际上是将该操作应用于作为该类型的子类型的数据。
结合两者
虽然这些都是将函数和结构分开使用的好方法,但是它们也可以很好地配合使用。这方面的一个很好的例子是我如何在我的 Julia 的模块化图形库中使用它们,
对于本例,我们有两种方法都用于绘制坐标,但是,其中一种方法特别需要数据帧,即 being _dfscatter:

我们的另一个函数将需要一个数组类型,它被称为 _arrayscatter:

虽然这两种方法都使用 duck-type 方法来使用特定类型的方法,类似于 Python 中的类,但根据数据类型调用不同的函数肯定不是最佳选择。因此,记住这一点,我们可以将这两个概念结合起来,首先用调度函数创建我们的对象,然后用子方法创建我们的变量。当然,首先我们将设置调度:
plot(x::DataFrame,y::Symbol,shape) = _dfscatter(x,y,shape)
plot(x::Array,y::Array,shape) = _arrayscatter(x,y,shape)
然后我们的输入变量:
df = DataFrame(:A => randn(1200), :B => randn(1200), :M => randn(1200), :P => randn(1200), :D => randn(1200), :Y => randn(1200));
shapes = [Circle(.5,.5,.02, :lightblue),Circle(.5,.5,.02, :pink),Circle(.5,.5,.02, :red), Circle(.5,.5,.02, :orange),
Circle(.5,.5,.02, :purple),Circle(.5,.5,.02, :green)];
将我们的地块创建为对象,并且
佩服的美!


如果你想了解更多,我也写了一篇文章!
[## 将 Julia 的多态分派添加到我的绘图库中(第 4 部分)
方法论之争!
towardsdatascience.com](/adding-julias-polymorphic-dispatch-to-my-plotting-library-part-4-1446e924774f)
性能指标
对于机器学习或计算机科学中的任何好的演示,考虑性能总是很重要的。对于这些性能指标,我们将使用 @time ,一个从 IJulia 导出的函数来代替 IPython 的%timeit。
首先,我创建了一个数据框架,它实际上会给我们带来足够的挑战,值得计时,有 100 万个值。虽然这肯定是一个很高的数量,但可能还不够高——但我们肯定会对 Julia 的调度和在 Julia 中键入 duck-typing 之间的度量标准有一个很好的了解。
这是我们的鸭式线性回归函数:
function LinearRegression(x,y)
# a = ((∑y)(∑x^2)-(∑x)(∑xy)) / (n(∑x^2) - (∑x)^2)
# b = (x(∑xy) - (∑x)(∑y)) / n(∑x^2) - (∑x)^2
if length(x) != length(y)
throw(ArgumentError("The array shape does not match!"))
end
# Get our Summations:
Σx = sum(x)
Σy = sum(y)
# dot x and y
xy = x .* y
# ∑dot x and y
Σxy = sum(xy)
# dotsquare x
x2 = x .^ 2
# ∑ dotsquare x
Σx2 = sum(x2)
# n = sample size
n = length(x)
# Calculate a
a = (((Σy) * (Σx2)) - ((Σx * (Σxy)))) / ((n * (Σx2))-(Σx^2))
# Calculate b
b = ((n*(Σxy)) - (Σx * Σy)) / ((n * (Σx2)) - (Σx ^ 2))
# The part that is super struct:
predict(xt) = (xt = [i = a + (b * i) for i in xt])
(test)->(a;b;predict)
end
这是我们的常规函数,以及它的调度和保存数据的结构:
mutable struct LinReg
x
y
endfunction lin_predict(m,xt)
if length(x) != length(y)
throw(ArgumentError("The array shape does not match!"))
end
# Get our Summations:
Σx = sum(x)
Σy = sum(y)
# dot x and y
xy = x .* y
# ∑dot x and y
Σxy = sum(xy)
# dotsquare x
x2 = x .^ 2
# ∑ dotsquare x
Σx2 = sum(x2)
# n = sample size
n = length(x)
# Calculate a
a = (((Σy) * (Σx2)) - ((Σx * (Σxy)))) / ((n * (Σx2))-(Σx^2))
# Calculate b
b = ((n*(Σxy)) - (Σx * Σy)) / ((n * (Σx2)) - (Σx ^ 2))
(xt = [i = a + (b * i) for i in xt])
return(xt)
endpredict(m::LinReg,xt) = lin_predict(m,xt)
当然,使用这些函数,我们将需要创建一个训练 X 和训练 y,以及一个测试 X 和测试 y。

trainX = train[:X]
trainy = train[:Y]
testX = test[:X]
testy = test[:Y]
现在我们可以简单地将它们插入到我们的方法中并比较时间!

结论
Julia 当然有很多很酷很有趣的方法来处理函数类型。我们不仅可以使用 Julia 的 dispatch,还可以利用该语言富有表现力的语法创建自己的临时类型来保存数据和函数。这不仅非常方便,而且还带来了特殊的性能提升。有一些缺点,比如如果不编写函数就不能读取类型或访问数据,但我认为这种权衡是值得的。这是我第一次把一个视频放到一个成熟的视频编辑器中,我真的希望这个结果是值得的——因为哇,Kden-live 吞噬内存就像它什么都不是,而我在整个过程中都被钉在 80%以上的内存使用率上!我的电脑也不喜欢渲染它,这是肯定的!







浙公网安备 33010602011771号