用 Flask 和 SQLalchemy,不要用 Flask-SQLAlchemy!
避免 Flask 应用程序中的 Flask-SQLAlchemy
作者: Edward Krueger 数据科学家兼讲师和 Douglas Franklin 助教兼技术作家。
在这篇文章中,我们将介绍如何使用带有 SQLAlchemy 的烧瓶,以及避免烧瓶 SQLAlchemy 的一些原因。此外,我们将展示拥有独立于应用程序的 SQLAlchemy 模型和数据库连接的好处。
Tobias Fischer 在 Unsplash 上拍摄的照片
SQLAlchemy 是什么?
SQLAlchemy 是一个 Python SQL 工具包和对象关系映射器(ORM ),允许应用程序开发人员使用 SQL 进行平滑和容错的事务数据库操作。ORM 将 Python 类转换成关系数据库的表,并自动将 Pythonic SQLAlchemy 表达式语言转换成 SQL 语句。这种转换允许开发人员使用 Python 语法编写 SQL 查询。SQLAlchemy 还抽象数据库连接,并自动提供连接维护。这些特性使 SQLAlchemy 成为装载和查询数据库的绝佳包。
什么是烧瓶?
Flask 是一个微框架,允许你用 Python 构建 web 应用。对于初学者来说,Flask 很容易上手,因为启动和运行一个简单的应用程序几乎没有样板代码。
例如,下面是一个有效的“你好,世界!”Flask web 应用程序:
**from flask import Flask****app = Flask(__name__)****@app.route('/')
def hello_world():
return "Hello, World!"****if __name__ == '__main__':
app.run()**
什么是 Flask-SQLAlchemy?
Flask-SQLAlchemy 是 Flask 的扩展,旨在通过提供缺省值和助手来完成常见任务,从而简化 SQLAlchemy 与 Flask 的使用。最受欢迎的助手之一是处理整个应用程序的数据库连接。但是,使用 base SQLAlchemy 可以确保数据库连接会话在整个应用程序中可用,而不需要 Flask-SQLAlchemy。
Flask-SQLAlchemy 的目的是处理连接的返回,以防止工作线程出现问题。当应用程序用户从一条路线切换到另一条路线时,就会出现这些问题。以下是 Flask 应用程序中出现线程问题时出现的常见错误。
**sqlite3.ProgrammingError: SQLite objects created in a thread can only be used in that same thread. The object was created in thread id 12345 and this is thread id 54321.**
这通常是由于会话或数据库连接对于应用程序的一部分不可用。这个错误实际上会破坏您的应用程序,必须解决它才能继续开发。
烧瓶-SQLAlchemy 弱点
我们选择避免 Flask-SQLALchemy 默认行为来防止这种错误,而是使用 SQLALchemy 特性来处理这些问题。这是因为 Flask-SQLALchemy 与 SQLALchemy 相比有劣势。
其中之一是 Flask-SQLAlchemy 有自己 API。这增加了复杂性,因为 ORM 查询和模型的不同方法与 SQLAlchemy API 是分开的。
另一个缺点是 Flask-SQLAlchemy 使得在 Flask 上下文之外使用数据库变得困难。这是因为,使用 Flask-SQLAlchemy,数据库连接、模型和 app 都位于 app.py 文件中。由于模型在应用程序文件中,我们与应用程序之外的数据库进行交互的能力有限。这使得在应用程序之外加载数据变得困难。此外,这使得很难在 Flask 上下文之外检索数据。
如果使用正确,Flask 和 SQLAlchemy 可以很好地配合使用。所以,你不用把 Flask 和 SQLAlchemy 杂交成 Flask-SQLalchemy!
使用 SQLAlchemy,而不是 Flask-SQLAlchemy!
一次定义数据库和模型
理想情况下,您应该只需要定义一次数据库模型!使用单独的 database.py 和 models.py 文件,我们可以一次性为其表建立数据库连接和类,然后在需要时调用它们。使用 Flask-SQLAlchemy 进行组件分离要困难得多,因为您必须使用应用程序本身来创建数据库。
下面是一个使用 SQLAlchemy 定义数据库连接的文件。
示例 database.py 文件
注意,我们将上面 database.py 文件中的‘Base’类导入到下面的 models.py 文件中,以使用declarative_base()
。
示例 models.py 文件
该文件为数据库中的表“Records”创建模型或模式。
使用 SQLAlcehmy 的declarative_base()
允许您为应用程序使用的每个表编写一个模型。然后,在 Python 中,在应用程序之外和数据库中使用该模型。
在应用程序外部加载数据
拥有这些独立的 Python 文件很好,因为您可以使用相同的模型在应用程序之外查询或加载数据。此外,每个模型和数据库连接都有一个版本,这简化了开发。
这些模型和数据库连接可用于在数据管道、报告生成或任何其他需要的地方引用相同的模型或数据库。
单单是加载脚本就是使用 SQLAlchemy 的一个很好的理由。该功能允许使用单独的 Python 文件加载数据库,而不是使用应用程序加载数据。
下面是一个示例 Python 文件,它从 CSV 读取数据,并将数据插入到数据库中。
load.py 脚本示例
请注意,我们导入了模型、我们的自定义会话 SessionLocal 和我们在其他 Python 文件中定义的引擎。
这个单独的 load.py 文件允许我们在不运行应用程序的情况下将数据插入数据库。
声明性库和元数据
**declarative_base()**
基类包含一个**MetaData**
对象,其中收集了新定义的**Table**
对象。当我们调用行**models.Base.metadata.create_all()**
来创建我们所有的表时,这个元数据对象被访问。
作用域会话和本地会话:处理线程问题
SQLAlchemy 包括一个助手对象,帮助建立用户定义的**Session**
范围。通过**scoped_session**
函数,SQLAlchemy 可以处理工作线程问题。
sessionmaker 是一个工厂,用于通过从引擎的连接池中请求连接并将连接附加到新的会话对象来初始化新的会话对象。
初始化新的会话对象也称为“检出”连接。数据库存储这些连接/过程的列表。因此,当您开始一个新的会话时,您也在数据库中启动了一个新的进程。
scoped_session 是所有这些创建的会话对象的注册表,其中注册表的键/标识是某种形式的线程安全 id。
我们在上面的 database.py 文件中定义了 SessionLocal,方法是调用会话工厂 sessionmaker,并向其传递一些参数。
来自 database.py 的会话本地
scopefunc '是传递给 scoped_session 的可选参数,用作标识 getter 函数,返回用于查找或注册新会话的密钥。正在添加
_app_ctx_stack。ident_func `是两个函数之一:
- 如果安装了 greenlet,它会使用
getcurrent
(from greenlet import getcurrent
) - 否则,它使用
get_ident
(from threading import get_ident
),返回线程 id。
默认情况下 scopefunc 是get_ident
。因此,对于简单的应用程序,您只需:
db_session = scoped_session(sessionmaker(autocommit=False, autoflush=False, bind=engine))
这个新的 SessionLocal 允许应用程序的不同部分调用一个全局scoped_session
,这样所有的应用程序路由都可以共享同一个会话,而不用显式地将会话传递给路由。我们在注册表中建立的 SessionLocal 将一直保留,直到我们通过调用scoped_session.remove()
明确地告诉我们的注册表处理它:
这里我们可以看到对 scoped_session 的调用,其中我们的自定义会话 SessionLocal 作为参数传递。
app.py 文件通过调用 scoped_session 来定义 app.session
此外,作用域会话使我们能够访问 query_property。因此,如果您的样式由 flask_sqlalchemy 使用,您可以将它与 sqlalchemy 一起使用:
Base = declarative_base()
Base.query = db_session.query_property()
结束交易
方法db_session.close()
只结束本地会话对象的事务,但不结束与数据库的连接,也不自动将连接返回到池。
通过添加 db_session.remove(),我们可以确保连接被正确关闭。
db_session.remove()方法首先运行app.db_session.close()
,然后将连接返回到连接池。因此,我们在本地和远程数据库终止了该进程。如果数据库没有关闭这些连接,则存在可以达到的最大连接数。数据库最终会终止像过时连接这样的空闲进程;然而,这可能需要几个小时才会发生。SQLAlchemy 有一些池选项来防止这种情况,但是最好在不再需要连接时删除它们!
结论
正如我们所看到的,SQLAlchmy 有工具来处理开发人员求助于 Flask-SQLAlchemy 来避免的错误。通过正确实现 sessionmaker 和 scoped_session,您的 Flask 应用程序应该不会有任何线程问题,这些问题会在通过您的路由连接到数据库时出现。
因此,在处理线程和数据库会话时,使用 Flask 和 SQLAlchemy,而不是 Flask-SQLAlchemy!
特别感谢 Seth Kaufman 帮助编写我们的 Flask 应用程序,请务必查看 GitHub 上的资源库。
使用谷歌工作表,S3 和 Python 快速建立一个网站
非 web 开发人员生存指南
在 Unsplash 上 barnimages 拍摄的照片
在我们开始之前,先介绍一些背景知识:
几天前,我在寻找因新冠肺炎而免费或打折的课程。我想其他人可能也在这样做,所以我决定收集一些资源并在网上发布。
我开始用 Google Sheet 编辑课程,并计划在获得足够的资源后分享它。然而,有件事困扰着我。在手机上打开页面很糟糕,大多数人用手机浏览互联网。我以为我能做得更好。
问题是我在网站开发方面没有什么经验。此外,我不想花费超过几个小时来开发和发布网站。所以我决定快速建造一些东西。
以下是我为快速建立网站而设定的要求,以及我为满足这些要求而采取的方法:
要求
- 该网站应该加载速度快,在手机上看起来很好(或者至少比谷歌表更好)
- 我需要能够快速、轻松地添加或删除资源。请记住,我可能希望在未来从客户端添加更多的交互性。
- 尽可能多地使用 Python(尽可能少地使用 HTML/CSS/JS)
- 我应该只需要几个小时就可以开发和发布
- 维护成本应该非常非常接近零😛
接近
- 用于引导程序(仅 CSS,不包括 JS 组件)
- 使用 Google Sheets 管理内容。我使用 Google Sheets API 以编程方式将内容放入站点。如果需要,可以很容易地从 Google Sheet 创建一个 web API。
- 使用 Jinja2 从基本的 HTML/CSS 模板生成网站
- 在 AWS S3 bucket + Cloudfront 上托管站点(用于 SSL 证书)。用 53 号公路买下了域名
- 我对 AWS 服务的使用属于免费层。我不认为这种情况会很快改变。到目前为止,我只在网站上投资了 15€
几个小时后,我发射了stayhomeandlearn.org。
本文的其余部分是关于如何使用 Google Sheets、AWS 和 Python 构建静态站点的教程。
对于本教程,**我们将建立一个从 Google Sheets 读取数据的脚本,使用预定义的模板生成一个静态站点,并将其部署到 S3 存储桶。**这篇文章是为那些对 web 开发知之甚少,但想快速上手的程序员准备的。
教程中有五个部分:需求,回顾代码和 Jinja 模板,使用 Google Sheets API,构建和部署你的站点。
要求
这些你需要自己设置或检查。为此,我添加了一些链接。
- Python > = 3.7
- 谷歌账户
- 谷歌云平台(GCP)账户
- 亚马逊 AWS 账户
- AWS CLI (如果你有 Mac 的话可以使用 brew)
- 在 AWS CLI 中配置的配置文件
- 一点 HTML 和 CSS
代码和 Jinja 模板
首先,创建一个名为my_pokemon_stats
的目录,并从那里打开一个终端。然后,创建一个虚拟环境并安装所需的软件包,如下所示:
接下来,在那里下载并保存这两个文件:template.html和 site_builder.py 。这些是生成站点的构建块。
template.html
是我们将用于构建网站的 Jinja 模板。这是一个类似 HTML 的文件,您可以在其中添加将用 Python 处理的逻辑,并生成最终的站点。该文件如下所示:
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<link href="[https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css](https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css)" rel="stylesheet"
integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous">
<title>My Pokemon Stats</title>
</head>
<body><header id="header">
<div class="container text-center">
<h1 class="pt-5 pb-1 font-weight-bold">
My Pokemon Stats
</h1>
<hr>
<p class="pt-2">
This is a site I use to store the stats of all my Pokemon.
</p>
</div>
</header><section id="pokemon_table">
<div class="container py-4">
<div class="table-responsive">
<table class="table table-hover">
<thead class="thead-dark">
<tr>
<th scope="col">Name</th>
<th scope="col">Type 1</th>
<th scope="col">Type 2</th>
<th scope="col">Total</th>
<th scope="col">HP</th>
<th scope="col">Attack</th>
<th scope="col">Defense</th>
</tr>
</thead>
<tbody>
{% for row in data %}
<tr>
<td>{{ row["Name"] }}</td>
<td>{{ row["Type 1"] }}</td>
<td>{{ row["Type 2"] }}</td>
<td>{{ row["Total"] }}</td>
<td>{{ row["HP"] }}</td>
<td>{{ row["Attack"] }}</td>
<td>{{ row["Defense"] }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</section>
</body>
</html>
让我们来分解一下:
- 您可以放心地忽略
<head>
标签中的大部分内容。这是你将在大多数页面中看到的标准 HTML5 代码。然而,我们将仔细研究两个有趣的标签:<link>
和<title>
。 - 在这种情况下,
<link>
标签用于导入引导组件库。我们将使用它来为页面的不同部分定义简单的样式,并毫不费力地使它看起来很好。<title>
定义页面的标题(你在浏览器标签中看到的),它对 SEO 和社交媒体分享很有用。 - 接下来,在
<body>
标签中有一个<header>
部分。这是我们定义将出现在页面上的文本的地方。它将看起来像下面的图像。我使用了 Bootstrap 的标准样式来居中文本,并添加了一些填充。
- 最后,我们有了
<section id="pokemon_table">
。<div>
和<table>
标签为构建表格提供了一些基本的样式。接下来,我们在<thead>
标签中定义表格的标题。标签里面是 Jinja 施展魔法的地方 {% for row in data %}
是一个循环,遍历口袋妖怪的每一行数据。在每个<td>{{ row["..."] }}</td>
中,我们从字段中获取每一行的信息(例如名称、类型 1、类型 2)。这会生成如下所示的内容:
接下来,我们有了site_builder.py
文件。这个脚本从 Google Sheets 下载口袋妖怪的数据,处理数据和template.html
文件,然后将结果文件上传到 S3 桶。
import csv
import boto3
import gspread
import jinja2
from oauth2client.service_account import ServiceAccountCredentials
AWS_PROFILE = "INSERT-AWS-PROFILE-HERE"
BUCKET = "INSERT-BUCKET-NAME-HERE"
WORKBOOK = "INSERT-WORKBOOK-NAME-HERE"
def download_data():
"""Download data using the Google Sheets API"""
scope = [
"https://spreadsheets.google.com/feeds",
"https://www.googleapis.com/auth/drive",
]
credentials = ServiceAccountCredentials.from_json_keyfile_name(
"credentials.json", scope
)
client = gspread.authorize(credentials)
worksheet = client.open(WORKBOOK).get_worksheet(0)
sheet_values = worksheet.get_all_values()
print(f"Downloading: {worksheet.title}")
with open("my_pokemon_stats.csv", "w") as f:
writer = csv.writer(f)
writer.writerows(sheet_values)
def generate_site():
"""Generate site in local directory"""
print("Process data and build site")
template_loader = jinja2.FileSystemLoader(searchpath="./")
template_env = jinja2.Environment(loader=template_loader)
template = template_env.get_template("template.html")
with open("my_pokemon_stats.csv") as csv_file:
csv_reader = csv.DictReader(csv_file)
data = [row for row in csv_reader]
output = template.render(data=data)
with open("index.html", "w") as f:
f.write(output)
def deploy_site():
"""Deploy site S3 bucket"""
print("Upload data to S3")
session = boto3.Session(profile_name=AWS_PROFILE)
s3 = session.resource("s3")
s3.Bucket(BUCKET).upload_file(
Filename="index.html", Key="index.html", ExtraArgs={"ContentType": "text/html"}
)
if __name__ == "__main__":
download_data()
generate_site()
deploy_site()
代码由三个功能构成:download_sheets
、generate_site
和deploy_site
。我们将在接下来的小节中详细介绍如何访问 AWS 和 Google Sheets API。
使用 Google Sheets API
按照以下步骤使用 Google Sheets API 下载口袋妖怪的数据:
- 在谷歌工作表中创建一个工作簿(你可以复制我的:我的口袋妖怪统计)
- 进入谷歌 API 控制台
- 创建一个名为 MyPokemonStats 的新项目。
- 点击启用 API 和服务。搜索并启用 Google Sheets API。
- 回到Google API 控制台并再次点击启用 API 和服务。现在搜索并启用 Google Drive API。
- 点击创建凭证。 下 4 题选择:Google Drive API, Web 服务器(如 node.js,Tomcat) ,应用数据,和不,我不使用它们。
- 点击我需要什么凭证?为服务账户选择一个名称(例如 get-data)授予其一个项目角色编辑*。为键类型选择 JSON 选项*
- 将会打开一个对话框。保存 JSON 文件,复制到
my_pokemon_stats
目录,重命名为credentials.json
。 - 打开
credentials.json
文件。找到一个名为client_email
的键,复制它的值(例如 get-data@iam…).在 Google Sheets 中返回到您的工作簿,点击右上角的共享按钮,将客户电子邮件粘贴到人员字段,赋予其编辑权限。点击发送。 - 转到
site_builder.py
脚本,将WORKBOOK
变量设置为您在第一步中给工作簿起的名字。
设置 S3 时段和与 AWS 相关的配置
现在,让我们创建 S3 桶,并配置我们的代码以编程方式访问 AWS:
- 前往亚马逊 S3 控制台
- 创建 S3 存储桶
- 一旦进入桶中,点击属性*,然后点击*静态网站托管**
- 选择选项使用此桶托管网站**
- 在索引文件和错误文件下放置
index.html
- 保存来自端点的 URL。您将使用该 URL 连接到您的站点。
- 转到权限并点击编辑**
- 清除阻止所有公共访问*,选择保存,并确认。当您更改此项时,互联网上的任何人都可以访问此存储桶的内容。当你发布一个网站时,这就是你想要的,但是,不要把任何私人的东西放在那里!*
- 现在转到 Bucket Policy ,替换下面策略中的 Bucket 名称,粘贴到那里,点击 Save。
*{
"Version":"2012-10-17",
"Statement":[{
"Sid":"PublicReadGetObject",
"Effect":"Allow",
"Principal": "*",
"Action":["s3:GetObject"],
"Resource":["arn:aws:s3:::BUCKET-NAME-HERE/*"
]
}
]
}*
10.转到site_builder.py
脚本。将变量AWS_PROFILE
变量设置为用于访问 AWS 的配置文件名称(在 UNIX 系统中,它应该是~/.aws/credentials
中的一个配置文件)。
构建和部署您的站点
最后,您应该能够从项目的根文件夹中运行python site_builder.py
来生成站点。这将从谷歌工作表下载数据,使用 Jinja 处理template.html
文件,并将网站上传到 S3 桶。
如果您想要检查站点,请转到端点 URL(上一节中的步骤 6)。
结束语
这种方法绝不是完美的,但会帮助你快速出货。我用这个策略建造了stayhomeandlearn.org,效果很好。从 4 月 1 日到 4 月 16 日,这个网站有超过 15000 的访问者,这超过了我最乐观的预期。
这个网站正在慢慢走向死亡。然而,这个过程教会了我关注运输而不是浪费时间寻找完美的工具有多重要。我很快建立了网站,人们喜欢它,第一天之后,它的流量已经超过了我迄今为止做过的任何一个附带项目。完美是好的敌人,这是真的。
在我的例子中,出于样式和部署的目的,我必须向脚本添加更多的功能。有兴趣的可以看看我的 GitHub 资源库里的代码。
最后,现在你可以做几件事来让你的网站更有趣:
本文原载于 我的博客 。
参考
[1] G. Bauges,谷歌电子表格和 Python (2017)
[2] V. Drake,H .用 AWS S3、Route 53 和 CloudFront 固定你的静态站点(2017)
[3] A .巴拉达斯,口袋妖怪统计 (2016)
用机器学习预测赛马
将机器学习知识应用于现实世界分类问题的指南
背景
我是 HKU 大学计算机科学硕士的非全日制学生。在上学期的考试之后,我在想将机器学习应用到真实世界的数据集上会很有趣也很酷。
我很快就找到了机器学习的好网站, Kaggle 。Kaggle 为数据科学学生和专业人士提供了许多有趣的数据集。我搜索“香港”,因为我想找到一个与我密切/相关的数据集,我得到了香港赛马的数据集。非常感谢格雷厄姆·戴利在 Kaggle 上发布数据集。
我非常兴奋能够从事我的第一个真实世界机器学习项目。我觉得赛马是可以预测的,是典型的分类问题。
Julia Joppien 在 Unsplash 上拍摄的照片
摘要
在这篇文章中,我将与你分享如何做数据预处理,神经网络模型的建立和训练。我们准备用熊猫、tensorflow、numpy、sklearn 等常见的机器学习包。最后,我也会分享一些我在这个项目之后走出来的思考。
数据预处理
数据集包含两个 csv 文件:races.csv 和 runs.csv。这两个表通过列“race_id”相关联。我们将需要连接这两个表,以便为训练生成有意义的数据。
处理 races.csv 文件
用于处理 races.csv 文件的代码
- 第 1-6 行:导入我们将用于项目的包。
- 第 8–9 行:使用 pandas 读取 csv 文件。然后,选择我认为重要的 6 个栏目。
- 第 11–13 行:数据集包含 NaN 是很常见的,这意味着某些字段丢失了。在这里,让我们只是放弃他们。
- 第 15–23 行:一些列是字符串。但是,神经网络模型只处理数字,所以我们需要对它们进行编码。字符串列有不同的类型:名义列和序数列。我们对列“venue”使用 LabelEncoder,而对列“config”和“going”使用 OrdinalEncoder。
到目前为止,我们对来自 races.csv 的数据很满意。
处理 runs.csv 文件
用于处理 runs.csv 文件的代码
runs.csv 文件包含每场比赛的马匹数据,通过 race_id 与 races.csv 相关联。通常,每场比赛有 14 匹或更少的马一起比赛。该代码与我们对 races.csv 文件所做的非常相似,所以我不打算逐行解释。需要注意的一点是,在上面的第 10–13 行,我们需要删除一些有噪声的数据,原因在代码行内注释中有解释。处理后的数据帧头如下图所示。
您可能认为我们已经完成了 runs.csv 文件,但事实并非如此,因为我们需要考虑如何将数据输入神经网络。基本上,我们需要 races.csv 中的一行加上 runs.csv 中属于同一种族的所有行,并将它们放在一起作为神经网络的一个样本输入。下面的代码将对 runs dataframe 进行整形,以便相同种族的行将形成一行。
- 第 1–5 行:我们定义了一个小函数 her 来对列进行排序。
- 第 7 行:magic pivot 方法对 runs.csv 数据进行了整形,使相同种族的行成为一行。列的排序如下[(马龄,1),(马龄,2),(马龄,3) … ]。但是,我希望列像[(horse_age,1),(horse_country,1),(horse_type,1)……(horse _ age,2),(horse_country,2),(horse_type,2)……],这样我们将按顺序拥有 horse-1-features,horse-2-features,horse-3-features。我花了很长时间才弄明白该用哪个熊猫 API 来做这件事。不得不提的是,我们需要避免使用循环来争取更好的计算效率。
- 第 8–10 行:使用第 1–5 行中定义的函数对列进行排序。除了对马的特性进行分组之外,它还将“结果”列放在最后。因此最右边的列将是[……(结果,11),(结果,12),(结果,13),(结果,14)]。将它们放在最后,因为它们是输出,而不是特征输入。
- 第 12–14 行:正如行内注释所解释的,我们的神经网络需要 14 匹马的数据作为输入。在这里,我们简单地用 0 填充虚拟马特征。
准备训练和测试数据集
你看,数据预处理的工作量很大。幸运的是,我们快到了。现在,让我们做最后一点,为神经网络准备好数据。
- 第 1 行:通过“race_id”连接我们上面准备的两个数据帧。
- 第 2 行:选择除最后 14 列之外的所有列作为输入 X 。
- 第 3-4 行:应用特征缩放技术,标准化,这使得培训更容易。否则,网络很难学习特征“申报体重”(1100 sth)和特征“马龄”(大约 3-5)的参数。
- 第 6 行:将列“结果”转换为输出 y 。这是一个二元分类。因此,如果这匹马得了第一名,我们就放 1 ,否则就放 0 。
- 第 8–9 行:简单地打印出输入和输出的形状来看看。我们可以看到,我们有 6348 个比赛,每个比赛有 104 个特征和 14 个输出。
(6348, 104)
(6348, 14)
- 第 11–12 行:使用 sklearn 提供的一个方便的 util 方法来分割数据以训练和测试(验证)集合。
建立神经网络模型
首先,我想向你展示神经网络的架构如下。这是项目的核心。到目前为止,我们所做的所有数据预处理都是为了输入神经网络。
图片由作者提供:赛马的神经网络
- 输入层:104 个节点,从 races.csv 文件中取 6,从 runs.csv 文件中取 7x14。
- 隐藏层:只有 96 个节点。我认为这对于我们的项目来说已经足够了,因为这个分类的功能并不复杂。
- 输出层:14 个节点,每个节点表示一匹马是否赢得了比赛。
代码如下所示,非常简单,这要感谢 keras。请注意度量。我选择 precision, 𝑝𝑟𝑒𝑐𝑖𝑠𝑖𝑜𝑛 = 𝑇𝑃/(𝑇𝑃+𝐹𝑃) 作为评估参数,因为赌赢是我们的兴趣所在。
模型细节打印如下。可训练参数是 11,438,这是神经网络的能力。这意味着网络将学习和调整这些参数中的每一个,以便找到一个函数 y = f(X)来拟合我们的数据。
训练网络,策划表演
- 第 1–4 行:在输入数据用于训练之前,进一步扭曲数据。
- 第 6–8 行:培训本身。在训练过程中,控制台会打印进度。
- 第 10–26 行:绘制性能图,图表如下所示。
作者图片:精确绘图
作者图片:损失的策划
该网络足够强大,可以很好地适应训练数据集。然而,有很大的过度拟合。我们在验证数据集上可以达到的最佳性能大约是精度=0.3,这发生在纪元 60~80 左右。Precision=0.3 意味着我们可以在 10 个赢注中正确获得 3 个。这个性能没有我预期的高。原因可能是大多数比赛都是障碍赛,这意味着跑步者携带不同的重量来平衡他们赢得比赛的机会,参见参考文献 Racing 101 。
一些想法
- 在数据分析之前,应该学习领域知识。我的教训是,我不理解赛马术语,如“win_odds”和“actual_weight ”,所以我不知道哪些列是有用的功能。在研究了指南之后,事情变得清晰多了。
- 在编写代码之前做更多数据分析。找到数据集后,我很快开始编码。你可以看到我的另一个笔记本,其中我犯了一个大错误,我把一行 runs.csv 文件当成了一个样本。一匹马能否赢得一场比赛是相对于同场比赛的其他马而言的,所以我们要把一场比赛的 14 匹马的数据放在一起作为一个样本。
- 我听人说过,数据预处理通常比建模和训练花费更多的时间。我发现这个项目确实如此。Pandas 有数百个强大的 API 供您操作数据帧,要想选择正确的 API 快速完成工作,需要花时间熟悉它们。
- 做这个项目真的可以学到很多东西。这比课程作业要多得多。
总结
在这篇文章中,我与您分享了如何在 Kaggle 的真实世界数据集上端到端地应用机器学习技能。我的完整笔记本可以在这里找到。非常感谢您的阅读,并欢迎您的评论。
2020 年使用 Numpy 进行统计和算术运算
用于数据科学和机器学习的大多数数据都存储为数据帧或数字数组。在本文中,我们将讨论 Numpy
NumPy 为什么
NumPy 是 Python 为科学计算提供的库。机器学习和数据分析需要存储大量的数据,而更多的时候,NumPy 是用来存储数据的。下面列出了它的一些好处
- 与列表相比,Numpy 使用更少的空间来存储数据。您可以指定数组数据类型,以减少数组消耗的内存。
- 它比列表更快。
- 它给了你执行算术计算的能力,比如两个数组的元素相加,两个数组的元素相乘等等。您还可以使用 EDA(探索性数据分析)函数,如 min、max、avg 等。
l = [1,2,3,4,5,6]print ("Size of list is ",sys.getsizeof(l))a1 = np.array([1,2,3,4,5,6])print("Size of a is " , a1.nbytes)a2 = np.array([1,2,3,4,5,6], dtype="int16")print("Size of a is " , a2.nbytes)**------------------ OUTPUT OF THE CODE-------------------------------**
Size of list is 112
Size of a is 48
Size of a is 12
作者显示内存差异的代码截图
如您所见,与 Numpy 数组相比,list 使用了两倍多的内存。
怎么了
要安装 Numpy,请键入以下命令
pip install numpy
要导入 NumPy 库,您需要键入以下代码。
import numpy as np
as np 是不必要的。它允许您在调用函数时使用“np”而不是“numpy”或作为别名。
创建数组
您可以使用 NumPy 创建多维数组。
# 1D arrayd1 = np.array([1,2,3,4,5,6])print(d1)# 2D arrayd2 = np.array( [ [1,2,3] , [4,5,6] ])print(d2)**------------------ OUTPUT OF THE CODE-------------------------------**[1 2 3 4 5 6][[1 2 3] [4 5 6]]
语法是
variable = np.array( [ elements/array of elements ])
您可以通过添加来指定数据类型
dtype = “int16”dtype= “ int32 “
形状和尺寸
.ndim
返回你的数组的维度,.shape
返回一个长度为.ndim
的元组,其中包含每个‘行’的长度。
arr = np.array([ [1,2,3],[4,5,6],[6,7,8] ])print(arr.ndim)print(arr.shape) #arr is 3x3 matrix**------------------ OUTPUT OF THE CODE-------------------------------**
2
(3, 3)
变量 arr 是一个二维数组,每个数组有 9 个元素。它有三个一维数组,每个数组有 3 个元素。它基本上是一个 3x3 的矩阵。
作者显示 Numpy 数组中的维度的代码截图
访问元素
访问 NumPy 数组中的特定元素就像访问列表中的元素一样。返回的元组的长度数。形状或返回的值。ndim 是访问多维数组中单个元素所需的索引数。
# print a single element# syntax: arr[row , col]print(arr[0 , 0] , arr[2 , 2] , arr[1 , 2])#prints all elements in row 0print(arr[0 , :])#prints all elements in col 0print(arr[: , 0])#prints all elements in the matrixprint(arr[:,:])**------------------ OUTPUT OF THE CODE-------------------------------**1 8 6[1 2 3][1 4 6][[1 2 3] [4 5 6] [6 7 8]]
作者展示如何访问元素的代码截图
初始化数组
- 全为零的多维数组
print ( np.zeros((2)) ) # A 1D array with 2 elements set to zeroprint("------------------------------")print (np.zeros((3,3)) ) # A 3x3 matrix will all elements set to
# zero**------------------ OUTPUT OF THE CODE-------------------------------**[0\. 0.]
------------------------------
[[0\. 0\. 0.] [0\. 0\. 0.] [0\. 0\. 0.]]
注意语法,。零( (尺寸))
2。全 1 多维数组
print ( np.ones((2)) ) # A 1D array with 2 elements set to oneprint("------------------------------")print (np.ones((3,3)) ) # A 3x3 matrix will all elements set to one**------------------ OUTPUT OF THE CODE-------------------------------**[1\. 1.]------------------------------[[1\. 1\. 1.] [1\. 1\. 1.] [1\. 1\. 1.]]
注意语法,。个( (尺寸))
3。每个元素等于一个数字的多维数组
print ( np.full((2) , 6 ) ) # A 1D array with 2 elements set to 6print("------------------------------")print (np.full((3,3),77)) #A 3x3 matrix will all elements set to 77**------------------ OUTPUT OF THE CODE-------------------------------**[6 6]
------------------------------
[[77 77 77] [77 77 77] [77 77 77]]
注意语法。全( (维),数)
4。具有随机小数/整数的多维数组
print ( np.random.rand(4,2) )print("----------------------")print (np.random.randint(0,100, size = (4,2) ) )**------------------ OUTPUT OF THE CODE-------------------------------**[[0.97398125 0.86285608] [0.84382674 0.76331437] [0.71798434 0.2150087 ] [0.38535155 0.33849209]]
----------------------
[[ 5 10] [73 65] [29 38] [39 26]]
注意语法, .random.rand( ( dimension),number )
注意语法, .random.randint( min,max,size = ( dimension ) )
作者展示如何初始化 Numpy 数组的代码截图
5。单位矩阵
np.identity(3)**------------------ OUTPUT OF THE CODE-------------------------------**array([[1., 0., 0.],[0., 1., 0.],[0., 0., 1.]])
由于它是一个大小为 N×N 的矩阵,所以只需要给出一个值。
当心抄袭
复制 NumPy 数组有正确的方法和错误的方法。显然,你必须使用正确的方法。
作者代码截图
始终使用**。copy()** 方法复制一个数组。简单地设置 b = a 将使 b 和 a 指向同一个数组。因此, b 中的所有变化也会反映在 a 中,而这通常是不可取的。
基本算术
你可以在两个或多个数组上执行基本的算术运算,如加法、乘法等。
您必须确保数组的维度和形状相同
该操作将一个元素一个元素地执行。如果我们对数组 a 和数组 b 使用加法运算,a 的第一个元素将被加到 b 的第一个元素上,a 的第二个元素将被加到 b 的第二个元素上,依此类推。如果我们在数组 a 和数组 b 上使用减法运算,b 的第一个元素将从 a 的第一个元素中减去,数组 b 的第二个元素将从数组 b 的第二个元素中减去,依此类推。
我们也可以用标量对数组进行算术运算。该操作将按元素方式执行。
a = np.array([1,2,3,4])b = np.array([5,6,7,8])print(a+b)print(a-b)print(b-a)print(a*b)print(a/2)print(a + 2)**------------------ OUTPUT OF THE CODE-------------------------------**[ 6 8 10 12]
[-4 -4 -4 -4]
[4 4 4 4]
[ 5 12 21 32]
[0.5 1\. 1.5 2\. ]
[3 4 5 6]
作者展示算术函数的代码截图
统计函数
NumPy 还允许我们执行用于 EDA 的各种统计功能,如最小值、最大值、总和、平均值等
a = np.random.randint(0,100,size=(10))print(a)print(a.max())print(a.min())print(a.sum())print(a.mean())**------------------ OUTPUT OF THE CODE-------------------------------**[35 73 93 24 14 39 66 96 89 69]961459859.8
作者代码截图
使再成形
NumPy 允许我们改变数组的形状。我们可以把 2×3 的阵列改成 3×2 的阵列。我们在执行整形操作时必须非常小心,因为它经常变得令人困惑。
健全性检查:整形时,整形数组中元素的乘积。形状元组必须等于原始数组的乘积。形状元组。也就是说,原始阵列和重新成形的阵列中的元素数量必须相同。
您还可以将 reshape 中的一个元素指定为-1,python 将为您计算未知维度。只能将其中一个元素指定为-1,如果将多个元素设置为-1,将会出现错误。在将数组传递给各种函数时,我们经常需要对数组进行整形。这可能看起来令人困惑,但阅读下面的解释,事情就会清楚了。
b = np.zeros( (3,2))print("Orignial")print(b)print(b.shape)print("---------------")print("Reshaped")print(b.reshape(2,3))print(b.reshape(2,3).shape)print("---------------")print("Reshaped")print(b.reshape(-1,3))print(b.reshape(-1,3).shape)print("Reshaped")print(b.reshape(2,-1))print(b.reshape(2,-1).shape)**------------------ OUTPUT OF THE CODE-------------------------------**Orignial[[0\. 0.] [0\. 0.] [0\. 0.]](3, 2)
---------------
Reshaped[[0\. 0\. 0.] [0\. 0\. 0.]]
(2, 3)---------------Reshaped
[[0\. 0\. 0.] [0\. 0\. 0.]](2, 3)---------------Reshaped[[0\. 0\. 0.] [0\. 0\. 0.]](2, 3)
作者代码截图
首先,我们创建了一个 2 维数组或 3 行 2 列的矩阵,所有元素都设置为 0。接下来,我们尝试将这个数组转换成 2 行 3 列的数组,因此我们使用。整形(2,3)。注意,两种情况下的乘积都是 6。
接下来,我们传递值(-1,3)。我们告诉 NumPy 我们需要 3 列,并要求它计算行数。乘积一定还是 6,所以 NumPy 用 2 代替-1,我们得到一个 2×3 的矩阵。
在最后一种情况下,我们传递值(2,-1)。我们告诉 NumPy 我们想要 2 行,并要求它计算列。乘积一定还是 6,所以 NumPy 用 4 代替-1,我们得到 2×3 矩阵。
下面是我们的原始数组的一些更多的整形。
作者代码截图
b . shape(-1,8)给出一个错误,因为整数乘以 8 不会产生 6。永远记得在整形前做健全检查。
结论
我希望我已经帮助你理解了 NumPy 的基础知识。NumPy 库中还有更多可用的函数,所有这些函数都只需谷歌搜索一下。
我最近用 WordPress 创建了一个博客,如果你能看看的话,我会很高兴的😃
[## Python 项目教程-使用这些 Python 项目教程改进您的简历/作品集。
使用 Streamlit 共享部署您的机器学习 Web 应用程序在我以前的文章中,我谈到过构建一个…
realpythonproject.com](https://realpythonproject.com/)
在 LinkedIn 上与我联系
[## Rahul baner JEE——产品工程实习生——EY | LinkedIn
查看 Rahul Banerjee 在世界上最大的职业社区 LinkedIn 上的个人资料。拉胡尔有 4 个工作列在他们的…
www.linkedin.com](https://www.linkedin.com/in/rahulbanerjee2699/)
在 Twitter 上与我联系
使用 Python 来自动化您的 Excel 工作!
自动化那些讨厌的 Excel 报表!
在这篇文章中,你将学习如何自动化一些常见的、乏味的 Excel 任务。
很有可能,你无法逃避 Excel(而且你不完全逃避可能是好的!).学习如何使使用应用程序更容易,以及如何自动化重复的任务,是一项重要的技能!
在接下来的几分钟里,您将学习如何组合来自多个工作表的数据,基于这些数据创建汇总统计数据,以及创建有用的可视化效果。几行代码就搞定了!
让我们从导入熊猫库开始。如果你不熟悉这个图书馆,可以看看我在 YouTube 上的熊猫介绍系列!
我已经为本教程创建了一个文件,其中包括三个工作表,涵盖了三个不同月份的数据。你可以在这里下载文件,或者使用 URL 将文件直接导入熊猫数据框架。
在第一段代码中,您已经导入了 Pandas 库(并给它分配了别名pd
)。然后,您将 Excel 文件分配给一个 Excel file 对象。最后,您打印出了文件中所有工作表的名称,但是使用了.sheet_names
属性。
现在您已经知道文件中包含哪些工作表,让我们实际创建所有数据的数据框架。
使用这种方法而不是列出工作表名称的最大好处是,随着月份被添加到文件中,列表不需要修改!
让我们首先创建一个 dataframe 对象,并添加额外的数据文件。
在上面的代码中,首先创建了一个空的 dataframe 对象。然后使用 for 循环遍历 Excel 工作簿中的每个工作表,生成一个包含数据的临时 dataframe(跳过前三行),并将每一项追加到 dataframe df
。最后,您使用.head()
方法打印了前五个记录。
或者,如果您想将数据组合在一行中,您可以编写以下代码,该代码使用熊猫.concat()
函数:
分析您的数据
现在你已经合并了数据,让我们对此进行一些分析。
假设,您被要求查看不同月份按类型和地区划分的每笔销售的总价值。
我们可以使用 Pandas .pivot_table()
函数来实现这一点,我在这个 YouTube 视频中已经详细介绍过了:
让我们创建一个熊猫数据透视表:
上面,您已经创建了一个包含两个索引(月份和类型)的数据透视表。月份是使用熊猫生成的。dt
访问器,以整数形式返回月份,其中 1 等于一月。这些柱子是由区域组成的。
可视化您的数据
最后,让我们创建一些数据的可视化。
也许您想要创建一个图表,用堆叠的柱形图来显示数据。
让我们使用 Pandas 中的 matplotlib 集成来生成这个图表:
这会生成下图:
资料来源:Nik Piepenbreier
结论
在这篇文章中,您学习了如何从多个 Excel 工作表中的数据创建一个熊猫数据框架。您学习了如何快速分析数据并创建数据的可视化。
查看我的其他帖子或者在 YouTube 上订阅我的常用技巧!
使用 Python 构建掷骰子应用程序
一个简单的逐步指南,以建立一个骰子滚动模拟器
我最近申请了一份数据科学的工作,提交申请的要求之一是将代码复制并粘贴到一个小窗口中…整个过程似乎有些贬低,但我还是完成了。
提示要求我使用我想要的任何编码语言构建一个简单的骰子滚动器。因为 Python 是我学习最多的地方,所以我很自然地选择了它。我用的 IDE 是 Jupyter 笔记本。我做了一个更好的图形用户界面(GUI ),这样你就可以选择骰子的边数和你想掷的骰子数,因为我喜欢挑战自己。
这是一个有趣而简单的小项目,所以我决定分享我是如何做的。
创建功能
对于项目的这一部分,我导入了 2 个库:statistics 和 randint(来自 random)。这个项目不需要统计库,但是我认为使用这个库来收集你决定做的任何滚动的统计数据是很好的。
from random import randint
import statistics
现在我们准备创建我们的掷骰子函数。对于这个函数,需要两个输入:n 和 x。
n 是你掷骰子的边数。
x 是你掷骰子的数目。
# Define the dice rolling function using two inputs.
rolls = []
def roll_many(n, x):
for i in range(x):
roll = randint(1,n)
rolls.append(roll)
print(roll)
就是这样!很简单。现在你可以使用这个函数来获得骰子点数。
# This cell will simulate rolling 2 six-sided dice.
rolls = []
roll_many(6,2)
以下是运行它时应该显示的内容示例:
如前所述,您可以使用统计库来收集您掷骰子的统计数据。
statistics.mean(rolls)
下面是一个如何使用统计库来获取掷骰子统计数据的示例:
为函数创建 GUI
我从未尝试过让我的 Python 代码在 GUI 中工作,所以这部分对我来说是新的。在 Google 上搜索了一下之后,我决定使用 tkinter 库来完成这个项目的这一部分。
from tkinter import *
因为这部分对我来说比较新,所以为了简单起见,我决定不掷出多个骰子,而只掷出一个骰子。
对于初学者来说,接下来的代码片段可能有点复杂。这里我定义了一个窗口类。在课堂上,我定义了将出现在窗口中的不同东西:一个键入骰子边数的区域,一个滚动骰子的按钮,以及所有使用文本向用户指定这些区域的标签。
class MyWindow:
def __init__(self, win):
self.lbl1=Label(win, text='# of Die Sides')
self.lbl2=Label(win, text='Roll Result')
self.lbl3=Label(win, text='Dice Rolling Simulator', font=("Helvetica", 20))
self.t1=Entry()
self.t2=Entry()
self.btn1 = Button(win, text='Roll Dice')
self.lbl1.place(x=100, y=100)
self.t1.place(x=200, y=100)
self.b1=Button(win, text='Roll Dice', font=("Helvetica", 16), command=self.roll)
self.b1.place(x=200, y=140)
self.b1.config(height=1, width=8)
self.lbl2.place(x=100, y=200)
self.t2.place(x=200, y=200)
self.lbl3.place(x=100, y=35)
接下来,我为按钮定义 roll 函数。这里的 roll 函数与我们上面看到的非常相似,但是还有一些额外的行,以便 GUI 可以使用这个函数。
def roll(self):
self.t2.delete(0, 'end')
n=int(self.t1.get())
result=randint(1,n)
self.t2.insert(END, str(result))
最后,我使用 tkinter 定义了窗口,我给窗口添加了一个标题(显示在顶部的窗口的名称标题)以及窗口在屏幕上显示的尺寸和位置。window.mainloop()是一个事件监听循环,因此我们的应用程序可以随时响应新的输入。
window=Tk()
mywin=MyWindow(window)
window.title('Dice Roller')
window.geometry("400x300+10+10")
window.mainloop()
总的来说,该代码片段如下所示:
from tkinter import *
class MyWindow:
def __init__(self, win):
self.lbl1=Label(win, text='# of Die Sides')
self.lbl2=Label(win, text='Roll Result')
self.lbl3=Label(win, text='Dice Rolling Simulator', font=("Helvetica", 20))
self.t1=Entry()
self.t2=Entry()
self.btn1 = Button(win, text='Roll Dice')
self.lbl1.place(x=100, y=100)
self.t1.place(x=200, y=100)
self.b1=Button(win, text='Roll Dice', font=("Helvetica", 16), command=self.roll)
self.b1.place(x=200, y=140)
self.b1.config(height=1, width=8)
self.lbl2.place(x=100, y=200)
self.t2.place(x=200, y=200)
self.lbl3.place(x=100, y=35)
def roll(self):
self.t2.delete(0, 'end')
n=int(self.t1.get())
result=randint(1,n)
self.t2.insert(END, str(result))window=Tk()
mywin=MyWindow(window)
window.title('Dice Roller')
window.geometry("400x300+10+10")
window.mainloop()
并且,在运行代码之后…
瞧啊。让我们为骰子点数输入 20,看看我们会得到什么:
幸运数字七!这不是什么花哨的东西,但它是一个工作的骰子滚动模拟器。
如果您在编写代码时遇到问题,或者只是想复制并粘贴代码,以便拥有掷骰子模拟器,以下是 GitHub 资源库链接:
此时您不能执行该操作。您已使用另一个标签页或窗口登录。您已在另一个选项卡中注销,或者…
github.com](https://github.com/jkundycki/RollDice)
祝你好运,感谢你的阅读!
直接在浏览器上使用“RStudio Cloud”上的 R 编程
在无需安装软件和配置的情况下,使用 R on Cloud 进行、共享、教授和学习数据科学
序幕
云计算的新时代开始了……我发现了 RStudio Cloud (在我撰写本文时,它正处于测试版),适合专业人士、业余爱好者、培训师、教师和学生使用 R 来进行、分享、教授和学习数据科学。对于那些不知道的人来说, RStudio 是一个成熟的 R 编程 IDE 。这是您将用作桌面版本的确切视图,旨在解决一些棘手问题,例如:
- 安装 R
- 安装 RStudio
- 安装软件包和依赖项
- 加载包
- 运行并重新加载代码
现在,有了新的云体验,我可以简单地登录并从浏览器运行代码。多么可笑的简单!
RStudio Cloud 现已发布测试版。来源:https://rstudio.cloud/
RStudio 云网络研讨会,来源:Mine etinkaya-Rundel(第 4 页)
登录选项
- 通过您的用户名和密码登录
- 通过谷歌登录
- 通过 GitHub 登录。
如果凭据有效,您将看到以下登录页面:
RStudio 云术语通俗易懂
- 项目 —封装你的 R 代码、包和数据文件。只需在“项目”区域点击“新建项目”按钮。您的新项目将在 RStudio IDE 中打开。
- 工作区 —获得一个名为您的工作区的个人工作区,在其中创建项目。
- 私人空间 —仅与私人空间的特定成员协作。
- 成员 —管理其成员资格。
- 项目访问—对空间的所有成员可见,还是对项目创建者保密?
我继续创建一个新项目,并给项目命名为“AutoML H2O.ai in R”。在我这边效果惊人。我根本不用安装 R 或者 RStudio。导航窗格与在我的本地计算机上使用 RStudio 的体验相同。
考虑和限制
- 每个项目分配 1GB 的内存。每个帐户分配一个私人空间,最多 10 个成员和 25 个项目。如果您达到这些空间限制之一,您可以向 RStudio 云团队提交增加容量的请求,他们会尽最大努力满足您的需求。如果你用的是专业的
shinyapps.io
账号,就不会遇到这些空间限制。 - 如果连接不好,项目可能会停滞。您可以随时重新启动项目。
- 您不必重新运行代码。如果您不小心关闭了视图,您总是可以返回到它并继续下一行代码。
- 工作区和项目之间的包安装不同步。因此,如果某个包是未知的,您仍然需要为不同的项目安装它。
附加材料
免责声明:以下内容未经我的雇主正式认可。本文表达的观点和意见仅代表作者的观点和意见,不一定反映当前或以前的雇主、组织、委员会、其他团体或个人的官方政策或立场。本文中进行的分析是基于有限日期的开源信息。分析中所做的假设并不反映任何前任或现任雇主的立场。
使用 R 计算用于会计分析的样板文件
研究
用 30 家电信公司 CSR 计算样本的实证。
图片来自 Pixabay 的 Dariusz Sankowski
什么是样板文件,为什么要去除它?
在文本分析中,样板词是可以从句子中删除而不会显著改变其原始含义的词的组合,例如“超过百万 In”或“在…的末尾”根据 Mark Lang 和 Lorien Stice-Lawrence 的说法,样板文件已经成为监管机构和标准制定者发现的年度报告中一个特别有问题的属性,他们的分析表明,随着样板文件的减少,年度报告披露(来自 15,000 多家公司)得到了改善。研究的详细内容可在这里找到。
样板文件影响年度披露质量的原因之一是样板文件可能为隐藏信息提供机会,降低整体信息含量( Hoogervorst,2013 )。
数据
我用 30 份企业社会责任报告作为数据来演示如何计算样板。都是来自通信行业的公司,比如 AT & T,Sprint Corporation 之类的。都是用英文和 pdf 格式写的。
展示区(代码)
在我的计算演示中,我将遵循 Mark Lang 对样板的定义:报告中包含至少一个“样板”短语(4 个单词的短语,或四边形)的句子的单词百分比。
第一步——标记每个文档的所有句子
在这一步中,我们使用了“tm”包和“tokenizers”包。为了生成正确的四边形并减少计算量,我们在这一步中删除了数字。由于所有的原始文档都是 pdf,“tokenize_sentences”功能可以充分识别不规则的段落换行符,并将文本拆分成句子。
作者代码
第二步——将四边形放入一个列表中
在这一步中,我生成了四边形,并将所有的四边形放在一个列表中以供进一步分析,因为 Mark Lang 的方法要求在所有文档级别中查找常见的 4 个单词的短语。
作者代码
第三步—获得频率在 30%和 75%之间的四边形
在此步骤中,我手动设置阈值,只有出现在整个文档语料库的 30%和 75% 之间的 中的四边形将用于计算。我之所以要设上限,在马克·朗的论文里有解释。但一般来说,四联图有时包含无害的短语或监管披露,这将传达信息内容。样板文件的目的是发现文档是多么没有信息,所以我必须从计算中排除最流行的四边形。
作者代码
部分输出如下所示:
图 1–四边形
第四步——计算每个句子的字数和四角形
当我最初开始这项任务时,我在这一步遇到了很多麻烦,因为如果我把这一部分搞砸了,最终的输出将会很糟糕。一个句子可能根本没有四边形,也可能同时有多个四边形。一句话的字数很容易翻倍或翻倍。为了解决这个问题,我设置了一个临时变量来标记句子,以避免多次计数。
作者代码
最后一步——计算样板文件
让我们回顾一下样板文件的定义:
图 2 —样板方程
在我标记了所有的样板句子之后,我生成了每个文档的长度:
作者代码
最终计算的代码:
作者代码
最终输出如下所示:
图 3 —最终输出
最后
计算样本有助于更好地理解会计文本分析中一组报表的信息含量。做计算时,你必须小心重复计算样板句。
我有意避免使用“lapply ”,并在代码中使用了许多“for 循环”。它可以有效地帮助构建和组织用于调试目的的代码。此外,多个任务需要多次将输出分配给环境变量。由于 R 一直在提升“for loops”的性能,“for loops”的速度也没有以前那么差了。
这项工作可以列举如下:
《情感:一个文本分析包》。软件影响,100456 (2022)。【https://doi.org/10.1016/J.SIMPA.2022.100456
供大家参考,在这里可以找到所有的 R 代码。
python 版本可在此处找到:
帮助研究人员计算样本、冗余、特异性、相对流行率的函数集合…
towardsdatascience.com](/morethansentiments-a-python-library-for-text-quantification-e57ff9d51cd5)
请随时与我联系LinkedIn。
在 Flask 应用程序中为异步任务使用 Redis 队列
使用 Redis 和 Redis 队列处理长请求,防止应用程序编程超时
By:内容由 爱德华·克鲁格乔希·法默道格拉斯·富兰克林 。**
伊恩·巴塔格利亚在 Unsplash 上拍摄的照片
当构建执行耗时、复杂或资源密集型任务的应用程序时,等待这些任务在前端应用程序中完成可能会令人沮丧。此外,前端的复杂任务可能会超时。Redis Queue 通过将更复杂的任务推给工作进程进行处理来解决这个问题。
将 Redis 与 Redis Queue 一起使用允许您将那些复杂的任务输入到一个队列中,因此 Redis worker 在应用程序的 HTTP 服务器之外执行这些任务。
在本文中,我们将构建一个应用程序,它使用 Redis 队列对作业进行排队,对这些作业执行一个函数,并返回函数的结果。
这里是这个项目的代码到 Github 库的链接。
Redis 是什么?
Redis 是一个开源的内存数据库、缓存和消息代理。Redis 处理的消息本质上是 JSONs。Redis 非常适合在临时数据库中快速方便地处理特定的数据类型,并提供非常快速的查询访问和传递。
对我们来说,Redis 提供了两个好处。首先,它将复杂的任务推到另一个空间进行处理。第二,通过将任务分成独立的功能,即主应用程序和队列,开发人员可以更容易地处理复杂的操作。
对于这个应用程序,我们将使用 Redis 来保存 JSON 消息队列。Redis 可以是一个独立的数据库,可由许多计算机或系统访问。在我们的示例中,我们将使用它作为本地内存存储来支持我们的应用程序。
什么是 Redis 队列?
Redis Queue 是一个 python 库,用于后台处理的作业排队。由于许多托管服务在长 HTTP 请求时会超时,所以最好设计 API 来尽快关闭请求。Redis Queue 允许我们通过将任务推送到一个队列,然后再推送到一个工作器进行处理来实现这一点。
将 Redis 与 Redis Queue 结合使用,可以请求用户输入,向用户返回验证响应,并在后台对流程进行排队。所有这些都不需要前端用户等待这些过程完成。过程可以是任何东西,从机器学习模型到复制图像到复杂的模拟。
任何比您希望在应用程序前端完成的时间更长的事情都属于 Redis 队列。
模块化应用程序结构
我们将介绍如何使用 Docker 运行 Redis 数据库并初始化 Redis 队列工作器。worker 将允许我们处理应用程序队列中的作业。
我们将在接下来的小节中更详细地讨论这些文件。
我们将讨论我们的应用程序的以下组成文件: main.py 、 functions.py 和 redis_resc.py 。
有两种方法可以启动 docker 容器。在本文中,我们将一步一步地介绍标准流程。然而,可以使用 docker-compose 创建 docker 容器,我们将在另一篇文章中介绍。
安装 Docker 和 Redis
码头服务是一种集装箱服务。这意味着 Docker 在一个容器中运行代码,该容器中有应用程序从一个系统可靠地运行到另一个系统所需的依赖关系。它确保我们的应用程序从开发到测试再到生产始终如一地运行。
跟随链接到 Docker 文档获取全面的安装指南,帮助您为您的操作系统选择正确的 Docker 版本。
一旦在系统上安装了 Docker,您将需要安装 Redis。
对于 Mac 用户来说,home-brew 命令 brew install redis
将起作用。Windows 需要从 Github 下载。有了 Linux,可以直接从 Redis 下载最新的 tarball。
自述文件概述
要运行我们的应用程序,我们必须理解自述文件中的说明。
我们将使用 Pipenv 创建一个虚拟环境并安装适当的包。用命令pipenv install -- dev
安装这个程序的包。添加--dev
标志安装开发包和生产需求。在进入下一步之前,使用命令 pipenv shell 激活环境。
我们必须启动三个服务才能让应用程序正常运行:Redis 数据库、Redis 队列工作器和 Flask 应用程序。其中两个服务将在我们用来安装虚拟环境和进入 shell 的同一个终端中开始。
首先,运行命令:
**docker pull redis**
这将从 Docker Hub 中提取 Redis 映像。
然后,运行命令:
**docker run -d -p 6379:6379 redis**
这个命令有几个用途。首先,它初始化 docker 容器。-d
标志在后台运行 Redis 容器,为下一步释放终端。-p
标志向主机发布容器的一个或多个端口。最后,6379:6379
将 docker 容器中的端口 6379 绑定到本地主机上的端口 6379。如果不绑定端口,当 docker 容器中的本地机器和进程试图相互通信时,就会遇到问题。
运行我们的应用程序之前的最后一步是启动 Redis 队列工作器。因为我们是在分离模式下运行 Redis 映像,所以我们可以在同一个终端中执行这个步骤。
用命令rq worker
启动 Redis 工作器。只有在安装了 Redis 队列包的情况下,rq
命令才可用于终端,我们用 Pipenv install 安装了依赖项,用 pipenv shell 进入环境。运行rq
命令将允许 Redis 队列开始监听进入队列的作业,并开始处理这些作业。
初始化烧瓶应用程序
我们现在准备初始化 flask 应用程序。在开发服务器上,这是通过以下命令完成的:
**export FLASK_APP=app.main:app && flask run — reload**
命令gunicorn app.main:app
将在生产服务器上运行这个。Gunicorn 需要 Unix 平台才能运行。因此,如果您使用的是 macOS 或基于 Linux 的系统,这个命令可以工作,但不适用于 windows。
现在服务正在运行,让我们转到应用程序文件。
烧瓶应用程序组件
redis_resc.py
这个文件设置 Redis 连接和 Redis 队列。
redis_resc.py
变量redis_conn
定义了这个连接的连接参数,redis_queue
使用队列方法。Queue 方法初始化队列,可以给定任何名称。常见的命名模式有“低”、“中”和“高”通过为队列方法提供无参数,我们指示它使用默认队列。除了名称之外,我们只是将连接字符串传递给 Redis 存储。
函数. py
py 是我们定义在 Redis 队列中使用的函数的地方。
做什么由你决定。它可以是您希望对传递给员工的数据做的任何事情。这可以是任何事情,从机器学习到图像处理,再到使用信息处理连接数据库中的查询。你想让后端做的任何事情都可以放在这个函数中,这样前端就不会等待了。
对于我们的例子,这个函数有两个目的。首先,创建 job 变量来检索当前正在处理的作业。我们使用time.sleep(10)
来演示作业处理过程中不同的作业状态代码。我们将在本文后面更详细地讨论作业状态代码。最后,我们返回一个 JSON,其中包含关于作业和作业处理结果的信息。
Main.py
Main.py 是驱动应用程序的主要 flask app。关于设置 flask 应用程序的更多细节在这篇中型文章中概述。
** [## 用 Flask 和 SQLalchemy,不要用 Flask-SQLAlchemy!
避免 Flask 应用程序中的 Flask-SQLAlchemy
towardsdatascience.com](/use-flask-and-sqlalchemy-not-flask-sqlalchemy-5a64fafe22a4)
第一个路由函数resource_not_found
被设计用来处理由于对应用程序的不正确请求而导致的任何 404 错误。定义为home
的路线功能是导航到回家路线时运行的默认路线。当您对[http://127.0.0.1:8000](http://127.0.0.1:8000)
或本地主机 URL 执行 GET 请求时,这个路由就会运行。这条路线旨在让你知道 flask 应用程序正在运行。
我们可以用 Postman 测试一下,如下图所示。
本地主机地址邮递员测试
其余的路由,特别是 Redis 队列,将在下面详细介绍。
/排队
/enqueue
路由创建任务,并将任务放入队列。Redis Queue 接受诸如键值对之类的东西,因此您可以使用字典创建 post 请求,例如:
{“hello”: “world”}
向/enqueue
路由提交一个 JSON post 请求将告诉应用程序将 JSON 排队到 function.py 文件中的函数 some_long_function
进行处理。
/排队路由邮递员测试
/enqueue
路由一旦完成就返回创建的任务的job_id
。请注意job_id
,因为我们将使用它作为应用程序剩余路由的 URL 字符串的一部分。
/check _ 状态
这个路由采用job_id
并检查它在 Redis 队列中的状态。这条路线的完整网址是http://127.0.0.1:8000?job_id=JobID
。其中JobID
是在/enqueue
路线创建作业时生成的job_id
。
/check_status 路由邮递员测试
/check_status
route 将返回一个 JSON,其中包含 URL 中传递的job_id
和使用 rq.job 的get_status
方法的作业状态。如果您在十秒钟计时器之前发出 get 请求,则状态将显示为“队列”some_long_function
完成后,状态将返回“完成”
/get _ 结果
一旦任务被/check_status route
显示为‘完成’,get_result
将返回已处理任务的结果。结果由您在some_long_function
中的设置决定。该 URL 遵循与/check_status
相同的结构,因此它是:
[http://127.0.0.1:8000?get_result=JobID](http://127.0.0.1:8000?get_result=JobID)
/get_resut 路由邮递员测试
我们的some_long_function
返回关于作业的数据,如上图所示。
结论
在本文中,我们学习了一些关于使用 Redis Queue 构建任务队列的知识,以及如何在 docker 容器中利用 Redis 数据库。我们讲述了如何构建一个应用程序,使耗时的任务排队变得简单,而不会占用或影响我们的前端性能。
运行 pipenv install — dev 来安装 env。运行 pipenv 运行预提交安装来初始化 git 挂钩。运行 pipenv…
github.com](https://github.com/edkrueger/rq-flask-template)
这里是这个项目的代码到 Github 库的链接。自己测试一下,尝试排队几个 JSON 任务并查看结果!**
用强化学习训练一只永远不会死的小鸟
故障排除和性能调整直到完美的故事
拍打鸟
最近,我开始学习强化学习算法,flappy bird 是一个用于强化学习的流行游戏,特别适合初学者玩。
Sarvagya Vaish 在他的帖子中详细解释了 Q-learning 理论和游戏如何运作。他用 javascript 实现了他的想法。我更喜欢在 python 中查找参考代码。感谢 Cihan Ceyhan 提供了一个很好的 python 示例。
通过仔细划分状态空间,慈汉 Ceyhan 的 AI 智能体(flappy bird)可以飞过 5000 分。
然而,正如你所看到的,小鸟不可能在每场比赛中都达到高分,它可能会在任何分数崩溃。不够稳定。
有没有可能在任何游戏中训练一只永远不死的鸟?
状态矢量空间
在 Sarvagya 的帖子中,他定义了三个维度来代表一种状态:
- X —到下一根管道的水平距离
- Y —到下一根管道的垂直距离
- V——鸟的当前速度
在慈汉杰伊汉的代码中,如果鸟进入隧道超过 30 个像素(管道宽度=52px),鸟会将眼睛移到下一个管道。但是,这可能会导致 Q 表的冲突结果。对于相同的 X,Y,V(到下一个管道),如果鸟的当前位置靠近当前管道的边缘部分(红色),鸟可能会坠毁在当时对鸟透明的隧道中。
左:原始状态空间,中:隧道里的鸟是盲的,右:状态空间中引入的新维度
我在状态中添加了第四维:
**Y1**
-相邻两个管道之间的垂直距离,它有助于鸟根据两个连续管道的高度差提前采取行动。该值仅在鸟进入隧道部分时使用。它可以减少状态空间。
此外,鸟仍然可以感知当前管道,直到隧道中 50 像素长。之后,这只鸟几乎飞出了隧道。刚刚通过的管道不能再冲击鸟了。是时候关注下一个管道了。
Q-learning 的奖励
通过以上改进,小鸟可以轻松飞到 10000 分。但是,它仍然不稳定,在达到 10000 分之前有许多失败。
按照萨瓦吉亚的解释,机器人每走一步获得活着的 +1 奖励,如果死亡则获得 -1000 奖励。它在大多数情况下都能很好地工作。
让我们看看下面的场景。下一个管道比前一个有巨大的落差,游戏中两个管道之间的最大垂直落差是 142px。考虑到小鸟在第一张图中所示的位置,如果小鸟正在下落,想要顺利通过这两条管道,可能需要走路线 1 或路线 2 ,但两者都无法顺利通过下一条管道。如果垂直差没有达到最大落差,它在大多数情况下都是可行的。参考中图。
**左:**下一个管道中的巨大落差,**中:**正常情况,容易通过,**右:**最坏情况下的正确轨迹
我们训练鸟几百万次,鸟积累了那个位置的大量正值。最坏的情况是一个相当低发生率的事件,即使有一次导致崩溃的情况,它只有负 1000 奖励。剩余值仍然是一个很大的正值,或者小鸟可以很容易地从成功的训练中获得另外 1000 奖励。所以小鸟一旦遇到类似的情况就会再次坠毁。
它迫使鸟着眼于长期生存,远离任何导致死亡的行为。不管这只鸟过去成功跑了多少次,它都会在死亡时得到-1000 奖励的惩罚。
经过这次改进,大大增加了稳定性。
从死亡中恢复游戏
很少有机会遇到最坏的情况。换句话说,这只鸟在这些情况下没有足够的训练。它可能会遇到一次,但下一次,它不会遇到类似的场景。可能要过很久才会再次发生。
不太适合训练。我实时记录了鸟类旅程的最后 50 步,**the game can resume from the last 50 steps before the crash**
。在短时间内遍历所有可能的状态很有帮助。
我们以之前的案例为例。鸟在进入隧道时处于下落状态,无论走路线 1 或路线 2 或任何其他路线,它仍可能撞在下一根管道上。游戏从这一点重新开始,它可能会尝试其他动作并死亡。重新开始游戏,直到小鸟发现它应该处于上升状态才能进入这种情况。那么它可以经历任何情况,包括最坏的情况。
内存问题
在Bot
类中,它保存了每个动作的状态信息,一旦鸟死了,这些信息将用于更新 Q-table。
当 bird 达到几百万次时,它会消耗大量内存。也减缓了训练速度。
更改前后的内存消耗
每 500 万步,相当于大约 139,000 个分数,我更新 Q 表,然后减少数组列表。我仍然留有 100 万步的缓冲,以避免在 600 万步后鸟坠毁时对鸟的冲击。
每 500 万步更新一次 Q 表
更改后最大内存消耗在 1GB 左右,比之前少了很多。
q 表初始化
在最初的解决方案中,它需要一个单独的步骤来初始化 q 表,并且它还包括许多鸟从未经历过的状态。
在我的解决方案中,只有当鸟经历一个新的状态时,状态才被初始化。所以 Q-table 只包含鸟曾经经历过的状态。它不需要单独的步骤来初始化 Q 表。
要从头开始新的培训,只需删除data/
文件夹下的qvalues.json
文件。
确认
经过长时间的训练(10 多个小时),我进行了验证测试,最高分为 10 分,第 2 集。一旦小鸟得分达到 10 米,游戏将重新开始。这项测试表明,训练有素的代理人可以飞行很长时间,没有任何坠毁。即使在没有 UI 的情况下训练,我的 Mac 也需要将近 2 个小时才能达到 1000 万分。这次测试我只跑了两集。
总训练集:2,最高分:1000 万
从起点到第一个管道,鸟将毫无障碍地飞行很长一段距离,第一个管道之前的状态不会与接下来的训练相同,接下来的测试表明受过训练的代理完美地处理了旅程的开始。设置最高分=10,集=100,000,代理通过了测试,没有任何失败。
总训练集:100,000,最高分:10
第三个测试演示了任何游戏的稳定性和再现性。在这个测试中,最高分=10,000,插曲=2,000,经过训练的代理也没有任何失败地通过了。
总训练集:2000,最高分:10000
出于好奇,我做了最后的测试,看看这只鸟能飞多少分。我只给一集设置了 Max Score=50,000,000。
小鸟达到了 50 米的成绩!!
结论
现在我们可以说受过训练的特工(flappy bird)永远不会死。
密码
在 github 中:https://github . com/kykin 78/rl-flappybird
参照(References)
http://sarvayavaish . github . io/flappybirdrl/
https://github . com/CHN cyhn/flappybird-qle learning-bot
https://github . com/soura HV/flappybird
使用 SHAP 损耗值来调试/监控您的模型
你应该如何理解和使用 SHAP 损失值
近年来,负责任的人工智能一直是一个非常热门的话题。问责制和可解释性现在成为机器学习模型的必要组成部分,特别是当模型做出将影响人们生活的决策时,例如医疗诊断和金融服务。这是机器学习的一个非常大的主题,许多正在进行的工作都致力于各个方面。您可以查看关于这个主题的更多资源[1]。在这篇文章中,我将重点介绍SHAP(SHapley Additive explaining),这是最受欢迎的可解释性包之一,因为它的通用性(局部/全局可解释性;模型特定/不可知)和来自博弈论的坚实理论基础。您可以找到许多帖子和教程来了解 SHAP 如何帮助您了解您的 ML 模型如何工作,即您的每个要素如何对模型预测做出贡献。然而,在这篇文章中,我将谈论许多人可能不太熟悉的 SHAP 损失值。我将通过一个例子来介绍一些关键概念。我也分享一下我的一些想法。
首先,你可能想要检查由 SHAP 软件包提供的例子。有两个重要的注意事项:
- shap loss 值将向您显示每个要素如何影响 logloss 值与预期值的比值。注意,在这篇文章中,当我说损失值时,它指的是 logloss,因为我们将研究分类问题)
就像预测的 SHAP 值一样,SHAP 损失值表示每个要素对对数损失的影响。预期值是依赖于标签的基线值。当数据实例为真时,通过将所有实例的标签设置为真来计算预期值(当数据实例为假时,设置为假)
- 你应该使用“介入”方法来计算 SHAP 损失值
本质上,这意味着在整合缺失的特性时,应该使用边际分布而不是条件分布。实现边缘分布的方法是用来自背景数据集中的值来分配缺少的特征。
使用“介入式”(即边缘分布)或“tree_path_dependent”(即条件分布)是一个重要的细微差别(参见 SHAP 包中的 docstring ),值得进一步讨论。但我不想一开始就让你困惑。你只需要知道,在通常的做法中,TreeShap 计算 Shap 值的速度非常快,因为它利用了来自模型的树结构的条件分布,但条件分布的使用会引入因果关系的问题[2]。
训练 XGBoost 分类器
这篇文章中的例子是从 SHAP 包中的教程例子修改而来的,你可以在这里找到完整的代码和笔记本。我首先训练了一个 XGBoost 分类器。数据集使用 12 个特征来预测一个人的年收入是否超过 5 万英镑。
['Age', 'Workclass', 'Education-Num', 'Marital Status', 'Occupation', 'Relationship', 'Race', 'Sex', 'Capital Gain', 'Capital Loss', 'Hours per week', 'Country']
您可以使用 SHAP 软件包来计算 shap 值。力图将为您提供局部可解释性,以了解这些特性如何对感兴趣的实例的模型预测有所贡献(图 1)。汇总图将给出全局可解释性(图 2)。你可以查看 Jupyter 笔记本的第一部分。没有什么新的,只是 SHAP 的常见用法,所以我将把细节留给你,并跳转到第 2 部分, shap 值的模型损失。
图一。力图显示了每个特征如何将模型输出从基础值推至模型输出。请注意,输出是在对数优势比空间中。
图二。SHAP 摘要图给出了全局可解释性。量值大的要素意味着它对预测有更重要的影响。
解释模型的对数损失
现在对模型损失的贡献更感兴趣,所以我们需要计算 shap 损失值。在某种意义上,这类似于残差分析。代码片段如下。请注意,您需要
- 提供背景数据,因为我们使用“介入”方法。并且计算成本可能是昂贵的。所以你要提供一个合理大小的背景数据(这里我用 100)。
- 现在 model_output 是“log_loss”。
# subsample to provide the background data (stratified by the target variable)X_subsample = subsample_data(X, y)explainer_bg_100 = shap.TreeExplainer(model, X_subsample,
feature_perturbation="interventional",
model_output="log_loss")shap_values_logloss_all = explainer_bg_100.shap_values(X, y)
力图
现在,数据实例的预测图具有与图 2 类似的解释,但是是根据测井曲线损失而不是预测。图 3 给出了成功的预测(基础事实为真,预测为真),而图 4 给出了错误的预测(基础事实为真,预测为假)。您可以看到蓝色的要素如何试图减少基本值的对数损失,而红色的要素会增加对数损失。值得注意的是,模型损失的基础值(期望值)取决于标签(对/错),因此它是一个函数而不是一个数字。期望值的计算是通过首先将所有数据标签设置为真(或假),然后计算平均日志损失,对此您可以在笔记本上查看更多详细信息。我不知道这样计算基值是否有特别的原因,但毕竟基值只是作为一个参考值,所以我认为这应该没有太大关系。
图 3。对于数据实例,地面真值为真,模式预测为真。
图 4。对于数据实例,地面真值为真,模式预测为假。
概要图
类似地,我们有模型对数损失的汇总图(图 5)。这将告诉您特征如何影响模型对数损失(计算基于绝对平均值)。贡献较大的功能意味着它对模型损失的贡献很大,可能会增加某些数据实例的对数损失或减少其他数据实例的对数损失。因此,这里的摘要图应该显示与图 2 中 shap 值的顶部特征的一致性。但是我们可以看到排名顺序有点不同。虽然“关系”仍然是最重要的,但“年龄”、“教育程度”、“资本收益”、“每周工作时间”、“职业”的顺序不同。并且图 5 中的“资本收益”比图 2 中的“资本收益”具有相对较大的贡献。这表明“资本收益”在减少测井损失中起着重要作用,而相对而言,与“关系”相比,模型进行预测可能并不那么重要。值得注意的是,应谨慎解释图 5 中的摘要图,因为图 5 中的条形图是基于绝对平均值计算的,这意味着在对某个特征的重要性进行排序时,会考虑降低对数损失和增加对数损失的影响。简单地说,大量的(绝对)贡献不一定意味着一个特性是“好”的特性。
图 5。类似于图 2,但基于模型对数损失。
当然,您可以使用散点图而不是柱状图来查看详细的分布,以便更深入地进行模型调试(例如,提高模型性能)。我研究的另一种方法是将形状损失值分解为负分量(图 6)和正分量(图 7)。就模型调试而言,您希望获得更大的负值,并减少所有功能的正值,因为您希望所有功能都减少最终的模型 logloss。
图 6。每个要素的所有负形状损失值的总和。
图 7。每个要素的所有正形状损失值的总和。
监控图
现在我们到了最有趣的部分:使用 shap 损失值来监控您的模型。模型漂移和数据漂移是现实世界中的问题,您的模型会退化并导致不可靠/不准确的预测。但这些通常都是悄无声息地发生的,很难找出根本原因。在 SHAP 作者最近的一篇论文[3]中,他们使用 shap 损失值来监控模型的健康状况。这个想法很吸引人,我希望在这方面做更多的探索。请注意, API 是可用的,但似乎正在开发中。
首先,我们需要计算训练数据和测试数据的形状损失值。在监控环境中,您需要计算来自不同时间快照的数据集的 shap 丢失值。您可能还记得,我们在本节开始时已经这样做了。但是请注意,我们使用从整个数据集采样的背景数据。出于监控的基本原理,通过使用来自训练数据集和测试数据集的背景数据,分别计算训练数据集和测试数据集的 shap 损失值更有意义。代码片段如下:
# shap loss values for training data
X_train_subsample = subsample_data(X=X_train, y=y_train)explainer_train_bg_100 = shap.TreeExplainer(model, X_train_subsample,
feature_perturbation="interventional", model_output="log_loss")shap_values_logloss_train = explainer_train_bg_100.shap_values(X_train, y_train)# shap loss values for test data
X_test_subsample = subsample_data(X=X_test, y=y_test)explainer_test_bg_100 = shap.TreeExplainer(model, X_test_subsample,
feature_perturbation="interventional", model_output="log_loss")shap_values_logloss_test = explainer_test_bg_100.shap_values(X_test, y_test)
顶部特征的监控图如图 8 所示。首先,所有数据实例将按索引排序。这里我们假设指数表示时间的演变(沿着轴从左到右)。在这个玩具示例中,我们没有来自不同时间快照的数据,因此我们简单地将训练数据视为当前数据,将测试数据视为我们想要监控的未来数据。
根据 SHAP 帕克凯奇目前的实施情况,理解这些监测地块有几个要点。为了查看 shap 损失值是否具有时间一致性,将重复进行 t 检验来比较两个数据样本。当前的实现使用 50 个数据点的增量来分割数据。这意味着,第一个 t 检验将比较数据[0: 50]和数据[50:];第二个将比较数据[0: 100]和数据[100:],依此类推。如果 p 值小于 0.05/n_features,t 检验将失败。换句话说,它使用 95%的置信度,并应用了 Bonferroni 校正。如果 t 检验失败,将绘制一条垂直虚线来指示位置。有点令人惊讶的是,我们看到监控图显示了[“关系”、“教育数量”、“资本收益”]的 shap 损失值的不一致性,当我们输入测试数据的时间快照时就会发生这种情况(图 8)。
图 8。监控顶部特征的图。训练数据集和测试数据集被连接以模拟来自不同时间快照的数据。请注意,测试数据从索引 26047 开始。
我不太清楚使用 50 个数据点增量的原因。而在这个例子中,由于我们知道[0:26048]是训练数据,[-6513:]是测试数据。我将增量修改为 6500,看看它是否会给出不同的结果。但是当比较测试数据时,监测图仍然显示相同的不一致性(即,t-测试的失败)(图 9)。
图 9。监控顶部特征的图。类似于图 8,但是现在我们使用 6500 个数据点的增量。目的是将测试数据与训练数据的最后一个“时间段”直接进行比较。
最后,我觉得直接对训练数据和测试数据进行 t 检验是个不错的主意。这再次验证了训练数据集和测试数据集之间的形状损失值不一致的结论。
# t-test for top features (assume equal variance)
t-test for feature: Relationship , p value: 2.9102249320497517e-06
t-test for feature: Age , p value: 0.22246187841821208
t-test for feature: Education-Num , p value: 4.169244713493427e-06
t-test for feature: Capital Gain , p value: 1.0471308847541212e-27# t-test for top features (unequal variance, i.e., Welch’s t-test,)
t-test for feature: Relationship , p value: 1.427849321056383e-05
t-test for feature: Age , p value: 0.2367209506867293
t-test for feature: Education-Num , p value: 3.3161498092593535e-06
t-test for feature: Capital Gain , p value: 1.697971581168647e-24
训练数据和测试数据之间的 shap 损失值的不一致实际上是非常意外的,并且可能是麻烦的。请记住,我们只是从整个数据集使用训练/测试分割,因此有很好的理由相信训练数据集和测试数据集在数据分布或 shap 损失值贡献方面应该是一致的。无论如何,这只是一个简单的实验,需要进行更多的调查才能得出确切的结论。但我认为,SHAP 软件包表明监控功能只是初步的,可能有一些原因,例如:
- 在我看来,使用 50 个数据点的增量是任意的;
- t-test 看起来非常敏感,可能会发出许多错误警报。
另一个有趣的讨论点是背景数据的使用。注意,对于监测图,使用不同的背景数据(来自训练数据集/测试数据集的子样本)计算训练数据集和测试数据集的 shap 损失值。由于计算 shap 损失值的“介入”方法非常昂贵,我只尝试了 100 个数据实例大小的子样本数据。这可能会产生 shap 损失值的高方差结果。也许大规模的背景数据将减少方差,并给出监测地块中形状损失值的一致性。当我使用相同的背景数据(来自整个数据集的子样本)时,监测图中不会出现不一致。所以你如何选择背景数据很重要!
结论和讨论
希望这篇帖子能给你有用的介绍 shap 损失值。通过调查 shap 损失值,可以更好地调试 ML 模型。它也可以是一种有用的方法来监控您的 ML 模型的模型漂移和数据漂移,这在社区中仍然是一个非常大的挑战。但是请注意它的局限性:为了使用 shap 损失值进行监控,您需要了解新数据的真实情况,这通常只能在一段时间后才能获得。此外,不幸的是,这一功能仍在开发中,使用 t-test 的适当性需要进一步证明。
最后但同样重要的是,通过边际分布或条件分布计算 shap 值(TreeShap)可以给出不同的结果(见等式)。条件分布的使用会引入因果关系的问题,而边际分布会给模型提供不太可能的数据点[4]。对于使用哪一个,取决于什么场景,似乎没有一致的意见[2,5]。这篇论文[6]对这个话题有一些有趣的评论,我想在这里引用一下:
一般来说,用户是否应该用不属于原始训练分布的输入来呈现他们的模型是一个正在进行的辩论的主题。
….
这个问题适合于一个更大的讨论,即你的归因方法是否应该“忠于模型”或“忠于数据”,这已经在最近的几篇文章中讨论过了。
说明如何使用边际分布和条件分布来整合缺失值(即缺失特征)。这里 X1 是呈现特征,而 X2、X3 是缺席特征。转载自[2]。
谢谢你的时间。并且不要犹豫留下任何评论和讨论!
本帖中的所有情节都是作者使用 SHAP 软件包创作的。如果你认为你的任何作品没有被恰当地引用,请告诉我。
[1] 负责任机器学习简介
[2] Janzing,d .,Minorics,l .,& Blö baum,P. (2019 年)。可解释人工智能中的特征相关性量化:一个因果关系问题。https://arxiv.org/abs/1910.13413
[3] Lundberg,S.M .,Erion,g .,Chen,h .,DeGrave,a .,Prutkin,J.M .,Nair,b .,Katz,r .,Himmelfarb,j .,Bansal,n .和 Lee,S.I. (2020)。用可解释的人工智能对树木从局部解释到全局理解。自然机器智能, 2 (1),2522–5839。
[4]https://christophm . github . io/interpretable-ml-book/shap . html
[5]m . Sundararajan 和 a . naj mi(2019 年)。用于模型解释的多个 Shapley 值。 arXiv 预印本 arXiv:1908.08474 。
[6] Sturmfels,p .,Lundberg,s .,和 Lee,S. I. (2020 年)。可视化特性属性基线的影响。提取, 5 (1),e22。
使用简化 it 共享来部署您的简化 it 应用
使用 Streamlit 共享部署您的机器学习 Web 应用程序
按作者部署 Streamlit 应用程序 GIF
在我以前的文章中,我谈到了从头开始构建 Github Web Scraper 和 KNN 分类模型,并为 UI 使用 Streamlit。
[## 如何构建一个 Streamlit 应用程序来抓取 Github 配置文件
在本教程中,我们将使用 Streamlit 构建一个 web 应用程序,它从 GitHub 中抓取用户信息。它显示了基本的…
medium.com](https://medium.com/python-in-plain-english/how-to-build-a-streamlit-app-to-scrape-github-profiles-f36d41fb98c) [## 如何从头开始构建 KNN 分类模型,并使用 Streamlit 对其进行可视化
虽然像 sklearn 这样的库让我们的生活变得更加轻松,但是从头开始制作一个模型总是一个很好的做法…
towardsdatascience.com](/how-to-build-a-knn-classification-model-from-scratch-and-visualize-it-using-streamlit-9fe8059cc418)
如果不能展示给别人看,那么构建 UI 又有什么意义呢?
进入 Streamlit 共享!
Streamlit sharing 是 Streamlit 提供的一项服务,用于轻松部署您的应用。下面我将介绍部署过程中的步骤。
获得简化的共享
Streamlit 共享目前处于测试模式,您需要加入等待列表才能访问它。通常需要几天才能进入,我在 72 小时内就进入了。去他们的网站注册
你注册的页面截图
几天之内,你应该会收到一封允许你访问的邮件。
用必要的库创建一个文本文件
创建一个包含依赖项的 requirements.txt 文件。下面是创建文件的命令
pip freeze > requirements.txt
在键入上述命令之前,请确保您的虚拟环境已激活
将文件上传到 GitHub
在 GitHub 上创建一个公共存储库并上传。py 文件和 requirements.txt 文件。
我的 Github 回购截图
我将部署以下回购
登录以简化共享
前往关注网站,使用您的 GitHub 账户登录。
https://share.streamlit.io/截图一
授权 Streamlit 并允许其访问您的 Github Repos。
创建新应用程序
https://share.streamlit.io/截图二
点击新应用程序
https://share.streamlit.io/截图三
选择您的存储库和分支。在我的例子中,它是回购协议的主分支,名为“rahulbanerjee 26/KNN-细流”
主文件路径应包含 app.py 文件的路径或。包含 Streamlit UI 代码的 py 文件。如果您的 app.py 文件位于名为“app”的文件夹中,路径将为“app/app.py”。确保您提供了正确的路径。填写正确信息后,单击部署。
现在只要坐好,放松☺️
部署完成后,您将收到一条成功消息,并能够看到应用程序的演示。
成功部署的屏幕截图
对回购进行的任何更新都会自动重新部署应用程序,几分钟后就可以看到您的更改,当然前提是没有错误。
你可以在这里找到我部署的应用
我最近用 WordPress 创建了一个博客,如果你能看看的话,我会很高兴的😃
[## Python 项目教程-使用这些 Python 项目教程改进您的简历/作品集。
使用 Streamlit 共享部署您的机器学习 Web 应用程序在我以前的文章中,我谈到过构建一个…
realpythonproject.com](https://realpythonproject.com/)
在 LinkedIn 上与我联系
[## Rahul baner JEE——产品工程实习生——EY | LinkedIn
查看 Rahul Banerjee 在世界上最大的职业社区 LinkedIn 上的个人资料。拉胡尔有 4 个工作列在他们的…
www.linkedin.com](https://www.linkedin.com/in/rahulbanerjee2699/)
在 Twitter 上与我联系
使用强力学习技术获得新技能
研究新的发展,跟上时代,用一个简单的技巧征服新知识
摄影爱好在 Unsplash 上
如果你刚刚进入职场或试图改变职业道路,学习新技能和发展新能力会让你走得更远,让你在就业市场脱颖而出。但是你如何有效地获得和保留新知识呢?
我们很幸运能够获得比以前任何一代人都多的知识和信息。如果你有正确的问题,正确的答案只需点击几下鼠标。然而,重要的是要对能从不同角度解决我们问题的教育者持开放态度。
有几种方式和数不清的方法可以帮助你不用回大学就能学到新的东西;书籍、在线课程、志愿工作、实习等。但是在这个故事中,我想强调一个帮助我做好准备的简单技巧:强力学习。你可以用它来实现你想做的任何事情,但我的经验来自信息技术和数字系统的世界。因此,这就是我的例子的来源。
Learning Rate 是为那些对 AI 和 MLOps 的世界感到好奇的人准备的时事通讯。你会在每周五收到我关于最新人工智能新闻和文章的更新和想法。订阅这里!
强力学习
想象一下,你正在尝试学习一门新的编程语言,一个像 Kubernetes 这样的新平台,甚至一个新概念,比如测试驱动开发(TDD)。首先,你试图理解为什么;为什么 Kubernetes 很重要?为什么 TDD 能帮助你成为更好的软件工程师?为什么 Python 是一种很棒的新语言,可以添加到我的工具箱中?
口袋里有了为什么,你会试着找一本介绍性的书或视频系列来理解基础知识:Python 中的变量、控制流和循环,Kubernetes 中的 pods 和服务,或者如何模拟服务。但是强力学习告诉你的是不要投资一门课程;拿两个!
**一开始,找一门很棒的入门课程,然后一头扎进去。**学习理论,研究习题,完成每一节。然后,找到另一个初学者的课程,从不同的角度接近同样的主题,做同样的事情。
但是,这对你有什么好处呢?那不是完全浪费时间吗?我认为不是。第一次学习一门学科,一切都是新的。你的大脑被新的术语、规则和概念轰炸。到课程结束时,你将已经内化了很大一部分,但不是全部。
从不同的角度重新审视整个事情,会让你专注于细节,打磨掉粗糙的边缘。第二次,并不是一切都是新的,啊哈时刻来得更频繁。而且,新的角度会给你一个新颖的观点,让你不再做一次枯燥的修改。
学完第二门课程后,你会更有信心进入更高级的部分,并继续努力达到精通。
结论
在这个故事中,我们看到了什么是强力学习技术,以及它如何帮助你学习和掌握新概念。我们很幸运能够获得比以前任何一代人都多的知识和信息。如果你有正确的问题,正确的答案只需点击几下鼠标。然而,对能从不同角度解决我们问题的教育者持开放态度是至关重要的。
关于作者
我叫迪米特里斯·波罗普洛斯,我是一名为阿里克托工作的机器学习工程师。我曾为欧洲委员会、欧盟统计局、国际货币基金组织、欧洲中央银行、经合组织和宜家等主要客户设计和实施人工智能和软件解决方案。
如果你有兴趣阅读更多关于机器学习、深度学习、数据科学和数据运算的帖子,请在 twitter 上关注我的媒体、 LinkedIn 或 @james2pl 。
所表达的观点仅代表我个人,并不代表我的雇主的观点或意见。
使用网络抓取和熊猫进行市场分析:西班牙的 ESL 教学
通过分析公开可用的数据,了解任何行业的竞争格局。以下是一个案例研究,利用西班牙最受欢迎的私人教师网站,考察了西班牙的 ESL(英语作为第二语言)市场。
介绍
互联网是一个势不可挡的地方。有大量公开可用的结构化数据集可供分析,但大多数信息都以 HTML 的形式存在,这些 HTML 是动态生成的,并经过优化以在视觉上取悦查看者,但对数据科学家的分析没有用处。
Web 抓取是一个强大的工具,它允许我们机械地获取公开可用的数据,并以一种对我们的需求有用的方式对其进行组织。一旦原始数据被提取、清理和处理,我们就可以轻松地使用 Pandas 来分析数据。
Web Scrape + Pandas = Insights / Competitive Advantage / Profit
使用这种有效工具组合的真实例子数不胜数:
- **竞争力分析。**浏览 Craigslist,查看旧金山的租赁市场,并代入等式,确定某栋房子的价格是否过低或过高。
- **市场概况。**浏览 Yelp,深入了解洛杉矶哪个县的塔克里亚斯代表不足。
- **可行性研究。**浏览 LinkedIn,评估拥有特定技能的员工的地理位置,以帮助确定在特定地区开设新分支机构是否有意义。
案例研究:西班牙的 ESL 私人教师
西班牙的英语水平是欧洲最低的之一,因此对 ESL 教学有很高的需求(最近的一项研究将其在 33 个欧洲国家中排在第 25 位)。随着世界变得越来越相互关联,企业越来越国际化,了解英语这一世界语言的重要性是至关重要的。
课外 ESL 教学可以采取多种形式;自学教材,书院集体课或者私教。本案例研究将考察一个最受欢迎的联系私人教师和学生的网站上的私人英语教师市场:Tus Clases speciales。
想象一下,一位新教师正在西班牙开始他们的 ESL 教学之旅,并希望对市场有所了解。我们想要回答的一些基本问题:
- **总体概述。**老师都在哪里?
- **小时费率。**哪些地区的时薪最高和最低?
- **教师需求。**根据人口数量,是否存在教师人数不足或过多的地区,这是否会影响报价?
- **收视率。**收视率影响价格吗,有百分之几的老师有?
- **付费广告。**有百分之多少的教师会为网站上的广告付费,这是否带来了明显的优势?
- **搜索结果。**谁在顶端?算法是否偏向有评论的老师,付费客户,新老师,有照片的老师…?
是时候开始刮了。
第 1 部分:从特定区域的结果页面获取概要文件 URL
让我们从访问瓦伦西亚地区英语教师的搜索结果开始,瓦伦西亚是地中海沿岸的西班牙海鲜饭的阳光之地。
第一步是直观地检查特定 URL 的结果,检查 HTML,并了解我们希望如何处理抓取。
url = '[https://www.tusclasesparticulares.com/profesores-ingles/valencia.aspx'](https://www.tusclasesparticulares.com/profesores-ingles/valencia.aspx')
(显示了 5 个广告列表,但是在每个结果页面上有 20 个——除了每个地区的最终结果页面)
让我们使用 chrome 上的“inspect”功能来挖掘一些广告列表背后的 HTML。
我们可以看到,每个广告列表都封装在一个特殊的 div 类中,遵循特定的模式:
#profile 1:
<div class="trr aitem int">
#profile 2:
<div class="trr aitem destacado plus int">
这个线索允许我们识别每个广告列表何时开始和结束。成功抓取网页的关键是识别这些线索和模式,以便机械地提取数据。所有有趣的细节都在这个 div 类中。
我们的攻击计划是使用极其有用的 Python 库 Beautiful Soup 遍历每个广告列表并提取所需的信息。原始的 HTML 很难处理,但是漂亮的 Soup 使用惯用的搜索、导航和编辑方式引导我们通过解析树。
#pull raw html
html = get(region_url)
soup = BeautifulSoup(html.text, 'html.parser')
让我们使用 soup.find_all 和 regex 包 re 来查找正确指向单个广告列表的文本模式——这将产生可迭代的 20 个教师简介。
for element in soup.find_all('div', {'class' : re.compile(r'^trr aitem')}):
在每个广告列表(div 类)中,解析并保存各种数据点,其中最重要的是单个列表的 URL(包含我们想要抓取的大部分数据)。
#look for a specific class that indicates the presence of a stock photo, indicating that the teacher does not use their own photo
if element.find('span', {'class' : 'spr_parrilla fotoparr'}) is None:
has_picture=True
else:
has_picture=False#look for ad listing URL (that directs to seperate page of listing) for link in element.find_all('div', {'class' : 'rightcontent'}):
if link.a is not None:
#construct full URL which links to specific ad
base_url = '[https://www.tusclasesparticulares.com'](https://www.tusclasesparticulares.com')
scrapped_url = link.a.get("href")
profile_url = base_url + scrapped_url
break
else:
#rare cases with no profile_url, log this information
profile_url = 'EMPTY'
break
我们暂时结束了!增加页面排名(记住,我们是在每个广告出现在页面上时循环播放),将所有内容保存到字典中,然后添加到列表中。
page_rank += 1
profe_dict = {'profile_url' : profile_url,
'results_page_url' : region_url,
'page_rank' : page_rank,
'results_ad_status' : results_ad_status,
'has_picture' : has_picture,
'html' : html}
landing_page_dict_list.append(profe_dict)
由此产生的词典列表可以很容易地转换成 Pandas 数据框架并打印出来,以显示原始数据。
我们立即拥有 20 条记录,每条记录都有几个有用的数据点。下一步是访问每个配置文件 URL,以获取每个广告列表的更多信息。
第 2 部分:Ping 每个配置文件的 URL,获取每个教师的详细数据点
让我们继续使用适用于第 1 部分的公式:
- 访问 URL 并使用 chrome 可视化检查 HTML。
- 找到数据存储的模式,并使用 Beautiful Soup 访问这些数据。
- 遍历 URL,将抓取的数据保存到字典列表中,转换成 Pandas Dataframe 并检查数据。
钻研 HTML。
用美汤解析。以下是代码示例。(GitHub 上有完整的笔记本,嵌入在本文末尾)。
#TITLE
if len(soup.find_all('div', {'class' : 'detinfotit'})) == 0:
ad_title = 'COULD NOT SCRAPE TITLE'
#add code to try alternative scraping method
else:
title_element = soup.find_all('div', {'class' : 'detinfotit'})[0]
ad_title = title_element.text.strip()
try:
ad_title
except NameError:
ad_title = 'EMPTY'#GEO LOCATION
for item in soup.find_all('p', {'id' : 'pProvincia'}):
province = ' '.join(item.text.split()[1:])
try:
province
except NameError:
province = 'EMPTY'#PRICE
for item in soup.find_all('div', {'id' : 'dvPrecio'}):
price = item.text.replace("\n","").replace("\r","")
try:
price
except NameError:
price = 'EMPTY'
#DESCRIPTION
for item in soup.find_all('div', {'class' : 'detcntsection c5'}):
description = item.text.strip()
try:
description
except NameError:
description = 'EMPTY'
#PROFE AD STATUS - BASIC, VERIFIED, PLUS
element = soup.find('p', {'class' : 'mgbottom5 fs16 bold'})
if element is None:
profe_ad_status = 'EMPTY'
else:
profe_ad_status = element.text#CREATE DICT WITH ALL ENTRIES AND RETURN DICTIONARY
scraped_profile={
'url': url,
'ad_title' : ad_title,
'teaching_subject' : teaching_subject,
'province' : province,
'class_level_para' : class_level_para,
'class_level_niveles' : class_level_niveles,
'method' : method,
'price' : price,
'teacher_name' : teacher_name,
'rating_count' : rating_count,
'profe_ad_status' : profe_ad_status,
'description' : description,
'page_rank' : profile_dictionary['page_rank'],
'results_ad_status' : profile_dictionary['results_ad_status'],
'has_picture' : profile_dictionary['has_picture'],
'results_page_url' : profile_dictionary['results_page_url'],
'alternative_scraping' : alternative_scraping,
'teacher_has_profile_page' : teacher_has_profile_page,
'is_academy' : is_academy,
'html' : html
}
return scraped_profile
酷!现在,我们从西班牙一个地区的单个结果页面中获得了大量有趣的数据。以下是数据示例:
对于每个地区,有几十个结果页面,西班牙有 54 个地区首府需要我们分析。我们还有一点工作要做。
分页问题很容易解决,因为 URL 可以机械地构造:
base_url = [https://www.tusclasesparticulares.com/profesores-ingles/valencia.aspx](https://www.tusclasesparticulares.com/profesores-ingles/valencia.aspx?pagina=7)next_page = [https://www.tusclasesparticulares.com/profesores-ingles/valencia.aspx?pagina=2](https://www.tusclasesparticulares.com/profesores-ingles/valencia.aspx?pagina=2)paginated_url = base_url + '?pagina=[PAGE_NUMBER]'
一些快速测试告诉我们,如果我们访问一个不存在的分页网址,即’?pagina=9999’ ,我们将被定向回基本 URL。使用一点逻辑,我们可以编写一个循环,该循环将访问特定区域的每个结果页面(以便找出给定区域有多少页面的搜索结果)。
results_page_profile_dicts = []
counter = 1
current_page = base_region_url#infinite loop and break out once pagination link false
while True:
html = get(current_page)
if html.url == current_page:
#If so, continue, pagination exists.
#CONTINUE WITH SCRAPING,
#dictionary with each profile and additional data from results page
results_page_profile_dicts.extend(get_profile_urls(current_page, html))
counter +=1
current_page = base_region_url + '?pagina=' + str(counter)
#If not, no more pagination exists
else:
break
#finish results page scraping
最后一个障碍是遍历西班牙所有地区的首府,以便提取大部分数据。让我们将所有值和 URL 存储在一个字典中,然后遍历每个值和 URL,合并分页逻辑,抓取广告列表结果页面,抓取各个个人资料页面,最后将所有内容保存到一个数据帧中。
region_lookup = {
'soria' : '[https://www.tusclasesparticulares.com/profesores-ingles/soria.aspx'](https://www.tusclasesparticulares.com/profesores-ingles/soria.aspx'),
'teruel': '[https://www.tusclasesparticulares.com/profesores-ingles/Teruel.aspx'](https://www.tusclasesparticulares.com/profesores-ingles/Teruel.aspx'),
......
'barcelona' : '[https://www.tusclasesparticulares.com/profesores-ingles/barcelona.aspx'](https://www.tusclasesparticulares.com/profesores-ingles/barcelona.aspx'),
'madrid' : '[https://www.tusclasesparticulares.com/profesores-ingles/madrid.aspx'](https://www.tusclasesparticulares.com/profesores-ingles/madrid.aspx')
}end_loop = len(region_lookup)
region_start = 0
for i in range(region_start, end_loop):
results_page_profile_dicts = []
counter = 1
current_page = base_region_url[PAGINATION LOGIC]
[SCRAPE]
[SAVE DATA TO DATAFRAME]
结果数据帧数据点:
我们有 57,123 项记录;大量的数据需要分析!在最初的装载和清理步骤之后,我们可以深入了解一些情况。
分析笔记
在我们开始分析之前,有一些事情需要记住。
- **范围。**我们只关注一个网站;这一分析并不全面涵盖西班牙的整个 ESL 教学市场——我们试图通过使用单个网站作为整体市场的代理来获得洞察力。
- **要价。**时薪是什么老师的单子;这并不代表他们实际得到的价格。
- **老师过滤。**这个分析旨在只看真正的老师;那些没有价格、没有个人资料图片或没有个人资料页面的照片被排除在分析之外。
每小时收费服务
教师的小时工资是如何分配的?哪些地区的产品价格最高和最低?小时费率的快速浏览:
我们可以看到,50%的教师要价在 10-15 欧元/小时之间,平均值为 12.6 欧元。
深入粒度,哪个自治区(地区)的费率最高?
不足为奇的是,像 Pais Vasco、Cataluñ和 Madrid 这样的经济强国接近榜首。
如果你是一名刚进入上述市场的新教师,这些数据将有助于确定收费标准。
教师的地理和收入潜力
老师在哪里?
最大的社区拥有最多的教师。有道理。
从总体人口来看,是否存在某些教师人数不足或过多的领域,这是否会影响每小时的工资?
使用从西班牙官方国家统计来源提取的人口数据,我们可以使用一些方法来比较 ESL 教学的供需。
下图显示了每个首都教师总数的百分比和居民总数的百分比。例如,马德里的居民占所有首都的 23%,但教师仅占总人口的 17%。有人可能会说,居民比教师更集中的地区,对新入职的教师来说,意味着更大的市场机会。
根据上述数据,马德里和萨拉戈萨似乎最需要教师。巴塞罗那、巴伦西亚、塞维利亚和格拉纳达都因教师过剩而引人注目(与居民相比)。这是有道理的,因为上述所有国家的首都都是外国人非常向往的城市。
让我们采取一种稍微不同的方法,简单地看一下居民与教师的比例。
有意思!使用这种方法,我们可以看到我们在之前的图表中选择的一些顶级球队(马德里和巴塞罗那)实际上位于中间位置。这种方法支持加的斯、巴伦西亚、格拉纳达和巴塞罗那都是市场饱和地区的观点。
只知道供给和需求并不能给我们全貌;我们应该将提供的教师时薪和外部工资数据(基于地区)结合起来,以更好地了解市场机会。使用相同来源的全国工资数据,我们计算了各地区的平均时薪,并将其与各地区 ESL 教师的时薪进行了比较。
按正速率差降序排列。
巴利阿里群岛在这一点上领先;教师的小时工资为 14 英镑,而地区平均工资约为 10.8 英镑。有人可能会说,基于生活成本和收入能力,教师的要求高于平均水平,在这个地区会做得很好。一个相反的论点可能是,与人们的收入相比,教师的要价太高了,他们将无法找到工作,因为他们太贵了。这是分析的局限性;我们只知道老师的问率;我们不知道他们是否真的以这样的速度找到了工作。
这里我们有相同的图表,但只是简单地看一下 ESL 教师和平均值之间的比率差异。
让我们尝试得到一个更全面的图片,并将前面的两个概念结合起来:下图是一个散点图,y 轴表示高于地区平均值的每小时教学工资,x 轴表示每个教师的居民人数。该地区的人口决定了点的大小。最理想的城市会在图表的右上方;工资高,教师需求量大(#居民/教师)。
*圆点代表人口的大小
看起来岛屿在召唤我们!巴利阿里群岛的首府帕尔马-德马略卡岛是当地工资收入最高的地方,教师的潜在客户也相对较多。
这是个教英语的好地方。帕特里克·努南的照片。
小时费率的变化
各种因素之间的小时工资率如何变化?基于以下因素计算小时费率的变化:
- 性别。** 大约有 10%的教师被授予“专业地位”(通过支付广告产品的费用被认为是特殊的),如“Profesora verificada”或“professor plus”。由于西班牙语是一种性别语言,我们可以解析出老师的性别。
- 已评级。有一个评分系统,客户可以给老师留下评论。这个类别只是看老师是否有评分。
- **广告助推。**教师可以购买一款广告产品,在结果顶部显示他们的列表,并为他们的广告添加醒目的黄色背景。
- **是本地人。**老师的母语是英语吗?这些所谓的“nativos”非常受欢迎,通常要价更高。我们试图通过在教师描述中使用正则表达式来解析出声称是“本地人”的配置文件。大约 7%的教师声称自己的母语是英语。
这里有很多东西要打开。
- 性别。** 当中位数相等时,男性倾向于更高的要价。
- **已评级。**至少有一个评级的教师倾向于列出更高的评级。有道理。
- 广告助推。为一个广告位置付费大大提高了要价。
- 是原生的。自称是本地人的老师比那些不是本地人的老师要求更高的工资。
深入探究收视率
大约 15%的教师至少有一个评级。其中,让我们深入了解每位教师的评级数量。
*只查看有评语的教师
大多数有评分的老师都不到 5 分;为了与其他老师区分开来,尝试获得 5 个以上的评分!收视率如何影响要价?
从逻辑上讲,随着收视率的增加,教师要求更高的时薪。神奇的范围似乎在 5-7 分左右。
付费广告的优势
大约 1.3%的教师支付广告费用来提高他们的成绩。下图显示了按“结果页”划分的教师人数,其中包括未付费教师和付费教师。搜索教师时,结果的第一页是第 1 页(包含 20 名教师)。结果的第二页是第 2 页。最多 500!
我们立即看到,绝大多数是未付费列表,一小部分付费列表位于图表的左下方。100 页之后的结果量是惊人的!谁会点击这么远去找他们的老师?
让我们深入研究一下前 20 页的结果。
我们可以更清楚地看到,付费列表导致更高的页面排名,此外,第一页结果的很大一部分是由支付广告费用的教师组成的。付费才能玩!
可视化另一种方式显然,付费教师将获得更相关的列表:
再一次,令人震惊的是 75%的老师(不付钱)在第 20 页的结果后被埋没了。这些老师肯定没有收到信息;它们必须是废弃或过时的配置文件。为了吸引眼球,人们必须支付广告费。不错的商业模式!
搜索结果
各种因素如何影响搜索结果中的排名?在上一节中,我们证明了广告可以带来更好的排名,但是如果有评级,有图片,个人资料页面或价格列表呢?人们可能会认为,拥有广告列表的这些基本元素会导致更高的搜索结果。
在存在评级的情况下,唯一有意义地影响结果排名的元素。所以,潜在的老师们,如果你想在结果中排名靠前,一定要有一些评级,并支付广告费用!
最后的想法
基于对我们的网络抓取数据的分析,我们可以提供以下行动项目:
- 通过支付的广告和评级,在搜索结果中获得更高的排名。
- 有至少 5 个评论,并声称你是一个母语人士,并保证要求更高的时薪。
- 选择一个对教师需求高、时薪高于平均水平的地区居住,比如帕尔马-德马略卡或圣塞瓦斯蒂安(这两个地方都很适合居住)。
结论
通过利用网络抓取和 pandas,我们已经获得了相当无序和混乱的数据,并将其转化为有意义的可视化和洞察力,让我们了解这个世界。
有用的链接
- https://www . data camp . com/community/tutorials/web-scraping-using-python
- https://www . plural sight . com/guides/web-scraping-with-beautiful-soup
- https://www.dataquest.io/blog/web-scraping-beautifulsoup/
笔记本电脑
所有的 Jupyter 笔记本和一个 CSV 格式的数据都可以在我的 Git 库中找到。
利用你的数据技能在线赚钱
卡尔·海尔达尔在 Unsplash 上拍摄的照片
从你的数据技能中获得在线收入的三种方法
现在,在世界大部分地区,许多人正在失去他们通常的谋生手段。冠状病毒疫情已经导致大量行业关闭,这意味着许多企业主无法进行交易,员工被大量解雇或休假。
在我所在的英国,国家统计局(ONS) 最近对 6171 家企业的调查显示,24%的企业暂时关闭,93%的企业营业额低于预期。在美国,仅上周就有 320 万人申请失业保险,使得自疫情开始以来的失业申请总数达到了惊人的 3300 万。
在美国,仅在上周就有 320 万人申请失业保险
作为一名数据科学家,我个人受到了这场危机的影响,暂时失去了目前朝九晚五的工作。我发现自己正处于一个巨大的个人经济不确定时期,在这个世界上,越来越少的公司在招聘,许多企业面临倒闭。
我发现自己正处于一个巨大的个人经济不确定时期,在这个世界上,越来越少的公司在招聘,许多企业面临倒闭
在过去的几周里,我一直在寻找独立使用我的技能来创造额外收入来源的方法,这可能至少部分地为未来提供一些财务保障。
在下面的文章中,我将分享我发现的三个关键领域,利用我的在线数据技能来创造额外的收入。
现在用这种方式赚取收入的额外好处是,你可以选择自己的时间来处理其他事情。如果你和我一样,也是一位家长,而你的孩子目前无法上学或去其他儿童保育机构,这一点尤其有用。
在线课程
如果你一直在数据领域工作,那么你就拥有了其他人可以学习的有价值的技能和知识。有许多在线平台允许任何有适当技能和经验的人创建和营销在线课程并赚取收入。
数据营 有一个由 270 名教练组成的网络,你可以申请加入。他们表示,他们目前拥有超过 590 万数据科学家的受众,课程讲师可以通过他们的收入共享平台赚取版税。目前网站上的常见问题是,讲师的平均月收入在 1000-2000 美元之间。
我目前正在申请成为这里的讲师,如果我成功了,我会用实际收入数据更新这篇文章。
如果你没有 Datacamp 的运气,或者觉得它不适合你,还有许多其他的在线课程平台,你可以在那里赚钱。其中之一就是 复数视线 。该课程平台招募数据专家来创建基于视频、书面或实践的教育材料。与 Datacamp 类似,付费是基于版税的,版税与你的内容获得的浏览量以及该平台的付费用户数量相关联。Troy Hunt 的这篇出色的 文章提供了一些关于为 Pluralsight 工作的见解,包括一些关于收入潜力的信息。
你可以在很多其他地方发布在线课程内容并赚钱。其中包括 Udemy 和 Skillshare 等课程平台。你也可以考虑在 Youtube 等社交媒体平台上发布一系列教程,这样你就可以从付费广告中赚钱。
博客
在过去的一年半时间里,我一直在写博客,并通过媒体合作伙伴计划赚取微薄的额外收入。就博客平台而言,媒体可能是最容易起步和赚钱的。你不一定需要有大量的追随者,如果你的作品有趣且质量高,那么 Medium 有可能以标签的形式发布,这样你就能获得大量的浏览量。
我目前每月收入约 400 美元,每周花 5-10 个小时写作,通常每周在平台上发布一次。我将很快写一篇后续文章,讲述我如何、何时以及为什么在媒体上写数据科学。
除了 Medium 之外,你还可以将自己的博客作为一个独立的网站。Wordpress 是一个很好的平台。你可以免费开博客,也可以付费购买额外的功能,比如独特的域名和更大的存储空间。如果你的博客能获得足够的流量,那么赚钱的方式有很多种,包括 Google Adsense 和像 Amazon Associates 这样的联盟项目。
自由职业
在这篇文章中,我要谈的最后一个领域是自由职业者机构和网站。网上有很多平台,你可以提供你的技能、经验和服务来换取报酬。Upwork 可能是最受欢迎的网站之一。在这里你可以创建你的个人资料和申请自由职业者的工作。
快速搜索现在广告中与数据相关的工作,会显示 16,744 个结果。
希望这篇文章为您提供了一个很好的起点,让您可以找到利用在线数据技能赚取额外收入的方法。冠状病毒的经济影响将被广泛感受到,因此你可能会发现,当你开始这些举措时,它们不会立即产生可观的收入。
然而,随着各种商业领域最终开始重新开放,我相信,如果你在这些领域建立了一些基础,你将看到你的收入增长。我认为,关键在于选择一个重点领域,持续不断地创造,并为未来建立一个轮廓。
感谢阅读!
我每月都会发一份简讯,如果你想加入,请点击此链接注册。期待成为您学习旅程的一部分!
放弃
本报告中表达的想法和观点仅代表我个人,不一定代表我公司的观点。本报告旨在提供教育,不应被理解为个人投资建议,也不应被理解为购买、出售或持有任何证券或采取任何投资策略的建议。
在我的锻炼中再次使用机器学习,这次是建立一个私人教练
以下完整视频
在之前的故事中,我试图解决如何发现锻炼是否以正确的形式进行,使用 OpenPose 收集锻炼数据,并使用动态时间扭曲将锻炼执行与作为基线的正确执行进行比较。
这一次,我添加了以下其他组件来大大增强整体体验:
- 令人敬畏的面部识别库(【https://github.com/ageitgey/face_recognition】T2),用于执行面部识别和加载适当的锻炼
- 令人敬畏的语音识别(【https://github.com/Uberi/speech_recognition】T4),用于处理用户的回答
- 令人敬畏的谷歌文本到语音转换(https://github.com/pndurette/gTTS),用来和用户交谈和指导
这是工作流程的图形表示
基本流程——作者绘制
我仍然使用 OpenPose 的“dockerized”版本(https://github.com/garyfeng/docker-openpose)——本地安装相当长——调整一些参数也有可能实现良好的 fps。
体验非常简单:
- 你进入锻炼区域,面部识别组件匹配你的面部,提取一个轮廓(现在只是一个名字)
- 培训师会问你是否准备好开始,并等待你的回答(至少回答“是”——这一部分是按照脚本编写的,当然,可以用更复杂的解决方案来扩展)
- 教练将你的相关“wod”(一天中的锻炼)与要进行的练习(目前只有一项)一起载入
- 在 OpenPose 组件记录身体数据的同时,您可以执行练习重复和设置
- 教练将收集的数据与正确的基线进行比较,评估身体每个部位的动态时间弯曲值,最后给出一个中值。
- 如果该值低于某个阈值,则认为锻炼是正确的,教练给出该反馈,否则它会发出警告(目前是一般的警告,但是应该可以根据哪些身体部位是不正确的给出具体的提示)
- 重复,直到所有的重复完成
但是让我们来看看实际情况——很抱歉视频编辑,肯定是我做得不好:)
我努力工作来获得一个体面的经历,但是,当然,还有很多事情要做。
例如,现在锻炼估计是有时间限制的,但是应该可以训练系统理解锻炼何时开始和结束,丢弃其他帧用于评估。
此外,这只是一种独特的锻炼类型(引体向上),是专门用我的身体测量值进行的基线正确锻炼,但应该可以用通用基线进行不同的锻炼。
关于执行过程中具体错误的更精确的反馈是另一个有趣的研究点。
实现了这一点,它可以用于任何类型的锻炼(健身,瑜伽等),也可以用于对象(使用自定义跟踪对象算法),在独立模式下,或作为真正的教练的助手。
这是另一个例子,展示了结合不同的工具和库,在几个小时内可以实现什么,这在几年前是不可想象的,并且一直让我感到惊讶!
我希望你喜欢看这个(但是也看看上面的视频),谢谢你!
附录
让我们来看一些代码,看看执行人脸识别和文本到语音转换是多么简单。
对于 OpenPose 部分和练习评价,只需参考我的以前的故事附录
人脸识别
只是使用了演示部分的代码
所有的魔法都在编码中。一旦一张已知的脸被编码,你就可以把它和相机捕捉到的画面进行比较
antonello_image = face_recognition.load_image_file("faces/anto.jpg")antonello_image_encoding = face_recognition.face_encodings(antonello_image)[0]
交谈并处理答案
只是将文本保存到 mp3 文件中,然后播放它来说话
def say(text, language = 'en'):
speech = gTTS(text = text, lang = language, slow = False)
mp3_file = 'text.mp3'
speech.save(mp3_file)
p = vlc.MediaPlayer(mp3_file)
p.play()
time.sleep(2)
为了处理答案,我们使用谷歌引擎。
代码超级简单
def process_answer():
recognizer = sr.Recognizer()
microphone = sr.Microphone()
with microphone as source:
recognizer.adjust_for_ambient_noise(source)
audio = recognizer.listen(source)try:
response = recognizer.recognize_google(audio)
print('Received response {}'.format(response))
except sr.RequestError:
print('Error')
except sr.UnknownValueError:
response = 'Sorry I cannot understand'return response
把所有这些放在一起,你可以进行一个基本的对话
person = recognize() # using the face recognitionsay('Hello ' + person + ', are you ready?')response = process_answer()if ('yes' in response):
say('Ok, let\'s start')
else:
say('No problem, maybe tomorrow')
基于机器学习的二手车价格预测
数据清洗、数据预处理、8 种不同的 ML 模型和一些来自数据的见解
图片由 Panwar Abhash Anil 提供
在我的 GitHub 页面 可以到达与此相关的所有 Python 脚本。如果您感兴趣,还可以在同一个存储库中找到用于这项研究的数据清理和数据可视化的脚本。
该项目也使用 Django 部署在Heroku上,也通过Dockerizing Django App部署在 Amazon EC2 上
内容
- 数据清理(识别空值、填充缺失值和移除异常值)
- 数据预处理(标准化或规范化)
- ML 模型:线性回归、岭回归、拉索、KNN、随机森林回归、Bagging 回归、Adaboost 回归和 XGBoost
- 模型的性能比较
- 来自数据的一些见解
为什么价格特征通过对数变换进行缩放?
在回归模型中,对于 X 的任何固定值,Y 都分布在这个问题数据中——目标值(价格)不是正态分布,它是右偏的。
为了解决这个问题,当目标变量具有偏斜分布时,对其应用对数变换,并且我们需要对预测值应用反函数来获得实际的预测目标值。
因此,为了评估模型,计算 RMSLE 以检查误差,并且还计算 R2 分数 以评估模型的准确性。
一些关键概念:
- **学习率:**学习率是一个超参数,它控制我们根据损耗梯度调整网络权重的程度。该值越低,我们沿下坡行驶的速度越慢。虽然在确保我们不会错过任何局部最小值方面,这可能是一个好主意(使用低学习率),但这也可能意味着我们将需要很长时间才能收敛——特别是如果我们被困在一个平坦区域。
- n_estimators :这是在进行最大投票或平均预测之前,你想要建立的树的数量。树的数量越多,性能越好,但代码速度越慢。
- **R 评分:**是数据与拟合回归线接近程度的统计度量。它也被称为决定系数,或多元回归的多重决定系数。0%表示该模型不能解释响应数据在其平均值附近的任何可变性。
1.数据:
本项目使用的数据集是从 Kaggle 下载的。
2.数据清理:
第一步是从数据集中移除不相关/无用的特征,如“URL”、“region_url”、“vin”、“image_url”、“description”、“county”、“state”。
下一步,检查每个要素的缺失值。
显示缺失值(图片由 Panwar Abhash Anil 提供)
接下来,通过适当的方法用适当的值填充现在缺失的值。
为了填补缺失值,使用 迭代估算器 方法,实现不同的估计器,然后使用cross _ val _ score计算每个估计器的 MSE
- 平均值和中位数
- 贝叶斯岭估计量
- 决策树回归估计量
- 树外回归估计量
- kneighbors 回归估计量
使用不同插补方法的均方误差(图片由 Panwar Abhash Anil 提供)
从上图中,我们可以得出结论,extractree regressor估计量将更好地用于填充缺失值的插补方法。
填充后缺少值(图片由 Panwar Abhash Anil 拍摄)
最后,在处理缺失值后,零个空值。
**异常值:**用四分位差(IQR)法从数据中剔除异常值。
显示异常值的价格箱线图(图片由 Panwar Abhash Anil 提供)
显示异常值的里程表方框图(图片由 Panwar Abhash Anil 提供)
年度柱状图和柱状图(图片由 Panwar Abhash Anil 提供)
- 从图 1 中可以看出,低于 6.55 和高于 11.55 的价格是异常值
- 从图 2 中,不可能得出什么结论,因此计算 IQR 来发现异常值,即低于 6.55 和高于 11.55 的里程表值是异常值。
- 从图 3 来看,1995 年以下和 2020 年以上的年份是异常值。
最后,处理前的数据集形状= (435849,25),处理后的数据集形状= (374136,18)。总共删除了 61713 行和 7 列。
3.数据预处理:
**标签编码器:**在我们的数据集中,12 个特征是分类变量,4 个是数值变量(价格列除外)。为了应用 ML 模型,我们需要将这些分类变量转换成数值变量。而 sklearn 库 LabelEncoder 就是用来解决这个问题的。
归一化:数据集不是正态分布。所有功能都有不同的范围。如果不进行归一化,ML 模型将尝试忽略低值要素的系数,因为与大值相比,它们的影响非常小。因此为了归一化,使用了 sklearn 库,即 MinMaxScaler 。
**训练数据。**在此过程中,90%的数据被分割为列车数据,10%的数据作为测试数据。
4.ML 型号:
在本节中,不同的机器学习算法用于预测价格/目标变量。
数据集是受监督的,因此模型按给定的顺序应用:
1)线性回归:
在统计学中,线性回归是一种模拟标量响应(或因变量)和一个或多个解释变量(或自变量)之间关系的线性方法。在线性回归中,使用线性预测函数对关系进行建模,其未知模型参数根据数据进行估计。这种模型被称为线性模型。 更多详情
系数:每个系数的符号表示预测变量和响应变量之间关系的方向。
- 正号表示随着预测变量的增加,响应变量也增加。
- 负号表示随着预测变量的增加,响应变量减少。
显示线性回归性能的图表(图片由 Panwar Abhash Anil 提供)
显示数据集重要特征的图表(图片由 Panwar Abhash Anil 提供)
考虑到这个数字,线性回归表明 年份、气缸、变速器、燃油和里程表 这五个变量是最重要的。
线性回归的结果(图片由 Panwar Abhash Anil 提供)
2)岭回归:
岭回归是一种分析多重共线性数据的技术。当多重共线性发生时,最小二乘估计是无偏的,但是它们的方差很大,因此它们可能远离真实值。
为了在岭回归中找到最佳 alpha 值,应用了黄砖库alpha selection。
显示 Alpha 最佳值的图表
从图中可以看出,最适合数据集的 alpha 值是 20.336。
注意:α的值不是常数,它随时都在变化。
使用 alpha 的这个值,实现了 Ridgeregressor。
显示重要特征的图表
考虑到这个数字,拉索回归表明 年份、气缸、变速器、燃油和里程表 这五个变量是最重要的。
这个模型的最终结果(图片由潘瓦尔·阿布哈什·阿尼尔提供)
岭回归的表现几乎和线性回归一样。
3)套索回归:
套索回归是一种使用收缩的线性回归。收缩是指数据值向中间点收缩。lasso 程序鼓励简单、稀疏的模型(即参数较少的模型)。
为什么使用套索回归?
套索回归的目标是获得预测因子的子集,使定量响应变量的预测误差最小化。lasso 通过对模型参数施加约束来实现这一点,这会导致某些变量的回归系数向零收缩。
这个模型的最终结果(图片由潘瓦尔·阿布哈什·阿尼尔提供)
但是对于这个数据集,没有必要进行套索回归,因为误差没有太大的差别。
4)k 近邻回归器:回归——基于 k 近邻。
通过与训练集的最近邻居相关联的目标的局部插值来预测目标。
k -NN 是一种基于实例的学习,或懒惰学习,其中函数仅局部近似,所有计算都推迟到函数求值。 阅读更多
每个 K 范围 1-9 的误差图(图片由 Panwar Abhash Anil 提供)
从上图可以看出,对于 k=5,KNN 给出的误差最小。因此,使用 n_neighbors=5 和 metric='euclidean '来训练数据集。
KNN 的最终结果(图片由潘瓦尔·阿布哈什·阿尼尔提供)
性能 KNN 更好,并且误差随着精度的增加而减小。
5)随机森林:
随机森林是由许多决策树组成的分类算法。它在构建每棵树时使用 bagging 和特征随机性,试图创建一个不相关的树木森林,其委员会的预测比任何单棵树都更准确。 阅读更多
在我们的模型中,用 max_features 0.5 创建了 180 个决策
随机森林的性能(真实值与预测值之比)
这是一个简单的柱状图,说明了 年 是汽车最重要的特征,然后是 里程表 变量,然后是其他。
随机森林模型精度表(图片由 Panwar Abhash Anil 提供)
随机森林的性能更好,精确度提高了大约。10%很好。由于随机森林在建造每棵树时使用装袋,因此将执行下一个装袋回归程序。
6)装袋回归器:
Bagging 回归器是一种集合元估计器,它将每个基础回归器拟合到原始数据集的随机子集上,然后聚合它们的预测(通过投票或平均)以形成最终预测。这种元估计器通常可以被用作一种方法,通过将随机化引入到其构造过程中,然后从中进行集成,来减少黑盒估计器(例如,决策树)的方差。 阅读更多
在我们的模型中,DecisionTreeRegressor 用作估计器,max_depth=20,它创建了 50 个决策树,结果如下所示。
Bagging 回归器精度表(图片由 Panwar Abhash Anil 提供)
随机森林的性能比 Bagging 回归器好得多。
随机森林和 Bagging 的关键区别:根本区别在于,在随机森林中,仅从全部和特征中随机选择一个子集,该子集中的最佳分裂特征用于分裂树中的每个节点,这与 bagging 中考虑所有特征来分裂节点不同。
7) Adaboost 回归器:
AdaBoost 可用于提升任何机器学习算法的性能。Adaboost 帮助你将多个“弱分类器”组合成单个“强分类器”。库使用:AdaBoostRegressor&阅读更多
这是一个简单的柱状图,说明了 年 是汽车最重要的特征,然后是 里程表 变量,然后是型号等。
在我们的模型中,DecisionTreeRegressor 用作具有 24 max_depth 的估计器,创建 200 棵树&以 0.6 learning_rate 学习模型,结果如下所示。
AdaBoost 回归器的精度表(图片由 Panwar Abhash Anil 提供)
8) XGBoost: XGBoost 代表极端梯度增强
XGBoost 是一种集成学习方法。XGBoost 是梯度提升决策树的实现,旨在提高速度和性能。这种强大算法的美妙之处在于其可扩展性,它通过并行和分布式计算驱动快速学习,并提供高效的内存使用。 阅读更多
这是一个简单的重要性递减柱状图,它说明了哪一个 特征/变量是汽车的重要特征更重要。
根据 XGBoost 的说法,里程表是一个重要的特征,而从以前的车型年是一个重要的特征。
在该模型中,创建了 200 个最大深度为 24 的决策树,并且该模型以 0.4 的学习速率学习参数。
XGBoost 回归器精度表(图片由 Panwar Abhash Anil 提供)
4)模型的性能比较:
每个模型的精度比较(图片由 Panwar Abhash Anil 提供)
总体精度表(图片由潘瓦尔·阿布哈什·阿尼尔提供)
从上图可以得出结论,XGBoost 回归器以 89.662%的准确率表现优于其他模型。
5)来自数据集的一些见解:
从这一对情节中,我们不能得出任何结论。变量之间没有相关性。
配对图以寻找相关性
从 distplot 中,我们可以得出结论,最初,价格快速上升,但在某个特定点之后,价格开始下降。
显示价格分布的图表(图片由 Panwar Abhash Anil 提供)
3 从图 1 中,我们分析出柴油版车型的车价高于电动版车型的车价。混动变形车价格最低。
显示每种燃料价格的条形图
4 从图 2 中,我们分析出各种燃料的汽车价格也取决于汽车的状况。
燃料和价格与色调条件之间的条形图
从图 3 中,我们分析出 1995 年后汽车价格每年都在上涨,从图 4 中,汽车的数量也每年都在增加,在某个时间点,即 2012 年,汽车的数量几乎相同。
显示价格每年变化的图表
从图 5 中,我们可以分析出汽车的价格也取决于汽车的状况,从图 6 中,价格也随着汽车大小的状况而变化。
显示车况价格的柱状图
7 从图 7-8 中,我们分析了汽车价格以及汽车的各个变速器的价格。人们愿意购买有“其他变速器”的汽车,而有“手动变速器”的汽车价格较低。
柱状图显示了传输的价格(图片由 Panwar Abhash Anil 提供)
8 下面有相似的图形,具有相同的洞察力,但功能不同。
结论:
通过执行不同的最大似然模型,我们的目标是获得更好的结果或最大精度的更少误差。我们的目的是预测有 25 个预测值和 509577 个数据条目的二手车的价格。
首先,执行数据清洗以从数据集中移除空值和离群值,然后实施 ML 模型来预测汽车价格。
接下来,借助数据可视化特性进行了深入探讨。检查特征之间的关系。
从下表可以得出结论,XGBoost 是预测二手车价格的最佳模型。XGBoost 作为回归模型给出了最好的 MSLE 和 RMSLE 值。
精度表(图片由 Panwar Abhash Anil 提供)
仅此而已!如果你读到这里,感谢你的阅读。感谢任何反馈!并且可以在Linkedin上联系到。