用 FastAPI 和 SQLAlchemy 构建数据 API
如何使用模块化数据库逻辑将数据加载到数据库中,并在应用程序中执行 CRUD 操作
由: 爱德华·克鲁格 数据科学家兼讲师和 道格拉斯·富兰克林 助教兼技术作家组成。
链接到 Github repo,app 代码:https://github.com/edkrueger/sars-fastapi
克里斯·利维拉尼在 Unsplash 拍摄的照片
什么是 FastAPI?
FastAPI 是基于 Pydantic 和 Starlette 的高性能 API。FastAPI 与许多包集成得很好,包括许多 ORM。使用 FastAPI,您可以使用大多数关系数据库。FastAPI 很容易与 SQLAlchemy 集成,SQLAlchemy 支持 PostgreSQL、MySQL、SQLite、Oracle、Microsoft SQL Server 等。
其他 python 微服务框架如 Flask 不容易与 SQLAlchemy 集成。通常使用 Flask 和一个名为 Flask-SQLAlchemy 的包。Flask-SQLAlchemy 是不必要的,它有自己的问题。关于这方面的更多信息,请阅读这篇文章。
没有 FastAPI-SQLAlchemy,因为 FastAPI 与 vanilla SQLAlchemy 集成得很好!
模块化应用程序结构
写 app 的时候,最好创建独立的、模块化的 python 代码。这里我们将讨论我们的应用程序的以下组成文件,database.py、models.py、schemas.py、main.py 和 load.py。
理想情况下,您应该只需要定义一次数据库模型!使用 SQLAlchemy 的**declarative_base()**
和**Base.metadata.create_all()**
允许您为每个表编写一个类,以便在应用程序中使用,在应用程序外的 Python 中使用,以及在数据库中使用。使用单独的 database.py 和 models.py 文件,我们一次性建立数据库表类和连接,然后在需要时调用它们。
为了避免 SQLAlchemy 模型和 Pydantic 模型之间的混淆,我们将拥有包含 SQLAlchemy 模型的文件**models.py**
,以及包含 Pydantic 模型的文件**schemas.py**
。同样值得注意的是,SQLAlchemy 和 Pydantic 使用稍微不同的语法来定义模型,如下面的文件所示。
Database.py
下面是使用 SQLAlchemy 定义数据库连接的文件。
database.py
声明性库和元数据
**declarative_base()**
基类包含一个**MetaData**
对象,其中收集了新定义的**Table**
对象。当我们调用行**models.Base.metadata.create_all()**
来创建我们所有的表时,这个元数据对象被访问。
会话本地:处理线程问题
SQLAlchemy 包含一个帮助对象,帮助建立用户定义的**Session**
范围。这有助于消除应用程序中的线程问题。
为了创建一个会话,下面我们使用**sessionmaker**
函数,并给它传递几个参数。Sessionmaker 是一个初始化新会话对象的工厂。Sessionmaker 通过从引擎的连接池中请求连接并将连接附加到新的会话对象来初始化这些会话。
来自 database.py 的会话本地
初始化新的会话对象也称为“检出”连接。数据库存储这些连接/过程的列表。因此,当您开始一个新的会话时,请注意您也在数据库中启动了一个新的进程。如果数据库没有关闭这些连接,则存在可以达到的最大连接数。数据库最终会终止像过时连接这样的空闲进程;然而,这可能需要几个小时才会发生。
SQLAlchemy 有一些池选项来防止这种情况,但是最好在不再需要连接时删除它们!FastAPI 文档包含一个**get_db()**
函数,该函数允许路由通过一个请求使用同一个会话,然后在请求完成时关闭它。然后**get_db()**
为下一个请求创建一个新的会话。
一旦我们建立了数据库连接和会话,我们就可以开始构建其他应用程序组件了。
Models.py
注意,我们将上面 database.py 文件中定义的**Base**
类导入到下面的 models.py 文件中,以使用**declarative_base()**
。
models.py
该文件为数据库中的表**Records**
创建模型或模式。
使用 SQLAlcehmy 的**declarative_base()**
允许您为应用程序使用的每个表只编写一个模型。然后,在 Python 中,在应用程序之外和数据库中使用该模型。
拥有这些独立的 Python 文件很好,因为您可以使用相同的模型在应用程序之外查询或加载数据。此外,每个模型都有一个版本,这简化了开发。
这些模块化 Python 文件可用于在数据管道、报告生成和任何其他需要的地方引用相同的模型或数据库。
Schemas.py
这里我们为 Pydantic 编写模式。记住 FastAPI 是建立在 Pydantic 之上的。在 Pydantic 中定义对象的主要方法是通过从**BaseModel.**
继承的模型
Pydantic 保证结果模型的数据字段符合我们使用标准的现代 Python 类型为模型定义的字段类型。
schemas.py
第**orm_mode = True**
行允许应用程序获取 ORM 对象并自动将它们翻译成响应。这种自动化使我们不必手动从 ORM 中取出数据,将它放入字典,然后用 Pydantic 加载它。
Main.py
这里是我们将所有模块化组件集合在一起的地方。
在导入我们所有的依赖项和模块化应用程序组件后,我们调用**models.Base.metadata.create_all(bind=engine)**
在数据库中创建我们的模型。
接下来,我们定义我们的应用程序并设置 CORS 中间件。
main.py
CORS 中间件
CORS 或“跨来源资源共享”指的是在浏览器中运行的前端具有与后端通信的 JavaScript 代码,并且后端与前端处于不同的“来源”的情况。
在您的 FastAPI 应用程序中配置 CORS 中间件
- 导入
**CORSMiddleware**
。 - 创建一个字符串形式的允许源列表,例如“http://localhost”、“http://localhost:8080”
- 将它作为“中间件”添加到您的 FastAPI 应用程序中。
您还可以指定您的后端是否允许:
- 凭证(授权头、Cookies 等。).
- 特定的 HTTP 方法(
**POST**
、**PUT**
)或所有带有通配符"*****"
的方法。 - 特定的 HTTP 头或所有带有通配符
"*****"
的 HTTP 头。
为了一切正常工作,最好明确指定允许的来源。此外,出于安全原因,明确哪些来源可以访问您的应用程序是一种很好的做法。
正确设置 CORS 中间件将消除应用程序中的任何 CORS 问题。
**get_db()**
函数确保任何通过该函数的路由在需要时都应该有我们的 SessionLocal 数据库连接,并且会话在使用后关闭。
**/records**
路线用于查看我们 app 的数据。注意,在这条路由中,我们使用 schemas.py 作为前端,models.py 查询后端。
fast API/记录路线
外部数据加载
我们没有使用应用程序加载数据,而是使用单独的 Python 文件加载数据库。
下面是一个示例 Python 文件,它从 CSV 读取数据,并将数据插入到数据库中。
load.py
请注意,我们导入了模型、我们的自定义会话 SessionLocal 和我们在其他 Python 文件中定义的引擎。然后我们读取 CSV 并使用**models.Record**
模式,通过**SessionLocal()**
连接将**db_record**
添加到数据库中。
数据库加载
如果您的应用程序设置正确,包括您的数据库连接字符串,您可以调用:
python load.py
这将加载您的本地或远程数据库,无需运行应用程序!
测试应用程序
要使用远程数据库在本地运行应用程序,请在终端中运行:
uvicorn app.main:app
这会在本地运行您的应用程序。这个本地实例连接到我们刚刚加载的云数据库,所以检查/records 路径来查看数据!
结论
所使用的模块化应用程序结构允许我们定义一个模型,然后根据需要使用它。这种做法使开发变得更加容易,因为如果出现问题,您只需要调试一个文件。此外,您的代码将更加可重用,并为另一个项目做好准备!
对于您的下一个数据项目,无论是仪表板、数据日志还是 API,一定要尝试一下 FastAPI!它很快也很容易上手,而且他们有很好的文档来指导你。
我们希望这对您有所帮助,感谢您的阅读,祝您好运!
FastAPI 永远毁了我的烧瓶
为什么我更喜欢使用 FastAPI 而不是 Flask
FastAPI 标志—https://fastapi.tiangolo.com
作为一名数据科学家,你最喜欢的是什么?这肯定是为了获得最佳结果而进行的建模和微调。但是如果一个好的模型从来没有被使用或者从来没有被部署过,那么它又意味着什么呢?
要产生一个机器学习模型,典型的做法是把它包装在一个 REST API 中,作为一个微服务来使用。创建 API 最广泛使用的框架之一是 Flask 。
Flask 被广泛使用的主要原因是它的简单性。一般情况下,我们只使用 API 对预测进行建模,所以不需要复杂的架构(例如: Django )。另一个原因是 Flask 是用 Python 写的,Python 是一般用来做机器学习建模的语言,所以我们很熟悉。
然而,如果您想要创建一个带有清晰的、静态的、经过验证的输入的 REST API,那么您必须包含几个不同的包,这些包来自几个互不合作的第三方。而且你必须创建定制的代码来运行一切。
这促使我寻找满足需求的替代方案,最终我找到了一个名为 FastAPI 的框架,它成为了我最喜欢的框架。以下是我喜欢使用 FastAPI 的原因。
证明文件
我从 FastAPI 注意到的第一件事是文档。FastAPI 有非常广泛的文档和丰富的例子,这使得事情变得更容易。如果你需要找一些关于 FastAPI 的东西,通常不需要去别的地方找。
此外,FastAPI 将从我们正在运行的程序中自动生成交互式 API 文档。这样我们就不需要编写自己创建的 API 文档了。众所周知,编写 API 文档是很烦人的,不仅因为有很多细节要处理,而且当最终用户在实现但没有匹配文档时,没有什么比这更令人沮丧的了。因此,自动生成 API 文档将非常有帮助
下面是 API 文档的显示示例(由 Swagger UI 提供)
http://127 . 0 . 0 . 1:8000/docs
还提供了替代的自动文件(由 ReDoc 提供)
http://127 . 0 . 0 . 1:8000/redoc
为了定义创建的模式,它使用了 Pydantic ,这是另一个很棒的 Python 库,用于数据验证。它的目的是验证进入 FastAPI 的输入。如果输入/请求不正确,则将返回相应的错误代码。
来源:https://medium . com/@ tiangolo/introducing-fastapi-FDC 1206d 453 f
表演
顾名思义,这确实是 FastAPI。根据来自 techempower 基准的数据,FastAPI 在性能方面胜过所有框架。像 Flask 和 Django 这样最常用的框架落后了。
框架性能— techempower
是啊,太快了。
异步的
Flask 的缺点之一是缺乏异步 I/O 支持。异步对于 HTTP 端点非常重要,因为 HTTP 端点往往需要等待大量的 I/O 和网络聊天,这使得它成为使用异步的并发性的一个很好的候选对象。
与 Flask 不同,FastAPI 使用async
/ await
Python 关键字支持开箱即用的异步代码。FastAPI 默认支持异步,这意味着我们不需要使用第三方框架来做异步。
您所要做的就是用 async 关键字声明端点,如下所示:
异步代码
总之,FastAPI 是一个快速的 web 框架,支持异步代码。此外,它还配有非常完整的文档和集成的验证系统,使其更易于使用。
你对使用 FastAPI 感兴趣吗?如果你想开始使用 FastAPI,这里有一些很好的资源。这里有一些你可以探索。
最简单的 FastAPI 文件可能是这样的:将它复制到 main.py 文件中
fastapi.tiangolo.com](https://fastapi.tiangolo.com/tutorial/first-steps/) [## FastAPI 简介
FastAPI 是一个现代、快速(高性能)的 web 框架,用于基于标准 Python 使用 Python 3.6+构建 APIs
medium.com](https://medium.com/@tiangolo/introducing-fastapi-fdc1206d453f)
使用 AMP 和 Tensor 内核的速度更快、内存效率高的 PyTorch 模型
只需添加几行代码
你知道 1986 年 Geoffrey Hinton 在《自然》杂志的论文中给出了反向传播算法吗?
此外,1998 年 Yann le cun 首次提出了用于数字分类的卷积网络,其中他使用了单个卷积层。直到 2012 年晚些时候,Alexnet 才通过使用多个卷积层在 imagenet 上实现最先进的水平,从而普及了 Convnets。
那么是什么让他们现在如此出名而不是以前呢?
只有在我们拥有大量计算资源的情况下,我们才能在最近的过去试验和利用深度学习的全部潜力。
但是,我们是否充分利用了我们的计算资源?我们能做得更好吗?
这篇帖子是关于利用张量核和自动混合精度来更快地训练深度学习网络的。
什么是张量核?
根据 NVIDIA 网站:
NVIDIA Turing 和 Volta GPUs 由 Tensor Cores 提供支持,这是一项革命性的技术,可提供突破性的 AI 性能。张量核可以加速大型矩阵运算,这是人工智能的核心,并在单次运算中执行混合精度矩阵乘法和累加计算。随着数百个张量内核在一个 NVIDIA GPU 中并行运行,这使得吞吐量和效率大幅提高
简单来说;它们 是专门的内核,非常适合特定类型的矩阵运算 。
我们可以将两个 FP16 矩阵相乘,并将其与 FP16/FP32 矩阵相加,从而得到一个 FP16/FP32 矩阵。张量核支持混合精度数学,即输入为半精度(FP16),输出为全精度(FP32)。上述操作对于许多深度学习任务来说具有内在价值,张量核为这种操作提供了专门的硬件。
现在,使用 FP16 相对于 FP32 主要有两个好处。
- FP16 需要更少的内存,因此更容易训练和部署大型神经网络。它还涉及更少的数据移动。
- 张量核在降低精度的情况下,数学运算运行得更快。NVIDIA 给出的 Volta GPU 的准确数字是:FP16 为 125 TFlops,FP32 为 15.7 TFlops 倍加速)
但是也有缺点。当我们从 FP32 转到 FP16 时,必然会降低精度。
FP32 vs FP16 : FP32 有 8 个指数位和 23 个分数位,FP16 有 5 个指数位和 10 个分数位。
但是需要 FP32 吗?
FP16 实际上可以很好地表示大多数权重和梯度。因此,存储和使用 FP32 需要所有这些额外的位只是浪费。
那么,我们如何使用张量核呢?
我检查了我的泰坦 RTX GPU 有 576 个张量核心和 4608 个 NVIDIA CUDA 核心。但是我如何使用这些张量核呢?
老实说,NVIDIA 通过几行代码提供的自动混合精度使张量核的使用变得微不足道。我们需要在代码中做两件事:
- 需要使用 FP32 进行的操作(如 Softmax)被分配到 FP32,而可以使用 FP16 完成的操作(如 Conv)被自动分配到 FP16。
- 使用损失缩放 来保留小的渐变值。梯度值可能会超出 FP16 的范围。在这种情况下,梯度值会进行缩放,使其落在 FP16 范围内。
如果你还不了解背景细节也没关系。代码实现相对简单。
PyTorch 混合精确训练:
让我们从 PyTorch 中的一个基本网络开始。
N, D_in, D_out = 64, 1024, 512
x = torch.randn(N, D_in, device="cuda")
y = torch.randn(N, D_out, device="cuda")
model = torch.nn.Linear(D_in, D_out).cuda()
optimizer = torch.optim.SGD(model.parameters(), lr=1e-3)for to in range(500):
y_pred = model(x)
loss = torch.nn.functional.mse_loss(y_pred, y)
optimizer.zero_grad()
loss.backward()
optimizer.step()
为了利用自动混合精度训练,我们首先需要安装 apex 库。只需在您的终端中运行以下命令。
$ git clone https://github.com/NVIDIA/apex
$ cd apex
$ pip install -v --no-cache-dir --global-option="--cpp_ext" --global-option="--cuda_ext" ./
然后,我们只需在神经网络代码中添加几行代码,就可以利用自动混合精度(AMP)的优势。我将下面添加的行加粗:
***from apex import amp***
N, D_in, D_out = 64, 1024, 512
x = torch.randn(N, D_in, device="cuda")
y = torch.randn(N, D_out, device="cuda")
model = torch.nn.Linear(D_in, D_out).cuda()
optimizer = torch.optim.SGD(model.parameters(), lr=1e-3)
**model, optimizer = amp.initialize(model, optimizer, opt_level="O1")**
for to in range(500):
y_pred = model(x)
loss = torch.nn.functional.mse_loss(y_pred, y)
optimizer.zero_grad()
***with amp.scale_loss(loss, optimizer) as scaled_loss:
scaled_loss.backward()*** optimizer.step()
在这里,您可以看到我们用amp.
初始化了我们的模型,我们还使用amp.scale_loss
指定了损耗比例
标杆管理
我们可以使用这个伟大的存储库对 amp 的性能进行基准测试,该存储库在 CIFAR 数据集上对 VGG16 模型进行基准测试。我只需要修改几行代码就可以让它为我们工作了。你可以在这里找到修改版的。要自己运行基准测试代码,您可能需要:
git clone [https://github.com/MLWhiz/data_science_blogs](https://github.com/MLWhiz/data_science_blogs)cd data_science_blogs/amp/pytorch-apex-experiment/python run_benchmark.pypython make_plot.py --GPU 'RTX' --method 'FP32' 'FP16' 'amp' --batch 128 256 512 1024 2048
这将在主目录中为您填充以下图形:
在这里,我使用不同的精度和批量设置训练了同一个模型的多个实例。我们可以看到,从 FP32 到 amp,存储器需求降低了,而精度基本保持不变。时间也会减少,但不会减少太多。这可能归因于简单的数据集或简单的模型。
根据 NVIDIA 给出的基准测试,自动混合精度比标准 FP32 型号快 3 倍左右,如下图所示。
来源:加速比是单精度和自动混合精度下固定历元数的训练时间比。
继续学习
如果您想了解更多关于实用数据科学的知识,请查看 Coursera 课程的 “如何赢得数据科学竞赛” 。我从卡格勒教授的这门课程中学到了很多新东西。
谢谢你的阅读。将来我也会写更多初学者友好的帖子。在 媒体 关注我或者订阅我的 博客 了解他们。一如既往,我欢迎反馈和建设性的批评,可以通过 Twitter @mlwhiz 联系到我。
此外,一个小小的免责声明——在这篇文章中可能会有一些相关资源的附属链接,因为分享知识从来都不是一个坏主意。
更快的 RCNN [1506.01497]
面向实时对象检测
来源:https://en.wikipedia.org/wiki/Object_detection作者: Mtheiler
我已经计划阅读主要的对象检测论文(虽然我已经浏览了大部分,但现在我将详细阅读它们,好到足以写一篇关于它们的博客)。这些论文与基于深度学习的对象检测相关。随时给建议或询问疑惑会尽我所能帮助大家。任何从这个领域开始的人都可以跳过许多这样的论文。当我看完所有的论文后,我也会写下它们的优先顺序/重要性。
我写这篇博客是考虑到和我相似并且仍在学习的读者。虽然我会通过从各种来源(包括博客、代码和视频)深入理解论文来尽力写出论文的关键,但如果您发现任何错误,请随时在博客上指出或添加评论。我已经提到了我将在博客末尾涉及的论文列表。
我们开始吧:)
老实说,我大约在两年前开始研究对象检测,并从更快的 RCNN、Yolo 和 SSD 等论文开始(我们将在接下来的博客中讨论这两篇论文)。这些题目让我很为难,很长一段时间都无法理解。我是深度学习的新手,没有耐心,也没有经验去研究完整的论文,也找不到任何博客能足够好地解释这些话题让我理解。在这篇博客中,我将尽可能地简化这篇文章,因此你可能会发现它有点大而且充满了理论。
快速 RCNN 和 RCNN 使用诸如选择性搜索的区域提议算法来提议图像中对象的估计位置。这些算法是当前目标检测系统的测试时间瓶颈。很直观,由 CNN 提取的特征最终用于分类并给出图像的包围盒。因此,这些提取的特征具有检测图像中的对象所需的信息。更快的 RCNN 架构基于这一观察。区域提议算法由区域提议网络代替,该网络给出具有对象的区域的估计。该区域提议网络基于 CNN,并从 CNN 提取的特征中给出区域提议。
因此,更快的 RCNN 架构是提出区域的 RPN 和使用这些提出的区域给出对象的最终边界框的快速 RCNN 检测器的组合。因为快速 RCNN 和 RPN 都需要基于 CNN 的特征提取器来执行几乎相似的任务(RPN 的最终任务是仅给出对象区域),所以我们可以使用单个特征提取器,而不是使用具有几乎相同权重的两个独立的模型。下图显示了 Fast RCNN 和 RPN 的统一结构。
快速 RCNN 网络(RPN+快速 RCNN)来源:快速 RCNN 论文作者:任
什么是锚盒
锚盒是现代物体探测器的主要部分。虽然在本文后面会讨论,但我觉得你应该在进入 RPN 之前了解一下。在对象检测中,我们试图为图像中的每个对象获得一个矩形框,因此在每个图像中有多个不同形状和大小的框。
现在,忘记早期的架构如快速 RCNN、RCNN 如何解决这个问题,而是将其视为正常的深度学习问题,类似于图像分类,我们可以使用一个带有回归头的层来预测对象的边界框。在图像分类中,我们为每个图像输出一个标签,但是在对象检测中,我们可以在每个图像中有多个对象,因此每个图像需要多个输出。我们可以通过获得多个预测而不是一个来解决这个问题,但是模型如何知道哪个边界框是针对哪个对象的呢?
快速 RCNN 和 RCNN 通过使用区域提议方法(如前所述,其具有缺点)解决了这个问题。在更快的 RCNN 中,我们使用深度神经网络来获得这些建议,因此我们可以说在某种程度上我们正在尝试使用该网络来解决对象检测的问题。因此,上面讨论的问题现在就有了眉目。
锚箱是救世主。锚定框只不过是放置在图像中不同位置的一些参考框。在我们的特征图(CNN 的输出)中为每个像素生成 k 个锚框。因此,锚盒的总数是 hwk(h*w 是特征图的输出大小)。这里是一个超参数。这些 k 锚盒有不同的大小和长宽比,这有助于覆盖各种形状和大小的对象。锚框是特定于数据集和该数据集中的对象类型的超参数(例如,在一些医学数据中,如果对象只能是单一尺寸,我们将只需要 3 个不同纵横比的锚框)。我们可以在下面看到这些锚盒。
锚箱来源:程序员搜索
不是获得一个对象的原始回归输出,而是作为锚框的偏移来计算(我们将在后面看到如何训练使用锚框的模型)。在大多数情况下,这种偏移是锚定框的轻微移动,因为这些锚定框被放置在整个图像上。检测到的对象将被多个锚定框重叠,而不仅仅是 1 个。这些冗余预测随后使用非最大抑制移除。我们的模型的输出是 4khw 维度(对于每个锚框有一个框预测,对于每个锚框也预测分类分数,给出它包含对象的概率)。这在理论上将 CNN 能够探测到的物体数量限制在 4khw,但实际上,这个数量已经足够大了。
锚盒可以通过使用不同大小的锚盒(上图中的红、绿、蓝盒子)来解决测试时使用多尺度的问题。
RPN
区域提议网络以特征地图作为输入,并输出一组矩形对象提议,每个提议具有一个对象性分数。对象性测量对象和背景之间的分数(因此,背景的分数较低,具有对象的区域的分数较高)。选择性搜索的区域建议时间为每幅图像 2 秒,而 RPN 仅为 10ms。
更快的 RCNN 使用 3 种纵横比和 3 种比例的锚盒。因此,对于特征图中的每个像素,有 9 个锚框。
该架构是一个简单的卷积层,其内核大小为 33,后面是两个完全连接的层(一个用于客观评分(分类),另一个用于建议的回归)。这个全连接层是使用 11 卷积层实现的。分类图层的输出大小应为 29(前景和背景),回归图层的输出大小应为 49(此处 9 是每个像素的锚点数量)。预测的总数现在是直观的,并且将是(4+2)9(H*W)(对于特征图中的每个像素)。
来源:快 RCNN 论文作者:任
损失函数
在快速 R-CNN 中使用的损失函数类似于快速 R-CNN,即多任务损失。
多任务损失函数求更快 R-CNN 来源:更快 RCNN 论文作者:任
在这个等式中,pᵢ是预测概率(从 cls 输出),pᵢ是基本事实。类似地,tᵢ是预测边界框,tᵢ是基本事实边界框。Lcₗs 是分类损失(对数损失),Lreg 是平滑 L₁损失。
如前所述,回归偏移是从最近的锚盒计算的。为了与区域提议技术相关联,锚盒现在充当区域提议。一张 4060 大小的特征地图,总共有 4060*9 ~ 20000 个锚盒。所有锚箱在训练时都不会造成损失。IOU 与地面真实值最高的锚和 IOU 重叠高于 0.7 的锚被分配正标签。IOU 低于 0.3 的锚标为负。既不积极也不消极的锚对训练目标没有贡献。跨界锚点也将被忽略。
培养
快速 RCNN 架构是由 RPN 和快速 R-CNN 组成的统一网络,CNN 层由两种架构共享。我们不能单独训练 RPN 和快速 RCNN(它将给出不同的权重,因此我们需要为它们中的每一个通过 CNN 两次)。作者使用了下面讨论的 4 步训练算法(也讨论了其他一些算法,你可以在论文中看到):
- 使用初始训练的 RPN 和来自图像网络的预训练权重。
- 使用由步骤 1 RPN 生成的建议来训练快速 RCNN。(此时两个网络不共享卷积层)。
- 使用来自步骤 2 的卷积层训练 RPN,并且仅更新 RPN 特有的层(卷积层的权重不更新)。
- 保持这个卷积层,对快速 R-CNN 特有的层进行微调。
在训练时,作为 RPN 输出的 20000 个锚(建议)首先通过移除跨界锚(给出 6000 个锚)来减少。应用非最大抑制(NMS)来移除阈值为 0.7 的冗余预测(给出 2000 个锚)。在 NMS 之后,使用排名前 N 位(分类分数)的建议区域。
测试
更快的 RCNN 是一种完全基于深度学习的方法,具有统一的网络,不依赖于像选择性搜索提议这样的算法。因此,图像被直接传送到网络,作为输出给出预测。RPN(20k 建议)的输出使用上一节最后一段中讨论的类似步骤进行二次抽样。
这是最重要的文章之一,也是理解未来大部分文章所必需的。如果我有不清楚的地方,请在评论中提出来,我会更新的。
和平…
参考
- https://arxiv.org/pdf/1506.01497.pdf
- https://tryolabs . com/blog/2018/01/18/faster-r-CNN-down-the-rabbit-hole-of-modern-object-detection/
- 【https://d2l.ai/chapter_computer-vision/anchor.html
- http://machinethink.net/blog/object-detection/
- https://towards data science . com/faster-r-CNN-for-object-detection-a-technical-summary-474 C5 b 857 b 46
论文列表:
- OverFeat:使用卷积网络的综合识别、定位和检测。[ 链接到博客
- 丰富的特征层次,用于精确的对象检测和语义分割(RCNN)。 [ 链接到博客
- 用于视觉识别的深度卷积网络中的空间金字塔池(SPPNet)。 [ 链接到博客
- 快速 R-CNN↓[链接到博客 ]
- 更快的 R-CNN:用区域提议网络实现实时目标检测。 ←你完成了这篇博客。
- 你只看一次:统一的,实时的物体检测。【博客链接】
- SSD:单次多盒探测器。[博客链接]
- R-FCN:通过基于区域的完全卷积网络的目标检测。【博客链接】
- 用于目标检测的特征金字塔网络。【博客链接】
- DSSD:解卷积单粒子探测器。[博客链接]
- 密集物体检测的焦点丢失(视网膜网)。【博客链接】
- YOLOv3:增量改进。[博客链接]
- 狙击手:高效多尺度训练。[博客链接]
- 标注像素和区域的高分辨率表示。【博客链接】
- FCOS:全卷积一级目标检测。[博客链接]
- 以物为点。[博客链接]
- CornerNet-Lite:高效的基于关键点的对象检测。【博客链接】
- CenterNet:用于对象检测的关键点三元组。[博客链接]
- 用于实时对象检测的训练时间友好网络。【博客链接】
- CBNet:一种用于目标检测的新型复合主干网络架构。【博客链接】
- EfficientDet:可扩展且高效的对象检测。[博客链接]
使用并行计算加快 Python 中的视频处理
python 中的多重处理
服务器(照片由伊恩·巴塔格利亚在 Unsplash 上拍摄)
如果要处理大量视频文件,可能需要几分钟到几小时,具体取决于视频的大小、帧数和帧尺寸。
怎样才能加快视频处理速度?
并行处理就是答案!
如果您正在批量处理图像,您可以利用并行处理的强大功能来加速任务。
在这篇文章中,我们将看看如何使用 python 对视频进行并行处理。
我们将从磁盘读取视频,执行人脸检测,并将带有人脸检测输出(边界框)的视频写回磁盘。
让我们开始吧。
安装依赖项
我们将需要以下包:
OpenCV: 是一个常用的计算机视觉库。在本帖中,我们将使用 OpenCV 来读写视频文件。
要在您的设备上安装 OpenCV,您可以使用 pip 命令或 apt-get 命令。
pip3 install opencv-python
或者
sudo apt-get install -y python3-opencv
FFmpeg: 是一个跨平台的解决方案,用于录制、转换和流式传输音频和视频。在这篇文章中,我们将使用 FFmpeg 来连接多个视频文件。
要安装 ffmpeg,使用下面的 apt-get 命令:
sudo apt-get install -y ffmpeg
导入 python 库
让我们导入所需的 python 库。
所用库的详细信息:
cv2: OpenCV 库读写视频文件。
**时间:**获取计算代码执行时间的当前时间。
子进程 : 启动新进程,连接到它们的输入/输出/错误管道,获取它们的返回代码。
多重处理 : 跨多个输入值并行执行一个函数,将输入数据分布到多个进程中
使用单个进程的视频处理流水线
我们将首先定义一种使用单个进程处理视频的方法。这就是我们通常读取视频文件、处理每一帧并将输出帧写回磁盘的方式。
让我们创建另一个调用视频处理器的函数,记录开始和结束时间,并计算执行管道所用的时间和每秒处理的帧数。
使用多进程的视频处理流水线
现在,让我们定义另一个利用多重处理来处理视频的函数。
上述功能的工作原理是,通常使用一个进程完成的视频处理工作现在在执行设备上可用的处理器总数中平均分配。
如果有 4 个进程,并且要处理的视频中的总帧数是 1000,则每个进程得到 250 个要处理的帧,它们是并行执行的。这意味着每个过程将创建一个单独的输出文件,因此当视频处理完成时,有 4 个不同的输出视频。
为了组合这些输出视频,我们将使用 ffmpeg。
创建一个管道来运行视频的多重处理,并计算执行时间和每秒处理的帧数。
结果
我在搭载 Ubuntu18.04 操作系统的联想 Yoga 920 上运行了这个实验。该设备上可用的逻辑处理器数量为 8。
****
从这个实验中,我们可以观察到,当使用所有内核处理视频时,每秒多处理 2 倍的帧。
发现这篇文章有用吗? 在下面留下你的想法作为评论。
关于作者
Sabina Pokhrel 在 Xailient 工作,这是一家计算机视觉初创公司,已经建立了世界上最快的边缘优化物体探测器。
用于文本分类的快速文本
我探索了一种用于多类分类的快速文本分类器。
在我的上一篇文章中,我探索了两种不同的 NLP 模型来完成文本分类的任务。虽然我没有计划把它做成一个系列,但我在 NLP 领域偶然发现了一些更新的模型,并决定写下它们。
如果你愿意,可以查看 第一篇 出来,在这篇文章中,我重点训练了自己的单词嵌入,并将其与预先训练的手套单词嵌入模型进行比较。
我们将使用 fastText 分类器对堆栈溢出问题的质量进行分类。如果你跟随本教程,请从 这里 下载数据集。
什么是 fastText?
fastText 是一个开源库,由脸书人工智能研究实验室开发。它的主要重点是实现文本分类和表示任务的可扩展解决方案,同时快速准确地处理大型数据集。
Marc Sendra Martorell 在 Unsplash 上拍摄的照片
我强烈推荐阅读脸书自己的 博客文章 和研究论文 关于 fastText 背后的动机,并了解它是如何实现其开发目的的。
根据他们的研究,fastText 在准确性以及训练和测试时间方面都令人印象深刻,超过了以前发布的最先进的模型。
它通过采用两种方法来解决文本的分类和训练单词表示来实现这种计算效率和准确性。
1.分级 Softmax
在多类分类问题中,Softmax 函数通常用作激活函数来输出给定输入属于 k 类的概率。
当存在大量类别并且数据中存在类别不平衡时,分层 Softmax 被证明是非常有效的。在这里,类被安排在一个树形分布中,而不是一个平面的、类似列表的结构。
分层 softmax 层的构建基于霍夫曼编码树,该编码树使用较短的树来表示更频繁出现的类,而使用较长的树来表示更罕见、更不频繁出现的类。
一个给定的文本属于一个类的概率是通过沿着不同分支的节点的深度优先搜索来探索的。因此,具有低概率的分支*(或者等价地,类)*可以被丢弃掉。
对于存在大量类别的数据,这将导致复杂度的极大降低,从而与传统模型相比显著加快分类过程。
2.单词 n-grams
仅使用一个单词包来表示文本忽略了重要的连续信息。对于大型数据集来说,考虑词序最终会导致计算开销很大。
因此,作为一种令人愉快的媒介,fastText 结合了一包 n-grams 表示和单词向量,以保存每个单词附近出现的周围单词的一些信息。
这种表示对于分类应用非常有用,因为几个不同单词串在一起的上下文含义也会导致该段文本所呼应的特定情感。
现在我们已经了解了 fastText 的一些主要特性,让我们来看看如何实现 fastText 和实现分类任务。
装置
遵循这些来自博览会的 安装和设置说明 。我们将使用 Python 实现这个项目。
数据准备
为了训练和评估这个分类器,我们必须以 fastText 期望的格式准备数据。
fastText 首先期望类别,在每个类别前加上前缀’ label ',然后是输入文本,像这样,
__label__positive 我非常喜欢这家餐厅。很想再去看看。
当然,我们将应用一些 NLP 预处理技术来删除不需要的符号、标点符号并将文本转换为小写。
下面的代码负责将前缀“label”添加到 category 列中的每一行。我们将使用 gensim 的simple _ preprocess方法来标记我们的问题并移除符号。
培训和评估
将数据帧保存为文本文件后,下一步是训练和测试我们的模型。
为了提高我们模型的性能,将 字组 参数设置为 2。换句话说,该模型是在二元模型上训练的,而不是考虑单个单词。
有两种方法来测试我们的模型,这两种方法略有不同。
预测 方法用于预测给定输入最可能的标签。在上面的代码中,我选择了一个属于类别“HQ”的观察,并针对它测试了模型。正如你在下面看到的,它正确地预测了“HQ”的类别,并且以 95%的概率做到了。
用一个句子来测试模型。
我使用 测试 方法在整个测试数据集(15000 个样本)上评估我的分类器,得到的精度值为 0.83,召回值为 0.83。
模型在测试集上的性能。
精度是分类器在所有标签中预测的正确标签的数量,召回是在真实标签中成功预测的标签的数量。
在我们的例子中,由于我没有指定参数 k 的值,默认情况下,模型将只预测它认为给定输入问题所属的 1 个类。
结论
与我以前训练自己的嵌入和使用预训练手套嵌入的模型相比,fastText 表现得更好。
fastText 比在多维单词向量上训练神经网络快得多,在测试集上也取得了良好的性能。
谢谢你过来看这篇文章。请关注本系列中的更多文章,因为我将发布更多的教程并学习更新的模型。
祝您愉快,在我的下一篇文章中再见!
使曲线变长
透过个人 Fitbit 数据的镜头看社交距离
Fitbit 心率数据——红框显示了我第一周的社交距离
面对前所未有的经济困难和对抗新冠肺炎疫情的社会距离措施,许多纸上谈兵的流行病学家正在分析社会如何“拉平曲线”。我不会在日益增长的喧嚣中加入我的声音,我会在个人旅程中利用我在健康指标和物联网方面的专业知识来评估社交距离行为是否可能“增肥我的曲线”。
Fitbit 数据
下载您的数据并生成该分析的指令和代码位于https://github.com/zwrankin/blog/tree/master/fitbit。你也可以参考 Fitbit API 文档或者其他关于下载你的 Fitbit 数据的媒体文章。我下载了每日活动统计数据(如步数和睡眠时间)以及每分钟的心率数据。
日常活动分析
我从 Fitbit API 下载了最近两个月的数据,并认为 3 月 14 日是我社交孤立的开始。首先,我绘制了日常活动指标的分布,区分了工作日和周末的观察结果。下面是久坐时间和睡眠效率的曲线图。
接下来,我使用普通最小二乘线性回归估计了社交距离对各种指标的影响,这是一种适用于有限数据和高度可解释性的方法。由于这不是火箭科学(或疫苗开发),我在分析中做了一些改动。首先,我没有确保我的数据的正态性(是的,步骤是计数数据,但由于高方差,它们不是泊松分布的)。第二,我很乐意“p-hacking ”,不去纠正多重比较。使用这个等式
y = β₀ + β₁weekend +β₂covid
我们将 β₀ 解释为基线工作日度量, β₂ 解释为 covid 社会隔离对度量 y 的绝对影响(以 y 为单位,如步数),将 β₂/β₀ 解释为社会隔离的相对影响(如-0.2 为工作日基线步数减少 20%)。计算相对效果有助于比较不同数量级的指标(如步数和静息心率)。
95%置信区间的相对效应。-0.2 表示因社交距离而减少 20%
鉴于我只有 7 天的社交隔离数据,并且这些指标具有很高的方差,因此许多数据没有达到统计学意义也就不足为奇了。然而,有一个强烈的信号表明,在社交隔离期间,我的身体活动减少了 20%,睡眠指标有所改善(坐立不安减少了 20%,睡眠效率提高了)。前面的分布图有助于说明回归结果。
心率模式可视化
最后,我研究了心率时间序列数据,以发现每日汇总统计数据没有捕捉到的活动模式的变化。
Fitbit 工作日心率数据——红框显示了我第一周的社交距离
一些明显的模式是缺乏早上的活动(由于没有早上的自行车通勤和健身课)。此外,很少有长时间的有氧运动——例如,三个周四晚上两个小时的心跳加速来自极限飞盘混战。周末数据(未显示)显示了类似的活动减少模式。
结论
尽管标题轻率,但我认为积极主动地解决我们的精神和身体健康问题对于抵御围绕这个疫情的焦虑至关重要。虽然我肯定怀疑我的身体活动减少了,但这种可见性重新激发了我的锻炼动机。我将继续慢跑,并增加对 Down Dog 应用的使用,该应用在新冠肺炎期间免费提供所有优秀的有氧运动、力量和瑜伽课程。我希望将来写关于使用序列分析或神经网络的算法模式识别的文章。
想要成为数据科学家的人最喜欢的播客
一直在学习
你应该听的数据科学和非数据科学播客
我每周听大约 30 个小时的播客(我以 1.4 倍的速度听)。播客是我除了孩子之外的头号娱乐来源,也是我工作中日常学习之外的教育来源。在过去的 10 年左右的时间里,我一直在听我的音乐列表(真的有那么久吗)?这是我最喜欢的播客列表。其中许多都是数据科学播客列表中的佼佼者,其他的你可能从未听说过。
在 Unsplash 上由 Austin Distel 拍摄的照片
数据科学
线性题外话——这个节目由本·贾菲和凯蒂·马龙主持,本·贾菲是网飞的前端工程师,以前在脸书工作。每集都是 15-30 分钟的讨论,由 Katie 就与数据科学相关的单一主题向 Ben 授课。凯蒂会讲得很深入,但因为她是在向本解释,她通常会用很棒的类比来帮助本和观众理解。如果你想学习数据科学,这是为你准备的播客。
对抗性学习——乔尔·格鲁什和安德鲁·穆塞尔曼主持这个有趣的播客。他们的剧集充满了 Joel 和 Andrew(有时会有客人加入)来回讨论与数据科学和生活相关的不同主题。他们从不深入技术层面。如果你喜欢讽刺和幽默,你会发现这两个很搞笑。他们的起源故事很有趣。安德鲁和乔尔是在安德鲁写了一篇批评乔尔的《从零开始的数据科学》初稿的文章时认识的。乔尔认为安德鲁是个混蛋。他们不知不觉地在一次聚会上相遇,并意识到他们长期以来对彼此的仇恨(见讽刺)。
Data Crunch — Data Crunch 是一家营利性公司,培训组织如何有效地使用数据。他们有一个由吉乃特·梅索特和柯蒂斯·塞尔主持的月度播客。吉乃特和柯蒂斯带来了在工业中使用数据科学的客人。他们覆盖了医疗保健、物流、管理、公用事业等多种行业。从大技术之外的角度来看待数据科学令人耳目一新。
数据怀疑论者——这是我大约 6 年前开始听的第一批纯数据科学播客之一。我看到 Kyle Polich 在一次会议上发言,他在发言结束时说他有一个关于数据科学的播客。不久后我开始跟踪他。最初几年,凯尔向妻子解释各种统计、数学和机器学习主题。最近这种情况越来越少了。现在,他要么自己解释一个概念,进行采访,要么与两位共同主持人 Lan 和 George 一起评论一些期刊文章。
不那么标准差——如果你对围绕数据科学、统计学、R 编程和咖啡的八卦感兴趣,这个播客是为你准备的。Stitch Fix 的希拉里·帕克和约翰·霍普斯金彭博公共卫生学院的罗杰·彭关系非常融洽。当他们谈论 R 的政治,思考什么是数据分析,讨论 R 包,比较数据科学和设计时,他们的友谊被展示出来。
实用人工智能(Practical AI)——本榜单另一播客 Changelog 的分支,实用人工智能(Practical AI)带领嘉宾深入探讨人工智能在工业中的应用。从芯片设计,自然语言处理,军事应用,该节目涵盖了许多角度。共同主持人克里斯·本森和丹尼尔·怀特纳克对所有人工智能都有真正的好奇心。Chris,Lockheed Martin 和 Daniel,SIL International 有深厚的技术背景,但他们的播客方法适用于所有技能水平。
TWIML AI 播客(之前是本周机器学习&人工智能) — Sam Charrington 是该节目的主持人,在聚会、会议和其他教育内容中领导 TWIML 品牌。萨姆采访了广泛的研究人员和行业专家。TWIML 给这个列表带来了更多的前沿研究,并且比这个列表中的其他一些播客更倾向于采访学者。
走向数据科学 —您知道最受欢迎的数据科学媒体出版物也有播客吗?如果你不知道,你现在知道了。该播客由 SharpestMinds 的联合创始人 Jeremie Harris 主持,讨论了许多关于数据科学入门的话题。Jeremie 和其他嘉宾一起探讨了什么是数据科学,如何找到一份数据科学家的工作,以及人们如何在工业中使用数据科学。Jeremie 对这些话题如此投入是有道理的,因为他的公司 sharpes minds 是一个导师项目,其模式是基于学员获得一份工作,sharpes minds 从第一年的工资中提成。
编程/数据工程
数据工程播客——托拜厄斯·小萌每周带他的听众体验不同的数据工程技术。他带来了来自不同开源和专有技术的专家,并允许他们解释他们的产品。来宾展示用例、不同的实现、何时使用以及何时不使用他们的技术。
Google Cloud Podcast——五年来,GCP 播客有过几次不同的主持人,但内容一直保持不变。该播客解释了如何与谷歌开发人员、产品经理和客户最好地利用谷歌云产品(或谷歌云上的开源产品)。这几集相当快,涵盖了从登录、计费、机器学习到游戏开发的所有内容。
Stack Overflow 播客——另一个播客,从 Stack Overflow 的创始人乔尔·斯波斯基( Joel on Software ) &杰夫·阿特伍德(编码恐怖)开始就有各种各样的主持人。这个播客确实深深植根于一些运行时间最长的软件开发博客。播客过去是针对堆栈交换网络上发生的不同事情的,但后来专注于编程、构建软件和当前事件。有一点没有变,那就是他们的幽默感。这是我最喜欢的播客之一,因为它让我发笑。
这是我开始听的第一批节目播客之一。超过 400 集,10 年后,Adam Stacoviak 和 Jerod Santo 已经把他们的播客变成了一个网络。他们旗下还有另外 8 个播客(你之前读过《实用人工智能》)。Changelog 深入探讨了与软件开发领导者的对话。他们专门让开源创始人和贡献者来谈论他们的项目和旅程。如果你对项目(或项目背后的人)是如何产生的感兴趣,这是给你的播客。
软件行业
规模大师——雷德·霍夫曼(LinkedIn、Paypal、Greylock 投资者)采访标志性公司的创始人和高管。节目一开始采访了硅谷的名人。在最初几集之后,里德扩大了他的嘉宾阵容,包括来自投资公司、音乐家、慈善机构、餐馆和制造业的高管。里德每周都教他的听众如何建立标志性的公司。
OpenView Build — OpenView 是一家专注于技术的标志性风险投资公司。他们的 Build 播客关注于如何打造一款人们喜爱的产品。每一季都有一个新的关注点,如定价、产品领先增长或持久打造。这是产品经理和产品营销人员必须要听的。
生长 TL;博士——本播客关注增长型营销。主持人基兰·弗拉纳根(Kieran Flanagan)和斯科特·图斯利(Scott Tousley)在 Hubspot 长大,向业内一些最优秀的人学习。我发现基兰和斯科特的戏谑娱乐。基兰是一个有趣的爱尔兰人,斯科特是一个在世界各地工作的流浪者。关注增长意味着他们的内容会频繁地进入数据分析领域。
官方 SaaStr 播客——我喜欢杰森·莱姆金。莱姆金是一名风投,前创始人,也是多产的博主 /Quora 海报。他将自己对 SaaStr】的热情转化为一项名为 SaaStr 的业务。SaaStr 发布了大量的内容、课程、播客、博客、视频和会议。该播客由哈里·斯特宾斯主持,莱姆金偶尔也会出现。来自世界各地的科技高管和风投在播客中接受采访,向大众传授如何经营 SaaS 公司的智慧。
Ycombinator —我是 Ycombinator 的粉丝男孩。他们知道如何制造可扩展的业务。这个节目暂停了,因为它现在正处于主持人之间。之前的播客由克雷格·坎农主持。他从创业界引进了各种各样的人。他们讨论创业成功的要素。克雷格已经采取了周期性的分歧路径采访物理学家和其他播客主持人。克雷格在许多场合提到他对乔·罗根的播客的喜爱,我认为这是对他自己的播客《创业 JRE》的一个很好的比较。
创业学校(y combinator)——y combinator 创建了一个名为创业学校的新项目,这是一个免费的在线项目,专注于教创始人如何创办公司和发展公司。他们把讲座变成了播客,非常棒。这些讲座的精简版可在 Ycombinator 播客订阅上获得。
经济学
魔鬼经济学——来自同名畅销书的合著者史蒂芬·都伯纳的播客让我迷上了播客。杜布纳涵盖了经济学,但不知何故,杜布纳和他的团队设法把每个话题都变成了经济学话题。当然,有非常传统的经济话题,如租金控制、小费和赌博,但它们也涉及缩短会议时间、如何改善教育、如何改变你的想法以及为什么有些人比其他人更成功(勇气)等话题。这个节目确实对所有这些话题采取了一种数据方法。作为一名数据专家,你可以学到很多关于如何思考数据的知识。
星球货币——来自 NPR,星球货币是魔鬼经济学简装版。它们涵盖了许多相同类型的主题。《星球货币》的长度只有《魔鬼经济学》播客的一半。如果你的通勤路程很短,这是一个简单、快捷的倾听方法。
商业
职业工具——我不知道 10 多年前我是如何偶然发现职业工具的,但它教会了我比职业生涯中任何事情都要多的职业知识。职业工具是这个列表中的下一项管理工具的姐妹播客。该节目由前高管、现管理顾问迈克尔·奥兹恩(Michael Auzenne)和马克·霍斯特曼(Mark Horstman)创作。他们创造了职业工具,其使命是让每个专业人士都富有成效。他们的剧集包括如何准备年度评估,如何与你的人际网络保持联系,如果你在工作中哭了该怎么办,如何问聪明的问题,等等。他们有一整套关于如何有效采访的播客。如果你想成为一名更好的专业人士,请收听本期播客。
经理工具 —如前所述,经理工具和职业工具是姐妹播客。都是由迈克尔·奥赞恩和马克·霍斯特曼创作的。职业工具致力于让每一个专业人员高效工作,而经理工具致力于让每一个经理高效工作。经理工具是一对一的巨大支持者,我肯定他们在每个播客上谈论他们。一对一是他们所谓的三位一体经理工具的一部分,一对一,反馈,辅导和授权。他们至少有一些关于三位一体主题的播客。像职业工具一样,经理工具有数百集,范围广泛,如如何进行面试,项目管理,设定目标,新员工入职,办公室政治,以及如何处理电子邮件。大多数经理都没学过如何管理。如果你想成为一名高效的经理,你需要开始自学。管理工具是很好的第一步。
在的影响下——来自 CBC 播客,Terry O’Reilly 探讨了营销对我们生活的影响。奥赖利挖掘了疾病品牌、在线评论、以发明者命名的产品、漫画书、商业模仿等背后最有趣的真实生活故事。奥赖利是一个伟大的故事讲述者,这是一个信息丰富,简单的 20 分钟播客,你可以和全家一起听。
其他
修正主义历史 —我不得不把我最喜欢的一个播客列入这个列表,即使它与成为一名数据科学家没有任何关系。著名作家马尔科姆·格拉德威尔用哲学的方法讲述故事。他从每个角色的角度出发,把他的每个故事分解成逻辑部分。为什么白人警察要射杀黑人小孩?马尔科姆擅长播客,擅长讲故事。
排名
也许你没有时间听 20+的播客。使用下面的列表进行优先排序。
教我数据科学
教我做生意
- 创业学校(Ycombinator)
- 官方 SaaStr 播客
- 音阶大师
- 职业工具
- 生长 TL;博士
让我思考
轻松聆听
非洲的外国直接投资:数据告诉我们什么?
整个 20 世纪所设想和应用的外国援助,长期以来一直是提高当地人口生活水平的一种手段,并为国内无法提供资金的某些社会领域提供资源。在最好的情况下,外国援助的存在是为了避免排挤当地投资。最好的援助是那些可行性和前景在西方民主国家的智囊团和办公室里被长期研究和模拟的。外国援助在紧急救援、难民危机以及作为提高低收入和中低收入经济体生活质量的临时手段时发挥了重要作用。
为了有效,更重要的是为了可持续,援助就其本质而言,必须作为任何类型问题的短期和临时解决办法而存在;无论是自然灾害还是打击腐败,腐败往往与国民经济密不可分。美国国际开发署是世界上最重要的援助提供者之一,它以自己的名义声明,是一个促进国际发展的机构,其目标是发展而不是援助。问责制和可核查性是援助运动或倡议中最令人痛苦的荆棘。2013 年,欧盟暂停了对乌干达的财政援助,因为几名部长承认挪用了价值超过 1300 万美元的援助。在有报道称阿富汗政府浪费了数千亿美元的重建资金后,正在进行的重建阿富汗的多国努力面临着毁灭性的批评。
尽管外援容易被滥用,但不管统计数字继续显示什么,外援将始终在国际发展中发挥作用。当援助活动持续时间越来越长,范围越来越大时,滥用权力和腐败的倾向似乎也随之增长。也许有人会说,援助最好是作为对人道主义危机的反应或受益者经过严格审查的短期项目来设计和实施的。虽然援助有助于人民和社会,但它并没有增强他们的能力,也不是为了增强他们的能力。赋权的形式是提供获得自给自足的工具和手段。自给自足只有在年复一年地发展技能和改进工艺后才能实现。发展技能导致生产手段,生产手段导致工业的产生。
如果没有工业化的生产方式,没有几代人不断改进国际贸易流程,没有一个国家能够从中等收入发展到高收入。当经济体缺乏工业竞争力时,外国直接投资可以催化工业增长。利用世界银行和联合国贸易和发展政策会议的公开数据,我们将看看外国直接投资是如何影响撒哈拉以南非洲国家的。该地区的构成属于世界银行的低收入、中低收入和中高收入类别。
图 1
r Script->https://github . com/stephenstirling/FDI-in-Africa-Blog-Post-8-/blob/master/GDP % 20 plot % 20WB。R
作为一个整体,撒哈拉以南非洲成员国的国内生产总值在 1990 年至 2016 年间出现波动。然而,在整个非洲大陆,外国直接投资流入占经济的比例一直在稳步上升。尽管如此,与世界其他地区相比,该地区吸引的外国直接投资水平相对较低。从 2010 年到 2016 年,非洲大陆仅吸引了全球外国直接投资的 1.8%。非洲的外国直接投资似乎存在矛盾。非洲大陆的部分地区自然资源丰富,同时以全球标准衡量,提供了相对廉价的劳动力。非洲的投资回报率也高于世界大多数地区,T4 为 11%,而全球平均回报率为 7.1%。然而,一些瓶颈仍然存在,例如政治不稳定和对人力资本的担忧。
图 2
r script->https://github . com/stephenstirling/FDI-in-Africa-Blog-Post-8-/blob/master/Inward % 20 FDI。R
然而,图 1 向我们表明,一些国家正在接受外国直接投资,因为它继续占其国内生产总值的较大部分。过去十年,随着中国成为基础设施项目的主要投资者,非洲的外国直接投资有所增加。在 2018 年中非合作论坛上,中国宣布以 600 亿美元的信贷额度、赠款、无息贷款和投资融资的形式提供新的资金。新的资本使得货物以公路和铁路的形式更容易流动,增加了工业生产能力,随之而来的是一个新的中产阶级的崛起。
外国投资行为可能会因为是在利他主义精神下构思的而受到称赞,然而,这似乎是既定目标之外的副产品。外国直接投资的决策是精心规划和分析其投资回报率的可行性。当外国劳动力能够以相对较少的初始投资完成与国内劳动力相同的任务时,外国直接投资的前期费用往往变得具有成本效益。在整个 20 世纪,中国在很大程度上充当了世界工厂的角色,这主要是由于其廉价的劳动力和高效的生产方式。随着 20 世纪 80 年代开始大量工业化,中产阶级开始出现。虽然中国的工业转型没有被描述为一场革命,但它的转型扩大了其人力资本,并使数百万人摆脱了赤贫。新资本让中国有能力投资海外,过去 20 年,中国外包的一个重要来源是非洲。
外国直接投资最明显的受益者是受投资项目影响最大的行业。图 2 超越了明显的接受者,着眼于微观层面的利益可能与外国投资相关的国家。使用 2018 年的数据,从这 33 个撒哈拉以南国家的总数中出现了三个不同的集群,埃及似乎是一个值得注意的异常值。
蓝色集群代表 2018 年获得外国直接投资最多的国家,以及人均国内生产总值最高的国家。这个组织中的国家人口相对较少,每个成员的人口不到 300 万。虽然不是结论性的,但这三个因素表明,外国投资可能在一定程度上有助于大量民众的财务福祉。当人口较少时,投资项目的长期效应对居民的影响更大。在以商品为基础的经济体中,很大一部分人口可能参与工业领域,受外国直接投资的影响更大。
组成绿色集群的成员国在外国直接投资流入方面优于红色集群,但不一定在人均国内生产总值规模方面。虽然人口在外国直接投资渗透到人口各个部分的方式中起着相当大的作用,但还有许多其他因素在起作用。X 轴和 Y 轴的交叉点附近会出现一个红色的国家子聚类。这组国家的经济对外国投资者的吸引力较低。这可能是由于几个因素,如专制政府,缺乏自然资源,或缺乏投资回报率。
随着非洲政府在某些情况下与腐败、通货膨胀和内战作斗争,那些拥有自然资源和职能政府的政府将开始扩大经济差距。聚集在该图最底部附近的国家可能会继续接受外国实体的援助,但是,在对海外投资者具有吸引力之前,可能需要进行改革或革命。任何有意义的改变都需要来自国内。一旦发生这种情况,它们的外国直接投资规模将大幅上升。虽然外国直接投资不会解决所有问题,但它具有潜力,正如我们所看到的那样,可以催化生产方式并带来经济扩张。
特征工程
亚历克斯·康德拉蒂耶夫在 Unsplash 上的照片
了解数据科学工作流程中最重要的步骤
介绍
特征工程是一套应用于数据科学的技术,旨在确保模型能够正确使用数据。它是科学和艺术的结合,可以说是数据科学工作流程中最重要的步骤,因为它是带来最大价值的步骤之一。
掌握它需要大量的学习和实践:您不仅应该理解特征变换背后的数学原理以便操作它们,而且还需要知道在每种情况下应用哪种类型的变换。我们现在将介绍适用于不同类型数据的主要转换,以及如何进行这些转换及其结果。
数字数据
二值化
这意味着将一个数字变量转换成二进制变量:例如,假设我们使用天气数据来预测销售,我们有关于降雨量(以毫米为单位)的信息。与其将此信息用作连续变量,不如采用一个二元变量,其中 0 =无雨,1 =任何降雨量。这样,你失去了信息,因为你不再知道降雨量,但在某些情况下,这些信息可能是不相关的。
扔掉
在前面的例子中,我们没有使用二元变量,而是将原始变量转换为分类变量,其中 0 =无雨,1 =小雨,2 =大雨,3 =暴雨。垃圾箱的数量和大小可能会有所不同,您可能需要尝试几次才能找到“正确的”垃圾箱。查看变量分布(直方图)有助于找到合理的分界点(如四分位数)。
对数变换
想象一个带有直方图的变量,如下图所示(实际数字无关)。
X 的直方图
正如我们所看到的,这个变量高度集中在一个尾部(重尾分布),这可能会给一些模型增加一些偏差和误差。为了解决这个问题,我们可以应用一个 log 转换,也就是说,我们可以使用一个新的变量:log(X ),而不是在模型中使用变量 X。我们的新变量 log_X 的分布更加“良好”:
对数直方图(X)
对于大多数模型来说,这种分布通常会产生更好的结果(想想线性回归——当我们的观察值不全部集中在 0 附近时,拟合直线更容易)。
电力转换
幂变换本质上是对你的特征 X 应用一个幂函数,比如平方或平方根。这有助于模型考虑要素和目标变量之间的非线性交互。
缩放比例
当您使用为变量分配不同系数的模型,并且希望稍后解释这些系数时,缩放特别有用。如果你的变量不在同一个范围内,就很难比较它们之间的系数。
最流行的缩放方法叫做标准化。特性标准化创建了一个新变量,其平均值为 0(将变量的分布移至 0 附近),方差为 1(缩放变量的方差)。方法是使用下面的公式,其中 std_dev(x)表示 x 的标准偏差:
然后,您的变量将采用负值和正值,分布在 0 附近。
如果不希望变量为负值,可以使用另一种流行的缩放方法最小-最大缩放,其公式为:
这一次,您的新变量将介于 0 和 1 之间,从而更容易解释它的新值和您的模型生成的任何系数。
文本数据
清洁
文本数据包含许多干扰,在我们尝试更高级的转换之前,应该先处理这些干扰。您应该处理大写/小写字母,通常是将所有字母都变成小写,但在处理含义可能不同的情况时要小心(“企鹅”和“企鹅”的意思完全相同,而“房子”可能表示电视节目而不是实际的房子)。
文本数据的另一个清理步骤是去掉停用词,这些词本身没有太多意义,比如“是”、“是”、“在”、“for”等。有许多不同语言的停用词公共列表,可以直接从 Python 和 r 的库中访问。
最后,还有清理多余的空间,修复打错的单词,等等。
堵塞物
一般来说,词干就是把一个词转化成它的词根。例如,单词“runs”、“runner”、“ran”、“run”都会变成“run”。有时它是一个有用的特性,但有时它也可以改变单词的含义(“新”和“新闻”不是一个意思),所以在应用这种转换之前,请考虑您的具体情况。
词汇袋
处理文本数据比处理数字要困难得多,所以大多数处理文本的方法都是基于将文本转换成数字。最基本的方法之一是将文本视为“单词包”,这意味着我们计算每个单词在文本中出现的次数,将这些计数转化为向量,并使用这些向量来比较不同的文本。这种方法的问题是显而易见的:单词可以有不同的含义,这取决于它们出现的顺序,或者它们前面的单词。例如,考虑下面两个句子:“一个人吃了一个汉堡”和“一个汉堡吃了一个人”。两者都有完全相同的字数,并且会以相同的方式表示,尽管它们的意思明显不同。为了解决这个问题,一个常见的解决方案是创建 n-grams 包,这意味着,我们不是计算单词,而是计算一起出现的单词集(通常最多 2 或 3 个单词)。因此,在我们的例子中,我们将计算“一个人”、“一个吃了的人”、“一个汉堡包”、“一个人吃了的人”等的出现次数。
TF-IDF
TF-IDF 代表词频—逆文档频率,这是一种考虑单词在一个文本中相对于其他文本出现的方式。假设我们正在分析特朗普的推文,我们想知道他使用最多的词是什么。如果我们只看简单的字数统计,我们会得到相当无聊的结果:像“和”和“我”这样的词会出现,因为它们很常见。问题是:它们在任何地方都很常见,而不是因为特朗普特别经常写它们,这就是 TF-IDF 可以帮助我们的地方。它可以用不同的方法计算,但一个简单的方法是:
通过这种方式,我们对不常用的词给予更多的权重,帮助我们看到与其他人相比特朗普使用得更多的词。这一次,我们可能会得到像“美国”、“国会”和“伟大”这样的词。
分类变量
一键编码
同样,我们的目标是把一个非数字变量变成一个数字变量。一键编码创建了 n 个新变量,其中 n 是原始变量中唯一类别的数量。例如,如果我们有一个性别变量,我们将只有两个类别:“男人”和“女人”。然后,我们将用两个新变量替换性别变量:男人和女人,如果观察值属于各自的性别,则这两个变量的值为 1:
虚拟编码
你有没有注意到上面的一个变量是多余的?我们可以只保留其中一个而不丢失任何信息。如果去掉女人一栏,还是可以知道这个人的性别(如果不是女人,那就是男人)。虚拟编码正是这样做的:
降维
主成分分析
PCA 本身就是一门学科。如果你想更好地了解细节,请查看这篇文章。
总的来说,PCA 是一种在保持尽可能多的信息的同时降低维度的方法。它采用原始特征(也可称为尺寸),并基于原始尺寸的投影创建新特征。你从 30 个维度开始,最终可以得到 2、3、4 或 18 个维度,它们被称为主成分。您将在减少维度和保持信息和方差之间进行权衡。保持至少 80%的原始方差是一个常见的基准,但它确实取决于您的应用程序。
结论
特征工程是一套在建模前准备数据的技术。在保留相关信息、将数据保持在可处理的大小以保持合理的计算时间和可解释性之间有许多权衡。确保您理解所用技术背后的数学原理,以了解它们是否适用于您的具体情况,并为这一步留出足够的时间,因为这是进行数据科学时最重要的步骤之一。最后,确保您在步骤之间迭代:在您完成第一个模型之后,在运行另一个模型之前,回到这一步,看看是否有可以改进的地方。
新冠肺炎面板数据的特征工程和集成
社会经济因素如何影响新冠肺炎在美国的传播?
随着新冠肺炎席卷全球,我们密切关注着新病例数量的攀升及其对个人、家庭和国家的影响。有大量关于新冠肺炎的信息涌入,并有大量的分析工作来理解这些数据。目前,Kaggle 社区有两个新冠肺炎竞赛:流行病学数据的预测分析和科学文献中关键问题的 NLP 研究。这两种方法都利用来自单个域的数据。
我认为,要深入了解新冠肺炎的情况,我们还需要研究来自多个领域的数据之间的相互作用。特别是,我想回答以下问题:
影响疫情传播的因素很多。他们哪个重要?
如何整合不同领域的 COVID19 相关数据?
我们的团队(、 Mandy Gu 、、、 Iris Wan )在新冠肺炎挑战赛在加州大学戴维斯分校进行拍摄。我们一起整合了美国的流行病学和社会经济数据,并执行特征工程。然后,我们进行了线性回归,以调查哪些因素对新冠肺炎的传播有影响。
背景
我们重点关注美国的情况,因为它是世界上确诊病例最多的国家,而且传播势头没有减弱。到目前为止,新冠肺炎已经横扫了美国所有的州,对纽约州的打击最大。
美国东部时间 4 月 20 日凌晨 2:25 最后更新资料来源:约翰霍普金斯 CSSE *注:CSSE 声称其数据依赖于多个来源的公开数据。
从上面的图像中,我们还观察到疫情在不同州的不同分布。具体来说,让我们放大纽约、华盛顿和加州之间的利差比较。
纽约和加州每百万人中有数千例确诊病例
我们看到病毒在纽约的传播速度大大超过了华盛顿和加利福尼亚。造成这种差异的因素很多。我们首先想到的是社会距离政策,这在中国非常有效。这三种状态有非常不同的实施社会距离的时间表。虽然加州在 3 月初先发制人地推出了社交距离政策,但纽约直到 3 月中旬确诊病例激增时才实行社交距离。除了社会距离,我们还考虑了其他因素,如各州的医疗资源、人口老龄化和旅行活动,这些因素都可能在新冠肺炎在不同州的不同传播中发挥作用。
问题定义
我们提出一个框架,将相关因素与新冠肺炎的传播联系起来。
将社会经济因素与新冠肺炎传播联系起来的框架
在我们的分析范围内,我们包括以下因素:社会距离、人口统计、医疗资源、交通和教育。我们使用新冠肺炎确诊病例的每日增量比率来定义疫情的传播。作为概念证明,我们执行线性回归,将因子(X 特征)映射到增量比(Y)。
数据源
我们从广泛的来源收集数据。以下是我们使用的州级数据的完整列表:
新冠肺炎确诊病例数:约翰霍普金斯 CSSE 日报
社会距离政策指数:khakie economics
人口老龄化:【census.gov
人口密度:维基百科
平均医疗主体成本:数据。医疗保险-链接 2
手术质量:数据。医疗保险-链接 1
机场数量:环球航空
教育指数:USA.com
特征工程
现在,让我们深入研究一下如何将原始数据转换成 X 和 Y 值。我们首先将我们的 X 特征分成美国不同州内的时不变信息和时变信息。
以加利福尼亚州为例,时间不变要素包括加利福尼亚州 65 岁以上人口比例、人口密度、医疗资源、机场密度和教育指数。
我们如何将原始数据转换成 X 和 Y 值
我们使用给定状态的状态式社会距离政策作为时变特征。重要的是,我们根据给定州的政策来划分时间线。在每个分段的时间间隔中,社交距离政策(例如,禁止聚集、关闭公共场所)保持不变。然后,对于每个时间间隔,我们计算确诊病例的每日增量比率并取平均值,得到平均增量比率(AIR),我们的 Y 变量。
通过这个过程,我们得到了关于状态信息和策略配置的模型特征,以及我们的模型目标变量 AIR。在开始建模之前,我们先来看看最终的数据框是什么样的:
在我们的数据框架中,我们包括了 14 个拥有社会距离数据的州。这些州包括新冠肺炎影响最大的纽约州、新泽西州、加利福尼亚州和华盛顿州。对于每一行,我们有各州的社会距离政策,这些政策的开始日期,其他州的信息,以及平均增长率。
对于每个策略配置,我们记录其开始日期。CA 的数据可以追溯到 1 月份,并在 3 月 5 日推出了第一个社交距离政策。
请注意,收集大小限制是一个序号变量,范围从 1 到 7。每个级别描述了策略的严格程度。0 表示没有聚集大小限制。1 表示最不严格的限制(禁止大于 1000 的聚集)。7 表示最严格的限制(禁止大于 10 的聚集)。
最后,我们有 AIR(平均增量比率),它是使用从同一行中的策略开始日期到下一行中的策略结束日期的数据计算的。
建模和见解
有了 X 和 Y,我们进行线性回归。我们回归了所有特征的平均增长率。在此过程中,我们还根据变量多重共线性和模型性能进行变量选择。我们移除 VIF(方差膨胀因子)大于 10 的变量,然后逐步向前回归。最终模型报告调整后的 R 平方为 0.26。可变系数图如下:
可变系数图
红色列是具有正系数的要素,因此值越大,空气越高。绿色列是具有负系数的要素,值越高,空气越低。老化有一个正系数,所以它恶化了空气。另一方面,教育指数、学校关闭和集会禁令减少了空气。从统计意义上来说,老龄化、人口密度、教育指数都有 p 值< 0.07 and thus are statistically significant.
Summing up both the coefficient value and p-value of the features, aging and education index have a statistically significant impact on the spread of COVID-19. Moreover, population aging has the strongest influence.
While we are not surprised by our findings on population aging, since we know for a fact that seniors and people with preconditions are more susceptible to the virus, we are interested in seeing that the education index plays a significant role. It means that areas with better education tend to see a slower spread of the virus. It could be that educated people are more conscientious in following government policies and good practices such as wearing face coverings. In this light, the government, in fighting the pandemic, should also pay attention to areas with lower education index.
Project Value and Next Steps
As proof of concept, the linear regression model shows that our combined dataset is able to reveal insights about the spread of COVID-19. Our findings show that, among all considered factors, population aging and education index has a stronger influence on the pandemic spread.
Moving forward, we may also add more state-wise data into the model, using our data processing pipeline. More elaborate machine learning methods, such as PCA and clustering can also be applied to the dataset to gain deeper insights.
Please refer to our GitHub 页面如果有兴趣可以随意接触!
特征工程
改进线性回归
上周,我发表了一篇博客,详细介绍了线性回归建模过程的所有步骤。在这篇文章中,我们将稍微处理一下数据,以减少我们的模型结果指标。然后我们将走过任何线性回归中最关键的一步:特征工程**。**所有代码都可以在这个笔记本里找到。这篇博客从笔记本的“特征工程”标题开始。
马库斯·温克勒在 Unsplash 上拍摄的照片
特征工程是从我们的数据集中提取某些变量(特征)并将其转化为预测模型的过程。本质上,我们将试图操纵单个变量和变量的组合,以便设计新功能。通过创建这些新特征,我们增加了一个新变量比原始的、未转换的变量对我们的结果变量具有更大预测能力的可能性。
原始模型概述
总的来说,我们的目标是在给定生成的数据的情况下,尝试预测汽车座椅的销售。我们对数据进行预处理,以考虑名义数据和分类数据,对数据进行缩放和标准化,移除异常值,构建模型,并验证我们的线性回归假设。我们的特征是这样的:
- 销售额(目标):每个地点的单位销售额
- CompPrice:在每个地点最接近的竞争对手收取的价格
- 收入:社区收入水平
- 广告:公司在各地的本地广告预算
- 人口:该地区的人口规模(以千计)
- 价格:每个站点汽车座位的收费价格
- 搁置位置:现场搁置位置的质量(好|差|中)
- 年龄:当地人口的平均年龄
- 教育:每个地点的教育水平
- 城市:商店位于城市还是农村
- 美国:无论商店是否在美国
我们采用的方法允许我们拼凑一个性能良好的模型,该模型返回的 r 平方度量为 0.88,均方根误差为 0.96。这两个指标都表明了一个非常强大的模型,但是我们必须记住,这些数据仅仅是出于练习的目的而生成的,并不是真实的数据。这很好,可以省去很多麻烦,但是现实世界的数据很少会在模型的第一次迭代后产生如此强的结果。
后退一小步
在我们进入我们的特征工程之前,让我们先来看看我们以前的回归数据。因为我们有如此强大的表现回归,为了这个博客的目的,我们想删除对我们的目标变量影响最大的变量。协方差测量两个变量如何相对于彼此变化(scale = [0,1])。如果变量 X 和变量 Y 之间的协方差很高,那就意味着这两个变量之间有很强的相互影响。下面的代码块将向我们展示如何查看模型训练集中所有变量之间的协方差:
X_train_prep.cov()
这个简单的方法将输出上面的数据帧,这是非常丰富的信息,但也有很多需要消化。我们读取 y 轴上的变量,并在每个 x 轴变量下找到相应的协方差分数。我们试图确定的是哪个变量对其他变量的影响最大,这样我们就可以将它们从模型中移除。再次重申,我们只是为了特征工程而识别和移除显著变量以降低我们的模型性能。在现实世界的模型中,如果没有领域知识的支持,这绝对是荒谬的。
我们可以通过在 seaborn 热图中可视化来使协方差输出更容易理解。
sns.heatmap(X_train_prep.cov())
热图只是通过颜色强度显示了我们的协方差程度。颜色越浅,两个变量的协方差越高(根据地图右侧的比例)。我们看到“CompPrice”对自身和“Price”都有很大的影响。这从本质上讲是有道理的,竞争对手价格的变化会影响原始业务的变化,改变其价格来反映。同样,这也是领域知识如此重要的地方。因为我们在“CompPrice”和“Price”中有两个非常相似的预测变量,并且“CompPrice”不是销售汽车座椅的企业固有的变量,所以我们将删除“CompPrice”以恶化我们的模型。
X_train_prep.drop('CompPrice', axis = 1, inplace = True)
lr2 = LinearRegression()
lr2.fit(X_train_prep, y_train)
y_hat_train = lr2.predict(X_train_prep)
r2_score(y_train, y_hat_train)
- r 平方得分= 0.7343
我们看到,简单地移除一个强预测因子会将我们的 R 平方值降低到 0.73,这是一个非常糟糕的值。在现实世界中,在模型的初始迭代之后,这实际上是一个有希望的分数。然而,我们不会满足于此,然后会转向特征工程来尝试和改进我们的模型。
特征工程—二元组合
现在,我们已经从数据框架中删除了“CompPrice ”,它看起来像这样(数据已经过缩放和标准化):
在特征工程中,我们希望在多个变量之间创建各种各样的交互,以便创建新的变量。例如,将价格乘以收入,或者按城市特定人群做广告。通过一起操作它们,我们创造了拥有新的和有影响力的特征的机会,这些特征可能潜在地影响我们的目标变量,从而设计我们的特征。对于这个论点,我们将使用 itertools 库中的“组合”方法创建尽可能多的预测变量的二元组合。
from itertools import combinationscolumns_list = X_train_prep.columns
interactions = list(combinations(column_list, 2))
现在,我们只需要操作列标题来生成所有可能的组合。我们的结果是我们的特征的 45 个可能的二元组合的列表。
虽然我们有自己的组合,但在回归中单独测试每一个组合将是令人难以置信的乏味和耗时。相反,我们会将每个组合添加到一个字典中,然后在迭代线性回归中将相应的字典项作为参数进行索引:
interaction_dict = {}
for interaction in interactions:
X_train_int = X_train_prep
X_train_int['int'] = X_train_int[interaction[0]] * X_train_int[interaction[1]]
lr3 = LinearRegression()
lr3.fit(X_train_int, y_train)
interaction_dict[lr3.score(X_train_int, y_train)] = interaction
在上面的代码块中,我们将 X_train 设置为一个新的副本变量,通过将两个特征相乘来建立一个新的特征*‘int’*,然后使用包含新组合特征的新数据帧作为我们新的 LinearRegression 的唯一参数(因此排除了已经在我们的数据帧中的原始特征)。
由于迭代,我们现在实际上已经完成了所有新的回归,但是我们看不到它们,因为它们都存储在“interaction_dict”中。让我们对字典进行排序,以返回五个表现最好的(r 平方得分)组合。
top_5 = sorted(interaction_dict.keys(), reverse = True)[:5]
for interaction in top_5:
print(interaction_dict[interaction])
我们表现最佳的组合是:
- 广告 x 教育
- 广告 x 美国
- 美国 x 广告公司
- 价格 x 年龄
- 好 x 时代
在这一点上,领域知识把它踢到了一个更高的档位。这里返回的变量应该开始在你的大脑中移动一些曲柄和齿轮,为什么某些变量更有影响力。发挥创造力吧,毕竟我们是工程师。为了这个博客,假设我们的领域知识已经假设任何分类变量都是不相关的,因为位置并不重要。我们将只关注“广告 x 教育”和“价格 x 年龄”。让我们运行一个新的回归,包括和这些组合。首先,我们将特征添加到包含单变量数据的原始数据帧中。
X_train_int = X_train_prep
X_train_int['ad_ed'] = X_train_int['Advertising'] * X_train_int['Age']
注意最右边的两个特征
现在,我们用数据框中的新特性运行另一次回归。
lr4 = LinearRegression()
lr4.fit(X_train_int, y_train)
lr4.score(X_train_int, y_train)
- r 平方得分= .74
我们可以看到,我们的线性回归得分从 0.73 增加到 0.74。这显然是一个很小的增加,也不是非常显著,但尽管如此,我们可以看到创建新的双变量特征项如何在改进我们的模型中发挥重要作用。
特征工程—多项式
同样,没有明确的准则来测试所有可能的变量操作。我们刚刚看到了如何使两个变量相互作用,但是我们可以设计新功能的另一种方法是什么?多项式!对于新特性,一个非常强有力的(通常)选择是增加单个变量的能力。出于我们的目的,我们将尝试看看是否所有现有的变量,包括我们的双变量组合,都可以通过增加功效来改善我们的回归。
为此,我们将使用 sklearn.preprocessing 库中的“PolynomialFeatures”对象。我们创建一个新的空字典来存储我们的新功能可能性(与双变量相同)。然后,我们将遍历我们的 X_train_int 特征集,并为每个相应的特征创建一个新的特征,通过五次方进行平方。然后,我们将对每个新的单个特征进行线性回归拟合,并选择性能最佳的特征用于最终回归。
from sklearn.preprocessing import PolynomialFeaturespoly_dict = {}
for feature in X_train_int.columns:
for p in range(2, 5):
X_train_poly = X_train_int
X_train_poly['sq'] = X_train_poly[feature] ** p
lr = LinearRegression()
lr.fit(X_train_poly, y_train)
poly_dict[lr.score(X_train_poly, y_train) = [feature, p]poly_dict[max(poly_dict.keys())]
- r 平方得分= 0.743
再一次,我们看到我们的 R 平方指标有微小但仍为正值的增长。有许多方法可以设计新的可能性并改进您的回归,但是这些对于任何新的数据科学家来说都是一个很好的起点!
特征工程——深入研究编码和宁滨技术
特征编码和特征宁滨技术的说明
作者图片
F特征工程是数据科学模型开发最重要的方面。原始数据集中有多种类别的要素。特征可以是文本、日期/时间、分类和连续变量。对于机器学习模型,数据集需要以数字向量的形式进行处理,以使用 ML 算法对其进行训练。
这篇文章的目的是演示特征工程技术,将分类特征转换成连续特征,反之亦然。
- **功能宁滨:**连续变量到分类变量的转换。
- **特征编码:**分类变量到数字特征的转换。
特色宁滨:
宁滨或离散化用于将连续或数值变量转换成分类特征。连续变量的宁滨引入了非线性,并倾向于改善模型的性能。它还可用于识别缺失值或异常值。
宁滨有两种类型:
- **无监督宁滨:**等宽宁滨,等频宁滨
- **监督宁滨:**基于熵的宁滨
无人监管的宁滨:
无监督宁滨是一种宁滨,它将数值或连续变量转换成分类箱**,而不考虑目标类别标签**。无监督宁滨分为两类:
1.等宽宁滨:
该算法将连续变量分成几个类别,这些类别具有相同宽度的仓或范围。
Notations,
x = number of categories
w = width of a category
max, min = Maximum and Minimun of the list
(作者图片),使用等宽宁滨算法对连续特征“年龄”进行分类
2.等频宁滨:
该算法将数据分成具有大致相同数量的值的不同类别。数据的值被平均分配到形成的类别中。
Notations,
x = number of categories
freq = frequency of a category
n = number of values in data
(图片由作者提供),使用等频宁滨算法对连续特征“年龄”进行分类
(作者代码),实现等宽宁滨,等频宁滨
(图片由作者提供),等宽等频宁滨条形图
监督宁滨:
监督宁滨是一种宁滨,它将数值或连续变量转换为分类变量,同时考虑目标类别标注。选择离散化切割点时,它指的是目标类标签。基于熵的宁滨是一种监督宁滨。
1.基于熵的宁滨;
基于熵的宁滨算法对连续或数值变量进行分类,一个容器或类别中的大多数值属于同一类标签。它计算目标类别标签的熵,并基于最大信息增益对分裂进行分类。
这个模块实现了穷举搜索一个序列的最高熵宁滨的功能
github.com](https://github.com/paulbrodersen/entropy_based_binning)
特征编码:
特征编码用于将分类特征转换成数字变量。大多数 ML 算法不能处理分类变量,因此进行特征编码是很重要的。有许多用于特征工程的编码技术:
1.标签编码:
标签编码是一种编码技术,通过给每个类别分配一个数值,将类别变量转换成数值变量。标签编码可用于序数变量。
(图片由作者提供),标签编码
2.序数编码:
顺序编码是一种编码技术,通过将原始分类变量转换为数字变量,确保变量的顺序性质得以保持。
(图片由作者提供),序数编码
3.频率编码:
频率编码是一种通过考虑数据的频率分布将原始分类变量转换为数值变量的编码技术。这对于名义特征很有用。
(图片由作者提供),频率编码
4.二进制编码:
二进制编码是一种编码技术,通过将类别编码为整数,然后转换为二进制代码,将原始分类变量转换为数字变量。对于具有大量类别的变量,这种方法更可取。
对于 100 个类别变量,标签编码创建 100 个标签,每个标签对应一个类别,而二进制编码仅创建 7 个类别。
(图片由作者提供),二进制编码
5.一个热门编码:
一种热门的编码技术是将每个类别分成一列。它为一个类别创建 k 个不同的列,并用 1 替换一列,其余的列为 0。
(图片由作者提供),一个热编码
6.目标均值编码:
均值编码是将分类变量转换为数值变量的最佳技术之一,因为它考虑了目标类别标签。基本思想是用相应目标变量的平均值代替分类变量。
这里需要编码的分类变量是自变量(IV ),目标类别标签是因变量(DV)。
均值编码步骤:
- 选择一个类别
- 按类别分组并获得总和(= a)
- 按类别分组并获得合计总数(= b)
- 该类别的数值= a/b
(图片由作者提供),目标意味着编码
实施:
(作者代码),编码器的 Python 实现
结论:
特征工程是一个循环过程,没有人能断定这种特征工程技术是最好的。在数据处理过程中,没有选择特定特征工程(编码或宁滨技术)的经验法则。因此,一个人需要尝试根据业务需求专注于特性工程,对每个过程进行多次尝试,并从中挑选出最好的。
感谢您的阅读
特征工程和特征选择
📈Python for finance 系列
如何将现代机器学习应用于体积扩散分析(VSA)
警告 : 这里没有神奇的公式或圣杯,尽管一个新的世界可能会为你打开大门。
📈Python For Finance 系列
在这些系列的前几篇文章的基础上,这一次我们将探索金融市场中真正的技术分析。很长一段时间里,我一直着迷于 TA 的内在逻辑,叫做量差分析(VSA)。我没有发现任何关于在这个时候应用现代机器学习来证明持久技术的文章。在这里,我试图抛出一条小鱼去抓一条鲸鱼。如果我能在这个领域引起一些注意,我在这篇文章上花费的时间是值得的。
特别是,在我读了大卫·h·韦斯的《即将发生的交易》之后,他在书中描述道:
“你应该能够倾听任何市场对自身的看法,而不是分析一系列指标或算法。”
密切倾听市场,正如下面这句话所说的,正如我们不可能预测未来一样,我们也很难忽视即将发生的事情。关键是捕捉即将发生的事情,并跟随潮流。
但是如何看待即将发生的事情,理查德·威科夫很久以前发表的一篇声明给出了一些线索:
“成功的读磁带[读图表]是对力的研究。它需要判断哪一方有最大的吸引力的能力,一个人必须有勇气去支持那一方。就像在企业或个人的生活中一样,每一次摇摆都会出现临界点。在这些关键时刻,似乎任何一边羽毛的重量都会决定当前的趋势。任何一个能发现这些点的人都会赢得很多,失去很少。”
但是如何解释市场行为呢?理查德·威科夫对市场力量的一个雄辩的描述很有启发性:
“市场就像一个缓慢旋转的轮子:轮子是会继续朝同一个方向旋转,静止不动还是倒转,完全取决于与它的轮毂和踏板接触的力量。即使当接触被打破,没有什么影响它的过程,车轮保留一定的冲力来自最近的主导力量,并旋转,直到它停下来或受到其他影响。”
David H. Weis 给出了一个极好的例子,说明了如何解读棒线,并将其与市场行为联系起来。通过他对一个假设的酒吧行为的构建,每一个酒吧都变得活跃起来,争相向你讲述他们的故事。
假设的行为
有关分析的所有细节,请参考大卫的书。
在这本书正式发行之前购买了它,并得到了大卫的签名。
在我们深入研究代码之前,最好给出一些关于体积扩散分析(VSA)的背景知识。VSA 是通过跟踪专业交易者,即所谓的做市商,研究量价关系来预测市场走向。对市场行为的所有解释都遵循 3 条基本法则:
- 供求法则
- 努力与结果的法则
- 因果定律
在 VSA 的发展史上,还有三位大名鼎鼎的人物。
- 杰西·利弗莫尔
- 理查德·威科夫
- 汤姆·威廉姆斯
大量的学习资料可以在网上找到。对于初学者,我推荐以下两本书。
- 掌握市场汤姆·威廉姆斯著
- 即将发生的交易
另外,如果你只想快速浏览一下这个话题,这里有一篇来自的关于 VSA 的好文章。
机器学习/深度学习的一大优势在于不需要特征工程。VSA 的基本如其名所言,成交量、价差的幅度、位置的变化与股价的变化密切相关。
这些特征可以定义为:
酒吧的定义
- 音量:非常直接
- 范围/价差:最高价和收盘价之间的差异
- 收盘价相对于区间:收盘价是接近价格柱的顶部还是底部?
- 股票价格的变化:非常直接
网上有很多关于 VSA 的资料。我发现这 7 个现场交易系列视频相当不错。
还有这个。
更多内容可以在 YouTube 上找到。
理查德·威科夫创造了许多“T4”术语,如“力量的象征”(SOS)、“软弱的象征”(SOW)等…然而,这些术语中的大多数纯粹是这 4 个基本特征的组合。我不认为,在深度学习的情况下,过度设计功能是一件明智的事情。考虑到深度学习的优势之一是它完全自动化了机器学习工作流程中过去最关键的步骤:特征工程。我们需要做的事情是告诉算法看哪里,而不是一步一步地照看他们。事不宜迟,让我们深入研究代码。
1.数据准备
为了一致性,在所有的📈Python for finance 系列,我会尽量重用相同的数据。关于数据准备的更多细节可以在这里,在这里和这里找到。
*#import all the libraries*
import pandas as pd
import numpy as np
import seaborn as sns
import yfinance as yf *#the stock data from Yahoo Finance*import matplotlib.pyplot as plt #set the parameters for plotting
plt.style.use('seaborn')
plt.rcParams['figure.dpi'] = 300#define a function to get data
def get_data(symbols, begin_date=None,end_date=None):
df = yf.download('AAPL', start = '2000-01-01',
auto_adjust=True,#only download adjusted data
end= '2010-12-31')
#my convention: always lowercase
df.columns = ['open','high','low',
'close','volume']
return df
prices = get_data('AAPL', '2000-01-01', '2010-12-31')
prices.head()
✍Tip!
我们这次下载的数据是通过设置 *auto_adjust=True*
调整 *yfinance*
的数据。如果你能得到分笔成交点的数据,尽一切办法。如果有马科斯·普拉多的 金融机器学习进展 中阐述的分笔成交点数据就更好了。反正 10 年调整后的数据只给出 2766 个条目,离“大数据”还差得远。
2.特征工程
将 VSA 与现代数据科学相结合的关键是,通过阅读和解释棒线自身的行为,人们(希望是算法)可以构建一个市场行为的故事。这个故事可能不容易被人类理解,但却以一种复杂的方式运作。
成交量结合价格区间和收盘位置很容易用代码来表示。
- 音量:非常直接
- 范围/价差:最高价和收盘价之间的差异
def price_spread(df):
return (df.high - df.low)
- 收盘价相对于区间:收盘价是接近价格柱的顶部还是底部?
def close_location(df):
return (df.high - df.close) / (df.high - df.low)#o indicates the close is the high of the day, and 1 means close
#is the low of the day and the smaller the value, the closer the #close price to the high.
- 股票价格的变化:非常直接
现在到了棘手的部分,
“从更大的角度来看,一些价格条有了新的含义.”
这意味着要看到完整的图片,我们需要在不同的时间尺度下观察这 4 个基本特征。
要做到这一点,我们需要在不同的时间跨度重建一个高(H),低(L),收盘©和体积(V)酒吧。
def create_HLCV(i):
'''
#i: days
#as we don't care about open that much, that leaves volume,
#high,low and close
''' df = pd.DataFrame(index=prices.index) df[f'high_{i}D'] = prices.high.rolling(i).max()
df[f'low_{i}D'] = prices.low.rolling(i).min()
df[f'close_{i}D'] = prices.close.rolling(i).\
apply(lambda x:x[-1])
# close_2D = close as rolling backwards means today is
#literally, the last day of the rolling window.
df[f'volume_{i}D'] = prices.volume.rolling(i).sum()
return df
下一步,根据不同的时间尺度创建这 4 个基本特征。
def create_features(i):
df = create_HLCV(i)
high = df[f'high_{i}D']
low = df[f'low_{i}D']
close = df[f'close_{i}D']
volume = df[f'volume_{i}D']
features = pd.DataFrame(index=prices.index)
features[f'volume_{i}D'] = volume
features[f'price_spread_{i}D'] = high - low
features[f'close_loc_{i}D'] = (high - close) / (high - low)
features[f'close_change_{i}D'] = close.diff()
return features
我想探索的时间跨度是 1、2、3 天和 1 周、1 个月、2 个月、3 个月,大致是[1、2、3、5、20、40、60]天。现在,我们可以创造一大堆功能,
def create_bunch_of_features():
days = [1,2,3,5,20,40,60]
bunch_of_features = pd.DataFrame(index=prices.index)
for day in days:
f = create_features(day)
bunch_of_features = bunch_of_features.join(f)
return bunch_of_featuresbunch_of_features = create_bunch_of_features()
bunch_of_features.info()
为了让事情容易理解,我们的目标结果将只是第二天的回报。
# next day's returns as outcomes
outcomes = pd.DataFrame(index=prices.index)
outcomes['close_1'] = prices.close.pct_change(-1)
3.特征选择
让我们来看看这些特征是如何与结果,即第二天的回报相关联的。
corr = bunch_of_features.corrwith(outcomes.close_1)
corr.sort_values(ascending=False).plot.barh(title = 'Strength of Correlation');
很难说有什么关联,因为所有的数字都远低于 0.8。
corr.sort_values(ascending=False)
接下来,让我们看看这些特性是如何相互关联的。
corr_matrix = bunch_of_features.corr()
我没有制作热图,而是尝试使用 Seaborn 的 Clustermap 按行或列进行聚类,看看是否有任何模式出现。Seaborn 的 Clustermap 功能非常适合制作简单的热图以及在行和/或列上都有树状图的分层聚类热图。这将重新组织行和列的数据,并显示彼此相邻的类似内容,以便更深入地理解数据。一个很好的关于聚类图的教程可以在这里找到。要获得一个聚类图,实际上只需要一行代码。
sns.clustermap(corr_matrix)
如果你仔细观察图表,可以得出一些结论:
- 价格差价与交易量密切相关,这一点在图表的中心可以清楚地看到。
- 和在不同时间跨度上彼此相关的接近的位置,如右下角所示。
- 从左上角的淡颜色来看,密切的价格变化确实与它本身成对,这非常有意义。然而,它有点随机,因为在不同的时间尺度上没有聚类模式。我希望 2 天的变化应该与 3 天的变化配对。
接近的价格差异的随机性可以归因于股票价格本身的特征。简单的百分比回报可能是一个更好的选择。这可以通过将关闭 diff()
修改为关闭pct_change()
来实现。
def create_features_v1(i):
df = create_HLCV(i)
high = df[f'high_{i}D']
low = df[f'low_{i}D']
close = df[f'close_{i}D']
volume = df[f'volume_{i}D']
features = pd.DataFrame(index=prices.index)
features[f'volume_{i}D'] = volume
features[f'price_spread_{i}D'] = high - low
features[f'close_loc_{i}D'] = (high - close) / (high - low)
#only change here
features[f'close_change_{i}D'] = close.pct_change()
return features
再做一遍。
def create_bunch_of_features_v1():
days = [1,2,3,5,20,40,60]
bunch_of_features = pd.DataFrame(index=prices.index)
for day in days:
f = create_features_v1(day)#here is the only difference
bunch_of_features = bunch_of_features.join(f)
return bunch_of_featuresbunch_of_features_v1 = create_bunch_of_features_v1()#check the correlation
corr_v1 = bunch_of_features_v1.corrwith(outcomes.close_1)
corr_v1.sort_values(ascending=False).plot.barh( title = 'Strength of Correlation')
有点不同,但不多!
corr_v1.sort_values(ascending=False)
特征之间的相关性会怎样?
corr_matrix_v1 = bunch_of_features_v1.corr()
sns.clustermap(corr_matrix_v1, cmap='coolwarm', linewidth=1)
嗯,模式保持不变。让我们将默认方法从“平均”改为“病房”。这两种方法类似,但“ward”更像 K-MEANs 聚类。关于这个话题的很好的教程可以在这里找到。
sns.clustermap(corr_matrix_v1, cmap='coolwarm', linewidth=1,
method='ward')
为了选择特性,我们希望挑选那些与目标结果有最强、最持久关系的特性。同时,尽量减少所选要素中的重叠或共线性,以避免噪音和计算机能力的浪费。对于那些在聚类中配对在一起的特征,我只挑选与结果有更强相关性的特征。通过查看聚类图,可以挑选出一些特征。
deselected_features_v1 = ['close_loc_3D','close_loc_60D',
'volume_3D', 'volume_60D',
'price_spread_3D','price_spread_60D',
'close_change_3D','close_change_60D']selected_features_v1 = bunch_of_features.drop \
(labels=deselected_features_v1, axis=1)
接下来,我们将看一看配对图,一个配对图是一个很好的方法来识别后续分析的趋势,允许我们看到单个变量的分布和多个变量之间的关系。同样,我们需要的只是一行代码。
sns.pairplot(selected_features_v1)
图表铺天盖地,很难看清。我们以一个小群体为例。
selected_features_1D_list = ['volume_1D', 'price_spread_1D',\ 'close_loc_1D', 'close_change_1D']selected_features_1D = selected_features_v1\
[selected_features_1D_list]sns.pairplot(selected_features_1D)
我立即注意到两件事,一是有异常值,另一个是分布不接近正常。
现在让我们来处理离群值。为了一气呵成,我会将结果与特征结合起来,并一起删除异常值。
features_outcomes = selected_features_v1.join(outcomes)
features_outcomes.info()
我将使用与这里描述的、这里描述的和这里描述的相同的方法来移除异常值。
stats = features_outcomes.describe()
def get_outliers(df, i=4):
#i is number of sigma, which define the boundary along mean
outliers = pd.DataFrame()
for col in df.columns:
mu = stats.loc['mean', col]
sigma = stats.loc['std', col]
condition = (df[col] > mu + sigma * i) | (df[col] < mu - sigma * i)
outliers[f'{col}_outliers'] = df[col][condition]
return outliersoutliers = get_outliers(features_outcomes, i=1)
outliers.info()
我设置 1 个标准差作为边界,把大部分的离群值挖出来。然后移除所有异常值和 NaN 值。
features_outcomes_rmv_outliers = features_outcomes.drop(index = outliers.index).dropna()
features_outcomes_rmv_outliers.info()
剔除异常值后,我们可以再次绘制配对图。
sns.pairplot(features_outcomes_rmv_outliers, vars=selected_features_1D_list);
现在,情况看起来好了很多,但几乎无法得出任何有用的结论。这将是很好的,看看哪些景点是向下移动,哪些是向上移动与这些功能相结合。我可以提取股票价格变化的信号,并在图上增加一个额外的维度。
features_outcomes_rmv_outliers['sign_of_close'] = features_outcomes_rmv_outliers['close_1'].apply(np.sign)
现在,让我们重新绘制pairplot()
,稍微调整一下,使图形更加漂亮。
sns.pairplot(features_outcomes_rmv_outliers,
vars=selected_features_1D_list,
diag_kind='kde',
palette='husl', hue='sign_of_close',
markers = ['*', '<', '+'],
plot_kws={'alpha':0.3});#transparence:0.3
现在,看起来好多了。显然,当价格上涨时,它们(蓝点)密度更大,聚集在某个位置。而在情绪低落的时候,它们会四处蔓延。
如果你能给这对搭档的情节提供一些线索,并在下面留下你的评论,我将不胜感激,谢谢。
以下是本文中使用的所有代码的摘要:
*#import all the libraries*
import pandas as pd
import numpy as np
import seaborn as sns
import yfinance as yf *#the stock data from Yahoo Finance*import matplotlib.pyplot as plt #set the parameters for plotting
plt.style.use('seaborn')
plt.rcParams['figure.dpi'] = 300#define a function to get data
def get_data(symbols, begin_date=None,end_date=None):
df = yf.download('AAPL', start = '2000-01-01',
auto_adjust=True,#only download adjusted data
end= '2010-12-31')
#my convention: always lowercase
df.columns = ['open','high','low',
'close','volume']
return dfprices = get_data('AAPL', '2000-01-01', '2010-12-31')#create some features
def create_HLCV(i):#as we don't care open that much, that leaves volume,
#high,low and closedf = pd.DataFrame(index=prices.index)
df[f'high_{i}D'] = prices.high.rolling(i).max()
df[f'low_{i}D'] = prices.low.rolling(i).min()
df[f'close_{i}D'] = prices.close.rolling(i).\
apply(lambda x:x[-1])
# close_2D = close as rolling backwards means today is
# literly the last day of the rolling window.
df[f'volume_{i}D'] = prices.volume.rolling(i).sum()
return dfdef create_features_v1(i):
df = create_HLCV(i)
high = df[f'high_{i}D']
low = df[f'low_{i}D']
close = df[f'close_{i}D']
volume = df[f'volume_{i}D']
features = pd.DataFrame(index=prices.index)
features[f'volume_{i}D'] = volume
features[f'price_spread_{i}D'] = high - low
features[f'close_loc_{i}D'] = (high - close) / (high - low)
features[f'close_change_{i}D'] = close.pct_change()
return featuresdef create_bunch_of_features_v1():
'''
the timespan that i would like to explore
are 1, 2, 3 days and 1 week, 1 month, 2 month, 3 month
which roughly are [1,2,3,5,20,40,60]
'''
days = [1,2,3,5,20,40,60]
bunch_of_features = pd.DataFrame(index=prices.index)
for day in days:
f = create_features_v1(day)
bunch_of_features = bunch_of_features.join(f)
return bunch_of_featuresbunch_of_features_v1 = create_bunch_of_features_v1()#define the outcome target
#here, to make thing easy to understand, i will only try to predict #the next days's return
outcomes = pd.DataFrame(index=prices.index)# next day's returns
outcomes['close_1'] = prices.close.pct_change(-1)#decide which features are abundant from cluster map
deselected_features_v1 = ['close_loc_3D','close_loc_60D',
'volume_3D', 'volume_60D',
'price_spread_3D','price_spread_60D',
'close_change_3D','close_change_60D']
selected_features_v1 = bunch_of_features_v1.drop(labels=deselected_features_v1, axis=1)#join the features and outcome together to remove the outliers
features_outcomes = selected_features_v1.join(outcomes)
stats = features_outcomes.describe()#define the method to identify outliers
def get_outliers(df, i=4):
#i is number of sigma, which define the boundary along mean
outliers = pd.DataFrame()
for col in df.columns:
mu = stats.loc['mean', col]
sigma = stats.loc['std', col]
condition = (df[col] > mu + sigma * i) | (df[col] < mu - sigma * i)
outliers[f'{col}_outliers'] = df[col][condition]
return outliersoutliers = get_outliers(features_outcomes, i=1)#remove all the outliers and Nan value
features_outcomes_rmv_outliers = features_outcomes.drop(index = outliers.index).dropna()
我知道这篇文章太长了,我最好把它留在这里。在下一篇文章中,我将进行数据转换,看看我是否有办法解决分布问题。敬请期待!
参考
- 即将发生的交易
- 罗洛磁带[pseud。],磁带阅读的研究(伯灵顿,佛蒙特州:弗雷泽,1910),95。
选举结果预测的特征工程(Python 语言)
詹姆斯·哈里逊在 Unsplash 上拍摄的照片
我最近参加了一个 Kaggle 比赛,我们必须使用机器学习来预测选举结果。该数据集来自 2019 年印度大选(见此)。本文介绍了如何清理和准备数据集,从现有要素中创建新要素,然后使用流行的机器学习算法预测结果。大多数基本的预处理、数据可视化和机器学习步骤在这里还没有解释清楚;相反,我关注的是特征工程以及它如何影响模型的性能。如果你对什么是特征工程没有清晰的认识,请参考我之前的文章《基础特征工程达到更高效的机器学习》。
首先,我们应该将数据集加载到笔记本中。大多数 ML 工程师和专家更喜欢在初始阶段使用笔记本进行机器学习和数据工程任务,因为用笔记本进行数据可视化和所有其他步骤真的很容易。我更喜欢使用 pandas 库来加载数据集,因为它使所有的数据处理步骤变得非常容易,不需要太多的努力。下面是我在笔记本中使用的 python 库。
import numpy as np
import pandas as pd
import os
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.preprocessing import LabelEncoder
from sklearn.preprocessing import MinMaxScaler
from sklearn.model_selection import train_test_split
from sklearn.neighbors import KNeighborsClassifier
from sklearn.feature_selection import SelectKBest, chi2
首先,让我们运行下面的命令,对数据集和原始数据有一个基本的了解。
dataset = pd.read_csv('/kaggle/input/indian-candidates-for-general-election-2019/LS_2.0.csv')
dataset.head()
dataset.info()<class 'pandas.core.frame.DataFrame'>
RangeIndex: 2263 entries, 0 to 2262
Data columns (total 19 columns):
STATE 2263 non-null object
CONSTITUENCY 2263 non-null object
NAME 2263 non-null object
WINNER 2263 non-null int64
PARTY 2263 non-null object
SYMBOL 2018 non-null object
GENDER 2018 non-null object
CRIMINAL
CASES 2018 non-null object
AGE 2018 non-null float64
CATEGORY 2018 non-null object
EDUCATION 2018 non-null object
ASSETS 2018 non-null object
LIABILITIES 2018 non-null object
GENERAL
VOTES 2263 non-null int64
POSTAL
VOTES 2263 non-null int64
TOTAL
VOTES 2263 non-null int64
OVER TOTAL ELECTORS
IN CONSTITUENCY 2263 non-null float64
OVER TOTAL VOTES POLLED
IN CONSTITUENCY 2263 non-null float64
TOTAL ELECTORS 2263 non-null int64
dtypes: float64(3), int64(5), object(11)
memory usage: 336.0+ KB
从上面的分析中我们可以看出,数据集包含很少的数字列,大多数列都是非数字的。WINNER 列包含表明候选人赢得或输掉选举的标签。另外,请注意,数据集包含一些“NaN”值,这些值基本上是缺失值。有些列名包含’ \n ‘字符,这很烦人。此外,资产和负债列值也包含’ \n '字符。
仅考虑可用数据,我们可以将州、选区、政党、性别、类别和教育视为分类特征。此外,您可以运行 dataset.describe()命令来对数字列进行一些统计汇总。
接下来,我们应该清理数据集,修复列名并处理丢失的值。首先,我修复了不正确的列名,并用下划线(’ _ ')替换了列名中的所有空格。这不是一个强制的步骤,但是不正确的列名让我很烦。
*# rename invalid column names*
dataset = dataset.rename(columns={'CRIMINAL**\n**CASES': 'CRIMINAL_CASES', 'GENERAL**\n**VOTES': 'GENERAL_VOTES', 'POSTAL**\n**VOTES': 'POSTAL_VOTES', 'TOTAL**\n**VOTES': 'TOTAL_VOTES', 'OVER TOTAL ELECTORS **\n**IN CONSTITUENCY': 'OVER_TOTAL_ELECTORS_IN_CONSTITUENCY', 'OVER TOTAL VOTES POLLED **\n**IN CONSTITUENCY': 'OVER_TOTAL_VOTES_POLLED_IN_CONSTITUENCY', 'TOTAL ELECTORS': 'TOTAL_ELECTORS'})
然后让我们搜索每一列中缺少的值。
dataset.isna().sum()STATE 0
CONSTITUENCY 0
NAME 0
WINNER 0
PARTY 0
SYMBOL 245
GENDER 245
CRIMINAL_CASES 245
AGE 245
CATEGORY 245
EDUCATION 245
ASSETS 245
LIABILITIES 245
GENERAL_VOTES 0
POSTAL_VOTES 0
TOTAL_VOTES 0
OVER_TOTAL_ELECTORS_IN_CONSTITUENCY 0
OVER_TOTAL_VOTES_POLLED_IN_CONSTITUENCY 0
TOTAL_ELECTORS 0
dtype: int64
您可以看到 10%的行值丢失了。处理缺失值有多种方法,如删除、使用后填或前填、常量值插补、均值/中值或众数插补等。但是,为了简单起见,我在这里删除了这些行(只丢失了 10%),但是请始终记住,删除值会使预测模型不太准确。你应该尽可能多地估算缺失值。
*# drop rows with NA values*
dataset = dataset[dataset['GENDER'].notna()]
让我们将资产、负债和 CRIMINAL_CASES 列转换为数字,因为它们代表金钱和计数,并且数字对模型有意义。为此,我们必须删除每个值字段中的“Rs”符号、“\n”字符和逗号。而且这些列包含“无”和“不可用”作为值。因此,在将它们变成数值之前,我们必须用一些有意义的值来替换它们(目前我用 0 来替换它们)。
*# replace Nil values with 0*
dataset['ASSETS'] = dataset['ASSETS'].replace(['Nil', '`', 'Not Available'], '0')
dataset['LIABILITIES'] = dataset['LIABILITIES'].replace(['NIL', '`', 'Not Available'], '0')
dataset['CRIMINAL_CASES'] = dataset['CRIMINAL_CASES'].replace(['Not Available'], '0')
*# clean ASSETS and LIABILITIES column values*
dataset['ASSETS'] = dataset['ASSETS'].map(lambda x: x.lstrip('Rs ').split('**\n**')[0].replace(',', ''))
dataset['LIABILITIES'] = dataset['LIABILITIES'].map(lambda x: x.lstrip('Rs ').split('**\n**')[0].replace(',', ''))
*# convert ASSETS, LIABILITIES and CRIMINAL_CASES column values into numeric*
dataset['ASSETS'] = dataset['ASSETS'].astype(str).astype(float)
dataset['LIABILITIES'] = dataset['LIABILITIES'].astype(str).astype(float)
dataset['CRIMINAL_CASES'] = dataset['CRIMINAL_CASES'].astype(str).astype(int)
现在让我们将非数字列改为数字列,以获得更好的性能。请注意,某些模型类型不能处理非数字数据。在这里,我将重点放在分类算法上,它当然不能在非数字数据上进行训练。所以我使用 sklearn LabelEncoder 对这些非数字列进行了标签编码。
*# label encode categorical columns*
lblEncoder_state = LabelEncoder()
lblEncoder_state.fit(dataset['STATE'])
dataset['STATE'] = lblEncoder_state.transform(dataset['STATE'])
lblEncoder_cons = LabelEncoder()
lblEncoder_cons.fit(dataset['CONSTITUENCY'])
dataset['CONSTITUENCY'] = lblEncoder_cons.transform(dataset['CONSTITUENCY'])
lblEncoder_name = LabelEncoder()
lblEncoder_name.fit(dataset['NAME'])
dataset['NAME'] = lblEncoder_name.transform(dataset['NAME'])
lblEncoder_party = LabelEncoder()
lblEncoder_party.fit(dataset['PARTY'])
dataset['PARTY'] = lblEncoder_party.transform(dataset['PARTY'])
lblEncoder_symbol = LabelEncoder()
lblEncoder_symbol.fit(dataset['SYMBOL'])
dataset['SYMBOL'] = lblEncoder_symbol.transform(dataset['SYMBOL'])
lblEncoder_gender = LabelEncoder()
lblEncoder_gender.fit(dataset['GENDER'])
dataset['GENDER'] = lblEncoder_gender.transform(dataset['GENDER'])
lblEncoder_category = LabelEncoder()
lblEncoder_category.fit(dataset['CATEGORY'])
dataset['CATEGORY'] = lblEncoder_category.transform(dataset['CATEGORY'])
lblEncoder_edu = LabelEncoder()
lblEncoder_edu.fit(dataset['EDUCATION'])
dataset['EDUCATION'] = lblEncoder_edu.transform(dataset['EDUCATION'])
现在,让我们训练一个 K-最近邻模型,并查看其准确性。KNN 是一个有监督的机器学习模型,根据分类算法进行分类。该算法通过获取一个数据点并找出 k 个最接近的数据点来工作。
*# separate train features and label*
y = dataset["WINNER"]
X = dataset.drop(labels=["WINNER"], axis=1)*# split dataset into train and test data*
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=1, stratify=y)# train and test knn model
knn = KNeighborsClassifier()
knn.fit(X_train, y_train)knn.predict(X_test)
print("Testing Accuracy is: ", knn.score(X_test, y_test)*100, "%")**Testing Accuracy is: 70.79207920792079 %**
该模型在没有太大影响的情况下实现了 70%的准确率。让我们归一化数据集,看看精度如何提高。我使用 scikit-learn 库中的 MinMaxScaler 将所有值缩小到 0-1 的范围内。
# scaling values into 0-1 rangescaler = MinMaxScaler(feature_range=(0, 1))
features = [
'STATE', 'CONSTITUENCY', 'NAME', 'PARTY', 'SYMBOL', 'GENDER', 'CRIMINAL_CASES', 'AGE', 'CATEGORY', 'EDUCATION', 'ASSETS', 'LIABILITIES', 'GENERAL_VOTES', 'POSTAL_VOTES', 'TOTAL_VOTES', 'OVER_TOTAL_ELECTORS_IN_CONSTITUENCY', 'OVER_TOTAL_VOTES_POLLED_IN_CONSTITUENCY', 'TOTAL_ELECTORS']dataset[features] = scaler.fit_transform(dataset[features])
正如您在下面看到的,通过规范化列值,准确性得到了很大的提高。然而,我们可以通过应用一些其他的特征工程技术来进一步提高精度。
**Testing Accuracy is: 90.5940594059406 %**
对现有特征进行更有意义的编码
如果我们考虑教育列,它包含特定候选人可以拥有的 11 个分类值。
文盲、识字、第 5 次通过、第 8 次通过、第 10 次通过、第 12 次通过、毕业生、研究生、专业毕业生、博士学位、其他
如果我们仔细想想,这些价值观中的每一个都代表了一种特定的教育水平。通过标签编码,我们只是为这些值中的每一个分配一些随机整数,而不考虑它们的层次级别。然而,如果我们根据教育资格有意义地分配该整数,则该模型有望表现得更好。注意,有一个名为“其他”的字段值,我们不知道它的层次位置。我只是给它分配了一个中间值。
dataset['EDUCATION'].value_counts()Post Graduate 502
Graduate 441
Graduate Professional 336
12th Pass 256
10th Pass 196
8th Pass 78
Doctorate 73
Others 50
Literate 30
5th Pass 28
Not Available 22
Illiterate 5
Post Graduate\n 1
Name: EDUCATION, dtype: int64 # encode education column
encoded_edu = []# iterate through each row in the dataset
for row in dataset.itertuples():
education = row.EDUCATIONif education == "Illiterate":
encoded_edu.append(0)
elif education == "Literate":
encoded_edu.append(1)
elif education == "5th Pass":
encoded_edu.append(2)
elif education == "8th Pass":
encoded_edu.append(3)
elif education == "10th Pass":
encoded_edu.append(4)
elif education == "12th Pass":
encoded_edu.append(7)
elif education == "Graduate":
encoded_edu.append(8)
elif education == "Post Graduate":
encoded_edu.append(9)
elif education == "Graduate Professional":
encoded_edu.append(10)
elif education == "Doctorate":
encoded_edu.append(11)
else:
encoded_edu.append(5)dataset['EDUCATION'] = encoded_edu
让我们来分析一下党的专栏。如果我们在每个政党前面列出候选人的人数,我们可以看到只有几个政党的候选人人数很多。将政党列包括在内的全部目的是让候选人的政党对赢得选举产生影响。如果该党没有大量的候选人,该党对候选人获胜的影响就很低。因此,我们可以将它们全部编码到一个通用类别中(我将它们编码为“其他”)。
dataset['PARTY'].value_counts()BJP 420
INC 413
IND 201
BSP 163
CPI(M) 100
...
AINRC 1
SKM 1
ANC 1
YKP 1
AJSUP 1
Name: PARTY, Length: 132, dtype: int64 # change party of the less frequent parties as Other
# 'BJP','INC','IND','BSP', 'CPI(M)', 'AITC', 'MNM': high frequent
# 'TDP', 'VSRCP', 'SP', 'DMK', 'BJD': medium frequentdataset.loc[~dataset["PARTY"].isin(['BJP','INC','IND','BSP', 'CPI(M)', 'AITC', 'MNM', 'TDP', 'VSRCP', 'SP', 'DMK', 'BJD']), "PARTY"] = "Other"
dataset['PARTY'].value_counts()
这两个步骤都应该在标记编码列之前执行。让我们再次训练我们的模型,看看准确性。
**Testing Accuracy is: 92.07920792079209 %**
你可以看到精确度提高了一点点。到目前为止,我们只考虑了现有的功能。然而,我们可以从现有的特性中创造出新的特性。
制作新功能
为了创建新的特性,我们应该对可用的原始特性以及它们如何影响给定场景有一个大致的了解。此外,还需要对问题领域有很好的理解。所以在这里,我们应该思考一下,在一次选举中,影响候选人获胜的因素是什么,我们投票给一个候选人或一个政党的考虑是什么。对于给定的场景,我不知道他们在投票给候选人之前考虑的国家因素(哪些特定国家的因素影响候选人获胜)。例如,我不知道类别如何影响选举结果。但是,通过假设一般的用例,我提出了以下特性。
数据集包含大量原始特征,这些特征将直接影响候选人的获胜机会(例如,州、选区、政党、犯罪记录、教育资格、资产等)。然而,特定的候选人也可能赢得选举,这取决于被提名政党的地位。以下是一些可以代表政党对候选人获胜概率的重要性的特征。
1。该党赢得的席位总数
在投票之前,我们可能会考虑候选人的党派。如果一个政党有很高的胜算,那么这个政党的候选人也有同样的胜算(并不总是如此)。我们可以通过制作一个表示政党赢得的席位数的特征来突出显示该场景(获胜概率将通过计数来突出显示)。
2。政党在选区赢得的席位数
有些政党总是会赢得某个特定的州或选区。我在这里考虑了选区,因为这是可用的最小区域。
3。针对当事人的刑事案件
在投票之前,我们通常会考虑针对一个政党的犯罪记录数量。如果一个政党有更多的刑事案件(更多的腐败政客),该党赢得选举的机会可能会更小(反之亦然;-) ).我们可以通过制作一个表示某一方犯罪记录数量的特征来突出这种情况。
4。每个选区政党的刑事案件数量
我们还可以通过考虑每个选区的犯罪记录,将上述场景划分为子案例。这可能很好地代表了上述特征。
5。党内候选人的教育水平
在投票之前,我们还会考虑该党候选人的教育背景。如果一个政党提名更多受过教育的候选人,该政党可能会赢得选举(更高的概率)。在更早的步骤中,我们已经对教育资格进行了有意义的编码。所以如果我们统计一个政党中每个候选人的数量,这个特征将代表这个政党的教育水平。
6。各选区党内候选人的教育水平
正如我们对刑事案件所做的那样,我们也可以考虑每个选区政党的教育水平。
7。按党派划分的选区候选人人数
正如我已经提到的,一个特定的政党可能有更高的胜算,这取决于选区。如果一个政党为一个选区提名更多的候选人,选票将在候选人之间分配,每个候选人的百分比将减少。那会直接影响中奖概率。
下面是我创建的一些可能影响候选人获胜概率的更多功能。
1.每个州的选民总数
2.每个选区的选民总数
3.每个州的选区总数
因此,我在现有功能的基础上总共创建了 10 个新功能。其中一些功能可能不会影响模型的性能。
**# Preparing feature values**cons_per_state = {}
voters_per_state = {}
party_winningSeats = {}
party_criminal = {}
party_education = {}
party_totalCandidates_per_cons = {}
party_winningSeats_per_cons = {}
party_criminal_per_cons = {}
party_education_per_cons = {}
voters_per_cons = {}# group by state
subset = dataset[['STATE', 'CONSTITUENCY', 'TOTAL_ELECTORS']]
gk = subset.groupby('STATE')# for each state
for name,group in gk:
# total constituencies per state
cons_per_state[name] = len(group)
# total voters per state
voters_per_state[name] = group['TOTAL_ELECTORS'].sum()# group by party
subset = dataset[['PARTY', 'CONSTITUENCY', 'CRIMINAL_CASES', 'EDUCATION', 'WINNER']]
gk = subset.groupby('PARTY')# for each party
for name,group in gk:
# winning seats by party
party_winningSeats[name] = group[group['WINNER'] == 1.0].shape[0]
# criminal cases by party
party_criminal[name] = group['CRIMINAL_CASES'].sum()
# education qualification by party (sum of candidates)
party_education[name] = group['EDUCATION'].sum()
# group by constituency
gk2 = group.groupby('CONSTITUENCY')
# for each constituency
for name2, group2 in gk2:
key = name2 + '_' + name # cons_party
# total candidates by party in constituency
party_totalCandidates_per_cons[key] = len(group2)
# party winning seats in the constituency
party_winningSeats_per_cons[key] = group2[group2['WINNER'] == 1.0].shape[0]
# criminal cases by party in the constituency
party_criminal_per_cons[key] = group2['CRIMINAL_CASES'].sum()# education qualification by party in constituency (sum of candidates)
party_education_per_cons[key] = group2['EDUCATION'].sum()# Total voters per constituency
subset = dataset[['CONSTITUENCY', 'TOTAL_ELECTORS']]
gk = subset.groupby('CONSTITUENCY')# for each constituency
for name,group in gk:
voters_per_cons[name] = len(group) **# Applying feature values**# new feature columns
total_cons_per_state = []
total_voters_per_state = []
total_voters_per_cons = []winning_seats_by_party = []
criminal_by_party = []
education_by_party = []total_candidates_by_party_per_cons = []
winning_seats_by_party_per_cons = []
criminal_by_party_per_cons = []
education_by_party_per_cons = []# iterate through each row in the dataset
for row in dataset.itertuples():
subkey = row.CONSTITUENCY + '_' + row.PARTYtotal_cons_per_state.append(cons_per_state.get(row.STATE))
total_voters_per_state.append(voters_per_state.get(row.STATE))
total_voters_per_cons.append(voters_per_cons.get(row.CONSTITUENCY))
winning_seats_by_party.append(party_winningSeats.get(row.PARTY))
criminal_by_party.append(party_criminal.get(row.PARTY))
education_by_party.append(party_education.get(row.PARTY))
total_candidates_by_party_per_cons.append(party_totalCandidates_per_cons.get(subkey))
winning_seats_by_party_per_cons.append(party_winningSeats_per_cons.get(subkey))
criminal_by_party_per_cons.append(party_criminal_per_cons.get(subkey))
education_by_party_per_cons.append(party_education_per_cons.get(subkey))# append columns to dataset
dataset['total_cons_per_state'] = total_cons_per_state
dataset['total_voters_per_state'] = total_voters_per_state
dataset['total_voters_per_cons'] = total_voters_per_cons
dataset['winning_seats_by_party'] = winning_seats_by_party
dataset['criminal_by_party'] = criminal_by_party
dataset['education_by_party'] = education_by_party
dataset['total_candidates_by_party_per_cons'] = total_candidates_by_party_per_cons
dataset['winning_seats_by_party_per_cons'] = winning_seats_by_party_per_cons
dataset['criminal_by_party_per_cons'] = criminal_by_party_per_cons
dataset['education_by_party_per_cons'] = education_by_party_per_cons
让我们再次训练我们的模型,看看准确性如何提高。无需对任何已创建的要素进行标注编码,因为它们都是数字。然而,在训练模型之前,我将它们标准化为 0-1 范围。
**Testing Accuracy is: 96.78217821782178 %**
功能重要性
移除不相关的特征或不太重要的特征将提高模型的准确性。到目前为止,我们已经在 28 个特性上训练了我们的最新模型,包括新创建的 10 个。然而,这些特征中的一些可能对基于训练数据得出结论没有太大贡献。移除那些特征不会降低模型的准确性,希望会提高它,因为不相关的特征可能为模型得出无效的推论。
首先,我们可以通过分析它们的贡献来删除一些特征。NAME 列不会对模型做出任何有用的推断,因为理想情况下,名称应该是惟一的。然而,在某些情况下,姓氏可能很重要,因为一些姓氏可能会对赢得选举产生影响。此外,PARTY 和 SYMBOL 这两个变量将表示相同的特征,我们将能够在不影响准确性的情况下移除它们中的一个。TOTAL_VOTES 列包含 GENERAL_VOTES 列和 POSTAL_VOTES 列的总和。所以我们也可以去掉这两个。如果我们绘制代表相关矩阵的热图,我们将看到这 3 个特征高度相关。
# remove unnecessary columnsX.drop(labels=["NAME"], axis=1, inplace=True)
X.drop(labels=["SYMBOL"], axis=1, inplace=True)
X.drop(labels=["POSTAL_VOTES"], axis=1, inplace=True)
X.drop(labels=["GENERAL_VOTES"], axis=1, inplace=True)
有一些技术可以根据重要性来选择特性。我将使用单变量选择方法来识别最重要的特征,并删除其他特征。我将使用 Scikit learn 库中的 SelectKBest 和卡方检验来评估特性的重要性。
# apply SelectKBest class to extract top most features
bestfeatures = SelectKBest(score_func=chi2, k=10)
fit = bestfeatures.fit(X, y)
dfscores = pd.DataFrame(fit.scores_)
dfcolumns = pd.DataFrame(X.columns)# concat two dataframes for better visualization
featureScores = pd.concat([dfcolumns, dfscores], axis=1)
featureScores.columns = ['Specs', 'Score']
print(featureScores.nlargest(30, 'Score'))
Specs Score
25 winning_seats_by_party_per_cons 486.207788
16 OVER_TOTAL_VOTES_POLLED_IN_CONSTITUENCY 285.347141
15 OVER_TOTAL_ELECTORS_IN_CONSTITUENCY 262.177373
14 TOTAL_VOTES 216.937788
12 GENERAL_VOTES 216.138799
21 winning_seats_by_party 199.662525
13 POSTAL_VOTES 65.126864
22 criminal_by_party 36.519437
3 PARTY 35.416433
23 education_by_party 6.576548
11 LIABILITIES 6.330339
24 total_candidates_by_party_per_cons 5.755538
20 total_voters_per_cons 5.302656
4 SYMBOL 4.128283
8 CATEGORY 4.047031
10 ASSETS 3.755575
7 AGE 2.077768
9 EDUCATION 0.888330
27 education_by_party_per_cons 0.840185
18 total_cons_per_state 0.481673
6 CRIMINAL_CASES 0.436667
19 total_voters_per_state 0.292948
26 criminal_by_party_per_cons 0.178720
5 GENDER 0.145870
1 CONSTITUENCY 0.143250
0 STATE 0.076833
17 TOTAL_ELECTORS 0.054486
2 NAME 0.003039
让我们去掉所有分数小于 3 的列。请注意,我之前创建的一些特性对于 KNN 模型也不重要。
X.drop(labels=["TOTAL_ELECTORS"], axis=1, inplace=True)
X.drop(labels=["STATE"], axis=1, inplace=True)
X.drop(labels=["CONSTITUENCY"], axis=1, inplace=True)
X.drop(labels=["GENDER"], axis=1, inplace=True)
X.drop(labels=["criminal_by_party_per_cons"], axis=1, inplace=True)
X.drop(labels=["total_voters_per_state"], axis=1, inplace=True)
X.drop(labels=["CRIMINAL_CASES"], axis=1, inplace=True)
X.drop(labels=["total_cons_per_state"], axis=1, inplace=True)
X.drop(labels=["EDUCATION"], axis=1, inplace=True)
X.drop(labels=["education_by_party_per_cons"], axis=1, inplace=True)
X.drop(labels=["AGE"], axis=1, inplace=True)
现在让我们用最重要的特征再次训练我们的模型,并评估准确性。
**Testing Accuracy is: 99.5049504950495 %**
结论
从上面的结果可以看出,特征工程步骤极大地提高了模型的性能。从 0.7 的初始精度开始,我们已经达到了 0.99 的精度。首先,我们清理数据集并将所有值转换成数字格式。然后,我们使用现有的功能创建一些新功能,并删除所有不太重要的功能。最后,我们将这些值缩小到 0–1 的范围,并训练一个 K-最近邻模型。请注意,我们可以通过应用交叉验证来进一步提高准确性。作为结论,我想把每个主要步骤后的准确性总结如下。
**Feature Engineering Step Testing Accuracy**Initially without any data processing 0.7079
Scale down values into 0-1 range 0.9059
Basic encoding of two columns 0.9208
Creating new features 0.9678
Removing unnecessary features 0.9950
数字数据的特征工程
工程数值的技巧
数字数据几乎是一种福气。为什么差不多?因为它已经是一种机器学习模型可以接受的格式。然而,如果我们把它翻译成与人类相关的术语,仅仅因为一个博士级别的教科书是用英语写的——我用英语说、读和写——并不意味着我能够很好地理解教科书以获得有用的见解。让教科书对我有用的是,它以一种考虑到我的心理模型假设的方式概括了最重要的信息,比如“数学是一个神话”(顺便说一下,这不再是我的观点,因为我真的开始喜欢它了)。同样,一个好的特征应该代表数据的突出方面,以及机器学习模型所做的假设的形状。
图 1:一名士兵在 Unsplash 上拍摄的照片
特征工程是从原始数据中提取特征并将其转换为机器学习模型可以接受的格式的过程。通常需要转换来降低建模的难度并提高模型的结果。因此,设计数字数据类型的技术是数据科学家(机器学习工程师等)的基本工具。
“数据就像机器学习中的原油,这意味着它必须被提炼为特征——预测变量——才能对训练模型有用。”- 威尔·科尔森
当我们努力掌握时,重要的是要注意,仅仅知道一个机制为什么工作以及它能做什么是远远不够的。精通知道事情是如何完成的,对潜在的原则有直觉,并有神经连接,这使得在面临挑战时绘制正确的工具成为一个无缝的过程。这不会来自于阅读这篇文章,而是来自于有意的实践,这篇文章将通过提供技术背后的直觉为你打开大门,这样你可以理解如何以及何时应用它们。
数据中的特征将直接影响您使用的预测模型以及您可以实现的结果。”——杰森·布朗利
注意:您可以在我的 Github 页面上找到本文使用的代码
与中等博客文章相关的演示代码。-路径/到/文件;链接到文章有效的数据可视化…
github.com](https://github.com/kurtispykes/demo/tree/master)
可能会有这样的情况,即在累积的要素上收集数据,从而具有无限的上限。这种类型的连续数据的例子可能是一个跟踪系统,该系统监视我的所有媒体帖子每天接收的访问量。这种类型的数据很容易吸引离群值,因为可能会有一些不可预测的事件影响我的文章累积的总流量,例如有一天,人们可能会决定他们希望能够进行数据分析,所以我关于有效数据可视化的文章可能会在那一天出现峰值。换句话说,当数据可以被快速和大量地收集时,那么它很可能包含一些需要工程的极端值。
处理此实例的一些方法有:
量子化
此方法通过将值分组到箱中来包含数据的比例。因此,量化将连续值映射为离散值,从概念上讲,这可以被认为是有序的二进制序列。为了实现这一点,我们必须考虑我们创建的箱的宽度,其解决方案分为两类,固定宽度箱或自适应箱。
注意:这对于线性模型特别有用,在基于树的模型中这是没有用的(基于树的模型进行它们自己的分割)。
在固定宽度的情况下,该值是自动或自定义设计的,用于将数据分割到离散的条块中,它们也可以线性缩放或指数缩放。一个流行的例子是以十年为间隔将人的年龄分成几个部分,例如容器 1 包含 0-9 岁,容器 2 包含 10-19 岁,等等。
请注意,如果这些值跨越大量的数字,那么更好的方法可能是将这些值分组为常数的幂,例如 10 的幂:0–9、10–99、100–999、1000–9999。请注意,仓位宽度呈指数增长,因此在 1000–9999 的情况下,仓位宽度为 O(10000),而 0–9 为 O(10)。获取计数的日志,以从计数映射到数据的 bin。
import numpy as np #15 random integers from the "discrete uniform" distribution
ages = np.random.randint(0, 100, 15)#evenly spaced bins
ages_binned = np.floor_divide(ages, 10)print(f"Ages: {ages} \nAges Binned: {ages_binned} \n")
>>> Ages: [97 56 43 73 89 68 67 15 18 36 4 97 72 20 35]
Ages Binned: [9 5 4 7 8 6 6 1 1 3 0 9 7 2 3]#numbers spanning several magnitudes
views = [300, 5936, 2, 350, 10000, 743, 2854, 9113, 25, 20000, 160, 683, 7245, 224]#map count -> exponential width bins
views_exponential_bins = np.floor(np.log10(views))print(f"Views: {views} \nViews Binned: {views_exponential_bins}")
>>> Views: [300, 5936, 2, 350, 10000, 743, 2854, 9113, 25, 20000, 160, 683, 7245, 224]
Views Binned: [2\. 3\. 0\. 2\. 4\. 2\. 3\. 3\. 1\. 4\. 2\. 2\. 3\. 2.]
当计数中有大的间隙时,自适应箱是更好的选择。当计数值之间有大的余量时,一些固定宽度的仓将是空的。
为了进行自适应宁滨,我们可以利用数据的分位数,即像中位数一样将数据分成相等部分的值。
import pandas as pd#map the counts to quantiles (adaptive binning)
views_adaptive_bin = pd.qcut(views, 5, labels=False)print(f"Adaptive bins: {views_adaptive_bin}")
>>> Adaptive bins: [1 3 0 1 4 2 3 4 0 4 0 2 3 1]
电力变换
我们已经看到了一个这样的例子…对数变换是方差稳定变换系列的一部分,称为幂变换。维基百科将幂变换描述为一种*“用于稳定方差、使数据更像正态分布、提高关联测量的有效性(如变量之间的皮尔逊相关性)以及用于其他数据稳定程序的技术。”*
为什么我们要转换数据以符合正态分布?很棒的问题!您可能希望使用参数模型——对数据进行假设的模型——而不是非参数模型。当数据呈正态分布时,参数模型非常强大。然而,在某些情况下,我们拥有的数据可能需要帮助来呈现正态分布的美丽的钟形曲线,例如,数据可能是倾斜的,因此我们应用幂变换来帮助我们的特征看起来更像高斯。
下面的代码利用数据科学框架(如 pandas、scipy 和 numpy)来演示电源转换,并使用 Plotly.py 框架将它们可视化以进行交互式绘图。使用的数据集是 Kaggle 的 房价:高级回归技术 ,可以轻松下载( 点击此处获取数据 )。
import numpy as np
import pandas as pd
from scipy import stats
import plotly.graph_objects as go
from plotly.subplots import make_subplotsdf = pd.read_csv("../data/raw/train.csv")# applying various transformations
x_log = np.log(df["GrLivArea"].copy()) # log
x_square_root = np.sqrt(df["GrLivArea"].copy()) # square root x_boxcox, _ = stats.boxcox(df["GrLivArea"].copy()) # boxcox
x = df["GrLivArea"].copy() # original data# creating the figures
fig = make_subplots(rows=2, cols=2,
horizontal_spacing=0.125,
vertical_spacing=0.125,
subplot_titles=("Original Data",
"Log Transformation",
"Square root transformation",
"Boxcox Transformation")
)# drawing the plots
fig.add_traces([
go.Histogram(x=x,
hoverinfo="x",
showlegend=False),
go.Histogram(x=x_log,
hoverinfo="x",
showlegend=False), go.Histogram(x=x_square_root,
hoverinfo="x",
showlegend=False), go.Histogram(x=x_boxcox,
hoverinfo="x",
showlegend=False),
],
rows=[1, 1, 2, 2],
cols=[1, 2, 1, 2]
)fig.update_layout(
title=dict(
text="GrLivArea with various Power Transforms",
font=dict(
family="Arial",
size=20)),
showlegend=False,
width=800,
height=500)fig.show() # display figure
图 2:可视化原始数据和各种电力转换电力转换。
注意:Box-cox 变换仅在数据为非负时有效
“这些中哪一个最好?你无法事先知道。你必须尝试它们,并评估结果,以实现你的算法和性能指标。”- 杰森·布朗利
特征缩放
顾名思义,特征缩放(也称为特征归一化)与改变特征的比例有关。当数据集的要素在比例上有很大差异时,对输入要素比例敏感的模型(即线性回归、逻辑回归、神经网络)会受到影响。确保特征在相似的范围内是必要的。然而,诸如基于树的模型(即决策树、随机森林、梯度增强)之类的模型不关心规模。
缩放要素的常用方法包括最小-最大缩放、标准化和 L 归一化,接下来是 python 中的简单介绍和实现。
最小-最大缩放 -特征被缩放到一个固定的范围(通常在 0-1 之间),这意味着我们将减少标准偏差,从而抑制异常值对特征的影响。其中 x 是实例的单个值(即人 1,特征 2),max(x),min(x)是特征的最大值和最小值,见图 3。关于这方面的更多信息,请参见 sklearn 文档。
图 3:最小-最大缩放的公式
标准化 -将重新调整特征值,使其符合均值为 0、标准差为 1 的正态分布的属性。为此,我们从特征实例值中减去特征的平均值(所有实例的平均值),然后除以方差,见图 4。 Sklearn 文档用于标准化。
图 4:标准化公式
L 归一化 -这种技术将原始特征值除以 L 范数(也称为欧氏距离),这是图 5 中的第二个等式。l 范数取所有实例的特征集中的值的平方和。L 范数的 Sklearn 文档(注意,也可以通过将norm
参数设置为"l1"
来进行 L 归一化)。
图 5:L 归一化的公式
特征缩放效果的可视化将给出正在发生的事情的更好的图像。为此,我使用可以从 sklearn 数据集导入的葡萄酒数据集。
import pandas as pd
from sklearn.datasets import load_wine
from sklearn.preprocessing import StandardScaler, MinMaxScaler, Normalizer
import plotly.graph_objects as gowine_json= load_wine() # load in datasetdf = pd.DataFrame(data=wine_json["data"], columns=wine_json["feature_names"]) # create pandas dataframedf["Target"] = wine_json["target"] # created new column and added target labels# standardization
std_scaler = StandardScaler().fit(df[["alcohol", "malic_acid"]])
df_std = std_scaler.transform(df[["alcohol", "malic_acid"]])# minmax scaling
minmax_scaler = MinMaxScaler().fit(df[["alcohol", "malic_acid"]])
df_minmax = minmax_scaler.transform(df[["alcohol", "malic_acid"]])# l2 normalization
l2norm = Normalizer().fit(df[["alcohol", "malic_acid"]])
df_l2norm = l2norm.transform(df[["alcohol", "malic_acid"]])# creating tracestrace1 = go.Scatter(x= df_std[:, 0],
y= df_std[:, 1],
mode= "markers",
name= "Standardized Scale")trace2 = go.Scatter(x= df_minmax[:, 0],
y= df_minmax[:, 1],
mode= "markers",
name= "MinMax Scale")trace3 = go.Scatter(x= df_l2norm[:, 0],
y= df_l2norm[:, 1],
mode= "markers",
name= "L2 Norm Scale")trace4 = go.Scatter(x= df["alcohol"],
y= df["malic_acid"],
mode= "markers",
name= "Original Scale")layout = go.Layout(
title= "Effects of Feature scaling",
xaxis=dict(title= "Alcohol"),
yaxis=dict(title= "Malic Acid")
)data = [trace1, trace2, trace3, trace4]
fig = go.Figure(data=data, layout=layout)
fig.show()
图 6:原始特性和各种缩放实现的曲线图。
功能交互
我们可以通过使用特征之间成对交互的乘积来创建逻辑 AND 函数。在基于树的模型中,这些交互是隐式发生的,但是在假设特征独立的模型中,我们可以显式地声明特征之间的交互以改进模型的输出。
考虑一个简单的线性模型,该模型使用输入要素的线性组合来预测输出 y:
图 7:线性模型的公式
我们可以扩展线性模型来捕捉特征之间发生的交互。
图 8:扩展线性模型
注意:使用线性函数是昂贵的,并且具有成对交互的线性模型的评分和训练将从 O(n)到 O(n)。但是,您可以执行特征提取来克服这个问题(特征提取超出了本文的范围,但是我将在以后的文章中讨论)。
让我们用 python 来编码这个,我将利用 scitkit-learn PolynomialFeatures
类,你可以在文档中读到更多关于它的内容:
import numpy as np
from sklearn.preprocessing import PolynomialFeatures# creating dummy dataset
X = np.arange(10).reshape(5, 2)
X.shape
>>> (5, 2)# interactions between features only
interactions = PolynomialFeatures(interaction_only=True)
X_interactions= interactions.fit_transform(X)
X_interactions.shape
>>> (5, 4)# polynomial features
polynomial = PolynomialFeatures(5)
X_poly = polynomial.fit_transform(X)
X_poly.shape
>>> (5, 6)
这篇文章很大程度上受到了《机器学习的特征工程:数据科学家的原则和技术》这本书的启发,我强烈推荐阅读这本书。虽然它是在 2016 年出版的,但它仍然信息量很大,解释清楚,即使对那些没有数学背景的人来说也是如此。
结论
在本文中,我们讨论了处理数字特征的技术,如量化、幂变换、特征缩放和交互特征(可应用于各种数据类型)。这绝不是特性工程的全部,每天都有更多的东西需要学习。特征工程是一门艺术,需要实践,所以现在你有了直觉,你就可以开始实践了。您可以在我的 Github 上找到本文中使用的代码(链接如下)。非常感谢您的宝贵时间!
与中等博客文章相关的演示代码。-路径/到/文件;链接到文章有效的数据可视化…
github.com](https://github.com/kurtispykes/demo/tree/master)
其他资源……
Jason Brownlee - 探索特性工程,如何设计特性以及如何做好它
Emre ren bero Lu-机器学习的特征工程基础技术
黛博拉·拉姆齐- 统计数据的类型:数字、分类和顺序
Alice Zheng & Amanda Casari - 机器学习的特征工程:数据科学家的原则和技术