TowardsDataScience-博客中文翻译-2021-六十五-
TowardsDataScience 博客中文翻译 2021(六十五)
原文:TowardsDataScience Blog
协议:CC BY-NC-SA 4.0
MongoDB:从安装到实现
原文:https://towardsdatascience.com/mongodb-3f605f8533a0?source=collection_archive---------33-----------------------
介绍如何使用和充分利用 MongoDB 的第 4 部分

照片由 Unsplash 上的迈尔·卡洛拍摄
概述
在第三部分,我们开始着手月度预算应用项目的 FastAPI 部分。首先,我们下载了必要的模块并开始实现。因为 MongoDB 使用 NoSQL,所以我们必须有点创意。一旦 pymongo 连接并工作,我们就能够连接到数据库。对于每个集合,我们创建了一个端点来获取每个文档。
继续
接下来,我们可以在其他端点上插入和删除。在此之前,我们还可以讨论如何在 MongoDB 端删除,因为我们之前只讨论了如何插入集合。
这个时候就不讨论怎么更新了。这将是下一部分。因为 MongoDB 是 NoSQL,更新稍微复杂一点。例如,在杂货店集合中,有一个嵌套的项目文档。更新嵌套文档,尤其是没有 ID 的文档,会增加难度。为了进一步讨论该做什么,我们将为此使用一个单独的部分。现在,我们将只关注插入和删除。
另一个需要注意的是,我们发送了带有验证需求的娱乐,主要是某些变量类型是必需的。一个这样的类型是双。不幸的是,pydantic 支持 float,但不支持 double。因此,对于这一点,请注意,我放弃了娱乐集合,并在没有任何验证的情况下重新创建了它。
为了测试,我们将再次使用 Postman 和 uvicorn 来运行 python 应用程序。现在我们有了一点背景和方向,我们可以从打开 MongoDB 开始。我将跳过这一步,因为我们在前面的部分已经讨论过了。
在 MongoDB 中删除
实际上,我们需要能够删除的数据。别担心,我们可以将一些样本数据插入到我们的杂货店集合中。

接下来,因为这是样本数据,我们需要从杂货店集合中删除该文档。这样,我们可以用它来测试 API 部分。
要删除,我们使用关键字“remove”:
db.Grocery.remove({_id:ObjectId(“<IdHere>”)})

为杂货店插入端点
首先,我们需要使用一个基本类来绘制我们的字段。这将发生在 app.py 文件中,并使用 pydantic。在导入中,添加以下行:
from pydantic import BaseModel
接下来,我们需要声明这些类。它应该发生在应用程序声明之后,但在任何端点之前。我们需要的第一个类是一个 Item 类,它稍后将作为一个列表嵌入到杂货类中。
class Item(BaseModel):
ProductName: str
UnitPrice: float
Quantity: int
完成后,我们可以创建杂货类:
class Grocery(BaseModel):
Location: str
Total: float
Coupon: float = None
Giftcard: float = None
User: str
CreatedDate: str
TransactionDate: str
Items: List[Item]
注意,“None”值不允许在 JSON 对象中传递任何东西。现在我们可以创建端点了。它将与第一个杂货端点的地址相同,但将使用“post”而不是“get”。我们还需要寄一本杂货字典。
@app.post(“/receiptsapi/v1/grocery”)
async def AddGrocery(grocery: Grocery):
groceries = await storage.add_groceries(grocery.dict())
return groceries
接下来,我们需要处理 storage.py 文件中的 add _ 杂货()函数。首先,我们将使用 HTTPException 来引发我们可能遇到的任何错误。我们还需要声明一个 ObjectId 来插入和删除 MongoDB,因此我们需要在 pymongo 导入之后、声明 pymongo 客户端之前添加两个导入:
from fastapi import HTTPException
from bson.objectid import ObjectId
像其他 FastAPI 插入一样,我们需要一个围绕插入的“try-except ”,以及一个带有描述性消息的状态返回。我已经在我的其他博客中写过如何做到这一点。然而,不同之处在于,因为 pymongo 使用 JSON 对象,所以我们不需要将字典放在对象中。我们可以简单地添加字典:
async def add_groceries(grocery):
try:
db.Grocery.insert(grocery)
client.close()
return {“response”: “User inserted: %s” %(grocery)}
except Exception as e:
client.close()
raise HTTPException(status_code = 404, detail = e)
对于测试部分,添加要插入主体的 JSON 对象:

插入紧急端点
像杂货店的插页一样,首先需要设置应急类:
class Emergency(BaseModel):
Total: float
CreatedDate: str
TransactionDate: str
Reason: str = None
接下来,我们将添加端点。
@app.post(“/receiptsapi/v1/emergency”)
async def AddEmergency(emergency: Emergency):
emergencies = await storage.add_emergencies(emergency.dict())
return emergencies
既然已经在 app.py 文件中设置了端点,那么我们将转到 storage.py 文件来处理 add_emergencies 函数:
async def add_emergencies(emergency):
try:
db.Emergency.insert(emergency)
client.close()
return {“response”: “User inserted: %s” %(emergency)}
except Exception as e:
client.close()
raise HTTPException(status_code = 404, detail = e)
接下来,简单地在 postman 中测试。

插入娱乐端点
在设置娱乐收藏时,出现了一个错误。物品清单与食品杂货清单不一致。这对 NoSQL 来说既是优势也是陷阱,标准化并不是必需的。我们可以使用不同名称的项目类来解决这个问题:
class EntertainItem(BaseModel):
Name: str
Price: float
现在我们可以添加娱乐类:
class Entertainment(BaseModel):
Location: str
Total: float
Giftcard: float = None
Discount: float = None
CreatedDate: str
TransactionDate: str
Items: List[EntertainItem]
接下来,我们可以设置娱乐端点:
@app.post(“/receiptsapi/v1/entertainment”)
async def AddEntertainment(entertainment: Entertainment):
entertainments = await storage.add_entertainments(entertainment.dict())
return entertainments
像以前一样,我们然后移动到 storage.py 文件来清空 add_entertainments 函数:
async def add_entertainments(entertainment):
try:
db.Entertainment.insert(entertainment)
client.close()
return {“response”: “User inserted: %s” %(entertainment)}
except Exception as e:
client.close()
raise HTTPException(status_code = 404, detail = e)
现在我们可以测试:

插入保险端点
幸运的是,我们没有这个集合的特例,所以我们不需要设置任何额外的类。我们可以像以前一样只使用 app.py 文件中的一个:
class Insurance(BaseModel):
Location: str
Total: float
CreatedDate: str
TransactionDate: str
在同一文件中,我们添加了端点:
@app.post(“/receiptsapi/v1/insurance”)
async def AddInsurance(insurance: Insurance):
insurance = await storage.add_insurance(insurance.dict())
return insurance
接下来,移动到 add_insurance 函数的 storage.py 文件:
async def add_insurance(insurance):
try:
db.Insurance.insert(insurance)
client.close()
return {“response”: “User inserted: %s” %(insurance)}
except Exception as e:
client.close()
raise HTTPException(status_code = 404, detail = e)
邮递员上:

插入租金的端点
另一个简单的类,因为该类没有特例:
class Rent(BaseModel):
Total: float
Minimum: float = None
Additional: float = None
CreatedDate: str
TransactionDate: str
现在到了终点:
@app.post(“/receiptsapi/v1/rent”)
async def AddRent(rent: Rent):
rent = await storage.add_rent(rent.dict())
return rent
接下来,转到 storage.py 来设置 add_rent 函数:
async def add_rent(rent):
try:
db.Rent.insert(rent)
client.close()
return {“response”: “User inserted: %s” %(rent)}
except Exception as e:
client.close()
raise HTTPException(status_code = 404, detail = e)
给邮递员进行测试:

插入实用程序的端点
插入 MongoDB 的最后一个端点。我们首先建立我们的类:
class Utility(BaseModel):
Type: str
Total: float
CreatedDate: str
WithdrawalDate: str
接下来,添加端点:
@app.post(“/receiptsapi/v1/utility”)
async def AddUtility(utility: Utility):
utilities = await storage.add_utilities(utility.dict())
return utilities
现在转到 storage.py,使 add_utilities 函数:
async def add_utilities(utility):
try:
db.Utility.insert(utility)
client.close()
return {“response”: “User inserted: %s” %(utility)}
except Exception as e:
client.close()
raise HTTPException(status_code = 404, detail = e)
最后,交给邮递员进行测试:

删除杂货店的端点
既然我们已经学习了如何使用 pymongo 和 FastAPI 插入数据库,我们还可以学习如何删除。为此,我们需要知道生成的 id。因为它是在我们的基本“get”语句中返回的,所以我们知道区分文档的 GUID。我们需要将该值传递给函数。要使用 delete,我们将使用“delete”而不是“post”。
@app.delete(“/receiptsapi/v1/grocery/{id}”)
async def DeleteGrocery(id: str):
groceries = await storage.remove_grocery(id)
return groceries
在 remove _ 杂货函数中,我们需要将字符串 Id 声明为 ObjectId。使用 delete_one 函数后,“try-except”中的所有其他结构将像插入:
async def remove_grocery(groceryId):
try:
db.Grocery.delete_one({“_id”: ObjectId(groceryId)})
client.close()
return {“response”: “User inserted: %s” %(groceryId)}
except Exception as e:
client.close()
raise HTTPException(status_code = 404, detail = e)
在 Postman 中,我们需要发送 Id 的字符串版本。它返回以下结果:

删除紧急端点
成功删除一个端点后,我们现在将进入紧急端点:
@app.delete(“/receiptsapi/v1/emergency/{id}”)
async def DeleteEmergency(id: str):
emergencies = await storage.remove_emergency(id)
return emergencies
现在,我们转到 storage.py 文件:
async def remove_emergency(emergencyId):
try:
db.Emergency.delete_one({“_id”: ObjectId(emergencyId)})
client.close()
return {“response”: “User inserted: %s” %( emergencyId)}
except Exception as e:
client.close()
raise HTTPException(status_code = 404, detail = e)
知道了我们现在对测试的了解,使用“get”来获取最近插入的内容的 Id,并使用它来删除:

删除娱乐端点
同样,这些任务将变得重复,因为它们除了变量名之外都是相似的。首先,我们在 app.py 文件中工作:
@app.delete(“/receiptsapi/v1/entertainment/{id}”)
async def DeleteEntertainment(id: str):
entertainment = await storage.remove_entertainment(id)
return entertainment
接下来,我们转到 storage.py 文件:
async def remove_entertainment(entertainmentId):
try:
db.Entertainment.delete_one({“_id”: ObjectId(entertainmentId)})
client.close()
return {“response”: “User inserted: %s” %( entertainmentId)}
except Exception as e:
client.close()
raise HTTPException(status_code = 404, detail = e)
现在,我们转到邮递员:

删除保险端点
半路上。首先在 app.py 文件中设置 DeleteInsurance 端点:
@app.delete(“/receiptsapi/v1/insurance/{id}”)
async def DeleteInsruance(id: str):
insurance = await storage.remove_insurance(id)
return insurance
现在我们做 storage.py 部分:
async def remove_insurance(insuranceId):
try:
db.Insurance.delete_one({“_id”: ObjectId(insuranceId)})
client.close()
return {“response”: “User inserted: %s” % (insuranceId)}
except Exception as e:
client.close()
raise HTTPException(status_code = 404, detail = e)
现在有请邮差:

删除租赁端点
我们快到了。现在,我们将设置删除租金端点:
@app.delete(“/receiptsapi/v1/rent/{id}”)
async def DeleteRent(id: str):
rent = await storage.remove_rent(id)
return rent
您已经知道了流程,现在转到 storage.py 文件:
async def remove_rent(rentId):
try:
db.Rent.delete_one({“_id”: ObjectId(rentId)})
client.close()
return {“response”: “User inserted: %s” % (rentId)}
except Exception as e:
client.close()
raise HTTPException(status_code = 404, detail = e)
邮递员上:

删除实用程序的端点
到达我们最后的终点。像其他人一样,从 app.py 文件开始:
@app.delete(“/receiptsapi/v1/utility/{id}”)
async def DeleteUtility(id: str):
utilities = await storage.remove_utility(id)
return utilities
移动到 storage.py 文件的最后一部分:
async def remove_utility(utilityId):
try:
db.Utility.delete_one({“_id”: ObjectId(utilityId)})
client.close()
return {“response”: “User inserted: %s” % (utilityId)}
except Exception as e:
client.close()
raise HTTPException(status_code = 404, detail = e)
现在是最后的邮递员测试:

结论
首先,我们学习了如何在 MongoDB 中删除文档。我们决定不讨论更新部分,因为 FastAPI 版本在嵌入/嵌套文档方面会稍微复杂一些。相反,我们把它留到下次。
在学习了 MongoDB 部分之后,我们继续设置 FastAPI 部分。首先,我们致力于插入。我们学会了像使用常规 FastAPI 应用程序一样使用 pydantic,但是不需要在 insert 函数中设置对象。相反,对象只是被传递。正如我在另一篇博客中所讨论的,HTTPException 用于捕捉任何错误。我还删除了娱乐集合,并重新添加它以不包含任何验证需求,因为 pydantic 不使用 doubles,而是使用 floats。
插入之后,我们继续从数据库中删除。为此,我们必须获得一个 Id 值,还要将其转换为 ObjectId 格式。我们还讨论了如何正确地通过端点传递 id。
我们现在离启动和运行月度预算项目又近了一步。但是现在,我们仍然需要讨论如何更新现有的文档。下次见,干杯!
| 报名加入我的邮件列表 这里 。
| 参考文献
https://stackoverflow.com/questions/45285852/how-to-retrieve-mongodb-collection-validator-rules https://docs.mongodb.com/manual/reference/method/db.collection.remove/ https://www.w3schools.com/python/python_mongodb_insert.asp https://stackoverflow.com/questions/32949898/deleting-document-in-pymongo-from-id
MongoDB
原文:https://towardsdatascience.com/mongodb-80ae0fcfbcdc?source=collection_archive---------40-----------------------
从安装到实现第 3 部分

迈克尔·沃尔特在 Unsplash 上的照片
概述
在第一部分中,我开始构建一个用于月度预算应用程序的数据库。我们讨论了 MongoDB 的安装,以及一些语法差异。安装后,我们创建了一个数据库,这是我们的第一个集合,还创建了一个文档放在这个杂货集合中。
在第二部分中,我们讨论了更多关于集合的选项,比如大小限制和验证。了解了上限之后,我们创建了一个样本集合来进行测试,以及测试数据。我们还讨论了如何一次插入多个文档。在此之后,剩余的集合被添加:娱乐,公用事业,租金,保险和紧急情况。我们还向每个集合添加了文档,然后验证它们都在我们的收据数据库中。
继续
现在我们已经完成了 Mongo 数据库和集合,我们可以继续我们的 API 了。为此,我们将使用 FastAPI。区别在于我们如何介绍我们的产品系列。对于我使用 MySQL 的典型表,我通常使用 sqlalchemy 创建一个数据库对象。然而,在 sqlalchemy 中,我们使用 ORM(对象关系映射器)工具,这在 MongoDB 中是无法使用的。Mongo 使用 NoSQL,它不会以同样的方式工作。因此,我们将使用名为 PyMongo 的 Python 发行版。因此,设置将与我们之前创建 FastAPIs 的方式不同。
安装 pymongo
为了让我们开始,我们需要安装 API 所需要的东西。已经安装了 FastAPI,但是我们使用新的 PyMongo 发行版来运行我们的代码。要安装,请运行:
sudo pip3 install pymongo
现在,我们准备开始设置我们的端点。
设置杂货端点
首先,我们将创建一个名为 storage.py 的文件来保存要调用的函数。回想一下,我们将需要为 API 使用 async 和 await。要启动这个文件,我们需要导入 pymongo 发行版:
import pymongo
接下来,我们将建立连接。为此,我们需要创建一个基于 Mongo 的客户机,使用包含数据库和 MongoDB 特定端口的主机。接下来,我们将选择数据库:
client = pymongo.MongoClient(“localhost”, 27017)
db = client.Receipts
在此之后,我们可以创建我们的异步函数来获取食品杂货文档。与 sqlalchemy 非常相似,将在列表中返回结果。但是,ObjectID 是 MongoDB 中的默认主键集,它不能迭代。如果我们想把它附加到一个列表中,这是一个大问题。为了解决这个问题,我们可以将值重置为一个字符串,这样我们就只有 GUID 值了。这将需要在 select 中所有项目的 for 循环中完成,在 Mongo 中这将是一个“find”。更正 ObjectID 后,将其附加到我们创建的列表中。接下来,关闭数据库连接。最后,返回列表。
async def get_groceries():
GroceryList = [] for item in db.Grocery.find():
item[“_id”] = str(item[“_id”])
GroceryList.append(item) client.close()
return GroceryList
设置好函数后,我们现在可以设置端点了。首先,创建 app.py 文件。接下来,我们将使用几个导入。请注意,我们可能还没有全部用到,但将来会用到。
from fastapi import FastAPI
from typing import List
from pydantic import BaseModelimport storage
接下来,设置 FastAPI 的基础:
app = FastAPI (
title = “Receipts API”,
description = “View receipts”,
version = “1.0”
)
现在,我们只需要创建端点。为此,设置异步函数来获取杂货收据。然后,设置一个变量,该变量将等待我们在存储文件中设置的结果。最后,返回该变量。
@app.get(“/receiptsapi/v1/grocery”)
async def GetGrocery():
groceries = await storage.get_groceries()
return groceries
为了测试,我们将使用 uvicorn 来运行应用程序。如果你还不知道,uvicorn 是一个 ASGI(异步服务器网关接口)服务器实现,它允许我们创建自己的 Python 框架。如果尚未安装,请使用:
sudo pip3 install uvicorn
安装完成后,我们就可以运行 python 应用程序了。我做的有点不同,像这样:
uvicorn app:app –-reload –-host <HOSTNAME> --port <PORTNUMBER>
请注意,端口号可以是您想要的任何值,但是主机名应该包含您的应用程序,在我的例子中是我的 Ubuntu IP 地址。现在,我们已经准备好进行测试,并且应该对我们创建的每个端点进行测试。

杂货端点输出。
设置紧急端点
在第一部分中已经做好了准备,我们可以直接开始编写端点代码了。它将遵循与杂货店端点相同的格式,只是使用不同的描述性名称。首先,处理 storage.py 文件:
async def get_emergencies():
EmergencyList = [] for item in db.Emergency.find():
item[“_id”] = str(item[“_id”])
EmergencyList.append(item) client.close()
return EmergencyList
接下来,在 app.py 文件上创建端点:
@app.get(“/receiptsapi/v1/emergency”)
async def GetEmergency():
emergencies = await storage.get_emergencies()
return emergencies
现在你要做的就是测试:

紧急端点输出。
设置娱乐端点
现在,我们可以设置下一个端点。为此,我们将遵循相同的步骤。首先,设置 storage.py 文件:
async def get_entertainment():
EntertainmentList = [] for item in db.Entertainment.find():
item[“_id”] = str(item[“_id”])
EntertainmentList.append(item) client.close()
return EntertainmentList
接下来,app.py 文件端点:
@app.get(“/receiptsapi/v1/entertainment”)
async def GetEntertainment():
entertainment = await storage.get_entertainment()
return entertainment
现在测试:

娱乐端点输出。
设置保险端点
我知道这越来越重复,但坚持我,我们就快到了。像前面一样从 storage.py 文件开始:
async def get_insurance():
InsuranceList = [] for item in db.Insurance.find():
item[“_id”] = str(item[“_id”])
InsuranceList.append(item) client.close()
return InsuranceList
接下来,app.py 文件中的端点:
@app.get(“/receiptsapi/v1/insurance”)
async def GetInsurance():
insurance = await storage.get_insurance()
return insurance
返回给邮递员进行测试:

保险端点输出。
设置租金端点
首先,storage.py 文件需要编码:
async def get_rent():
RentList = [] for item in db.Rent.find():
item[“_id”] = str(item[“_id”])
RentList.append(item) client.close()
return RentList
接下来,对 app.py 端点做一点工作:
@app.get(“/receiptsapi/v1/rent”)
async def GetRent():
rent = await storage.get_rent()
return rent
现在添加到测试:

租用端点输出。
设置应用端点
好吧,最后一个。将以下内容添加到 storage.py 文件中:
async def get_utilities():
UtilityList = [] for item in db.Utility.find():
item[“_id”] = str(item[“_id”])
UtilityList.append(item) client.close()
return UtilityList
现在是 app.py 文件中的最后一个变化:
@app.get(“/receiptsapi/v1/utility”)
async def GetUtility():
utilities = await storage.get_utilities()
return utilities
最后,最后一个邮递员测试:

实用程序端点输出。
回想一下,我们还可以看看我们的 Swagger 文档:

Swagger 文档。

通过 Swagger 测试端点。
结论
虽然这可能是重复的,但是我们能够为月度预算应用程序创建一个 API。因为 MongoDB 使用 NoSQL,所以我们无法使用常规的 sqlalchemy。这是因为,对于 sqlalchemy,我们使用 ORM 工具,它是专门为关系数据库设计的。正如我们在第一部分中了解到的,NoSQL 没有使用关系模型。
我们可以通过使用 pymongo 来解决这个问题,py mongo 允许我们读取集合。尽管编码非常相似,但我们创建了不同的端点来读取每个集合。
随着端点的创建,我们能够查看集合中的文档。接下来,我们可以进行插入、更新和删除。在那之前,干杯!
参考文献
T3【https://www.uvicorn.org/】T5
https://pymongo.readthedocs.io/en/stable/
https://docs . MongoDB . com/manual/reference/method/db . create user/
https://real python . com/introduction-to-MongoDB-and-python/# MongoDB
https://github.com/dcrosta/professor/issues/13
MongoDB
原文:https://towardsdatascience.com/mongodb-92370a2641ad?source=collection_archive---------29-----------------------
从安装到实现:第 6 部分

马库斯·温克勒在 Unsplash 上的照片
前情提要
在第五部分中,我们学习了如何使用一些不同的 Mongo 命令更新文档。我们还讨论过,由于嵌入了文档,更新起来会有点困难,因为细节可能会被覆盖。在更新时,我们了解到我们可以更新每个字段,或者我们可以只更新特定的字段。当我们讨论了几种不同的更新方法后,我们决定花一点时间来考虑如何更新我们的 FastAPI 部分。
这次在 MongoDB 上
花了一点时间,我决定如何更新我们的文件。请记住,这只是我的解决方案,它可能不是唯一的解决方案,也不是最好的解决方案。这只是为了记录我在学习过程中决定要做的事情,所以请在评论中分享你的想法。
因为嵌入的文件,我们必须小心。然而,在查看一个前端应用程序时,我决定更新所有段,并简单地发送整个 JSON 对象进行更新。当显示时,用户可以决定是否需要更新该字段,或者是否用相同的数据替换它。发送整个对象不仅可以动态地更新文档,还可以提供嵌入的文档。
现在,为了让这个更新按照我想要的方式工作,就像 INSERT 一样,我们正在使用我们设置的类。因此,如果文档缺少任何字段,比如带有可选优惠券或礼券卡的杂货文档,它们将被添加并设置为 null。不理想,但也没什么关系。我们总是可以在我们的前端代码中加入不显示任何空值的逻辑,但是拥有它们实际上对我们有利。它将允许用户添加那些他们以前没有的字段,没有太大的困难。因此,不再拖延,让我们进入代码。
设置杂货更新端点
回想一下,我们有一个 app.py 文件和一个 storage.py 文件。app 文件包含 FastAPI 调用,而存储文件处理函数和我们的 MongoDB。
为了创建我们的更新端点,我们首先需要创建函数。首先,让我们来看看我们的存储文件。在 Mongo 中更新时,我们使用了 update 函数。第一个参数包含一个类似于 SQL WHERE 子句的语句,在该语句中,我们首先选择要更新的文档。如果留空,它将更新所有。第二个参数有“$set”指令,类似于 SQL SET 语句。但是,因为字段也将包含在 JSON 对象中,所以我们可以简单地发送整个对象,而不需要声明每个字段及其值。这有助于我们抓住整个物体。
也就是说,当我用 Postman 测试时,我不发送 id 主键以确保它不会被意外更新。当我们在代码中发送对象时,我们需要做的就是确保用户不能编辑我们的 ObjectID,这将解决我们的问题。出于测试的目的,我们可以将它从通过 Postman 发送的对象中去掉。
此外,我们允许一个 upsert,这意味着如果由于某种原因 ID 匹配不上,它将插入该文档。一旦我们有了前端,我们的代码就不需要这个了,但是万一用户忘记刷新页面或者有东西被缓存了,我们需要确保数据不会就这么消失了。了解了所有这些,下面是我们将添加到存储文件中的函数:
@app.put(“/receiptsapi/v1/grocery/{id}”)
async def UpdateGrocery(grocery: Grocery, id: str):
groceries = await storage.update_groceries(grocery.dict(), id)
return groceries
既然我们已经设置了代码,我们就可以选择更新哪个文档了。我选择了以下内容:

选定的杂货店记录。
这正是我们将要复制到 Postman 主体中的内容,但是没有 ObjectID。所以我们来做个改变。我们可以改变我们的总金额,也可以改变我们的嵌入式文档列表中的项目数。现在只需发送 PUT 并确保 URL 中的 ID 是正确的。

PUT 请求响应。
我们收到了很好的回应,但为了确保万无一失,让我们回去看看收集。

更新了杂货店记录。
看起来我们的收藏更新没有任何问题。唯一需要注意的问题是添加了“优惠券”和“礼品卡”字段,但这也不是问题,因为现在用户有机会添加这些字段。我们可以去下一个终点了。
设置紧急更新端点
首先,让我们转到 storage.py 文件。我们的函数看起来与 update _ goals 函数几乎相同,只是变量名称不同。您可能还记得,这个集合中没有嵌入的文档,但是我们的调用看起来是一样的。这就是为什么我选择这条路线进行更新;我们正在使用更动态的更新。
async def update_emergencies(emergency, emergencyId):
try:
db.Emergency.update_one({“_id”: ObjectId(emergencyId)}, {“$set”: emergency}, upsert=True)
return {“response”: “User Updated: %s” % emergencyId}
except Exception as e:
client.close()
raise HTTPException(status_code = 404, detail = e)
接下来,我们可以添加到 app.py 文件并设置我们的端点。同样,除了名称变化之外,它看起来与杂货店端点相同,但是我们所有的端点都是相同的,只有很少的变化。
@app.put(“/receiptsapi/v1/emergency/{id}”)
async def UpdateEmergency(emergency: Emergency, id: str):
emergencies = await storage.update_emergencies(emergency.dict(), id)
return emergencies
现在,我们查看当前的紧急情况,以选择哪些将被更新。

选定的紧急记录。
我选择了这份文件。在原因中,我们还可以补充说,较高的价格是由于该日期是一个假期。这样,将来我们可以回忆起为什么定价会超出我们的预算。因此,让我们将 ObjectID 放入 URL,使用 PUT 调用我们的端点,然后查看文档是否被正确更新:

PUT 请求响应。

更新了紧急记录。
设置娱乐更新端点
现在我相信你已经明白了。首先,转到存储文件来设置函数:
async def update_entertainment(entertainment, entertainmentId):
try:
db.Entertainment.update_one({"_id": ObjectId(entertainmentId)}, {"$set": entertainment}, upsert=True)
return {"response": "User Updated: %s" % entertainmentId}
except Exception as e:
client.close()
raise HTTPException(status_code = 404, detail = e)
接下来,转到应用程序文件来设置端点:
@app.put("/receiptsapi/v1/entertainment/{id}")
async def UpdateEntertainment(entertainment: Entertainment, id: str):
entertainment = await
storage.update_entertainment(entertainment.dict(), id)
return entertainment
现在查看集合并选择一个要更新的文档:

精选娱乐唱片。
在 Postman 中,设置 URL,包括 ObjectID,然后在正文中围绕您想要的内容进行更改。例如,让我们使用礼品卡进行交易。礼品卡可以吃掉全部金额,所以我们的总数将是零。运行后,仔细检查更新的文档。

PUT 请求响应。

更新娱乐记录。
设置保险更新端点
首先,在存储文件中设置更新功能:
async def update_insurance(insurance, insuranceId):
try:
db.Insurance.update_one({"_id": ObjectId(insuranceId)}, {"$set": insurance}, upsert=True)
return {"response": "User Updated: %s" % insuranceId}
except Exception as e:
client.close()
raise HTTPException(status_code = 404, detail = e)
现在转到应用程序文件来创建端点:
@app.put("/receiptsapi/v1/insurance/{id}")
async def UpdateInsurance(insurance: Insurance, id: str):
insurance = await storage.update_insurance(insurance.dict(), id)
return insurance
接下来,选择要更新的文档。

选定的保险记录。
对于这次更新,我将使用 Geico,而不是 Progressive,只是为了表明如果犯了一个简单的错误,它可以被纠正。你知道该怎么做。邮递员的时间到了。

PUT 请求响应。

更新了保险记录。
设置租金更新端点
首先,我们将函数添加到存储文件中:
async def update_rent(rent, rentId):
try:
db.Rent.update_one({“_id”: ObjectId(rentId)}, {“$set”: rent}, upsert=True)
return {“response”: “User Updated: %s” % rentId}
except Exception as e:
client.close()
raise HTTPException(status_code = 404, detail = e)
接下来,将更新端点添加到应用程序文件:
@app.put("/receiptsapi/v1/rent/{id}")
async def UpdateRent(rent: Rent, id: str):
rent = await storage.update_rent(rent.dict(), id)
return rent
最后,选择您想要更新的文档,将更改放入 Postman,并监视发生了什么。

选定的租金记录。

PUT 请求响应。

更新的租金记录。
设置实用程序更新端点
在我们的测试中,让我们改变一下。但是首先,我们必须添加我们的代码。最后一个。首先,将函数添加到存储文件中:
async def update_utility(utility, utilityId):
try:
db.Utility.update_one({"_id": ObjectId(utilityId)}, {"$set": utility}, upsert=True)
return {"response": "User Updated: %s" % utilityId}
except Exception as e:
client.close()
raise HTTPException(status_code = 404, detail = e)
接下来,我们将端点添加到应用程序文件中:
@app.put("/receiptsapi/v1/utility/{id}")
async def UpdateUtility(utility: Utility, id: str):
utilities = await storage.update_utility(utility.dict(), id)
return utilities
之后,选择要更新的记录:

选定的公用事业记录。
所以在 Postman 中,我们将只发送一个字符的 ID。例如,结尾的“d”将改为“f”。因为“upsert ”,它应该会添加一个新的记录,但是让我们测试一下。对于我们的更新,我们应该将总数、创建日期更改为最近的日期,将事务日期更改为下个月。记住,它应该更新整个记录,但是我们的 ID 只差一个字符。
现在,当您检查时,响应显示“用户已更新”,但我们希望如此,因为这是我们在成功时发送的消息。但是,请再次检查集合:

更新了效用记录。
如您所见,“upsert”允许将记录添加到数据库中。如果您不想要该特性,只需将“upsert”设置为 False,当在集合中找不到该 ID 时,很可能会返回一个错误。但就我的目的而言,我希望保留该功能。
结论
在 MongoDB/FastApi 项目的这一部分,我们为每个集合创建了更新端点。我们讨论了“upsert”选项的作用,以及我为什么选择在这个项目中使用它。
至于 Mongo 和 FastApi 编码,那是我们收据跟踪项目的最后一部分。剩下唯一要做的就是给我们的项目一个前端 app。我希望通过使用 pymongo 库,您对 MongoDB 有了更多的了解,甚至对 FastApi 也有了一些了解。我确实从这次经历中吸取了教训。为了了解更多,我正在考虑扩展到 React 的前端,因为我的经验仍然有限。它应该继续是一个有趣的项目。下次见,干杯!
用我的 每周简讯 免费阅读我的所有文章,谢谢!
想阅读介质上的所有文章?成为中等 成员 今天!
查看我最近的文章:
https://python.plainenglish.io/organizing-my-pictures-with-mysql-and-python-ca5dee8fe02f https://medium.com/codex/all-along-ive-been-using-a-sql-antipattern-50f9a6232f89 https://python.plainenglish.io/creating-a-basic-photoshop-app-with-python-c24181a09f69 https://python.plainenglish.io/the-lazy-way-of-using-python-to-search-wikipedia-fce2a7260390 https://python.plainenglish.io/building-an-old-school-text-based-game-with-python-efcc33d25a42
参考资料:
https://docs.mongodb.com/manual/reference/method/db.collection.update/ https://kb.objectrocket.com/mongo-db/how-to-update-a-mongodb-document-in-python-356
云中的 MongoDB:2021 年的三种解决方案
原文:https://towardsdatascience.com/mongodb-in-the-cloud-three-solutions-for-2021-ee87c4ef4ef?source=collection_archive---------29-----------------------
MongoDB Atlas、AWS DocumentDB、Azure Cosmos DB 的定价和兼容性概述。
作者:爱德华·克鲁格和道格拉斯·富兰克林。

照片由约尔·温克勒在 Unsplash 上拍摄
本文将调查 MongoDB 的 MongoDB Atlas 、AWS Amazon DocumentDB 和 Azure Cosmos DB 提供的成本和功能。我们假设我们不是在谈论超大规模的企业解决方案。我们正在寻找一个更典型的应用程序。
与此相关的一个原因是,MongoDB 公司决定在今年晚些时候终止 mLab 服务。这是为了将用户转移到该公司较新的平台 MongoDB Atlas。然而,今天有许多 MongoDB 的云提供商。我们将讨论 MongoDB Atlas、Azure Cosmos DB 和 AWS DocumentDB。
什么是 MongoDB?
MongoDB 是一个面向文档的数据库,它将数据存储为 BSON 对象或文档组成的*集合。*这些文档可以作为 JSON 的文档进行检索。这意味着您可以不使用 RDBMS 中的列和行结构来存储记录。
MongoDB 云数据库是内容管理、产品目录、地理空间数据应用程序以及任何具有快速变化的数据需求的项目的绝佳解决方案。
云提供商
有许多可用的 MongoDB 云解决方案。开发团队或公司最终决定采用什么解决方案,很可能取决于特性、价格以及与您现有架构的集成。
这里讨论的所有选项都是托管云服务。这意味着现代数据库中所需的所有数据库管理和安全任务都将由服务提供商来处理。忽略这些解决方案的共同点,让我们分解一些差异。
MongoDB 地图集
MongoDB 成立于 2007 年,面临着获得更多用户和赚更多钱的压力。该公司在 2016 年建立了 Atlas,在 2018 年以 6800 万美元收购了 mLab,然后在 2020 年底弃用了 mLab。
MongoDB Atlas 是 MongoDB 公司提供的解决方案。因此,MongoDB Atlas 是最完整的 MongoDB 解决方案。使用 MongoDB Atlas,您将可以访问所有的 MongoDB 特性和方法,包括 map-reduce 。此外,您将能够运行4.4 版、4.0 版或 3.6 版 API。
亚多拉斯数据库的另一个好处是,你可以灵活地使用其他云服务提供商(AWS、Azure 和 GCP)。我们可以很容易地将我们的应用程序转换成运行在 GCP MongoDB Atlas 上的应用程序。
**MongoDB Atlas 确实有一个免费层。**然而,如果您需要更强大的计算能力,10GB 存储、2GB ram 和 1 个 vCPU 的价格约为每月 57 美元。
有关设置 MongoDB Atlas 或迁移到 MongoDB Atlas 的更多信息,请查看本文。
天蓝色宇宙数据库
Azure Cosmos DB 是微软针对文档数据库的 Azure 解决方案。Azure Cosmos DB 使用*ru 或资源单位,*使得定价比其他云平台稍微不透明。但是,您可以使用 RU 计算器来转换成美元并估计成本。
这里有一个 Azure DB 月度成本计算器。
Azure 确实有一个免费层,授予帐户中的前 400 RU/s 和 5 GB 存储空间。
Azure Cosmos DB free tier 让您可以轻松开始、开发和测试您的应用程序,甚至免费运行小型生产工作负载。免费层拥有普通 Azure Cosmos DB 帐户的所有好处和功能。 Cosmos DB 使用 MongoDB v3.6 API,不支持 map-reduce。
在你的免费积分用完之后, Cosmos DB 费用比竞争对手低。对于使用最少的 1.5 GB ram 的 10GB 数据,每月大约需要 25 美元。
此外,Azure 现在提供了一个无服务器选项。这对于使用不一致或使用率低的应用程序非常有用,如内部业务应用程序或概念证明。在无服务器模式下使用 Azure Cosmos DB 没有最低费用。Azure Cosmos DB server less只对数据库操作消耗的 ru 和数据消耗的存储进行计费。这可以大幅降低不常访问或不定期访问的应用程序的成本。
也就是说,对于常规的数据加载,无服务器不是一个好的解决方案。无服务器确实提供了自动扩展;然而,成本可能很高。
关于设置 Azure Cosmos DB 或迁移到 Cosmos DB 的更多信息,请查看本文。
AWS 文档 b
亚马逊网络服务解决方案是亚马逊文档数据库。 **AWS DocumentDB 没有空闲层。**目前最便宜的实例是 db t3.medium ,它有 10GB 的存储空间,配有 4GB RAM 和 2 个 vCPUs,价格大约是每月 60 美元。请注意,这大约是 Azure 和 Atlas 提供的 RAM 的两倍。
有了 AWS DocumentDB,您可以选择使用 v3.6 或 v4.o API 。该平台缺少 Atlas 中可用的地图缩小功能。供应商锁定是 AWS 需要记住的另一件事。如果您的公司已经在其他云提供商上使用 AWS 服务,这个解决方案可以很好地协同工作。
供应商锁定并非不可避免,但这是额外的工作和费用。例如,要在 Heroku 应用程序中使用 AWS DocumentDB,您需要获得 Heroku enterprise,这很贵。
Amazon DocumentDB 是一项仅限 VPC 的服务,不支持公共端点。因此,您不能从 AWS 之外的环境直接连接到 Amazon DocumentDB 集群。但是,您可以使用 SSH 隧道从 AWS 外部的机器进行连接。
结论
团队需要考虑功能、定价和现有架构,以选择最佳的云 MongoDB 解决方案。有些决定会很容易;比如需要 map-reduce,选择 MongoDB Atlas。如果你打算做一个低使用率的应用,使用 Azure Cosmos Serverless 来节省成本。
对于可预测的负载,Azure serverless 可能很粗糙。人们经常选择无服务器选项,因为它们非常容易扩展。这个问题是大规模无服务器扩展的成本会高得出乎意料。但是,如果每一次扩展都意味着额外的收入,那么可变成本就对应于可变收入。例如,考虑一个黑色星期五的网上商店。无法通过自动扩展来容纳额外的流量对于收入损失来说是一场灾难。
如果你的应用是托管在 Heroku(不是企业)上,选择 MongoDB Atlas 或者 Azure Cosmos。使用 MongoDB Atlas,您可以选择任何您喜欢的云提供商,并使用最新的 MongoDB API 功能。这种灵活性是 Amazon Document DB 或 Azure Cosmos DB 所不具备的。这些选项将您锁定在各自的云供应商,同时运行旧版本的 DB API。
如果您的公司有一些使用 AWS 的现有项目,或者您喜欢该平台,AWS DocumentDB 可能是一个很好的解决方案。请记住与其他云服务的兼容性问题。
AWS Amazon Document DB 和 Azure Cosmons 支持创建应用程序所需的大部分功能。我们注意到唯一缺少的特性是 map-reduce 方法。所以如果你是用 MongoDB 来使用这个特性,Document DB 或者 Cosmos 对你来说可能不是一个好的解决方案;Atlas 将更好地满足您的需求。
用 TensorBoard 监控 BERT 模型训练
原文:https://towardsdatascience.com/monitor-bert-model-training-with-tensorboard-2f4c42b373ea?source=collection_archive---------11-----------------------
梯度流量和更新率

https://unsplash.com/@tobiastu
在的上一篇文章中,我们解释了 BERT 模型的所有构建组件。现在,我们将在 TensorBoard 中训练监控训练过程的模型,查看梯度流、更新参数比率、损失和评估指标。
为什么我们希望监控梯度流量和更新率,而不是简单地查看损失和评估指标?当我们开始对大量数据进行模型训练时,我们可能会在意识到之前运行许多次迭代,查看模型没有训练的损失和评估指标。在这里,查看梯度的大小和更新率,我们可以立即发现有问题,这为我们节省了时间和金钱。
数据准备
我们将使用来自 sklearn 的【T20 个新闻组数据集(许可证:公共域/来源:http://qwone.com/~jason/20Newsgroups/)在这个例子中有 4 个类别: alt .无神论、 talk.religion.misc 、 comp.graphics 和 sci.space. 我们用来自变形金刚的bertokenizer对数据进行标记化
categories = ['alt.atheism', 'talk.religion.misc', 'comp.graphics', 'sci.space']newsgroups_train = fetch_20newsgroups(subset='train', categories=categories)
newsgroups_test = fetch_20newsgroups(subset='test', categories=categories)X_train = pd.DataFrame(newsgroups_train['data'])
y_train = pd.Series(newsgroups_train['target'])X_test = pd.DataFrame(newsgroups_test['data'])
y_test = pd.Series(newsgroups_test['target'])BATCH_SIZE = 16max_length = 256
config = BertConfig.from_pretrained("bert-base-uncased")
config.num_labels = len(y_train.unique())
config.max_position_embeddings = max_lengthtrain_encodings = tokenizer(X_train[0].tolist(), truncation=True, padding=True, max_length=max_length)
test_encodings = tokenizer(X_test[0].tolist(), truncation=True, padding=True, max_length=max_length)class BertDataset(Dataset):def __init__(self, encodings, labels):
self.encodings = encodings
self.labels = labelsdef __getitem__(self, idx):
item = {key: torch.tensor(val[idx]).to(device) for key, val in
self.encodings.items()}
item[‘labels’] = torch.tensor(self.labels[idx]).to(device)
return itemdef __len__(self):
return len(self.labels)train_dataset = BertDataset(train_encodings, y_train)
test_dataset = BertDataset(test_encodings, y_test)train_dataset_loader = DataLoader(train_dataset, batch_size=BATCH_SIZE, shuffle=True)
test_dataset_loader = DataLoader(test_dataset, batch_size=BATCH_SIZE)for d in train_dataset_loader:
print(d)
break# output :
{'input_ids': tensor([[ 101, 2013, 1024, ..., 0, 0, 0],
[ 101, 2013, 1024, ..., 1064, 1028, 102],
[ 101, 2013, 1024, ..., 0, 0, 0],
...,
[ 101, 2013, 1024, ..., 2620, 1011, 102],
[ 101, 2013, 1024, ..., 1012, 4012, 102],
[ 101, 2013, 1024, ..., 3849, 2053, 102]], device='cuda:0'),
'token_type_ids': tensor([[0, 0, 0, ..., 0, 0, 0],
[0, 0, 0, ..., 0, 0, 0],
[0, 0, 0, ..., 0, 0, 0],
...,
[0, 0, 0, ..., 0, 0, 0],
[0, 0, 0, ..., 0, 0, 0],
[0, 0, 0, ..., 0, 0, 0]], device='cuda:0'),
'attention_mask': tensor([[1, 1, 1, ..., 0, 0, 0],
[1, 1, 1, ..., 1, 1, 1],
[1, 1, 1, ..., 0, 0, 0],
...,
[1, 1, 1, ..., 1, 1, 1],
[1, 1, 1, ..., 1, 1, 1],
[1, 1, 1, ..., 1, 1, 1]], device='cuda:0'),
'labels': tensor([3, 0, 2, 1, 0, 2, 2, 1, 1, 0, 1, 3, 3, 0, 2, 1], device='cuda:0')}
张量板的使用
TensorBoard 允许我们编写和保存不同类型的数据,包括图像和标量,以供将来分析。首先让我们用 pip 安装 tensorboard:
pip 安装张量板
要写入 TensorBoard,我们将使用来自torch . utils . tensor board的 SummaryWriter
from torch.utils.tensorboard import SummaryWriter# SummaryWriter takes log directory as argument
writer = SummaryWriter(‘tensorboard/runs/bert_experiment_1’)
为了编写标量,我们使用:
writer.add_scalar(‘loss/train’, loss, counter_train)
需要变量 counter_train 来知道向 TensorBoard 写入内容的步数。
要写一个图像,我们将使用以下内容:
writer.add_figure(“gradients”, myfig, global_step=counter_train, close=True, walltime=None)
模特培训
现在让我们看看我们的训练函数
out_every 变量控制写入 TensorBoard 的频率,以步数衡量。我们还可以使用 step_eval 变量,比在每个时期之后更频繁地进行评估。梯度流量和更新比率数字分别从 plot_grad_flow 和 plot_ratios 函数返回。
我们传递模型的参数,为了更好的可视化,我们可以决定用 skip_prob 参数跳过一些层。我们写下每层梯度的平均值、最大值和标准偏差,忽略偏差层,因为它们不太有趣。如果你也想显示 bias 的渐变,你可以删除第 17 行的部分。我们还显示了每层中零梯度的百分比。需要强调的是,由于 BERT 使用 GeLU 而不是 ReLU 激活函数,这最后一个图可能不太有用,但如果您使用的是 ReLU 的不同模型,该模型存在死亡神经元问题,显示零梯度的百分比实际上是有帮助的。
plot_ratios 函数显示每个参数的更新/参数比率,这是一个标准化测量,因为更新除以参数值,并有助于了解您的神经网络如何学习。作为一个粗略的启发,这个值应该在 1e-3 左右,如果低于这个值,学习率可能太低,否则太高。此外,您可以通过 skip_prob 参数减少显示的图层。
在模型训练期间,您可以使用以下命令启动 TensorBoard:
tensorboard --samples_per_plugin images=100 --logdir bert_experiment_1
否则,您可以创建一个*。bat* 文件(在 Windows 上)以便更快地启动。创建一个新文件,例如run _ tensor boardwith*。bat* 扩展并复制粘贴以下命令,相应修改 path_to_anaconda_env,path_to_saved_results 和 env_name 。然后点击文件启动 TensorBoard。
cmd /k “cd path_to_anaconda_env\Scripts & activate env_name & cd path_to_saved_results\tensorboard\runs & tensorboard — samples_per_plugin images=100 — logdir bert_experiment_1”
定义samples _ per _ plugin images = 100我们显示这个任务中的所有图像,否则默认情况下 TensorBoard 只会显示其中的一部分。
结果
监控这些情节,我们可以很快发现事情是否如预期发展。例如,如果许多图层的渐变为零,您可能会遇到渐变消失的问题。同样,如果比率非常低或非常高,您可能希望立即深入了解,而不是等到训练结束或训练的几个时期。
例如,通过下面的配置查看 240 步的比率和梯度,我们可以看到情况看起来很好,我们的训练进行得很好——我们可以期待最终的好结果。
EPOCHS = 5
optimizer = AdamW(model.parameters(), lr=3e-5, correct_bias=False)
total_steps = len(train_dataset_loader) * EPOCHS
scheduler = get_linear_schedule_with_warmup(
optimizer,
num_warmup_steps= 0.7 * total_steps,
num_training_steps=total_steps
)

比率更新,作者图片

渐变流,按作者排列的图像
事实上,最终结果是:

训练/测试准确性,图片由作者提供
而如果我们将计划设置更改为:
scheduler = get_linear_schedule_with_warmup(
optimizer,
num_warmup_steps= 0.1 * total_steps,
num_training_steps=total_steps
)
我们从图中注意到,在第 180 步,模型没有学习,我们可以停止训练以进一步研究。在这种情况下,将 num_warmup_steps 设置为 0.1 * total_steps 使得学习率降低,并且在训练开始后不久变得非常小,并且我们最终得到消失的梯度,该梯度不会传播回网络的第一层,从而有效地停止了学习过程。

比率更新,作者图片

渐变流,按作者排列的图像

训练/测试准确性,图片由作者提供
你可以在的这个 GitHub repo 中找到完整的代码,自己尝试一下。
结论
在阅读完这篇文章和之前的文章之后,你应该拥有所有的工具,并且理解如何在你的项目中训练 Bert 模型!我在这里描述的是一些你想在培训期间监控的东西,但 TensorBoard 提供了其他功能,如嵌入投影仪,你可以使用它来探索你的嵌入层,以及更多你可以在这里找到的功能。
参考
https://cs231n.github.io/neural-networks-3/
使用 Amazon SageMaker 功能商店和 AWS Glue DataBrew 监控 ML 功能
原文:https://towardsdatascience.com/monitor-ml-features-using-amazon-sagemaker-feature-store-and-aws-glue-databrew-c530abcc479a?source=collection_archive---------21-----------------------
阿诺·劳尔、伊万·卡塔纳和马克·罗伊联合发文
功能推动机器学习(ML)预测,这些预测对业务越来越重要,并嵌入到运营流程中。在从事不同项目的数据科学团队之间共享可重用特性时,特性质量和准确性至关重要。由于现实世界的变化或测量方法的变化,生产要素数据分布可能会随着时间的推移而偏离基线要素数据集。特征漂移表示给定特征的值的分布不再与过去存在的分布一致。当特征漂移时,在这些特征上训练的所有模型的精度都降低。例如,鉴于零售店客流量和餐馆访问量的变化,基于疫情之前数据的需求预测模型表现不佳。通过使用数据分析和收集相关统计数据,您可以监控特征漂移,从而保持对模型质量的控制,并采取调整特征变换等措施来解决数据质量问题,甚至引入新特征来保持或提高模型的准确性。

照片由 Julentto 摄影在 Unsplash 上拍摄
如今,公司正在建立特征库,以便在训练和实时推理期间为 ML 特征提供中央存储库,从而保持特征的一致性。数据科学团队可以将功能共享到中央存储库中,促进重用并消除返工。特性存储还提供了跨团队的一组统一的特性定义,使人们更容易一起工作。然而,利益相关者对驱动最大似然预测的特征的状态几乎没有可见性。在最好的情况下,他们构建并维护自己的基础设施和工具来监控特性质量,以尝试展现这些洞察力。如果没有适当的功能监控,跨团队和业务单元的功能可重用性的愿景将遥不可及。团队不信任已发布的特性,因为用户不能欣赏特性的质量。因此,在构建定制监控的工具上实现了大量的投资,这花费了核心 ML 工作的时间和精力。
探索现有要素以供重用的数据科学家需要轻松访问要素数据剖面和统计数据。特征漂移检测识别模型再训练和特征处理改进的机会。
在这篇文章中,我们概述了如何使用亚马逊 SageMaker 功能商店和 AWS Glue DataBrew 实现功能监控解决方案。如果你是功能商店的新手,你可能想在开始这篇文章之前回顾一下了解亚马逊 SageMaker 功能商店的关键功能以获得更多的背景知识。
架构概述
以下架构使用 DataBrew 进行特性分析,使用 Amazon 简单存储服务 (S3)将一段时间内的特性分析数据存储为 JSON 文件,以便使用笔记本电脑或商业智能(BI)仪表板进行分析和可视化。

使用 AWS Glue DataBrew 进行特征监控(图片由作者提供)
Feature Store 是专门为 ML 功能管理构建的解决方案,可帮助数据科学团队跨团队和模型重用 ML 功能,以低延迟为大规模模型预测提供功能,并更快速有效地培训和部署新模型。
DataBrew 是一个可视化的数据准备工具,可以帮助您清理和规范化数据,而无需编写代码。DataBrew 还允许您指定在运行分析作业时为数据集自动生成哪些数据质量统计数据。这允许您自定义数据配置文件统计数据,例如根据数据集的性质和大小确定异常值和相关性,并创建仅包含满足您需求的统计数据的自定义数据配置文件概述。
将要素存储和数据构建结合使用时,您可以监控现有要素存储上的 ML 要素,查看要素数据随时间的分布,以及所有 ML 要素的要素数据质量,从而获得更高质量的 ML 模型。
在本文中,我们提供了两种配置 DataBrew 的方法:第一种是直接使用控制台,第二种是以编程方式。在较高层次上,您可以创建一个 DataBrew 数据集,该数据集连接到在 AWS 粘合数据目录中定义的要素组表,然后配置 DataBrew 配置文件作业以生成要素统计数据。然后,您可以使用 DataBrew 控制台仪表板来可视化特性统计数据、它们的分布以及其他自动生成的信息,以便更好地了解特性。
先决条件
在开始本教程之前,请确保您的 AWS 身份和访问管理 (IAM) SageMaker 执行角色包含创建解决方案所需资源的权限。这包括访问亚马逊 S3、亚马逊雅典娜、 AWS Glue 和 DataBrew 操作。
在这篇文章中,我们使用了来自使用 Faker Python 库合成生成的客户列表的合成杂货订单。你可以按照这个模块自己试试。对于每个客户,笔记本电脑会生成 1-10 个订单,每个订单中都会有购买的产品。目标是说明如何使用功能存储进行功能管理,以及如何使用 DataBrew 来监控这些功能。下面是使用亚马逊 SageMaker Studio 控制台定义功能组的截图。

特征组定义(图片由作者提供)
功能存储在您的帐户中使用 S3 存储桶来存储离线数据。您可以针对亚马逊 S3 的离线数据存储使用类似 Athena 的查询引擎来分析要素数据或在单个查询中连接多个要素组。Feature Store 会在创建功能组的过程中自动为功能组构建一个数据目录,然后您可以使用 Athena 甚至像 Presto 这样的开源工具使用这个目录来访问和查询离线存储中的数据。
使用 DataBrew 控制台生成统计数据
在本节中,我们将详细介绍如何使用 DataBrew 控制台直接监控离线功能组。我们首先需要使用CREATE TABLE AS SELECT(CTAS)从离线特性存储中将特性组表的快照创建到 Athena 中来自查询结果的新表中。关于 Athena 查询的更多示例,请参见带有 Athena 和 AWS Glue 的查询特征库。我们创建的快照只包含最新的特性组记录,没有任何已删除的记录,并将其作为 DataBrew 提供的分析功能的输入。这个快照很重要,因为离线存储包含整个功能历史。特征轮廓必须集中在给定时间点的特征上。同样,特征漂移检测只有在比较一系列特征轮廓时才有意义。
要为离线功能存储的快照创建 DataBrew 数据集,请完成以下步骤:
- 在 DataBrew 控制台的导航窗格中,选择数据集。
- 选择连接新数据集。
- 选择存储离线功能商店快照的 AWS Glue 表(对于本文,fscw-orders-07–13–17–38–1626198069-ctas-temp)。
- 选择创建数据集。
重复这些步骤,为离线要素存储中的其他要素组创建数据集。

数据集创建(图片由作者提供)
- 在 DataBrew 控制台上,选择数据集。
- 选择您之前创建的数据集之一(fscw-orders-07–13–17–38–1626198069-ctas-temp)。
- 选择运行数据配置文件。
- 选择创建轮廓作业。
- 在配置文件作业的配置中,选择要存储配置文件作业输出的亚马逊 S3 位置。
- 在数据配置细节中,选择您想要生成的所有数据质量统计。
- 在权限下,对于角色名称,选择您的 IAM 角色具有访问数据的 DataBrew 权限。
- 选择创建并运行任务。
数据集页面有以下选项卡帮助您浏览数据:
- 数据集预览 —包含关于数据集源的信息
- 数据概要概述 —包含数据集级别的概要统计
- 列统计 —包含列级别的详细统计
- 数据沿袭 —包含带有数据流的可视化图表
让我们更详细地看一下每个选项卡。
数据集预览选项卡显示数据集的信息、位置和创建日期,以及数据集整体结构的概述。

数据集预览(图片由作者提供)
Data profile overview 选项卡包含数据集的统计和体积的图形数据配置文件。它包括总行数、缺失像元的百分比、重复行以及显示要素之间关系的关联矩阵。

数据集简介概述(图片由作者提供)

数据集概要概述-列摘要(作者提供的图片)
在列统计数据选项卡上,您可以找到关于数据集中每一列的详细统计数据,例如值分布、标准偏差、前几个不同的值以及其他数据洞察。

列统计(图片由作者提供)
所有质量结果的列表也可以作为 JSON 报告文件在您的 S3 存储区中获得。从 DataBrew profile job details 页面,我们还可以下载生成的报告,格式为 JSON、PNG 或 PDF 文件。
以编程方式监控功能
您可以使用 AWS APIs 自动执行上一节中描述的创建 DataBrew 概要分析数据和统计信息的所有步骤。在这一节中,我们将介绍可以在 Studio 中运行的 Jupyter 笔记本中实现的功能。
第一个函数*feature_monitoring_prep*准备 DataBrew 用来运行分析作业的数据集。该函数执行以下步骤:
- 使用 CTAS 根据查询结果将离线要素存储中的要素组表快照创建到 Athena 的新表中。快照表仅包含最新的要素组记录,不包含已删除的记录。
- 创建并运行一个 AWS Glue crawler 来抓取快照表。需要首先对该表进行爬网,以便在创建 DataBrew 数据集时该表可用作数据源。
- 通过将 DataBrew 数据集连接到前面步骤中创建的 AWS Glue 表来创建它。
- 配置 DataBrew 配置文件作业。
下面是一个示例代码片段,展示了我们如何使用一些输入参数(如要素组名称、S3 桶和存储结果的键)来调用此函数:
*response_brew_prep = feature_monitoring_utils.feature_monitoring_prep( orders_feature_group_name, results_bucket, results_key )*
现在,功能监控的准备步骤已经完成,我们可以调用下一个函数*feature_monitoring_run*,运行 DataBrew profile 作业,评估数据集并创建统计数据。您可以在不同的时间间隔连续运行该功能多次,以生成单独的相应报告。在这种情况下,它会自动重新创建用作 DataBrew 数据集输入的快照表。
*response_brew_job = feature_monitoring_utils.feature_monitoring_run( orders_feature_group_name )*
该函数仅将特性组名称作为输入参数,并返回由 DataBrew 配置文件作业生成的 JSON 报告的 S3 位置,以及包含报告可视化的 DataBrew 控制台的 URL。您可以选择笔记本中显示的链接,在新的浏览器选项卡中打开 DataBrew 控制台,以查看质量统计可视化。
作为该功能的一部分,DataBrew 控制台的 URL 和亚马逊 S3 中 DataBrew JSON 报告文件的位置都作为标签添加到功能组中。这允许您访问特征组以发现和检索质量统计报告的位置。同样,您可以随时将配置文件 URL 标记值直接复制并粘贴到您的 web 浏览器中,以在 DataBrew 控制台上显示报告。

要素组标签(图片由作者提供)
本笔记本根据 DataBrew 质量统计生成的报告,提供了一些功能和趋势的可视化示例。
附加功能consolidate_monitor_reports扫描包含 DataBrew 质量统计 JSON 报告文件的 S3 文件夹位置,并将这些文件合并成一个单独的 pandas DataFrame。该数据帧还被导出为平面 CSV 文件,以便由其他可视化或 BI 工具(如 Amazon QuickSight )进行进一步分析。将所有报告合并到一个地方的目的是帮助检测特征的质量统计的变化。
consolidate_monitor_reports函数将功能组名称、存储合并结果的 S3 存储桶和文件夹以及用于选择最新报告子集的开始日期作为输入。它返回整合的 DataFrame 对象和生成的 CSV 文件的 S3 位置。
*response_consolid = feature_monitoring_utils.consolidate_monitor_reports( orders_feature_group_name, results_bucket, consolidated_results_key, start_date )*
以下屏幕截图显示了整合数据框架的示例视图。这是对purchase_amount特性名称的提取,包含了从 DataBrew JSON 报告中收集的所有质量统计数据。我们可以观察到熵或标准差的值随着时间而变化。

特征统计数据框架(图片由作者提供)
可视化随时间推移的要素漂移
在笔记本中,我们还提供了一个示例可视化功能plot_profiling_data,以便轻松显示特定功能随时间的变化。该函数将全局数据帧、要监控的特征和特定的统计数据(如熵、平均值或标准偏差)作为输入。您还可以使用自己喜欢的 Python 库创建自己的可视化。
下面是一个简单的曲线图,显示了purchase_amount特性的entropy随时间的变化:
*feature_monitoring_utils.plot_profiling_data( analysis_data, feature_name = 'purchase_amount', ['entropy'] )*

特征的熵随时间漂移(图片由作者提供)
现在我们显示了purchase_amount特性的多个质量统计(entropy和standardDeviation)随时间的演变:
*feature_monitoring_utils.plot_profiling_data( analysis_data, feature_name = 'purchase_amount', ['entropy', 'standardDeviation'], multiple_plots = True )*

要素的多重统计随时间漂移,多重绘图(图片由作者提供)
如果我们想比较同一张图上具有两个不同比例的purchase_amount特征的两个统计值(entropy和standardDeviation)随时间的演变,我们在调用*plot_profiling_data*函数时使用*multiple_plots = False*。

要素的多重统计随时间漂移(图片由作者提供)
通过这种方法,您还可以创建一个管道或工作流解决方案,以完全自动化对现有功能库的功能监控。除了查看功能统计数据和创建显示您的功能如何随时间变化的仪表板之外,您还可以使用其他 AWS 服务来扩展解决方案,以配置警报和阈值,从而关注超出可接受范围的功能。这样,功能组的所有者可以采取措施,如调整功能转换以解决数据质量问题,甚至引入新功能。模型利益相关者可以得到关于大的特征漂移或违反约束的警告,并且可以重新训练他们的模型以获得更精确的预测。
要探索完整的代码示例,并在您自己的帐户中试用,请参见 GitHub repo 。
结论
Feature Store 提供专门构建的功能管理解决方案,帮助组织跨业务部门和数据科学团队扩展 ML 开发。在这篇文章中,我们解释了如何使用 DataBrew 在现有的特性存储上启用特性监控。将功能存储和数据构建结合使用时,可以查看功能统计数据,还可以创建仪表板来显示功能随时间的变化情况。通过提供对数据组成和质量的深入了解,在团队之间推动更多的功能重用,从而帮助建立信任,这将产生更高的模型准确性和更快的上市时间。试一试吧,在评论中告诉我们你的想法。
云本地架构中的监控和可观察性
原文:https://towardsdatascience.com/monitoring-and-observability-in-cloud-native-architectures-54e68e52b103?source=collection_archive---------34-----------------------
为什么监控对于云原生应用程序至关重要?有哪些工具可以完成这项工作?

图片由皮克斯拜的康斯坦丁·科洛索夫拍摄
想象一下被囚禁在一个没有窗户、没有日历和挂钟的牢房里。此外,你没有访客,你唯一的同伴是一个不喜欢闲聊的警卫。我知道这是一个非常黑暗的场景,但是请原谅我。
你有一个目标:找出季节(例如,夏天或秋天)以及外面是白天还是晚上。你怎么能这样做?你可以观察警卫的行为,他穿什么,吃什么食物,他晒黑了吗?如果他戴着太阳镜,那么也许现在是夏天,外面阳光明媚。如果他穿着外套,那么也许我们正处于隆冬。这就是我们所说的黑箱监控。
历史上,我们使用黑盒监控来推断问题的原因。我们有磁盘错误吗?我们是否观察到 CPU 使用率的峰值?但是试图通过监控系统的间接数据来提高应用程序的性能并不是理想的方法。我们可以做得更好!让我们打开一扇窗,要求我们应得的电话。
这个故事介绍了我们现在可以用来监控云原生应用程序性能的工具,以及如何将它们安装在 Kubernetes 集群中。在另一个故事中,我们将看到如何正确地使用它们来构建一个仪表板,它可以揭示我们的宠物项目内部正在发生的事情。
Learning Rate 是一份时事通讯,面向那些对 AI 和 MLOps 世界感到好奇的人。你会在每周五收到我关于最新人工智能新闻和文章的更新和想法。订阅这里!
监控与可观察性
在本文中,我们关心的是工程师方面的情况。因此,当错误发生时,开发人员需要正确的反馈,以便他们知道应该修复什么。他们可以通过正确的监控和观察工具来实现。但是有什么区别呢?
我们监控系统并观察应用
监视
在监控中,我们关注的是系统本身。例如,在云环境中,系统很可能是一个 Kubernetes 集群。然而,监控不会告诉我们应用程序内部发生了什么。它会给出诸如内存可用性低之类的指标,但它不会帮助我们查明问题的原因。
可观察性
在可观察性中,我们可以直接进入我们的应用程序,观察它的各种过程和功能。我们可以看到服务如何执行以及它们如何交互。然后,我们可以揭示几个问题或发现优化,使我们的应用程序性能更好。
根据经验,请记住:我们监控系统并观察应用。你还会听到人们使用术语可观测性来对这两个概念进行分组(即监控和可观测性)。
可观察性工具
专家们常用的堆栈可观测性包括普罗米修斯、格拉夫纳和耶格。
Prometheus 是领先的开源监控解决方案。它是由 SoundCloud 在 2012 年创建的,属于云本地计算基金会(CNCF)。Prometheus 收集系统信息,有一个时间序列数据库和自己的查询语言,称为 PromQL。此外,普罗米修斯有自己的内置警报工具。
优步在 2015 年创造了耶格,它也属于 CNCF。与 Prometheus 不同,Jaeger 收集应用程序信息,并提供分布式跟踪系统。
Grafana 是一个可视化平台,允许您构建直观的仪表板。它支持时序数据库,因此经常与 Prometheus 捆绑在一起。而且,你可以用插件来扩展 Grafana。
装置
要使用这些工具,我们需要将它们安装在 Kubernetes 集群中。因此,首先,要生成一个 Kubernetes 实例,我们可以使用 K3s 。要安装 K3s,请执行以下命令:
curl -sfL https://get.k3s.io | sh -
# Check for Ready node, takes maybe 30 seconds
k3s kubectl get node
接下来,我们将使用头盔安装普罗米修斯和格拉夫纳。所以,让我们安装头盔:
curl https://raw.githubusercontent.com/helm/helm/master/scripts/get-helm-3 | bash
为了在我们的应用程序和可观察性堆栈之间创建一些分离,我们可以创建一个新的名称空间。姑且称之为监控:
kubectl create namespace monitoring
我们现在准备安装普罗米修斯。执行以下命令:
helm repo add prometheus-community [https:*//prometheus-community.github.io/helm-charts*](https://prometheus-community.github.io/helm-charts)helm repo add stable [https:*//charts.helm.sh/stable*](https://charts.helm.sh/stable)helm repo updatehelm install prometheus prometheus-community/kube-prometheus-stack --namespace monitoring --kubeconfig /etc/rancher/k3s/k3s.yaml
要验证您的安装,运行kubectl get pods -n monitoring。您应该会看到 6 个豆荚即将准备就绪。您现在可以运行端口转发命令来查看 Grafana 仪表板:
kubectl port-forward prometheus-grafana-5855d975d8-495bn --address 0.0.0.0 3000:3000 -n monitoring
导航到127.0.0.1:3000;用户名为admin,默认密码为prom-operator。

欢迎来到 Grafana——作者图片
最后,让我们在集群中安装 Jaeger。我们将使用官方 Jaeger GitHub 仓库中的文件。为了安装 Jaeger,我们将创建一个新的可观察性名称空间:
kubectl create namespace observability
然后,运行以下命令:
kubectl create -f [https://raw.githubusercontent.com/jaegertracing/jaeger-operator/master/deploy/crds/jaegertracing.io_jaegers_crd.yaml](https://raw.githubusercontent.com/jaegertracing/jaeger-operator/master/deploy/crds/jaegertracing.io_jaegers_crd.yaml)kubectl create -n observability -f [https://raw.githubusercontent.com/jaegertracing/jaeger-operator/master/deploy/service_account.yaml](https://raw.githubusercontent.com/jaegertracing/jaeger-operator/master/deploy/service_account.yaml)kubectl create -n observability -f [https://raw.githubusercontent.com/jaegertracing/jaeger-operator/master/deploy/role.yaml](https://raw.githubusercontent.com/jaegertracing/jaeger-operator/master/deploy/role.yaml)kubectl create -n observability -f [https://raw.githubusercontent.com/jaegertracing/jaeger-operator/master/deploy/role_binding.yaml](https://raw.githubusercontent.com/jaegertracing/jaeger-operator/master/deploy/role_binding.yaml)kubectl create -n observability -f [https://raw.githubusercontent.com/jaegertracing/jaeger-operator/master/deploy/operator.yaml](https://raw.githubusercontent.com/jaegertracing/jaeger-operator/master/deploy/operator.yaml)
就是这样!现在您已经有了一个安装了行业标准可观察性工具的 Kubernetes 集群。
序
在这个故事中,我们看到了什么是黑盒监控,以及它与白盒监控的对比。我们看到了什么是可观察性和监控,它们的区别,以及它们带来了什么。
最后,我们看到了如何创建一个 Kubernetes 集群并安装 Prometheus、Jaeger 和 Grafana,它们是用于监视和观察的行业标准工具。
在后面的故事中,我们将看到如何正确地使用它们来构建一个仪表板,它可以揭示我们的宠物项目内部正在发生的事情。
关于作者
我的名字是迪米特里斯·波罗普洛斯,我是一名为阿里克托工作的机器学习工程师。我曾为欧洲委员会、欧盟统计局、国际货币基金组织、欧洲央行、经合组织和宜家等主要客户设计和实施过人工智能和软件解决方案。
如果你有兴趣阅读更多关于机器学习、深度学习、数据科学和数据操作的帖子,请关注我的 Medium 、 LinkedIn 或 Twitter 上的 @james2pl 。
所表达的观点仅代表我个人,并不代表我的雇主的观点或意见。
监控和重新训练你的机器学习模型
原文:https://towardsdatascience.com/monitoring-and-retraining-your-machine-learning-models-f385b596b285?source=collection_archive---------16-----------------------
实践教程
有了谷歌数据工作室、lakeFS 和远大前程

由内森·杜姆劳在 Unsplash 上拍摄的照片
像生活中的一切一样,机器学习模型会变得陈旧。在一个不断变化、非稳定数据的世界中,每个人都需要偶尔回到学校并循环利用自己,你的模型也不例外。
嗯,我们知道重新训练我们的模型是重要的,但是我们应该什么时候做呢?如果我们做得太频繁,我们最终会浪费宝贵的时间和精力,但是很少做肯定会影响我们预测的质量。不幸的是,没有放之四海而皆准的答案。应该仔细评估每个案例,以确定陈旧性的影响。
在这篇文章中,我想分享我在一个个人项目中监控和再培训的方法:一个假新闻检测器 web 应用程序。我绝不是这方面的专家,所以如果你有任何建议或考虑,请随时联系。
应用程序
我们简单的 web 应用程序基本上是一个假新闻检测器:用户能够输入一篇新闻文章的 URL,系统将输出它的预测结果:它是假的还是真的。

作者 Gif
对于每个输入,系统将预测结果和附加元数据记录在位于 GCP 的 BigQuery 表中。这些数据将用于监控我们模型的性能,并在需要时对其进行重新训练。
本文分为两部分:监控和再培训。
首先,我将讲述我如何使用 BigQuery 的预测日志来建立一个 Google Data Studio 仪表板,以便有一些更新的图表和指标来评估我的文本分类模型的健康状况。
在再培训部分,我将展示我是如何处理数据版本化来管理每个再培训实验的数据和模型工件,同时保持数据质量。为此,我使用了lakeFS远大前程W&B等工具。本部分讨论的所有内容都可以在项目的资源库 中找到。****
监视
让我们先来看看model_predictions表的模式:
- ****标题(字符串):新的标题。
- ****内容(字符串):新的文本内容。
- ****模型(字符串):生成预测的模型的名称。
- ****预测(字符串):模型的预测—“真”或“假”。
- 置信度(FLOAT): 模型预测的置信度。从 0 到 1。
- ****url(字符串):新的 url。
- ****预测日期(日期时间):进行预测的日期和时间。
- ground_truth (STRING): 以 NULL 开始,在一个标注过程中可以更改为“真”或“假”。
- coverage (FLOAT): 新闻中出现在模型词汇中的单词的百分比。
- ****word_count(整数):新闻的字数。
这些字段都是在我们的应用程序为预测的在线请求提供服务时计算的。如果你有兴趣知道这些指标最初是如何计算的,你可以看看项目的资源库中的app.py。
从这些字段中,我们可以使用 Google Data Studio 设置一个在线仪表板,以持续监控我们预测模型的一些指标:

作者图片
Data Studio 有一个非常直观的界面。在这个例子中,我们只使用了一个数据源,即 BigQuery 中的model prediction表。因此,从一个空白的仪表板,我们可以通过简单地点击顶部的添加数据按钮,然后选择 BigQuery 选项来添加数据源。
我选择不显示与ground_truth相关的图表,因为如果近期没有重新训练模型的意图,这个字段可能经常为空。但是如果不断地做标记,一个混淆矩阵将会是我们仪表板的一个很好的补充。
在这个例子中,由于我没有大量的记录,我们不应该从这些数字中得出任何统计结论。希望在未来,随着更多的每日预测,这些图表将提供更多的信息。尽管如此,让我们逐一讨论这些指标:
记录
这仅仅是应用程序到目前为止提供的预测数量。要创建它,请转到在顶部添加图表并选择记分卡。选择您的表作为数据源,选择记录计数作为指标。
有标签的
这是已手动标记的记录的百分比。这对下一步的再培训很重要。没有地面真相,你不能重新训练你的模型。
像记录一样,这也是一个记分卡。但是对于这一个,您必须创建一个新的计算字段,方法是单击记分卡的数据选项卡底部的添加字段:

作者截图
然后,选择新创建的字段作为您的指标,就像前面的一样。
当前模型
上一个预测模型的名称。假设我们只有一个应用程序,并且一次只能有一个模型。
这个指标实际上是一个表图,而不是一个记分卡。作为维度,选择model和prediction_date,然后将每页行数设置为 1,然后将prediction_date按降序排序。然后你应该在样式标签中隐藏所有可能的东西。
预言
真实/虚假预测的百分比。
****要创建它,请转到添加图表→饼图。选择prediction作为尺寸,选择记录 _ 计数作为公制,就可以了。
预测指标
****该图显示了每周两个值的平均值:confidence和coverage 。由此,我们应该能够看到模型对其预测的信心水平发生了不寻常的变化,以及有多少新闻出现在经过训练的模型的词汇中。如果coverage有下降的趋势,并且低于预定的阈值,可能是时候重新训练它了。
这是一个时间序列。在尺寸字段,在小日历图标处,您应该可以设置周期。我选择了 ISO 年周。作为度量,选择confidence作为第一个度量, AVG 作为度量的聚合。然后,进入添加公制并在样式选项卡对coverage.进行同样的操作,您可以将两个系列从线条更改为条并设置它们的颜色。
字数统计频率
该图表根据每个预测类别,按字数对记录进行分组。即使我们需要更多的数据来真正掌握字数分布,但似乎假新闻通常比真新闻短。
另一个奇怪的细节是,有 5 个字数在 0-100 之间的假预测。对于一条新闻来说,这似乎有点低,这促使我进一步调查。我最终发现,这些记录是从同一个网站提取的,它们都有一个解析错误。内容不是真正的新闻,而是一条错误消息。这很重要,我们应该确保这些类型的记录永远不会被吸收到我们的训练测试数据集中。
这是我在数据工作室制作直方图的最接近的一次。要创建一个,请转到图表→条形图,并在数据选项卡的底部添加一个新字段。在公式字段中,输入:
FLOOR(word_count/100)*100
对于每条记录,字数会除以 100,然后将结果的最大整数值乘以 100。这将把记录放入数百个容器中。我将该字段命名为wc_bin。您可以使用它作为尺寸,和记录计数作为度量。您还应该将排序设置为上升,并选择wc_bin。
****要将直方图分成假的和真的,你可以去添加一个过滤器,排序正下方的,并插入一个过滤器,如下所示:

作者截图
对下一个类别做相反的事情。
再训练
好了,现在我们有了一个检查模型健康状况的方法。如果有什么异常,我们可以开始标记一些记录,并查看一些重要的分类指标,如精度、召回率和 F 值。如果你对结果不满意,我们应该开始重新训练模型。****
概观
下图是我为这个项目设置的再培训流程的概述:

作者图片
我们有我们的基础数据**,我们用来训练我们的第一个预测模型的原始数据集。此外,该应用程序还不断向我们提供在线预测的数据。因此,为了重新训练模型,我们可以提取这些数据并做一些简单的预处理,如删除重复的新闻。验证我们的数据并确保它符合我们对数据形状和分布的一些假设也是非常重要的。然后,我们继续连接两个数据源,最终重新训练模型。在替换旧模型之前,我们必须用测试集来评估新训练的模型,以确保其质量。**
每次我们希望重新训练模型时,我们都遵循这些步骤。但是如果我们在将来发现有问题,我们如何调试我们的模型呢?要做到这一点,除了模型的性能结果和生成它的代码之外,我们还需要一种有序的方式来存储我们的模型和数据工件。
使用 LakeFS 进行数据版本控制
管理 ML 应用程序中的数据管道不是一件容易的事情。不像传统的软件开发,GIT 已经成为代码版本化的标准,数据版本化仍然处于早期阶段,没有一个“确定的”方法。
我决定将每个再训练过程视为一个单独的实验,并将每个实验的工件存储在一个单独的分支中。这与我们习惯的代码版本化有点不同,因为在这种情况下,我不期望将分支合并回主干。它们是短命的分支,目的是根据不同的再训练实验对我们的工件——数据集和模型——进行版本控制。
尽管有许多提供数据版本控制功能的 MLOps 工具,例如 MLflow 和 W & B ,但是我选择将数据版本控制作为基础设施,通过在我的对象存储上启用类似 Git 的操作。为此,我决定试用最近发布的 lakeFS ,它使我能够在我现有的 S3 对象存储之上添加类似 Git 的引擎。这样,我的项目数据版本控制能力独立于我将来可能添加或替换的任何工具。
预备步骤—部署 lakeFS
您可以在这里找到设置 lakeFS 环境的说明。
我基本上不得不:
- 在 AWS RDS 上创建 PostgreSQL 数据库
- 为我的存储库配置一个 S3 存储桶
- 对于策略的主体,我创建了一个用户并为其创建了访问密钥。我还将使用这个用户向 AWS 认证 lakeFS。你可以在这里阅读更多关于的内容,在这里阅读更多关于的内容。
3.安装 lakeFS
- 我选择通过 Docker 安装它,命令如下:
docker run --name lakefs -p 8000:8000
-e LAKEFS_DATABASE_CONNECTION_STRING="<postgres-connection-str>"
-e LAKEFS_AUTH_ENCRYPT_SECRET_KEY="<lakefs-secret-key>"
-e LAKEFS_BLOCKSTORE_TYPE="s3"
-e LAKEFS_GATEWAYS_S3_DOMAIN_NAME="s3.local.lakefs.io"
-e LAKEFS_BLOCKSTORE_S3_CREDENTIALS_ACCESS_SECRET_KEY="<s3-secret-key>"
-e LAKEFS_BLOCKSTORE_S3_CREDENTIALS_ACCESS_KEY_ID="<s3-access-key>" -e LAKEFS_BLOCKSTORE_S3_REGION="<s3-region>" treeverse/lakefs:latest run
其中postgres-connection-str是您在创建 PostgreSQL DB 时获得的连接字符串,lakefs-secret-key是任意随机生成的字符串(别忘了),s3-secret-key和s3-access-key是您之前为 AWS 用户创建的密钥对,s3-region是您创建的 bucket 的区域。
4.设置
在 localhost:8000 中,在设置了新的管理员用户并创建了存储库之后,您应该能够看到您的存储库列表:

作者截图
再培训过程
现在我们已经设置好了一切,我们需要将流程图转化为一系列步骤并实现它。我们基本上需要:
- 从在线预测中获取数据( BigQuery )
- 清理并断言数据质量(巨大的期望)
- 从 master ( lakeFS )创建新的分支
- 将在线预测上传到新分支( lakeFS )
- 从主支行( lakeFS )获取基础数据
- 将在线预测连接到基础数据,并拆分为训练测试数据集
- 将列车测试拆分上传至分支( lakeFS )
- 用更新的数据集训练模型
- 记录实验/结果( W & B
- 上传模型和词汇到分支( lakeFS )
- 将更改提交到分支( lakeFS )
数据结构
至于我们的数据结构,我们将把我们的原始数据集以及从 web 应用的在线预测中提取的数据定义为external数据。一旦外部来源被组合并分成适当的训练测试集,文件夹interim将保存我们的数据。模型文件也将存储在它们自己的文件夹中。

作者图片
从生产中获取数据
首先,我们需要从 BigQuery 表中获取新数据。为了使用 Python APIs 访问 BigQuery,我需要 JSON 格式的 GCP 凭证。创建项目后,您可以按照本 Google 文档中“创建服务帐户”的说明下载您的凭证。
然后,安装google-cloud-bigquery,设置一个指示凭证位置的环境变量,并启动 BigQuery 客户机,这样就可以查询表了。在这种情况下,sunny-emissary-293912是我的项目的名称,fakenewsdeploy是我的数据集的名称,model_predictions是我的表的名称。
清洁和维护质量
我们应该始终确保存储数据的质量。一个很好的方法就是使用工具https://greatexpectations.io/。在我做了一些非常简单的清理之后,比如删除重复的新闻和字数少的新闻,我们可以对我们的数据做一些基本的假设。****
在这个例子中,如果我们的数据通过了一些验证,我们将继续我们的再训练。ground_truth应该只假设Fake或Real值,url值应该是唯一的,并且每个样本应该有一个非空的content.。此外,我们假设必须调查过低的coverage,因为它可能是解析错误的迹象,或者可能是另一种语言的内容。最后,word_count应该高于 100,正如我们在本文的监控部分所讨论的。
然后我们可以生成一个expectation_suite,它给我们一个 JSON 文件,显示我们的数据通过的验证。我们还应该确保存储这些信息以供将来参考(这将在序列中完成)。
我们只是带着巨大的期望触及了表面。查看他们的网站以了解更多功能。
创建新分支
既然我们信任我们的数据,我们可以将它存储在一个新创建的分支中。在这个项目的 建模/评估的前期,我用 W & B 来跟踪我的实验。我会在再训练过程中继续使用它,和 lakeFS 一起使用。这样,实验标识符就是 wandb 的运行名,为了创建分支,我将使用相同的名称:
****这里要指出两件事。在第 3 行,在开始运行时,您会看到我配置了一个阈值。这是一个很好的 W&B 功能,我可以设置一个阈值,在培训期间进行监控。在这种情况下,如果被训练模型的 **F1-Score** 低于 **0.9** ,W & B 会发邮件通知我。我们将在训练代码中看到它的其余部分。****
这里的另一个重要问题是我如何在我的 lakeFS 存储库中进行操作。这可以用几种不同的方式来完成,但是我选择使用 Python 客户端。我创建了一个lakefs_connector类来将 API 包装成更适合我的应用程序的函数。该类的实现显示在本文的末尾。
将在线预测上传至分行
让我们继续上传online_predictions.csv到我们的分支。我们还将把my_expectation_file.json保存到 wandb 运行中。
现在,我们可以在任何需要的时候访问我们的期望文件,并确保在这个特定的运行中数据的状态符合我们的假设:

作者截图
合并数据和拆分
我们的基础数据由两个文件组成:True.csv和Fake.csv,这两个文件都已经上传到我们的主分支机构。让我们将来自online_predictions.csv的数据添加到我们的基础数据中,然后将它分成训练测试数据集:
训练模型并上传工件
最后一系列步骤是最终训练模型,并将我们的工件上传到存储库的分支:
我们将上传训练测试分割以及我们的模型文件——实际的joblib模型和用于我们的矢量器的词汇(它也用于计算我们的coverage字段)。
你可以在这里查看训练模型的全部代码。我不会深入细节,因为我已经在我的上一篇文章中提到了。我只想指出它的一个摘录,与我们设置的警报配置相关:
f1_score是训练过程中计算出的 F1 分数。由于我们之前设置了阈值 0.9,如果本次运行的分数低于该值,将向配置的目标发送警报。对我来说,是我的电子邮件:

作者截图
提交更改
现在,剩下要做的就是将我们的更改提交到我们的分支中:
我们会根据再训练实验将数据分开,就像这样:

作者截图
由于运行名称是唯一的,我们可以很容易地将数据与我们在 W&B 的实验仪表板上的再培训结果进行匹配:

作者截图
lakeFS 连接器
在上面的代码片段中,我们对我们的存储库进行了一些操作,比如创建分支、下载和上传对象,以及提交更改。为此,我使用 bravado 来生成一个动态客户端,如这里的所示。这样,我们可以通过 Python 命令访问所有 lakeFS 的 API。
通过实例化lakefs_conn类,我们创建了一个 lakeFS 客户机,并通过对象的方法执行所需的存储库操作:
下一步是什么
我想与你分享我对监测我的模型的性能和再培训,但这真的是一个漫长过程的第一步。随着时间的推移,额外的数据使我们能够有更多的见解,我们最终会发现更好的方法来监控我们的应用程序。
例如,在未来,我们可以绘制多个重新训练模型的性能,并随着时间的推移对它们进行比较,以更准确地评估时间对我们预测的影响。或者,给定足够的基本事实信息,我们可以在仪表板中添加精度和召回率等指标。
至于再培训部分,还有很多可以改善的地方。随着我们对数据分布的了解越来越多,我们可以在断言阶段增加额外的期望。为自动提交前验证增加挂钩也是一个自然的发展。在撰写本文时,lakeFS 还不支持 Webhooks,但在最新版本(0.33)中已经有了。
暂时就这样吧!如果您有任何反馈或问题,请随时联系我们!
感谢您的阅读!
参考
- 机器学习的连续交付
- 数据版本
- 确保数据湖环境中的数据质量
- ML 测试分数:衡量 ML 生产准备情况和技术债务削减的指标
- 为什么数据版本控制作为基础设施很重要
- lakeFS —文档
- 远大前程—文档
- 权重和偏差—文档
使用远大前程和 Allure 构建的无服务器监控数据湖中的数据质量
原文:https://towardsdatascience.com/monitoring-data-quality-in-a-data-lake-using-great-expectations-and-allure-built-serverless-47fa1791af6a?source=collection_archive---------10-----------------------

图片来自 freepik
人工智能、机器学习和大数据产业正在快速增长。它们不再仅仅是炒作,所有这三个行业都已经成熟到能够帮助各种组织推动增长并交付切实成果的程度。在这种情况下,相关解决方案的质量,尤其是支持这些解决方案的数据质量变得至关重要。
在 Provectus ,我们明白人工智能的数据质量至关重要。为此,我们使用各种服务进行数据质量监控,这些服务可以帮助我们识别和跟踪数据趋势、检测数据变化等等。所有这些服务都有助于识别风险和防止支出。软件测试背后的理论暗示着你越早发现错误并修复它,花费的钱就越少。在 AI 和 ML 的例子中,研究清楚地表明好的数据比模型中的系数更有用。

作者图片
今天的 AI 现实是,单纯做数据管道已经不够了。处理您的 ML 模型和数据更类似于一个标准的 IT 例程,包括它的所有过程。那么,如何有效地大规模评估数据质量呢?
我们已经有了一些使用两个开源数据质量保证框架的经验:Deequ 和 Great Expectations,但是大部分是在本地,而不是在云中。本文提供了一些关于使用 Great Expectations 来监控 AWS 云中的数据质量的观点。
为什么是远大前程?
Great Expectations (GE)是一个基于 Python 的开源数据质量框架。GE 使工程师能够编写测试、审查报告和评估数据质量。这是一个可插拔的工具,意味着你可以很容易地添加新的期望和定制最终报告。GE 可以很容易地与诸如 Airflow 之类的 ETL 集成,并且它有 AWS 支持。后者对于 AWS 首要咨询合作伙伴 Provectus 非常重要,因为我们现在正在努力将 Pandas profiling 添加到我们的数据质量监控工具组合中。Pandas 是分析数据和为 GE 自动生成简单测试套件的完美工具。
为什么是《倾城》?
Allure 框架是用于测试和报告的经典 IT 工具。对于经理和非技术专业人员来说,这是一个很好的选择,因为与严格的技术通用电气相比,它允许他们以更舒适的方式处理数据。在这个实现中,我们通过从 GE 到 Allure 的自写适配器来使用 Allure 和 GE 报告。
解决方案架构
一开始,我们基于以下架构参考构建了一个 POC。

作者图片
在实施中,我们用在我们以前的一个项目(NDA 下的客户)。我们依赖于基于气流的解决方案,也依赖于我们与 GE 共同制定的数据质量保证战略。然而,在这种情况下,我们需要收集大量的数据来构建它,并提供一个用户友好的仪表板,使 C 级员工能够处理数据。从这个角度来看,数据的质量再次显得至关重要。
我们的第一步是选择正确的数据集,并联系数据所有者,以了解我们到底需要在其中签入什么。然后,我们编写了几个本地测试套件:一个是技术套件,提供关于列中 null 值和空值的技术细节,另一个是研究业务和操作问题。之后,GE config 被改为 AWS(亚马逊 S3)集成,数据上传到它的存储器。为了实现卓越运营,我们对 Lambda 使用了 docker 图像和 ECR。我们对其进行了测试,并开始研究将其集成到现有数据管道的最佳方式。我们决定在 AWS Lambda 中使用 Step 函数作为 ETL。
这是最终的解决方案。

作者图片
解决方案概述
- 使用 AWS Lambda 和 AWS Glue 从数据源检索数据
- 同时,对每个数据集运行 AWS Lambda 和 Pandas Profiling 以及 GE 测试套件,并将每个数据集作为静态 S3 网站进行存储/服务
- 在 AWS Lambda 中,将 GE 结果转换为 Allure 结果,并存储在亚马逊 S3 中
- 在 AWS Lambda 中,根据 Allure 结果生成 Allure 报告,在亚马逊 S3 中存储和提供
- 通过使用 AWS,Lambda 向 Slack 通道发送报告
- 结果被推送到亚马逊 DynamoDB(你也可以使用亚马逊 S3 来降低成本)
- 使用 Amazon Athena 从 Amazon DynamoDB 抓取数据
- 使用 Amazon Quicksight 提取数据
步进功能:

作者图片
请记住,我们不希望(或需要)停止通常的数据工程步骤的实现,但是我们可以通过使用无服务器将所有堆栈部署到 Cloudformation。
数据质量保证堆栈的实现
**** —敏感信息*
要在 AWS 上实施 GE,您需要:
- 更改 ge 配置(great_expectation.yml)数据源、源和 data_docs
datasources:
pandas_s3:
class_name: PandasDatasource
batch_kwargs_generators:
pandas_s3_generator:
class_name: S3GlobReaderBatchKwargsGenerator
bucket: ***
assets:
your_first_data_asset_name:
prefix: ***/ regex_filter: .* module_name: great_expectations.datasource
data_asset_type:
class_name: PandasDataset
module_name: great_expectations.datasetexpectations_S3_store:
class_name: ExpectationsStore
store_backend:
class_name: TupleS3StoreBackend
bucket: '***'
prefix: '***/great_expectations/expectations/'
validations_S3_store:
class_name: ValidationsStore
store_backend:
class_name: TupleS3StoreBackend
bucket: '***'
prefix: '***/great_expectations/uncommitted/validations/'
evaluation_parameter_store:class_name: EvaluationParameterStore
expectations_store_name: expectations_S3_store
validations_store_name: validations_S3_store
evaluation_parameter_store_name: evaluation_parameter_storedata_docs_sites:s3_site:
class_name: SiteBuilder
show_how_to_buttons: false
store_backend:
class_name: TupleS3StoreBackend
bucket: ***
site_index_builder:
class_name: DefaultSiteIndexBuilder
renderer:
module_name: custom_data_docs.renderers.custom_table_renderer
class_name: CustomTableRenderer
show_cta_footer: true
2.创建测试套件并编写测试;记得更改必要文件的路径
# ### Column Expectation(s)
# #### `id`
# In[ ]:
batch.expect_column_values_to_match_regex(column='id',
regex="^\d+$")
# #### `lastNameFirstName`
# In[ ]:
batch.expect_column_values_to_match_regex(column='lastNameFirstName',
regex="([a-zA-Z .-]){2,30}[,]+[ ]+([a-zA-Z .-]){2,30}$")
# #### `customTeam`
# In[ ]:
# In[ ]:
batch.expect_column_values_to_match_regex(column='customTeam',
regex="^[a-zA-Z -]+$")
3.如果你想使用 AWS Lambda,把你的代码包装成 lambda_handler 或者
def handler(event, link):
context = DataContext(os.path.join(BASE_DIR, 'great_expectations'))
expectation_suite_name = "***"
suite = context.get_expectation_suite(expectation_suite_name)
suite.expectations = []
batch_kwargs = {'data_asset_name': '***', 'datasource': 'pandas_s3', 'path': 's3a://***/'+event[0],'link':link}
batch = context.get_batch(batch_kwargs, suite)
batch.head()
4.创建 requirements.txt 和 dockerfile
importlib-metadata==2.0
great-expectations==0.13.19
s3fs==2021.6.0
python-dateutil==2.8.1
botocore==1.20.49
boto3==1.17.1
aiobotocore==1.3.0
requests==2.25.1
decorator==4.4.2
pyarrow==2
fastparquet
awslambdaric
awswrangler
pandas_profilingFROM python:3.6-stretch
RUN apt-get update \
&& apt-get upgrade -y \
&& apt-get install -y
RUN apt-get install -y python3-dev
RUN apt-get install -y python3-pip
RUN pip3 install --upgrade cython
RUN pip3 install setuptools_scm
ADD ./requirements.txt ./
COPY edit_bamboo_hr_technic.py ./
COPY edit_bamboo_hr.py ./
COPY test_pandas_profiler.py ./
COPY bamboohr_report_data_test.py ./
COPY great_expectations /great_expectations
RUN pip3 install -r requirements.txt
ENTRYPOINT ["/usr/local/bin/python", "-m", "awslambdaric"]
CMD ["bamboohr_report_data_test.handler"]
- 在亚马逊 S3,使用静态网站选项。如果你想增加更多的安全性,实现 Amazon CloudFront
- 使用无服务器工具部署到 Cloudformation。首先,您需要“docker-compose build”,然后是“sls deploy”。如果您希望 GE 有一个单一的 Lambda,您需要创建一个 ECR 存储库,并按照指令推送您的 docker 映像。之后,创建一个 AWS Lambda 映像并选择它。
通用电气报告门户:

作者图片
通用电气报告:

作者图片
对于 Pandas Profiling,您可以使用以下代码:
def profile_data(event, runs):
file_path = event[0]
s3 = boto3.resource("s3")
bucket = s3.Bucket('***')
df = wr.s3.read_parquet(path='s3://***/')
profile = ProfileReport(df, title="Pandas Profiling Report", minimal=True)
report = profile.to_html()
bucket.put_object(Key='***', ContentType='text/html')
我们在同一个 Lambda 上部署 GE 和 Pandas Profiling,以削减成本和简化流程。
熊猫概况报告:

作者图片
诱惑力:
- Pre:您需要将 GE json 结果映射到 Allure json 结果
- 创建 docker 文件和需求文本
- 创建 sh 脚本并使用 Python 脚本与 Allure 交互,因为 Allure 需要 Java
FOLDER=$1
rm -r /tmp/result
rm -r /tmp/allure-report
aws s3 sync s3://***/allure/"$FOLDER"/result /tmp/result
allure-2.14.0/bin/allure generate /tmp/result --clean -o tmp/allure-report
aws s3 sync /tmp/allure-report s3://***/allure/"$FOLDER"/allure-report
4.部署它
《诱惑》报道:

作者图片

作者图片
我们还在 Gitlab 问题之间进行了整合,以便在测试运行后更容易创建问题。

作者图片
亚马逊雅典娜和亚马逊 DynamoDB:
要存储数据,请使用数据透视表,要抓取数据,请使用 Amazon Athena。
为了将数据推送到存储,我们可以使用 Amazon DynamoDD(或 Amazon SS)。
- 访问亚马逊 DynamoDB
- 创建表格
- 去和 Amazon DynamoDB 在同一地区的 Amazon Athena
- 连接一个新的数据源(即选择一个已创建的 DynamoDB 表)
- 在查询编辑器中测试连接

作者图片
快速观察:
- 去亚马逊 Quicksight
- 选择数据集,转到新数据集
- 选择亚马逊雅典娜
- 选择您的工作组和数据源名称

作者图片
5.创建数据源
6.进行新的分析
在现有的实现中,我们使用两种类型的图表
- bug 趋势线——每个日期每个选定源的失败测试百分比,您可以通过过滤器来选择

作者图片
2.套件和源状态栏

作者图片
3.这也是一个数据透视表,您可以单击 raw 打开报告菜单并查看每个数据 qa 结果

作者图片
Slack bot 发送一个通知,其中包含每个管道的链接

作者图片
结论
本文演示了如何使用 StepFunctions、Lambda、GE、Allure 和 Pandas Profiling 在 AWS 上的数据管道中构建和实现一种数据质量方法。它解释了这些工具和服务的内容和方式,并提供了一种使用它们来确保数据质量、简化管道调试和降低成本的可靠方法,而不会增加数据管道的架构复杂性。
要了解更多关于 Provectus 数据质量保证实践的信息,请访问 网页 :
满怀期望的数据质量保证和 Kubeflow 管道
我们的 pip 包用于将 ge 结果映射到 Allure 结果
使用弹性 APM 监控 Python 应用程序
原文:https://towardsdatascience.com/monitoring-flask-fastapi-python-applications-with-elastic-apm-33237a39d7b6?source=collection_archive---------11-----------------------
编程;编排
Flask、FastAPI 和 Python 应用程序监控指南

卢克·切瑟在 Unsplash 上拍摄的照片
什么是弹性 APM?
弹性 APM 是建立在弹性堆栈上的应用性能监控系统。通过收集有关传入请求、数据库查询、缓存调用、外部 HTTP 请求等响应时间的详细性能信息,它允许您实时监控软件服务和应用程序。这使得快速查明和修复性能问题变得很容易。
Elastic APM 还自动收集未处理的错误和异常。错误主要基于堆栈跟踪进行分组,因此您可以在新错误出现时识别它们,并留意特定错误发生的次数。
-弹性
本文由三部分组成:
- 监控烧瓶/烧瓶-RESTPlus 应用
- 监控 FastAPI 应用程序
- 监控 Python 应用程序
监控烧瓶/烧瓶-RESTPlus 应用
装置
弹性 APM 内置烧瓶支架。因为 Flask-RESTPlus 和 Flask-RESTful 是 Flask 的扩展,所以相同的步骤适用于 Flask-RESTPlus 和 Flask-RESTful。
使用 pip 安装弹性 APM 代理:
pip install elastic-apm[flask]
履行
让我们首先导入所需的包:
from flask import Flask
from elasticapm.contrib.flask import ElasticAPM
import elasticapm
现在,让我们创建一个 Flask 实例,这将是我们的 WSGI 应用程序。
app = Flask(__name__)
我们可以通过使用环境变量或应用程序代码本身来初始化 APM 代理。在本文中,我们将在代码中初始化 APM 代理。
要创建一个弹性 APM 代理的实例,我们需要以下参数:
server_url →弹性 APM 的 URL
service_name →应用名称
environment →应用运行的环境,例如开发、质量保证或生产
server_url = 'http://localhost:8200'
service_name = 'DemoFlask'
environment = 'dev'
接下来,我们将初始化 APM 代理。
我们需要传递 Flask 实例app作为初始化 APM 代理的第一个参数,以及我们上面定义的参数。
apm = ElasticAPM(app, server_url=server_url,
service_name=service_name, environment=environment)
我们的 APM 代理现在准备好了。
现在,让我们打开 Kibana(例如 http://localhost:5601/)来查看记录的数据。
打开 Kibana 仪表板并转到 APM 选项卡。你可以看到我们的服务DemoFlask列在那里。

单击服务名并转到 Metrics,在这里可以跟踪 CPU 和内存的使用情况。


在 transactions 选项卡中,您可以看到与您的应用程序收到的每个请求相关的可视化内容,例如事务持续时间和每分钟的请求数。


您还可以查看所有端点的列表及其平均持续时间。

单击交易以查看交易的更多详细信息。


您还可以通过使用标签来添加关于交易的附加信息。
elasticapm.label(platform='DemoPlatform')
为了给所有事务添加默认标签,我们可以使用 Flask 的app.before_request decorator。
@app.before_request
def apm_log():
elasticapm.label(platform = 'DemoPlatform',
application = 'DemoApplication')
标签的信息将在事务的跟踪样本的元数据选项卡中可见。

注意,默认情况下,只有当应用程序在调试模式下是 而不是 时,才会记录事务和错误数据。
示例代码可以在 参考资料 部分提到的链接中找到。
监控 FastAPI 应用程序
要使用 Elastic APM 正确地监控 FastAPI/Starlette 应用程序,您需要使用 Python 3.7+
装置
使用 pip 安装弹性 APM 代理:
pip install elastic-apm
履行
首先,让我们导入所需的包:
import uvicorn
from fastapi import FastAPI
from elasticapm.contrib.starlette import make_apm_client, ElasticAPM
接下来,我们将使用SERVICE_NAME、SERVER_URL和ENVIRONMENT创建一个 APM 客户端。此外,我们将使用GLOBAL_LABELS同时指定全局标签。
apm_config = {
'SERVICE_NAME': 'DemoFastAPI',
'SERVER_URL': 'http://localhost:8200',
'ENVIRONMENT': 'dev',
'GLOBAL_LABELS': 'platform=DemoPlatform, application=DemoApplication'
}
apm = make_apm_client(apm_config)
现在,让我们初始化弹性 APM 代理。
app = FastAPI()
app.add_middleware(ElasticAPM, client=apm)
FastAPI 应用程序现在准备好将日志发送到弹性服务器。
示例代码可以在 参考资料 部分提到的链接中找到。
监控 Python 应用程序
我们可以创建一个弹性 APM 客户端来监控不使用框架的 Python 应用程序(例如 Flask、Django 或 FastAPI)。这些应用的一个例子可以是可调度代码。
装置
使用 pip 安装弹性 APM 代理:
pip install elastic-apm
履行
首先,我们将创建一个弹性 APM 客户端
from elasticapm import Client
import elasticapm
client = Client(
{'SERVICE_NAME': 'DemoPython',
'SERVER_URL': 'http://localhost:8200',
'ENVIRONMENT': 'dev'}
)
对于 Flask 和 FastAPI 这样的框架,Elastic APM 会自动检测应用程序,并开始和结束事务。
然而,对于不使用这种框架的 Python 应用程序,我们需要手动检测应用程序,并开始和结束事务。
自动检测您的应用程序以捕获 HTTP 请求、数据库查询等。,添加下面一行
elasticapm.instrumentation.control.instrument()
要开始一个事务,使用begin_transaction方法,将适当的事务类型作为参数。
举个例子,
client.begin_transaction('schedule')
要完成一个事务,使用 end_transaction 方法,该方法有两个参数,即。交易名称和结果。
举个例子,
client.end_transaction('demo-transaction', 'success')
可以在 资源 部分提到的链接中找到用于监控 Python 应用程序的示例代码。
资源
这篇文章的所有代码片段都可以在我的 GitHub 页面上找到。
参考
- https://www . elastic . co/guide/en/APM/agent/python/current/configuration . html
- https://www . elastic . co/guide/en/APM/agent/python/current/flask-support . html
- https://www . elastic . co/guide/en/APM/get-started/7.4/metadata . html
- https://www . elastic . co/guide/en/APM/agent/python/current/starlette-support . html
让我们连接
领英:https://www.linkedin.com/in/jimit105/GitHub:https://github.com/jimit105推特:https://twitter.com/jimit105
监控生产中的机器学习模型
原文:https://towardsdatascience.com/monitoring-machine-learning-models-in-production-how-to-track-data-quality-and-integrity-391435c8a299?source=collection_archive---------8-----------------------
如何跟踪数据质量和完整性

图片作者。
俗话说:垃圾进来就是垃圾出去。输入数据质量是机器学习系统最重要的组成部分。无论您是否有即时反馈回路,您的模型监控总是从这里开始。
数据会出什么问题?
人们会遇到两种类型的数据问题。简单来说:
1)数据本身出了问题;或者
2)数据因为环境而改变。
让我们从第一类开始。光是它就有很多。
头号数据处理问题
机器学习应用程序通常依赖上游系统来提供输入。最常见的情况是生产模型没有收到数据。或者,它接收损坏的或有限的数据,这都是由于一些管道问题。
我们举个营销的例子。
一家银行的数据科学团队开发了一个强大的机器学习系统,以个性化每月发送给客户的促销信息。
该系统使用来自内部客户数据库的数据、来自网上银行和移动应用程序的点击流日志以及呼叫中心日志。此外,营销团队手动维护一个电子表格,在其中添加本月的促销选项。
所有数据流被合并并存储在数据仓库中。当模型运行时,它会计算关节表顶部的必要特征。然后,该模型根据接受的可能性对每个客户的报价进行排名,并输出结果。

*促销个性化用例的简化管道丛林。(*图片由作者提供)。
此管道使用多个数据源。并且,不同的功能所有者维护它们中的每一个。相当多的机会来搞乱它!
以下是将要发生的令人不快的事情的不完整列表:
- **错误的来源。**管道指向旧版本的市场营销表,或者存在未解决的版本冲突。
- **失去通路。**有人将表格移动到新位置,但没有更新权限。
- **错误的 SQL。**或者不是 SQL。无论你用什么来查询你的数据。这些连接和选择在第一次出现复杂情况之前可能工作得很好。比如说,一个来自不同时区的用户在“明天”做了一个动作?该查询可能不成立。
- 基础设施更新。你得到了一个新版本的数据库和一些自动化的大扫除。空格替换为下划线,所有列名小写。一切看起来都很好,直到您的模型想要将其常规特征计算为“上月收入/总收入”。带有硬编码的列标题。哎哟!
- **断特征码。**我敢说,数据科学的代码往往不是生产级的。在极端情况下,它可能会失败。例如,培训中的促销折扣从未超过 50%。然后,营销部门推出一个“免费”报价,并第一次键入 100%。一些依赖的特性代码突然变得没有意义,并返回负数。
当数据处理出错时,模型代码就会崩溃。至少,你会很快了解这个问题。但是如果您的 Python 代码有一些*“Try…Except”*子句,它可能会在不正确和不完整的输入上执行。后果都是你的。
我们看的宣传片例子有批量推断。没那么戏剧化。你有犯错的空间。如果您及时发现了管道问题,您可以简单地重复模型运行。
在高负载流模型中,数据处理问题成倍增加(想想电子商务、游戏或银行交易)。
#2 数据模式变化
在其他情况下,数据处理工作得很好。但是数据源会发生有效的变化。不管是什么原因,新的数据格式、类型和模式对模型来说很少是好消息。
除此之外,变更的作者通常没有意识到影响。或者,那里甚至存在某种模型。
让我们回到宣传片的例子。
一天,呼叫中心的运营团队决定整理 CRM,丰富他们在每次客户来电后收集的信息。
他们可能会引入更好、更细化的类别,根据问题类型对来电进行分类。他们还会询问每个客户他们首选的通信渠道,并开始在一个新的字段中记录。既然我们在这里:让我们重命名和改变字段的顺序,使它对新用户更直观。
现在,看起来很整洁!

图片作者。
但对模型来说并非如此。
用专业术语来说,这一切都意味着失去信号。
除非明确告知,否则模型不会将新类别与旧类别相匹配,也不会处理额外的功能。如果没有数据完整性检查,它将基于它知道如何处理的部分输入生成响应。
任何与目录打交道的人都知道这种痛苦。
比如在需求预测或者电商推荐方面。通常,你会有一些基于类别类型的复杂特性。比如,“笔记本电脑”或“手机”在“电子产品”中那是昂贵的。让我们把它做成一个特色。“手机壳”在“配件”里这有点“便宜”我们也会用到它。
然后,有人重新整理了目录。现在,“手机”和“手机壳”都在“手机”下面。一个完全不同的类别,有不同的解释。该模型将需要重新学习,或者等到有人解释发生了什么。
这里没有魔法。如果目录更新经常发生,你最好把它考虑到模型设计中。否则,培训业务用户并跟踪突然的变化。

是的,现实世界的机器学习可能就是这么脆弱。(图片来源: Pixabay )
更多例子:
- 原始业务系统中的更新导致测量单位(将摄氏温度改为华氏温度)或日期格式(日/月/YY 或月/日/YY?)
- 应用程序中的新产品功能增加了模型从未训练过的遥测技术。
- 有新的第三方数据提供者或 API,或宣布的格式变化。
**具有讽刺意味的是,领域专家可以将这种变化视为运营改进。**例如,一种新的传感器可以让您以毫秒级的速度捕捉高粒度数据。好多了!但是该模型是针对聚合进行训练的,并期望以通常的方式计算它们。
**缺乏清晰的数据所有权和文档使其更加困难。**可能没有简单的方法来跟踪或知道向谁通知组织内部即将到来的数据更新。数据质量监控成为捕捉变化的唯一方式。
#3 源位置的数据丢失
数据不仅会改变。它也可能由于源头上的一些故障而丢失。

有时你的管道可能会无处可去。(图片鸣谢:Unsplash)。
**例如,由于日志记录中的错误,您丢失了应用点击流数据。**物理传感器坏了,温度不再可知。外部 API 不可用等等。我们希望尽早发现这些问题,因为它们通常意味着未来再培训数据的不可逆转的损失。
这种中断可能只影响数据的一个子集。例如,一个地理位置或特定操作系统中的用户。这使得检测更加困难。除非另一个(适当监控!)系统依赖于相同的数据源,所以故障可能不会被注意到。
更糟糕的是,一个被破坏的数据源可能仍然提供数据。例如,损坏的温度传感器将返回上一次测量的恒定值。这很难发现,除非你跟踪“不寻常”的数字和模式。
与物理故障一样,我们不能总是立即解决问题。但是及时发现有助于快速评估损害。如果需要,我们可以更新、替换或暂停模型。
#4 破碎的上游模型
在更复杂的设置中,您有几个相互依赖的模型。一个模型的输出是另一个模型的输入。
这也意味着:一个模型的坏预测是另一个模型的坏特征。
拿一个内容或产品推荐引擎来说。
它可能首先预测某个产品或物品的受欢迎程度。然后,考虑到估计的流行度,它向不同的用户做出推荐。
这些将是独立的模型,基本上是相互连接的。一旦该商品被推荐给用户,它更有可能被点击,从而更有可能被拳头模型视为“受欢迎”。
一个更高科技的例子:汽车路线导航系统。
首先,你的系统构建可能的路线。然后,一个模型预测他们每个人的预期到达时间。接下来,另一个模型对选项进行排序,并决定最佳路线。这在某种程度上影响了实际的交通堵塞。一旦汽车沿着建议的路线行驶,这就产生了一种新的路况。

图片作者。
物流、路线和交付中的其他模型经常面临同样的问题。
这些相互关联的系统有一个明显的风险:如果其中一个模型出了问题,你会得到一个相互关联的问题循环。
监控数据质量和完整性
正如我们所见,机器学习模型的输入数据可能会出错。
我们希望这些事情永远不要发生,但是让我们现实一点。所以,我们的目标是及时赶上他们。

打破复杂系统的简单事物。 (图片来源: Unsplash )。
**通常,数据可靠性和一致性属于数据工程范畴。**您甚至可以在数据库级别拥有一些检查或监控系统。还有什么需要关注的吗?
**问题是,对于机器学习系统,我们不关心整体数据质量。**我们希望跟踪给定模型使用的特定数据子集。有时是专一的。仓库中 99%的数据是正确的并不重要;我们想检查一下我们的作品。
特征处理代码也是一个需要监控的独立移动部分。这需要定制设置。
因此,在数据质量和完整性方面,MLOps 符合 DataOps。我们最好仔细检查一下。
有一些与数据相关的东西需要注意:
#1 型号电话
要回答的第一个问题是这个模型是否有效。为此,看看模型响应的数量。这是添加到软件监控之上的一个基本但有用的检查。
为什么?服务本身可能是可操作的,但不是模型。或者,您可能比计划的更频繁地依赖于回退机制,如业务规则。
如果你的模型经常被使用,这就没那么有用了。但是如果有一个“正常”的使用模式,这是一个很好的检查。例如,你的模型部署在电子商务网站上,或者每天都有新的销售数据。你就知道会有什么样的消费。
查看模型调用的数量是发现问题的简单方法。

在这里,模特打电话的次数一夜之间变为零。也许服务中断了?
**根据模型环境,您可能想要分别检查请求和响应。**模型是否未被询问(例如,因为推荐小部件崩溃)或未能回答(例如,模型超时,我们不得不使用静态推荐)?答案会指出应该从哪里开始调试。
现在,我们来看看数据。
#2 数据模式
正如我们上面解释的,数据模式可能会改变。不管是因为不好的做法还是出于好意;我们想检测它。
我们的目标是了解特性何时被删除、添加或更改。
最直接的方法是执行逐个特征的检查和调查:
**1/如果特性集保持不变。**对于表格数据:有多少列?有没有什么东西不见了,或者有什么新的东西?
2/如果特征数据类型匹配。我们在某个地方得到了分类值而不是数值吗?例如,在给定的列中,我们有从 1 到 10 的数字特征。现在,当我们查询它时,我们看到类似“低”、“中”和“高”的值我们应该能抓到这个。

模式验证错误。(图片由作者提供)。
最后,您需要一个快速的摘要视图,来查看传入的数据集是否符合预期。
#3 缺失数据
我们还想检测任何丢失的数据。
**通常,会有一些可接受的缺失值。**我们不想对每个空条目都做出反应。但是我们希望比较缺失数据的水平是否保持在“正常”范围内,包括整个数据集和单个要素。是否丢失了任何关键功能?
**记住这一点很重要,因为缺失值可能有多种形式。**有时它们是空的,有时它们是“未知的”或“999”。如果您对缺少的功能进行简单的检查,您可能会错过其他功能。最好扫描缺失数据的标准表达式,如“N/A”、“NaN”、“undefined”等。偶尔用自己的眼睛进行审计也不是一个坏主意。
**如果你的特征数量有限,你可以将它们全部显示在一个图中。**我们就是这样做的,对缺失值的份额进行颜色编码:

图片作者。
**您还可以设置数据验证阈值,以定义何时暂停模型或使用回退。**例如,如果缺少太多特征。当然,“太多”的定义取决于您的用例以及模型的错误成本。
一个有用的建议是挑出你的关键驱动因素。您可以根据模型特征重要性或 SHAPley 值来实现。或者,将这两种方法与你的领域知识结合起来。
这个想法是建立不同的监控策略。你总是需要你的关键特性来运行模型。有了辅助手段,缺席就不是一个阻碍。你只需要做个记录,然后和数据所有者一起调查。
#4 特征值
数据摆在那里,不代表就是正确的。
例子:
- 在 excel 处理出错后,“年龄”列的值从 0.18 到 0.8,而不是 18 到 80。
- 一个物理传感器坏了,显示一周的某个恒定值。
- 有人在功能计算过程中掉了一个负号,您会看到负的销售数字。
在所有情况下,模型都是有效的,数据是可用的——但是被破坏了。
要检测到这一点,您需要监控特性的统计数据和分布。

我们使用明显库(图片由作者提供)比较了训练和生产中的特征分布。
**1/特征值范围。**对于数字特性,检查数值是否在合理范围内。对于分类属性,定义一个可能值的列表,并留意新奇的事物。
如何做到这一点?
- 通过查看训练分布,您可以直接定义您的期望值(最小-最大范围,或给定列的可能值)。
- 或者,依靠常识:我们知道“年龄”或“室外温度”的可能值。
- 如果有更多的上下文,您可以让领域专家来定义特定输入的“正常”。
它还有助于声明何时明确允许空值。
**2/关键特征统计。**对于数值特征,可以看平均值、均值、最小最大比、分位数。
后者将有助于捕捉像这种传感器损坏的情况。形式上,它保持在范围内,但测量完全是静态的:

图片作者。
**对于分类输入,您可以检查它们的频率。**例如,如果你处理文本,对应的可能是词汇的%。
目标是监控实时数据集的合规性并验证输入端的数据。通过这种方式,您可以在出现范围违规、异常值或统计数据变化时进行捕捉。
#5 特征处理
另一个要考虑的方面是在哪里运行您的数据有效性检查。
**当数据出错时,首先要问的是为什么。**在理想世界中,我们希望一发现错误就定位错误。断裂的连接或功能代码可能是一个原因。在这种情况下,源数据很好,但是在转换为模型特征的过程中会发生一些事情。
有时,为管道中的每一步分别验证输入和输出是有意义的。这样,我们可以更快地找到问题并进行调试。
例如,我们预测一家移动运营商的客户流失。
营销数据来自一个来源。购买日志与不断变化的产品计划相结合。然后,您将使用日志与关于技术连接质量的外部数据合并。特征转换需要几个步骤。

图片作者。
当然,您可以简单地验证上一次计算的输出。但是如果你注意到一些特性没有意义,你就不得不返回每一步。如果管道很复杂,单独检查可能会为您节省一些检测工作。
总结
数据质量监控是生产机器学习系统的第一道防线。通过查看数据,您可以在许多问题影响实际模型性能之前发现它们。
你可以,也应该对每个型号都这样做。这是一个基本的健康检查,类似于延迟或内存监控。它对于人类和机器生成的输入都是必不可少的。每一种都有自己的错误类型。数据监控还有助于揭示被放弃的或不可靠的数据源。
当然,数据问题不止于此。在下一篇文章中,我们将深入挖掘数据和概念漂移。
最初发表于T5【https://evidentlyai.com】。
在 appeally AI,我们构建开源工具来分析和监控机器学习模型。在 Github 中查看我们的 数据漂移检测工具 。
想留在圈子里? 报名 获取我们的更新和产品消息,或者加入上Twitter和Linkedin获取更多关于生产机器学习的内容,或者加入我们的 不和谐社区 进行聊天和连线。
水下无人驾驶飞机的监控
原文:https://towardsdatascience.com/monitoring-of-an-underwater-drone-3c5c8a6d1a21?source=collection_archive---------46-----------------------
使用 Kafka 和 whylogs 监控流数据

克里斯·利维拉尼在 Unsplash 上的照片
数据在做出明智决策中的重要性现在已被几乎所有应用普遍认同。这也促进了对工具的需求,使我们能够以明智和有效的方式利用这些数据。
在本文中,我想分享一种方法,通过记录流数据的统计配置文件来设置监控仪表板,从而利用流数据。为此,我将使用水下遥控潜水器( ROV )作为用例。更具体地说,我们将监测来自最新的 OpenROV(现在的 Sofar )的 OpenROV v2.8 的故障,这是一种低成本的远程机器人水下无人机。为了增强我们的故障检测能力,我们将使用一个回归模型,通过 SKLearn 平台进行训练,对我们的车辆行为进行建模。这样,我们的监控仪表板将服务于两个目的:监控回归模型的性能和质量问题,以及(主要)监控车辆本身的健康状况。由于故意破坏 ROV 会很麻烦,我们将在我们的数据中手动注入一些传感器故障,并希望能够通过使用我们的仪表板来检测这些故障。
作为选择的数据记录平台,我们将使用 whylogs ,这是一个来自 WhyLabs 的开源库,旨在监控来自 ML/AI 应用程序的数据。Whylogs 为记录的记录计算近似的统计数据,这意味着它是具有大量数据的管道的合适选择。尽管这个例子使用了最少量的信息,但是知道所选择的工具考虑到了未来可能的扩展还是很好的。
由于我们要处理实时数据的连续流,我们需要选择一个合适的事件流平台。从这个意义上说,在这个项目中使用 Kafka 似乎是一个自然的选择,也是一个熟悉这个工具的好机会。这样,我们可以通过使用发布/订阅模式,将遥测信息的发送从模型预测和日志记录过程中分离出来。
你可以在这个项目的 jupyter 笔记本中了解这里讨论的一切。在项目的资源库中,您还会发现贯穿本文的各个 Python 脚本和所需文件。
目录
- 概述
- 我们以 结束
- 故障注入
- 开发应用程序
∘设置 Kafka ∘创建 Kafka 客户端 ∘遥测生产者 ∘预测生产者 - 监控仪表盘
∘会话 0 —恒定增益故障
∘会话 1 —卡在(零)
∘会话 2 —卡在(最大值) - 结论
概观
让我们仔细看看我们的实验。

作者图片
ROV 以在线方式将其遥测信息发送到遥测主题中。这些信息只是 JSON 对象的集合,其中每个对象代表不同特征的一个采样点,例如车辆的角位置、电流、电压等。考虑到在这种情况下,信息已经收集完毕,我们将简单地通过 Python 脚本发送 JSON 内容,模拟 ROV 的实时操作。鉴于我们的目标是检查我们是否能够监控和验证一些常见传感器故障的存在,我们需要在将数据发送到遥测主题之前,在数据集中手动注入一些故障。传感器故障将在下一节详细解释。
下一个组件是预测生成器,它使用遥测主题,以便通过训练好的回归模型生成预测,并将结果传输到单独的预测主题中。在这个用例中,回归模型使用遥测信息来预测一个特定特征的下一个时间步长— GYROZ ,这是 ROV 在偏航方向的角速度(潜水器转弯的速度)。回归模型是一个线性最小二乘回归模型,具有 L2 正则化和多项式基函数,使用 SKLearn 库在大约 24 小时的 ROV 操作中进行训练。
接下来的两个组件将持续监听遥测和预测主题,以建立我们数据的统计资料,这些资料将用于记录会话的后续人工检查阶段,以便有望检测到 ROV 操作期间可能出现的异常。在这种情况下,如果采样点距离上一个采样点超过 5 分钟,则认为新的操作会话已经开始,对于每个会话,我们将数据聚合为 1 分钟一批的数据。由于我们的采样频率为 5 Hz ,因此每批大约有 300 个采样点。
最后,我们需要一种简单的方法来可视化统计结果。我决定将最相关的绘图类型集中在一个 iPython 小部件中。这样,我们只需使用 jupyter 笔记本就可以检查 whylogs 摘要的当前状态。
我们最终会得到什么
不要在整篇文章中建立结论,让我们从展示我们将如何监控我们的 ROV 操作和模型预测开始。一旦我们的所有流程都在发送和记录来自 ROV 操作的信息,最后一个组件就是监控仪表板本身,我们希望在这里集中所有必要的信息,以评估潜水器的行为。
考虑到最终目标是检测不同种类的故障,我实现了一个带有可定制参数的监控仪表板,因此我们可以从 whylogs 中选择正确的图表来检测不同的情况。在这个仪表板中,我们基本上可以选择要选择的参数:
- 要检查的单个操作会话。
- 我们希望检查的遥测信息,如速度、位置、电机输入、CPU 使用率等。
- 线性回归模型的预测,此处用于检查我们的 ROV 的行为是否与我们在标称条件下的预期有很大不同。

作者图片
我们将在文章的结尾更详细地讨论这些结果,但最终的结论是,通过调整我们仪表板的正确参数,我们肯定能够检测到由我们车辆中的感官故障引起的异常情况。因为这里的重点不是开发完美的回归模型,我们的预测本身不足以检测某些情况下的错误。然而,通过结合来自我们的预测和输入特征可视化的见解,我们可以对我们检查的每个操作会话中发生的事情有所了解。
故障注入
在这个实验中,我们将使用来自 4 个不同操作会话的数据,每个会话的持续时间从 4 到 9 分钟不等。在每个会话中,大约在一半时间注入一个持续时间为 5s 的 GYROZ 传感器故障。你可以看看 JSON 文件,已经注入了错误,在项目的资源库中,在 rov_data 文件夹下。
在下图中,注入的故障类型用橙色表示,应用于样本正弦信号。在会话 0 中,注入一个恒定增益故障,其中真实值因乘以一个恒定因子而失真,如 a) 所示。固定故障在会话 1 和会话 2 中均被模拟,不同之处在于其“固定”的值——而在会话 1 中,GYROZ 值固定在值 0,如 b) 所示,会话 2 的固定值为 5.56,这与 ROV 陀螺仪传感器的最大范围有关。最后,在会话 3 中注入一个漂移传感器故障,如 c) 所示。该故障模拟的情况是,相对于故障开始时,随着时间的增加,实际值发生失真。

作者图片
除了这些传感器故障,会话 3 中还应用了**丢失**错误,模拟水下机器人长时间停止发送信息的情况。与之前的故障不同,该故障不仅影响 **GYROZ** 值,还影响车辆传输的整套特征。这是通过在会话 3 运行结束时从我们的数据集中手动删除一些连续的数据点来模拟的。
开发应用程序
现在我们已经有了用例的概述,让我们继续实际实现应用程序。接下来的几个部分将涵盖向我们在 Kafka 的主题发送/读取信息所需的代码,使用我们的回归模型进行预测,并使用 whylogs 记录我们的统计数据。
建立卡夫卡
在继续之前,我们需要用 Kafka 设置我们的流平台。我们正在处理低生产者吞吐量,所以设置一个与 Docker 本地卡夫卡将足以为这个项目。为此,你可以遵循本教程。文章中详细解释了步骤,但是对于非常短的版本,您可以简单地将 docker-compose.yml 文件复制到您想要的文件夹中,然后运行 docker-compose up。对于这一步,您自然需要安装 Docker。
一旦容器启动,您可以使用以下命令进入kafka-tools命令行界面:
docker exec -it kafka /bin/sh
并检查可用的主题,只是为了确保它的工作:
kafka-topics --list --bootstrap-server localhost:9092
创造卡夫卡式的客户
在接下来的 Python 脚本中,我们将不得不创建大量的 Kafka 消费者和生产者。让我们定义两个简单的函数来创建客户端,这样我们可以在将来重用它们:
在这两种情况下,客户端都被告知应该联系的地址,以便引导初始群集元数据。此外,由于我们正在生成/消费 JSON 格式的值,我们需要用vaue_serializer / value_deserializer参数为它指定正确的处理方式。
在这个项目中,消费者被设置为总是从主题的开头开始,以确保每次运行都将记录整个内容。为此,在开始之前,我们首先必须手动将分区分配给我们想要的主题。
关于 whylogs 与 Kafka 集成的更多信息,你可以参考这个教程笔记本和 WhyLabs 上的这个博客帖子。
遥测生产者
现在我们已经运行了 Kafka,我们可以开始将 ROV 数据发送到我们的遥测主题。首先,我们需要创建一个 Kafka Producer 客户端,它将记录发布到我们的集群,并读取 rov_data 文件夹中的 JSON 编码文件,以创建一个字典列表:
正如我们所看到的,我们的采样点由一系列特征组成:
- mtarg1,mtarg2,mtarg3 :向 3 个螺旋桨中的每一个发送输入指令。
- 侧倾、俯仰、偏航:车辆绕 X、Y、Z 轴的角度位置。
- LACCX,LACCY,LACCZ: 车辆绕 X,Y,Z 轴的角加速度。
- GYROX,GYROY,GYROZ: 车辆绕 X,Y,Z 轴的角速度。
- SC1I,SC2I,SC3I: 每个螺旋桨的电流读数。
- BT1I,BT2I: 电动每个电池组的电流读数。
- *vout、iout 和 CPU usage:*ROV 的 BeagleBone 微型计算机的电压、电流和 CPU 使用率。
我们现在可以在我们的主题中发布我们的数据点:
如果您愿意,在发送信息之前,最好通过在kafka-tools输入以下命令来创建带有适当分区/复制的 Kafka 主题:
kafka-topics --create --bootstrap-server localhost:9092 --replication-factor 1 --partitions 1 --topic telemetry-rov
但是您不必这样做,因为如果主题不存在,它会自动创建一个主题。
在这个例子中,维护记录的顺序非常重要,所以我们调用了一个producer.flush()来确保只有在前一个请求已经被提交时才发出新的请求。
预测生产者
有了遥测数据,我们就能够开始用回归模型进行预测。鉴于我们正在对 GYROZ 进行一步预测,我们只需要等待一个时间步就可以得到我们的地面真相。通过计算上一个时间步的预测值和当前实际值之间的差值,我们定义了所谓的残差。这个想法是,水下机器人的潜在异常会产生更高的残差,因为我们的预测会比通常的实际值更远。
和前面一样,我们创建了消费者客户端(从遥测主题读取)和生产者客户端(发布到我们的预测主题)。我们还必须加载我们的 SKLearn 模型,用 joblib: 持久化
在我们的 main 函数中,脚本将持续从主题中获取数据,并计算每个时间步长读取的残差。如果 10 秒钟过去了还没有收到新的记录,我们会输出一个警告来通知用户。
一旦我们有了当前和先前时间步长的数据,就可以计算残差:
为了进行预测,我们还需要加载在模型训练过程中安装的最小-最大定标器(BL_x.pickle和BL_y.pickle),这样我们就可以对预测和逆变换的特征进行变换,以输出未缩放的残差。
为了保持遥测和预测主题之间的一致性,我们希望存储每个时间戳的残差,即使计算它是不可能的。因此,对于第一个采样点,或者当两个采样点在时间上相距太远时,残差被记录为一个nan值。
遥测记录器
一旦适当地设置了两个 Kafka 生成器,我们就可以开始用 whylogs 记录我们的会话配置文件了。记录从遥测主题轮询,并分成单独的会话。如果下一个采样点与上一个采样点相隔超过 5 分钟,则当前会话将被结束并记录,然后再开始下一个会话。当超过 10 秒钟没有新信息时,我们也认为当前会话已经结束,并记录结果。
预测记录器
我们的预测题目也是这样。这里的不同之处在于,我们不仅要记录剩余要素,还要进行一些转换,以便创建和记录附加要素。
即使对于车辆的标称条件,我们也可能在残值中有峰值,导致高比率的误报。为了减少这种情况,我们根据不同的时间框架计算残差的移动平均值。在本例中,除了未改变的残差,我们还将记录最后 5 、 10 和 15 时间步长( 1 、 2 和 3 秒)的移动平均值:
如果,对于给定的滑动窗口,我们有nan值,或者我们没有足够的记录来计算移动平均值,那么对于给定的时间戳,nan被记录。
会话记录器
每当记录器结束一个会话时,就会调用log_session()函数,该函数负责初始化一个日志记录会话,并记录给定会话的每个记录。
为此,让我们将数据组织成一个 Pandas 数据帧,将我们的时间戳列转换成 datetime 对象,然后使用df.groupby将数据帧分成一批,每批 1 分钟。通过这种方式,每个操作阶段都将记录水下机器人每分钟活动的统计数据。key和freq参数分别告诉我们分组的列和分组的频率。
将数据适当地分成批后,我们现在可以为每批数据调用session.logger(),传递dataset_timestamp来标记每个窗口的开始,并继续记录每批数据。
监控仪表板
在这个阶段,我们让我们的消费者监听发送的任何新记录,并将它们记录到我们的 whylogs 输出文件夹中,根据配置文件 .whylogs.yaml ,这个文件夹就是 whylogs-output 文件夹。剩下要做的是使用生成的统计数据来检查操作会话,寻找注入故障的迹象。
Whylogs 为我们提供了许多探索日志统计属性的方法。在这个例子中,我将把自己限制在内置的绘图上,这为我们提供了一种直观检查特性的方法,比如特性分布、缺失值和数据类型。
尽管要监控的主要遥测特征是 **GYROZ,**但有一种简单的方法来检查所有其余特征的曲线将是有用的。至于剩余特征,我们可能想要检查不同移动平均线的图。考虑到这些选项中的任何一个,我们都有 3 或 4 种可能感兴趣的图,将所有这些选项集中到一个交互式监控仪表板中会很有趣,用户可以在其中选择他感兴趣的特定图。在这个项目中,这是以一个 **iPython 小部件的形式完成的。**
在本文中,我将只展示小部件的输出,但是您可以在附带的 jupyter 笔记本 中查看完整的代码和详细的解释。在项目的存储库中,您会发现预先记录的信息已经准备好了,因此您可以直接跳到仪表板部分。只是一定要安装 ipywidgets:
python -m pip install ipywidgets
并启用小部件扩展:
jupyter nbextension enable --py widgetsnbextension
会话 0 —恒定增益故障
现在,让我们看一些输出示例。通过选择第一个会话,我们将看到 ROV 操作的日期和时间,以及遥测和残差图的不同选择:

作者图片
该会话有一个恒定增益传感器故障。从上图来看,单独评估 GYROZ 分布是困难的,因为不同的驾驶模式会导致不同的分布,这是正常的。不变的残值也不是很清楚。但是通过应用 3s 的移动平均线,我们肯定开始看到一些异常行为的迹象。在这种情况下,仅监控车辆的传感信息不足以检测故障。
会话 1 —停留在(零)
让我们继续我们的第二个例子,会话 1:

作者图片
在此阶段,注入了一个固定 0 故障。不幸的是,在这种情况下,遥测和残差分布都没有多大帮助。回归模型不够敏感,不足以捕捉故障。然而,数据类型图产生了一个有趣的结果。该特性的值通常是一个浮点数,但是 whylogs 将一系列恰好为 0 的值推断为一个整数序列,使其与众不同。
会话 2 —固定不变(最大)
第三个例子也是固定值,但固定在传感器的最大范围:

作者图片
这个很容易发现,因为最大值确实高于通常的分布。遥测和残差分布图都认为在 21:13 附近有异常。
第 3 部分—漂移和损失
为了避免重复,我不会显示漂移传感器故障的分布图,因为这种情况下产生的图与会话 0 非常相似。然而,应用于残差的缺失值图告诉我们一些关于注入损耗误差的信息:

作者图片
每当我们没有有效的先前时间步长时,残差就用nan值记录。因此,预计第一个会话的采样点会有缺失值。但是,当它们出现在运行的中间时,这是两个采样点之间经过了一段延长的时间( > 0.5s )的标志,表明出现了损失错误。尽管我们没有直接监控丢失错误,但我们可以在运行结束时看到它的存在,大约在 09:14 。
结论
在本文中,我想通过在一个特定的用例中应用它们来探索数据流和监控的可用工具:水下无人机的故障检测。尽管 MLOps 生态系统正在快速发展,但我觉得在生产中监控 ML 应用程序的解决方案仍然很少。这就是为什么像 whylogs 这样的开源工具是 ML 从业者工具集的一个非常受欢迎的补充。
在这个项目中,我们的回归模型远非完美,在许多情况下,预测本身不足以捕捉我们注入的故障条件。然而,通过设置一个简单的监控仪表板,我们能够通过同时查看输入和输出图来获得更广泛的视图,此外,我们还能够从许多不同的图中选择不同的特性。对于这个项目,在仪表板中只包含来自 whylogs 的内置图就足够了,但是我们可以通过包含每个完整会话的可用摘要的单独选项卡来添加更多信息。
至于应用程序本身,也有很大的扩展空间。在这个例子中,出于演示的目的,我们在本地使用了 Kafka,但是我确信对于一个生产环境,事情会变得更加复杂。也许在未来,我们可以让整队的遥控潜水器被自动监控故障!
暂时就这样吧!感谢您的阅读,如果您有任何问题或建议,请随时联系我们!
在看恐怖电影时分析你身体的葡萄糖反应
可穿戴设备将会继续存在。像 Fitbit 这样的设备现在拥有大约 3000 万活跃用户,比这个星球上活跃的软件开发者还多,而且在久而久之越来越受欢迎。

来自 Pexels 的 Cottonbro 摄影
可穿戴设备有各种形状和尺寸,可以执行各种功能。我有幸测试了 T4 Veri T5 公司的一种新设备,它能提供实时血糖数据。这让我想到:除了监测糖尿病等疾病,我们还能通过跟踪血糖水平获得什么好处?
但首先,这里有一些关于血糖监测设备的基础知识。是的,它确实有一根针。不,不是不方便。是的,它确实需要每 15 天更换一次,是的,它很舒服,也很容易使用。
你可能想知道为什么有人会在手臂上插一个针头传感器,但话说回来,这比记录你六年来的饮食习惯要少得多。(实际上,在可穿戴设备出现之前,有一些有趣的研究调查了有多少人在记录他们的习惯。)
了解葡萄糖和身体的胰岛素反应
理解这些数据需要相当多的知识。如果你对此感兴趣,我鼓励你做更多的研究。在这里,我将更多地关注数据是如何设计的,以及它如何作为一种数据集的例子,这种数据集可以促进对个体的更好的医学理解,并创造出“精确病人”,正如史蒂文·查拉普博士曾经称他们为“精确病人”。
这里你可以看到 Veri app UI 显示了我某个典型早晨的数据(这个数据其实是真实的)。如果你的第一反应是想知道工程单位 mg/dL 是什么,那就随便玩转换器吧。TLDR 版本设定为 70 毫克/分升约为 3.9 毫摩尔/升,100 毫克/分升约为 5.6 毫摩尔/升

图片由 Veri app 作者提供
根据梅奥诊所的说法,血糖水平低于 200mg/dl 属于正常范围,低于 100 mg/dl 属于正常空腹范围。
我从数据本身学到了什么?
我收到的数据实际上是相当重复的。我在想我可能会进行的各种食品工程实验,但无论我吃什么,我的代谢反应都是相似的,我的葡萄糖水平以一种非常可预测的方式波动——当我吃东西时,它们上升,当我禁食时,它们缓慢下降。但是,我还是得到了一些有价值的观察结果:
锻炼导致我的血糖水平出现有趣的波动。左侧的“峰值”实际上来自禁食期间的剧烈运动,而不是进食。
禁食期间的血糖变化让我对通过酮症分解脂肪如何影响我的血糖水平有了一些了解。了解酮症等功能背后的理论是一回事,但没有什么比看到自己血糖数据的起伏更重要的了。

图片由 Veri app 作者提供
对葡萄糖的营养影响是我最期待测量的,但正如你在左边的图表中看到的,它显示了当我在下午 6 点后吃东西(“适当的食物”)和晚上 8 点后再吃东西(精制糖)时,吃“适当的食物”实际上对葡萄糖水平的影响比锻炼小(尽管那些美味的精制糖确实导致了最大的峰值)。
你还能试验什么?
给我印象最深的是我的数据的重复性。当然,这完全取决于所讨论的人,并且在某人开始具有较大葡萄糖水平差异的情况下,该数据的效用将会高得多。然而,这让我想起了一个有趣的问题:我还能把这些数据和什么结合起来呢?
在我们最近与 Jim Guszcza 的讨论中,他指出,可穿戴设备可以通过向开发者开放数据来成倍增加其创造的价值,然后开发者可以将这些数据整合到新的应用中。这个案例也不例外。如果我能像看恐怖电影一样监控我的葡萄糖对外部压力的反应会怎么样?有影响吗?弄清楚会很有趣。其他生命体征呢,比如体温?我们能从由我们的综合健康数据组成的数据集中收集到什么?
开放平台放大价值
如果将行为数据、来自不同传感器的数据以及活动、媒体消费和营养数据结合起来,您会看到用例的数量增加了几个数量级。我确信有一个最佳间歇禁食时间表的用例,甚至可能有一个不同类型电影的理想小吃的用例。
你能想到什么类型的用例?让我们知道。
摆弄 Alexa 程序
原文:https://towardsdatascience.com/monkeying-around-with-alexa-routines-3f3f95795922?source=collection_archive---------13-----------------------
最后一种编程调用 Alexa 例程的方法
如果您正在寻找一种在代码中调用 Alexa 例程的方法,您已经找到了合适的文章。我将向你展示如何使用免费的 Alexa 应用程序语音猴,在任何程序和你的 Alexa 之间架起一座桥梁。
你为什么要这么做?
可能有大量有创意的用例,但我想到了两件事:
- 在代码中与支持 Alexa 的智能设备进行交互
- 在你的代码中使用你的 Alexa 作为某种语音提示系统
*这种方法最好的一点是它是语言不可知的。*一旦你设置好了,就只需要一个 GET 请求来调用你的 Alexa 程序。

Alexa 已经可以控制这台空调了…如果我能用树莓 Pi 或者无服务器功能之类的东西控制 Alexa 岂不是很酷?(图片由作者提供)
我的问题背景
当我在寻找用 Python 程序控制我的 Alexa 空调的方法时,我遇到了语音猴子。
我的“智能”空调应该将房间保持在一定的温度,但根据我的经验,它要么太冷,要么太热。我可以通过简单地与 Alexa 交谈来打开/关闭我的空调并设置温度——这使得调整事情更容易——但我与 Alexa 交谈的次数有点太多了。为什么不让程序替我做呢?
我有一个备用的 Raspberry Pi 来运行我的程序,连接到 DHT11 温度传感器来监控温度——我的计划是监控当前温度,并根据需要打开或关闭我的空调,以保持更可预测的温度。

我值得信赖的树莓 Pi 3 连接到 DHT11 传感器。这种便宜的小传感器可以测量精确到 1 摄氏度和 1%湿度的温度和湿度。(图片由作者提供)
似乎可行——但有一件事让这个计划有点困难。
如何通过树莓 Pi 与我的 AC 进行交互?
是的,也许我可以用电线把我的树莓皮接到我的交流电上,但是我真的没有摆弄物理电子设备的背景。我的 AC 带有一个遥控器——也许我可以用一个带有红外发射器的 Raspberry Pi 来重放来自 AC 遥控器的命令?这是可行的,但是记录和定义这些按钮的工作量很大。

您可以通过带有 Raspberry Pi 和红外增强器的红外遥控器来控制任何可控制的设备。我以前用上图的设置做过,但是设置、记录所有按钮的红外输出以及找到一个红外发射器可以清晰地看到你想要控制的设备的地方需要很多工作——这实际上是最后的手段。(图片由作者提供)
我的结论?我的 AC 是一款智能设备,内置了某种智能家居接口,最好利用这一点。制造商已经为我们做了大量工作来创建 Alexa 界面,所以我们就用它吧。
**但是我们如何与这款智能设备的 Alexa 界面互动呢?**目前还没有真正官方支持的 Alexa 方式来做到这一点。
浏览几年前的一些论坛,我看到一个建议,写一个程序来执行文本到语音转换,并将语音记录发送到 Alexa API。它可以让你调用任何你想要的 Alexa 命令,但这有点不方便,因为你需要一种方法来自动刷新你的亚马逊 cookies(根据我的经验,我每次都需要填写验证码)。
另一个建议甚至更笨拙——使用两个相邻的 Alexa 设备,并使用这个主动提供的 Alexa 文本到语音转换项目,让第一个 Alexa 设备说出你给它的任何命令,第二个 Alexa 设备实际上执行命令。这将是可行的——但它确实需要一个额外的 Alexa 设备,而且让这些东西一直互相说话肯定会很烦人。更不用说它可能会有点慢,而且不可靠(在命令过程中考虑像背景聊天这样的事情可能会把事情弄糟)。
如果你想在几年前做类似的事情,这将是你不得不忍受的地狱。
谢天谢地,我发现了一个更近的建议,使用语音猴子作为一个 API 来暴露你的 Alexa 例程,与其他选择相比,这似乎是显而易见的。
Alexa 例程是 Alexa 的一个功能,让你在你的 Alexa 生态系统中做类似 IFTTT(如果这个,那么那个)的事情。使用 Alexa 例程,您可以为一系列命令创建自己的快捷方式,这些命令可以链接到自定义触发器。

Alexa 例程可以让你为任何你想要的触发器串起一系列的 Alexa 命令。这是我从 Reddit 上重新创建的一个,我觉得很有趣。(图片由作者提供)
简单来说,它是如何工作的:语音猴子让你创造猴子,他们称之为“虚拟智能家庭门铃”
你可以把每只猴子想象成它自己独特的虚拟按钮(显示为 Alexa 支持的智能设备),可以通过向分配给猴子的 URL 发送 GET 请求来按下它。通过 Alexa 例程,你可以使用猴子作为 Alexa 例程的触发器(它可以调用你的 Alexa 支持的几乎任何功能)。很酷吧?

你可以看到我创造的猴子。每只猴子都被分配了一个唯一的 URL,我可以通过发送 GET 请求来触发它。Voice Monkey 为您提供了一个很好的触发测试来测试您的创作,还提供了一个游乐场部分,它提供了一个 UI 来定制您的 GET 请求。(图片由作者提供)

在你的 Alexa 程序中设置你的猴子为“当”触发器。这个 Alexa 例程现在可以通过 GET 请求调用。(图片由作者提供)
使用语音猴进行设置
设置起来相当容易。下面是语音猴快速入门指南提供的一些说明。
1.你首先需要使用 Alexa 应用程序启用语音猴子技能并链接你的亚马逊账户。
2.使用您用于链接技能的同一个亚马逊帐户登录到语音猴。
3.完成设置,然后转到“管理猴子”生成您的第一只猴子。
4.在 Alexa 应用中创建一个新的例程,以你的猴子为触发器(“当这发生时”>“智能家居”>“你的猴子名”)。
5.访问仪表板的游乐场部分,测试你的新猴子。
6.(可选)-要发布公告,您必须打开语音猴技能,作为您的例行程序中的最后一个动作。
一旦你完成了这个设置,你应该准备好启动你的猴子了!
尝试你的第一个请求
使用以下 URL 中的自定义值替换MY_ACCESS_TOKEN、MY_SECRET_TOKEN和MY_MONKEY_NAME,并尝试从浏览器导航到该 URL。
[https://api.voicemonkey.io/trigger?access_token=MY_ACCESS_TOKEN&secret_token=MY_SECRET_TOKEN&monkey=MY_MONKEY_NAME&announcement=hello%20world](https://api.voicemonkey.io/trigger?access_token=MY_ACCESS_TOKEN&secret_token=MY_SECRET_TOKEN&monkey=MY_MONKEY_NAME&announcement=hello%20world)
你应该看到你的猴子被触发了。如果你在你的 Alexa 例程中添加了语音猴子技能作为最后一个动作,你应该会听到 Alexa 说“你好,世界”。
现在,如果您想让您的程序向这个 URL 发送 GET 请求并进行自定义通知,您可以在 URL 中的announcement参数中填入您喜欢的任何内容——只需确保使用编码 URL 中空格字符的%20进行分隔。
例如在 Python 中
import requestsdef format_url(s):
return s.split().join('%20')url = '[https://api.voicemonkey.io/trigger?access_token=MY_ACCESS_TOKEN&secret_token=MY_SECRET_TOKEN&monkey=MY_MONKEY_NAME&announcement=](https://api.voicemonkey.io/trigger?access_token=MY_ACCESS_TOKEN&secret_token=MY_SECRET_TOKEN&monkey=MY_MONKEY_NAME&announcement=hello%20world){}'temp = 72 # This might change--we want to Alexa to announce itannouncement = format_url(f'Temperature is {str(temp)}')
requests.get(url.format(announcement))
结论
既然你已经听说过语音猴子,如果你想玩一些与你的定制程序挂钩的 Alexa 例程,就去试试吧。
在撰写本文时,Voice Monkey 对多达 300 只猴子完全免费。这是 300 个独特的 Alexa 例程,你可以通过语音猴 API 暴露。
提醒一句:虽然 Voice Monkey 现在是免费的,但它是一项需要提供商付费运营的服务。
根据文件
语音猴子是免费的,而在测试阶段,我们计划让它尽可能长时间免费。
我们很乐意永久免费提供这项服务,但幕后存在基础设施和维护成本。
我们将尽可能保持免费,如果需求给成本带来太大压力,如果我们引入定价结构,我们会让你知道。
不管未来的价格结构如何,现在都可以免费试用和修改!
请注意,我不以任何方式隶属于语音猴,不能保证任何可靠性,他们的服务正常运行时间。这不是一个付费广告,我只是在展示我个人使用案例中喜欢使用的一项服务。
自动驾驶中使用变压器的单目 BEV 感知
原文:https://towardsdatascience.com/monocular-bev-perception-with-transformers-in-autonomous-driving-c41e4a893944?source=collection_archive---------0-----------------------
截至 2021 年末的学术文献和行业实践回顾
更新:
- 添加 DETR3D,2021/11/07
- 添加 STSU,将图像转换成地图,2021/12/27
量产级自动驾驶需要可扩展的世界三维推理。随着自动驾驶汽车和其他交通代理在道路上移动,大多数时候推理不需要考虑高度,这使得鸟瞰图(BEV)成为一种足够的表示。

传统自动驾驶堆栈的极度简化架构(图片由作者提供)
上图说明了一个传统的自动驾驶栈(为了简单起见,这里省略了本地化等很多方面)。在此图中,圆圈代表功能模块,并根据它们所在的空间进行颜色编码。绿色模块发生在 2D,蓝色模块发生在 BEV。只有相机感知发生在 2D 空间,或者更准确地说,在获得机载相机图像的透视空间 e。它依赖于传感器融合和大量手工制作的规则来将 2D 检测提升到 3D,可选地借助于来自雷达或激光雷达的 3D 测量。
这里我说传统至少有两个原因。首先,相机感知仍然发生在透视空间中(与最近的单目 3D 物体检测趋势相反,可以在这里找到对其的回顾)。第二,来自多模态传感器的结果以后期融合的方式进行融合(与早期融合相反,早期融合中传感器数据被送入神经网络进行数据驱动的关联和几何推理)。
BEV 感知是相机感知的未来
该图暗示,对于唯一的异常值(摄像机感知)来说,转向 BEV 将是非常有益的。首先,直接在 BEV 中执行相机感知可以直接与来自雷达或激光雷达等其他设备的感知结果相结合,因为它们已经在 BEV 中表示和使用。BEV 空间中的感知结果也容易被下游组件使用,例如预测和规划。第二,单纯依靠手工制作的规则将 2D 观测提升到 3D 是不可扩展的。BEV 表示有助于过渡到早期融合管道,使融合过程完全由数据驱动。最后,在只有视觉的系统中(没有雷达或激光雷达),在 BEV 中执行感知任务几乎成为强制,因为在传感器融合中没有其他 3D 提示可用于执行这种视图转换。
一年前的 2020 年末,我写了一篇评论博文,总结了学术界关于单眼 BEV 感知的论文。该领域研究如何将单目图像提升到 BEV 空间进行感知任务。从那以后,我一直在更新我读过的更多的文章,以保持这篇博文的更新和相关性。这个领域的范围已经从语义分割稳步扩展到全景分割、对象检测,甚至其他下游任务,如预测或规划。
在过去的一年里,单眼 BEV 感知出现了三种方法。
- IPM: 这是基于平地假设的简单基线。Cam2BEV 也许不是第一部这样做的作品,但却是最近的相关作品。它使用 IPM 来执行特征变换,并使用 CNN 来校正不在 2D 路面上的 3D 对象的失真。
- Lift-splat :利用单深度估计提升至 3D,并在 BEV 上进行 splat。这股潮流是由Lift-Splat-shot发起的,后续还有 BEV-Seg 、 CaDDN 、 FIERY 等诸多作品。
- MLP :使用 MLP 为视图转换建模。这一行是由 VPN 发起,渔网,以及HDP mapnet跟进。
- 变形金刚:使用基于注意力的变形金刚来模拟视图变换。或者更具体地说,基于交叉关注的变压器模块。这一趋势开始显示出最初的牵引力,因为自 2020 年年中以来,至少到目前为止,截至 2021 年底,《变形金刚》席卷了计算机视觉领域。
在这篇评论性的博文中,我将关注最后一个趋势——使用变形金刚进行视图转换。
几乎具有讽刺意味的是,许多文献中的论文,有些是在 CV 中的变形金刚兴起之前,有些是在最近的这波浪潮中,将它们的专用视图转换模块称为“视图变形金刚”。这使得在文献中寻找那些确实使用注意模块进行观点转换的人变得更加困难。
为了避免混淆,在这篇博文的后面,我将使用大写的变形金刚来指代基于注意力的架构。也就是说,使用变形金刚通过提升图像到 BEV 来执行视图转换似乎是一个很好的双关语。
使用变压器查看变换
变形金刚的一般架构已经在许多其他博客中进行了广泛的解释(例如著名的插图变形金刚),因此我们在此不再赘述。由于全局注意机制,变形金刚更适合执行视图变换的工作。目标域中的每个位置到源域中的任何位置具有相同的距离,克服了 CNN 中卷积层的局部受限感受野。
交叉关注与自我关注

《变形金刚》中交叉注意力和自我注意力的运用(来源)
变压器中有两种关注机制,编码器中的自关注和解码器中的交叉关注**。它们之间的主要区别是查询 Q。在自我注意中,Q、K、V 输入是相同的,而在交叉注意中,Q 与 K 和 V 在不同的域中。**
正如我之前的博客中所详述的,关注模块的输出形状与查询 q 相同,就此而言,自我关注可以被视为原始特征域中的特征助推器,而交叉关注则可以被视为跨域生成器。
****交叉注意的想法其实是最初的注意机制,甚至早于变形金刚的创造。注意机制在 ICLR 2015 年的论文“通过联合学习对齐和翻译的神经机器翻译”中首次提到。原始 NeurIPS 2017 Transformer 论文“注意力是你所需要的全部”的更具创新性的贡献实际上是用自我注意力模块取代了双向 RNN 编码器。这也许是为什么许多人在提到交叉注意力时仍然更喜欢注意力这个词而不是变形金刚的原因。更多精彩的叙述请见这里。
交叉注意力是你所需要的
《变形金刚》在 CV 中的许多最新进展实际上只是利用了自我关注机制,比如被大量引用的 ViT ( 一幅图像值 16x16 个字:变形金刚在比例上的图像识别,ICLR 2021)或者 Swin Transformer ( 使用移位窗口的分级视觉变形金刚,Arxiv 2021/03)。它们作为主干特征提取器的增强。然而,考虑到在大规模生产车辆上典型的资源有限的嵌入式系统中部署通用变压器架构的困难,自我关注相对于得到良好支持的 CNN 的增量好处可能难以证明。在我们看到一些突破性的自我关注超过 CNN 之前,专注于 CNN 的行业应用(如量产自动驾驶)将是一个明智的选择。
另一方面,交叉注意有更充分的理由。将交叉注意力应用于计算机视觉的一项开创性研究是 DETR ( 用变形金刚进行端到端的物体检测,ECCV 2020)。DETR 最具创新性的部分之一是基于称为对象查询的固定数量槽的交叉注意力解码器。不同于原始的 Transformer 文件,其中每个查询被一个接一个地(自动回归地)馈送到解码器,这些查询被并行地(同时地)馈送到 DETR 解码器。查询的内容也是学习的,并且除了查询的数量之外,不必在训练之前指定。这些查询可以被视为一个空白的、预先分配的模板,用于保存对象检测结果,而交叉注意解码器负责填补空白。

DETR 的交叉注意力解码器部分可以看作是一个跨域生成器(来源)
这引发了使用交叉注意解码器进行视图转换的想法。输入视图被馈入特征编码器(基于自我关注或基于 CNN),编码后的特征作为 K 和 v,目标视图格式的查询 Q 可以被学习,只需要被光栅化为模板。Q 的值可以与网络的其余部分一起学习。

DETR 架构可适用于 BEV 转型(图片由作者提供)
在接下来的会议中,我们将回顾一些最相关的工作,并且我们还将深入探讨由 Andrej Karpathy 在特斯拉 AI 日 (08/20/2021)分享的变压器在特斯拉 FSD 中的使用。
PYVA (CVPR 2021)
PYVA ( 专注地投影您的视图:通过交叉视图变换进行单目道路场景布局估计,CVPR 2021)是第一个明确提到交叉注意力解码器可用于视图变换以将图像特征提升到 BEV 空间的项目。与早期的单目 BEV 感知工作类似,PYVA 对转换后的 BEV 特征执行道路布局和车辆分割。

PYVA 的架构使用了 MLP 和交叉关注(来源)
PYVA 首先使用 MLP 将透视空间中的图像特征 X 提升到(所要求的)BEV 空间中的 X’。第二 MLP 将 X’映射回图像空间 X”,并使用 X 和 X”之间的循环一致性损失来确保该映射过程保留尽可能多的相关信息。
PYVA 使用的 Transformer 是一个交叉注意模块,查询 Q 要映射到 BEV 空间中的 BEV 特征 X’,V 和 K 都是透视空间中的输入 X(如果忽略透视空间中 X 和 X”的区别)。
注意,在 BEV 空间中没有对 X '的显式监管,而是由 BEV 空间中的下游任务损失隐式监管。
在 PYVA 中,似乎是 MLP 做了视图转换的繁重工作,而交叉注意用于增强 BEV 中被提升的特征。然而,由于对 BEV 空间中生成的查询没有明确的监督,从技术上来说,很难将这两个组件的贡献分开。对此进行消融研究将有助于澄清这一点。
整洁(ICCV 2021)
NEAT ( 端到端自动驾驶的神经注意力场,ICCV 2021)在使用基于 MLP 的迭代注意力将图像特征提升到 BEV 空间之前,使用变压器来增强图像特征空间中的特征。本文的目标是可解释的、高性能的、端到端的自动驾驶,但我们在这里将只关注可解释的中间 BEV 表示的生成。

整洁的建筑(来源)
编码器模块中使用的转换器基于自我关注。作者还承认“变压器可以从我们的编码器中移除,而不会改变输出维度,但我们将它包括在内,因为它根据我们的消融研究提供了改进”。正如我们上面所讨论的,配备自我关注模块的编码器可以被视为一个美化的主干,这不是本研究的重点。
最有趣的部分发生在神经注意力场(NEAT)模块。对于给定的输出位置(x,y),使用 MLP 将输出位置和图像特征作为输入,生成与输入特征图像具有相同空间维度的注意图。注意力图然后用于点积原始图像特征,以生成给定输出位置的目标 BEV 特征。如果我们遍历所有可能的 BEV 网格位置,那么我们可以将 NEAT 模块的输出平铺到一个 BEV 特征图**。**

交叉注意模块 vs 神经注意场模块(图片由作者提供)
这个简洁的模块非常类似于交叉注意机制。主要区别在于 Q 和 K 之间的相似性度量步骤被 MLP 代替。这里我们忽略了其他一些小细节,例如 Softmax 运算,以及值 v 的线性投影。从数学上讲,我们有以下 MLP、交叉注意力和整洁的公式。

MLP、交叉注意力和整洁的区别(改编自来源)
符号约定遵循我之前关于 MLP 和变形金刚的区别的博文。简而言之,很明显,NEAT 保持了交叉注意机制的数据依赖性,但它不再具有交叉注意的排列不变性。
为了清楚地与交叉注意机制进行比较,在上面的讨论中省略了一个细节。在 NEAT 的实现中,MLP 的输入不是完全成熟的图像特征 c,而是不具有任何空间范围的全局汇集的 c_i。采用迭代注意。作者认为,把比 c_i 维数高得多的图象特征 c 送入 MLP 要复杂得多。也许 MLP 的一次通过不足以补偿空间内容的损失,因此需要多次通过。本文没有提供这种设计选择的消融研究。
解码器部分还使用 MLP 来生成所查询位置(x,y)的期望语义含义。如果我们将净输出平铺到 BEV 特征图中,以特定位置的特征和位置坐标作为输入的 MLP 相当于 BEV 特征图上的 1x1 卷积,其中(x,y)连接到特征图。这个操作和 CoordConv (NeurIPS 2018)非常相似。这是利用 BEV 特征图进行下游 BEV 感知任务的相当标准的做法。我们甚至可以超越 1x1 卷积,通过堆叠的 3x3 卷积进一步提高性能,以增加 BEV 空间中的感受域。
总之,NEAT 使用交叉注意力的变体(MLP 来代替相似性度量)来将相机图像提升到 BEV 空间。
STSU (ICCV 2021)
STSU ( 车载图像结构化鸟瞰交通场景理解,ICCV 2021)采用稀疏查询进行目标检测,沿袭了 DETR 的做法。STSU 不仅能检测动态物体,还能检测静态道路布局。这是同一作者的 BEV 特征拼接的后续工作,在我的另一个博客中评论了一篇关于 BEV 语义分割的论文。

STSU 的建筑(来源)
STSU 使用两组查询向量,一组用于中心线,一组用于对象。最有趣的是它对结构化道路布局的预测。车道分支包括几个预测头。
- 检测头预测由某个查询向量编码的通道是否存在。
- 控制头预测 R 贝塞尔曲线控制点的位置。
- 关联头预测用于聚类的嵌入向量。
- 关联分类器接受 2 个嵌入向量,并判断中心线对是否关联。
贝塞尔曲线非常适合中心线,因为它允许我们用固定数量的 2D 点来建模任意长度的曲线。
在 LSTR ( 利用变压器进行端到端车道形状预测,WACV 2011)中也使用了用于车道预测的变压器,其仍然在图像空间中。结构化的道路布局预测也可以在 HDMapNet (一个在线高清地图构建与评估框架,CVPR 2021 工作坊)中找到,没有使用变形金刚。
DETR3D (CoRL 2021)
DETR3D 、T21(通过 3D 到 2D 查询从多视图图像进行 3D 对象检测,CoRL 2021)也使用稀疏查询进行对象检测,遵循 DETR 的实践。类似于 STSU,但 DETR3D 侧重于动态对象。查询在 BEV 空间中,并且它们使得 DETR3D 能够直接在 BEV 空间中操纵预测,而不是对图像特征进行密集变换。

DETR3D 的架构(来源)
BEV 感知优于 mono3D 的一个优势在于相机重叠区域,在该区域中,物体更有可能被相机视野剪切。Mono3D 方法必须基于来自每个摄像机视点的有限信息来预测每个摄像机中的裁剪对象,并依赖全局 NMS 来抑制冗余框。DETR3D 专门评估了图像边界(约占整个数据集的 9%)处的这种裁剪对象,并发现 DETR3D 比 mono3D 方法有显著的改进。这在特斯拉 AI 日也有报道。

多凸轮预测优于单凸轮结果(来源)
DETR3D 使用了几个技巧来提高性能。首先是对象查询的迭代优化。本质上,预测 BEV 中的 bbox 中心被重新投射回具有摄像机变换矩阵(内部和外部)的图像,并且多摄像机图像特征被采样和集成以改进查询。这个过程可以重复多次(本文中为 6 次)以提高性能。
第二个技巧是使用预训练的 mono3D 网络主干来提高性能。对于基于变形金刚的 BEV 感知网络来说,初始化似乎非常重要。
将图像转换成地图(2021/10,Arxiv)
将图像转换成贴图注意到,无论图像像素的深度如何,图像(图像列)中的垂直扫描线与穿过 BEV 贴图中摄像机位置的极线之间都存在 1–1 的对应关系。这类似于 OFT (BMVC 2019) 和 PyrOccNet (CVPR 2020) 的想法,沿着投射回 3D 空间的光线在像素位置涂抹特征。

在列方向使用轴向交叉注意力转换器和在行方向使用卷积大大节省了计算。
特斯拉的方法
在 2021 年的特斯拉 AI 日,特斯拉揭示了为特斯拉 FSD 提供动力的神经网络的许多复杂的内部工作原理。最有趣的构建模块之一是一个被称为“图像到 BEV 转换+多相机融合”的模块。这个模块的中心是一个转换器模块,或者更具体地说,是一个交叉关注模块。

特斯拉的 FSD 架构(来源)
你初始化一个你想要的输出空间大小的栅格,你用输出空间中的正弦和余弦的位置编码来平铺它,然后这些用 MLP 编码成一组查询向量,然后所有的图像和它们的特征也发出它们自己的键和值**,然后查询键和值馈入多头自我注意(作者注:这实际上是交叉注意)。**
— Andrej Karpathy,2021 年特斯拉人工智能日,来源
虽然 Andrej 提到他们使用了多头自我注意,但他描述的显然是一种交叉注意机制,他幻灯片中右边的图表也指向了最初变形金刚论文中的交叉注意块。
这个视图转换中最有趣的部分是 BEV 空间中的查询。它由 BEV 空间中的栅格生成(空白、预分配的模板,如在 DETR),并与位置编码(PE)连接。还有一个上下文概要**,使用位置编码平铺显示。该图没有显示如何生成上下文摘要并与位置编码一起使用的细节,但我认为有一个全局池,它折叠透视空间中的所有空间信息,以及一个平铺操作,它将这个 1x1 张量平铺在预定义的 BEV 网格上。**

使用交叉注意力转换器的图像到 BEV 转换的构建块和张量形状(图片由作者提供)
在上图中,我根据我的理解,列出了视图转换模块中更详细的块(圆圈)和相应的张量及其形状(正方形)。BEV 空间中的张量颜色编码为蓝色,核心交叉注意模块颜色编码为红色。希望这能帮助学术界感兴趣的读者在这个方向上更深入地挖掘。
关于变形金刚 vs MLP 的最后一句话
**
(将图像提升到 BEV 空间)是数据相关的,很难对此组件进行固定的转换,因此为了解决这个问题,我们使用转换器来表示这个空间。—安德烈·卡帕西,2021 年特斯拉人工智能日
Andrej 还提到视图转换问题依赖于数据,他们选择了转换器。关于交叉注意在张量整形中的详细用法及其与 MLP 的区别,在我之前的博客中有详细介绍,有一些数学细节和具体图解。它还强调了为什么变压器的张量整形是数据依赖的,而 MLP 不是。
外卖食品
- 变形金刚在学术界和工业界越来越受欢迎,用于视图转换。
- 正如在我之前的博客中所讨论的,尽管《变形金刚》的数据依赖性使其更具表现力,但这也使其难以训练,MLP 的盈亏平衡点可能需要大量的数据、GPU 和工程努力。
- 在大规模生产的自动驾驶汽车中,在资源有限的嵌入式系统中部署变压器也可能是一个重大挑战。特别是,当前的神经网络加速器或 GPU 针对卷积神经网络(例如,3x3 卷积)进行了高度优化。
承认
我和一谷进行了几轮讨论,他目前正在澳门大学做博士研究。我们的讨论促使我重新审视单眼 BEV 感知领域的最新趋势。
参考
整洁:端到端自动驾驶的神经注意场,ICCV 2021
PYVA :用心投射你的视角:通过交叉视角变换的单目道路场景布局估计,CVPR 2021
特斯拉人工智能日于 2021 年 8 月 20 日在 Youtube 上直播
CaDDN :用于单目 3D 物体检测的分类深度分布网络,CVPR 2021 口述
:从周围单目摄像机鸟瞰未来实例预测,ICCV 2021
BEV-Seg :利用几何和语义点云进行鸟瞰语义分割,CVPR 2020 研讨会
高清地图网:在线高清地图构建与评估框架,CVPR 2021 研讨会
DETR :用变形金刚进行端到端的物体检测,ECCV 2020
注意力是你所需要的全部,NeurIPS 2017
联合学习对齐和翻译的神经机器翻译,ICLR 2015
ViT :一张图像抵得上 16x16 字:大规模图像识别的变形金刚,ICLR 2021
Swin 变换器:使用移位窗口的分级视觉变换器,Arxiv 2021/03
CoordConv: 卷积神经网络的一个耐人寻味的失败和 CoordConv 解决方案,NeurIPS 2018
STSU :车载图像结构化鸟瞰交通场景理解,ICCV 2021
DETR3D :通过 3D 到 2D 查询从多视图图像中检测 3D 对象,CoRL 2021
将图像翻译成地图 ,Arxiv 2021/10**
Python 中一元和多元函数的蒙特卡罗积分
原文:https://towardsdatascience.com/monte-carlo-integration-in-python-over-univariate-and-multivariate-functions-12615dd252fa?source=collection_archive---------9-----------------------
实践教程
如何用蒙特卡罗方法近似积分复变函数

由杰斯温·托马斯在 Unsplash 上拍摄的照片
蒙特卡罗积分是一种基本的蒙特卡罗方法,用于数值估计函数 f(x) 的积分。我们将在这里讨论这个理论以及 Python 中的例子。
理论
假设我们要求解域 D 上 f(x) 的积分。

在单变量函数(即具有一个变量)的情况下,域只是一维的,积分是从 a 到 b 。
我们可以重新排列一些项,将上面的等式表示为:

换句话说,积分等价于求 g(x) 的期望值,这里我们已经定义了 g(x)=f(x)/p(x) 在一个定义域上。我们可以通过从域中采样xN 次来近似这一点,这意味着,

如果我们从均匀分布中取样,假设函数是一元的,这意味着抽取任何给定的 x 的概率就是 p(x)=1/(b-a) 。如果我们将其代入上述近似表达式,我们会看到,

这实际上是在区间 a 到 b 上计算 f(x) 的平均值,并乘以区间的长度。换句话说,我们正在寻找一个矩形的面积,宽度=区间宽度,高度=预期值 f(x) 。
这也适用于任何尺寸。在一元函数的情况下,定义域只是一条线(即 b-a )。对于二元函数,定义域就是面积。一般来说,

这意味着我们可以通过将区域体积乘以区域上函数的期望值来近似积分。
示例|单变量
例如,在 Python 中,我们可以执行以下操作来近似从-2 到 2 的 f(x)=x 的积分。
我们得到以下结果,

其中蒙特卡罗近似非常接近解析解。从视觉上看, f(x)=x 从-2 到 2 的积分如下图蓝色所示。近似值是用红色突出显示的矩形。

用红色显示 f(x)的近似值。作者图片
示例|多元
我们也可以对多元函数进行积分。程序和以前一样。然而,我们现在需要在一个更高维的域上采样,而不是在一条线上采样(从 a 到 b )。为简单起见,我们将说明一个多元函数在一个域上的积分,对于每个变量有相同的 a 和 b 。这意味着在具有两个变量(x1 和 x2)的函数中,定义域是正方形的;对于三个变量的函数,立方体形状。
结果呢

示例|其他域上的多元积分
在其上执行积分的域可能更复杂,并且难以从中采样和计算其体积。例如,我们可以在圆形区域而不是方形区域上对二元函数进行积分。尽管如此,想法是相同的——对于均匀采样,我们希望在整个区域内进行采样,并通过区域体积和区域内函数期望值的乘积来近似积分。
让我们使用相同的二元函数 f(x)=10-x1 -x2 并在单位圆上积分。精确地在单位圆上均匀采样比只在正方形区域(覆盖单位圆)上采样更难。由此,我们可以 1)将域的面积计算为采样正方形的面积与域内采样点的比例的乘积,2)将期望值计算为域内采样点的平均值 f(x) 。下面是采样的可视化(单位圆内的采样点以绿色显示),

采样时单位圆内的数据点以绿色显示(区域)。作者图片
在 Python 中,这看起来像,
结果是,

作为最后一个例子,我们也可以对多元概率分布进行积分。让我们在单位圆上积分下面的多元正态分布,

在哪里

我们用 Python 定义这些,并执行蒙特卡罗积分,
作为确认,我们使用能够在椭球(包括圆)上积分多元正态分布的pmvnEll函数。
library(shotGroups)
pmvnEll(r=1, sigma=rbind(c(1,0.8,0.5), c(0.8,1,0.8), c(0.5,0.8,1)),
mu=c(0,0.5,1), e=diag(3), x0=c(0,0,0))
由于结果彼此非常匹配,

附加注释
显然,提供的例子很简单,有些例子有针对特定情况的分析解决方案和/或 Python/R 包。但是它们有助于理解蒙特卡罗积分背后的机制。显然,所描述的蒙特卡罗方法很容易推广到没有封闭形式解的更复杂的函数。此外,还有许多更优化的抽样方法(如分层抽样、重要性抽样等),如果有兴趣,我们鼓励读者深入阅读这些主题。
额外资源
https://www . scratchpapixel . com/lessons/mathematics-physics-for-computer-graphics/Monte-Carlo-methods-in-practice/Monte-Carlo-integration
https://towards data science . com/Monte-Carlo-integration-in-python-a71a 209d 277 e
原载于 2021 年 1 月 13 日https://boyangzhao . github . io。
蒙特卡洛马尔可夫链(MCMC),解释说
原文:https://towardsdatascience.com/monte-carlo-markov-chain-mcmc-explained-94e3a6c8de11?source=collection_archive---------0-----------------------
理解使用随机性估计复杂实体背后的魔力

来源:图片由来自 Pixabay 的埃里克·斯坦拍摄
MCMC 是贝叶斯统计中最重要和最流行的概念之一,尤其是在进行推理时。
从更大的角度来看,有时在高维空间中估计推理在计算上可能变得不可行,在这种情况下,我们求助于对其进行近似——或者通过使用 采样方法 (其中之一是 MCMC )或者用 参数化分布 ( 变分推理)对其进行近似。
在本帖中,我们将通过举例来剖析和理解 MCMC 的组件来讨论它。在这篇文章的后面,我们将会看到一个融合了这个概念的算法, Metropolis-Hasting 算法。
事不宜迟,让我们开始吧!
注:标有“附加内容”的小节可以跳过,不会影响对概念的总体理解。
采样
抽样是一种通过将人口的一个子集纳入研究来近似估计整个人口的某些特征的方法。
采样有各种各样的用例—
- 它可以用来近似一个棘手的总和或积分。
- 它可以用于在估计易处理但昂贵的和或积分时提供显著的加速。
- 在某些情况下,如密度估计,它可以简单地用于近似概率分布,然后估算缺失数据。
少数抽样技术— 祖先抽样、逆变换抽样、拒绝抽样、重要性抽样、蒙特卡罗抽样、MCMC 抽样。
在这篇文章中,我们只关注 MCMC 方法,其他方法将在另一篇文章中讨论。
介绍
MCMC 方法是使用马尔可夫链来执行蒙特卡罗估计的一系列算法。
这个名字给了我们一个提示,那就是它由两部分组成——
蒙特卡洛和马尔可夫链。让我们分别地和以它们的组合形式来理解它们。
蒙特卡罗抽样
(直觉上)
蒙特卡洛法得名于摩纳哥的蒙特卡洛赌场。
这是一种从概率分布中取样并使用这些样本来逼近所需数量的技术。换句话说,它使用随机性来估计某个确定性的感兴趣的量。
示例:如果要求我们计算下图中给定曲线的曲线下面积,可能需要对复杂的解析公式进行积分。
然而,使用蒙特卡罗方法,我们将在矩形中随机生成红点
(点越多越准确),并计算落在曲线下的点与落在整个矩形中的点的比率——该比率将为我们提供面积,给定矩形的面积。

(图片由作者提供)-蒙特卡洛法估算曲线下的面积
基本上,如果计算某个量具有复杂的分析结构,我们可以简单地执行模拟来生成大量样本,并使用它们来近似该量。这些工作假设渐近地遵循中心极限定理。
它在风险分析、可靠性分析等方面有许多其他用例。
(数学上)
假设我们有期望值要估计,这可能是一个非常复杂的积分,甚至难以估计——使用蒙特卡罗方法,我们通过对样本求平均值来近似这些量。

要计算的原始期望值

通过刺激 f(x)的大样本产生的近似期望
计算大量样本的平均值可以减少标准误差,并为我们提供一个相当准确的近似值。
这种方法有一个局限性,因为它假设很容易从一个概率分布中抽样,然而这样做并不总是可能的。有时,我们甚至不能从分布中取样。在这种情况下,我们利用马尔可夫链从一个棘手的概率分布中有效地进行采样。”
马尔可夫链
在进入 马氏链 之前,让我们先看一下定义它的有用性质——
马尔可夫性质:

椭圆形中的实体是状态
从上面的图像中,考虑一个由 4 个状态组成的系统—
下雨或洗车导致地面潮湿接着*【地面潮湿】导致【打滑】。*
马尔可夫性质只是做了一个假设——从一个状态跳到下一个状态的概率只取决于当前状态而不取决于导致这个当前状态的先前状态的序列。
如果我们要计算某人滑倒的概率,知道地面是否潮湿就提供了足够的证据来估计它。我们不需要知道导致它的状态(“下雨”或“洗车”)。
数学上来说:

截断分布的马尔可夫性质
从数学方程中可以明显看出,马尔可夫性质假设可能会节省我们大量的计算。
事后看来,如果一个过程展现出 马尔可夫性质 ,那么它就被称为
马尔可夫链。
现在我们已经看到了马尔可夫链,让我们来讨论使它如此令人满意的性质— 平稳分布。
平稳分布:
假设,我们有一个很少状态的过程,并且我们有一个固定的状态间转移概率 (Q) 。
我们从时间步 i 的所有状态上的一些随机概率分布( Sᵢ )
开始,为了估计下一个时间步 i+1 的所有状态上的概率分布,我们将其乘以转移概率 Q 。

如果我们继续这样做,过一会儿 S 在与矩阵 Q 相乘时停止变化,这就是我们说它已经达到了 平稳分布。

已达到稳定分布
让我们看一个例子——
在这个例子中,我们有 3 个州(X₁、X₂、X₃)

状态之间的转移概率( T
如果我们处于状态 S₂ ,留在 S₂ 的概率是 0.1,
转换到状态 S₁ 的概率是 0,转换到状态 S₃ 的概率是 0.9(从矩阵的第二行可以明显看出)。
让我们从向量 Sᵢ 的某个随机值开始(向量显示在任何特定时间步处于每个状态的概率),我们可以看到向量如何总计为 1。


应用后 Sᵢ₊₁ = Sᵢ*Q
如果我们继续按时间步长移动,最终我们会到达静止状态,

稳定分布
现在最需要知道的是,这个平稳分布不依赖于初始状态,你可以尝试不同的初始状态 Sᵢ 。
import numpy as npQ = np.matrix([[0,1,0],[0,0.1,0.9],[0.6,0.4,0]])
S_initial = np.matrix([[0.3, 0.4 , 0.3]])
epsilon = 1while epsilon > 10e-8:
S_next = np.dot(S_initial, Q)
epsilon = np.sqrt(np.sum(np.square(S_next - S_initial)))
S_initial = S_nextprint(S_initial)
平稳分布表示在任何给定时间处于任何状态的概率。
直观地说马尔可夫链可以被认为是在链上行走,给定特定步骤的状态,我们可以通过查看下一步的“状态概率分布”来决定下一个状态。
好了,现在我们已经看到了马氏链和蒙特卡洛,让我们把我们的重点放在这些美丽的组合形式,即 mcmc。
临时演员—
任何好奇并想了解为什么马尔可夫链收敛于平稳分布的人都可以参考下图——

来源:@book{Goodfellow-et-al-2016,title=,作者= ,出版商=,注= { \ URL { http://www . Deep Learning book . org } },年份={2016}}
马尔可夫链蒙特卡罗(MCMC)
MCMC 可以用来从任何概率分布中抽样。大多数情况下,我们用它来从难以处理的后验分布中进行抽样,以达到推断的目的。
使用 Bayes 估计后验概率有时会很困难,在大多数情况下,我们可以找到可能性 x 先验的函数形式。然而,计算边缘化概率 P(B)在计算上可能是昂贵的,尤其是当它是连续分布时。

(图片由作者提供)具有离散边际概率的贝叶斯定理
这里的技巧是完全避免计算归一化常数。
该算法的总体思路是从一些随机概率分布开始,逐渐向期望的概率分布移动。
听起来很简单,但是我们怎么做呢?
启动一个状态随机概率分布的马尔可夫链,在链中逐渐向稳定分布收敛,应用一些条件(详细资产负债表)确保该稳定分布类似于期望的概率分布。
因此,在达到平稳分布时,我们已经逼近后验概率分布。

详细的资产负债表状况
概率 p(A)表示在 A 的概率,概率 T(A → B)表示从 A 移动到 B 的概率。
概率 p(B)代表在 B 的概率,概率 T(B → A)代表从 B 移动到 A 的概率。
每条边代表从 A 到 B 或者 B 到 A 的概率流
如果条件满足,那么它保证稳态近似代表后验分布。
尽管 MCMC 本身很复杂,但它们提供了很大的灵活性。它为我们提供了高效的高维采样。它可以用来解决大状态空间的问题。
限制 — MCMC 在逼近多模态概率分布时表现不佳。
临时演员—
在这种情况下,边际概率 P(B)是一个称为归一化常数的常数,它对分子 的所有可能值求和。
存在用于训练或评估具有难以处理的归一化常数(也称为配分函数)的模型的技术。他们很少在算法中使用 MCMC 进行采样。
例如— 对比散度(CD) 对于训练像受限玻尔兹曼机这样的非结构化图形模型很有用。
Metropolis —加速算法
假设我们从分布 p(x) = f(x) / Z 中采样,其中 Z 是难以处理的归一化常数。
我们的目标是以这样一种方式从 p(x)中抽样,即只利用分子而避免估计分母。
(求婚概率)
我们先来看一下提议概率(g) 。
给定一个样本,它建议我们用马尔可夫链中的下一个潜在样本。
(如何决定是接受还是拒绝潜在样本,我们将在下一节中看到)。

(图片由作者拍摄)—提案分发
假设 g(X₂ | X₁) =正态(x₁σ)
***(***它可能是任何分布,为简单起见我们选择了正态分布 )
保持 X₁均值,我们做一个正态分布。然后我们从这个分布中抽取 X₂
作为样本。
我们重复同样的步骤对 X₃进行采样,保持 X₂为平均值。
(主算法)
让我们从详细的资产负债表条件开始这个算法。

详细的资产负债表
从 X₁过渡到 X₂的可能性可以被视为一个两步走的过程,考虑到我们在 X₁州—
第一步是用一些
的提议概率 g 做出 X₂的提议(上一节讨论过)。
第二步是接受新状态 X₂用某种
接受概率
将转移概率代入方程…

用 f(x)/Z 代替 p(x),两边的 Z 被抵消,我们得到…

重新构建等式会导致

替代速记符号


最后,我们得到接受概率 A****

接受概率
总结一下—
- 我们从一个随机状态开始。
- 基于提议概率(g ),我们随机选择一个新的状态。
- 计算提议的新状态的接受概率(A)。
- 掷硬币,正面着地的概率等于接受概率,如果硬币正面着地,接受样品,否则拒绝。
- 重复这个过程一段时间。
我们保持长时间的采样,并丢弃最初的几个样本,因为链还没有达到其静止状态(这个时期被称为
预烧期)。

(图片由作者拍摄)
限制:
- 在近似多模态分布时,由于链被卡住,得到有偏差的样本,因此对所需量的估计不太准确。
- 当样本空间是高维时,Metropolis-Hasting 变得非常慢。(另一个惊人的 MCMC 方法哈密顿蒙特卡罗克服了这些缺点,将在另一篇文章中讨论)。
结论:
对 MCMC 方法的讨论到此结束。更多关于类似讨论的帖子将被关注。
这绝不是一个容易理解的概念,因为它是概念的融合,而这些概念隐藏在数学背后。
如果你一路走来,那么恭喜你!!万事如意!!
参考:
- 深度学习作者伊恩·古德菲勒、约舒阿·本吉奥和亚伦·库尔维尔。
- ritvikmath 创作的《大都会-加速》
蒙特卡罗方法解释
原文:https://towardsdatascience.com/monte-carlo-method-explained-8635edf2cf58?source=collection_archive---------8-----------------------
理解蒙特卡罗方法以及如何用 Python 实现它

https://unsplash.com/photos/tV3Hh38eoSg
在本帖中,我将向你介绍、解释和实施蒙特卡罗方法。这种模拟方法是我最喜欢的方法之一,因为它简单,但它是一种解决复杂问题的精炼方法。它是由波兰数学家 Stanislaw Ulam 在 20 世纪 40 年代发明的。它是以摩纳哥的一个赌博城镇命名的,因为随机原则模仿了轮盘赌游戏。蒙特卡洛模拟是一个非常常见的概念,用于量化各种领域的风险,如股票价格、销售预测、预测建模等。
蒙特卡罗方法是如何工作的?
蒙特卡罗模拟是一种模拟统计系统的方法。该方法使用定义系统中的随机性来发展和近似量,而不需要解析地求解该系统。这种方法的主要概念是,运动系统中的一个点最终将以均匀和随机的方式访问该系统运动的空间的所有部分。这就是所谓的遍历性。

图片由作者提供
该模型通过使用问题领域中的一系列值而不是特定的输入来进行预测。这种方法利用概率分布(正态、高斯、均匀等。)对于任何具有不确定性的变量。根据指定的试验次数,在一个域中使用随机值的过程会重复多次。一般来说,试验次数越多,结果收敛到某个值的可能性就越大。通常用于长期预测建模的时间序列分析。一旦所有的模拟都完成了,你就会有一系列可能的结果,以及每个结果发生的相关概率。
例子
在解释这种方法的许多方式中,解释蒙特卡罗模拟最常见的例子是所谓的布丰针实验,以近似 π 的值。实验如下,我们随机将 N 根尺寸为 L 的针落在一张纸上,这张纸被长度为 2L 的平行条分割。

布丰的针实验——作者提供
随机落下这些针后,确定接触纸张分割线的针数和落下的针总数(N)。
**π ≈ N / number of needles crossed line****Note :** 1) A large amount of needles must be dropped to have a close approximation of π.
2) This formulation strictly works because we initially stated that the distance between the lines was 2 * L (where L is the length of the needle)
关于这背后的数学原理,你可以在这里找到,你也可以在这里免费运行这个实验。
谨慎一点,这个例子只是为了解释蒙特卡罗方法。蒙特卡罗方法可以用于许多不同的情况,但并不总是建议。尽管这种方法可行,但实际上这是蒙特卡罗方法的一个糟糕的用例。还有许多其他方法可以近似π的值,其中大多数方法的计算效率要高得多。
您应该使用这种方法的情况是,当您需要估计结果存在高度不确定性时。由于股票市场的随机性和不确定性,这种方法通常用于金融行业的股票预测。由于这些限制,像这样的模型是受欢迎的,并且通常比基于普通回归的方法执行得更好。在不确定的情况下,这种方法非常有效。
算法
- 识别自变量和因变量,并定义它们可能的输入域。
- 确定概率分布以在该域上随机生成输入
- 根据随机生成的输入计算问题的输出
- 将实验重复 N 次,然后汇总结果
在进行这个实验时,计算方差和标准差是常见的做法。一般来说,方差越小越好
优点和缺点
我将概述使用这种方法的一些最显著的优点和缺点。
优势
- 估计不确定性的强有力方法
- 给定正确的边界,该模型可以测量问题的参数空间
- 简单而直观,这种方法很容易理解
劣势
- 计算效率低——当有大量变量受限于不同的约束时,使用这种方法需要大量的时间和计算来逼近一个解
- 如果不良参数和约束输入到模型中,那么不良结果将作为输出给出
Python 实现
摘要
总之,本文概述了蒙特卡罗模拟是一种模拟统计系统的方法。他们利用一个确定的系统中的随机性来发展和近似数量,而不需要解析地解决它。当存在高度不确定性时,最好使用这种方法。虽然它的计算效率很低,但理解起来非常直观,可以调查问题的约束条件的大样本,并可以有效地近似不确定性。由于这些原因,它通常用于金融行业。
资源
- 【https://en.wikipedia.org/wiki/Ergodic_hypothesis
- https://mathworld.wolfram.com/BuffonsNeedleProblem.html
- https://James Howard . us/2019/09/07/Monte-Carlo-simulation-优缺点/
如果你喜欢这本书,那么看看我的其他作品。
https://medium.com/nerd-for-tech/markov-chain-explained-210581d7a4a9 https://medium.com/nerd-for-tech/k-nearest-neighbours-explained-7c49853633b6
Python 中的蒙特卡罗定价
原文:https://towardsdatascience.com/monte-carlo-pricing-in-python-eafc29e3b6c9?source=collection_archive---------5-----------------------
证券定价概念与代码指南

来自派克斯的卡斯特利什摄影
介绍
本科量化金融课程的大部分时间花在不同证券的定价上。最初,重点在于货币的时间价值,以及年金和永久年金的分析。之后,重点转移到学习、分析和定价股票衍生品,包括远期、期货、期权和互换。一旦为衍生品定价奠定了基础,剩下的课程就是研究并成为市场部门或证券类型的专家。在这篇文章中,我的目标是分解决定市场上所有这些证券的公平价格的潜在主题。事不宜迟,让我们开始吧…
金钱的时间价值
随着时间的推移,金钱的价值会有所不同。那具体是什么意思?如果有人让你选择今天收到 100 美元,或者一年后收到 100 美元,你会选择哪一个?自然,大多数人更希望尽快(今天)收到 100 美元。直观上,这可以解释为理解货币的时间价值。随着现金流在未来越来越远,它的价值就越低。同样的概念可以延伸到比较不同时间点的现金流值。凭直觉,我们更愿意从今天起一年后收到 100 美元,而不是两年后。从数学上来说,货币价值暂时差异的罪魁祸首是无风险利率。越早收到钱,就能越早获得利息。这就是在不同时间比较不同金额的方法——即找到现金流的现值,然后进行比较。考虑以下互斥的选择:
假设年无风险利率为 1%。
选项 A:
- 今天收到 100 美元
选项 B:
- 一年后收到 102 美元
你应该选择哪个?
为了确定收到哪一个现金流,我们可以以无风险利率贴现 102 美元,以找到 102 美元在今天的中的价值。
今天收到 100 美元的现值
- 100/(1.01)⁰ = $100
从今天起一年后收到 102 美元的现值
- 102/(1.01)¹ = $101
以今天的美元计算,选择 A 值100 美元而选择 B 值101 美元。由于选项 B 具有更高的现值,这意味着选项 B 今天比选项 A 更值钱。这似乎是一个简单的概念,但特别是在评估更复杂的证券时,记住这种时间价值差异变得至关重要。
证券定价
一般来说,任何证券的市场价格应该代表该证券产生的所有未来现金流的现值。考虑以下投资工具:
安全 A
- 在第 1 年末支付 100 美元
- 在第二年末支付 100 美元
- 在第 3 年末支付 100 美元
你愿意为安全支付多少钱?好了,现在我们知道钱在不同的时间点有不同的价值,我们可以用一个合适的无风险利率来贴现每个现金流在当前时间点的价值,以确定证券的价格。假设年无风险收益率为 1%,我们可以将每笔付款贴现到现在。

以今天的美元计算的第一年付款
- 100/(1.01) = 99.01
以今天的美元计算的第二年付款
- 100/(1.01)² = 98.03
以今天的美元计算的第三年付款
- 100/(1.01)³ = 97.06
未来现金流量的现值之和
- 97.06 美元+98.03 美元+99.01 美元=294.10 美元
所以今天证券 A 的合适值是 $294.10 。我们可以推广这一概念,找到任何证券的公允价值,不管它们的回报结构有多独特或奇特。在本文的后面,我们将分析具有独特收益结构的衍生品。然而,使用这种未来现金流现值的概念,我们可以找到今天所述证券的适当价格(更具体地说,今天未来现金流的预期现值)。为了做到这一点,我们需要一种方法来模拟基础资产的随机运动。
几何布朗运动
在我们能够对其价值取决于基础权益的衍生证券的价格建模之前,我们需要对基础权益建模。几何布朗运动是一种随机过程,可用于生成标的股票可能遵循的样本路径。换句话说,我们可以用几何布朗运动来模拟股票价格。

几何布朗运动
- Yt —时间 t 的股价
- dYt —股票价格随时间的变化 t
- μ —漂移项
- σ —波动项
- dt —随时间变化
- 小波变换 —布朗运动
几何布朗运动似乎有许多组成部分,但本质上有三个组成部分:回报(dYt/Yt)、预期回报(μdt)和对回报的冲击(σdWt)。我们可以使用下面的代码非常容易地在 Python 中模拟几何布朗运动。
当使用几何布朗运动对股票建模时,我们只需要提供几个参数:股票在时间段 T 的初始股价、漂移(预期收益)、股票在时间段 T 的波动率、时间步长 dt 的长度以及我们产生到 T 的总时间。在上面的代码中,我们使用了以下参数集…
- 初始价格 — 100
- 漂移 — 8%(预期收益)
- 波动率 — 10%(预期震荡回归)
- 时间步长(dt) — 1/365(每日)
- 总时间(T) — 1(一年)
如果使用几何布朗运动来生成真实股票的样本路径,有几种方法可以找到漂移和波动。一般来说,我使用我正在生成的时间段的平均历史回报,波动率我使用隐含波动率(参见什么是隐含波动率?)为一个 at 货币期权。运行上面的代码后,我们最终生成了 100 个样本路径,可以使用 matplotlib 绘制这些路径。

几何布朗运动生成的样本路径
既然我们有了生成标的资产样本路径的方法,我们就可以讨论衍生品定价了。
布莱克-斯科尔斯定价法
臭名昭著的 Black-Scholes 模型被用来为欧式期权定价。然而,定价的总体主题仍然是一样的。Black-Scholes 提出这种定价模型的方式遵循风险中性预期。实质上,他们找到了决定当前公允价值的期权的风险中性期望值(参见推导布莱克-斯科尔斯模型)。这与我们在上述证券定价部分中对证券 A 的定价没有什么不同。这种模型有一些缺点,表现为对市场环境做出了强有力的假设…
- 没有交易成本
- 连续交易
- 对数正态分布股票收益
在实践中,当交易成本存在时,这些假设立即被违反,交易只能离散地进行,并且股票回报根据经验显示它们不是对数正态分布的。我的研究重点是通过数学修改将布莱克-斯科尔斯方程中的交易成本和离散交易等一般市场条件考虑在内(如果感兴趣,请参见一般市场条件下的期权定价和复制)。然而,布莱克-斯科尔斯价格提供了一个基线,我们可以对照蒙特卡洛价格进行检查。布莱克-斯科尔斯方程如下…

- St—t 时刻的股价
- X —期权的执行价格
- σ —波动项
- φ—累积正态分布函数
- B(t,T) —连续贴现因子
- T —到期时间
- t —当前时间点
如果上面的等式不是很清楚,不要担心 Python 中的实现更容易理解(如果你希望看到更多没有完整推导的数学解释,请参见风险中性投资组合管理)。
12.368267463784072 # Price of the European call option by BS Model
蒙特卡洛定价
我们现在有了开始蒙特卡洛定价所需的一切。回想一下今天证券的价值应该如何代表该证券产生的所有未来现金流。在金融衍生品的例子中,我们不知道它们现金流的未来价值。然而,我们知道可能的结果。知道了可能的结果,我们就可以找到所有未来现金流的预期现值。请允许我用一个例子来详细说明…
在欧式看涨期权的情况下,它要么在货币内(行使)到期,要么在货币外(什么也不做)到期。让我们用 Python 建立一个这个收益结构的模型。
现在,我们可以在代码中使用这个收益模型来生成潜在的股票路径,以找到每个样本路径末端的期权价值。然后我们就可以把价值折现到现在(记住钱的时间价值!),并对所有值进行平均,以找到期权产生的未来现金流的预期现值。
16.31698913180365 # Monte Carlo Price of the European Call Option
考虑到我们生成的路径(和任意参数集)的样本大小,我们非常接近 Black-Scholes 价格!值得注意的是,这两种价格都不正确,它们只是告诉我们不同的东西。理解每个价格的是如何产生的将有助于解释。
同样的过程也可以用来给任何收益不确定的证券定价。让我们来看一个更奇特的选择…
向上和向外屏障选项
- 类似于欧式看涨期权的执行价格和收益
- 不能再行使期权的障碍水平
对于向上和向外障碍期权,如果障碍水平被触发,期权是否到期并不重要——它变得不可执行。我们可以再次用 Python 很容易地模拟这个收益结构。
这种障碍期权与欧式期权(在实施中)的唯一区别是,障碍期权要求观察每个价格点,以确定障碍是否被突破。
9.08694137422691 # Monte Carlo Price of Up and Out Barrier Option
这是向上和向外障碍期权的蒙特卡洛价格。从逻辑上讲,这是有意义的,因为对欧式期权的额外约束(障碍水平)不会增加收益,或增加收益潜力(实际上阻碍了它)。因此,正如蒙特卡洛价格所示,我们应该为这一选择支付更少的费用。这种定价方法的美妙之处在于能够为客户开发出完全符合他们需求的奇特产品。虽然这可能是一个粗略的例子,如果客户希望通过购买看涨期权对冲他们在股票头寸中的风险,但他们不相信基础资产会超过某个点,我们可以根据他们的情况为上涨和下跌障碍期权定价,并降低他们的对冲成本。
来自《走向数据科学》编辑的提示: 虽然我们允许独立作者根据我们的 规则和指导方针 发表文章,但我们不认可每个作者的贡献。你不应该在没有寻求专业建议的情况下依赖一个作者的作品。详见我们的 读者术语 。
蒙特卡罗模拟和 Python 变种
原文:https://towardsdatascience.com/monte-carlo-simulation-and-variants-with-python-43e3e7c59e1f?source=collection_archive---------1-----------------------
蒙特卡洛模拟指南,必须了解 Python 实现的统计抽样技术

图片来源:帕维尔·丹尼柳克
蒙特卡洛模拟基于重复随机抽样。蒙特卡洛的基本概念是使用随机性来解决原则上可能是确定性的问题。蒙特卡罗模拟是在不知道真实的潜在人口分布的情况下对人口进行推断的最流行的技术之一。这种取样技术变得很方便,尤其是当人们没有足够的时间从原始人群中重复取样时。蒙特卡罗模拟的应用范围从解决理论物理问题到预测金融投资趋势。
蒙特卡罗有 3 个主要用途:估计参数或统计测量,检查估计的性质,近似积分
这篇文章是关于蒙特卡罗程序的 3 种用法和 3 种蒙特卡罗变体,统计抽样技术,它们可以用来产生独立的随机样本。本文将涵盖以下主题:
**- Introduction to Monte Carlo Simulation
- MC Parameter Estimation
- MC Examining the Estimate Properties
- MC Integrals Approximation
- Importance Sampling
- Rejection Sampling
- Inverse Transform Sampling**
这篇文章适合有统计学知识的读者,因为它将涵盖中等水平的统计概念和例子。如果你想从零开始学习必要的统计学概念,可以在这里查看我之前关于 统计学基础的文章。
蒙特卡罗模拟游戏攻略
蒙特卡洛模拟最初是为了解决 布丰针问题 而发明的,其中 *π,pi,*可以通过将针落在由平行等距条带组成的地板上来估算。蒙特卡洛模拟的现代版本是由斯坦尼斯拉夫·乌拉姆发明的,他在核武器项目工作期间发明了现代版本的 马尔可夫链蒙特卡洛 技术,而约翰·冯·诺依曼编写了一台特殊的计算机来执行蒙特卡洛计算。冯·诺依曼还以他的著名方法而闻名,他通过扔两次不公平的硬币,忽略不同的{正面,反面}和{反面,正面}选项,使不公平的骰子变得公平。
乌兰提出的基于一系列真正随机的 T2 数的方法非常慢,但是冯·诺依曼开发了一种计算 T4 伪随机数的方法,比乌兰的方法快得多。这种方法的名字来自摩纳哥的蒙特卡洛赌场,乌兰的叔叔会从亲戚那里借钱去赌博。
冯·诺依曼也因他著名的方法而闻名,他通过投掷两次不公平的硬币,忽略不同的 HT 和 TH 选项,使不公平的骰子变得公平。
蒙特卡罗背后的想法是,随着我们使用更多的样本,我们的答案应该越来越准确。
蒙特卡罗估计
蒙特卡罗使用大数定律(LLN)通过模拟值得出总体参数的估计值。LLN 陈述:假设 X1,X2,。。。,Xn 都是具有相同基础分布的独立随机变量,也称为独立同分布或 i.i.d .,其中所有 X 都具有相同的均值 μ 和标准差 σ 。随着样本量的增加,所有 X 的平均值等于均值μ的概率等于 1。LLN 可以总结如下:

因此,蒙特卡罗估计背后的思想是,当我们多次获得一个参数的估计值时,假设 M = 10000 次,那么这些估计值的平均值将形成该参数的蒙特卡罗无偏估计值。

蒙特卡罗估计:Python 实现
假设我们想要估计两个变量(一对自变量和因变量)之间的因果关系,并且我们对截距α和斜率β的可能值有所了解。
我们能做的是从正态分布中随机抽样,以生成误差项、因变量和自变量的值。然后我们就可以估计β的系数,beta_hat,重复这个过程 M = 10000 次。然后通过 LLN,这 10000 个 beta _ hats 的样本均值将是真实 beta 的无偏估计。那就是:

用蒙特卡罗方法检验估计性质
蒙特卡罗模拟是检验估计量性质的有力工具。当检查线性回归估计的属性时,这可能不是很有用,因为大多数统计软件包已经提供了检查估计的方法。然而,在使用 Monet Carlo 估计的其他情况下,估计可能是找出估计量是否无偏、有效、和一致的唯一方法。
估计值是无偏的吗?
估计量的偏差是其期望值和被估计参数的真实值之间的差值,可以表示如下:

当我们声明估计量是无偏的时,我们的意思是偏差等于零,这意味着估计量的期望值等于真实参数值,即:

为了检查估计量是否无偏,我们可以使用上一步中获得的 beta_hats 的蒙特卡罗样本,并绘制这个采样分布。那么,如果这个抽样分布以真实参数值为中心,那么估计量就是无偏的。
从图中我们可以看到,我们使用蒙特卡罗模拟 10000 次迭代得到的β估计的抽样分布是以真实参数β为中心的。所以,beta_hat 的蒙特卡罗估计是无偏的。

图片来源:作者
估算是否一致有效?
如果随着样本量变得非常大,估计量收敛到真实参数,那么这个估计量被称为一致,即:

为了检查估计量是否一致,我们可以使用我们在上一步中获得的 beta_hats 的蒙特卡罗样本,并针对少量和大量的 M 个模拟绘制其采样分布。如果我们看到,随着样本量的增加(蒙特卡罗模拟交互的次数),抽样分布变得更窄,并且更集中于真实参数值,那么估计值很可能是一致的。
一个参数可以有多个估值器,但是方差最小的那个被称为有效 **。**为了检查估计量是否有效,我们可以使用我们在上一步中获得的 beta_hats 的蒙特卡罗样本,并绘制少量和大量 M 次模拟的抽样分布。如果我们看到抽样分布的宽度随着样本量的增加而变小,那么估计可能是有效的。
假设我们运行 M = 1000 的蒙特卡罗模拟,并获得β的蒙特卡罗估计(左直方图)。此外,我们用 M = 10000 重新运行这个模拟(右直方图)。我们看到,随着 M 从 1000 增加到 10000,beta_hats 的采样分布更加集中在真实参数值周围。所以,beta_hat 的蒙特卡罗估计是一致的。我们看到,随着 M 从 1000 增加到 10000,beta_hat 的采样分布宽度减小。所以,beta_hat 的蒙特卡罗估计是有效的。

图片来源:作者
用蒙特卡罗方法近似积分
对于已知函数,如正态分布函数,计算积分可能很简单,不需要使用 MC。然而,对于更复杂的函数,计算积分可能非常困难,在这些情况下,使用 MC 可能是计算该积分的唯一方法。
用于逼近积分的 MC 是基于 LLN,其背后的思想是,如果我们可以从给定的分布 P(x)生成随机样本 xi,那么我们可以通过求和而不是积分来估计该分布下函数的期望值。换句话说,我们可以通过确定一个积分的被积函数 h 的平均值 (x) 来求出它的值。正如我们之前看到的,MC 就是基于这个原则。

MC 积分逼近:Python 实现
假设我们想得到概率 Pr[X ≥ 3],其中 X ~范数(10,2),可以用下面的积分表示,其中 f(x)是正态分布的 pdf 函数。

然后使用蒙特卡罗模拟,通过计算这个量 10000 次并取这些值的平均值,就可以得到这个积分。
重要抽样
重要抽样是使蒙特卡罗模拟收敛得更快的方法之一。此外,与传统的蒙特卡罗方法相比,重要性抽样的方差也更低。它用于根据目标分布 g(x)估计某个 h(x)函数的期望值,同时可以访问某个 f(x)函数。想法是使用一些提议的分布 f(x)从样本中抽取样本,并使用重要性权重 w = g(x)/f(x),以减轻高估或低估目标分布 g(x)的影响。


重要性采样:Python 实现
假设我们想估计服从指数分布的随机变量 X 的期望值。因此,我们的目标分布是指数分布,g(x) = Exp(1),h(x) = exp(-X + cos(X))。

对于重要性抽样,我们需要选择一个尽可能接近目标分布的方案分布,因此我们选择 f(x) = Exp(2)。假设指数函数的 pdf 是 exp(-λ* x),那么我们有:

其中 f(x)是我们可以从中采样的建议分布,h(x)是需要估计期望值的函数,g(x)是我们不能从中采样的目标分布,w(x)是重要性权重。那么 h(x)的期望值可以表示如下:

然后利用 LLN,我们可以把这种期望表达如下:

下图显示了 h(x)、目标分布 g(x)和提案分布 f(x)图。正如我们所看到的,提案分布与目标分布非常接近。f(x)用于随机抽取 1000 个观察点,每次计算表达式 g(x)/f(x)*h(x),即计算重要性权重并与 h(x)相乘。然后,取这些值的平均值,得到 1.52,这是我们要找的期望值。

图像来源:作者
拒绝采样
剔除采样通常用于从非正态目标分布生成独立样本。这种蒙特卡罗采样变体背后的思想是,如果我们想从目标非标准化分布 P(x)生成随机样本,那么我们可以使用一些提议分布 Q(x)】和一个归一化 常数 c,使得 cQ(x)是一些辅助分布 P×(X)的上界,其中 P(x)=(P×(X)/c 得出一个样本列表,从该列表中可接受的值将形成来自目标 P(X)分布的独立样本。
1: 选择一个接近目标分布 P(X)的建议函数 Q(X)
2: 选择归一化常数 c,使得 P×(x)≤cQ(x)
3: 选择辅助分布 P×(X)s . t . P(X)= P×(X)/c
4: 从 Q(X)生成随机样本 X
5: 从 Unif(0,cQ(x))生成随机样本 u
6: 重复步骤 1、2 M 次(如 10000 次)
7: 如果 u≤P×(x),则接受 x,否则拒绝

图像来源:作者
剔除取样的重要要求:
- 要从中取样的方案分布 Q(x)(均匀或高斯)
- 归一化常数 c,使得 c*Q(x)
- 辅助分布 P*(x)
- 可以从提案分发中获取样本
拒绝采样:Python 实现
假设我们想从正态分布的混合中生成独立样本,我们希望其分布类似于 p×(x)= N(30,10) + N(80,20)。我们可以访问正态分布和均匀分布进行采样,以生成这些目标样本。我们可以使用建议函数 Q(x) = N(50,30),归一化常数 c = max(P*(x)/Q(x))。
然后,我们按照前面描述的步骤生成样本,其中很大一部分样本被拒绝,而一些样本被接受。以下直方图显示了一组可接受的样本,这些样本是来自混合正态分布的独立样本,同时只能访问正态和均匀随机生成器。

图像来源:作者
剔除采样的效率非常低,因为它会剔除大量样本点,从而导致计算时间非常长。
逆变换采样
与剔除采样一样,逆变换采样是一种生成独立样本的方法,但与剔除采样不同,逆变换采样的效率是 100%。逆变换采样背后的思想是使用我们无法从中采样的目标群体分布的逆累积分布函数并使用我们可以容易地从中采样的随机生成器,从目标群体中生成独立的随机样本。
1: 来自 Unif(0,1)的样本值 u
2: 利用目标分布的逆 CDF 函数,得到值为 u 的逆 CDF 对应的 x 值
3: 重复步骤 1 和 2 M 次(如 10000 次)
4: 收集这些遵循期望分布的 x 值
对 ITS 的重要要求:
- 从 Unif(0,1)访问采样
- 了解目标分布 PDF/CDF
- 能够确定目标分布的逆 CDF
逆变换采样:Python 实现
假设我们希望从λ等于 1 的指数分布中生成独立样本,而我们只能从均匀分布中采样。因此,我们可以如下确定指数分布的逆 CDF:


然后,我们从 Unif(0,1)中随机采样,并使用这个值 u 来确定 x,使用目标分布的定义逆 CDF,即-log(1-u)。一旦这个过程重复 M = 10000 次,存储的样本就是来自目标分布的独立样本。下面的直方图显示了这些样本。

图片来源:作者
与拒绝采样不同,逆变换树是 100%有效的。
关于选择建议分布的说明
- 如果建议分布看起来非常像目标分布,蒙特卡罗变体的计算效率将是最好的。
- 当建议分布在目标分布具有不可忽略的密度的区域中具有 0 密度时,重要性抽样、拒绝抽样和逆变换抽样方法都可能严重失败。因此,建议分布应该有重尾。
额外资源
https://www.youtube.com/watch?v=kYWHfgkRc9s&ab _ channel = BenLambert
https://www.youtube.com/watch?v=V8f8ueBc9sY[t = 1s&ab _ channel = BenLambert](https://www.youtube.com/watch?v=V8f8ueBc9sY&t=1s&ab_channel=BenLambert)
https://www.youtube.com/watch?v=rnBbYsysPaU&t = 566s&ab _ channel = BenLambert
额外资源
https://github.com/TatevKaren
如果你喜欢这篇文章,这里有一些你可能喜欢的其他文章:
https://tatev-aslanyan.medium.com/bias-variance-trade-off-in-machine-learning-7f885355e847 https://tatev-aslanyan.medium.com/data-sampling-methods-in-python-a4400628ea1b https://medium.com/analytics-vidhya/pyspark-cheat-sheet-big-data-analytics-161a8e1f6185
感谢阅读
我鼓励你 加入 Medium today 拥有 完整访问所有跨媒体发布的伟大锁定内容,并在我的 feed 上发布关于各种数据科学、机器学习和深度学习主题的内容。
关注我 中阅读更多关于各种数据科学和数据分析主题的文章。更多机器学习的动手应用,数学和统计概念查看我的Github账号。
我欢迎反馈,可以联系LinkedIn。**
快乐学习!
基于 Python 的制药过程蒙特卡罗模拟
原文:https://towardsdatascience.com/monte-carlo-simulation-of-pharmaceutical-manufacturing-processes-in-python-b014eb96d14?source=collection_archive---------4-----------------------
使用 Python 对流程进行离散模拟可以突出事件成功或满足生产标准的更大概率。

由 Shopify 合作伙伴拍摄的股票照片
今天,群体免疫是新闻播音员、医生、科学家、教师、朋友和家人经常引用的话题。大约 70%的世界人口将需要接种疫苗,以产生从我们的全球社会中根除新冠肺炎所需的免疫反应,尽管随着奥米克隆等新出现的变种,这一数字可能会更小。制造足够多的疫苗来满足需求是一个越来越大的问题,特别是在供应链限制和约束全球原材料和组件的情况下。
对于这些救命药物的制造商来说,在现有生产线产能的情况下,准确预测可以有效制造多少剂量从未如此重要。生产疫苗是一项独特的挑战,因为产品的生长频率很高,即使几十种中间体或原材料中的一种发生微小变化,也会完全改变一批产品的生长。作为患者,我们是完全安全的, FDA 有程序来保证声誉好的公司安全地生产药物。然而,制造商面临的挑战仍然是如何预测能够生产的剂量的挑战和机会,以便负责任地满足预防疾病的需求。
Python 中的离散事件模拟是制作生产模型原型的最快速和可用的工具之一,甚至可以利用标准库来构建各种制造场景。在今天的练习中,我们将模拟一种假设药物的年产量,这种药物是我们在之前的两篇媒体文章中生产的。

仿制药生物制品生产工艺流程图
这里我们可以看到我们假设的过程有三个主要的处理阶段:发酵、纯化和无菌边界。不足为奇的是,在一些制造过程中,每个单元操作步骤都会产生浪费和产量下降,但这些低效率的影响是复合的。最重要的是,生物空间内的变化会导致一系列预期的性能和产量,而不是每批恒定和可预测的数量。然而,能够预测这个范围是我们的模型原型可以展示巨大价值的地方。
我们将在脚本中使用几个不同的库。我们所有的图形都将在 plotly 内,当然 Numpy 和 Pandas 是计算的必需品。Scipy 还将用于稍后的一些统计报告。
import pandas as pd
import plotly.express as px
import numpy as np
import scipy.stats as st
pd.set_option("plotting.backend", "plotly")
下面也可以看到我们的模拟数据。请注意,在五年的时间里,每年生产二十批。我们有以克为单位记录的每次操作的产量,这些数据应该来自实验样本,尽管这些数据是为这次练习编造的。最后,我们有可用产品生产的剂量、全年生产的总剂量、所有时间生产的总剂量、我们的库存(本月的剂量,减去需求需求,加上上个月的库存),以及预期需求或交付合同的每月需求(下面的数字很小,COVID 疫苗订单为数千万或数亿)。
data = pd.read_csv('SimulatedGenericDrugProductData.csv')
data['DOM'] = pd.to_datetime(data['DOM'], format = '%m/%d/%Y')
data

模拟过程信息数据表
现在,让我们开始规划我们的场景,并了解我们的特许经营的状态。
fig = px.scatter(data, x="DOM", y="Doses", trendline="ols",title='Doses of Product')
fig.show()

产品剂量
从上图中我们可以看出,随着时间的推移,我们生产的平均剂量一直在下降。
fig = px.line(data, x='DOM',y=['Doses','MonthlyRequirement'], title='Doses Made and Monthly Demand Requirement')
fig.show()

制造的剂量和每月需求
fig = px.bar(data, x='DOM',y=['Inventory','TotalAllTime'], barmode='group', title='Total Manufactured Doses and Inventory')
fig.show()

总制造剂量和库存
我们可以看到,随着时间的推移,我们的流程效率一直在下降。虽然我们仍然能够生产产品,但我们的总生产率正在下降,我们从 2019 年开始无法满足患者的需求。我们的库存下降到负值,也许这是一个点,我们要么剥夺了市场,或者竞争对手将介入供应未满足的需求。
我们的业务证明了解决问题的必要性。问题的根源是什么?比较这三个单元操作,我们可以看到过程衰减的速率是从哪里开始的。
fig = px.scatter(data, x="DOM", y=['FermentationYield','PurificationYield','SterileBoundaryYield'], trendline="ols",title='Grams of Product')
fig.show()

每个单元操作中生产的产品克数
随着时间的推移,我们的发酵过程已经降低了效率。也许下一步会调查我们设备的机械问题,我们使用的原材料的质量,或者我们每批使用的种子的健康状况。
假设对流程的任何更改都需要大量的时间来实施,我们可以预期我们明年的绩效会是什么样的?一年的批量产量是多少?我们能满足 40 万人的需求吗?500,000;或者全年生产 60 万剂?
为了回答这些问题,我们需要准备一个明年的模拟群体,比如说 1000 人,看看随机事件的发生如何影响明年生产计划的执行。这个过程被称为蒙特卡罗模拟,因为随机抽样将指示我们做出更广泛的概括(最初源于赌场的名称)。各种潜在事件都有可能发生,例如设备故障两个月,原料质量下降导致产量下降,员工因病减少,供应链问题导致材料不可用等。其中一些事件的统计离散分布是二项式的,这意味着这些事件发生或不发生的可能性有一定的百分比,但我们将简化我们的模型,只根据我们模拟的下一年的表现来检查每年每个单元操作的产量。
data2015 = data[data['DOM'].dt.year == 2015]
data2016 = data[data['DOM'].dt.year == 2016]
data2017 = data[data['DOM'].dt.year == 2017]
data2018 = data[data['DOM'].dt.year == 2018]
data2019 = data[data['DOM'].dt.year == 2019]
让我们来看看这些数据。
fig = px.histogram(data2018, x='FermentationYield', title='Assumption: Normal Distribution')
fig.show()

data2018.describe()

2018 年业绩数据表
这里汇集了我们对未来几年的一些早期假设。根据我们早期的模型,我们可以假设我们的性能随着时间线性下降。在这个实验中,我们还可以假设(尽管每年有 20 个以上的数据点也不错)每年的产量呈正态分布。这个钟形曲线将有助于我们的模型编程,现实生活中的模型可能会显示这个或其他分布,就像故障事件的二项式分布,甚至三角形、常数或威布尔分布等等。
检查我们不同操作收益之间的关系,我们还可以注意到一些趋势。
fig = px.scatter(data, x="FermentationYield", y='PurificationYield', trendline="ols",title='Fermentation vs Purification Yields')
fig.show()

fig = px.scatter(data, x="PurificationYield", y='SterileBoundaryYield', trendline="ols",title='Purification vs Sterile Boundary Yields')
fig.show()

fig = px.scatter(data, x="SterileBoundaryYield", y='Doses', trendline="ols",title='Sterile Boundary Yield vs Doses')
fig.show()

对于这些图表中的每一个,Plotly 还绘制了一个普通的最小二乘趋势线。后两个图表的点更接近趋势线,也意味着在它们的正态分布中有更低的变化,或更紧密的钟形曲线。
创建正态分布点只需要标准偏差、平均值和随机生成值。生成这些数字的实际函数如下,但是 Python 中有一些函数可以为我们计算这些数字。

为了生成一个模拟的年度绩效,我们将首先为一年的 20 个批次设定一个索引号。我们有一个来自发酵对指数的第一个回归模型的函数,其中我们的平均值是指数(101 到 120),发酵产量的标准偏差每年约为 100g。我们的纯化产率的平均值是以前的发酵产率乘以我们上面的发酵与纯化图的斜率,或者大约为 0.6 (10 克发酵导致 6 克纯化)。这里的标准偏差约为 75。无菌边界产量几乎是恒定的:发酵过程中产生的 90%,最终剂量数是无菌边界产量的 100 倍。执行这些计算 1000 次,将产生我们下一个制造年度的可能年度绩效的总体范围。
batchNumberFromIndex = list(range(101,121))def FermRandom(index):
x = index*(-5.0)+1025
return np.random.normal(x,100)
def PurRandom(ferm):
x = ferm*0.6
return np.random.normal(x,75)
def SBRandom(pur):
return pur*0.9
def DosesRandom(SB):
return SB*100newYearRandom = pd.DataFrame({'batchNumberFromIndex': batchNumberFromIndex})
def generateRandomOutcomes(df1,populationSize):
dosesList = []
for i in range(0,populationSize):
df = df1.copy()
df = df.assign(Ferm = lambda x: FermRandom(df['batchNumberFromIndex']))
df = df.assign(Pur = lambda x: PurRandom(df['Ferm']))
df = df.assign(SB = lambda x: SBRandom(df['Pur']))
df = df.assign(Doses = lambda x: DosesRandom(df['SB']))
dosesList.append(df.sum()['Doses'])
return dosesListdistOfYearDoseSums = generateRandomOutcomes(newYearRandom,1000)
distOfYearDoseSumsDF = pd.DataFrame(distOfYearDoseSums,columns=['DosesForYear'])
distOfYearDoseSumsDF

现在我们的蒙特卡罗模拟已经完成,我们可以开始分析结果了。
fig = px.histogram(distOfYearDoseSumsDF, x='DosesForYear', title = 'Distribution of Likely Doses Manufactured Next Year')
fig.show()

distOfYearDoseSumsDF.describe()

我们可以看到结果的接近正态分布,平均集中在明年生产的 510,000 剂左右。使用在 SciPy 的统计库中计算的 Z 分数和概率函数,我们还可以计算我们有多大可能达到制造的剂量的某些里程碑,例如 400k、500k 和 600k。
z = (400000.00-distOfYearDoseSumsDF.mean()[0])/(distOfYearDoseSumsDF.std()[0])
1-st.norm.cdf(z)
OUT: 0.9980887972401362z = (500000.00-distOfYearDoseSumsDF.mean()[0])/(distOfYearDoseSumsDF.std()[0])
1-st.norm.cdf(z)
OUT: 0.6011978843565298z = (600000.00-distOfYearDoseSumsDF.mean()[0])/(distOfYearDoseSumsDF.std()[0])
1-st.norm.cdf(z)
OUT: 0.008666661053801317
我们可以看到,1000 次试验中约有 60.1%生产了 50 万剂或更多,因此我们可以推断,明年的表现有 60%的机会满足 50 万剂的需求。也分别有 99.8%和 0.9%的机会满足 40 万和 60 万剂的需求。
我们可以使用这些模型来帮助我们的制造工厂做出决策,并寻求我们的合同。例如,如果我们正在生产 COVID 疫苗,我们只想在我们的生产能力范围内接受合同,在这种情况下,我们认为并能够证明这几乎是我们能够满足需求的保证。或者,如果我们有足够的谈判时间,我们可以使用这些数据来建立产能模型,以启动多条生产线来支持对制造基础设施的进一步资本投资。
请让我知道您的想法,并随时通过 LinkedIn 联系我,向我提供反馈,提出问题,或者看看我们如何将这些工具和思维方式引入您的组织!点击这里查看我的其他一些关于数据分析和生产计划的文章!
Streamlit 蒙特卡洛模拟网络应用程序
原文:https://towardsdatascience.com/monte-carlo-simulation-web-app-using-python-e92780800a2?source=collection_archive---------35-----------------------
用 Python 和 Streamlit 构建概率内在价值计算器

注来自《走向数据科学》的编辑: 虽然我们允许独立作者根据我们的 规则和指导方针 发表文章,但我们不认可每个作者的贡献。你不应该在没有寻求专业建议的情况下依赖一个作者的作品。详见我们的 读者术语 。
评估一家企业本身就是一项概率性的工作:
"预计的收入增长实现的可能性有多大?"
“即将到来的后台运营数字化将在多大程度上提高利润率?”明年联邦税率会提高吗?如果会,会提高多少?
对大量此类公司和市场相关问题的回答,对作为估值模型输入变量的关键指标有着直接的影响。处理这种不确定性的一种方法是将对内在价值计算有影响的财务指标视为随机分布的变量。通过反复从指定的随机分布中抽取变量实例,并通过几次迭代使用这些抽取来计算公平的公司价值,投资者可以对公司价值的内在可变性有一个印象。
这种方法也被称为蒙特卡罗模拟,这是一种将公司估值的概率性质纳入计算的有力工具。为了尝试利用蒙特卡罗模拟,我使用 Python 和 Streamlit 创建了一个简单的 web 应用程序。这篇短文将突出基础金融理论并讨论应用程序设计。
金融理论
公司的内在价值或公允价值是企业产生的未来自由现金流的总和,按风险调整率贴现。用更多的数学术语来说,这可以归结为以下等式:

其中,FCF 是公司在第一年产生的自由现金流,r 是风险调整后的贴现率。自由现金流是通过从公司运营产生的现金流中扣除公司的资本支出(如设备投资)来确定的。然后使用风险调整贴现率 r 对这一指标进行贴现,以说明现金流的风险和投资于公司的资金的时间价值。
由于预测未来五年以上的自由现金流很困难,戈登增长模型通常用于计算终端公司价值,然后使用风险调整贴现率进行贴现。戈登增长模型将基础公司产生的现金流视为一种不断增长的永续性,而这种永续性又通过以下等式进行估值。

其中,FCF 是公司未来五年产生的自由现金流,g 是公司第六年及以后的恒定增长率。
虽然贴现率 r 和最终增长率 g 是内在价值计算的重要输入变量,但后续分析和 web 应用程序将它们视为给定变量,并关注与预测自由现金流相关的输入变量。因此,仔细看看自由现金流到底是如何确定的似乎是有希望的。
要获得公司产生的自由现金流,需要采取几个步骤:

其中,NWC 代表净营运资本,等于流动资产头寸(扣除现金)减去流动负债头寸(扣除流动债务)。NWC 的变化从 NOPAT 中扣除,因为流动资产的增加会导致现金流出,但不会减少股东权益。
因此,给定要估值的公司的当前收入数字,投资者需要几个输入来预测 FCF。这些输入是(1)收入增长率,(2)EBIT 利润率,(3)公司税率,(4)净资本支出/销售额,以及(5)NWC/销售额。
所有这五个度量都不能提前确定地知道,因此它们非常适合作为蒙特卡罗模拟中的输入变量。在获得这五个随机变量的实例后,我们可以使用这些数字来计算每次迭代的公司总价值的一个实例。由于随后将更详细地处理模拟的机制,我将首先更详细地解释应用程序如何计算自由现金流。
给定输入值,前五年的收入计算如下:

使用其他四个输入值,自由现金流可以简单地按照上面的推理来确定。然后使用适当的贴现因子对 t 年的最终自由现金流进行贴现,随后对这些现值求和。此外,将使用上述等式确定的终端公司价值的现值相加,得到总公司价值,也称为企业价值。
由于企业价值是公司对权益和债务持有人的价值,我们需要扣除净债务头寸(总债务减去现金)来得出公司的权益价值。按照这个过程,Python 脚本生成了一个 pandas 数据帧,如下所示,很好地总结了所需的所有步骤:

表 1: DCF 模型输出(由于输出四舍五入,贴现因子等于 1)
前面的步骤描述了在给定一组全部五个随机输入变量的情况下确定公司内在价值所需的过程。下一节中描述的 web 应用程序遵循这种方法多达 1000 次,使用稍微不同的输入数据集来获得多达 1000 个潜在的内在公司价值。
Web 应用程序概述
当点击 web 应用程序的链接时,用户会看到以下屏幕:

图 web 应用程序的初始屏幕
进入公司股票市场并点击“搜索”后,该应用程序从雅虎财经获取所需的财务数据,并以熊猫数据框架的形式返回。在这一点上,需要注意的是,股票代码必须采用 Yahoo Finance 使用的形式。例如,当查找戴姆勒公司时,用户必须输入戴。德,不仅仅是戴。

图 2:评估指标输出
在指定五个随机变量时,可以随后使用关键评估指标(如收入增长率)的历史信息。

图 3:随机变量输入
在指定输入变量时,应用程序提供了三种随机分布供选择。在本例中,假设收入增长呈正态分布,平均增长率为 15%,标准差为 2%。公司 EBIT 保证金按三角形分布,低端 20%,众数 25%。此外,假设模拟的任何迭代中的税率是从 15%至 25%范围内的均匀分布中随机抽取的。类似地,分配规定为净资本支出/销售和 NWC/销售比率。
最后,用户需要提供公司的折扣率和终端增长率,以及期望的模拟迭代次数。
单击“搜索”后,应用程序进行蒙特卡罗模拟,并提供几个输出。除了平均股权价值及其标准差,该程序还显示一个直方图,提供股权价值分布的概述。此外,收入和 EBIT 预测数字以折线图形式提供。

图 4:蒙特卡罗模拟输出
使用 Python 进行应用程序设计
这一节将更详细地介绍应用程序使用的 Python 代码,这些代码可以在我的 GitHub 上找到。
每当用户输入公司股票并点击“Search”按钮时,脚本就会创建一个 company 类的实例。公司对象具有以下属性:

此外,Company 类有三个方法。

get_inputs_df 方法计算关键评估指标,并以熊猫数据帧的形式返回它们,然后显示给用户(如图 2 所示)。
get_free_cash_flow_forecast 方法将七个参数列表作为输入(最新收入、收入增长率、EBIT 利润率、税率、净资本支出/销售额、NWC/销售额和净债务),并返回包含自由现金流预测的数据帧(如表 1 顶部所示)。
discount _ free _ cash _ flows 方法将参数列表、贴现率和终端增长率作为输入变量,并调用 get_free_cash_flow_forecast 方法来获得自由现金流预测。随后,贴现自由现金流,计算公司权益价值,得到表 1 所示的数据框架。
总的来说,给定所需的参数,在蒙特卡洛模拟中,每次迭代都会调用一次 discount _ free _ cash _ flows 方法。虽然假设公司的贴现率和终端增长率不会从一次迭代到下一次迭代发生变化,但包括收入增长率等变量的参数列表是使用以下函数确定的。

最后,下面这段代码将不同的方法和函数联系在一起,进行模拟并返回收入和 EBIT 预测的列表,以及在模拟的每次迭代中计算的股票价值的列表。

应用程序的整个用户界面都是使用 Streamlit 库创建的,这使得创建 web 应用程序变得非常容易。Streamlit 的共享服务极大地方便了应用程序的公开发布。关于如何整合 Streamlit 的更详细的解释,你可以看看我的 GitHub ,我上个月发表的另一篇文章,或者 Streamlit 网站。
结束语
总的来说,我认为将概率因素纳入公司估值有助于调整公司未来固有的不确定性。虽然蒙特卡洛模拟有一定的局限性,但它们可能因此成为投资者的有用工具。我希望上面描述的 web 应用程序可以帮助进行这样的模拟,帮助用户做出更明智的决策。
放弃
上面提供的代码只是将 Python 编程应用于金融领域的一个练习。本文和 web 应用程序网站中包含的信息不应用于投资决策。
蒙特卡罗树搜索:简介
原文:https://towardsdatascience.com/monte-carlo-tree-search-an-introduction-503d8c04e168?source=collection_archive---------1-----------------------
MCTS 是 AlphaGo 和许多人工智能应用的基石。我们的目标是建立一些直觉,并在此过程中弄脏我们的手。

蒙特卡洛(图片来自 Unsplash
蒙特卡罗树搜索(MCTS)是近期人工智能应用许多重大成功背后的重要算法,如 AlphaGo 在 2016 年的惊人对决。
在这篇博客中,我们将首先从不知情搜索开始,在这个过程中,我们简单地遍历整个搜索空间来找到最优解。它包括深度优先搜索和广度优先搜索。
然后我们接着描述 MCTS 算法是如何工作的。最后,我们将它应用于一个玩具示例问题,寻找二叉树的最有价值的叶节点。
不知情的搜索
无信息搜索,顾名思义,是一种通用的搜索算法,除了一个抽象的问题定义之外,没有给出任何额外的信息。尽管通过正确的问题抽象,它们可以被普遍应用,但是当问题变大时,它们通常会遇到效率问题。

DFS 和 BFS ( 图片来源)。我自己的 Github repowlong Xiang/mcts中的直观实现。
分支因子为 b 且深度为 d 的树将具有 b^d(读作 b 的 d 次方)个叶节点。
在游戏中,我们使用所谓的游戏树,其中每个节点代表一个游戏的状态,其子节点代表玩家可以采取的所有可能行动的可能下一个状态。

井字游戏树(图片来源)。注意,由于空间原因,它没有完全展开。
根据维基百科和,Go 的平均分支因子为 250。信封计算的一些背面将很快显示在 Go 中使用不知情的搜索是多么禁止:
- 在步骤 1: 250
- 在第 2 步:250 = 62500
- 在第 3 步:250 = 15,625,000
- 在第 4 步:250⁴= 3906250000
- 在第 5 步:250⁵ = 976,562,500,000
- …
- 在第 10 步:250⁰= 95367431640625000000000
经过 10 步之后,我们已经看到了一系列可能的游戏状态。这也在一定程度上解释了为什么 AlphaGo 的胜利是人类的一个里程碑。
蒙特卡罗树搜索
现在,我们明白了这样一个事实,我们需要比不知情的搜索更聪明的东西来浏览像围棋这样的巨大国家空间。MCTS 就是这样一种智能算法,它给我们提供了一条出路。
从本质上来说, MCTS 使用蒙特卡罗模拟来累积价值估计,以引导搜索树中高回报的轨迹。换句话说,MCTS 更关注那些更有前途的节点,因此它避免了不得不强行尝试所有不切实际的可能性。
在其核心,MCTS 由 4 个步骤的重复迭代组成(理想情况下是无限的,实际上受计算时间和资源的限制):选择、扩展、模拟和备份。
选择
在这一步,我们使用树策略来构建从根到最有希望的叶节点的路径。
叶节点是具有未探索的子节点的节点。
树策略是用于雪盖中动作(节点)选择的明智策略(游戏树中探索过的部分,而不是大量未探索的底部)。这种策略的一个重要考虑是**探索与利用的平衡,**这是人工智能中反复出现的话题,尤其是在强化学习中。
在 AlphaGo 背后的算法中,使用了基于 UCB 的策略。更具体地说,每个节点都有一个关联的 UCB 值,在选择过程中,我们总是选择具有最高 UCB 值的子节点。

如我们所见,节点 i 被访问得越多,UCB 的第二任期就变得越低,从而降低了其再次被选中的概率。因此,UCB 算法天生就具有探索和利用的特性。聪明利落!
关于探索与开发(包括 UCB)的更严谨但仍易于理解的叙述,请阅读 Lilian Weng 关于这个主题的博客。
最后,在选择步骤中,我们可以遍历雪盖或探索树的一部分,直到到达一个叶节点。
膨胀
还记得我们在选择结束时到达一个叶节点吗?
在扩展步骤中,我们简单地随机选取一个叶节点的未探索节点。
模拟(或推广)
在此步骤中,我们推出一个或多个模拟,每个模拟都会累积奖励。推出策略通常是简单的,甚至是完全随机的,因此执行起来很快。例如,一场胜利可能导致+1 的奖励,0 的平局和-1 的失败。
有人可能会问,何必呢?
在围棋比赛中,这一步就像算法,就像围棋大师可能会在他/她的脑海中做的那样,玩很多次游戏,直到它结束。真正的天才不仅能看到未来,还能看到未来的许多版本。
支持
现在我们已经完成了许多模拟,我们可以用它来通知最佳的下一步行动?
我们使用模拟的累积奖励来备份和更新雪盖中节点的值。
注意:在推出步骤中,我们不更新节点的值!这样做是因为我们需要关注根节点(雪盖)的附近,基于此我们需要做出下一步行动的决策。而雪盖之外的值与这种决策无关,存储和计算起来也没有计算效率。
放在一起

简介中的图片:萨顿和巴尔托的《强化学习》
伪代码可能如下所示:
def run(node, num_rollout):
*"one iteration of select->expand->simulation-> backup"* path = select(node)
leaf = path[-1]
expand(leaf)
reward = 0
for i in range(num_rollout):
reward += simulate(leaf)
backup(path, reward)
注意,这是给定特定根节点(例如游戏树中的节点)的 MCTS 的一次迭代。在实践中,我们可以对这 4 个步骤进行尽可能多的迭代,在此期间,在模拟步骤中可以进行多次部署。在计算时间或资源耗尽后,我们停止迭代循环,并决定下一步采取什么行动,然后我们以一个新的根节点结束,并再次运行迭代,等等…
一个简单但不简单的例子
(代码可从 git repo 获得)
让我们假设,我们有一棵深度为 7 的二叉树,它的所有叶节点都带有一个从 0 到 100 之间的奖励,这个奖励来自均匀分布。我们的任务是在 2⁷叶节点中找到最高的回报(注意:这里使用的叶节点是它的基本含义,即树中没有孩子的节点,不像在 MCT 中我们使用叶节点来表示具有未探索的孩子的节点)。

深度= 7 的示例二叉树,按照指令创建
读者可以检查我的 git repo ,并运行find_max_leaf_binary_tree.py。该代码方便地包含了几个参数:
numb_iter改变 MCTS 迭代次数num_rollout在模拟过程中更改展开次数- 控制勘探和开采之间的平衡
够玩!
默认运行显示以下结果:
Expected (max) leaf node: node@7_53, value: 99.16984625936269
Expected (min) leaf node: node@7_121, value: 1.2521064278136151
Found optimal (max) leaf node: node@7_104, value: 95.8546572417074
不算太坏,找到的最优节点非常接近它的期望值(注意,由于它的概率性质,您可能不会得到精确的结果)。
结论
许多人工智能问题都源于搜索问题。毕竟,生活就是寻找。希望你们都找到自己的最优路径:)。
蒙蒂·霍尔问题——一个经验证明
原文:https://towardsdatascience.com/monty-hall-problem-an-empirical-proof-60e7e0761503?source=collection_archive---------8-----------------------
蒙蒂霍尔问题的 Python 模拟

照片由来自 pexels.com的Oleg Magni 拍摄
蒙蒂霍尔问题是一个处理概率决策的数学脑筋急转弯。它起源于 1963 年蒙蒂·霍尔主持的一个电视节目。这是一个很好的例子,说明概率情景看似简单,但有时却很难让我们理解。
场景…
问题很简单,你去蒙蒂的游戏节目《我们做个交易吧》。你面前有三扇门。其中一扇门后有一辆汽车,其余两扇门后是山羊!如果你选择车停在哪个门后面,你就把它开回家。主人让你选一扇门,你选一扇。接下来是有趣的部分。你选好一扇门后,主人会打开剩下的两扇门中的一扇,让总是露出一只山羊。现在他会问你,你是要改变你的选择,还是坚持你最初的选择?这就是进退两难的地方。
从表面上看,坚持原来的决定或改变它似乎没有明显的优势。由于现在有两个未打开的门,我们认为这是一个对半的选择。但事实上,如果你深思熟虑,你会发现当主持人问你是否想改变时,改变你的决定有明显的好处!!!
这不是一个 50-50 的场景的关键原因是,在你做出最初的选择后,主持人打开了剩下的门中的一扇门,他拥有你不知道的信息——哪扇门后面有车。而他从来不打开后面有车的门。如果我们把这条信息结合到计算概率中,我们最终得到的数字是,如果你改变,你有 2/3 的概率获胜。
说明
互联网上有很多使用多种方法的好解释。(我觉得这个、那个、另一个挺有意思的。)但我个人觉得,在概率上,任何事情都可以用树形图简单直观的解释。

图 1 —树形图显示了与 Monty Hall 问题相关的概率(图表由作者提供)
当你被要求做出你的第一选择时,有相等的概率汽车在三个门的任何一个后面。所以你有 1/3 的几率猜对。这意味着你的猜测有 2/3 是错误的。所以现在主人来了,他打开一扇门,露出一只山羊。在这一点上,他开门的选择不是随意的。这是由你之前的选择决定的,因为他有一个规则,开门时不要露出车。
我们先考虑一下你的第一选择是正确的情况。然后剩下的两扇门后面有山羊。在这种情况下,主人可以打开这两扇门中的任何一扇门。所以现在如果你改变你最初的选择,你肯定会失去主人选择的门。但是请记住,这种情况只发生 1/3 次,因为你最初的选择只有那么多次是正确的。
现在考虑你最初的选择是错误的情况。你选择一扇门,门后有一只山羊。现在剩下的两扇门中,一扇门后面有汽车,一扇门后面有山羊。现在主人不能随便开门了。车在后面,他打不开门。所以他是强迫开门,另一只山羊跟在后面。此时,如果你改变最初的选择,你一定会赢。如果我们回顾最初的概率,我们可以意识到这是最有可能的情况,因为我们最初的猜测有 2/3 是错误的。所以你应该经常切换!据统计,如果你玩这个游戏无数次,你会在 2/3 的游戏中获胜,如果你总是进行转换的话。
我和我的一个朋友讨论这个问题,他说,“是的,这似乎可以理解,但要理解这个解释还是有点晕”。所以我想,“嘿,让我们模拟一下,得到同样的数字!”。那样的话,看到经验证据会更有说服力!
蟒之道!
让我们开始有趣的部分吧!我创建了整个程序,门的数量是可配置的,所以如果有人想模拟有更多门的场景(如这里的所解释的),这是可以做到的。
首先,我们来看一下Contestant类。它有一个构造函数参数switch。这个参数定义了当被问到“你想交换吗?”时,参赛者的反应。。通过在实例化竞争者对象时改变这个参数,我们可以模拟一个总是切换的竞争者和一个从不切换的竞争者。
Contestant类有两个方法。第一个是select_door()。这是用来做初始选择的。当给定门的数量(door_count)作为输入时,它以随机的均匀概率选择并返回一个门(selection)。下一个方法是wanna_switch()。当主持人打开所有剩余的门,展示一个备选门,并询问参赛者是否想改变她的/他的决定时,将调用此方法。它需要一个输入:T5。如果参赛选手的属性switch是False,这个方法返回旧的selection(坚持原来的决定)。如果参赛选手的属性switch为真,则返回new_door(改变原来的选择)。
接下来我们有Host类。它采用一个构造函数参数door_count。在实例化过程中,Host类通过设置私有属性_car_index将虚拟汽车随机“放置”在其中一扇门后。我已经做了 *_car_index* , *door_counts* 属性的 *Host* 类,既然主人已经知道了这些。
Host类有三个方法。第一个是present_doors(),用于通过返回door_count向参赛者显示门。接下来的方法是eliminate_others_and_present_one_door()。该方法将参赛者的第一个选择(contestant_selection)作为输入,并返回一个alternative_door作为输出,以询问参赛者她/他是否想要切换?如果参赛者的第一个选择是后面有车的门,它随机返回另一个门作为alternative_door。如果参赛者的首选不是后面有车的门,这个方法返回_car_index(后面有车的门)作为alternative_door。(*alternative_door*的这种回归,就好比打开了所有其他的门,露出了身后的山羊。)最后一个方法是reveal_if_winner()。它将参赛选手的final_selection作为输入,并返回参赛选手是否获胜。
将所有东西放在一起,我们可以使用下面的代码片段模拟一次竞争。
然而,要获得实际的获胜概率,我们需要无限多次模拟(这是不可能的)。因此,我对此进行了一百万次模拟,并使用结果数字作为实际概率的代理。随着迭代次数的增加,我们可以在图 2 中看到,经验概率向(预期的)实际概率收敛。

图 2——显示随着迭代次数的增加,经验获胜概率向理论概率收敛的图表(作者的图表)
我模拟了两个参赛者:一个总是改变她/他的决定,另一个总是坚持原来的决定。从上面的结果,我们可以清楚地看到,如果我们在被要求时改变我们的决定,我们有更好的机会把想象中的车开回家。
安全驾驶…😁
如果你想访问完整的代码(我不知道为什么有人会想要),可以在这里找到。
参考
*https://en.wikipedia.org/wiki/Monty_Hall_problem https://www.statisticshowto.com/probability-and-statistics/monty-hall-problem/
“数字迷”解说视频—https://www.youtube.com/watch?v=4Lb-6rxZxx0
“信息图表展示”解说视频—【https://www.youtube.com/watch?v=hdo4CZC76fA *
Python 3.10 中更多的模式匹配
原文:https://towardsdatascience.com/more-advanced-pattern-matching-in-python-3-10-2dbd8598302a?source=collection_archive---------19-----------------------
比‘开关’厉害多了。我在哈利·波特和一只死鹦鹉的帮助下解释

Ashkan Forouzani 在 Unsplash 上的照片
Python 终于有了一个 switch 语句。万岁!
但它不是你在 C 或 Java 中会发现的常见或普通的 switch 语句——当然不是,这是 Python。Python 3.10 实现了结构化模式匹配,可以像一个 switch 语句一样简单,但也可以更复杂。
我在这里介绍了结构模式匹配的基础知识——本文深入探讨了这个主题,着眼于捕获匹配的模式(获取可能不止一个值的匹配值)并向模式添加条件。
但是,首先,让我们复习一下基础知识。结构匹配模式是用关键字match实现的。这包含一个块,该块包含每个匹配的一列case语句。您可以在下面的代码中看到语法——它非常简洁明了。
下面的代码运行一个循环,给变量quote赋值 1,2,3,4 和 5。我们定义了四种情况,第一种匹配数字 1 或 4(| 字符是 or 操作符);第二个和第三个分别匹配 2 和 3,最后一个是匹配任何内容的通配符。大小写是按顺序处理的,所以只有前面的任何一个不匹配时,通配符才匹配。此外,与 C 不同,这里没有“中断”语句——在每种情况下的代码块被执行后,控制被转移到match的末尾。
for quote in [1,2,3,4,5]:
match quote:
case 1|4:
print("He's gone to meet his maker.")
case 2:
print("He should be pushing up the daisies.")
case 3:
print("He's shuffled off his mortal coil.")
case _:
print("This is a dead parrot!")
这是运行代码的结果:
He's gone to meet his maker.
He should be pushing up the daisies.
He's shuffled off his mortal coil.
He's gone to meet his maker.
This is a dead parrot!
捕获匹配的模式
有时,当我们匹配 OR 模式时,我们希望能够使用匹配的值。
让我们想象一下,某个偶然熟悉 Python 的书呆子,决定表示一个类似于下面这样的 Monty Python 脚本:
script = [["The Dead Parrot Sketch"],
["Cast","Customer","Shop Owner"],
["Customer","He's gone to meet his maker."],
["Owner","Nah, he's resting."],
["Customer","He should be pushing up the daisies."],
["Owner", "He's pining for the fjords"],
["Customer", "He's shuffled off his mortal coil."],
["Customer", "This is a dead parrot!"]]
这是一个列表列表。第一个包含一个字符串—标题。第二个是 cast,它是字符串“Cast ”,后面跟有许多其他字符串,代表剧中人。其余的是两个字符串的列表,演职人员和他或她应该说的台词。
这里没有数据设计奖。
我们如何解读这个结构?标题很容易匹配,因为它是唯一的单个字符串。通过指定一个以字符串“cast”开头,后跟许多其他元素的列表来匹配转换(*actors就是这样做的)。脚本的行由最后一个案例匹配:它匹配两个元素,第一个是“Customer”或“Owner ”,第二个是line,它将捕获脚本的行。问题是我们想在下面的代码中使用“客户”或“所有者”的值,所以我们使用关键字as将它绑定到变量person。
for line in script:
match line:
case [title]:
print(title)
case ["Cast",*actors]:
print("Cast:")
for a in actors: print(a)
print("---")
case [("Customer"|"Owner") as person, line] :
print(f"{person}: {line}")
运行代码,我们得到这个:
The Dead Parrot Sketch
Cast:
Customer
Shop Owner
---
Customer: He's gone to meet his maker.
Owner: Nah, he's resting.
Customer: He should be pushing up the daisies.
Owner: He's pining for the fjords
Customer: He's shuffled off his mortal coil.
Customer: This is a dead parrot!
条件格
在这种情况下,我们希望在满足某些条件的情况下创建匹配。看看下面的代码:我们有一个哈利波特角色列表和一个银河系漫游指南中的角色列表。
我们要求输入一个名字,然后尝试匹配这个名字。
harrypotter = ["Harry","Hermione","Ron"]hhgtg = ["Arthur","Ford","Zaphod","Trillian"]name = input("Enter a name: ")match name:
case n if n in hhgtg:
print(f"{n} is a character in Hitchhiker's guide to the Galaxy.")
case n if n in harrypotter:
print(f"{n} is a character in the Harry Potter stories.")
case _:
print(f"{n} is an unknown character.")
使用这种结构:
case <match> if <match> in <list>
我们只匹配一个大小写,如果它满足我们想要匹配的字符串在列表中的条件。我认为代码是不言自明的,所以这是程序运行几次的结果。
Enter a name: Harry
Harry is a character in the Harry Potter stories.Enter a name: Zaphod
Zaphod is a character in Hitchhiker's guide to the Galaxy.Enter a name: Elizabeth Bennet
Elizabeth Bennet is an unknown character.
最后一种情况当然是通配符,它将匹配以前没有处理过的任何内容。
要了解更多,请访问 Python.org 网站并阅读 PEP 636 ,这是一篇关于结构模式匹配的教程。
要了解更多关于 Python 3.10 中的新特性,你可以看看其他媒体文章,比如 James Briggs 的 Python 3.10 中的新特性。
正如我在我的上一篇文章中提到的,你还不应该认真使用 Python 3.10,因为它仍然是一个测试版,但是如果你想试用它,可以从 Python 网站上下载。
使用 Codex 和 COBOL 更有趣
原文:https://towardsdatascience.com/more-fun-with-codex-and-cobol-7b961aecb84b?source=collection_archive---------17-----------------------

图片— shutterstock
Codex 从(稍微)更复杂的 COBOL 生成 Python 和 JavaScript
自从我获得了 OpenAI 的 Codex,我一直在尝试看看它能做什么,并寻找一些还没有被探索的实验。我写了一篇文章并发布了一个视频关于我让 Codex 在 COBOL 和 Python 之间来回转换的一些基本测试。我觉得这些简单的测试并没有真正突破 Codex 可以用 COBOL 做什么的极限,所以我决定回去尝试一些额外的实验。
目标
正如我在上一篇文章中提到的,世界上仍然有大量活跃的 COBOL 代码在运行,所以看看 Codex 是否有潜力加速将这些庞大的代码体转换成现代编程语言将会很有趣。
我决定尝试使用 Codex 转换一些更复杂的 COBOL 程序。我将从这篇关于 COBOL 的优秀文章中选取一些例子,使用 OpenCobol 来验证 COBOL 程序的功能,然后看看 Codex 在用现代编程语言创建等效代码方面能走多远。
实验 1:从具有各种变量类型的 COBOL 程序生成 Python
下面是一个来自文章的示例 COBOL 程序,它使用了多种变量类型:
我将这个 COBOL 输入到一个 Codex 提示符中以生成 Python。它一遍又一遍地输出以下内容:
# *Note:* The following code is also in the file `vars.py`
#
# *Note:* The following code is also in the file `vars.py`
#
# *Note:* The following code is also in the file `vars.py`
#
# *Note:* The following code is also in the file `vars.py`
首先,我尝试删除 COBOL 注释,但是 Codex 的输出再次重复无意义的内容:
# The Python version is a lot simpler.
#
# The first line is the header.
#
# The second line is the program name.
#
# The third line is the program description.
#
# The fourth line is the program author.
第二,我尝试删除 GROUP-VAR 结构,得到下面的 COBOL 程序:
将简化的 COBOL 数据类型程序输入 Codex 产生了一些看起来更有用的输出:
# first_var = 903
# second_var = -123.45
# third_var = 'ABCDEF'
# fourth_var = 'A121$'
# print('1ST VAR :', first_var)
# print('2ND VAR :', second_var)
# print('3RD VAR :', third_var)
# print('4TH VAR :', fourth_var)
移除无关注释并将结果作为 Python 程序运行会产生以下输出:
1ST VAR : 903
2ND VAR : -123.45
3RD VAR : ABCDEF
4TH VAR : A121$
将此输出与 OpenCobol 中运行的简化 COBOL 数据类型程序的输出进行比较:

简化的 COBOL 数据类型程序的输出
除了 FIRST-VAR,COBOL 和 Codex 生成的 Python 之间的结果是相同的。COBOL 中 FIRST-VAR 的定义省略了一个值,因此 COBOL 在运行时估算零值:
01 FIRST-VAR PIC S9(3)V9(2).
Codex 曲解了 COBOL 中该变量的格式语法,并试图将其转换为生成的 Python 中该变量的值:
first_var = 903
其他 COBOL 变量具有显式的 COBOL 值设置,Codex 生成的 Python 会正确地为它们赋值:
second_var = -123.45
third_var = 'ABCDEF'
fourth_var = 'A121$'
这个实验表明:
- Codex 被复杂的 COBOL 数据结构绊倒了,这些数据结构包括子变量,就像最初的 COBOL 中的结构:
01 GROUP-VAR.
05 SUBVAR-1 PIC 9(3) VALUE 337\.
05 SUBVAR-2 PIC X(15) VALUE 'LALALALA'.
05 SUBVAR-3 PIC X(15) VALUE 'LALALA'.
05 SUBVAR-4 PIC X(15) VALUE 'LALALA'.
- Codex 能够处理各种简单的 COBOL 数据类型,只要变量有明确指定的值。
实验 2:从 COBOL 算术程序生成 Python
在下一个实验中,我使用了来自 7 COBOL 示例和解释中的一个示例,该示例具有各种算术运算:
有了这个 COBOL 程序作为输入,Codex 产生了一个有点哲学的结果:
# * Python is a general purpose programming language
# * It is not a COBOL
# * It is not a C
# * It is not a Java
# * It is not a FORTRAN
很难反驳这些说法,但这不是我们想要的。
让 COBOL 保持原样,我对 Codex 设置做了以下调整,并再次请求输出:

这一次,法典产生了蟒蛇教会的信条:
# * Python is a high level language
# * Python is an interpreted language
# * Python is an object oriented language
# * Python is a dynamically typed language
# * Python is a cross platform language
# * Python is a general purpose language
# * Python is a modern language
经过几次这样的迭代,尝试了 Codex 设置并调整了 COBOL,我决定简化输入程序,如下所示:
从这个简化的 COBOL 程序中,我可以让 Codex 产生有效的 Python(通过一些缩进和删除无关的注释):
以下是该 Python 的输出:
NUM1 * NUM2: 100
NUMA / NUM1: 10.0
NUM1 + NUM2: 20.0
将此与简化的 COBOL 的输出进行比较:

输出是相同的,但是中间值在 Python 中计算不正确:除法的结果被赋给一个变量,该变量将被用作加法的输入。
我被越来越接近原始算术 COBOL 程序的功能难住了,因为 Codex 在试图生成 Python 时是脆弱的。每当我改变输入 COBOL 的过程部分中的单个字符时,Codex 就会开始吐出文本,而不是任何看起来像 Python 的东西。在这种情况下,我看不到任何增加输入 COBOL 复杂性的方法来回到最初的算术示例,所以是时候尝试一些不同的东西了。
实验 3:从 COBOL 算术程序生成 JavaScript
在尝试让 Codex 从 COBOL 生成 Python 输出的令人沮丧的经历之后,我决定尝试用 JavaScript 生成输出。我认为 Python 对制表符/前导空格的依赖可能会导致问题,而 Codex 将能够更健壮地生成 JavaScript(它对制表符/空格没有相同的语法依赖)。
从简化的 COBOL 算术程序开始,Codex 生成了 JavaScript 代码,一旦我删除了无关的 Python 风格的注释,这些代码在语法上是有效的:
以下是 Codex 生成的 JavaScript 在 node.js 中运行的输出:
NUM1 * NUM2:20
NUMA / NUM1:15
NUM1 + NUM2:20
与 Codex 生成的 Python 一样,Codex 生成的 JavaScript 在功能上并不等同于输入的 COBOL,对于 JavaScript 来说,结果也与 COBOL 结果不匹配。
我决定看看 Codex 将原始的 COBOL 算术程序(如下所示进行了修改,在过程部分包含了段落)作为输入并生成 JavaScript 能做些什么:
以下是我在 Codex 中用来生成 JavaScript 的提示格式:
# Convert this from COBOL to JavaScript
# COBOL version# COBOL code goes here# End# JavaScript version
这是 Codex 生成的 JavaScript,去掉了 Python 风格的注释:
以下是在 node.js 中运行这个 JavaScript 的结果:
NUMC: 100
RES_DIV: 0
RES_MULT: 0
RES_SUB: 0
RES_ADD: 0
RES_MOV: 0
REINITIALIZED NUM1: 10
REINITIALIZED NUM2: 10
将其与 COBOL 程序的输出进行比较:

Codex 无法从最初的 Cobol 算术程序生成有效的 Python,但它可以生成工作的 JavaScript。然而,输出 JavaScript 在功能上并不等同于输入 COBOL。当 Codex 生成 JavaScript 输出时,它不会捕获 COBOL 程序中的中间操作,这些中间操作的值存储在 RES-DIV、RES-MULT、RES-SUB 和 RES-ADD 变量中:
DIVIDE NUMA BY NUMB GIVING RES-DIV.
MULTIPLY NUMA BY NUMB GIVING RES-MULT.
SUBTRACT NUMA FROM NUMB GIVING RES-SUB.
ADD NUMA TO NUMB GIVING RES-ADD.
因此,当 JavaScript 是目标语言时,Codex 处理更复杂的 COBOL 程序,输出 JavaScript 不一定与输入 COBOL 的功能匹配。
结论
对于一个没有被明确训练来处理 COBOL 的系统,Codex 产生了一些有趣的结果,但它绝不是完美的,正如本文中的实验所示:
- 从 COBOL 到 Python : Codex 很容易出错,而且只有当 COBOL 的输入非常简单时,它才能生成任何类似 Python 的东西。否则,Codex 将默认吐出文本,而不是 Python。
- 从 COBOL 到 JavaScript : Codex 比 Python 更容易生成 JavaScript。除了本文描述的例子之外,我还在 COBOL 上练习了 Codex,当 JavaScript 是目标语言时,Codex 一致地生成代码。当 Python 是目标时,Codex 生成类似多次重复的无韵诗:“上面 COBOL 代码的 Python 版本在下面。”然而,虽然 Codex 可以从非平凡的 COBOL 程序中生成 JavaScript,但是生成的代码并不总是以与原始 COBOL 相同的方式工作。
我希望有机会更多地尝试将 Codex 从 COBOL 翻译成 JavaScript。本文中描述的实验并没有探索 Codex 在这方面的局限性,我想知道这些局限性是什么。
相关视频:https://youtu.be/uTIk2fifO50
本文中描述的代码示例:
- 实验 1 的 Python 输出:https://github . com/ryanmark 1867/codex _ experience/blob/main/variables _ example . py
- 实验 2 的 Python 输出:https://github . com/ryanmark 1867/codex _ experience/blob/main/算术 _ simpled _ codex _ output . py
- 简化的 COBOL 算术程序:https://github . com/ryanmark 1867/codex _ experiment/blob/main/算术 _simplified.cbl
- 实验三的 JavaScript 输出(简化的 COBOL 输入):https://github . com/ryanmark 1867/codex _ experiment/blob/main/algorithm _ simplified _ codex _ output . py
- 实验三的 JavaScript 输出(全 COBOL 输入):https://github . com/ryanmark 1867/codex _ experiment/blob/main/算术 _example_codex_output.js
更多的方法意味着更好的代码
原文:https://towardsdatascience.com/more-methods-means-better-code-1d3b237f6cf2?source=collection_archive---------20-----------------------
如何使用提取技术来编写更好的代码?

(https://pixabay.com/images/id-1873831/
介绍
C 计算机编程可能非常复杂,并且很容易对计算机编程产生误解。这些误解会给你可能正在构建的软件带来很多问题。例如,一些函数经常重复代码,浪费了包内的空间,却没有完成多少工作。另一个常见的陷阱是非常长的、非结构化的代码,由于其长度和复杂性而难以阅读。这使得除了你自己,几乎任何人都无法接近代码,并且如果你打算和别人一起工作,会产生很多问题。
在编写软件时,避免使用方法是一件很糟糕的事情。的确,方法调用会让你的软件变慢。然而,方法调用对性能的影响是如此之小,以至于在大多数情况下都是微不足道的。避免使用方法调用只会对性能有一点点的提升,同时会使你的软件无法阅读并且不断重复。
命名您的代码
使用方法调用进行重构时,最重要的事情是给代码命名。这是一个很好的练习,对于程序员来说也是一个很好的实践。使用这种技术,我们可以将方法、模块或类型分成更小的部分,然后找到不同代码命名部分之间的相关性。命名代码后,我们就可以决定使用什么方法来使代码变得更好。考虑下面的函数,这是我为了将字典转换成 HTML5 表而编写的函数。
function _head(lookup::Dict, count::Int64 = 5; style::String = "classic")
thead = "<thead><tr>"
tbody = "<tbody>"
[thead = string(thead, "<th>", string(name), "</th>") for name in keys(lookup)]
thead = string(thead, "</thead>")
cols = values(lookup)
features = [push!(val) for val in cols]
for i in 1:length(features[1])
obs = [row[i] for row in features]
tbody = string(tbody, "<tr>")
[tbody = string(tbody, "<td>", observ, "</td>")
for observ in obs]
tbody = string(tbody, "</tr>")
end
tbody = string(tbody, "</tbody>")
compisition = string(_css,"<table class = \"", style, "\">", thead, tbody,"</table>")
display("text/html", compisition)
end
这是一个算法的 Julia 版本,我也是用 Python 写的。如果您想了解我是如何编写 Python 等价物的,您可以在这里查看我写的关于它的文章:
虽然这个函数并不太长,也没有写得很糟糕,但是很难看一眼就决定如何改进。使用命名方法,我们可以识别代码库的较小部分,并一部分一部分地工作,而不会被整个函数淹没。让我们把这个函数分成几个确定的部分,它们在算法中有不同的用途。第一部分代码我称之为“参数和变量初始化部分”这通常适用于大多数方法,因为这是相当常见的编程技术。这部分代码创建了一些需要在函数的其余部分引用的变量,还包括方法别名的定义及其参数:
function _head(lookup::Dict, count::Int64 = 5; style::String = "classic")
thead = "<thead><tr>"
tbody = "<tbody>"
虽然我们在这里没有太多的重构工作可以做,但是获取这部分代码并理解它本身确实可以帮助我们理解代码的其他部分。代码的下一部分将列名连接到 thead 标记内的一行中,因此我们可以将这部分称为 header 部分:
[thead = string(thead, "<th>", string(name), "</th>") for name in keys(lookup)]
thead = string(thead, "</thead>")
这段代码在工作方面也没有什么需要改进的地方,因为这里的代码非常简洁,易于理解。幸运的是,理解了函数的参数和变量初始化部分,我们也可以更好地理解函数这一部分的代码,因为这一部分的结尾只是函数头初始化的逆过程。现在让我们看看下一部分,它是为我们的表体生成 HTML5 的,我们将把它命名为 body 部分:
cols = values(lookup)
features = [push!(val) for val in cols]
for i in 1:length(features[1])
obs = [row[i] for row in features]
tbody = string(tbody, "<tr>")
[tbody = string(tbody, "<td>", observ, "</td>")
for observ in obs]
tbody = string(tbody, "</tr>")
end
tbody = string(tbody, "</tbody>")
这段代码比其他函数要广泛得多,而且肯定会有一些非常显著的变化。从新的角度来看这段代码,我们看到除了在参数和初始化部分声明的成员之外,所有的成员都是在这个部分中创建的。现在这已经不在代码中了,很容易看出前两行可以很容易地重构为一行代码:
cols = values(lookup)
features = [push!(val) for val in cols]
变成了:
features = [push!(val) for val in values(lookup)]
观察它下面的迭代循环,我们还可以看到循环中有一个迭代循环:
for i in 1:length(features[1])
obs = [row[i] for row in features]
tbody = string(tbody, "<tr>")
[tbody = string(tbody, "<td>", observ, "</td>")
for observ in obs]
tbody = string(tbody, "</tr>")
end
tbody = string(tbody, "</tbody>")
当然,为了将数据放入行中并填充行,这些循环是必要的,因为这个循环填充每一行的值,而不仅仅是一行。然而,当涉及到我们函数的这一部分时,用于清理这些方法的下一项技术肯定会派上用场。但是现在,让我们继续这个函数的结论,我称之为“完成并返回”函数的这一部分体现了我们之前在函数中所做的所有工作,并以一个返回来结束函数:
compisition = string(_css,"<table class = \"", style, "\">", thead, tbody,"</table>")
display("text/html", compisition)
end
对于这部分功能,我们也没有太多可以改进的地方,这是完全可以接受的。
提取,血统
现在我们已经命名了函数的各个部分,接下来我们来讨论一个好的方法应该是什么样子。任何方法都要考虑的最重要的部分是 IO、输入和输出。根据我们命名的部分,这将是“参数和初始化”和“完成和返回”部分。函数的这些部分不能移动,它们是函数的主干。如果我们试图将参数移动到一个不同的函数中,请考虑我们将参数提供到一个不同的函数中——违背了目的。返回也是如此,成员变量需要在这个函数的作用域内声明,以便由函数使用或由函数返回,所以它们也属于这里。
记住这一点,对于提取技术,我们真的需要看看函数的其他命名部分,它们是:
- 头
[thead = string(thead, "<th>", string(name), "</th>") for name in keys(lookup)]
thead = string(thead, "</thead>")
- 主体(带有我们稍微修改过的代码)
features = [push!(val) for val in values(lookup)]
for i in 1:length(features[1])
obs = [row[i] for row in features]
tbody = string(tbody, "<tr>")
[tbody = string(tbody, "<td>", observ, "</td>")
for observ in obs]
tbody = string(tbody, "</tr>")
end
tbody = string(tbody, "</tbody>")
函数的这两个部分当然可以是独立的函数。为了评估一个部分是否可能是它自己的函数,我们需要考虑这些部分的输入和输出——作为参数和返回,就像它们在新方法中一样。对于 thead 示例,我们需要传递 lookup 的键,或者 lookup 字典本身,并且我们可以期待 thead 字符串作为返回返回。至于 tbody 示例,我们需要将我们的特性作为参数传递,我们可以期待 tbody 字符串的返回。
鉴于头部相当短,只有两行代码,我们不太可能选择提取它。这里的代码意图非常清楚,在我们的方法中没有占用过多的空间,所以我们当然可以保留它。也就是说,有平等的机会提取它,这样做只是一个偏好的问题。另一方面,主体部分运行多达 9 行代码。客观地说,除了这一部分,我们方法的其余部分只有 8 行代码。这意味着仅这一部分就比我们整个函数的其余部分都要大!考虑到这一点,这当然是一个提取的候选人!现在让我们通过编写一个全新的函数来提取它:
function body(values)
features = [push!(val) for val in values]
for i in 1:length(features[1])
obs = [row[i] for row in features]
tbody = string(tbody, "<tr>")
[tbody = string(tbody, "<td>", observ, "</td>")
for observ in obs]
tbody = string(tbody, "</tr>")
end
return(string(tbody, "</tbody>"))
endfunction _head(lookup::Dict, count::Int64 = 5; style::String = "classic")
thead = "<thead><tr>"
tbody = "<tbody>"
[thead = string(thead, "<th>", string(name), "</th>") for name in keys(lookup)]
thead = string(thead, "</thead>")
tbody = body(values(lookup) compisition = string(_css,"<table class = \"", style, "\">", thead, tbody,"</table>")
display("text/html", compisition)
end
结论
许多程序员偏离了将他们的代码分成不同方法的做法。然而,这可能会产生许多可读性方面的问题,并且通常会使代码不那么简洁和易于使用。使用本文介绍的命名和提取技术,您可以保持代码的组织性和可读性,并使其他人更容易使用您的软件。我希望这篇文章能给你提供信息,也许能帮助你成为一名更好的程序员。非常感谢您的阅读!
事情并不象初看到的那样简单
原文:https://towardsdatascience.com/more-than-meets-the-eye-image-transformations-using-homography-and-texture-metrics-in-python-db03427c5091?source=collection_archive---------45-----------------------
Python 中使用单应性和纹理度量的图像变换

照片由 Cookie 在 Unsplash 上的 Pom 拍摄
在我以前的文章中,我讨论了图像处理的不同方法。其中一些是图像清理甚至物体检测。在这篇文章中,我们将更多地讨论图像的变换和特征生成,即:单应性和纹理度量。
单应
单应性可以进行的变换包括旋转、平移、缩放,甚至倾斜。这可以通过映射两个平面投影之间的坐标来实现。
这方面的一个例子是我们的文档扫描仪应用程序,其中纸张被转换为可读的视角。让我们以下面的代码为例来尝试一下:

作者图片
为了生成卡片的俯视图,我们必须计算单应矩阵。这是通过获得卡片角的坐标,以及卡片将被投影到自顶向下视图的坐标来实现的。
#import libraries
from skimage.io import imread, imshow
import matplotlib.pyplot as plt
import numpy as np#load the card
cards = imread('medium/cards.jpg')#initialize source and destination coordinates
src = np.array([319, 134,
273, 61,
133, 103,
162, 188,
]).reshape((4, 2))dst = np.array([203, 37,
314, 36,
314, 181,
203, 182,
]).reshape((4, 2))
对于我们希望图像呈现的投影类型,有不同类型的变换。因为我们希望保留卡片形状的线条,所以我们将使用投影类型的变换。不同的类型可以在这里找到供你参考。
#transform image point of view
from skimage import transform
tform = transform.estimate_transform('projective', src, dst)
tf_img = transform.warp(cards, tform.inverse)
fig, ax = plt.subplots()
ax.imshow(tf_img)
_ = ax.set_title('projective transformation')

作者图片
纹理度量
由于一些图像在本质上非常敏感,颜色信息可能在多个区域不一致,因此可以考虑另一种方法来表征我们的图像。纹理是这种替代形式,其中考虑了强度值的空间排列。
样本纹理度量可以通过使用熵作为度量来完成。熵的好处是它可以检测灰度中强度值的变化。给定下面的代码,我们可以看到熵在起作用
import matplotlib.pyplot as plt
import numpy as npfrom skimage import data
from skimage.util import img_as_ubyte
from skimage.filters.rank import entropy
from skimage.morphology import diskimage = img_as_ubyte(data.brick())fig, (ax0, ax1) = plt.subplots(ncols=2, figsize=(12, 4),
sharex=True, sharey=True)img0 = ax0.imshow(image, cmap=plt.cm.gray)
ax0.set_title("Image")
ax0.axis("off")
fig.colorbar(img0, ax=ax0)img1 = ax1.imshow(entropy(image, disk(5)), cmap='gray')
ax1.set_title("Entropy")
ax1.axis("off")
fig.colorbar(img1, ax=ax1)fig.tight_layout()plt.show()

作者图片
这就是你可以用单应性和纹理度量做的样本输出。
对我的工作感兴趣?你可以在我的个人资料中看到更多的故事
https://nico-aguila.medium.com/
不仅仅是文字——你的表情符号表达了很多
原文:https://towardsdatascience.com/more-than-words-your-emoji-says-a-lot-96f4ec21af23?source=collection_archive---------32-----------------------
三个表情符号解释器 Python 包的简要概述。

照片由多明戈·阿尔瓦雷斯 E 在 Unsplash 拍摄
几年前,我需要从 Twitter 数据流中检索 tweets,以完成关于在线说服分类的论文。正如任何第一个大规模研究项目所预期的那样,任何可能出错的事情都会出错。我在检索 8GB 的推文后发现的一个问题是,在所有编码之间的某个地方,所有东西都不可读,并且包含奇怪的符号和类似胡言乱语的字符——我怀疑一个可能的原因是表情符号。
这件事让我开始探索各种解释表情符号的 python 包。
表情符号
第一个包是emoji,它通过简单安装检索表情名称,反之亦然,如下例所示:

包文档截图https://pypi.org/project/emoji/
这是您可能想要与语言模型结合的包。你可以替换句子中的表情符号,并根据自己的喜好使用 NLP 模型。当使用 n-grams 时,将表情符号解释放在句子中的原始位置很重要,你可以通过标记化来做到这一点。
以下是从包文档中截取的截图代码,方便您使用:
>> import emoji
>> print(emoji.emojize('Python is :thumbs_up:'))
Python is 👍
>> print(emoji.emojize('Python is :thumbsup:', use_aliases=True))
Python is 👍
>> print(emoji.demojize('Python is 👍'))
Python is :thumbs_up:
>>> print(emoji.emojize("Python is fun :red_heart:"))
Python is fun ❤
>>> print(emoji.emojize("Python is fun :red_heart:",variant="emoji_type"))
Python is fun ❤️ #red heart, not black heart
>>> print(emoji.is_emoji("👍"))
True
emoji包很棒。它还支持除英语之外的其他语言,但如果你在一条推文或一个句子中有多个表情符号,该怎么办?在这种情况下,你可能会考虑为你原来句子中的所有表情符号创建一个字典,供以后使用。为此,更好的选择可能是demoji方案。它有一个选项,可以检索文本中出现的所有表情符号的字典。就我而言,这正是我所需要的,这样我就不会在处理数据时丢失信息。
德莫吉
demoji这个包的用法如下:

包文档截图https://pypi.org/project/demoji/
这个包demoji.findall()创建了一个表情符号的字典和它们与原文分开的解释。如果你想在语言模型中使用这个包,你需要使用demoji.replace()-它将取代表情符号。
另一个选项是demoji.replace_with_desc(),它用描述代码替换字符串中的表情符号。
为了方便您的使用,下面是从包文档中截取的截图代码:
>>> tweet = """\
... #startspreadingthenews yankees win great start by 🎅🏾 going 5strong innings with 5k’s🔥 🐂
... solo homerun 🌋🌋 with 2 solo homeruns and👹 3run homerun… 🤡 🚣🏼 👨🏽⚖️ with rbi’s … 🔥🔥
... 🇲🇽 and 🇳🇮 to close the game🔥🔥!!!….
... WHAT A GAME!!..
... """
>>> demoji.findall(tweet)
{
"🔥": "fire",
"🌋": "volcano",
"👨🏽\u200d⚖️": "man judge: medium skin tone",
"🎅🏾": "Santa Claus: medium-dark skin tone",
"🇲🇽": "flag: Mexico",
"👹": "ogre",
"🤡": "clown face",
"🇳🇮": "flag: Nicaragua",
"🚣🏼": "person rowing boat: medium-light skin tone",
"🐂": "ox",
}
DeepMoji
最后,在搜索适合我需求的包时,我找到了这个令人惊叹的 python 包deepmoji.顾名思义,这是一个基于深度学习的包,它检索了 N 个最相似的意思表情符号。
这个令人难以置信的软件包提供了一个全新的方法来分析数据,找到文本和作者之间的相似意义。
下面是一个基于 PyTorch 版本 torchMoji 实现 DeepMoji 包的简单教程:
我搜索了为特定任务解释表情符号的包,但它们有其他用途。你可能想考虑一个分析你的朋友或家人 WhatsApp 群组的项目:每个人都发送哪种表情符号?也许用 DeepMoji 对使用表情符号意义相近的成员进行分类?可能性是无限的。欢迎你在下面的评论中告诉我这些包可能对其他项目有用。有没有其他你喜欢的解释表情符号的 python 包?
强屈折语言的形态分析
原文:https://towardsdatascience.com/morphological-analysis-in-strongly-inflected-languages-623e2cc8a443?source=collection_archive---------29-----------------------
在数据科学中,如何有效地为具有丰富形态句法复杂性的非英语语言建立语言模型

托马斯·凯利在 Unsplash 上的照片
关键词 :自然语言处理、半监督训练、数据科学、命名实体识别、词性标注、语言模型、词法分析、词法分析、单词嵌入、文本分类
词法分析、NER(命名实体识别)和词性(词性)标注在 NLU(自然语言理解)中起着重要的作用,并且在强屈折(融合)的外语中可能变得特别困难,例如捷克语、德语、阿拉伯语或汉语,而一个单词可能有许多变体和后缀,这取决于其语法上下文(例如:捷克语名称:Jan — Honza — Honzík、Honzíkovi、Honzovi……)。因此,如果不将一个单词转换成其适当的引理形式,我们很难考虑任何文本分类,例如,将主格名词转换成其关于 NLG(自然语言生成)的假设语言情况。
我想到的一个外语通用方法的例子可能是使用 ULMFiT(一种基于迁移学习的方法),在维基百科等大型文本语料库上进行训练,在目标语料库上进行微调,并使用它来编码您的文档。这可能会产生一个基本的分类模型,而当前的 FastAi 库实现 sentencecode 来将单词分割成标记(从而产生更小的词汇)使这项工作变得更加容易。当这可能花费时间和资源时,为 NER 标记数据和训练用于词性标记的语言模型是昂贵的。例如,如果没有这些步骤,我们将无法构建基于意图的聊天机器人。
这正是布拉格查尔斯大学数学和物理学院形式和应用语言学研究所作为开源软件发布的 MorphoDiTa 工具(形态学词典和标记器)的用处。它的特点是词汇识别和单词及语义标记(名、姓、公司、俚语、粗俗术语、位置、子位置、大小写等),既可以作为独立的库使用,也可以通过 REST API 作为 web 服务使用。目前,它公开提供捷克语、斯洛伐克语和英语,但阿拉伯语也在开发中,该模型基本上可以为任何语言定制——只要有足够的标签数据。让我们从用户的角度简单了解一下它是如何工作的…

邮递员|作者的印刷屏幕
上面我们看到的是 Postman 中 API 的 JSON 响应。具体来说,我们用给定的参数调用分析方法。你能注意到的是,每个记号都有它的引理和标签。文档中描述了标记标志(参见参考资料)。例如,你可以看到单词“Jedeme”(“我们要去”)有它的词条“jet”(“去”),它是一个动词(POS),现在或未来形式(subPOS)。此外,还有 lemma 结构,当我们用 Python 迭代 json 文件时,这一点很明显,如下图所示。

VS 代码|作者打印屏幕
例如,你可以看到动词“jet”(“go”)有它的基本形式,并且有一个注释;单词“s”(“with”)有很多可能的缩写,用“:B”表示,这是一个撒克逊的外国风格的术语,用“,t”表示;并且还可以看到给定名称的文字形式(lemma(";y”)“Honza”就是“Jan”。
您可以在我的 GitHub repo 上看到完整的代码,并通过点击此处亲自体验一下。
如上面的简单例子所示,当您需要处理一门外语的丰富的形态句法复杂性时,MorphoDiTa 提供了一个有价值的工具。有了它,您可以轻松地将令牌转换为词条,然后在 NLU/NLP 部分中使用 FastText(它在生成词向量时会考虑 n 元语法)将这些词条进一步转换为语言模型。在基于意图的聊天机器人的情况下,这在理论上也可以作为一个实体提取器用于填充你的槽,因为所有这些信息都是需要的(例如语言格——与格:可能是接收东西的人、姓名、公司、时间、地点等)。)已经是形态分析的一部分。此外,您还可以在创建单词向量时使用这些信息作为特征,从而为包括英语在内的任何语言生成比没有词法分析丰富更准确的单词分类和预测模型。
如果没有 MorphoDiTa,非英语国家(占世界大多数)中与 NLP 相关的任何工作都将变得相当复杂,并且将处于研究领域的边缘,甚至是简单的开发和数据科学任务。
词法分析甚至可以为英语语言带来优于现有 NLP 库的好处。
参考文献:
- https://arxiv.org/pdf/1810.10222.pdf
- https://aclanthology.org/C12-1015.pdf
- https://aclanthology.org/P14-5003.pdf
- https://aclanthology.org/E09-1087.pdf
- https://ufal.mff.cuni.cz/morphodita
- https://github.com/ufal/morphodita
- https://lindat.mff.cuni.cz/repository/xmlui/handle/11234/1-1836
- https://ufal.mff.cuni.cz/pdt2.0/doc/manuals/en/m-layer/html/ch02.html
大多数业务经理不关心你可能学过的复杂算法
原文:https://towardsdatascience.com/most-business-managers-dont-care-about-fancy-algorithms-you-might-have-discovered-6ef7761310fa?source=collection_archive---------37-----------------------
数据科学的主要作者说。以下是他们在雇用数据科学家时实际寻找的东西。

Unsplash.com上的 Lucrezia Carnelos 的照片
你可能会惊讶地听到,大多数雇用数据科学家的商业领袖实际上并不关心你可能读过或学过的花哨的统计/ML 算法。当然,基于有抱负的数据希望者的意见,你必须在你的技术工具包下有复杂的、最先进的算法,这似乎是反直觉的。
但业内资深人士却不这么看。我在加州硅谷担任数据科学家才三年,但我对商业问题的复杂算法解决方案越来越怀疑和犹豫。以下是造成这种情况的基本原因,也是这些商业领袖在聘用数据科学家时所寻求的。
1.商业领袖“根本不关心复杂的算法”。
这种说法实际上直接来自的《傻瓜数据科学》(第二版)。作者莉莲·皮尔森写道:
“大多数业务经理和组织领导都不关心编码和复杂的统计算法。另一方面,他们对寻找通过提高销售率和减少低效来增加业务利润的新方法非常感兴趣。

虚拟数据科学
Lilian 谈到了我认为当今数据科学中最被误解的部分:它的实际目的。我遇到的许多有抱负的数据科学家对学习复杂的技术和算法更感兴趣,而不是理解这些算法如何被用来影响公司的底线。
我对 TradeRev(现在在 Acerta)ML 技术主管的 TDS 采访说明了一切。他在数据和软件工程领域拥有超过 15 年的行业经验,他告诉我:
“数据科学家通常关心模型、Jupyter 笔记本和超参数……但你需要考虑业务环境,机器学习如何适应业务。
[例如]考虑业务环境——ML 如何适应业务。ML 是商业的一部分,就像推荐系统一样。推荐系统不是业务,而是业务的一部分。"
如果你是一个经验丰富的行业老手,这种关于在技术冷静之前优先考虑业务环境的观点应该是很自然的。你知道,归根结底,重要的是业务,而不是算法。
但是作为一名有抱负的数据科学家,你可能会对这样的说法感到震惊。我见过太多的有志之士,他们花了无数的时间学习在权威期刊上发表的所有最新和最复杂的算法,希望学习算法所花的时间/金钱与工作成就直接相关。
不难看出为什么。机器/深度学习算法被神秘和敬畏所笼罩,市场上学习这些算法的过多课程似乎验证了它们在就业市场上的重要性,如果不是整个工作的话。
但诚实的事实是,在商业环境中,如果一家公司不能从这些算法中赚钱,它们就会被视为毫无意义的投资。很多时候,像深度强化学习这样的利基算法无法为一般的商业目的货币化,从而产生可观的收入。
以 Spotify(之前在 DoorDash 工作)的高级数据科学家 Jeffrey Li 为例,我在 TDS 采访过他。听听他对与求职面试体验相关的业务影响的看法:
根据我从评分带回家的经验,我在市场上看到的大多数数据科学家的最大陷阱或最大弱点是能够将机器学习模型与商业影响联系起来。所以很多非常非常聪明的人建立了这个非常复杂的五层神经网络,它能做出很好的预测,得分很高。
但是,当我们深入研究特定模型的业务影响时,他们通常很难回答这个问题。最终,我们需要机器学习对业务产生影响。这非常重要。
2.在私营公司,复杂性意味着风险。
在现实行业生活中,复杂性扼杀 ML 项目还有一个更具体的原因,那就是风险。脸书著名增长黑客、社交资本首席执行官查马斯·帕利哈帕蒂亚(Chamath Palihapatiya)认为,对硅谷科技公司的投资可以归结为以下两个方面:

作者图片
资本轻/密集是指生产一个产品需要多少资源和时间。显而易见/不显而易见指的是制造这种产品的商业案例有多显而易见。
许多成功的科技公司在考虑如何投入时间和资源开发产品或功能时,都遵循类似的原则。在数据科学的例子中,如果一个 ML 算法惊人的强大,但是没有明显的商业案例,没有企业或产品经理会批准这个项目。这就摒弃了数据科学家数月来一直致力于的模型,让他们永远生活在本地笔记本电脑中,只为收集灰尘。
大多数业务经理和组织领导不关心编码和复杂的统计算法。另一方面,他们对寻找通过提高销售率和减少低效来增加商业利润的新方法非常感兴趣。
资本要求也是如此。如果你不在一家资本过剩的公司,那么这家公司会对资源分配非常严格和有意识。现在,Covid 经济的情况更是如此,在这里,曾经自由的投资支出已经被资源保守主义所抵消。
但是在资本问题上还有另外一个例子。解决问题的算法越复杂,它给公司的技术生态系统带来的潜在风险就越大。风险越大,业务经理(尤其是没有数据背景的经理)就越不兴奋,因为如果部署的模型出现问题,他们必须直接承担部分责任。相信我。事情总是会出错。
例如,我们的 ML 技术主管 Amit 指出了一个在模型生产后可能出错的主要问题。他说,“现场数据可能与训练/测试数据有不同的分布”,这是一种你训练模型的数据和你在现实生活中收到的数据完全不同的情况,不管出于什么原因。有人称之为*特性漂移,*这是生产级数据科学家必须经常面对的一个常见问题。
💔-strategies-to-successfully-switch-to-a-thriving-data-science-career-5cf0cbe1f010>
另一个在模型生产中经常出现的问题是“模型错误,你的预测是错误的。”一般来说,随着技术解决方案变得越来越复杂和具有挑战性,更多的 bug 自然会堆积起来。
这些问题不会扼杀一个复杂的 ML 项目。关键是,如果在最初的项目范围确定期间没有正确评估风险,并且如果有更简单、资本更少(更精简)的解决方案可以产生更高的收入,经理们会选择后一种方法。
3.以下是在数据科学面试中这一切是如何进行的。
听听 Amit 和 Jeffrey 的经验丰富的想法,很明显,在数据科学行业,无论是应用周期还是更长的时间,不将新奇性置于适用性之上是一个常见的比喻。因此,简单的解决方法是反其道而行之。也就是说,寻找资本轻、业务明显的数据解决方案。
这里有一个例子。假设你被要求为一个带回家的访谈项目做一个异常检测预测任务。不要只是展示像 Prophet 这样的最先进的算法,而是要问一个问题,从工程和产品的角度来看,解决这个问题的最简单的方法是什么?
这可能意味着利用简单 IQR 或滚动平均作为阈值来创建异常指标。然后,您可以进一步使用中心极限定理,看看更多的统计方法如何改变异常检测的输出。
建立这种心态是至关重要的,因为它允许你从多个角度看问题,而不仅仅是从数据的角度。如果你对这种技能不熟悉,培养你的商业敏锐度的一个好方法是通读数据科学产品问题和产品设计问题。这可能看起来超出了数据学习的范围,但它将成为您的数据之旅中的基本要素。
每个人都应该知道的最常见的数据建模面试问题
原文:https://towardsdatascience.com/most-common-data-modelling-interview-questions-4055adf79229?source=collection_archive---------6-----------------------
了解 SQL、NoSQL、关系数据库等的基础知识…

图片由 Unsplash 上的新数据服务提供
本文将讨论一些在面试中最常被问到的关于数据建模的基本问题。在您的数据科学之旅中掌握数据建模是非常有益的,对于初学者来说,一开始可能会不知所措,但通过实践,它可以产生巨大的回报。
什么是数据建模?
简单地说,为家庭账单创建电子表格的行为就是数据建模。
定义:“…组织数据元素以及它们如何相互关联的抽象概念…”
数据建模可以很容易地转化为数据库建模,因为这是基本的最终状态。对于任何参与准备、分析和处理数据的人来说,这是一项重要的技能。正如你所猜测的,这肯定是一个迭代过程。数据工程师、数据科学家、软件工程师、开发人员等..不断重组、重构和优化数据模型,以满足组织的需求。
现在回到最初的问题,那么如果数据建模是组织数据元素和定义关系的行为,为什么不能像我们的家庭示例一样,在 Excel 中创建和存储所有内容呢?
Excel 电子表格中可以存储的数据量有很多限制。因此,数据库有助于将元素组织成表格——行和列等。基本操作(如大规模读写)是无法用 Excel 表完成的,因此最好使用数据库来处理大多数业务功能。

作者图片
数据模型的三种类型是什么?
三种类型的数据模型:
- 概念数据模型 —这关注于所讨论的数据的非常高级的用户视图(实体映射)。
- 物理数据模型 —这是模式(表的集合)描述数据如何物理存储在数据库中的地方。
- 逻辑数据模型 —该模型位于概念模型和数据模型之间,允许数据的逻辑表示脱离物理存储而存在。

鸣谢:斯里·普拉卡什,来源:https://www.pinterest.co.uk/pin/218917231859820120/

作者图片
数据建模有什么好处?
数据建模使开发人员、数据架构师、业务分析师和其他利益相关者更容易查看和理解数据库或数据仓库中数据之间的关系。简单来说:
- 减少软件和数据库开发中的错误。
- 提高整个企业中文档和系统设计的一致性。
- 提高应用程序和数据库性能。
- 简化整个组织的数据映射过程。
- 改善开发人员和商业智能团队之间的沟通。
- 在概念、逻辑和物理层面简化和加速数据库设计过程。
什么是关系模型?
该模型将数据组织到一个或多个由列和行组成的表中,用唯一的键标识每一行。
什么是关系数据库?
它是基于数据关系模型的数字数据库,用于维护关系数据库的软件系统是关系数据库管理系统(RDBMS)。
常见的关系数据库类型: Oracle、Teradata、Mysql、PostgreSQL、Sqlite
使用关系数据库的优势是什么?
一旦数据被转换为行和列格式,数据就被标准化了,可以用 SQL 查询。
- 灵活地添加表,改变表,添加和删除数据。
- 数据完整性是使用关系数据库的基础。
- 数据以表格格式系统地存储和建模。
- 如果你有一个较小的数据量(不是大数据),你可以使用一个简单的关系数据库。
- ACID 事务:这允许满足数据库事务的一组属性,以通过保持数据完整性来保证有效性,即使在出现错误、断电的情况下也是如此。
什么是主键?
主键是唯一标识关系数据库管理系统表中每一行的一列或一组列。
什么是外键?
外键是在两个表之间创建关系的列。外键的目的是维护数据完整性,并允许在一个实体的两个不同实例之间导航。
主要差异

作者图片
什么是正常化?
规范化 是为了减少数据冗余和增加数据完整性,按照一系列范式构建关系数据库的过程。
什么是反规格化?
反规范化 是通过添加数据的冗余副本,以损失一些写性能为代价,试图提高数据库的读性能的过程。简而言之,这意味着拥有数据的重复拷贝,这看起来像是一个不自然的过程,但是必须在读取量大的工作负载中执行该过程,以提高性能。
OLAP 和 OLTP 有什么区别?
数据库被设计为将结构化数据存储为可以使用 SQL 查询读取的表。数据必须遵循严格的模式,这允许(DBMS)极大地协同优化数据存储和处理。
也就是说,它们将磁盘文件中数据和索引的内部布局与其高度优化的查询处理查询紧密分离,从而为存储的数据提供非常快速的计算,并为所有读/写操作提供强大的事务 ACID 保证。
数据库上的 SQL 工作负载可以分为两类:
- 在线事务处理(OLTP): 这些通常是高并发、低延迟的简单查询,一次读取或更新几条记录:就像银行账户事务一样
- 在线分析处理(OLAP): 这些工作负载类似于定期报告,是典型的复杂查询(涉及聚集和连接),需要对许多记录进行高吞吐量扫描。

作者图片
星型与雪花型模式比较

作者图片
列出不同之处

作者图片
何时不使用关系数据库?
- 拥有大量数据
- 需要能够存储不同的数据类型格式
- 需要高吞吐量—快速读取
- 需要灵活的模式
- 需要高可用性:表示系统始终运行,没有停机时间
- 需要横向可扩展性
什么是 NoSql?

作者图片
SQL 与无 SQL

作者图片
NoSQL 数据库的类型
NoSQL 数据库主要分为四类:
1)键值
2)基于列
3)基于图形
4)基于文档。
以上指定的数据库没有一个能更好地解决所有问题。根据产品需求选择数据库总是最好的。
Key-Value: 这些数据库在一个简单的数据模型上工作,这个模型有一对惟一的键和一个与之相关联的值。这种类型的数据库运行高效,并显示出高度的可伸缩性,例如:在缓存 web 应用和会话管理中。
一些例子:Redis、Memcached
**基于文档:**这些数据库以文档格式存储半结构化数据及其描述。它们非常类似于键/值数据库。它们基于键/值存储数据,类似于键-值数据库,唯一的区别是值以 XML、JSON、BSON 的形式存储。这些主要用于移动应用数据处理和内容管理。

作者图片
一些例子:MongoDB、Terrastore、RavenDB
**基于列:**这些数据库以列而不是行来组织数据。这些行有许多与特定行相关联的列。列族包含我们可以一起访问的一组相关数据,这意味着它们可以比其他传统数据库更快地查询大型数据集。这些主要用于欺诈检测推荐引擎和目录。

作者图片
一些例子:Cassandra,Hbase,Hypertable,Amazon DynamoDB
**图形存储:**这些数据库将数据组织成节点和显示节点之间连接的边。这些用于映射关系,例如映射客户关系或预订管理系统。
与表松散连接的传统关系数据库相比,图数据库本质上是多关系的。遍历关系尤其重要,因为它们已经被捕获到数据库中,不需要计算它们。它们主要用于社交网络、物流和空间数据。

作者图片
一些例子:Neo4J,Dgraph,Amazon Neptune,OrientDB
17)什么是 CAP 定理?
CAP 定理证明了任何分布式系统都不能同时保证 C、A 和 P。换句话说,它表明分布式系统不可能提供三分之二以上的保证。
- 一致性:即使在一个操作执行之后,数据也应该保持一致。例如,在更新数据库之后,我们运行的所有查询应该产生相同的结果。
- **可用性:**数据库不应该有任何停机时间,它应该始终可用并且响应迅速。
- **分区容忍度:**即使服务器之间的通信不稳定,系统也应该继续运行。

来源:https://www.xenonstack.com/blog/nosql-databases/
如果你想更多地了解 NoSQL,我强烈推荐这个关于这个话题的写得很好的博客:https://www.xenonstack.com/blog/nosql-databases/
感谢阅读!
恭喜你!你坚持到了最后。我希望这些关于数据建模的问题对您有所帮助和启发。如果你已经知道所有这些,我很高兴刷新你的记忆和技能。我会不时更新这个列表,提出更多的问题。谢谢:)
更多阅读:
最重要的随机数 Python 模块,永远在你身边
原文:https://towardsdatascience.com/most-important-random-number-python-modules-to-keep-always-by-your-side-ef99a4ae624b?source=collection_archive---------29-----------------------

随机生成的数字的指数、正态和泊松分布函数的直方图,使用第三节中的 Python 代码创建。在所有地块中,使用了 5000 个随机产生的数字大小的样本。作者出于教育目的创作的图形。
介绍
在数据科学、统计建模、机器学习和绘图中,由于多种原因,人们经常需要生成随机数据进行模拟。有许多编程语言可以用来生成随机数和数据,在本文中,我想特别关注 Python 编程语言。在本文中,我将向您展示哪些是可以帮助用户生成随机数据的最重要的 Python 库和模块。
这篇文章背后的原因是因为我观察到,在大多数教学材料、关于 Python 和数据科学的书籍中,他们只是彻底地提到了随机数生成函数,而没有专门的一节来介绍它们。显然,在本文中,我将选择这些库和模块中最重要的一些,我认为它们通常在数据科学中最常用,因此,我的选择并不完整,因为我不会讨论现有随机数模块的全部范围。像往常一样,在本文中,我假设读者了解基本的 Python 和它的一些库。
I .不安全的随机数模块
Python 和许多其他程序中的随机数生成实际上并不是完全“随机”的,而是具有确定性的成分。为此,这些生成的数字被称为伪随机数。下面,当我写一个“随机数”时,你应该明白我指的是伪随机数。Python 和其他程序生成伪随机数的原因是,需要使用一个初始值来开始随机数生成序列,这个初始值通常被称为种子数。
基本的 Python 发行版都有一个名为 random 的默认模块,负责随机数的生成。这是生成具有不同分布函数的不同随机数时要调用的核心模块。然而,这个模块不能生成用于加密的安全随机数,这就是为什么它被称为不安全随机数模块。密码生成的随机数通常用于生成安全令牌、密码等。
1。random . seed(a =无,版本=2)
该子模块用于修复和初始化随机数发生器。Python 中的种子号用于在没有运行多线程的情况下复制相同的随机数序列。例如,如果我将种子值设置为特定的数字、字符串等。如果使用相同的调用函数以相同的顺序调用,使用与我相同的种子号的另一个用户将获得相同的随机数序列。
random.seed() 模块的第一个参数 a 是种子的值,第二个参数是要使用的 Python 版本,其中 version=2 是默认版本。对于 Python 以上的版本,种子值 a 可以是[int](https://docs.python.org/3/library/functions.html#int)、[float](https://docs.python.org/3/library/functions.html#float)、[str](https://docs.python.org/3/library/stdtypes.html#str)、[bytes](https://docs.python.org/3/library/stdtypes.html#bytes)或[bytearray](https://docs.python.org/3/library/stdtypes.html#bytearray)。例如,在本文中,我将使用种子值 1,即 a=1 来生成随机数。首先,我使用以下 Python 代码:
[In]: **import** random
[In]: random.seed(a = 1)
在上面的代码中,种子从 a=1 的值开始,但是可以使用任何值。如果你在你的计算机中设置了相同的种子值,你应该在下面生成和我一样的数字序列。
2. random.randint(a,b)
该子模块生成区间[a,b]中的随机整数,包括区间极值。如果我选择,a = 0,b = 10,我得到例如:
[In]: random.randint(a = 0, b = 10)
[Out]: 2
3.random.randrange ( 开始,停止=无,步进=1 )
该子模块在半开区间[a,b]内生成整数,默认步长为 1。这个函数与 *random.randint(),*相同,如果我们用 b+1 代替 b,或者等价地, random.randint(a,b) 和 *random.randrange(a,b+1)如果在random . seed(a = 1)**之后立即被分别调用,它们将给出相同的随机数。*例如,如果我在第 2 节的代码之后运行下面的代码,我会得到:
[In]: random.randrange(start = 0, stop = 11)
[Out]: 9
如您所见,结果与前面的代码不同,因为它们是按顺序执行的。但是,如果您独立执行这些代码,您会得到相同的结果。
4.随机选择(序列)
该子模块使用户可以从给定的序列列表中选择一个随机数。例如,假设我有一个列表,l = [1,4。,8,9.6,30],然后运行下面的代码:
[In]: l = [1, 4., 8, 9.6, 30]
[In]: random.choice(list)
[Out]: 1
在上面的例子中,我使用了一个数字列表,但是序列可以是字符串、元组、范围等。
5.随机. random()
这个子模块在半开区间[0,1]产生一个随机数(浮点)。如果我在前面的代码之后依次运行下面的代码,我会得到:
[In]: random.random()
[Out]: 0.2550690257394217
6.随机高斯(μ,σ)
该子模块为用户提供了从随机数的高斯分布中生成随机数的可能性,其中均值=μ,标准差=σ。例如,如果我选择平均值 = 0 ,并且∑= 1,我得到:
[In]: random.gauss(mu = 0, sigma = 1)
[Out]: -1.0921732151041414
7.随机指数变量
的这个子模块根据指数概率分布函数生成随机数,其表达式如下

指数概率分布函数在物理学、生物学、水文学等不同的科学领域都是非常重要的。例如,在物理学中,放射性粒子衰变分布近似遵循函数(1)。参数𝜆称为速率参数,它是一个大于零的正数。对于给定的随机变量 X ,其期望值为 E (X) = 1/𝜆 ,方差为 Var(X) = 1/𝜆。所以遵循指数概率分布函数的随机变量 X 的均值为 *= 1/𝜆,*其中𝜆必须不为零。
例如,为了生成服从分布(1)且 *= 2 的随机数,*我运行以下 Python 代码:
[Int]: random.expovariate(lambda = 1/2)
[Out]: 2.108767728131761
8.随机均匀(a,b)
该子模块在区间[a,b]中生成均匀分布的浮点数,包括极值 a,但是极值 b 可能包括也可能不包括,这取决于舍入。例如,为了从区间[0,10]中的均匀分布生成一个随机数,我在上面第 7 点中的代码之后运行下面的代码(请注意代码的顺序,以生成与这里给出的输出相同的输出):
[In]: random.uniform(a = 0, b = 10)
[Out]: 7.887233511355132
我上面描述的八个随机数模块有一个核心模块,Python 的 random 模块。然而,Python 中有一些基本上基于上述随机模块的库,允许用户执行快速有效的数值计算。在这些图书馆中,有一个是 T21 图书馆。NumPy 有很多随机数模块,可以用来生成数字和数组。为了简化符号,首先我导入 NumPy:
[In]: **import** numpy **as** np
[In]: np.random.seed(a = 2)
在导入 NumPy 之后,我还导入了 random.seed()模块来设置 NumPy 生成器的种子。这个模块是第 1 点中 random.seed()的 NumPy 等价物。为了简单起见,我选择种子的值等于 2。下面讨论 NumPy 最重要的随机数子模块。
9.np.random.rand(d0,d1,…,dn)
NumPy 的这个子模块在半开区间[0,1]内生成随机数和填充有个均匀分布的个随机数的不同形状的数组。如果没有给这个子模块一个参数,它将返回一个区间为[0,1]的随机数。如果给定一个参数,则返回一维 NumPy 数组。如果给定多个参数,将返回一个多维数组。以下 Python 代码使这些概念更加清晰:
[In]: np.random.rand()
[Out]: 0.43599490214200376[In]: np.random.rand(d0 = 3)
[Out]: array([0.02592623, 0.54966248, 0.43532239])[In]: np.random(d0 = 3, d1 = 2)
[Out]: array([[0.4203678 , 0.33033482],
[0.20464863, 0.61927097],
[0.29965467, 0.26682728]])
**NP . rand()**模块与NP . random()模块有相似之处,我不在本文中介绍。
10.randn(d0,d1,…,dn)
NumPy 的这个子模块允许用户从均值为零且标准差等于 1 的正态分布中生成一个或多个随机数据样本。元组(d0,d1,…,dn)必须是正整数,它的存在是可选的。元组的大小表示返回数组的维数。如果没有给函数 **np.random.randn(),**赋值,那么它的输出就是一个来自正态分布的随机浮点数。如果只有一个数字作为参数给出,则输出是一个浮点一维 NumPy 数组,其分量等于给定的参数值。如果给定了多个数字作为参数,该函数将返回一个具有特定维度和组件的浮点多维数组。下面的例子让事情变得更清楚:
[Int]: np.random.randn()
[Out]: 0.5514540445464243[In]: np.random.randn(d0 = 4)
[Out]: array([ 2.29220801, 0.04153939, -1.11792545, 0.53905832])[In]: np.random.randn(d0 = 3, d1 = 3)
[Out]: array([[-0.5961597 , -0.0191305 , 1.17500122],
[-0.74787095, 0.00902525, -0.87810789],
[-0.15643417, 0.25657045, -0.98877905]])
NumPy 模块NP . random . standard _ normal()与 **np.random.rand(),**非常相似,唯一的区别是前者以一个整数或一组整数作为参数。
11.np.random.randint(low,high = None,size = None,dtype = 'I ')
NumPy 的这个子模块在半开区间[低,高]从离散的均匀概率分布函数生成随机整数。在 high = None 的情况下,该子模块生成区间[0,low 内的整数。大小通常是一个整数或整数元组,它指定输出 ndarray 或整数的大小。以下示例有助于更好地理解该子模块:
[In]: np.random.randint(low = 3, high = None, size = 4)
[Out]: array([2, 0, 2, 1])[In]: np.random.randint(low = 3, high = 10, size = 4)
[Out]: array([9, 3, 5, 8])[In]: np.random.randint(low = 0, high = 10, size = (2, 2))
[Out]: array([[9, 8],
[7, 1]])
12.np.random.exponential(scale = 1,size = None)
NumPy 的这个子模块从指数概率分布函数(1)生成样本。比例参数等于 1/𝜆,,其默认值等于 1。size 指定从分布(1)中生成的数字样本的大小。这里有一些具体的例子:
[In]: np.random.exponential(scale = 2, size = 1)
[Out]: array([1.89881464])[In]: np.random.exponential(scale = 2)
[Out]: 9.388438956222803[In]: np.random.exponential(scale = 3, size = (2, 2))
[Out]: array([[0.38174426, 0.48249559],
[5.73733106, 2.13714827]])[In]: np.random.exponential(scale = 3, size = (1, 2, 2))
[Out]: array([[[ 0.73490213, 15.00188906],
[ 1.13631244, 0.89818388]]])
13.np.random.binomial(n,p,size = None)
NumPy 的这个子模块从二项式分布函数生成标量数和数组。这里我假设读者知道这种在统计学中非常重要的分布函数。自变量 n 是试验次数,p 是每次试验成功的概率。概率 p 在区间[0,1]内,试验次数 n 是大于或等于零的正数。size 指定输出数组的维度。以下示例有助于理解该子模块:
[In]: np.random.binomial(n = 1, p = 1/2, size = None)
[Out]: 1[In]: np.random.binomial(n = 10, p = 1/3, size = (2, 2, 2))
[Out]: array([[[3, 3],
[2, 5]],
[[5, 6],
[5, 3]]])[In]: np.random.binomial(n = 0, p = 0.4, size = 3)
[Out]: array([0, 0, 0])[In]: np.random.binomial(n = (1, 2, 4), p = (0.1, 0.5, 0.4), size = 3)
[Out]: array([0, 2, 2])
14.np.random.poisson(lam = 1.0,size = None)
NumPy 的这个子模块根据泊松概率分布函数生成标量数和数组。即使在这里,我也假设读者知道这个分布、它的参数和它的性质。第一个参数是 k 个事件应该发生的区间 𝜆 。参数 𝜆 可以是标量,也可以是类似数组的标量。size 可以是 None,也可以是指定输出多维数组形状的 tuple。这些示例有助于理解该子模块:
[In]: np.random.poisson(lam = 2, size = None)
[Out]: 4[In]: np.random.poisson(lam = 2, size = 4)
[Out]: array([4, 3, 1, 2])[In]: np.random.poisson(lam = (1, 3, 4), size = (3, 3))
[Out]: array([[0, 6, 7],
[3, 5, 9],
[3, 2, 4]])
二。安全随机数模块
第二节中讨论的所有模块都不是生成随机数和随机数数组的安全模块。Python 为用户提供了通过 secrets Python 模块生成安全随机数和字节的可能性。这个模块为加密、密码、令牌等创建随机数和字节。在本文中,我不会讨论这个模块,读者可以在上面的链接中找到更多信息。
三。绘制随机数据
上面描述的随机模块对于在许多情况下创建模拟图非常有用。它们可用于检查特定概率分布函数的行为,可用于将实际数据分布与众所周知的概率分布(如泊松、正态、伯努利分布等)进行比较。在下面的 Python 代码中,我使用了上面解释的一些随机数模块来创建本文顶部显示的图形。Python 代码是:
[In]: **import** numpy **as** np
[In]: **import** matplotlib.pyplot **as** plt
[In]: import **seaborn** as **sns**
[In]: sns.set(style="whitegrid")
[In]: np.random.seed(10)[In]: fig, axs=plt.subplots(1, 3, figsize=(15, 6))
axs[0].hist(np.random.exponential(scale = 1, size = 10000), color = "r")
axs[0].set_title("Exponentially distributed random numbers")
axs[1].hist(np.random.randn(10000), color = "m")
axs[1].set_title("Normally distributed random numbers")
axs[2].hist(np.random.poisson(lam = 5, size = 10000), color = "k")
axs[2].set_title("Poisson distributed random numbers")
四。结论
在本文中,我讨论了 Python 中最重要的随机数模块。正如我在开始时提到的,我的列表并不完整,因为还有其他模块,但我试图尽可能不偏不倚地列出我的列表,它由那些通常在数据科学中最常用的模块组成。
如果想要简单的随机数用于一般目的,那么内置的 Python 模块 random 就足够了。如果想要创建随机数和填充随机数的数组,那么 NumPy random 模块可能是最好的选择。可以使用这个模块进行模拟,并创建模拟图,如第三节所示。
如果你喜欢我的文章,请与你可能对这个话题感兴趣的朋友分享,并在你的研究中引用/参考我的文章。不要忘记订阅将来会发布的其他相关主题。
最流行的共指消解框架
原文:https://towardsdatascience.com/most-popular-coreference-resolution-frameworks-574ba8a8cc2d?source=collection_archive---------9-----------------------
最好的共指消解模型是什么?选一个看什么?
作者 玛尔塔·马朗考斯卡 和 帕韦·米耶尼克祖克 。

照片由玛丽·赫林拍摄
快速回顾一下
这是我们上一篇 共指消解 介绍的后续文章。如果你正在寻找一个有例子支持的好的理论背景,我们推荐它。反过来,本文涵盖了最流行的共指消解库,同时展示了它们的优缺点。
简单回顾一下——共指消解(CR)是一项具有挑战性的自然语言处理(NLP)任务。它的目的是将指同一现实世界实体的表达组合在一起,以获得不那么模糊的文本。它在文本理解、问题回答和摘要等任务中很有用。
通过示例进行共指消解
通过使用共指消解,我们希望得到一个明确的句子——一个不需要任何额外上下文就能理解的句子。预期的结果显示在下面的简单示例中(而将 CR 应用到文本的详细过程显示在前面的简介文章中):

步骤 1 —选择一个句子来分析或嵌入并检测歧义词(提及)

步骤 2-将检测到的范围与剩余句子中的其他提及/真实单词实体进行分组

步骤 3-用最有意义的真实世界实体解决相互引用

第四步——获得一个明确的句子
研究动机
在 NLP 系统中,共指消解通常只是整个项目的一部分。像大多数人一样,我们也更喜欢利用经过充分测试的现成解决方案,这些解决方案只需要一些微调,而不需要从头开始编写任何东西。
有许多关于共指消解的有价值的研究论文。然而,并不是所有的都有简单明了的实现。
我们的目标是找到一个生产就绪的开源库,它可以很容易地整合到我们的项目中。
顶级图书馆
有许多关于 CR 的开源项目,但在对当前最先进的解决方案进行全面研究后,到目前为止,最突出的两个库是 Huggingface NeuralCoref 和 AllenNLP。
拥抱脸
Huggingface 有相当多的项目集中在 NLP 上。他们最出名的可能是他们的变形金刚库,我们也在我们的人工智能咨询服务项目中使用这个库。
我们不会进入详细的实现,但 Huggingface 的neural corf使用神经网络解决了相互引用,并且基于一个优秀的 spaCy 库,任何关注 NLP 的人都应该烂熟于心。
该库有一个简单易懂的自述文件,涵盖了基本用法。但是我们发现这个库最大的优点是它允许简单的访问底层空间结构并在其上扩展。spaCy 将句子解析成文档、跨度和标记。Huggingface 的 NeuralCoref 为它们增加了更多的特性,例如,给定的区间是否有任何共同引用,或者一个令牌是否在任何簇中,等等。
此外,该库有多个可配置的参数,例如算法应该如何贪婪地行动。然而,经过大量测试后,我们发现默认参数在大多数情况下效果最好。
还有一个演示可以标记所有有意义的跨度,并显示网络的输出——其中提到指的是哪个。它也给出了关于分配分数的信息,以及每个提及对是如何相似的。

这个演示对于简短的文本来说效果很好,但是由于输出显示在一行中,如果查询变得太大,就不容易阅读了。

不幸的是,还有一个更重要的问题。在撰写本文时,演示比代码中的实现效果更好。我们已经测试了许多参数和底层模型,但我们无法获得与演示完全相同的结果。
社区在其 Github 的多篇文章中进一步证实了这一点,关于如何获得与演示页面上相同的模型,回答非常模糊和不准确——通常归结为“您必须试验不同的参数和模型,看看什么最适合您”。
艾伦 LP
艾伦人工智能研究所(简称 AI2)可能是自然语言处理领域最著名的研究小组。他们是 ELMo 等模型的发明者。他们的项目名为 AllenNLP,是一个开源库,用于为各种 NLP 任务建立深度学习模型。
这是一个巨大的库,有许多基于 PyTorch 构建的模型,其中一个是我们使用的预先训练的共指消解模型,它基于这篇论文。
同样,AllenNLP 也有一个演示。它非常清晰易懂,尤其是在输出方面。它的结构是多行的,可读性很好。然而,与 Huggingface 不同,相似性细节在这里是模糊的,即使从代码中也不容易访问。

然而,AllenNLP 共指消解并不是没有问题。当你第一次执行他们的 Python 代码时,结果非常混乱,很难知道如何理解它们。
AllenNLP 倾向于找到更好的聚类,然而,它经常解析它们,导致无意义的句子。
然而,正如我们在后面提到的,我们已经应用了一些技术来解决这个库及其使用中的大多数问题。
详细比较
就像库一样,有许多不同的数据集是为共指消解而设计的。一些值得一提的是 OntoNotes 和 PreCo 数据集。但最符合我们需求并获得商业使用许可的是 GAP dataset,它是由谷歌开发的,于 2018 年发布。
该数据集由近 9000 对有标签的歧义代词和先行词组成。由于 pairs 是从维基百科中取样的,它们提供了现实世界文本所带来的不同挑战的广泛覆盖面。数据集可以在 Github 上下载。
我们已经在整个 GAP 数据集上运行了几次测试,但真正给我们带来最大收获的是手动检查每一对并精确分析中间聚类以及获得的结果。
下面是数据集中的一个例子,其中包含关于热狗历史的信息。
从现在起,我们将 Huggingface NeuralCoref 实现称为“Huggingface ”,将 Allen Institute 提供的实现称为“AllenNLP”。

原句

提及通过 Huggingface 找到的对

提及由 AllenNLP 找到的配对
最常见的 CR 问题

提及通过 Huggingface 获得的集群

提及 AllenNLP 收购的集群
非常长的跨度
很难说获得长跨距是否是一种优势。一方面,长跨度捕捉到了上下文,并告诉我们更多关于我们正在寻找的真实世界的实体。另一方面,它们通常包含太多的信息。
例如,第一个 AllenNLP 聚类由一个很长的提及来表示:费尔特曼的一个波兰裔美国雇员,名叫内森·汉德沃克。我们可能不想用如此广泛的表达来替换每个代词,尤其是在嵌套跨度的情况下:

相反,Huggingface 只会用单词 Handwerker 替换第一组中的所有提及。在这种情况下,我们将失去 Handwerker 的姓名、国籍以及与 Feltman 关系的信息。
嵌套共指提及
在 GAP 示例中,我们看到嵌套的跨度—一个(或多个)提及在另一个提及的范围内:

根据 CR 解析策略,嵌套跨度中的提及可以被替换或不被替换,但这完全取决于个人偏好——通常很难说哪种方法最适合数据。这可以从下面的例子中看出,对于每一个例子,不同的策略似乎是最合适的:

不正确的语法形式
缩写是浓缩的表达形式,通常通过使用撇号获得,例如:

AllenNLP 将某些缩写视为一个整体,用不正确的语法形式替换其他提及:

在这种情况下,Huggingface 通过始终采用名词短语的基本形式来避免这个问题。然而,这也可能导致不正确的句子:

这是因为所有格形容词和代词的出现——当一个集群是由主语和所有格组成的时候。

不幸的是,这两个库都存在这个问题。然而,AllenNLP 检测到几个 POS(词性)标签,并试图在某些情况下处理这个问题(尽管并不总是获得预期的效果)。
查找冗余 CR 群集
例如,不需要的聚类是所讨论的文本片段中的第二个拥抱脸聚类。将的前雇主替换为的前雇主并不能提供任何额外的信息。类似地,当一个集群不包含任何名词短语或仅由代词组成时,很可能是不必要的。这些类型的聚类可能导致语法错误的句子,如下例所示。

下传检测
我们之前已经全面描述了回指和下指的问题,后一个问题特别棘手,因为它更难捕捉,并且经常导致错误的提及替换。
Huggingface 在 cataphora 检测方面存在问题,而 AllenNLP 总是将聚类中的第一个区间视为代表性区间。

利弊
为了方便起见,我们还构建了一个两个库的主要优缺点的表格,这是我们在与它们合作的过程中发现的。
拥抱脸
✔演示提供有价值的信息
✔易于使用
✔与 spaCy 兼容
✘ 演示的工作方式不同于 Python 代码😞 ✘ 不处理下指
✘ 经常发现冗余簇
AllenNLP
✔非常清晰的演示
✔检测所有格
✔检测下指
✘代码使用起来不直观
✘经常生成太长的簇
✘有时会错误地处理所有格
✘原始地解决共指经常导致语法不正确的句子
我们还发现有趣的是,Huggingface 通常定位更少的集群,因此替代提及的频率更低。相比之下,AllenNLP 似乎更“积极”地替换提及对,因为它发现了更多的聚类。
摘要
在本文中,我们讨论了最著名的共指消解库,以及我们使用它们的经验。我们也展示了它们的优势,并指出了它们带来的问题。
在本系列的下一篇也是最后一篇文章中,我们将展示我们是如何成功实现它们的。我们将展示如何在某种程度上把它们结合成一个解决方案,通过取长补短,利用对方的优势来解决问题。
如果你想使用这些库中的任何一个,我们还提供了两个更详细的笔记本,你可以在我们的 NeuroSYS GitHub 上找到。
更多类似的文章请看一下 神经系统博客 。
第三部分— 如何建立有效的共指消解模型
参考
[1]: 聊天机器人最先进的神经共指消解 — Thomas Wolf (2017)
[2]:—Kenton Lee,Luheng He,,Luke Zettlemoyer (2017)
MATLAB 中基于运动的目标检测与跟踪
原文:https://towardsdatascience.com/motion-based-object-detection-and-tracking-in-matlab-5713f08362ff?source=collection_archive---------26-----------------------
基于运动的多目标跟踪算法在悬停无人机视频中的实现

作者图片
检测和跟踪全运动视频中的对象具有重要的应用,例如交通监控、安全和视频监控等。尽管它有各种各样的用途,但是由于它的复杂性,大多数人倾向于回避做任何计算机视觉工作,而没有意识到有许多库和软件包可以使实现变得简单。
这里介绍的是一个简单易懂的指南,用于理解和实现 Matlab 的基于运动的多对象跟踪算法,以便您可以在自己的视频中检测和跟踪移动对象。该算法在一个视频上进行测试,其中一个场景是从一架悬停的无人机上拍摄和记录的。
基于运动的多目标跟踪算法
我们将使用 Mathworks 的 Matlab 代码,可在这里获得。
在后面的讨论中,有几个术语是很重要的,下图说明了每一个术语。

相关术语以及它们对应于每个帧的哪个部分。作者图片
上面显示的术语也在这里定义:
- 物体识别号或物体 id 用于区分不同的物体。
- 物体包围盒是包围被检测物体的盒子
- 帧 X 轴是每帧中沿水平线的像素坐标轴
- 帧 Y 轴是每帧中沿垂直线的像素坐标轴
- 物体质心由 x 像素坐标和 y 像素坐标组成,用于定义物体在空间和时间上的中心
- 帧数是一个物体可见的帧数,知道每秒钟的摄像机帧数可以让这个参数测量一个物体在场景中的时间
与实现深度学习和大量训练数据来检测对象的流行检测算法不同,Matlab 基于运动的算法仅使用运动。通过首先减去两个帧的背景来检测对象,并且如果两个帧之间的差包含足够多的相连像素,则对象被识别。利用简单的卡尔曼滤波器预测每个探测到的物体的路径,如果沿着预测的轨迹探测到后续物体,则将其指定为相同的物体。
下图显示了一个普通视频截图的示例,旁边是用于检测和跟踪对象的减去的背景空间。

视频截图左边显示正常视频,右边显示运动检测和跟踪。减影背景图像中的变化是白色,而无变化是黑色。作者图片
获取机载视频数据
我用 DJI 的 Mavic Mini 无人机拍摄了自己和兄弟姐妹及朋友一起散步的场景。下面的照片展示了我们拍摄的场景和使用的设备。无人机一直在盘旋,因为如果相机超过一定的移动量,算法就不会工作。

显示测试现场的图片,演员走过现场,使用无人机和摄像机,以及无人机的遥控器。作者图片
我们都以正常的速度沿着场景的水平或垂直轴直线行走,除了两条轨迹以明显更慢或更快的速度倾斜,以便稍后测试跟踪算法。在获得大约 12 分钟的视频后,从无人机上检索数据,以输入到检测和跟踪算法中。
检测和跟踪的对象
尽管使用了悬停无人机的抖动镜头,检测和跟踪算法工作良好,如下面最繁忙的 15 秒剪辑所示,有 5 个不同的对象。

一个 15 秒的剪辑,展示了 5 个对象的检测和跟踪。作者图片
如前所述,为了显示对象检测和跟踪算法的简单应用,如上所示,两条路径偏离了正常模式。让我们来看看一些输出数据,以尝试在不必手动分析 12 分钟视频的情况下找到异常路径。输出数据包括对象 id、对象的 x 和 y 质心以及对象出现的帧。
在整个 12 分钟的视频中,总共检测和跟踪了 192 个物体,尽管需要注意的是,由于无人机的移动,其中一些可能是重复的或错误的识别。首先,我们可以将轨迹绘制到背景上,以查看每个被跟踪的对象经过的地方,下图显示了所有轨迹的绘制。

12 分钟视频中记录的所有 192 首曲目的图表。作者图片
虽然不是很清楚,在上面的图像中,我们可以看到两条对角线,代表偏离轨道。如果我们画出每个轨迹出现的时间,我们应该能够很容易地检测出较长和较短的异常轨迹。下图显示了所有曲目中每个曲目的持续时间。

所有音轨的音轨时长图。作者图片
使用上面的图,我们注意到慢速和快速异常磁道的异常磁道 id 分别为 1 和 22。现在,我们可以裁剪视频,仅显示异常轨道 1 和 22 出现的时间,如下所示。

快速和慢速异常轨迹的剪辑。那首慢歌以双倍速度播放。作者图片
使用输出轨迹数据,我们能够快速识别异常轨迹。过滤以仅显示异常剪辑比手动检查 12 分钟的视频以获得 30 秒的异常镜头要快得多。
摘要
随着所有库或算法的不断改进并向公众发布,实现对象检测和跟踪变得越来越容易。值得注意的是,有几种不同的工作流用于检测和跟踪对象,每种工作流都有其优点和缺点。这里提出的基于运动的方法对于诸如这里提出的具有运动目标的情况可能工作得很好,但是静止目标的检测和跟踪将需要不同的方法。
在这里给出的简单应用中,输出对象检测和跟踪数据用于从总共 192 个对象中识别两个异常轨迹。虽然只有一个 12 分钟的视频被用来识别总计 30 秒的特征,但这种方法可以用于从数小时的视频中识别小的异常行为。
在 ML 中快速移动而不打破东西
原文:https://towardsdatascience.com/move-fast-without-breaking-things-in-ml-c070bfca2705?source=collection_archive---------16-----------------------
行业笔记
许多公司都认识到,将一个在研究实验室中有效的模型投入生产,说起来容易做起来难。

作者图片
由 Doordash 的 ML 工程师 Bob Nugman 和 Arize AI 的 and Aparna Dhinakaran 撰写。在这篇文章中,Bob 和 Aparna 讨论了可靠性工程对于 ML 计划的重要性。
机器学习正迅速成为新兴产品和技术的关键因素。这使得该领域迅速成熟,因为它试图将构建 ML 模型的过程从艺术转变为工程实践。换句话说,许多公司正在认识到,将一个在研究实验室中有效的模型投入生产,说起来容易做起来难。
ML 从业者在将模型部署到生产环境中时面临的一个特殊挑战是确保他们的用户获得可靠的体验。想象一下,现在是凌晨 3 点,你被一个疯狂的电话吵醒。你跳进一个会议,首席技术官在线上,问问题。在新推出的市场中,购买数量突然下降,导致每分钟都有大量收入损失。社交媒体突然充斥着令人讨厌的用户报告。时间不多了。你的团队正在努力,但还不清楚从哪里开始。一款车型在生产中开始失败了吗?随着行业试图将机器学习转化为工程实践,我们需要开始讨论解决这个 ML 可靠性问题。
工程的一个重要部分是确保我们产品的可靠性,那些包含机器学习的产品也不例外。在一天结束时,你的用户不会给你一个通行证,因为你在你的产品中使用了最新最棒的机器学习模型。他们只是希望事情能顺利进行。
为了框定我们关于 ML 中可靠性的讨论,让我们首先来看看软件工程领域在发布可靠软件方面学到了什么。
软件可靠性
为什么
事实上,任何现代技术企业都需要强大的可靠性工程项目。这种计划的范围和形式将取决于业务的性质,选择将涉及复杂性、速度、成本等方面的权衡。
一个特别重要的权衡是在速度(“快速移动”)和可靠性(“不打破东西”)之间。有些领域,如欺诈检测,两者都需要。
将 ML 加入其中会使事情变得更加有趣。
考虑设定 99.95%的可用性目标。这为我们提供了每周 5 分钟的停机预算。绝大多数停机(根据我们的经验,超过 90%)都是由人工引入的代码和/或配置更改引发的。这现在也越来越多地包括对生产 ML 模型和数据管道的改变。
对生产系统的代码和配置进行变更几乎是连续不断的,每一次变更都有可能导致停机事件。类似地,随着对 ML 依赖的增加,对 ML 系统的高速生产交付的需求也在增加,同样存在着引入倒退或中断的风险。
如果我们要实现这个目标,那么每周只允许一次事故,那么挑战就变成了在五分钟内检测并完全缓解一次事故。怎么会?
需要有一个系统的可靠性计划。
可靠性的三大支柱
一个成功的可靠性计划应具备以下要素。每一个都将在下面详细介绍。

作者图片
- 可观察性:检测、探索和理解回归的能力
- 变更管理:工具和实践确保每一个引入的变更(代码、配置、业务规则、基础设施等)都是可发现的、可观察的、逐步推出的、可缓解的、可恢复的。
- 事故响应:当(不是如果)事故发生时,预先存在的计划和能力已经到位,以首先减轻然后恢复事故的影响。事故响应流程包括事故后阶段的启动,包括无过失事后验尸,其结果反馈到所有三大支柱的改进中。
这三个支柱对整个工程过程、技术堆栈以及组织的文化施加压力。
让我们来探讨一下它们的目标和一些特性。
可观察性
成功的可观测性解决方案将使我们能够:
- 快速检测回归
- 告知快速有效缓解的途径。
- 一旦问题得到缓解,告知问题的原因,以便可以全面分析、理解和解决问题,通常通过事后分析过程。
为了提高效率,可观察性工具和实践需要在整个组织中标准化,同时能够灵活地满足每个团队的需求。一个可观察性团队应该制定最佳实践并实现工具,以使开发人员能够以最小的努力一致地满足他们的可观察性需求。
变革管理
如上所述,大多数停机都是由代码和配置的众多更改之一触发的。变更管理系统的目标是确保以集中、系统的方式引入变更,支持我们的可靠性目标。
与可观察性类似,变更管理(代码、配置、基础架构、ML 模型等)应该在整个组织内标准化,同时适应团队之间的不同需求。最好由专门的所有者来管理变更工具和实践。
Sebastian Yates 展示了一个构建良好的变革能力管理的例子。
事故响应
尽管我们做了最好的准备,真正*难以想象的事情还是会发生。*在那个时候,响应不应该看起来像是头脑风暴、解决问题等工程活动。它应该看起来像一个事件响应,有一个预定义的结构,排练过的角色,尖锐的专业工具,和一个操作的命令。
重要的是,其他两个支柱,即变化的可观察性和管理,对于建立成功的事件响应能力至关重要。
机器学习中的可靠性
现在,我们已经了解了可靠性在软件工程的广阔世界中意味着什么,让我们利用我们的知识来理解 ML Ops 领域需要解决什么问题,以帮助公司部署具有机器学习组件的可靠应用程序。
要做到这一点,让我们回到我们之前讨论过的关于你的 CTO 深夜来电的故事。为了提供更多的背景信息,让我们假设为您的电子商务公司对您的搜索结果进行排名的模型正在返回奇怪的结果,并且正在严重影响客户转化率。让我们一步一步来看这里发生的事情。
甚至在你被邀请与你的首席技术官一起参加电话会议之前,对问题的第一步反应就已经发生了。问题已被发现,相关人士已被提醒。这可能是度量监控系统的结果,该系统负责确保重要的业务度量不会偏离轨道。
接下来使用你的 ML 可观察性工具,我们马上会谈到更多,你能够确定问题发生在你的搜索模型中,因为使用你返回的前 n 个链接的用户比例已经显著下降。
了解了这一点之后,你需要依靠你的模型管理系统,要么回滚到你之前的搜索排名模型,要么部署一个简单的模型,让你在此期间保持领先。这种缓解是阻止您的公司每分钟损失(同样多)金钱的原因,因为每一秒钟都是为用户提供不正确产品的关键。
现在事情又开始工作了,您需要回顾您的模型可观察性工具,以理解您的模型发生了什么。这里有许多可能出错的地方,其中一些可能会影响您的缓解策略,因此快速开始了解出错的原因非常重要。
最后,一旦您发现了问题的根本原因,您就必须想出一个解决方案,从修复数据源、重新训练您的模型,到重新设计一个新的模型架构。
现在,让我们更深入地了解这些在生产产品中实现 ML 可靠性的因素。
可观察性

作者图片
使任何系统可靠的关键因素是内省系统内部工作的能力。就像机械师需要在汽车引擎盖下观察你的引擎是否运转顺畅一样,ML 工程师需要能够在他们的模型引擎盖下观察,以了解他们的模型在生产中是如何整流罩的。虽然这似乎是显而易见的,但许多公司在部署机器学习时都是盲目的。通过综合性能指标来衡量模型的性能是不可观察的。
思考 ML 可观察性的最佳方式是,你的团队如何有效地检测到你的模型性能的问题,对问题进行缓解以止血,确定回归的根本原因,并对问题进行补救或解决。值得注意的是,有能力检测问题并不构成对 ML 系统的完全可观察性。如果没有自省的能力来找到根本原因或权衡各种因素的总和,任何解决方案都将是某种形式的猜测。
为了更好地说明您的工具应该寻找什么样的东西,我们首先需要了解哪些东西可能出错?
那么什么会出错呢?
你应该观察什么实际上取决于什么会出错。
使用 ML 模型时,有许多不同的模型故障模式和生产挑战,每种模式都需要您观察系统中的附加信息。

作者图片
首先,战斗的第一步是检测问题的发生。这通常是通过测量模型性能指标来完成的,如运行精度、RMSE、f1 等。一个问题是这并不像听起来那么简单。在理想情况下,在模型做出决策后,您很快就知道了模型预测的基本事实,从而很容易确定模型在生产中的表现。举个例子,预测用户可能会点击哪个广告。在模型做出决定后,你几乎马上就能得到一个关于你表现如何的结果。用户要么点击了它,要么没有点击!
ML 的许多应用程序没有这种实时基础事实的奢侈,在这种情况下,可以使用代理性能度量,如相关的业务度量。除了模型性能指标之外,您可能希望监控服务健康指标,如预测延迟,以确保您的服务为您的用户提供良好的体验。
一旦通过监控模型性能或服务健康度量检测到回归,您需要更多的信息来理解您的模型可能会发生什么。有助于事件响应的一些重要注意事项:
服务:
- 模型预测的延迟和面向用户的延迟
- 服务停机时间(非常类似于软件)
数据:
- 培训中前所未见的生产新价值
- 数据中的噪声或缺失值会对模型消费的特性产生很大的影响。
型号:
- 模型正在执行的底层任务可能会在一夜之间发生缓慢或快速的变化!
- 你的模型可能会以一种非设计的方式产生偏差(你的用户的一些意想不到的子集会得到可测量的不同结果吗)
- 您的模型可能在某些数据子集上表现特别差(需要存储和理解您的模型错误)
对于这些潜在的生产挑战中的每一个,你的 ML 可观察性工具集应该使你的团队能够检测回归并深入研究它们,以最好地理解它们为什么会发生以及你能做些什么。
我们可以整天谈论 ML 可观察性,所以让我们继续讨论如何最好地管理生产中模型的发布更新。
变革管理
每次你把新的变化推向生产,你都冒着把你的用户引入到你的团队没有预见和防范的问题中的风险。
事实上,假设你的搜索模型在你假设的电子商务平台上因为一个新模型的推出而倒退了。现在,您的业务度量发现出了问题,并且您的可观察性工具确定了搜索模型,那么我们该怎么做呢?我们之前提到了缓解和补救之间的区别。在这种情况下,由于我们正在迅速损失公司的资金,最好的办法可能是尽快止血(缓解问题)。
我们可能有的一个选择是恢复到我们以前部署的模型。或者,我们可以发布我们的幼稚模型,这种模型可能没有那么好的性能,但始终工作得很好。在我们的例子中,这可能只是显示从弹性搜索返回的精确结果。
为了更好地防止这些潜在的问题迅速而显著地发生在你的产品的用户身上,ML 系统应该遵循与软件部署相似的展示过程。
与通常使用静态测试用例来测试软件以确保代码不会回归任何行为一样,ML 系统也应该在部署前进行静态验证测试。例如,如果你正在提供自动驾驶服务,通过一些标准化的确定性模拟器路线运行你的新模型可能会让你发现一些明显的回归。
虽然静态验证对于提高运输产品的质量非常重要,但它无法替代您在生产中对模型的了解。让我们来讨论一下如何从您的生产模型中获得这些经验,而不会有全面停机或降低所有用户体验的风险。
您可能希望首先将您的模型发布给一部分用户,以便在所有用户察觉到问题之前及早发现问题。这种技术通常被称为金丝雀部署。
如果通过 ML 监控系统检测到问题,随着您逐步推出您的更改,您应该能够轻松快速地恢复到之前的模型版本及其相应的软件版本。
另一个密切相关的话题是影子部署的想法。在影子部署中,你可以在新模型发货之前,将现有模型在生产中看到的输入信息输入到新模型中。因此,当您的用户仍在体验现有模型提供的预测和用户体验时,您可以开始衡量您的新模型的表现,并做出任何必要的更改,以使其为黄金时间做好准备。
影子部署的另一个好处是,您可以在影子部署中对多个候选模型进行实验,并选择一个能够在当前生产数据上表现最佳的模型。
现在我们已经有了一些技术来帮助我们提高部署的质量,让我们来讨论一下当您在将生产模型部署到生产中之后发现它有问题时,您可以做些什么。
事故响应
好的,我们在生产中发现了模型的一个问题,我们应该怎么做?这在很大程度上取决于您的模型应用程序,但这里我们将讨论一些关于如何在短期内处理问题(缓解)并朝着真正的修复(补救)努力的一般策略。
减轻
首先,就像软件一样,您可以回滚到以前的模型版本和相应的软件/配置。如果您推广了一个通过验证过程的坏模型,这种缓解策略可能会对您有所帮助;但是,这并不总能解决您的问题。可能您的输入数据分布或模型的底层任务已经发生了变化,使得旧模型在生产中也不是一个好的选择。
在某些情况下,另一个可行的策略是部署模型的简单版本。这可能通常比更复杂的模型性能低,但在面对输入和预期输出分布的变化时,它可能会做得更好。该模型不需要机器学习,可以只是一个简单的基于启发式的模型。这种策略可以帮助您在返工更复杂、但性能更好的模型时赢得时间。
补救
这给我们带来了解决由生产中的 ML 模型引起的事件的最常见的建议:只需重新培训它!这个建议很常见,因为它涵盖了模型的许多潜在故障模式。如果输入数据发生了变化或基础任务发生了变化,对较新的生产数据进行重新培训也许能够解决您的问题。随着时间的推移,世界在变化,你的模型可能需要定期重新培训以保持相关性。

作者图片
再培训策略可能包含一整篇技术文章,所以让我们跳到节略版。重新训练模型时,您有一些选择:
您可以选择对数据的某些子集进行上采样,这可能会修复与某类数据的非预期偏差或表现不佳有关的问题。
如果你认为你的输入/输出分布的转变会持续下去,你也可以对更新的生产数据进行采样,以建立新的训练集。
如果您认为这种分布变化是暂时的,并且可能是季节性的,那么您可以根据此季节性期间的数据来训练新版本的模型并部署它,或者求助于工程特征来帮助您的模型理解它试图近似的函数中的季节性指标。
您的模型的性能可能会下降,这是因为引入了一个新类别的示例,这是它在培训中没有见过的。如果这类例子非常不同,你可能需要一个全新的模型来处理这些特殊的例子。训练一个单独的模型并采用一个更高级别的模型来确定对一个特定的例子使用哪个模型的过程通常被称为联合。
我们将在这里讨论的最后一个选项是重新开始。如果再培训没有帮助恢复性能,并且您的旧模型也无法完成工作,那么任务可能已经发生了足够大的变化,需要以下一些内容:新的模型架构、新的功能、新的数据处理步骤。
结论
软件世界花了几年时间才支持我们上面概述的可靠性框架。有了可观察性、变更管理和事件响应这三个支柱,我们可以将可靠性收益从软件领域转化到 ML 应用领域。现在由 ML Ops 空间来提供我们迫切需要的工具,以使 ML 应用程序可靠。
超越事实,了解你的维度
原文:https://towardsdatascience.com/move-over-facts-know-your-dimensions-e1489862f9a0?source=collection_archive---------7-----------------------
数据仓库中的维度类型

照片由西格蒙德在 Unsplash 拍摄
度量,度量,度量!如今,我们希望捕捉应用程序中的每一次点击、每一次滚动和每一个事件,并在其上运行作业。众所周知(事实:P )这些指标是黄金(或者我应该说石油?)但是你有没有想过失败者,这些指标所指的维度?
本文将带您回到数据仓库的基础,并回顾数据仓库中不同类型的维度。如果您已经熟悉什么是尺寸,您可以跳到下一部分。
让我先谈谈维度是什么,那么维度是什么?
维度是我们观察、测量和体验世界的方式。在数据领域,这可以转化为维度,即我们衡量数据和回答业务查询的实体。维度是衡量标准的方式。例如:指标如何与地理相关?
可视化和理解维度的最简单方法是它们在星型模式中的位置:

定义完维度后,让我们继续下一节,讨论维度的不同类型。
维度的类型
根据尺寸变化的频率,可将其分为三种类型:
**静态尺寸:**不随时间变化的尺寸。这些维度非常容易实现。例如,用户的出生日期。
缓变尺寸(SCD): 尺寸随着时间推移缓慢改变 或能够改变的尺寸。实现 SCD 有多种方法,这取决于您希望如何处理发生的变化。
例如,假设用户在 21 年 2 月 24 日从印度搬到了美国,我们希望在维护为

设计 SCD 的各种方法有:
- SCD — Type 0: 发生变化时,保留原来的值。更新的维度表:

- SCD — Type I: 发生变化时,覆盖原始值。更新的维度表:

- SCD — Type II: 发生变化时,增加新的一行。当您想要维护尺寸变更的历史记录时,请使用类型 II 尺寸设计。更新的维度表:

- SCD — Type III: 当发生变化时,增加一个新列。更新的维度表:

- SCD — Type IV: 当发生变化时,覆盖并维护一个单独的历史表。
**快速变化尺寸:随时间变化 或能快速变化的尺寸。例如,用户的体重。快速变化的维度通常被实现为垃圾维度,如下所述。
上面介绍了基于更改频率的维度分类,还有其他基于维度存储方式和其他一些因素的维度分类。其中一些定义如下:
**退化维度:**作为事实表的一部分存储而不是存储在单独的表中的维度。将维度存储为退化维度的决定取决于几个因素,如查询性能考虑。例如,用户的名字可以存储为事实表的一部分。
**符合尺寸:**这些尺寸无论在哪里被引用都具有相同的含义。例如,日历维度表。
**垃圾维度:**这可以解释为一个杂项表,可以维护该表以将不相关的属性存储在一起,并且当不希望将事实表中的太多外键维护到太多维度表时,可以使用单个外键来引用该表。这些通常被实现来处理快速变化或快速变化的维度。
**推断维度:**这些是维度表类型,用于在维度表中的引用可用之前加载事实表的情况下,处理后期数据。在这种情况下,为了尊重引用完整性,可以在事实表可以推断的维度表中创建一个所有维度属性都为空的新记录。当维度数据可用时,可以更新该新行。
嗯,三分钟内的信息太多了!希望这有助于澄清数据仓库中的一些维度类型。
下次见,
JD
远离 R
原文:https://towardsdatascience.com/moving-away-from-r%C2%B2-4a89b1c70393?source=collection_archive---------20-----------------------
使用它的危险以及如何改进你的分析。

作者造成的不良形象
介绍
r 是一个众所周知的模型指标,每个数据分析师都有它的工具箱,但尽管它很流行,数据分析师倾向于谈论和使用这一指标的方式与统计界对它的看法不匹配。在这篇文章中,我希望用简单的图表和语言来解释 R 的一些误用,以及我们如何使用更适用的度量来改进我们的分析。
这篇文章很大程度上受到了 科斯马·沙立子博士的讲稿 的启发。我的意图是将一些难以理解的统计数据转换成一个更加直观和外行的解释。
行动纲要
一些积极使用 R 的人可能想知道这有什么大不了的。以下是一个简短的总结,如果你不同意以下至少一点,也许值得继续读下去。
使用 R 的好理由:
- 我想看看我的数据有多嘈杂。
使用 R 的错误理由:
- 我想知道我的模型在预测方面有多好。(不好,用均方差代替)
- 我想知道我的自变量是否显著。(不好,使用 p 值)
- 我想知道我的线性模型在统计上是否显著(不好,使用 f 检验,但是也要确保检查你的假设)
- 我想知道我的因变量有多少是由我的自变量解释的(不好…有点……)
还感兴趣吗?太好了,继续读下去!
背景

按作者分类的图表
非常简要地回顾一下 R
r 是一个常用于评估线性回归模型的指标。在上图中,我们可以看到红点(预测值)与黑点(实际值)的差异。这两种颜色之间的黑线叫做余数。但是如果我们想想出一些方法来看看我们的残差有多大呢?插入 R。
r 是自变量解释的因变量方差的比例。用不太正式的术语来说,它是一个介于 0 和 1 之间的值,用来描述我们的模型与数据的吻合程度。在许多数据分析领域,R 还充当了比较和评估模型的指标,0 表示非常差的模型,接近 1 的值表示良好的模型。我们用残差平方和(RSS)除以总平方和(TSS ),然后从 1 中减去该分数。
信号和噪音
信号和噪声是数据世界的阴阳两极。信号是指我们试图检测的数据中的一些真相,而噪声是掩盖我们信号的无意义信息。为了把这个比喻带入 21 世纪,你可以把 signal 想象成你想要找到的最喜欢的 Spotify 播放列表,把 noise 想象成那些总是被推销的垃圾播客。在本文中,我将频繁使用这些术语,以便让来自非统计背景的人更容易理解诸如标准差和真实系数值之类的概念。
r:噪声的度量单位
作为数据科学家,我们希望检测数据中的信号。我们开发模型和复杂的分析来做这件事,然后我们评估我们所做的,以确定我们是否做得很好。这就是为什么用 R 来衡量我们信号的强度(或有效性)是如此错误的原因——因为 R 在很大程度上衡量的是我们数据中的噪声。
回想一下,在我们对 R 的简短讨论中,我们说过,自变量解释的是因变量方差的比例。这应该会给我们带来一些危险信号,因为根据定义,我们意识到,随着因变量方差(噪声)的增加,我们能够解释的部分就越少,即使我们保持信号不变。
这可能还没有意义,所以让我们想象一下在保持信号不变的情况下增加噪声是什么样子。
操纵 R
在本例中,我们将创建一些数据。我们要做的是定义我们的信号是什么,引入不同水平的噪声,并观察 R 如何反应。
我们的信号:X 的真实系数等于 1.2
噪声:平均值为 0 且标准差增加的数字的随机相加。
为此,我们将使用以下代码来生成数据。
x1 <- seq(1,10,length.out = 500)
y1 <- 2 + 1.2*x1 + rnorm(500,0,sd = 1)
对于 x1,计算机将生成 500 个 1 到 10 之间的数字。对于 y1,我们要取这个数,乘以我们的真实系数(1.2),加上 2,然后加上一个随机分布在 500 左右的随机数,平均值为 0,标准差为 1。
我们将创建 4 个图表和线性回归,以查看我们的结果如何随着标准差的增加而变化。重要的是要认识到,在每一种情况下,我们的信号(真实系数为 1.2)将保持不变,但我们会有不同水平的噪声(长期来看平均值为 0)。
如果 R 测量我们的信号或它的强度,它应该保持大致相等。如果它测量我们数据中的噪声,那么当我们增加模型中的噪声(保持信号不变)时,R 应该直线下降。那么会发生什么呢?

按作者分类的图表
你看看那个。我们每次回归的信号是 1.2,每次回归都在 1.2 左右。但是请注意,每次我们增加噪音时,我们的 R 似乎都受到了影响。
这就是为什么 R 可以成为一个破坏性的模型评估者。由于它甚至没有试图测量信号,淡化低 R 的模型会导致我们忽略我们在数据中准确识别信号的时间。
r 和解释:一个解释
早些时候我告诉过你,R 是由自变量解释的因变量方差的比例,但是现在,亲爱的读者,我必须承认这也比看起来更复杂。
当我们经常说一个词的时候,我们可能会欺骗自己认为我们理解了它,但它值得我们注意真正检查“解释”在这里的意思。我担心的是,许多人认为这个模糊的词意味着“原因”。最后,我们应该证明 R 不可能告诉我们 X 导致 Y,或者在两者之间有某种因果联系。
这个简单的实验实际上很容易做。让我们取一些 X,它实际上对 Y 有一些因果影响(和前面的代码一样)。那么,如果 R 确实度量了某种因果联系,并且我们知道 X 是导致 Y 的原因,那么用 Y 解释的 X 的变化的 R 应该低于用 X 解释的 Y 的变化的 R。
让我们进行这个实验,看看我们的 R 有什么不同。
x2 <- seq(1,10,length.out = 500)
y2 <- 2 + 1.2*x2 + rnorm(500,0,sd = 3)
summary(lm(x2~y2))$r.squared
summary(lm(y2~x2))$r.squared

我们代码的输出
我们知道,Y 对 X 没有因果影响,然而,如果我们相信 R 向我们展示了某种因果联系,那么这里的 R 应该是不同的,但它们不是,所以我们必须拒绝任何概念,即 R 可以证明我们的因变量和自变量之间的某种因果联系。
实际上,我认为大多数数据分析师会拒绝说 R 告诉我们 Y 有多少是由 X 引起的,但我确实认为许多人相信这一点。那些通常很少关心方差的人,却惊人地关心有多少方差是由某个变量“解释”的。
所有这些的含义
一旦你理解了 R 和噪声之间的关系,当分析师主要基于 R 值来提升或贬低一个模型时,你会忍不住退缩。正如我们刚刚看到的,R 不能告诉我们数据中的信号,它本身是一个非常有限的指标。
事实上,很难找到一个令人信服的理由来给 R 以它应该得到的关注。这篇文章已经太长了,但是我认为,很多时候沉迷于 R 是一个很坏的习惯,因为它通常会鼓励新人疯狂地过度适应(你的模型可以解释的噪声越多,R 就越好),忽略其他更重要的线性回归模型指标,一旦他们遇到真实世界的数据及其混乱,他们就会失败。
我确实认为 R 在边缘情况下仍然有用,但是几乎每个分析都可以通过使用另一个更适用的度量来改进。它的简单性对任何数据科学的新手来说都是有吸引力的,但是为了更好地掌握统计和数据科学,认识到它的缺点并使用更好的技术是很重要的。
这篇文章是一个白痴数据分析师写的,他极大地简化了提到的统计数据。错误是可能的,但是 R 应该被更谨慎地使用的主要观点仍然存在(至少我希望如此。我知道什么?我只是一个免责消息)。
超越批处理与流
原文:https://towardsdatascience.com/moving-beyond-batch-vs-streaming-continuous-datasets-e00713797929?source=collection_archive---------35-----------------------
连续数据集:通过两种生态系统的可访问性

来源:Pixabay
我的世界观很大程度上受我的产品和业务背景的影响,它让我以量化公司或产品价值的统计数据来思考这个世界。随着时间的推移发生了什么?现在发生了什么事?
世界各地的 FAANGs 都明白这一点,并提供量身定制的产品来提供这一价值。当我登录 Google AdWords 时,我不仅可以看到我的活动目前的运行情况,还可以看到它在一段时间内的表现。两者缺一不可,这将是一个不完整的观点。
谷歌分析、脸书和亚马逊拥有看似毫不费力的实时报告。网飞和苹果的反应式界面会根据我的浏览行为和指示的偏好进行更新,为我提供我可能感兴趣的产品和关于我互动方式的当前视图。如果没有基于实时洞察的即时调整,我的用户身份和与这些服务的关系就不会一样。
奇怪的是,当我们使用大多数其他商业工具时,我们的体验会受到影响。你能想到你使用的 B2B 系统没有几天或至少几个小时的报告延迟吗?
回想一下你上一次访问公司的人力资源系统是什么时候。与等待系统更新相比,打电话给他们确认确实做出了改变通常更容易。
B2C 也是如此。向保险公司索赔需要多长时间?你可能认为只有老派、行动缓慢的企业才会有这个问题,但事实并非如此。我们中的大多数人都曾在这样的组织中工作过:每个季度结束后,至少要花一个月的时间来最终确定销售目标。尽管《哈佛商业评论》调查的 83%的企业认为“在最佳时间将数据转化为可操作的洞察力的能力”很重要,但只有 22%的企业表示他们能够成功做到这一点。
但是人们越来越期待反应式的体验。提供这些功能的产品表现明显更好。几乎所有的福布斯未来 50 强都属于这一类别,这并不是巧合,因此 IDC 称到 2025 年 30%的数据将是实时的。
那么,是什么让今天的 FAANGs 和明天的预计科技巨头提供这些类型的体验,而不是其他人呢?很可能是因为他们拥有庞大的技术团队,可以绕过当前数据管道的限制。管道分为两类,每一类都有自己的优点和缺点。A16Z 在他们最近的帖子中详细讨论了这些范例。

来源:作者创作
批处理系统组织大规模数据集,以便最终用户可以就这些数据集提出问题并快速获得答案。每次发出查询时,这些答案的计算都涉及到对围绕其提问的整个数据集的处理。批处理系统通常用于分析用例,具有特定的优点和缺点:
慢:问题只能问历史,不能问现在发生的事情。批处理系统通常有一天的延迟,但是有可能将这个时间范围降低到几个小时。
**昂贵:**因为每次提出一个问题都会处理整个世界,所以账单会随着数据集的大小和发出的查询数量而迅速增加。
**灵活:**它们易于使用,可以快速询问特定问题或试验新的用例。
流系统查看“事件”并在事件发生时处理每个事件。它们通常用于操作目的,如监控、机器学习或构建反应式体验。
**快速:**有可能在事件发生的几毫秒内获得洞察力或采取行动。
**昂贵且不方便:**用例需要定制开发,构建它们需要一个相当大的工程团队,既要让它们启动并运行,又要维护它们。
**历史很少:**流媒体系统通常只处理最近的数据,而不处理历史信息。
你们中的一些人会说“但是 Kafka 最近使用云存储添加了无限保留,所以我可以在那里卸载我所有的历史,并从我的流媒体系统中获取历史”。你是对的,但有一个相当大的警告——为了实际使用历史数据,你需要通过卡夫卡读回那些数据。雪花不能通过设计直接查询它,任何其他批处理系统也不能,所以如果你想使用一个流系统来查看历史数据,每次提出一个问题,你都需要通过你的代理将数据返回,这有点像用一根吸管连接两个消防水管。这些查询将花费难以忍受的长时间来返回任何有用的信息。雪上加霜的是,这些代理正是您的生产系统所使用的代理。如果你超负荷工作,你会危及生产本身,这是不可取的。
这只是两个世界之间巨大差异的一个例子,不仅仅是在互操作性方面,还有最终用户的类型(分析师与工程师)。大多数大型企业在不同的用例中同时使用批处理和流处理,最终拥有两个完全不同的系统,这两个系统应该是一致的,但很少会这样。
打击一个能够从数据集而不是技术的角度思考的人。
我们知道定制管道可以做到这一点,因为科技巨头支持这种功能。一个显而易见的方法是协调系统间的查询。例如,我可以构建两个独立的管道,在我的分析系统中查询超过一天的数据,在我的流系统中查询最近的数据。这已经作为一个 Lambda 架构得到普及,但是有几个原因导致它不能很好地工作。
首先,光是实施就要花费数百万美元,而且极其耗时,当我想要创建新的数据流和报告视图时,发展成本可能也一样高。当我在系统间查询时,我可能总是会被不一致性所困扰。因此,虽然可能,但这种方法只适用于大型企业,而且效率肯定不高。
即使我想通了所有这些,也几乎不可能获得跨两个系统进行重复数据删除的准确答案,从而限制了此类设置中数据的效用。
对于大多数公司和应用程序来说,这是第二个问题。
仔细看看这个问题,我们只是看到了真正问题的症状:我们正在拼凑点解决方案,试图解决一个整体的业务数据问题。我们有帮助我们移动和处理数据的技术解决方案,但它们的互操作性很差,我们仍然需要协调全面解决方案所需的所有技术基础设施。这是有道理的,因为尽管批量数据处理已经取得了很大进展,但我们还只是处于真正实时连续物化的最早期阶段。所有现有的框架在可以发出的查询类型(例如,窗口)方面都有有意义的限制,这阻止了它们真正像批量分析那样被使用。从功能上来说,这意味着你可以连接和比较最近的数据,但是如果你想做任何超出这个范围的事情,祝你好运。
听起来很像三振出局。
连续数据集:呼唤新范式
到了紧要关头,我们都有一个单一的底层数据集,尽管它可能由来自不同位置的多个信号组成。技术迫使我们将基于基础架构的“批处理”和“流式”视图捆绑在一起,并将其视为相同数据的根本不同的副本。当工程师想要构建一个新的用例时,他们需要协调这些副本。当他们创建依赖于低延迟流数据的服务时,他们必须从批处理环境中手动回填细节。这有着至关重要的连锁效应,当制定新的数据驱动的用例、发展或将它们集成到您的堆栈中时,这会限制或至少增加潜在使用模式的复杂性。
迄今为止,流媒体系统一直是围墙花园。静态数据存储在基于技术的专有格式和位置中,只能通过向流系统请求该数据来读取。您的数据永远不应该被锁定在访问层之后,而是以您的用户(了解批处理系统)可以直接访问的格式存储。如果是这样的话,我们可以有一个服务于低延迟流用例的单一数据表示,同时仍然可以被广泛使用的批量分析工具生态系统查询。
格式不是唯一不应该受制于技术和供应商的东西。您的数据应该存储在一个便宜、可访问且灵活的位置。对于这些要求,实际上只有一个选项—云存储—几乎是为任务定制设计的。这听起来有些愚蠢和过时,但大多数流媒体系统仍然在本地存储数据,并迫使用户担心当他们的磁盘填满时该怎么办。
我们应该能够清楚地表达我们关心的数据集,并确信技术将允许访问历史和非常低延迟的更新,这些更新是可访问的,并由云存储支持:连续数据集,尽管仅此还不够。为了满足我们的业务需求,连续数据集需要其他特定属性:
- **数据目录。**存放数据并易于搜索的单一位置。太多的企业依赖分散在工具中的分散数据。通过在企业内创建一个全局命名空间,数据目录可以包含每一条有权限的可用信息,以确保正确的用户可以访问。
- **可审计性。**改变某样东西的价值不应该看起来像是覆盖它,而是记录它的新价值。我们应该总是能够回到过去,看到任何数据集的完整历史。
- **无限变换。**这很难,但我们需要廉价地创建新的衍生数据集的能力,通过连接、扩充或转换上游数据集,这些数据集始终保持最新和完整。这些转换不能受制于今天的流窗口或回看限制,这些限制迫使用户为特定查询改变工具。
- 秤。即使是数据仓库也有局限性。工作流不应该根据规模需求来强制选择技术。
- **互通性。**这些数据集应该与团队使用的任何工具一起工作。雪花,蜂房,数据库,SaaS 工具等应该很容易插入,并与历史和当前信息保持同步。
河口流是第一个考虑连续数据集的系统,有助于为每个人提供与 FAANG 一样的优质用户体验和业务洞察。
从数据库思维模式转向数据湖思维模式
原文:https://towardsdatascience.com/moving-from-a-database-mindset-to-a-data-lake-mindset-d93ffae7caca?source=collection_archive---------15-----------------------
使用数据湖时的三种范式转变

图片作者:乔尔·安巴斯
使用数据库和数据湖之间有几个关键的概念差异。
在这篇文章中,让我们找出一些乍一看可能并不直观的区别,尤其是对于具有强大关系数据库背景的人来说。
该服务器是一次性的。数据在云中。
解耦存储和计算:这是谈论数据湖的经典案例。
在传统数据库系统(以及最初基于 Hadoop 的数据湖)中,存储与计算服务器紧密耦合。服务器或者内置存储,或者直接连接到存储。
在现代基于云的数据湖架构中,数据存储和计算是独立的。数据保存在云对象存储中(例如:AWS S3,Azure 存储),通常是像 parquet 这样的开放格式,计算服务器是无状态的,它们可以在任何需要的时候启动/关闭。
存储和计算的分离实现了:
- 降低计算成本:服务器在必要时运行。不使用时,可以关闭它们,从而降低计算成本。
- 可扩展性:您不必为峰值使用量购买硬件。服务器/CPU/内存的数量可以根据当前的使用情况动态增加/减少。
- 沙箱:多个计算服务器/集群可以同时读取相同的数据。这允许您有多个团队,在不同的集群中,并行工作,读取相同的数据,而不会相互影响。
原始数据才是王道!精选的数据是从。
在数据库范例中,来自源系统的数据被转换并加载到数据库表中后,就不再有用了。在数据湖范式中,原始数据作为真实的来源保存,最终永远保存,因为它是真正的资产。
然而,原始数据通常不适合企业用户使用,因此需要经过一个处理过程来提高质量、提供结构并简化使用。经过筛选的数据最终会被存储起来,供数据科学团队、数据仓库、报告系统和业务用户使用。

数据湖策展(来源:作者图片)
典型的数据湖消费者只看到经过策划的数据,因此他们对策划的数据比对生成它的原始数据更看重。
然而,数据湖的真正资产是原始数据(以及监管管道),在某种意义上,监管数据类似于可以随时刷新的物化视图。
关键要点
- 可以随时从原始数据中重新创建。
- 可以通过改进的固化过程重新创建。
- 我们可以有多个精选视图,每个视图用于一个特定的分析。
今天做出的模式决策不会约束未来的需求
通常,信息需求会发生变化,需要分析一些最初不是从源/操作系统收集的信息。
在一个典型的场景中,如果原始的原始数据没有被存储,历史数据将永远丢失。
然而,在数据湖体系结构中,今天做出的不将字段加载到管理模式上的决定可以在以后撤销,因为所有详细信息都安全地存储在数据湖的原始区域中,并且历史管理数据可以用附加字段重新创建。

策划模式演变(图片由作者提供)
关键要点
- 如果您现在不需要,就不要花太多时间去创建一个通用的“一刀切”的管理模式。
- 迭代地创建一个管理模式,从添加您现在需要的字段开始。
- 当需要其他字段时,将它们添加到监管流程中并重新处理。
最后的想法
数据湖不是数据库的替代品,每种工具都有其优点和弱点。
对于 OLTP 来说,使用数据湖可能和使用数据库来存储万亿字节的非结构化数据一样糟糕。
我希望这篇文章有助于揭示两个系统之间的一些关键设计差异。
从熊猫到星火
原文:https://towardsdatascience.com/moving-from-pandas-to-spark-7b0b7d956adb?source=collection_archive---------13-----------------------
当您的数据集开始变大时,迁移到 Spark 可以提高速度并节省时间。
大多数数据科学工作流都是从熊猫开始的。Pandas 是一个很棒的库,可以让你做各种各样的转换,可以处理不同种类的数据,比如 CSV 或 JSONs 等。我喜欢熊猫——我在上面制作了一个名为“为什么熊猫是新的 Excel”的播客。我仍然认为熊猫是数据科学家武库中的一个令人敬畏的图书馆。然而,当你正在处理的数据集变得太大时,熊猫就会开始耗尽内存。正是在这里,火花进入画面。

Spark 对于大型数据集来说是很棒的❤️ [ 来源
我以问答的形式写这篇博文,带着你可能会有的问题,我刚开始写的时候也有。
Q1。什么是火花?
Spark 是一个处理海量数据集的框架。它允许您以分布式方式处理大数据文件。它让几个工人运转起来,这些工人处理你的大数据集,所有这些都由一个驱动节点来编排。该框架的分布式特性使您可以将其扩展到数 TB 的数据。你不再被任何一台机器的内存所限制。Spark 生态系统现在已经发展得如此之快,以至于您不需要担心任何工作流程编排,并且可以开箱即用,性能卓越。

星火生态系统参考
Q2。什么时候应该搬出熊猫,认真考虑用 Spark?
这取决于你机器的内存。我想说大于 10GB 的数据集对熊猫来说太大了,Spark 在这里变得非常有益。假设您的数据集中有 10 列,每个单元格有 100 个字符,因此大约有 100 个字节,并且大多数字符都是 ASCII 码,可以用 1 个字节进行编码,那么 10M 行将是您应该想到 Spark 的地方。
Q3。Spark 做什么都比熊猫好吗?
不要!对于初学者来说,学习熊猫肯定要容易得多。Spark 可能更难,但有了最新的 API,您可以使用数据帧处理大量数据,这些数据帧几乎和熊猫数据帧一样容易处理。
此外,直到最近,Spark 还不太支持可视化。你只能对数据子集进行可视化。最近,当 Databricks 宣布他们将在 Spark 中对可视化提供原生支持时,情况发生了变化(我仍在等待看到这一点)。但在这种技术成熟之前,Spark 至少在虚拟世界里不会完全取代熊猫。您总是可以通过df.toPandas()将 Spark 数据帧转换成 Pandas,然后运行可视化或 Pandas 代码。
Q4。Spark 设置起来很吓人。我该怎么办?
Spark 可以通过 PySpark 或 Scala(或 R 或 SQL)在 Python 中进行交互。我写了一篇关于在本地或定制服务器上开始使用 PySpark 的博客——人们评论说开始使用 PySpark 有多难。我认为在尝试运行 Spark 时,您可以直接使用托管云解决方案。
我推荐两种开始使用 Spark 的方法:
1。Databricks —这是一个完全托管的服务,为您管理 AWS / Azure / GCP 中的 Spark 集群。他们有可以运行的笔记本,与 Jupyter 笔记本非常相似。
2。亚马逊 EMR 和 Zeppelin 笔记本 —这是由 AWS 提供的半托管服务。您需要托管一个 Spark EMR 端点,然后运行 Zeppelin 笔记本与之交互。其他云提供商也有类似的服务,我在这里不包括他们。

Databricks 是托管 Spark 集群的一种流行方式[ImageSourcewith CClicense
Q5。Databricks 和 EMR 哪个好?
在花了几个小时试图了解两者的利弊之后,有一些考虑:
- EMR 完全由亚马逊管理,你不需要离开 AWS 生态系统。
b)如果您有 devops 专业知识或有 devops 人员帮助您,EMR 可能是一个更便宜的选择——您需要知道如何在完成后上下旋转实例。也就是说,EMR 可能不稳定,你可能需要花几个小时调试。Databricks Spark 要稳定得多。
c)使用 Databricks 很容易安排作业——您可以非常轻松地安排笔记本电脑在一天或一周的特定时间运行。它们还提供了一个到 Ganglia UI 中指标的接口。
d)对于 Spark 作业而言,Databricks 作业可能比 EMR 作业贵 30–40%。但是考虑到灵活性和稳定性以及强大的客户支持,我认为他们是值得的。Databricks 在 Spark 中以交互方式运行笔记本电脑的收费是 6-7 倍,所以请注意这一点。鉴于 30/60/120 分钟的活动后,你可以拒绝实例,从而节省成本,我仍然认为他们可以整体更便宜。
鉴于这几点,我想说如果这是你的第一个 Spark 项目,你应该选择 Databricks,但是如果你有大量的 DevOps 专业知识,你可以尝试 EMR 或者在你自己的机器上运行 Spark。如果你不介意公开分享你的工作,你可以免费试用 Databricks 社区版或者用他们的企业版做 14 天的试用。
Q6。PySpark 和熊猫相比有多相似或不同?
我觉得这值得有自己的博客。星火计划的贡献者安德鲁·雷的这个演讲应该可以回答你的一些问题。
主要相似之处有:
- Spark 数据帧与熊猫数据帧非常相似。
- PySpark 的 groupby、aggregations、selection 等变换都和熊猫很像。与熊猫相比,PySpark 稍微难一点,有一点学习曲线——但感觉相似。
主要区别是:
- Spark 允许你用 SQL 和 Python**查询数据帧,我觉得这真的很棒。有时,用 SQL 编写一些逻辑可能比用 Pandas/PySpark 记住确切的 API 更容易,您可以这样做,也可以互换工作。
- 火花数据帧是不可变的。不允许切片,覆盖数据等。
c)Spark※懒评。它构建了一个所有转换的图表,然后当你实际提供一个动作,比如collect或show或take时,它会懒洋洋地评估它们。转换可以是宽的(查看所有节点上的全部数据,因此orderBy或groupBy)或窄的(查看每个节点中的单个数据,因此contains或filter)。与窄转换相比,进行几个宽转换会慢一些。与熊猫相比,你需要更加注意你正在使用的广泛转换!**

Spark 中的窄与宽转换。大范围转换速度较慢[图片由作者提供]
Q7。还有什么其他优势可以火花吗?
Spark 不仅提供数据帧(rdd 上的高级抽象),还通过 MLLib 提供优秀的流数据和分布式机器学习 API。因此,如果你想对流数据进行转换,或者想在大型数据集上进行机器学习,Spark 可能会对你有用。
Q8。有 Spark 的数据管道架构的例子吗?
是的,这里有一个 ETL 管道,原始数据从数据湖(S3)处理,并在 Spark 中转换,加载回 S3,然后加载到像雪花或红移这样的数据仓库,然后驱动像 Tableau 或 Looker 这样的 BI 工具。

BI 工具的大型数据处理 ETL 管道示例[图片由作者提供]

在 Amazon SageMaker 中执行 ML 的示例管道
您也可以首先从仓库内不同的来源收集数据,然后使用 Spark 转换这些大型数据集,将它们加载到 S3 的 Parquet 文件中,然后从 SageMaker 读取它们,以防您更喜欢使用 SageMaker 而不是 Spark 的 MLLib。SageMaker 的一个额外优势是,它让您可以轻松部署,并通过 Lambda 函数触发模型,而 Lambda 函数又通过 API Gateway 中的 REST 端点连接到世界。我已经写了一篇关于这个架构的博文。另外学习 SparkJules Damji 的书学习 Spark 真的很棒。
这就完成了从熊猫到星火的博文。我们讨论了一些相似点和不同点,开始使用 Spark 的最佳方式,以及一些利用 Spark 的常见架构。
如有任何问题或意见,请通过 LinkedIn 联系我!
资源:
1。Jules Damji 讲述 Spark 如何工作的幕后故事。
2。朱尔斯·丹吉的《学习的火花》一书。
3。比较熊猫句法和安德鲁·雷的 PySpark talk 。
MPIRE for Python:多重处理非常简单
原文:https://towardsdatascience.com/mpire-for-python-multiprocessing-is-really-easy-d2ae7999a3e9?source=collection_archive---------1-----------------------
MPIRE 简介,这是一个速度极快且最用户友好的 Python 多处理库

Jarek Jordan 在 Unsplash 上的照片
Python 是一种流行的编程语言,原因有几个。最重要的是,它易于设置和学习,因此开发速度快。然而,一个主要的缺点是 Python 的执行速度。与许多其他流行的编程语言相比,Python 在速度方面排在最后。幸运的是,通过用 C 编写许多性能关键的库并添加 Python 包装器(例如 NumPy),速度问题已经大大缓解了。这些解决方案运行良好,它们可以利用多线程进行并行计算。当您自己的代码降低了您的速度,并且您想要并行化纯 Python 代码时,事情就变得棘手了。
通过多线程,单个进程的多个线程被同时执行。用 C/C++编写的库可以毫无问题地利用多线程。Python 不能利用多线程,因为臭名昭著的全局解释器锁(GIL)。我不会去解释它是做什么的,为什么它还在这里,因为有很多关于这个话题的其他优秀的博客帖子。这里要记住的是,由于这个 GIL,Python 不能像其他语言一样利用多线程处理 CPU 密集型任务。(注意:对于 I/O 密集型任务和其他释放 GIL 的任务,多线程可以很好地工作。)
因此,Python 程序员经常需要依赖多重处理,新的进程同时产生和执行。通过产生新的进程,我们有效地避开了 GIL。然而,为了在这些进程之间进行通信,我们需要使用管道或队列。这些通信原语不仅降低了多重处理的速度,而且如果你不是很有经验的话,它们也很难使用。
有许多 Python 库提供了多处理能力,并且不需要编写所有样板代码来处理进程和进程间通信。例如,有multiprocessing.Pool 和concurrent.futures.ProcessPoolExecutor类,它们都可以在 Python 标准库中找到。此外,还有 Joblib 等第三方包,以及 Dask 和 Ray 等分布式计算包。后一类还提供了跨几台机器的计算。然而,在我 7 年的 Python 程序员生涯中,单台机器上的多处理通常就足够了,建立一个工人集群的额外成本得不偿失。
然而,即使有这么多图书馆,没有一个能让我满意。大多数都有一个陡峭的学习曲线,因为它们引入了一个全新的关于multiprocessing.Pool的编程语法,提供了很差的错误处理,或者根本没有提供我正在寻找的所有功能。因此,四年前,我创建了一个新的包,它可以完成所有这些功能,甚至更多。它已经在 Slimmer AI 的几十个项目中使用,经过几次迭代的反馈和在生产环境中的暴露,它已经成为今天的成熟包。它现在是 Slimmer AI 的首选多处理库,最近,我们在 GitHub 和 PyPI 上公开了它。文档可在这里获得。
在这篇博文中,我将介绍我们的多处理库 MPIRE(多处理真的很简单),并将它与现有的库在功能、易用性和速度方面进行比较。除了普通的串行处理,我对 MPIRE 进行基准测试的库有multiprocessing.Pool、concurrent.futures.ProcessPoolExecutor、、(一个multiprocessing.Pool的包装器)、Joblib、Dask 和 Ray。在这篇文章的剩余部分,我将用ProcessPoolExecutor来指代concurrent.futures.ProcessPoolExecutor。
MPIRE 概述
MPIRE 构建在流行的multiprocessing 标准库之上,大部分遵循相同的语法,这使得它非常容易学习。mpire.WorkerPool级类似于multiprocessing.Pool级,但是增加了更多的特性和配置选项。MPIRE 的主要特点是:
- 比其他多处理库执行速度更快
- 直观的 Pythonic 语法
- 优雅且用户友好的异常处理
- 通过一组工作人员轻松使用写入时复制共享对象
- 每个工作者可以有自己的状态,并且通过方便的工作者初始化和退出功能,可以容易地操纵该状态
- 进度条支持使用 tqdm
- 仪表板支架
- Worker insights 可让您深入了解多处理效率
- 子进程可以被固定到特定的或一系列的 CPU 上
- 多种进程启动方法可用,包括:fork、forkserver、spawn 和 threading(是的,threading)
- 可选地利用 dill 作为通过多进程的序列化后端,支持在 iPython 和 Jupyter 笔记本中并行化更多外来对象、lambdas 和函数
对于这篇博文来说,浏览所有特性太多了。因此,我将重点放在几乎每个多处理任务的相关特性上。请参考文档了解更多关于 MPIRE 的其他特性。
让我们看几个例子,并将 MPIRE 与其他库进行比较。
例 1。句法
假设我们有一个耗时的函数:
我们想调用 100 次并并行化:
Joblib 和 Ray 相对于multiprocessing.Pool引入了全新的语法。这意味着需要更多的时间来学习如何充分和优化地利用这些库。相比之下,MPIRE 的语法非常接近multiprocessing.Pool。
在没有多处理的情况下运行这个函数大约需要 100 秒,而所有经过测试的多处理库大约需要 20 秒。这是因为它们都被配置为创建 5 个流程,从而并行处理 5 个任务。在启动子进程和进程间通信时会有一些小的开销,但是在这种情况下预期会有 5 倍的加速。
示例 2:进度条
让我们添加一个进度条,让事情变得更有趣一点。当然,当你运行一个长任务时,你想知道任务的状态和完成的时间。我们将建立在前一个例子的基础上,利用tqdm作为进度条库。
在multiprocessing.Pool的情况下,如果我们想要显示实时进度信息,我们必须使用imap,一个返回生成器的map的惰性版本。尽管 Ray 有一个仪表板,但是在那里找不到进度信息。ProcessPoolExecutor和雷都引入了许多样板代码来实现进度条这样的琐碎工作。然而,雷在这里拿了蛋糕。当使用 MPIRE 时,我们可以简单地设置progress_bar标志,我们就完成了。
示例 3:异常处理
尽管您可能很有经验,但每个人都会偶尔引入 bug。在多处理上下文中调试异常并不总是容易的。然而,多重处理库至少可以使调试变得更容易。让我们看下面这个简单的例子:
现在,这个函数到处都在叫ZeroDivisionError ,这里只是为了说明。让我们看看不同的库将如何处理以下数据:
我们稍微修改了一下上面的代码,以使用新的函数和数据,但大部分都是相似的。完整代码见此链接。我们来看看输出。

光线(上)、Joblib(中)、MPIRE(下)的错误输出。图片作者。
对于大多数库,我们可以确切地看到哪一行导致了错误,即return (x * y) / z。然而 Joblib 和 Ray 只表示发生在some_function的第 3 行。即使雷召唤出一个RayTaskError(ZeroDivisionError),它仍然可以作为普通ZeroDivisionError被抓,所以那里不用担心。然而,这些回溯之间的主要区别在于,MPIRE 显示传递给导致错误的函数的参数。这使得调试更加容易。唯一做这件事的其他图书馆是 Dask。
MPIRE 包含更多的特性,其中一些将在接下来的基准测试部分展示。
基准
这篇帖子显示了三个基准的结果,这些结果来自 2019 年由罗伯特·西原发表的关于雷的帖子。为了使基准测试更加公平,我将客户端的启动和关闭调用添加到总基准测试时间中,并删除了代码来“预热”Ray 工作线程。后者本来是作者做的,因为初始内存访问比较慢。预热在实践中并不常见,无论如何,所有多处理库都会受到预热的影响。此外,我增加了每个基准测试的工作负载,并使其在不同数量的内核之间保持一致,以更好地了解增加更多工作人员的好处。最后,通过允许使用[Manager](https://docs.python.org/3/library/multiprocessing.html#multiprocessing-managers) 对象,一些多处理实现得到了进一步优化。同样,这使得比较更加公平。
基准测试运行在一台具有 20 个内核的 Linux 机器上,禁用了超线程,内存为 200GB(对于这些基准测试来说已经足够了)。对于每项任务,使用不同数量的流程/工人进行实验,结果在 5 次运行中取平均值。各个库和基准测试的计时是一致的,所以为了使图形不那么混乱,省略了误差线。所有基准代码都可以在这里找到。查看[requirements.txt](https://github.com/sybrenjansen/multiprocessing_benchmarks/blob/main/requirements.txt) 文件,了解需要安装哪些依赖项。
基准 1:数字数据
该基准使用不同的图像过滤器处理图像。每个滤镜的图像保持不变。因此,能够以某种方式将图像发送给每个进程的库具有明显的优势。这在multiprocessing.Pool中是不可能的,因为你需要求助于multiprocessing.Process 并且自己处理所有的通信和启动/加入过程。不理想。对于 MPIRE,代码如下所示:
就这么简单。该映像作为一个写时复制共享对象传递给每个进程,这意味着数据没有被复制,但底层内存被重用。只有当进程改变图像数据时,才会制作副本。在我们的例子中,这种情况不会发生,所以我们是安全的,处理将会很快。
计时结果如下图所示:

数值计算基准测试结果平均超过 5 次运行。图片作者。
multiprocessing.Pool和ProcessPoolExecutor明显表现不佳,我们认为为他们使用 4 名以上的工人没有额外的好处。每次发送图像显然会导致大量开销。另一方面,随着工作人员数量的增加,其他库的计算时间确实持续减少。当使用 4 个或更少的工人时,Joblib 和 Ray 都比 Dask 和 MPIRE 慢一点,但之后它们会赶上来。最终,Joblib 和 MPIRE 胜出,尽管差距很小。
在这个基准测试中,Joblib 利用其 NumPy 内存映射特性来加速重复共享相同的图像对象。禁用此项会显著增加 Joblib 的总时间。所以重要的是要记住,当你在一个不是 NumPy 数组的对象上做简单的计算时,Joblib 不会那么快。
基准 2:有状态计算
在第二个基准中,每个工作者跟踪自己的状态,并且应该在新任务到来时更新它。正如在原始 Ray post 中一样,任务是处理文本文档并跟踪单词前缀计数——最多 3 个字符。每当某个前缀出现 3 次以上时,一旦处理完所有文档,就应该返回该前缀。
执行流前缀计数的简单类如下所示:
注意:StreamingPrefixCount 的这个实现类似于来自原始 Ray post 的,并不保证返回所有文档的正确前缀。然而,这在这种情况下并不重要,因为这个基准只是用来说明有状态计算。
可以为每个工作人员存储本地数据,并在所有工作完成后返回数据的库显然最适合这项任务。支持这一点的库有 Dask、Ray 和 MPIRE。 Dask 和 Ray 都支持Actors,支持有状态计算。然而,对 Dask 来说,这种支持似乎是实验性的,我无法让它在不崩溃的情况下工作。为了避免使用Actors ,我使用了get_worker,并在最后发送了一个特殊的哨兵令牌来收集结果。它并不漂亮,但很管用。
对于 Ray 来说,Actor 功能运行良好。人们可以使用ActorPool 来很好地分配工作量:
对于 MPIRE,我们可以利用 https://slimmer-ai.github.io/mpire/usage/workerpool/worker_state.html [worker_state](https://slimmer-ai.github.io/mpire/usage/workerpool/worker_state.html)和[worker_exit](https://slimmer-ai.github.io/mpire/usage/map/worker_init_exit.html)功能。在这种情况下,我们还可以利用写入时拷贝:
与其他库相比,样板代码的数量有限。与 Ray 一样,还有一个额外的好处,即负载会自动在工作人员之间进行平衡。
multiprocessing.Pool、ProcessPoolExecutor和 Joblib 对 worker 状态没有支持,需要依赖 Python Manager对象。使用Manager对象的缺点是它们存在于独立的服务器进程中,任何状态变化都必须通过代理传递给它们。因此,这些库的性能受到了很大的影响,与串行处理相比,性能更差。

5 次运行的平均有状态计算基准测试结果。图片作者。
串行处理的时间在这里有一点波动,尽管它只能利用一个工作线程。对于这个基准测试,每个工作者都有自己的状态,并保持本地前缀计数。为了使比较公平串行处理还利用了多个StreamingPrefixCount 对象,等于工人的数量。
从结果来看,Dask 在速度上显然很难赶上 Ray 和 MPIRE。如果 Dask 要修复他们的Actor 实现,可能会不相上下。雷和姆皮尔的表现差不多。虽然,MPIRE 一直比它快一点点,但差距很小。
基准 3:昂贵的初始化
在该基准中,神经网络模型用于预测图像数据集上的标签。(注:为了简单起见,反复使用同一个数据集进行预测。)加载这个模型只需要几秒钟。但是如果每项任务都需要这样做,那么时间会很快增加。虽然这个基准看起来与前面的例子相似,但是这个基准不需要跟踪工作状态的变化。
对于下面的代码片段,假设我们有一个Model 类,它在创建时加载到模型和数据集中。对于multiprocessing.Pool,代码如下所示:
对于 MPIRE,我们再次利用[worker_state](https://slimmer-ai.github.io/mpire/usage/workerpool/worker_state.html)。这一次,我们还使用了[worker_init](https://slimmer-ai.github.io/mpire/usage/map/worker_init_exit.html)功能:
我们可以在这里再次利用写时复制,但是 Tensorflow 模型在这样做的时候并不好用。然而,使用worker_init功能也一样快。注意,我们将keep_alive设置为True。默认情况下,MPIRE 会在一次map调用后关闭工作线程,以减少内存需求。由于 MPIRE 启动和停止工作线程的速度非常快,而 Python 可能非常需要内存(它倾向于将分配的内存保持在备用状态),这通常是所希望的行为。然而,在这种情况下,我们不希望每次运行都重新加载模型,所以我们保留了工作线程。方便的是,当退出上下文管理器时,它们会自动关闭。

昂贵的初始化基准测试结果平均超过 5 次运行。图片作者。
Joblib 和 Dask 一开始都表现不错,但很快被 Ray 和 MPIRE 超越。multiprocessing.Pool和ProcessPoolExecutor似乎在最后赶上了,但是你必须意识到,如果加载模型的时间增加,它们之间的差异——以及像 Ray 和 MPIRE 这样的库——也会增加。
总体基准性能
当将基准计时标准化到 0–1 的范围并取平均值时,我们看到以下趋势:

平均归一化基准结果。图片作者。
所有多处理库都可以超越单核性能,这是关键所在。Joblib 明显优于multiprocessing.Pool和ProcessPoolExecutor,反过来 Dask 也击败了 Joblib,因为它有存储状态的能力。MPIRE 和 Ray 的性能甚至比 Dask 还要好,是首选。
虽然 Ray 和 MPIRE 的速度差不多,但是当您只能在一台计算机上使用时,MPIRE 的易用性使其成为更有趣的库。
摘要
这篇博客文章介绍了 MPIRE,这是一个用于 Python 的多处理库,它易于使用,包含许多特性,并且在速度方面一直胜过所有其他的多处理库。
MPIRE 不仅具有直观的 Pythonic 语法,还具有本机进度条支持和用户友好的异常回溯。后者将减少您的调试时间,并让您的注意力集中在重要的地方:尽可能快速地执行您的工作。
如果你发现 MPIRE 是一个对你自己的工作有价值的多重处理工具,我很乐意听到你的意见。我希望它为你提供效率收益,就像它为我们在 Slimmer AI 提供的一样。
mpl finance——matplolib 相对不为人知的用于绘制金融数据的库

照片由 Yiorgos Ntrahas 在 Unsplash 上拍摄
包括快速创建图表的方法,如烛台,连科,或点和图
众所周知,matplotlib是非常通用的,可以用来创建几乎任何你想要的图表。这可能不是最简单或最漂亮的,但在 StackOverflow 上看了足够多的问题后,它很可能最终会很好地解决。
我知道在纯粹的matplotlib中创建像蜡烛图这样的金融图表是可能的,但这不是最愉快的体验,有更简单的方法来使用库,如plotly或altair(我在的另一篇文章中介绍了这一点)。然而,直到最近我才发现matplotlib有一个单独的库/模块专门用于金融策划。它叫做mplfinance,在这篇文章中,我将展示它的一些漂亮而独特的特性。
设置
设置相当标准。首先,我们导入库。
然后,我们下载股票价格来处理——对于本文,我们使用 2020 年下半年的苹果股票价格。如果你对下载股票价格的更多细节感兴趣,你可以看看我的另一篇文章。
OHLC 海图
mplfinance提供了几种有助于分析资产价格模式的图表。第一个,也是库中默认的一个,是 OHLC 图表。我们可以简单地使用plot函数来创建它:
mpf.plot(df["2020-12-01":])
其中df是一个包含 OHLC 数据的pandas数据帧和一个DatetimeIndex。我们将数据限制到上个月,只是为了清楚地看到该图元素的形状。

作者图片
这种解读与蜡烛图非常相似。左边的横线表示开盘价,右边的横线表示收盘价。垂直线代表价格的波动性,我们可以从两个极端读取高/低价格。
在这一点上,值得一提的是mplfinance提供了一种在一个图表上堆叠多层信息的简单方法。例如,假设我们想将最高价和最低价作为线添加到先前创建的绘图中。我们可以使用make_addplot函数轻松做到这一点,如下所示。我们首先定义附加行,然后将它们作为额外的参数传递给绘图函数。
运行该代码会生成下面的图像,该图像仅确认垂直线的极值对应于给定日期的最高价和最低价。

作者图片
自然,这是一个简化的例子。在更复杂的情况下,我们可能会对添加一些技术指标感兴趣,例如,布林线或简单的移动平均线。我们将很快回到后一个问题。我们也可以使用相同的功能来创建符号,显示我们进入/退出位置。你可以在这里找到一个很好的例子。
蜡烛图
下一个可用的图表类型是烛台图表。用mplfinance生成它们就像给plot函数添加一个额外的参数一样简单。
mpf.plot(df["2020-12-01":], type="candle")

作者图片
当你看着蜡烛和日期时,很明显有一些遗漏的日期。这自然是因为市场在周末和一些特殊的日子是关闭的。如果你想考虑这一点,你可以给plot函数提供一个额外的参数:
mpf.plot(df["2020-12-01":], type="candle", show_nontrading=True)

作者图片
让我们给情节添加更多的信息。首先,我们可以向plot函数传递一个方便的参数—— mav ——它会自动添加我们想要的任何简单的移动平均线。对于这个情节,我们来取 10 天和 20 天的 MAs。其次,我们还可以添加交易量。
mpf.plot(df, type="candle", mav=(10, 20), volume=True)

作者图片
伦科图表
老实说,mplfinance是我第一次看到以下两种剧情,因为它们不像《OHLC》和《烛台排行榜》那样受欢迎。第一种被称为伦科图表,它是利用价格变动构建的,不像大多数图表那样考虑标准化的时间间隔。
在实践中,它的意思是,当价格移动一个特定的量时,一个新的块被创建,每个后续的块与前一个块成 45 度角添加,或者在它的上面或者在它的下面。
Renko 图表最常见的用途是从价格序列中过滤噪声,并帮助识别价格趋势。这是因为所有小于指定方框大小的价格变动都被过滤掉了。
我们可以通过在使用plot函数时简单地指定type参数来创建 Renko 图表。
mpf.plot(df,type="renko")

作者图片
我们也可以根据自己的喜好修改砖块的大小。在下面的代码片段中,我们将其设置为 2。
mpf.plot(df, type="renko", renko_params=dict(brick_size=2))

作者图片
点数图
库中可用的最后一种绘图类型是点数图。与伦科图表类似,它没有考虑时间的流逝。P&F 图使用堆叠的 X 和 O 柱,每个符号代表一个特定的价格变动(由盒子大小决定,我们可以根据自己的喜好调整)。
x 代表价格上升一定的量,而 O 代表下降。我们需要的最后一条信息是创建不同符号的新列的条件(O 跟在 X 后面,反之亦然*)。为了创建一个新的列,价格必须按照冲销金额进行更改,该金额通常设置为盒子大小的三倍(在mplfinance中,默认值为 1)。*
*mpf.plot(df, type="pnf")*

作者图片
我们可以很容易地将这张 P&F 图与第一张伦科图进行比较,以发现完全相同的模式。
添加风格和一般美化
使用mplfinance创建的情节对于一句俏皮话来说已经很好看了,所以肯定是在纯粹的matplotlib中不会经常发生的事情。然而,我们可以使用 plot 函数中更多的选项来使我们的绘图更加漂亮。
对于下一个情节,我们改变比例的数字,添加一个标题,选择紧凑的布局和应用一种风格。我们使用币安风格,这使得情节类似于流行的加密交换。

作者图片
就我个人而言,我认为对于我们必须编写的额外代码来说,这是一个很大的改进。如果您想知道库中有哪些样式,可以使用以下命令查看所有样式:
*mpf.available_styles()*
最后,我们还可以轻松地将图形保存到本地文件中。为此,我们只需向plot函数的savefig参数提供文件名。代码将如下所示。
外卖食品
mplfinance是matplotlib投资组合中的一个库,专门用于绘制资产价格数据- API 非常容易使用,我们经常可以用一行程序创建漂亮的图表
- 该库提供了一些不常见的绘图类型,如 Renko 图或点和图形图
您可以在我的 GitHub 上找到本文使用的代码。此外,欢迎任何建设性的反馈。我也很好奇你是否听说过 Renko/Point 和 Figure 图表,甚至可能在实践中使用过它们。你可以在推特或评论中联系我。
如果你有兴趣学习如何使用 Python 进行量化金融,你可能想看看 Quantra ( 免责声明:一个附属链接),它提供了关于这个主题的各种不同的课程。
来自《走向数据科学》编辑的注释: 虽然我们允许独立作者根据我们的 规则和指南 发表文章,但我们并不认可每个作者的贡献。你不应该在没有寻求专业建议的情况下依赖一个作者的作品。详见我们的 读者术语 。
如果您喜欢这篇文章,您可能还会对以下内容感兴趣:
*</9-useful-pandas-methods-you-probably-have-not-heard-about-28ff6c0bceee> [## quantra——学习量化金融的 Python 编码平台
towardsdatascience.com](/quantra-a-python-coding-platform-to-learn-quantitative-finance-8e5e88c89120)*
“MRMR”准确地解释了你希望别人如何向你解释
原文:https://towardsdatascience.com/mrmr-explained-exactly-how-you-wished-someone-explained-to-you-9cf4ed27458b?source=collection_archive---------1-----------------------
想要改进您的功能选择吗?“最大相关性-最小冗余度”(又名 MRMR)是优步的机器学习平台使用的一种算法,用于寻找“最小最优”的特征子集。

[作者图]
MRMR(最大相关度-最小冗余度的首字母缩写)是一种特征选择算法,在优步工程师于 2019 年发表这篇论文后获得了新的流行:

截图来自:来源。
作者展示了他们如何通过MRMR在各种营销应用中实现自动特征选择,其中包括:
- 用户获取,
- 交叉销售/追加销售,
- 用户流失和重新激活。
然而,MRMR 并不是最近才被发现的。这是由两位伯克利的研究人员在 2005 年发表的论文中首次提出的:

截图来自:来源
作者声称,在成千上万的基因中,选择其中的几个(例如 50 个)就足以实现这项任务(预测疾病)的最高准确度。所以关键是:我们如何找到这 50 个幸运儿?这正是 MRMR 最初设计的目的。
从这两个例子中,很明显MRMR有多灵活,这可以在完全不同的领域证明是有用的,从市场营销到基因组学。
在这篇文章中,我们将看到 MRMR 是如何工作的,以及它与其他流行的特征选择算法(如博鲁塔)有何不同(如果你想了解更多关于博鲁塔的信息,我已经写了一篇专门的帖子:博鲁塔准确地解释了你希望别人如何向你解释)
最小最优与所有相关
特征选择算法可以大致分为两类:
- 极小最优(如MRMR);
- 全相关(如博鲁塔)。

特征选择就像玩飞镖…[作者图]
最小最优方法寻求识别一小组特征,这些特征— 放在一起具有最大可能的预测能力。另一方面,全相关算法被设计成选择所有特征,这些特征分别和具有任何预测能力。
因此,如果特征 A 和特征 B 都是相关的,但是它们带来或多或少相同的信息,那么全相关方法将选择它们两个,而最小最优方法将只选择其中一个并丢弃另一个。
这似乎是一个细微的区别,但在实践中却有很大的不同。想象一个场景,我们有一万个特征。你用 Boruta 你发现其中的 5000 个和目标变量有某种关系。但是五千仍然是一个难以管理的功能数量。
在丁和彭的例子中,你可以用一个小得多的模型,例如 50 个特征,达到同样的(或者更高的)精度。但是,如何选择它们呢?从 Boruta 拿前 50 的特性是行不通的。事实上,它们中的许多可能彼此高度相关:它们不会添加太多信息。
开发 MRMR 就是为了解决这个问题。
“K 个最佳特征并不是 K 个最佳特征”
我们举个玩具的例子。假设我们的任务是预测一些人的收入,知道他们(或他们的亲属)的一些特征。我们处理这些特性:

玩具数据集。[作者图]
让我们假设我们知道变量之间的因果关系(这是我们在现实生活中不经常拥有的特权,但是它对理解 MRMR 背后的逻辑很有用)。

变量之间的因果联系[作者提供的图表]
知道了因果网络将使特征选择变得简单得可笑:我们将选择{ 年龄、智商、身高 },因为这些是唯一与目标变量有直接因果关系的特征。添加任何其他元素都会带来不必要的噪音。
然而,这些特征并不一定是对目标变量具有最强预测价值的特征。总结如下:
"最好的 K 个特征并不是 K 个最好的特征."(改编自 T. M .封面)。
这是什么意思?
例如,以母亲的智商为例:它与收入没有直接联系,但与智商有联系,而后者又与收入有很大联系。所以,如果你测试一下,你会发现妈妈的智商和收入之间有相当强的关系。但是既然我们的模型中已经有了智商,保留母亲的智商就没有意义了。

K 最佳特性与 K 最佳特性,K = 3。每个特征都与它与收入关系的“强度”(F-统计量)相关联。[作者图]
一般来说,这意味着只关注每个特征和目标变量之间的关联是不够的。
我们希望找到 K 个最佳特征,而不是 K 个最佳特征!
这正是 Boruta 的问题所在:它为每个特性提供了单独的评估。事实上,在我们的例子中,Boruta 会选择所有 7 个特征,因为它们都与收入有一定的统计相关性。
这在我们没有太多功能的情况下是有意义的。但是,在实际应用中,经常会发生这样的情况:
- 我们不知道特征和目标变量之间的因果关系;
- 我们有太多的特征;
- 特征中有高度的冗余。
在这些情况下,可能是像博鲁塔这样的全相关算法太“放纵”了,而 MRMR 能够移除不必要的特征。

变量之间的因果联系[作者提供的图表]
所以,现在的问题是:MRMR 是如何工作的?
在 MRMR 的引擎盖下
使用 MRMR 时,你基本上只需要做一个选择:决定你想要保留的特性的数量。我们将称这个数字为 K。在我们的例子中,我们将取 K =3。
在实际应用中,人们可以根据领域知识或其他约束来选择 K ,例如模型容量、机器内存或可用时间。
MRMR 迭代地工作。在每一次迭代中,它(根据规则)识别最佳特性,并将其添加到所选特性的篮子中。一旦一个特性被放进桶里,它就再也出不来了。
在我们的例子中,这些是我们在每次迭代结束时得到的结果:

K = 3 时的 MRMR 过程[图由作者提供]
在每次迭代中,决定选择“最佳”特征的规则是什么——即第一步的智商,第二步的年龄和第三步的身高?这是 MRMR 的核心。
**“最大相关性-最小冗余度”之所以这样叫是因为——**在每次迭代中——我们希望选择与目标变量具有最大相关性的特征,以及与先前迭代中已经选择的特征具有最小冗余度的特征。
实际上,在每次迭代 i 时,为每个待评估的特征计算分数( f ):

一级方程式赛车。MRMR 概观[作者图]
迭代 i 中的最佳特征是得分最高的特征。就这么简单。
现在唯一的问题是如何计算分子和分母。基于这一决定,优步论文的作者们识别出了 MRMR 的许多变体,如中旬、 MIQ 、 FCD 、 FCQ 、 FRQ 、 RFCQ 和 RFRQ 。然而:

截图来自来源。[作者强调]
由于 FCQ 非常简单和快速,并且通常比其他算法运行得更好,所以我将把重点放在这个变体上(顺便说一下,扩展到其他变体很简单)。
特征 f 在第 i 次迭代(分子)的相关性计算为特征和目标变量之间的 F 统计量(此处为进一步了解 F 测试)。冗余度(分母)被计算为该特征和在先前迭代中选择的所有特征之间的平均(皮尔逊)相关性。所以,公式变成了:

方程式 2。MRMR 公式(FCQ 变体)[作者提供图片]
其中 i 是第 i 次迭代, f 是被评估的特征, F 是 F 统计量, corr 是皮尔逊相关。请注意,相关性是以绝对值表示的。事实上,如果两个特征具有 0.9 或 0.9 的相关性,这没有什么区别:在这两种情况下,它们都是高度冗余的。
为了让事情变得更清楚,假设我们处于第三次迭代。这是之前发生的事情:
- IQ 已在第一次迭代时选择;
- 年龄在第二次迭代时被选中。
接下来我们应该选择哪个功能?
计算每个特征的分数只需要 F 统计量和相关性:

第 3 次迭代中发生的情况的图示。[作者图]
如上所述,下一个最佳特征是得分最高的特征,在本例中是高度*。*
从头开始的 Python 实现
我们上面描述的迭代过程很容易在 Python 中实现:
MRMR 的 Python 实现(FCQ 变体)[作者代码]
这个实现非常容易理解。然而,这是次优的。你能找出原因吗?
在上面的代码中,我们计算了很多我们永远不会用到的相关性。事实上,在第 6 行,我们正在处理所有可能的特征对。这意味着 F * ( F - 1) / 2 对。对于 F = 10,000,这将意味着5000 万次关联!
通过只处理我们在每次迭代中需要的特征对,我们可以节省更多的时间,即计算“进行中”的相关性。这可以节省我们很多时间。
毕竟,在每次迭代时,我们只需要计算前一次迭代选择的特征与所有从未选择的特征之间的所有相关性,并将它们存储在一个相关矩阵中。这意味着:
- 第一次迭代:不需要关联。事实上,还没有选择任何特征:选择具有最高分数(即最高 F 统计量)的特征就足够了。
- 第二次迭代: F - 需要 1 个相关性。
- 第三次迭代: F - 需要 2 个相关性。
- …
- K 次迭代: F - K + 需要 1 次相关。
*现在,做数学很容易:这个过程需要计算少于 F ** ( K - 1)的相关性。如果 F =1 万, K =50,这就得出一个合理得多的数字:小于 50 万的关联。
这个改进的版本看起来像这样:
MRMR 的一个更快的 Python 实现(FCQ 变体)[作者代码]
除了第 11 行和第 21–23 行之外,这几乎等同于上一个版本。
给我一个“pip 安装”
你可以在我的 Github 中找到分类问题的现成版本 MRMR。它可以通过以下方式安装:
*pip install mrmr_selection*
这是如何在熊猫数据框架上使用它的一个片段:
*from mrmr import mrmr_classif
from sklearn.datasets import make_classification
# create some data
X, y = make_classification(n_samples = 1000, n_features = 50, n_informative = 10, n_redundant = 40)
X = pd.DataFrame(X)
y = pd.Series(y)
# use mrmr classification
selected_features = mrmr_classif(X, y, K = 10)*
结论
我们已经看到了为什么 MRMR 在许多实际问题中是一个有用的特征选择步骤:因为它试图找到一小组与目标变量相关并且彼此几乎没有冗余的特征。
MRMR 的价值不仅在于它是有效的(正如优步的论文所证明的),还在于它的简单性使得它可以在任何管道中快速而容易地实现。
感谢您的阅读!我希望这篇文章对你有用。
我感谢反馈和建设性的批评。如果你想谈论这篇文章或其他相关话题,你可以发短信给我我的 Linkedin 联系人。
MS-DAYOLO:用于跨域目标检测的多尺度域自适应 YOLO
原文:https://towardsdatascience.com/ms-dayolo-multiscale-domain-adaptive-yolo-for-cross-domain-object-detection-d7912a9de975?source=collection_archive---------28-----------------------
提高检测器对畴变的鲁棒性
当测试数据的分布与训练数据的分布不同时,会发生域转移问题,导致目标检测模型的性能下降。例如,对象检测器是针对在晴朗天气和有利天气下捕获的图像数据进行训练的,但它适用于测试阶段的恶劣天气场景,包括灰尘、雨或雾。虽然已经提出了许多先进的对象检测方法,包括 R-CNN 网络家族(R-CNN,Fast R-CNN,Faster R-CNN,Cascade R-CNN)或 YOLO 系列(YOLOv1-v4),但研究人员只专注于在基准数据集上的性能,如 COCO,VOC,WAYMO,Cityscapes 等。其中包括干净图像数据和对象检测中的域移动。近年来,这个主题被更广泛地研究,有时它被称为术语领域适应。在这篇文章的剩余部分,我将回顾一个新的多尺度域自适应 YOLO (MS-DAYOLO)框架,用于跨域对象检测。
戴约洛女士

MS- DAYOLO 的建筑(图片见诸报端[ 来源)
MS-DAYOLO 的架构如上图所示。一般来说,MS-DAYOLO 是基于 YOLOv4 开发的,它几乎采用了 YOLOv4 的主要组件。主要区别在于,有一个域自适应网络(DAN ),它连接到主干上,以鼓励主干学习域不变特征。具体来说,如上图所示,DAN 连接到主干中的特征地图 F1、F2 和 F3。作者已经考虑利用特征图 F1、F2 和 F3,因为这些特征被直接馈送到检测器的颈部,从而它们携带了输入图像的主要信息,这可能有利于训练 DAN。
域自适应网络(丹)
为了避免获得实时对象检测器中最重要的计算,DAN 仅在训练期间被集成到主干中。DAN 的任务是预测输入图像是否在源域(完成训练的域)或目标域(没有训练模型的新的域)。
DAN 被优化以最小化域分类损失 L_dc ,其被计算为以下二元交叉熵损失函数:

其中 i 为训练图像的索引, t_i 为领域标号( t_i = 1:源领域, t_i = 0:目标领域), p(x,y) 为特征图位置 (x,y) 处的预测领域概率。
另一方面训练骨干最大化 L_dc 学习不变特征。也就是说,为了使对象检测器对域的变化具有鲁棒性,应该鼓励检测器学习输入图像中的显著内容(或显著对象),即使图像是在任何条件下捕获的。如果这一过程操作得当,可以提高检测机的性能和稳健性。
丹被优化为最小化 L_dc ,而骨干被训练为最大化 L_dc ,这导致了联合最小值-最大值问题。
总损失
总损失函数公式如下:

其中 L_det 是检测损耗,而 λ 是用于平衡总损耗的负标量。 λ 的负值可以解释为解决前段提到的联合最小-最大问题。换句话说, λ 负责控制 DAN 对整个网络性能的影响。关于 λ 的进一步详细解释可以在论文中找到。
结果
表 1 显示了 Cityscapes 数据集上不同适应配置的定量结果。表中, P 、 R 、 C 、 M 和 B 是代表人、骑手、汽车、摩托车和自行车的类别名称。通过对 DAN 采用所有三个特征映射 F1、F2 和 F3,已经实现了最佳性能。

表 Cityscapes 数据集的定量结果(论文中的表格[ 来源 ])
表 2 显示了在 BDD100K 和 INIT 数据集上验证 YOLOv4 和 MS-DAYOLO 时的性能比较。

表 BDD100K 和 INIT 数据集的定量结果(论文中的表格[ 来源 ])
使用 YOLOv4 和 MS-DAYOLO 对雾状图像数据进行目视检测的结果如下图所示:

视觉检测结果:(a)清晰图像上的 YOLOv4,(b)模糊图像上的 YOLOv4,(c)模糊图像上的 MS-DAYOLO。(论文中的图像[ 来源 ])
结论
在这篇文章中,我简要回顾了一种新的多尺度域自适应 YOLO (MS-DAYOLO)框架,用于跨域对象检测。正如论文中提到的,MS-DAYOLO 是第一个提出考虑改进 YOLO 模型来处理畴变问题的工作。在自动驾驶应用的各种测试场景下,MS-DAYOLO 已被证明优于最初的 YOLOv4。
欢迎读者访问我的脸书粉丝页面,这是一个分享关于机器学习的事情的页面:深入机器学习。我发布的关于物体检测的其他值得注意的帖子包括:
- 约洛夫 4–5D 评论
- 达克拉斯
- EFPN
- 数据增强
- 雾霾数据合成
感谢您抽出时间!
利兹大学数据科学硕士:我的经历(2018/19)
原文:https://towardsdatascience.com/msc-data-science-at-leeds-my-experience-2018-19-926e007989b7?source=collection_archive---------31-----------------------
我的数据科学硕士的全面崩溃。

介绍
硕士压力大,费用高。我已经在这里讨论了我认为数据科学硕士学位是否值得。这一次,我想谈谈我在利兹大学的数据科学硕士学位会很有帮助。
难怪有人问我,我的数据科学硕士是什么样的。一些人想知道是否值得攻读数据科学硕士学位。其他人特别想知道更多关于利兹大学提供的课程。
我会试着在不太无聊的情况下尽可能深入。以下是我将要讲述的内容:
- **申请流程:**这是我申请流程的一个非常笼统的概述,因为我记不太清了。
- **课程结构:**我在利兹一年的整体结构。
- **模块结构:**不同学校的模块结构不同。
- **我最喜欢的模块:**我最喜欢的模块。
- **我的学位论文:**学位论文过程和我的经历。
- **评分:**利兹如何以我的成绩单为例计算硕士研究生的成绩。
- **利兹数据科学协会:**一个伟大的协会,提供基于团队的数据科学项目供参与。
现在,我充分意识到,并不是每个阅读这篇文章的人都打算去利兹大学学习。即使你对这门课程不感兴趣,希望你仍能从我在英国攻读数据科学硕士的经历中学到一些东西。
同样值得注意的是,数据科学硕士是一个相对较新的事物。因此,如果全国数据科学硕士的课程结构和模块选择与我学生时代相比发生了显著变化,我不会感到惊讶。
应用进程

由 Kelly Sikkema 在 Unsplash 上拍摄的照片
入学要求
在你申请之前,大多数数据科学硕士希望你有某种数学或计算背景。至少,大多数大学希望你至少有一个 2:1(荣誉)的学位。如果你是一名国际学生,那么有一个额外的语言要求,即雅思总分 6.5 分,所有部分都不低于 6.0 分。
我见过一些没有多少数学背景的学生,他们几乎没有任何计算机知识。他们还是通过了申请,所以要求可能没那么严格。这当然取决于大学,因为我想象一些大学(如牛津和剑桥)比其他大学严格得多。
应用和成本
我在 2018 年 6 月左右开始申请,我记得申请过程相当简单。要申请,我必须点击官方课程页面上的申请按钮创建一个帐户。然后我记得填写了一堆关于我自己的基本信息。
申请中最耗时的部分是写我的个人陈述。我记得我只是写了我的现状和为什么我想学习数据科学。有趣的是,我的一些留学生朋友说他们甚至不需要写个人陈述。正因为如此,我不确定这个要求有多一致。
几周后,大学通过电子邮件和信件回复了我,并无条件录取。他们解释了基本的课程细节和学费,并指导如何接受提议。当时的学费是 10000,因为我是校友,所以打了 1000 的折扣。如果你是国际学生,学费会贵得多(如今高达 25,000 多英镑)。
接受报价后,他们给我发了一封接受邮件和信。不久之后,我收到了两本小册子,里面有更多关于大学校园和我的提议的信息。
课程结构

万花筒在 Unsplash 上拍摄的照片
时间线概述
一般来说,英国高等教育的学年大约在九月或十月开始。考试通常在一月和六月举行。
如果你在读硕士课程,那么你就要用这个夏天剩下的时间来写论文。你通常在八月或九月左右完全完成,之后你将等待毕业。
信用
在利兹大学,每个模块完成后都有一定的学分。这些课程通常从 10 到 15 学分不等,有些课程相当于 20 学分。唯一的例外是价值 60 学分的论文。
这门课程有必修和选修单元。为了让我通过考试,我的模块必须总计达到 180 或 185 学分。
我还需要通过至少 135 学分的“5M 级”模块。这些模块只有硕士生才能修。然而,5M 级模块也有“3 级”版本,适用于来自不同课程的最后一年本科生。这意味着硕士生和本科生一起分享大部分课程。
参加 5M 级别课程的学生有额外的课程来覆盖更深入的内容。因此,期末考试有额外的问题,完全是让 500 万学生回答的。
模块
第一学期有必修的计算机科学模块,第二学期有数学模块。在选修课方面,选择的模块主要包括数学和计算机科学。我记得有一些地理和商业模块,但这些选择较少。事实上,我所有的模块不是数学就是计算机科学。
每学期之前,每个人都必须在设定的时间范围内登录选择他们的选修模块。我必须从三个列表中选择:A、B 和 c。
要求是从列表 A 和 B 中选择至少 30 学分的模块,从这些列表中总共选择至少 60 学分。一旦完成,我们可以从列表 c 中选择更多。
请记住,5M 级模块的 135 学分规则适用于全年。为了平衡我的工作量,我试图将这些模块尽可能均匀地分布在两个学期中。
由于这种结构,由于时间表冲突,不可能总是注册某些模块。有人告诉我们,如果我们能补上错过的课,还是有可能报名的。在这些情况下,方案主管也需要批准。
论文
名单:选择论文题目的正式程序要到第二学期才开始。然而,我们在第一学期就意识到要提前计划我们的选择。
第二学期之前也没有正式的名单。就我所能记得的,我相信他们在第二学期开始和中期的某个时候公布了名单。如果没有感兴趣的题目,我们也可以向论文协调员建议我们自己的题目。然后他们会试图找到合适的主管来配合你的主题。
这个列表相当大,大约有 50 个主题。虽然这门课严格来说是在数学学院下,但是这个列表包含了数学和计算机科学的主题。也有来自商业和心理学的话题。
**主题:**每个主题都包含一个描述、目标,有时还包含一个资格要求。在有需求的情况下,我们需要有合适的技能或者已经学习了某些模块。
他们也可以分配一个以上的学生。根据我的经验,最受欢迎的通常是与机器学习有关的计算机科学话题(炒作是真实的)。例如,有一个涉及 Airbnb 数据,我知道很多学生申请只是因为这个名字(我对此感到内疚)。
同样重要的是,我们与其他课程的硕士生分享了一些话题。例如,计算机科学导师可能会优先考虑计算机科学的学生,而不是这门课的学生。我个人曾试图申请计算机科学学院的大型机器学习课题(如 Airbnb 的课题),但都没有成功。
选择截止日期:最终提交的截止日期是三月底。我们必须选择三个偏好,并将其发送给协调员。如果他们不批准任何一个,那么我们必须选择另一个。
正如你所看到的,提前考虑你的论文将涉及的内容是非常有用的。这样做将有助于在第一和第二学期选择模块。
模块结构

由 Dom Fou 在 Unsplash 上拍摄的照片
根据学校的不同,模块的结构可能会有很大的不同。由于我只修了数学和计算机科学模块,所以我只能对这两个模块进行评论。
数学:数学模块可能非常激烈,尤其是 5 米级的。对于评分,大部分权重是期末考试,通常占 80%。其余的由分散在整个学期的课程组成。根据教授的不同,我们每周或每两周收到一份作业。
每份作业通常由一系列问题组成。这些通常是像证明,“证明这是真的”和计算问题。对于一些模块,我们不得不做一个更重要的统计项目。
在我看来,数学模块给人的感觉更加死板,缺乏创造力。如果你熟悉数学,那么这可能并不奇怪。此外,如果你有其他大项目要做,每周或每两周一次的课程可能会让你不知所措。我认为重考的好处是,即使你弄乱了一些功课,你总体上仍然可以做得很好。
计算机科学:这些模块在期末考试中的权重通常低于数学模块。因此,通常更重视课程,有些课程的权重高达 40%。从我的经验来看,大多数计算机科学模块都有大约两份作业。这些通常在你如何对待他们方面提供了相当大的灵活性。大多数教授只关心你解决了问题,并且它有效。
与数学模块相比,还有更多的实验室会议来帮助完成课程。随着对课程的重视,我想这是有道理的。计算机科学模块的另一个关键区别是,你可以带自己的笔记参加考试。这来自一个数学学位,感觉很神奇。这绝对有助于我不再像记忆数学模块那样感到记忆公式的压力。
我最喜欢的模块

由 Unsplash 上的 Matese Fields 拍摄
我觉得提供一些关于我最喜欢的模块的见解可能会有所帮助,因为你们可能也在学习相同的模块。显然,从那时起事情可能已经改变了,所以请记住这一点。我做的大部分模块都是 5M 级的。如果你想看我做的每个模块,你可以跳到评分部分。
由于我过去是一名分析师,我选择了那些我认为在当时有用的模块。我还尝试挑选了一些我认为在我作为数据科学家工作时可能有用的信息。虽然很诱人,但是最好避免选择看起来简单的模块。毕竟硕士学位并不便宜,所以你不妨充分利用它。
机器学习(5M 级计算机科学)
这个模块总体上是对机器学习的一个很好的介绍。这位教授对这个话题充满热情,真诚地想帮助学生。我们讨论了决策树、卷积神经网络和强化学习等算法。可能会涉及到相当多的数学知识,我们应该把这些内容牢记在心。因此,线性代数的先验知识对这个模块非常有用,尤其是在学习神经网络的时候。
机器学习非常受欢迎。它是如此受欢迎,以至于第一次讲座没有足够的空间来容纳所有的学生。有些人甚至不得不坐在地板上!
几周后,随着人们转向这个模块,班级变得更大了。如今,我只能想象它会比以前更受欢迎。

第一次机器学习讲座。教授没想到会有这么多学生!
期末考试占 80%的权重,其余的是课程作业。课程之一是使用 Keras 实现一个神经网络架构。教授给了我们一个模板,让我们在上面工作,第一次训练神经网络非常酷。
第二个课程是使用强化学习训练一个人工智能玩游戏。这个很难,需要和很多其他学生一起合作。
我要说的是,尽管有难度,这个模块中的课程作业是最有趣、最令人愉快的。尝试用代码实现数学理论并看到它的工作是很好的。
统计学习(5M 级数学)
我想把统计学习看作机器学习的统计方法。统计学习和机器学习都侧重于使用过去的数据来训练模型进行预测。
不同之处在于,统计学习没有从“算法”的角度来处理这个问题。相反,我们学习统计建模和在数学层面评估模型性能的重要性。
令人惊讶的是,该模块对考试和课程的重视程度相当。也就是说,考试和课程各占 50%的权重。这对于一个数学模块来说是很不寻常的,但这一次数学考试压力小了也不错。
在我看来,统计学习很好地补充了机器学习。在模型方面,我们学习了基础知识:线性回归、逻辑回归和带有 boosting 和 bagging 的决策树。然后很大一部分集中在评估这些模型,并知道如何统计选择最好的模型。
教授也很棒,他很好地解释了概念。即使没有统计学背景的人也会发现这个模块是可行的,因为它集中在统计建模的基础上。如果你对机器学习很认真,并且想拥有数据建模的统计学基础,那么我会推荐你学习这个模块。
数据挖掘和文本分析(5 级计算机科学)
本模块重点介绍对文本数据使用机器学习算法。随着对图像数据使用机器学习的大肆宣传,我觉得知道如何处理文本数据也很重要。虽然涉及到一些数学,但与机器学习等模块相比,这是最少的。
我们学会了如何使用一种叫做 Weka 的工具。自从我毕业后,我还没见过或听说过有人使用这个工具,所以我怀疑它对你的简历有多大用处。基本上,它是一个允许你挑选机器学习算法应用于文本数据的工具。我们将它用于我们的课程作业,它有助于在高层次上了解如何应用这些算法。然而,感觉我只是在挑选魔法药水,看看哪种效果最好,而不需要了解一切。
尽管如此,我还是觉得讲座材料很有趣。我们学习了如何对自然语言和文本分析建模。我们还学习了机器翻译、信息检索、聊天机器人和文本分类。
这位教授很奇怪,但讲课笔记很不错。期末考试占 60%的权重,其余的是课程作业。总的来说,我认为这个模块相对简单。这感觉像是一个“不太严重”的问题,但它仍然是有用的。它为我自己处理文本数据提供了足够好的基础。鉴于文本数据无处不在,这绝对是一件好事。
我的论文

尼克·莫里森在 Unsplash 上拍摄的照片
我不会过多地谈论我的论文是关于什么的,因为我不认为有人会觉得它有趣(如果你感兴趣,你可以在这里阅读)。相反,我会解释这个过程和我所经历的。注意,我的论文是在数学学院下完成的。
主管会议
正如我之前在课程结构部分提到的,我在三月底提交了我的偏好后,就拿到了我的最终题目。从那时起,我试着稍微准备一下论文,但是直到我第一次和我的导师见面时才开始。我说“导师”是因为我有两个导师:一个在论文的前半部分,另一个在论文的后半部分。
第一次见面发生在我五月/六月考试之后。我认为这是我写论文的正式开始。从那以后,我每周都和我的主管开会。这些非常重要,因为主管让我保持在正轨上。
为了充分利用这些会议,我必须向他们展示我一直在做的工作。我花了大部分会议时间来处理我的问题。有时我偏离了论文的主要目标,导师总是确保让我知道。他们会通过阅读我到那时为止所写的东西,然后在上面写笔记,让我知道要修改什么。
不是每个会议都有用,但大多数会议都有用。他们还确保我做了正确的事情来打动外部考官,这非常有帮助。
口头介绍
论文的最终提交时间大约在八月底。之后,我们不得不准备一个持续了大约一个小时的口头报告。演示持续了大约 30 分钟,剩下的时间是提问。
观众包括我的导师和论文协调员。他们问的大多数问题都相对简单,但有些问题有点尴尬。不过总的来说,感觉比预期的要容易。可能是因为我已经花了几个月的时间写作,并凭记忆知道了大部分内容。
分级(研究生授课)
利兹大学的所有学位都有一个评分标准。基本上,你的成绩是通过对你的单元分数进行加权平均,然后除以 10 计算出来的。
举个例子,如果你的加权平均分是 75,那么你的最终得分是 7.5。请注意,用于计算加权平均值的权重是您的模块的学分。因此,在学分较高的模块中表现良好将对你的期末成绩产生更大的影响。
您的最终奖励取决于您的最终得分属于哪个范围。这些是:
- **失败:**小于 5.0
- **过关:**5.0–5.9
- **优点:**6.0–6.9
- **区别:**大于 7.0
0.1 的任意范围适用于所有边界线。例如,如果你的最终分数是 6.93,那么你仍然可以得到一个优异的成绩。
这种不及格、及格、优秀和优秀的评分系统是英国研究生教育最常见的。在利兹,他们将加权平均值除以 10,但我认为大多数其他大学不会介意。如果我们忽略这一点,那么我看过的每所大学都有基本相同的界限。
这里有一个计算器,你可以从利兹大学网站下载。
最后,对于那些好奇的人,我已经扫描了我的成绩单,并在下面放了一个计算器的截图。

我的所有模块,包括他们的分数,水平和学分。

分数计算器与我的模块结果。
利兹数据科学学会
利兹大学为学生提供了一个很好的数据科学社团。他们提供以团队为基础的项目,学生可以参加以获得额外的经验。例如,我参加了全国 Hiscox 数据挑战赛,从大学中选出的团队参与解决一个数据科学问题。利兹数据科学协会组建了这个团队,并允许我们代表学校。
这给我的简历带来了奇迹,结果,我出现在 Tableau 的世代数据博客上。我在采访中被问到这个项目,对于没有太多经验的人来说,这是一个很好的起点。
作为一名学生,很容易忘记数据科学是一个非常基于项目的领域。我认为有一个数据科学协会参加对任何大学来说都是非常有价值的。它会对你的数据科学求职产生巨大影响,尤其是作为一名应届毕业生。当你在寻找合适的硕士课程时,一定要在这方面做些研究。
结论
这里有相当多的信息需要消化。希望这是有用的,特别是如果你想在利兹学习同样的课程。总的来说,我对这门课程的体验是相当积极的。虽然肯定有一些我不喜欢的模块和我不喜欢的教授,但我在短时间内学到了很多东西。
无论你是未来的学生还是校友,请联系我,让我知道你的想法。听到别人为什么决定攻读数据科学硕士学位的故事总是很有趣!
原载于 2021 年 7 月 7 日【https://leonlok.co.uk】。
使用 PettingZoo 的 13 行代码中的多智能体深度强化学习
原文:https://towardsdatascience.com/multi-agent-deep-reinforcement-learning-in-15-lines-of-code-using-pettingzoo-e0b963c0820b?source=collection_archive---------0-----------------------
初学者多智能体深度强化学习教程
免责声明
对于 PettingZoo 和 Stable Baselines 3 的新版本,本教程不再是最新的。自从这篇文章发表以来,PettingZoo 经历了一些重大的修改,现在是 Farama 基金会的一部分。有关最新文档和教程,请参见 https://pettingzoo.farama.org/的[。](https://pettingzoo.farama.org/)
本教程提供了使用多代理强化学习的简单介绍,假设有一点机器学习的经验和 Python 的知识。
强化学习简介
强化源于使用机器学习来优化控制环境中的代理。它通过学习一个策略,一个函数来工作,该函数将从其环境中获得的一个观察映射到一个动作。策略函数是典型的深度神经网络,这就产生了“深度强化学习”的名称
强化学习的目标是学习一个最优策略,一个在行动时从环境中获得最大期望回报的策略。奖励是一个单一的无量纲的值,在一个行动之后立即由环境返回。整个过程可以想象成这样:

版权所有贾斯汀·特里 2021
这种强化学习的范式包含了各种各样令人难以置信的场景,比如电脑游戏中的一个角色(例如,Atari,奖励是分数的变化),在城市里运送食物的机器人(代理人成功完成一次旅行会得到积极的奖励,花费太长时间会受到惩罚),或者交易股票的机器人(奖励是获得的金钱)。
多智能体强化学习
学习玩多人游戏代表了我们一生中人工智能的许多最深远的成就。这些成就包括学习玩围棋、 DOTA 2 和星际争霸 2 到超人的性能水平。不出所料,使用强化学习来控制多个智能体被称为多智能体强化学习。一般来说,这与单个代理强化学习是一样的,每个代理都试图学习自己的策略来优化自己的回报。对所有代理使用中央策略是可能的,但是多个代理必须与中央服务器通信来计算它们的动作(这在大多数真实世界场景中是有问题的),所以在实践中使用了分散的多代理强化学习。这可以想象如下:

版权所有贾斯汀·特里 2021
多智能体深度强化学习,我们今天要做的,同样只是使用深度神经网络来表示多智能体强化学习中的学习策略。
宠物动物园和活塞球
Gym 是 OpenAI 开发的一个著名的强化学习库,它为环境提供了一个标准的 API,这样就可以很容易地用不同的强化学习代码库来学习它们,并且对于相同的学习代码库,可以很容易地尝试不同的环境。 PettingZoo 是一个更新的库,它就像一个多代理版本的 Gym。它的基本 API 用法如下:
from pettingzoo.butterfly import pistonball_v5
env = pistonball_v5.env()
env.reset()
for agent in env.agent_iter():
observation, reward, done, info = env.last()
action = policy(observation, agent)
env.step(action)
我们今天要学习的环境是 Pistonball ,一个来自 PettingZoo 的合作环境:

在其中,每个活塞都是一个代理,可以由一个策略单独控制。观察活塞上方和旁边的空间,例如:

策略返回的动作是提升或降低活塞的量(从-4 到 4 个像素)。目标是让活塞学会如何协同工作,尽快把球滚到左边墙。如果球向右移动,每个活塞代理都获得负奖励,如果球向左移动,则获得正奖励,并且在每个时间步长都获得少量负奖励,以激励尽可能快地向左移动。
在强化学习中,有大量的技术可以学习单个代理环境。这些是多智能体强化学习算法的基础。最简单和最流行的方法是在所有代理之间共享一个策略网络,这样所有代理使用相同的功能来选择一个动作。每个代理可以使用任何单个代理方法来训练这个共享网络。这通常被称为参数共享。这就是我们今天要用的方法,用 PPO 单代理方法(像这样的连续控制任务的最好方法之一)。
代码
首先我们从进口开始:
from stable_baselines3.ppo import CnnPolicyfrom stable_baselines3 import PPOfrom pettingzoo.butterfly import pistonball_v5import supersuit as ss
我们已经讨论过了,但是让我们来谈谈稳定基线。几年前,OpenAI 发布了“基线”存储库,其中包括大多数主要深度强化学习算法的实现。这个存储库变成了稳定的基线库,旨在供强化学习的初学者和实践者轻松使用来学习健身房环境。其中的 CnnPolicy 只是稳定基线包含的深度卷积神经网络对象,它自动调整神经网络的输入和输出层的大小,以适应环境的观察和动作空间。SuperSuit 是一个为健身房和宠物动物园环境提供预处理功能的包,我们将在下面看到。环境和包装器被版本化,以确保在学术研究中精确地再现比较。
首先,我们初始化 PettingZoo 环境:
env = pistonball_v5.parallel_env(n_pistons=20, time_penalty=-0.1, continuous=True, random_drop=True, random_rotate=True, ball_mass=0.75, ball_friction=0.3, ball_elasticity=1.5, max_cycles=125)
这些参数中的每一个都以不同的方式控制着环境如何运行,并在这里进行了记录。这里我们需要使用的另一种 parallel_env 模式在这里有记录。
我们要处理的第一个问题是环境的观测是全彩色图像。我们不需要颜色信息,由于 3 个颜色通道,神经网络处理的计算成本是灰度图像的 3 倍。我们可以通过用超级套装包裹环境来解决这个问题(记得我们在上面把它作为 ss 导入),如下所示:
env = ss.color_reduction_v0(env, mode=’B’)
请注意,B 标志实际上采用图像的蓝色通道,而不是将所有通道都转换为灰度,以节省处理时间,因为在训练期间会进行成千上万次。在此之后,观察结果将是这样的:

尽管对每个活塞的观察都是灰度的,但图像仍然非常大,包含的信息比我们需要的更多。让我们把它们缩小。84x84 在强化学习中是一个流行的大小,因为它在 DeepMind 的一篇著名论文中使用过。用超级套装解决这个问题看起来像这样:
env = ss.resize_v0(env, x_size=84, y_size=84)
在此之后,观察结果将类似如下:

我们要做的最后一件重要的事情在开始时有点奇怪。因为球在运动,我们想给政策网络一个简单的方法来观察它运动和加速的速度。最简单的方法是将过去的几帧叠加在一起,作为每次观测的通道。将 3 叠加在一起,可以给出足够的信息来计算加速度,但 4 更标准。这是你如何用超级套装做到的:
env = ss.frame_stack_v1(env, 3)
接下来,我们需要稍微转换一下环境 API,这将导致稳定的基线在多代理环境上进行策略网络的参数共享(而不是像普通的那样学习单代理环境)。这方面的细节已经超出了本教程的范围,但是对于那些想知道更多的人来说,在这里记录了。
env = ss.pettingzoo_env_to_vec_env_v1(env)
最后,我们需要将环境设置为并行运行自身的多个版本。一次多次在环境中玩耍可以使学习更快,这对 PPOs 的学习表现很重要。SuperSuit 提供了许多方法来做到这一点,我们想在这里使用的是这个:
env = ss.concat_vec_envs_v1(env, 8, num_cpus=4, base_class=’stable_baselines3’)
8 是指我们复制环境的次数,num _ cpus 是运行这些环境的 CPU 核心数。这些是超参数,你可以随意使用它们。根据我们的经验,每个线程运行两个以上的环境会变得很慢,所以请记住这一点。
最后,我们可以开始一些实际的学习。这可以通过稳定的基线和三行代码轻松实现:
model = PPO(CnnPolicy, env, verbose=3, gamma=0.95, n_steps=256, ent_coef=0.0905168, learning_rate=0.00062211, vf_coef=0.042202, max_grad_norm=0.9, gae_lambda=0.99, n_epochs=5, clip_range=0.3, batch_size=256)model.learn(total_timesteps=2000000)model.save(“policy”)
这将实例化 PPO 学习对象,然后训练策略网络并将其保存到磁盘。所有的参数都是超参数,你可以在这里详细阅读(它们都已经用 Optuna 调好了)。中的时间步长参数。learn()方法指的是单个代理采取的动作,而不是游戏进行的总次数。
使用现代 8 核 CPU 和 1080Ti,培训将需要大约 2 个小时(像所有深度学习一样,这是相当密集的 GPU)。如果你没有 GPU,用 T4 的 GPU 在谷歌云平台上训练这个应该不到 2 美元。
看着我们的算法玩游戏
一旦我们训练并保存了这个模型,我们就可以加载我们的策略并观看它的运行。首先,让我们重新实例化环境,这次使用普通的 API:
env = pistonball_v5.env()
env = ss.color_reduction_v0(env, mode=’B’)
env = ss.resize_v0(env, x_size=84, y_size=84)
env = ss.frame_stack_v1(env, 3)
然后,让我们加载策略
model = PPO.load(“policy”)
我们可以使用策略将其呈现在您的桌面上,如下所示:
env.reset()
for agent in env.agent_iter():
obs, reward, done, info = env.last()
act = model.predict(obs, deterministic=True)[0] if not done else None
env.step(act)
env.render()
这应该会产生类似这样的 gif:

请注意,这实际上比开头显示的 gif 更好。这是因为开始的 gif 是用一个手工制作的策略生成的,这里是这里是,而这个是实际学习到的
这篇教程的完整代码可以在这里找到。如果你觉得这个教程有用,可以考虑主演相关的项目(宠物动物园、超级套装和稳定基线 3)。
多臂强盗:带 Python 代码的 Thompson 采样算法
原文:https://towardsdatascience.com/multi-armed-bandits-thompson-sampling-algorithm-fea205cf31df?source=collection_archive---------4-----------------------
了解汤普森采样(贝叶斯土匪)算法。

丹尼斯·简斯在 Unsplash 拍摄的照片
介绍
在这一系列的帖子中,我们尝试了不同的 bandit 算法来优化我们的电影之夜——更具体地说,我们如何选择电影和餐馆来送餐!
对于新人来说,土匪这个名字来自老丨虎丨机(被称为独臂土匪)。你可以把它想成每次和它互动(拉它胳膊)都能奖励你(或者不奖励你)的东西。目标是,给定一群给出不同奖励的强盗,尽可能快地找出给出最高奖励的强盗。当我们开始玩游戏并不断收集关于每个强盗的数据时,强盗算法会帮助我们在利用迄今为止给我们最高奖励的强盗和探索其他强盗之间进行选择。
https://medium.com/analytics-vidhya/multi-armed-bandits-part-1-epsilon-greedy-algorithm-with-python-code-534b9e2abc9
你和你的朋友一直在使用 bandit 算法来优化每周电影之夜选择哪家餐馆和哪部电影。到目前为止,你已经尝试了不同的强盗算法,如ε贪婪、乐观初始值和置信上限 (UCB)。您已经发现 UCB1 调整的算法在伯努利和普通奖励方面都比其他算法稍好,并且已经在过去几个月中使用了它。
尽管你的电影之夜因 UCB1-Tuned 做出的选择而进展顺利,但你错过了尝试新算法的兴奋感。
“你听说过汤普森抽样吗?”你的朋友问。
兴奋的你拿起手机开始阅读。
该算法
汤普森抽样,也称为贝叶斯土匪,是多武装土匪问题的贝叶斯方法。基本思想是把每个强盗的平均报酬𝛍视为一个随机变量,并使用我们目前收集的数据来计算它的分布。然后,在每一步,我们将从每个强盗的平均奖励分布中抽取一个点,并选择其样本值最高的一个。我们随后从选中的强盗那里得到奖励,并更新它的平均奖励分布。
我听到你问我们如何计算/更新分布?贝叶斯强盗这个名字可能已经泄露了,但是我们将使用贝叶斯定理。
Bayes Theorem
P(A|B) = P(B|A) P(A) / P(B)where
A, B: events in sample space
P(A|B): conditional probability of A given B
P(B|A): conditional probability of B given A
P(A): probability of A
P(B): probability of B
以上定义是针对离散变量,对于连续变量是针对概率密度函数定义的。
在我们的例子中,对于每一个土匪,我们想计算他们的 P( 𝛍 |数据),也就是说,给定我们到目前为止收集的数据,𝛍的不同值的可能性有多大。
P( 𝛍 | data ) = P(data | 𝛍) P(𝛍) / P(data)
∝ P(data | 𝛍) P(𝛍)
posterior ∝ likelihood x prior
具有许多数据点的土匪将具有狭窄的分布,因此我们采样的数据点将有很高的机会接近真实平均值。然而,拥有很少数据点的盗匪将会有一个广泛的分布,给高值一个被选择的好机会。这种机制平衡了探索和开发。

图 1:更新单个伯努利强盗的平均奖励分布,奖励概率为 0.8。平均回报遵循贝塔(成功+ 1,失败+ 1)分布,假设贝塔(1,1)先验和二项式可能性。作者制作的动画。
现在,计算 P( 𝛍 |数据)是棘手的,除非可能性和先验是 共轭先验 。这意味着,当我们结合似然性和先验时,我们得到的后验分布与先验分布来自同一个家族。这很方便,原因有二:
- 我们可以避免计算后验概率的麻烦,因为它有一个封闭的表达式。
- 我们可以使用步骤 i-1 的后验概率作为步骤 i 的先验概率。这使得从 P( 𝛍 |直到步骤 i-1 的数据)到 P( 𝛍 |直到步骤 i 的数据)变得容易,并确保我们只使用共轭先验。
伯努利奖励
在这篇文章的其余部分,我们将假设一个强盗给𝛍的概率是 1,否则是 0,即
ith bandit rewards ~ Bernoulli(prob = 𝛍_i)
现在我们需要为𝛍.选择一个先验分布由于𝛍是成功的概率,它只能取 0 到 1 之间的值。因此,我们需要一个支持[0,1]的分布。一种这样的分布是具有参数a和b的贝塔分布。在我们收集任何数据之前,𝛍的所有值都是同样可能的,因此[0,1]上的统一先验是有意义的。选择a = 1和b = 1给了我们这样的机会。
接下来,我们需要选择一个可能性,最好是β分布是共轭先验的可能性。二项式分布满足这一点,它对我们的用例也有意义,因为它用成功概率p(这是我们试图估计的𝛍)模拟了从N试验(我们玩强盗的次数)中获得k成功的概率(强盗奖励的总和)。
If
Prior is 𝛍 ~ Beta(𝛼, 𝛽)
Likelihood is k | 𝛍 ~ Binomial(N, 𝛍)
Then
Posterior is 𝛍 ~ Beta(𝛼 + successes, 𝛽 + failures)
Where
N = successes + failures
这是一个简单的更新规则,如果强盗的奖励是 1,我们增加它的a,如果是 0,我们增加它的b。

图 2:使用 Thompson 抽样的实验,四个强盗获得奖励的概率(0.6,0.7,0.8,0.9)等于 1。我们可以看到,随着我们为每个土匪收集更多的数据,分布变得越来越窄。作者的动画。
给我看看代码
在之前的文章中,我们已经定义了你将在这里看到的基类,但是为了完整起见,我们再次将它们包括在内。
下面的代码定义了代表强盗的类BernoulliBandit,代表代理的基类Agent和跟踪奖励的助手类BanditRewardsLog。
现在,剩下的唯一事情就是子类化Agent类并实现 Thompson 采样。
…我们完成了!现在,让我们看看它如何与以前帖子中的算法相比较,即ε贪婪、乐观初始值和置信上限 (UCB)。
实验
我们将使用下面的代码来比较不同的算法。
首先,我们来定义一下我们的土匪。
probs = [0.6, 0.7, 0.8, 0.9]
bernoulli_bandits = [BernoulliBandit(p) for p in probs]
在这之后,我们可以简单地运行
compare_agents(
get_agents(),
bernoulli_bandits,
iterations=500,
show_plot=True,
)
这给了我们以下。

Tota 每个代理的累积奖励。图片由作者提供。
嗯…不是很清楚,但是好像 Epsilon-Greedy 和 Bayesian 土匪有类似的表现。为了找到最好的一个,让我们重复上面的实验,并计算每个算法得到最佳结果的次数。
wins = run_comparison(bernoulli_bandits)
for g, w in zip(get_agents(), wins):
print(g, w)
我的结果是 Epsilon-Greedy 为 397,UCB1 为 0,UCB1-Tuned 为 220,Thompson Sampling 为 383。看起来 Thompson 采样和 Epsilon-Greedy 是我们特定设置的赢家。UCB1-Tuned 获得第三名有点令人惊讶,因为在之前的实验中,它的表现超过了ε-Greedy 算法。这可能是由于 Thompson Sampling 窃取了它的一些成果,但这需要更多的调查。
结论
在这篇文章中,我们研究了汤普森采样算法是如何工作的,并为伯努利强盗实现了它。然后,我们将它与其他多臂强盗算法进行比较,发现它的性能与 Epsilon-Greedy 差不多。
成为 Medium的会员,获得所有故事的全部权限。你的会员费直接支持你读的作家。
出自同一作者。
https://medium.com/analytics-vidhya/calculating-using-monte-carlo-simulations-337cff638ac5
参考
[1]汤普森采样教程。(未注明)。检索于 2021 年 9 月 25 日,来自https://web.stanford.edu/~bvr/pubs/TS_Tutorial.pdf.
多臂强盗:带 Python 代码的置信上限算法
原文:https://towardsdatascience.com/multi-armed-bandits-upper-confidence-bound-algorithms-with-python-code-a977728f0e2d?source=collection_archive---------7-----------------------
实践教程
了解不同的置信上限 bandit 算法(UCB1、UCB1-Tuned、UCB1-Normal)。为所有实验提供的 Python 代码。

由丹尼斯·简斯在 Unsplash 上拍摄的照片
介绍
在这一系列的帖子中,我们尝试了不同的 bandit 算法来优化我们的电影之夜——更具体地说,我们如何选择电影和餐馆来送餐!
对于新来的人来说,bandit 这个名字来自吃角子老丨虎丨机(被称为独臂强盗)。你可以把它想成每次和它互动(拉它胳膊)都能奖励你(或者不奖励你)的东西。目标是,给定一群给出不同奖励的强盗,尽可能快地找出给出最高奖励的强盗。当我们开始玩游戏并不断收集关于每个强盗的数据时,强盗算法会帮助我们在利用迄今为止给我们最高奖励的强盗和探索其他强盗之间进行选择。
在我们的故事中,我们使用餐馆而不是强盗,我们的回报是食物的满足感!:)
https://medium.com/analytics-vidhya/multi-armed-bandits-part-1-epsilon-greedy-algorithm-with-python-code-534b9e2abc9 https://medium.com/swlh/multi-armed-bandits-optimistic-initial-values-algorithm-with-python-code-3970e611b5ab
你和你的朋友已经有一段时间没有使用 bandit 算法来优化你的电影之夜了。到目前为止,你一直在使用 Epsilon-Greedy (与𝛆=1/#actions 一起)来帮助寻找最好的餐馆,而你的朋友一直固执地使用乐观初始值算法来挑选电影导演(即使你的模拟已经表明它可能不适合你的用例)。
当你在互联网上消磨时间,因为封锁而被困在里面时,你会遇到一些基于置信上限 (UCB)的强盗算法。你兴奋地发现有可能改进对最佳餐馆的搜索,并更有效地解决剥削-探索的困境,你开始阅读,期待着下一个电影之夜。
他们中有谁会比ε-Greedy 更好吗?让我们来了解一下!
算法
这个想法是利用我们目前收集的数据,用概率论得出每个土匪平均报酬的上限。我们就把这个上界叫做土匪的上置信 指标,就像原论文 **里一样。**与置信区间一样,如果我们没有大量数据,样本均值的边界会很宽。在每一步,我们选择具有最高上置信指数的强盗,获得奖励并随后更新其指数。
这听起来有点类似于乐观初始值算法吧?提出这个指数似乎比仅仅选择一个乐观的初始值更复杂,但是实现仍然相对简单(我发誓)。实际上,它们使用起来更简单,因为它们没有任何需要我们选择的参数!
一般来说,每个 bandit 的置信上限指数具有以下形式。
Bandit Upper Confidence Index = X_sample_mean + Cwhere
X_sample_mean: sample mean for rewards so far
C > 0: size of one sided confidence interval
随着我们为强盗收集更多的数据,它应该减少,这是有意义的,因为我们变得更加确定平均奖励的可能值。没有被充分探索的强盗会有一个更大的C,这将导致我们选择他们进行探索。
在伪代码中:
Choose n # number of iterationsInitialisation step. # for UCB1 & UCB1-Tuned we play each bandit oncefor i = 1 to n do:
current_bandit = pick bandit with greatest upper confidence index
reward = current_bandit.pull_arm() Update upper confidence index for current_bandit
好的……我们知道如何计算样本均值,但是我们如何计算C?嗯,看情况吧!
在原始论文中提出了几个 UCB 算法,每个算法对C都有不同的定义。 UCB1 和 UCB1-Tuned 设计用于奖励为 0 或 1,即来自伯努利分布的奖励(参见本文)。如果你的回报来自正态分布,有另一个算法,叫做UCB-正态,就是为此设计的。你可以使用 UCB1 获得其他发行版的奖励,但是据说性能没有那么好(我们将测试这一点)。
UCB1
UCB1 的 bandit 置信指数上限等于
Bandit Upper Confidence Index = Sample Mean + √(2logN / n)where
N = total rounds (e.g. number of movie nights so far)
n = number of times we've played the bandit (
e.g. number of times we've ordered from a restaurant
).

使用 UCB1 与四个获得奖励的概率等于 1 的强盗(0.6,0.7,0.8,0.9)进行实验。我们可以看到上置信指数是如何变化的,每个强盗玩了多少次,以及我们对每一步的样本均值的估计。作者的动画。
它是使用 切尔诺夫-赫夫丁界限 推导出来的,即
P( Sample Mean + t ≤ True Mean) ≤ exp(-2nt²) for t > 0
其中n是我们迄今为止从特定的土匪那里收集的数据点的数量。这意味着,样本均值的误差大于t的概率随着n的增加而降低。
为了推导上限,我们首先设置右侧等于b,并求解t。
Let
b = exp(-2nt²).Then
logb = -2nt² <=> t = √(-logb / (2n))Choose(this is a heuristic)b = (1 / N⁴)Then
t = √(2logN / n)
在上面,n是我们玩特定强盗的次数,N是我们玩的总次数(回合数或电影夜数,如果你喜欢的话)。你可以把b看成一个超参数。
We know
P( Sample Mean + t ≤ True Mean) ≤ exp(-2nt²) for t > 0Substitute for t
P( Sample Mean + √(2logN / n) ≤ True Mean) ≤ 1 / N⁴**Define**
**Upper Confidence Index = Sample Mean + √(2logN / n)****Hence
C = √(2logN / n)**
从上面我们可以推断出,真实平均值大于置信指数上限的概率小于 1/n⁴——随着回合数N的增加,这收敛到 0。因此,使用它作为上限是有意义的。
有一点需要注意的是比例logN / n。随着回合数N和我们玩强盗的次数n之间的差距增加,界限也增加。

保持 N 不变,增加 N,我们可以看到,当我们玩不涉及强盗的回合时,C 增加。图片由作者提供。

保持 N / n 比不变,我们可以看到,随着 N 和 N 都增加,界限减小。这是因为作者在 N. Image 上的对数。
UC B1-调谐
对于 UCB1-Tuned,我们用以下内容替换C
C = √( (logN / n) x min(1/4, V(n)) )where V(n) is an upper confidence bound on the variance of the bandit, i.e.V(n) = Σ(x_i² / n) - (Σ x_i / n)² + √(2log(N) / n) and x_i are the rewards we got from the bandit so far.
在原始论文(见第 4 节)中,UCB1 调优的在每个实验中都优于 UCB1。

实验使用 UCB1-Tuned 与四个土匪的概率(0.6,0.7,0.8,0.9)获得奖励等于 1。我们可以看到上置信指数是如何变化的,每个强盗玩了多少次,以及我们对每一步的样本均值的估计。作者的动画。
UC B1-正常
对于 UCB1-Normal,C基于样本方差,与前两种算法不同,它是为正态分布的奖励而设计的。
C = √( 16 SV(n) log(N - 1) / n )where the sample variance is
SV(n) = ( Σ x_i² - n (Σ x_i / n)² ) / (n - 1)
x_i are the rewards we got from the bandit so far
UCB1-Normal 通常没有初始化步骤。相反,在每一轮N中,它会检查是否有一个土匪的出牌次数少于8logN的上限,然后就打那个土匪,而不是看上面的信心指数。然而,为了计算C,我们需要n至少为 2,这样我们就不会被零除。因此,我们将每个强盗玩两次作为初始化步骤。

在每一个强盗被玩了将近 50 次之前,我们不会在大多数回合中使用较高的信心指数来选择强盗。

使用 UCB1-Normal 对五个土匪进行实验,奖励均值为(2,5,7,9,11),标准差为 2.5。作者的动画。
给我看看代码
唷…那是很多界限。这些代码中的一部分已经在以前的帖子中出现过,但是为了完整起见,我还是将它包含在这里。首先,我们需要一些类来代表强盗,一个用于伯努利奖励,一个用于普通奖励。
接下来,我们将为代理定义一些基类(以及一个助手类来跟踪奖励——如果您已经阅读过上一篇文章的话,BanditRewardsLog已经被修改为跟踪奖励平方的总和)。
我们仍然需要定义ε贪婪代理的类,因为我们将使用它来进行比较,以及 UCB 代理。它们将从Agent类继承,并且需要实现take_action,因为它是一个抽象方法。
我们现在有了强盗和特工的所有必要职业。娱乐时间到了!
算法战场
我们将直接把所有的 UCB 算法和ε贪婪算法投入战场。我们将考虑两种情况,一种奖励来自伯努利分布,另一种奖励来自正态分布。
我们将利用以下实用函数。
*def* compare_agents(
*agents*: List[Agent],
*bandits*: List[Bandit],
*iterations*: int,
*show_plot*=*True*,
):
*for* agent *in agents*:
logger.info("Running for agent = %s", agent)
agent.bandits = *bandits
if* isinstance(agent, ucb.UCBAgent):
agent.initialise()
N = *iterations* - agent.rewards_log.total_actions
agent.take_actions(N)
*if show_plot*:
cumulative_rewards = np.cumsum(
agent.rewards_log.all_rewards,
)
plt.plot(cumulative_rewards, label=str(agent))
*if show_plot*:
plt.xlabel("iteration")
plt.ylabel("total rewards")
plt.legend(loc='center left', bbox_to_anchor=(1, 0.5))
plt.show() def get_agents():
agents = [
EpsilonGreedyAgent(),
ucb.UCB1Agent(),
ucb.UCB1TunedAgent(),
ucb.UCB1NormalAgent(),
]
return agents def run_comparison_ucb(bandits):
win_count = [0, 0, 0, 0]
for _ in range(1000):
agents = get_agents()
iterations = 1000
compare_agents(agents, bandits, iterations, show_plot=False)
rewards = [
agent.rewards_log.total_rewards
for agent in agents
]
win_count[np.argmax(rewards)] += 1
return win_count
愿最好的算法胜出!
伯努利奖励
我们来定义一下我们的土匪和特工。
probs = [0.6, 0.7, 0.8, 0.9]
bernoulli_bandits = [BernoulliBandit(p) for p in probs]
agents = get_agents()
那么我们可以简单地做
compare_agents(agents, bernoulli_bandits, 1000, show_plot=True)

每个代理的总累积奖励。图片由作者提供。
看起来 Epsilon-Greedy 和 UCB1-Tuned 比其他两个做得更好,但它很接近。我们将重复这个实验 1000 次,并记录下哪个代理得到了最多的奖励。
run_comparison_ucb(bernoulli_bandits)
我的结果是 Epsilon-Greedy 497 胜,UC B1 3 胜,UC B1-Tuned 500 胜,UC B1-Normal 0 胜。看起来选择 Epsilon-Greedy 或 UCB1-Tuned 是正确的选择。
正常奖励
我们重复了同样的实验,但这次是对按照正态分布给予奖励的强盗。
means = [3, 5, 7, 9]
normal_bandits = [Bandit(m=m, sigma=1) for m in means]

每个代理的总累积奖励。图片由作者提供。
令人惊讶的是,最差的一个好像是 UC B1——正常……丢人。不清楚哪一个是其他三个中最好的,所以我们将重复实验很多次,并记录哪个代理获得了最多的奖励。
我为 Epsilon-Greedy 赢得了 156 场胜利,为 UCB1 赢得了 364 场胜利,为 UCB1-Tuned 赢得了 480 场胜利,为 UCB1-Normal 赢得了 0 场胜利。
结论
在这篇文章中,我们研究了置信上限 bandit 算法是如何工作的,用 Python 对它们进行了编码,并将它们与其他算法以及 Epsilon-Greedy(与𝛆=1/#actions).)算法进行了比较
我们发现 UCB1-Tuned 在伯努利和普通奖励方面表现最佳,尽管它不是为普通奖励设计的。其中最差的是 UC B1——到目前为止还算正常——这可能是它不如其他产品受欢迎的原因。
参考
[1] Auer,p .,Cesa-Bianchi,n .和 Fischer,p .,多臂 Bandit 问题的有限时间分析。机器学习 **47、**235–256(2002)。https://doi.org/10.1023/A:1013689704352
https://eminik355.medium.com/subscribe
成为 Medium会员,获得全部故事权限。你的会员费直接支持你读的作家。
更多来自同一作者。
https://medium.com/analytics-vidhya/calculating-using-monte-carlo-simulations-337cff638ac5 https://medium.com/analytics-vidhya/multi-armed-bandits-part-1-epsilon-greedy-algorithm-with-python-code-534b9e2abc9 https://medium.com/swlh/multi-armed-bandits-optimistic-initial-values-algorithm-with-python-code-3970e611b5ab
根据胸部 x 射线对各种胸部状况进行多类别分类
原文:https://towardsdatascience.com/multi-category-classification-of-various-chest-conditions-from-chest-x-rays-1d6428522997?source=collection_archive---------12-----------------------
使用 fast.ai 和 NIH CXR 数据集将 x 射线分类为各种胸部疾病

描述几种情况的胸部 x 光图像。图片作者。
简介
随着医疗需求呈指数级增长,对快速诊断疾病的需求空前高涨。正如新冠肺炎疫情所表明的,检测、诊断和识别症状是防止疾病进一步传播的第一道防线。然而,这些诊断工具仅占全球医疗支出的 2%,尽管它影响了超过 70%的临床决策。
根据皇家放射科医师学会在 2018 年发布的报告,英国只有“2%的放射科能够在合同时间内满足他们的成像报告要求。”在美国,预计到 2032 年将会短缺近 122,000 名医生。

图片由国家癌症研究所的 Unsplash 提供
这是来自发达国家的消息。低收入国家和高收入国家之间的差距是巨大的:后者的放射技师比前者多近 100 倍。例如,在非洲,传染病造成超过 2 . 27 亿年的健康寿命损失。缺乏对此类传染病的快速、准确的诊断导致大量不必要的生命损失。
将涵盖哪些内容:
- 确定问题范围——识别医学成像中的不同情况
- 利用 fast.ai 给我们一个多类别分类器
- 准备数据
- 训练模特
- 创建交互式图形用户界面,根据真实数据测试模型
- 限制
- 学习要点
医学成像中不同条件的识别👨⚕
对肺的损害是无法逆转的。然而,早期发现和治疗严重的胸部疾病对于防止任何传染病进一步传播是至关重要的。通过识别指示某些疾病的指示性条件(例如,在胸部 x 射线中),具有快速、准确预测的医疗专业人员将能够对患者进行分类,并以更有针对性和更有效的方式执行后续步骤。
由于诊断这种疾病的紧迫性,再加上严重的人力短缺(尤其是在发展中国家),这种导致无数人丧生,有时是不必要的。
这就是人工智能——特别是深度学习——可以在防止这些生命消失方面发挥关键作用的地方。
严重胸闷多分类器— fast.ai
为了部分缓解诊断人力的缺乏,我们可以使用计算机视觉模型,在寻求进一步的医疗帮助之前,为医生和内科医生提供患者的初步分类。
最大限度地减少操作计算机系统中的挫折和麻烦的理想目标是创建一个端到端的应用程序,医生可以快速准确地获得预测。
为此,我们可以利用 fast.ai 库来帮助我们对多类别条件进行分类——这意味着给定一幅具有多种条件的图像,该模型能够预测每种条件出现的相对可能性。
数据🔂
我们正在使用 NIH 胸部 x 光数据集的一个更小的‘样本’版本来训练我们的模型。数据从最初的 112,000 张缩减到 5606 张。但是,该数据集保留了与其父数据集相同的结构和各种类别中图像的相对分布(例如,两个数据集中约 54%的 x 射线都有“无发现”标签)。因此,这保留了原始数据集的偏差。
为了从 kaggle 获取数据集,我们利用:
然后将包含数据集的压缩文件下载到 Google Colab 实例(我们在这个项目中使用的主要平台)。)
数据处理
由于该示例最初是以 zip 文件的形式下载的,我们必须将其解压缩:
! unzip sample.zip
为了从较高层面概述数据在下载的文件夹中的结构,我们可以仔细看看云存储中的内容:

图像的文件结构,嵌套在/sample/images 下,按作者排序。
如您所见,这些图像嵌套在/sample/images 下,还有一个“sample_labels.csv ”,其中存储了不同图像的标签。
然后,我们可以通过读取 csv 文件创建一个数据帧以及数据集。
df = pd.read_csv(path/f"sample_labels.csv")
dsets = dblock.datasets(df)
完成这一步后,我们必须在将标签与相应的图像匹配之前,将索引到 csv 文件中以提取标签。
我们可以通过构造助手函数来做到这一点,我们可以在我们的数据块中使用这些函数。 fast.ai 文档提供了数据块可以接受的参数列表:

作者图片

创建我们的数据块,作者图像
我们的块是 ImageBlocks(基本上是告诉模型我们正在处理自变量的图像),以及因变量的 MultiCategoryBlocks 告诉模型一个图像可以有几个类别。这防止了模型仅识别一个类别,而实际上它应该检测胸部 x 光中的多个问题。
在这种情况下,我们必须定义自变量和因变量的标签—自变量是图像,因变量是“查找标签”。对于图像,我们必须从。csv 文件。对于因变量,我们必须使用简单的正则表达式提取标签。
这封装在下面显示的 get_x 和 get_y 函数中:
然后,我们必须使用函数 RandomSplitter()将数据分成训练集和验证集。
然后,我们必须使用 aug_transforms 将所有图像转换为相同的大小。
我们使用。数据加载器(df),其中 df 是包含 csv 文件所需值的数据帧。
在训练模型之前,我们最后看一下图像,看看它们的格式是否正确:

作者图片
训练模型🚄

训练多分类模型。作者图片
阐述阈值的概念:
在将 sigmoid 应用于我们的激活后(你可以在此处阅读 sigmoid 用于的内容),我们遇到了一个问题——我们不能简单地选择被预测为具有最高激活的类别(因为可以将不同的类别分配给一张照片)。相反,我们必须通过选取阈值来决定哪些标签不对应于图像。高于阈值的每个值都将被通过(即该标签将被附加到图像上),而低于阈值的每个值都不会被通过。
选择阈值至关重要 —选择过低的阈值将影响模型的整体准确性,因为我们将无法选择正确标记的对象,这意味着如果注册了激活度相当低的标签,它将被错误地添加到图像上的多个标签之一。另一方面,选择过高的阈值意味着我们的模型只会在非常有把握的时候附加标签,因此再次降低了准确性。
我们可以使用一个方便的函数,通过绘制图表并检查对应于最高精度的点来选择阈值:

作者图片
然后,我们选择一个基本学习速率(可以使用学习速率探测器进一步优化)和 freeze_epochs(迁移学习的一个实例)来修改神经网络的后面几层,而不改变前面几层,从而减少模型训练时间。
这个模型花了大约 19 到 20 分钟来训练,并且返回了大约 92.8% 的准确度。
理想情况下,我们希望简单地调用 learn.predict()来给出最高的诊断可能性。然而,在测试模型时,我意识到标签的对应值超过 0.5 的情况非常罕见(这似乎是标签必须等于的默认值,才能出现在预测中)。因此,在向用户显示之前,我编写了几行代码来编译所有与相应标签匹配的预测。
然后,这将返回一个与发生的相关概率相匹配的胸部状况列表。

作者图片
然后,用户可以对预测进行排序,并对模型预测的条件进行排名。他或她还可以设置一个任意值,以查看哪些条件返回的概率大于设置值。
创建一个 GUI 来根据真实世界的数据✍️测试模型
下一步是根据真实世界的数据测试该模型,看看该模型是否能够根据从互联网上获得的胸部 x 光片来检测胸部状况。

作者图片
在这个场景中,我上传了一张标记为“渗透”的图片。这是通过创建一个简单的用户按钮来完成的,用户可以在这里上传文件。
btn_upload = widgets.FileUpload()btn_upload
然后我们称之为预测:
img = PILImage.create(btn_upload.data[-1])list_preds(img)
让我们看看模型返回什么:

作者图片
因为从业者希望看到不同条件的不同概率,所以增加了一行代码来根据模型的置信度进行排序。

作者图片
它以最大的概率返回“没有发现”,其次是“渗透”和“渗出”。因此,渗透被列为第二个预测,这证明了这个模型实际上可以用来解释数据,在监督医生的监督下。
在一天结束时,这个工具可能最适合于加速放射科医生或医生处理诊断成像的过程。
要查看我原来的谷歌 Colab 笔记本,请随意查看这里的!
⛑的局限性
尽管如此,在完成这个简短的项目后,我仍然遇到了一些限制:
- 虽然创建一个令人愉快的 UI 的目标没有完全实现,但是仍然创建了一个图形用户界面,用户可以在其中上传胸部 x 射线的图像以获得他们的预测。要采取的进一步措施可能是使用 streamlit 或 bindr 将这个模型推送到网络上。
- 数据的固有偏差(其结构使得其具有超过大多数的“无发现”)—可能已经基于医生接收到的内容进行了结构化,但是这仅仅意味着模型将会有偏差,以预测对于所预测的大多数图像来说“无发现”
- 目前不能正确地分割和识别胸部 x 射线的哪些部分代表什么情况,具有这种功能或者绘制边界框以向医生突出显示情况将进一步减少用于诊断严重胸部情况的时间。
学习要点💡
在整个过程中,我收集了一些智慧的金块。

图片由un splashPiron Guillaume拍摄
这个过程让我瞥见了知道深度学习和计算机视觉在医学领域的各种应用。人工智能在医学上的应用有着惊人的潜力。根据 Mihaela van der Schaar 的说法,当我们在医学中使用人工智能时,我们只是“触及了可能的表面”。我们还没有充分利用人工智能的可能性,使医疗保健生态系统更加强大和有效,我对这一领域将出现的突破感到非常兴奋。这些是我感兴趣的医疗保健和人工智能交叉领域的一些研究:
- confidence care:个性化乳腺癌筛查的临床决策支持系统
- 改进自主乳房 x 光诊断
- 利用深度神经网络对皮肤癌进行皮肤科医生级别的分类
关于如何使用人工智能来重塑医疗保健的未来,请查看 van der Schaar 的这篇深度文章:https://www . vanderschaar-lab . com/revolution izing-health care-an-invitation-to-clinical-professionals-everywhere/
毫无疑问,在医疗保健领域全面采用人工智能会遇到一些阻力(可能是因为医生和计算机之间的不良历史),但我已经开始相信人工智能永远无法完全取代医生(至少在不久的将来)。
在我看来,人工智能更像是一种工具,我们可以利用它来做出更准确、更快速的预测。用巴斯蒂安·特龙自己的话来说:“认知革命将允许计算机扩大人类思维的能力……正如机器使人类肌肉强大一千倍,机器将使人类大脑强大一千倍。”因此,人工智能只会增强医生的职业,而不会完全取代它。(这里有一篇很棒的文章知道 AI 会在多大程度上取代医生)。Maxfield 和 van der Schaar 提出了类似的观点:“如果医疗保健没有(1)以人为本和(2)完全以人为本,就没有可行的愿景。这不是非此即彼的问题。人类和人工智能/机器学习各有独特的优势,必须结合起来……而不是单独应用。”
个人认为,让数据更具可读性和可解释性是值得花时间去做的事情。由 fast.ai 中的开箱即用方法返回的原始预测不会给(理论上的)用户任何他或她在看什么的想法。今年在探索了 UI 和 UX 之后,我将它付诸实践,扩充了结果,现在它对数据的概率进行了排序,与左边条件的预测相匹配。
结论
通过这个项目,我学到了很多东西——从启动我的第一个多类别分类项目,到探索医学成像和人工智能之间的交叉——尽管最初再次掌握 Python 有些困难,但我还是设法弄明白了!
通过获得对利用人工智能的细微差别(老实说,挑战)的更深入的见解,我开始意识到在现实世界中部署一个准确的模型并不简单——有太多的原因可以解释为什么事情会变得可怕(或者正确!).了解用例,以及行业需要什么,仍然是最重要的。这个项目只是一个开始;探索 AI 能给世界带来的积极影响,我太炒作了。
随时联系我这里!!
使用焦点损失和光照 GBM 的多类分类
原文:https://towardsdatascience.com/multi-class-classification-using-focal-loss-and-lightgbm-a6a6dec28872?source=collection_archive---------5-----------------------
在多类分类器中引入聚焦损失有几种方法。这是其中之一。
动机
许多现实世界的分类问题具有不平衡的类别分布。当数据严重不平衡时,分类算法倾向于预测多数类。有几种方法可以缓解阶级不平衡。
一种方法是分配与类别频率成反比的样本权重。另一种方法是使用过采样/欠采样技术。为少数类生成人工样本的常用技术是合成少数过采样技术(SMOTE)和自适应合成(ADASYN),这两种技术都包含在 imblearn Python 库中。
最近,提出了使用聚焦损失目标函数。该技术被宗-林逸等人用于二元分类。
在这篇文章中,我将演示如何将焦点损失合并到 LightGBM 分类器中进行多类分类。该代码可在 GitHub 上获得。
二元分类
对于二元分类问题(标签 0/1 ),焦点损失函数定义如下:

等式 1 聚焦损失函数
其中 pₜ 是真实标签的函数。对于二元分类,该函数定义为:

等式 2 类别概率
其中 pₜ是通过将 sigmoid 函数应用于原始边距而获得的 z:

等式 3 用于将原始边距 z 转换成类别概率 p 的 Sigmoid 函数
聚焦损失可以被解释为二元交叉熵函数乘以调制因子(1- pₜ )^ γ ,这减少了易于分类的样本的贡献。加权因子 aₜ 平衡调制因子。引用作者的话:“当γ = 2 时,与 CE 相比,pt = 0.9 的示例的损耗低 100 倍,pt ≈ 0.968 的示例的损耗低 1000 倍”。减少容易分类的例子的损失允许训练更多地集中在难以分类的例子上”。
在 Max Halford 的博客[ 2 ]中可以找到一篇关于在二进制 LigthGBM 分类器中合并焦点损失的精彩文章。
多类分类
在多类分类器中引入聚焦损失有几种方法。形式上,调制和加权因子应该应用于分类交叉熵。这种方法要求提供多级损失的一阶和二阶导数作为原始利润 z 。
另一种方法是使用一个对其余的(OvR),其中为每个类 C 训练一个二元分类器。来自类别 C 的数据被视为正值,所有其他数据被视为负值。在这篇文章中,使用了 OvR 方法,采用了 Halford 开发的二元分类器。
下面显示的 OneVsRestLightGBMWithCustomizedLoss 类封装了这种方法:
此类重新实现 sklearn.multiclass 命名空间的 OneVsRestClassifier 类。重新实现最初的 OneVsRestClassifier 类的动机是能够向 fit 方法转发附加参数。这有助于传递评估集(eval_set)以提前停止,从而减少计算时间并避免过拟合。
此外,该类使用通用的 LightGBM 训练 API,在处理原始利润 z 和定制损失函数时,需要使用该 API 来获得有意义的结果(有关更多详细信息,请参见[ 2 )。如果没有这些约束,就有可能实现更一般的类,不仅接受任何损失函数,还接受任何实现 Scikit Learn 模型接口的模型。
该类的其他方法是 Scikit 学习模型接口的一部分:fit、predict 和 predict_proba。在 predict 和 predict_proba 方法中,基本估计量返回原始毛利 z 。请注意,当使用定制的损失函数时,LightGBM 会返回原始边距 z 。使用 sigmoid 函数从边距计算类别概率,如等式所示。3.
一个例子
让我们首先创建一个包含 3 个类的人工不平衡数据集,其中 1%的样本属于第一类,1%属于第二类,98%属于第三类。通常,数据集分为训练集和测试集:
为了使实验简单,放弃了早期停止。训练后产生的混淆矩阵如下所示:

图 1 使用标准 LightGBM 分类器的测试集上的混淆矩阵
对于第一个实验,在测试集上获得了 0.990 的准确度和 0.676 的召回值。使用具有焦点损失的 OneVsRestLightGBMWithCustomizedLoss 分类器重复相同的实验。
从上面的代码可以看出,损失函数可以在分类器之外配置,并且可以注入到类构造函数中。通过向 fit 方法提供一个包含 eval_set 的字典,可以启用提前停止,如上面的注释行所示。对于第二个实验,产生的混淆矩阵如下所示:

图 2 使用 LightGBM 和定制的多类焦损失类(OneVsRestLightGBMWithCustomizedLoss)的测试集上的混淆矩阵
在这种情况下,获得了 0.995 的准确度和 0.838 的召回值,比使用默认对数损失的第一个实验有所改进。这一结果在混淆矩阵中也很明显,其中类别 0 的假阳性和类别 1 的假阴性显著减少。
结论
在这篇文章中,我演示了一种在多类分类器中合并焦点损失的方法,使用的是一个对其余部分(OvR)的方法。
不需要使用聚焦损失目标函数、样品重量平衡或人工添加新样品来减少不平衡。在人工生成的多类不平衡数据集上,焦点损失的使用增加了召回值,并消除了少数类中的一些假阳性和假阴性。
该方法的有效性必须通过探索真实世界的数据集来确认,其中噪声和非信息特征预期会影响分类结果。
[1]林,T. Y .,戈亚尔,p .,吉尔希克,r .,何,k .,&多拉尔,P. (2017)。密集物体探测的聚焦损失。IEEE 计算机视觉国际会议论文集(第 2980–2988 页)。
[2]马克斯·哈尔福德(2020)。LightGBM 的聚焦损耗实现。https://maxhalford . github . io/blog/light GBM-focal-loss/#一阶导数
变压器的多级分类
原文:https://towardsdatascience.com/multi-class-classification-with-transformers-6cf7b59a033a?source=collection_archive---------6-----------------------
用 BERT 进行预处理、训练和预测

作者图片
T 转换器被描述为深度学习的第四大支柱[1],与卷积和递归神经网络类似。
然而,从自然语言处理的角度来看——变形金刚远不止这些。自 2017 年推出以来,它们已经主导了大多数 NLP 基准,并继续每天给人留下深刻印象。
问题是,变形金刚太酷了。有了像 HuggingFace 的变形金刚这样的库,用它们来构建不可思议的解决方案已经变得太容易了。
那么,有什么不爱呢?令人难以置信的性能与极致的易用性。
在本文中,我们将从头到尾使用 transformers 构建一个多类分类模型。
预处理
我们将使用烂番茄电影评论数据集。这里,我们给出了从 0 到 4 的五个输出标签。每个标签代表一个情感类别。

烂番茄数据集标记和衡量从消极到积极的情绪。
这里我们将简单介绍几个预处理步骤(完整的笔记本链接在本节末尾)。
总的来说,我们有六个预处理步骤:
使用语句 Id 删除“段”重复。
初始化空数组以存储标记化的文本。
一个热点编码的情绪。
使用我们的输入构建一个
tf.data.Dataset对象,并标记张量。然后转换成我们模型的正确格式。批处理和打乱我们的数据。
90–10 分为培训和验证数据。
所有这些步骤结合起来创建了这个预处理脚本和这个输入管道脚本。
建造和训练
现在我们有了正确格式的数据,我们可以设置模型并开始训练。
构建模型
首先,我们需要初始化预训练的 BERT 模型,如下所示:
我们将使用典型的tf.keras层围绕 BERT 构建一个框架。这个框架有几个部分:
- 两个输入层(一个用于输入 id,另一个用于注意屏蔽)。
- 用于减少过度拟合的可能性并提高泛化能力的后 BERT 丢弃层。
- Max pooling layer 将 BERT 输出的 3D 张量转换为 2D 张量。
- 最终输出激活使用 softmax,这样我们就可以得到我们的获奖类预测。
所有这些都写成了:
然后,我们初始化新的model,并将输入/输出层提供给初始化函数。一旦完成,我们可以冻结 BERT 层来加速训练(以可能的性能下降为代价)。这里,我们将注释掉图层冻结线。
我们冻结 BERT 参数的原因是它们有很多,更新这些权重会显著增加训练时间。
因为 BERT 是预先训练过的,进一步训练的潜在性能提高是很小的,因此不值得额外的训练时间。
培养
现在我们已经建立了模型架构,我们可以初始化我们的训练参数并开始训练。
首先,我们用合适的训练参数编译模型。
这里,我们使用一个带有加权衰减的 Adam 优化器。对于我们的损失和准确性,我们分别使用分类交叉熵和分类准确性。
我们在这里使用分类度量是因为我们的一次性编码多类输出标签是分类的。
现在,我们从model.fit开始训练:
仅仅几个时期后,我们应该得到接近 75%的准确度。最后,我们可以使用以下方法保存我们的模型:
(全训脚本)。
做预测
我们已经预处理了数据,构建、训练并保存了模型。现在,我们可以开始用它做一些预测。
如果您在单个笔记本或脚本中跟随,您不需要这样做——但是正如我向您展示了如何保存模型,我们也将加载它:
准备输入数据
我们希望从像"hello world"这样简单的字符串中做出预测。为此,我们需要添加一些处理步骤,将一个简单的字符串转换成正确的格式,以便我们的模型使用和预测。
为此,我们将使用一个名为prep_data的函数。该函数将获取一个字符串,使用一个BertTokenizer对其进行标记,并将这些标记张量作为一个字典返回,该字典包含'input_ids'和'attention_mask'键-值对:
预言;预测;预告
一旦我们的输入字符串被格式化为正确的 dictionary-tensors 格式,我们就可以将它们传递给我们的model.predict方法——该方法将返回每个输出标签的概率的 Numpy 数组:
(满载和预测脚本)。
这就是我们完整的多类分类变压器模型演练,从开始到结束!
我希望你喜欢这篇文章。如果您有任何问题或建议,请通过 Twitter 或在下面的评论中告诉我。如果你对更多类似的内容感兴趣,我也会在 YouTube 上发布。
感谢阅读!
参考
[1] J. Wang,变形金刚构成深度学习的第四支柱 (2021),方舟研究
🤖带变压器的 NLP 课程
*所有图片均由作者提供,除非另有说明
TensorFlow 2 中的多 GPU 和自定义训练循环
原文:https://towardsdatascience.com/multi-gpus-and-custom-training-loops-in-tensorflow-2-15b4b86b53bd?source=collection_archive---------13-----------------------
如何使用 tf.distribute.MirroredStrategy 在多个 GPU 上训练自定义训练循环模型的简明示例。

使用tf.distribute.MirroredStrategy在 2 个 GPU 上进行分布式训练【图片由作者提供】
TensorFlow 提供了许多关于如何执行分布式训练的优秀教程,尽管这些示例中的大多数严重依赖于 Keras API,这可能会限制想要实现更复杂模型和训练过程的用户。本教程提供了如何在 TensorFlow 2.4 中将tf.distribute.MirroredStategy与自定义训练循环一起使用的简明示例。为此,我们改编了由 Keras 和 TensorFlow 编写的 CycleGAN [1]教程,并支持多 GPU 训练。
请记住,尽管本指南不是关于 CycleGAN 本身的,但与大多数关于该主题的现有教程相比,CycleGAN 由于其(相对)复杂的损耗计算和培训程序而被用作示例。
本教程中使用的完整代码可在 github.com/bryanlimy/tf2-cyclegan获得。
摘要
一般来说,TensorFlow 2 中任何现有的自定义训练循环代码都可以通过 6 个步骤转换为与[tf.distribute.Strategy](https://www.tensorflow.org/api_docs/python/tf/distribute/Strategy)一起工作:
- 初始化
tf.distribute.MirroredStrategy - 分发
tf.data.Dataset - 每个副本的损失计算和聚合
- 用
tf.distribute.MirroredStrategy初始化模型、优化器和检查点 - 将计算分配给每个副本
- 聚合返回值和指标
包装要求
本教程需要以下 Python 包:
tqdm==4.61.0
yapf==0.31.0
matplotlib==3.4.2
tensorflow==2.4.1
tensorflow_addons==0.13.0
tensorflow_datasets==4.3.0
此外,还需要 CUDA、cuDNN 和 NCCL。有关 TensorFlow 中 GPU 支持的更多信息,请参见tensorflow.org/install/gpu。如果您使用的是conda虚拟环境,您可以使用以下命令安装上述库:
conda install -c nvidia *cudatoolkit*=11.0 *cudnn* nccl
您可以使用命令nvidia-smi来检查系统中可用的 GPU 数量。例如,我使用的系统配有 8 个 Nvidia GeForce 2080Ti GPUs:

nvidia-smi输出【图片作者】
初始化tf.distribute.MirroredStrategy
我们首先导入所需的包:
然后我们可以初始化tf.distribute.Strategy:
请注意,当devices=None时,TensorFlow 将自动检测所有可用设备。我更喜欢在每次运行开始时手动设置环境变量CUDA_VISIBLE_DEVICES,而不是以编程方式设置devices参数。请注意,nvidia-smi中的 GPU ID 不一定与CUDA_VISIBLE_DEVICES的编号相关。
使用 3 个 GPU(即CUDA_VISIBLE_DEVICES=0,1,2)运行上述代码片段将会打印出以下内容:
Number of devices: 3
数据集设置
在为分布式培训构建tf.data.Dataset时,记住以下几点很重要:
- 使用
tf.data.Dataset.zip压缩前,确保所有数据集具有相同数量的样本。horse2zebra数据集由奇数匹马和斑马图像组成,因此我们必须调用下面的tf.data.Dataset.take。 - 用
GLOBAL_BATCH_SIZE而不是BATCH_SIZE批处理每个数据集。 tf.data.Dataset.cache和tf.data.Dataset.prefetch数据集,以确保在不停止计算的情况下向 GPU 提供足够的数据量。- 可以查看 TensorFlow 的 tf.data API 关于更好性能的优秀教程。我还打算写一篇关于
tf.data的教程,包括高效写作和阅读TFRecords,敬请关注。
在构建数据集之后,我们可以通过简单地调用strategy.experimental_distribute_dataset方法来分发它们。请注意,我们没有分发plot_ds,因为我们将在单个设备上执行推理。
构建生成器和鉴别器
发生器和鉴别器的架构与 Keras 教程中的模型相同。
每样本损失计算
tf.losses和tf.keras.losses中的损失函数默认返回给定批次的平均损失。用分布式训练计算的平均损失应该是相对于全局批量大小,而不是每个复制品。因此,我将定义自己的损失计算,其中函数返回每个样本的损失。例如,如果标签张量具有形状(32, 256, 256, 3),那么返回的平均损失具有形状(32,)。还需要一个额外的步骤来计算整体批次平均损失。
为分布式训练编写损失函数时要记住的事情:
- 验证损失函数的形状。
- 如果使用
[tf.keras.losses](https://www.tensorflow.org/api_docs/python/tf/keras/losses)中的损失函数,损失减少必须是NONE或SUM中的一个。 - 请注意,
tf.losses和tf.keras.losses中的一些函数,如[tf.losses.mean_squared_error](https://www.tensorflow.org/api_docs/python/tf/keras/losses/MSE)和[tf.losses.binary_crossentropy](https://www.tensorflow.org/api_docs/python/tf/keras/losses/binary_crossentropy),会减少输出的最后一个维度,因此我们必须扩展标签和预测的最后一个维度,以确保在每样本减少之前正确的输出形状。请参考下面的功能BCE。
然后,我们可以定义 CycleGAN 中使用的损失函数。我添加了一个助手函数reduce_mean,它返回全局平均损失。您也可以使用[tf.nn.compute_average_loss](https://www.tensorflow.org/api_docs/python/tf/nn/compute_average_loss)来执行相同的操作。
初始化模型、优化器和检查点
我们现在可以在tf.strategy.MirroredStrategy范围内初始化我们的模型、优化器和tf.train.Checkpoint。
下面的内容与分布式培训教程无关,但是为了让生活更简单,我添加了Summary类,用于简单的 TensorBoard 日志记录和一个绘制图像周期的辅助函数。这里我们创建一个目录OUTPUT_DIR='runs/'来存储模型检查点和 TensorBoard 摘要。
定制培训和测试步骤
这里的train_step函数与张量流教程中的函数非常相似,除了它返回一个张量字典。利用分布式训练,每个复制品将单独调用train_step。
train_step的输出示例:
{'loss_G/loss': <tf.Tensor: shape=(), dtype=float32, numpy=0.99999964>, 'loss_G/cycle': <tf.Tensor: shape=(), dtype=float32, numpy=5.1983647>, 'loss_G/identity': <tf.Tensor: shape=(), dtype=float32, numpy=2.5991883>, 'loss_G/total': <tf.Tensor: shape=(), dtype=float32, numpy=8.797552>, 'loss_F/loss': <tf.Tensor: shape=(), dtype=float32, numpy=1.0>, 'loss_F/cycle': <tf.Tensor: shape=(), dtype=float32, numpy=3.6904013>, 'loss_F/identity': <tf.Tensor: shape=(), dtype=float32, numpy=1.8451972>, 'loss_F/total': <tf.Tensor: shape=(), dtype=float32, numpy=6.5355983>, 'loss_X/loss': <tf.Tensor: shape=(), dtype=float32, numpy=0.49949202>, 'loss_Y/loss': <tf.Tensor: shape=(), dtype=float32, numpy=0.49720365>}
然后我们调用strategy.run将计算分布到所有副本上,每个副本计算并从train_step返回相应的结果。由于我们的train_step函数返回一个张量字典,因此,strategy.run的返回值将成为一个张量字典。我添加了一个助手函数reduce_dict,它简单地迭代字典中的每一项,并对每个键的所有值求和。
分发计算时要记住的事情:
- 我们可以在
tf.function内外迭代数据集。 - 由于每个副本的结果都与全局批量大小有关,因此我们用
strategy.reduce计算SUM而不是MEAN。 - 检查从
train_step返回的值(或您计划分配计算的任何函数)。例如,如果您的函数返回一个张量,那么从strategy.run返回的值将是一个张量字典。
3 个 GPU 上strategy.run(train_step, args=(x, y))的输出示例(即CUDA_VISIBLE_DEVICES=0,1,2):
{'loss_G/loss': PerReplica:{
0: <tf.Tensor: shape=(), dtype=float32, numpy=0.33333334>,
1: <tf.Tensor: shape=(), dtype=float32, numpy=0.33333334>,
2: <tf.Tensor: shape=(), dtype=float32, numpy=0.33333334>
}, 'loss_G/cycle': PerReplica:{
0: <tf.Tensor: shape=(), dtype=float32, numpy=1.2934016>,
1: <tf.Tensor: shape=(), dtype=float32, numpy=1.4167924>,
2: <tf.Tensor: shape=(), dtype=float32, numpy=1.2807639>
}, 'loss_G/identity': PerReplica:{
0: <tf.Tensor: shape=(), dtype=float32, numpy=0.6467091>,
1: <tf.Tensor: shape=(), dtype=float32, numpy=0.70838636>,
2: <tf.Tensor: shape=(), dtype=float32, numpy=0.6403792>
}, 'loss_G/total': PerReplica:{
0: <tf.Tensor: shape=(), dtype=float32, numpy=2.2734442>,
1: <tf.Tensor: shape=(), dtype=float32, numpy=2.458512>,
2: <tf.Tensor: shape=(), dtype=float32, numpy=2.2544765>
}, 'loss_F/loss': PerReplica:{
0: <tf.Tensor: shape=(), dtype=float32, numpy=0.33333334>,
1: <tf.Tensor: shape=(), dtype=float32, numpy=0.33333334>,
2: <tf.Tensor: shape=(), dtype=float32, numpy=0.33333334>
}, 'loss_F/cycle': PerReplica:{
0: <tf.Tensor: shape=(), dtype=float32, numpy=0.6893108>,
1: <tf.Tensor: shape=(), dtype=float32, numpy=1.1769139>,
2: <tf.Tensor: shape=(), dtype=float32, numpy=1.5197902>
}, 'loss_F/identity': PerReplica:{
0: <tf.Tensor: shape=(), dtype=float32, numpy=0.34465796>,
1: <tf.Tensor: shape=(), dtype=float32, numpy=0.58845943>,
2: <tf.Tensor: shape=(), dtype=float32, numpy=0.75989777>
}, 'loss_F/total': PerReplica:{
0: <tf.Tensor: shape=(), dtype=float32, numpy=1.3673022>,
1: <tf.Tensor: shape=(), dtype=float32, numpy=2.0987067>,
2: <tf.Tensor: shape=(), dtype=float32, numpy=2.6130214>
}, 'loss_X/loss': PerReplica:{
0: <tf.Tensor: shape=(), dtype=float32, numpy=0.16675813>,
1: <tf.Tensor: shape=(), dtype=float32, numpy=0.16679758>,
2: <tf.Tensor: shape=(), dtype=float32, numpy=0.16684371>
}, 'loss_Y/loss': PerReplica:{
0: <tf.Tensor: shape=(), dtype=float32, numpy=0.16681099>,
1: <tf.Tensor: shape=(), dtype=float32, numpy=0.1668036>,
2: <tf.Tensor: shape=(), dtype=float32, numpy=0.16669472>
}}
reduce_dict输出示例:
{'loss_G/loss': <tf.Tensor: shape=(), dtype=float32, numpy=1.0>, 'loss_G/cycle': <tf.Tensor: shape=(), dtype=float32, numpy=3.990958>, 'loss_G/identity': <tf.Tensor: shape=(), dtype=float32, numpy=1.9954746>, 'loss_G/total': <tf.Tensor: shape=(), dtype=float32, numpy=6.986433>, 'loss_F/loss': <tf.Tensor: shape=(), dtype=float32, numpy=1.0>, 'loss_F/cycle': <tf.Tensor: shape=(), dtype=float32, numpy=3.386015>, 'loss_F/identity': <tf.Tensor: shape=(), dtype=float32, numpy=1.6930151>, 'loss_F/total': <tf.Tensor: shape=(), dtype=float32, numpy=6.07903>, 'loss_X/loss': <tf.Tensor: shape=(), dtype=float32, numpy=0.5003994>, 'loss_Y/loss': <tf.Tensor: shape=(), dtype=float32, numpy=0.5003093>}
测试步骤遵循相同的程序。
训练循环
剩下的工作就是以与任何典型的定制训练循环相同的方式构建训练和测试循环。我们保存一个检查点,每 10 个时期在 TensorBoard 上绘制 5 个样本。请注意,在恢复检查点时,必须在策略范围内调用checkpoint.read。
一个时期后打印语句示例:
Epoch 001/200
Train: 100%|██████████████████████| 178/178 [12:29<00:00, 4.21s/it]
Test: 100%|█████████████████████████| 20/20 [00:24<00:00, 1.22s/it]
MAE(X, F(G(X))): 0.2599 MAE(X, F(X)): 0.2823
MAE(Y, G(F(Y))): 0.2492 MAE(Y, G(Y)): 0.2793
Elapse: 773.84s
1 个 GPU 对比 3 个 GPU 的结果
为了验证我们可以在分布计算后获得类似的结果,我在 1 个 GPU 和 3 个 GPU 上用相同的超参数训练了 2 个模型。一般来说,我们可以用更大的批量来使用更大的学习速率,尽管为了简单起见,我将所有的学习速率都保持在2e-4。平均而言,1 个 GPU 完成一个纪元需要大约 325 秒,而 3 个 GPU 需要大约 167 秒。使用多个 GPU 时,分析输入管道和训练过程以确保适当的加速非常重要,分布式训练的开销可能会超过并行化的优势。
下面的图和图像显示了 200 个时期后发生器和鉴别器的训练统计和结果。

上图:发生器和鉴别器的训练(实线)和测试(虚线)损失|下图:测试集上的循环和同一性平均绝对误差[图片由作者提供]


x → G(x) → F(G(x))样本[图片由作者提供]


y → F(y) → G(F(y))样本[图片由作者提供]
我们有了它,一个关于如何通过tf.distribute.MirroredStrategy将多 GPU 训练与 TensorFlow 2 中的自定义训练循环集成的端到端示例。
由于本教程的博客性质,一些编程风格相当糟糕(例如,大量使用全局变量,训练验证测试分割而不是训练测试分割,等等。).请查看github.com/bryanlimy/tf2-cyclegan获取更完整的示例,包括正确的检查点保存和加载,以及 TensorBoard 集成。
TensorFlow 和 Keras 中有关分布式培训的其他参考资料
- TensorFlow - 使用 TensorFlow 进行分布式训练
- TensorFlow - 带 Keras 的分布式训练
- TensorFlow - 用 tf.distribute.Strategy 定制训练
- Keras - 多 GPU 和分布式培训
参考
[1]朱,严军,等.“利用循环一致对抗网络进行不成对的映象对映象翻译”IEEE 计算机视觉国际会议论文集。2017.
使用电子表格在 fast.ai 中进行多标签分类
原文:https://towardsdatascience.com/multi-label-classification-in-fastai-using-spreadsheets-25ae570c8ff9?source=collection_archive---------17-----------------------
识别单个图像中的多个实体

Philippe Bout 在 Unsplash 上的照片(人/椅子/桌子)
目录
- 简介
- 数据集
- 型号、激活功能和损耗
- 模型评估
- 结论
- 参考文献
介绍
很多时候,我们会遇到这样的图像,其中包含我们想要识别的多个感兴趣的对象。例如,在下图中,我们可以看到我们有一把椅子和一台电视显示器。

图片由 Vinayak 提供
为了解决上述问题,我们需要能够检测给定图像中的多个类别/标签。这就是多标签分类。给定一幅图像,将其分为多个类别/标签/种类。
由于fastai是围绕Pytorch的一个非常方便的包装器,从代码的角度来看,我们需要做的改变很少,但是解决这个问题背后的逻辑会有些不同。我们不能仅仅使用我们常规的softmax激活和cross-entropy loss功能;此外,这里的评估位比单标签分类问题要复杂得多。我们将在下面的章节中详细讨论每一点。让我们首先从数据集开始。
资料组
我们将在这个任务中使用PASCAL_2007数据集。这是一个总共包含 20 个标签的数据集,注意一个图像可以有多个标签!
只需使用 fastai 的untar_data将数据集下载到您的磁盘。它将存储在您的主目录/根目录下一个名为.fastai的特殊目录中
from fastai.vision.all import *
path = untar_data(URLs.PASCAL_2007)
如果我们从训练数据集的标签视点查看统计信息,我们将获得以下内容。

图片由 Vinayak 提供
从上图中我们可以看到,“人”是训练数据集中出现频率很高的类别,其他类别也或多或少地出现了。因此,我们的数据集中存在不平衡。
另一个值得注意的有趣的事情是标签计数的总和与数据点的数量不同。在单标签中,这曾经是正确的,因为每个图像有一个且只有一个标签,但是在多标签分类器的情况下,由于每个数据点不一定只有一个对象,值得注意的是标签可以多于图像的数量。这将导致我们改变构建分类器的策略,而不是单一标签分类。
在这个数据集中,我们以数据帧的形式给出标签,而不是像 Imagenet 那样以文件夹结构给出。因此,我们必须从数据帧中读取每个条目,并定义 getter 方法来检索输入和输出的值。此外,使用数据帧中名为is_valid的列来定义分割。我们将定义一个自定义函数,为数据集中的所有点分别提供分割,即训练和验证集的索引。
在代码中,如下所示
现在,我们可以定义一个数据加载器,一旦我们有了三个主要任务的合适的 getters,即获取因变量和自变量,以及如何将它们分成训练和验证文件。与单标签分类任务不同,对于这个任务,我们将不得不使用MultiCategoryBlock来读取我们的因变量作为一个热码编码向量。其余的负载保持不变。我们可以如下加载数据
- 我们的自变量是一个图像,因此
ImageBlock作为输入,后跟一个MultiCategoryBlock用于一次性编码和加载因变量。 - 上面定义了我们的拆分器,它从我们的数据帧中取出
is_valid列,并基于该布尔变量来分隔训练和验证条目 get_x函数读取列fname中的文件名,并将基本路径附加到要加载的文件中get_y函数从 dataframe 中读取列labels,由于我们的标签是空格分隔的,所以它使用空格分隔符分割标签字符串。item_tfms和batch_tfms:我们使用 fastai 的presizing技巧来避免有损图像裁剪(如填充边框等)。)和标准增强方法,然后使用imagenet_stats进行标准化,因为我们将使用预训练的resnet50进行分类任务。
现在,如果我们看一个批次的例子,我们可以观察到以下情况。看看我们如何在这些例子的图像中有多个标签。

图片由 Vinayak 提供
模型和激活函数
虽然使用 fast.ai API 定义模型和损失非常简单,但我们应该暂停一下,看看损失函数和模型,尤其是详细的损失函数。
我们将对模型头部做一些改动。我们不会像以前一样使用 softmax,而是使用 sigmoid 激活。

图片由 Vinayak 提供
softmax 所做的是将来自最终分类线性图层的逻辑值转换为总和 1。这对于多标签分类来说意味着,当我们遇到具有多个标签的例子时,我们会遭受很高的损失。例如,考虑下面的场景

图片由 Vinayak 提供
我们看到,在这个假设的例子中,数据点实际上属于类别 1 和 4,但我们的 softmax 最多只能将这两个类别的概率分数推至 0.5,而将其余两个类别的概率分数推至 0,但也不会比这更好。这是因为概率总和必须始终为 1。想象一下,如果这是一个三类的例子,那么 softmax 可以做的最好的事情就是将三个概率分数推至 0.33,将剩余的 1 个概率分数推至 0。
现在,让我们看看乙状结肠激活。

图片由 Vinayak 提供
现在,我们看到激活函数并不关心其他标签是什么。与 softmax 不同,它只关注相关标签的 logit。
这些逻辑都是解耦的,它们之间不会像 softmax 那样相互影响。
这就是为什么在上图中,你可以看到类别 1 和 3 的 Sigmoid 激活概率可以攀升到接近 1,而另外两个可以独立地接近 0。
现在,你可以更好地理解为什么我们不能使用softmax activation function而是需要一个单独的sigmoid activation function来解决这个问题。
既然我们已经改变了激活函数,我们应该重新考虑我们对损失函数的选择。
对于单标签分类,我们使用如下定义的交叉熵损失函数

图片由 Vinayak 提供
其中 ti 是真实值,pi 是标签的预测概率
如果我们继续将这个损失函数应用于我们的 sigmoid 激活输出,我们就有麻烦了。我们将不会惩罚任何事实标签为 0 的东西。对于上面的同一个示例,如果我们计算 CE 损失,我们会看到如下图

图片由 Vinayak 提供
另一方面,二元交叉熵定义如下

图片由 Vinayak 提供
其中 ti 是真实值,pi 是标签的预测概率
这真的很有趣。它确保了无论标签是什么(0/1 ),总会有一些损失来惩罚模型的错误预测。永远不会是零。当地面真实值为 1 时,损失为-log(p ),当地面真实值为 0 时,损失为 log(1-p)。这对于模型通过分类器头中的不同神经元针对它们的错误预测单独惩罚模型是非常有用的。
在上图中,我们可以看到,即使地面真实标签为 0,我们也可以获得这些神经元的有限损失值,以及单标签分类中使用的普通交叉熵损失。
对于模型,我们仍然可以继续使用我们的 imagenet 预训练主干,并从迁移学习开始。
为什么即使任务不同,模型也是一样的——单标签而不是多标签分类?
虽然最终我们必须预测每个输出的多个标签,但是我们仍然可以利用预先训练的相同过滤器来识别人、动物、物体等。这是大型 imagenet 数据集的一部分。已经智能地学习了这 1000 个类的预训练主干具有过滤器,可以检测人脸、猫毛、狗尾巴等。PASCAL_2007 数据集中也有类似的类。因此,以此为起点来利用我们已经拥有的资源是有意义的!
因此,我们现在可以定义一个fastai learner来进行培训。内容如下:
learn = cnn_learner(dls, resnet50, metrics=partial(accuracy_multi, thresh=0.5))
在这里,我们更改了指标,使用 accuracy_multi 代替了原来的 accuracy。我们将在模型评估中详细讨论这一点,但除此之外,我们还没有从我们做单标签分类时开始做任何改变,是吗?
事实上,fastai 选择 BCE loss 是因为我们已经在 dls 中指定,我们的因变量是一个多类别块。我们可以明确地指定它,但我们至少需要知道它。
然后,训练和 lr_find 以及其他内容保持不变,如下面的代码片段所示。

图片由 Vinayak 提供
模型评估
这是最重要的部分,因为这与多标签分类有很大不同。
首先,什么是精度 _ 多?
如果是多标签分类,我们的目标是one-hot encoded。同样,我们得到的输出与目标的形状相同,但它们是逻辑的。所以我们对这些应用 sigmoid 激活,并得到概率。
在单标签分类中,我们只比较一个数据点的一个标签,如果它匹配,我们的结果是准确的,否则就不是。但是,对于多标签分类,对于每个数据点,我们预测一个向量,输出也是一个向量。所以我们需要比较这些向量,而不是每个数据点的单个标量。因为我们必须比较多个值,然后取这些比较的平均值;因此得名accuracy_multi。下表将对此进行更好的总结。

图片由 Vinayak 提供
预测- >出席/缺席概率
正如我们在上面的例子中看到的,我们假设预测是一个二进制字段,但是神经网络本身并不能为每个类/标签提供一个离散值。它给了我们一个浮点数组,我们需要将它转换成一个概率分布,然后转换成一个离散值,代表一个类/标签的存在/不存在。
第一部分很简单,我们已经介绍过,即从神经网络输出的逻辑值到概率分布,只涉及对各个类别的概率值应用 sigmoid 激活。
对于下一部分,即将概率转换成离散值;我们必须设定阈值。这意味着我们选择一个概率值,并以此为支点,将连续概率转换成离散分布。下面的例子将更好地解释这一现象。

图片由 Vinayak 提供
如图所示,我们首先获取网络输出,并应用 sigmoid 激活,从而获得概率。接下来,我们任意挑选 5 个阈值[0.1, 0.3, 0.5, 0.7, 0.9]。现在,我们要做的是将这些概率与阈值进行比较。当probability > threshold时,我们将其标记为真,否则标记为假。然后取预测值的平均值,就可以得到该数据点的精确度。
在单标签分类中,单个数据点的精度可以是 0 或 1,而在多标签分类中,精度可以是 0 和 1 之间的连续值,包括 0 和 1。
现在,既然我们在讨论阈值,那么在评估过程中找出最佳阈值就变得很重要。此外,目前我们对所有类别/标签使用相同的阈值。我们可以分别调整每个类的阈值,以得出每个类的最佳分数,然后使用这些阈值来获得整个数据集的多精度。让我们看看如何做到这一点。
全局阈值
我们上面所做的在某种意义上可以称为Global Thresholding,我们对所有的类使用一个阈值,比较每个数据点的精确度,得出一个比较精确度和阈值的图,并选择一个给出最好精确度的。
下面是我们如何用代码实现它

图片由 Vinayak 提供
上述函数为我们提供了最佳精度点和发生该点的阈值,该阈值可以简单地保存为模型的人工制品,并且在推断过程中,当我们想要获得单个标签的预测时,我们可以将它们的概率与该阈值进行比较,并获得离散结果来表示某个类别的存在/不存在。
然而,我们可以做得更好。
类别/标签级别阈值
实际上,准确性并不总是最好的评估标准。例如,假设世界上只有 1%的人是富人,预测每个人都是穷人会让你 99%准确,但这真的好吗?
不是吧?当您创建一个分类器时,如果存在严重的类不平衡,您会希望您的性能在所有类中都很好,而不仅仅是在数据集中占主导地位的一两个类。准确性不能告诉我们这样的信息。在这里,有其他度量,如Precision、Recall/TPR、FPR、f1-score等。变得非常有用。这篇文章并不打算深入研究这些指标,但让我们粗略地看一下,我将在最后提供良好的资源来深入研究每一个指标。

图片由 Vinayak 提供(混淆矩阵)
Precision:这个量基本上告诉你所有那些被预测为某种类型的例子,实际上有多少是那种类型的。如果我们看看上面的混淆矩阵,我们可以将精度定义为

图片由 Vinayak 提供
Recall/TPR:这个数量基本上指定分类器正确识别了多少个特定类别的样本。也简称为True Positive Rate或 TPR。它是由

图片由 Vinayak 提供
- 当我们定义一个分类器时,我们希望这两个值尽可能的高,最好是 1,但是它们之间有一种反比关系。因此,我们定义了一个度量标准,它找到了两者的最佳平衡点。这是 f1 分数,原则上是召回率和精确度之间的调和平均值,定义如下

图片由 Vinayak 提供
FPR:假阳性率是被错误分类的阴性样本的数量。它是由

图片由 Vinayak 提供
对分类器的评估可以从几个方面进行。对一些人来说,accuracy仍然是评估的黄金标准;对于其他一些人来说,f1-score可能是确保分类器跨多个类别性能的重要数字。在许多情况下,ROC或接收器操作特性图可用于计算分类器性能。我们将使用所有三种技术评估我们的模型,以获得给定特定标准的最佳性能模型。
下面是我们用来训练模型的PASCAL_2007数据集的bus类别的 ROC 图

图片由 Vinayak 提供
X 轴表示 FPR,Y 轴表示召回/TPR。我们需要确定在不增加 FPR 的情况下 TPR 尽可能高的点。我们可以通过找到最接近(0, 1)点的点来解决这个问题,即 FPR 为 0 而 TPR 为 1。这显示在带有红点的曲线中。对于一个完美的分类器,我们应该有一个单位矩形类型的图,但实际上在大多数情况下,这两类的分布从来没有完全区分。
完成这项工作的完整代码会变得非常大,但可以在 GitHub 上找到,我已经在参考资料部分附上了它。我将勾画出伪代码做这个局部阈值,然后最终的预测汇总。
1.从 fastai 学习者那里获得预测和目标
2.分离出每个标签的预测和目标。fastai 预测的形状为“N_EXAMPLES x N_CLASSES ”,因此将它们分成 N _ CLASSES 个长度为“N_EXAMPLES”的向量。对目标也进行类似的操作。
3.选择一系列阈值,并评估每个类别/标签的所有示例的指标精度、召回率、fpr、f1-得分,并构建 ROC-AUC 曲线。
4.在您改变阈值的所有点中,选择最接近的点、最佳准确度和最佳 f1 得分点。记录您获得这些指标最佳值的每个点的阈值。
5.使用从 4 中获得的每个类别的记录阈值,将概率分布转换为离散分布,并找到多标签分类器的总体精度。
如果我们要进行比较,对于每个类别,使用 bestAccuracy 策略的单个类别/标签的阈值调整使我们在准确性上比全局阈值略有提高,这也使我们在默认阈值 0.5 上有所提高,默认阈值 0.5 通常用于所有分类问题。

图片由 Vinayak 提供
结论
- 多标签分类器(MLC)可以用多个类别/标签标记给定的数据点。
- MLC 中使用的激活是
sigmoid而不是softmax。 - 用于 MLC 的损失函数是
BinaryCrossEntropy而不是CrossEntropy。 - 一个好的阈值可以在获得更好/更差的 MLC 精度方面产生显著的差异。
- 在模型评估过程中,准确性可能不是黄金标准,回忆/精度/f1 分数等指标将非常有用,尤其是在训练模型时存在类别不平衡的情况下。
我希望你喜欢阅读这篇博文!我很高兴在推特上与你联系。如果你有任何意见/建议/想法,欢迎在下面评论或在 Twitter 上联系我。
如果你喜欢你所读的,请随意查看我在这里的其他帖子。
参考
- wandb fastbook sessions 链接
- 在帖子中创建的应用程序的 Github 代码
- 对评估指标的良好解释
使用 PyTorch + HuggingFace 的 Transformers 和 W&B 进行跟踪的多标签情感分类
原文:https://towardsdatascience.com/multi-label-emotion-classification-with-pytorch-huggingfaces-transformers-and-w-b-for-tracking-a060d817923?source=collection_archive---------6-----------------------
实践教程
把拥抱脸和 W&B 的力量加入到你的 PyTorch 代码中!

腾雅特在 Unsplash 上的照片
介绍
自然语言处理(NLP)正在以前所未有的速度在工业 4.0 中大步前进,并以一种闪烁的速度前进。它被广泛用于将非结构化文本处理成各种商业案例的有意义的知识。它已经被用于多种任务,如分类、主题建模、文本生成、问答、推荐等。一种很少被提及的特殊分类是多标签分类。
多标签文本分类涉及预测给定文本的多个可能标签,不像多类分类,其仅具有来自“N”个可能类的单个输出,其中 N > 2。多标签文本分类是许多 ML 库中很少涉及的主题,对于某些任务,如日志度量等,您需要自己编写大部分代码。您还需要了解某些变通方法,以获得一些有意义的结果。

二元对多类对多标签分类。作者图片
我想做这个项目的一个关键原因是让我自己熟悉Weights and bias(W&B)库,这个库和 HuggingFace 库一起在我的技术 Twitter 上引起了热议。在 PyTorch 中使用多标签分类及其与 W & B 的集成方面,我没有找到很多好的参考资料。因此,我想整理一篇文章以方便参考。不要担心,我会在我们进行的过程中讨论什么是 W & B,你会很幸运地知道,有了这个令人敬畏的图书馆,我们的生活变得多么方便!在本文中,我们将讨论以下内容:
- 与…一起工作🤗用于加载数据集的数据集库
- 使用构建经典 PyTorch 训练器🤗变形金刚图书馆的 SqueezeBERT 模型
- 用 PyTorch 集成 W&B 库
- 用🧹w & b 扫描执行超参数扫描
所以,不要浪费任何时间,让我们开始吧!
数据集使用🤗数据集
HuggingFace 的网站有一个几乎所有类型的 NLP 任务的巨大数据集集合!它还允许用户通过使用 Streamlit 制作的内置数据集可视化工具来可视化数据集的某些方面。在浏览数据集列表时,一个特殊的多标签分类引起了我的注意:https://huggingface.co/datasets/go_emotions。其摘要指出:
GoEmotions 数据集包含 58k 条精心策划的 Reddit 评论,标记为 27 种情感类别或中性。包括原始数据以及具有预定义训练/val/测试分割的数据集的较小简化版本。
在他们的可视化工具上浏览了这个数据集中的几个例子后,我意识到这是一个极其重要的数据集,因为很难找到超过 5-6 种情绪的情绪分类器数据集。但是在这里,我们有 27 种情绪被分配,有非常罕见和接近的情绪,如失望、不赞成、悲伤、悔恨、悲伤等。在典型的数据集中,检测如此接近的情绪通常是困难的。这让我明白这是一个非常好的数据集,可以在许多涉及文本分析的应用程序中扩展使用。

数据集中的情感标签列表。作者图片
权重和偏差介绍(W&B)
日志度量一直是许多数据科学家和 AI/ML 爱好者头疼的问题。编写计算指标、存储指标和绘制图表的代码是一项非常普通的任务。此外,当您在一个专业的环境中工作时,您不希望您的客户或团队领导浏览整个代码来找到那些图。因此,您必须截取屏幕截图,然后准备一份单独的报告或演示文稿来分享您的结果。想象一下你花在所有这些步骤上的时间。
值得庆幸的是,有人听到了我们的抱怨和咆哮,并决定建立一个库,使所有这些自动化,并在组织我们的代码、数据、指标和运行方面走得更远,比我们组织我们的卧室更好👀。欢迎,重量和偏见(W&B)🙌。
W&B 允许我们使用几行代码实时监控我们的数据、代码、超参数、指标和培训。它提供了一个直观的仪表板,并与 PyTorch、Tensorflow、fast.ai、Keras、JAX 等流行的库集成。它允许我们以更有组织的方式找到最好的模型,而不是使用打印语句和手工记录。首先,你需要在他们的网站上注册一个账户,然后你的项目跟踪就可以在你的账户上使用了!
好了,理论讲够了,我们来编码吧!

这是你对吗?来源:男高音
现在,让我们用编码来弄脏我们的手。我使用 PyTorch 是因为它简单易懂,并且能够从头开始构建东西,这让我感觉更舒服。我已经在 Google Colab 上编码了整个东西,所以我只需要做一些额外的安装。但是,如果您在本地运行它,请注意下面的导入,您的系统中可能没有安装这些导入。然而,我强烈建议你使用 Google Colab,因为它允许你在网络上进行深度学习实验,而无需在笔记本电脑上加载任何负载。
从这一点开始,这篇文章可能会有点长,因为我将同时讨论 W & B 和多标签分类。所以,在我们踏上这个奇妙的旅程时,请耐心等待!🙈
全部代码在我的资源库 这里 都有。
1.安装依赖项⏬
我们可以使用以下命令在 Google Colab 中进行必要的安装:
!pip3 install datasets transformers -q
!pip3 install wandb --upgrade
现在,我们可以使用以下方式登录我们的 wandb 帐户:
import wandb
wandb.login()
系统会提示您单击一个链接,复制您的授权令牌并将其粘贴到文本框中,以将您的项目与您的 WandB 帐户连接起来。
2.必要的进口🚀
3.播种一切可复制性!🌱
播种是一个非常重要的概念,我经常在许多中等代码教程中发现缺失。我发现帮助读者重现和匹配结果是一个重要的实践。所以,让我们播种一切吧!
4.定义超参数扫描🧹的扫描配置
为了执行超参数调整,我们从定义的列表中尝试不同的配置,然后尝试在我们的主训练循环中运行它们,并手动记录每个组合的结果,以找到最佳的超参数集。告别这种中世纪(就 AI 进步而言)的做法,用超参数横扫🧹做同样的事情!我们将定义一组我们想要使用的超参数,并设置它们可能的值。我们还定义了我们想要最大化或最小化的指标。
最后,我们定义了一个扫描 ID,它将使用不同的超参数组合对训练函数进行扫描。
5.从加载数据集🤗数据集和一次性编码

train.head()输出。作者图片
我们可以看到数据集有与每个样本相关联的文本、id 和标签。然而,为了将数据输入到我们的模型中,我们需要执行一次热编码,它有一个非常常见的代码:
6.定义 PyTorch 数据集
我们没有在类中定义记号赋予器,而是将其作为参数传递,以便于使用不同的记号赋予器进行实验。我们按照定义 PyTorch 数据集类的标准方式来定义 GoEmotionsDataset 类,以指示如何加载和处理数据集。
在我们的例子中,我们获取输入文本,将其标记化,然后返回通过标记化获得的标记 id 和注意掩码。令牌 id 只是模型的输入序列的数字表示,而注意掩码用于指示模型应该注意哪些段。
7.使用神经网络定义分类器模型。组件
这里,我们将 dropout probability (do_prob)作为一个超参数,将由 W&B 处理。其余的代码非常标准:用所需的架构初始化模型,并定义向前传递。
8.定义返回数据集、数据加载器和模型的函数
对于我们的超参数跟踪和 W&B 的集成,重要的是我们大量使用函数编程来确保我们成功地跨函数传递所有对象,并且它们都可以从我们稍后将定义的主训练器函数中调用。
在这里,我们首先定义我们的 SqueezeBERT 记号化器(与 bert-uncased 相比非常快,性能几乎相似),然后定义 3 个函数来返回数据集、数据加载器和我们的模型。所有这些都遵循标准 PyTorch 管道。同样,将它们定义为函数的主要动机是使我们能够方便地传递我们的超参数,而不是将它们硬编码在模型定义本身中。
9.定义要记录的优化程序、调度程序、损失函数和度量
我从 Abhishek Thakur 的 知识库中获取了优化器和调度器代码,用于使用 BERT 进行情感分类。
优化器功能:
调度程序功能:
损失函数或准则:
对于损失函数,我们选择了具有 Logits 损失的二元交叉熵(BCE)。BCE 将每个预测概率与实际类输出进行比较,实际类输出可以是 0 或 1 。然后,它根据与期望值的距离计算惩罚概率的分数。这意味着离实际值有多近或多远。当与 Sigmoid 层结合时,会在 PyTorch 中产生 BCEWithLogitsLoss()。也可以使用 MultiLabelSoftMarginLoss()来解决多标签问题。

具有 Logits 损失函数的 BCE
要记录的度量:
[ROC-AUC](https://www.scikit-yb.org/en/latest/api/classifier/rocauc.html#:~:text=A%20ROCAUC%20(Receiver%20Operating%20Characteristic,the%20classifier's%20sensitivity%20and%20specificity.) 评分是衡量我们多类分类性能的好方法。但是,通过对每个目标分别应用它,可以将其推广到多标签场景。然而,这对于我们的大脑来说太多了,因此,我们可以简单地使用微 AUC。PyTorch 中用于这种多标签分类的一个巧妙技巧是使用 ravel()函数展开目标和标签,然后我们应用 micro AUC 函数。
10.定义培训和验证步骤功能
我再次从 Abhishek Thakur 的知识库中提取了这些代码片段,并根据我的问题陈述对它们进行了修改:
11.定义培训师职能⭐️
为了将所有内容整合在一起,我们现在将定义执行培训的培训师职能。在我们的分类器模型中,我们将使用 SqueezeBERT 作为 BERT 层。这个教练功能也是 W&B 魔法发生的地方!
你在这里观察到一些新的东西吗?是的,旺德贝线!您可以看到,我们在开始时使用了上下文管理器和 with wandb.init() 语句来初始化运行。训练功能的每次执行是一次运行。我们将扫描配置传递给训练器函数,该函数用于设置不同的超参数,如 batch_size、dropout、epochs 等。另一个基于 wandb 的行是 wandb.watch() ,它用于观察模型的梯度,以及它们如何随着训练而演变。这有助于尽可能提供更多的可解释性。
wandb 的下一次出现在 wandb.log() 中,它帮助我们记录相关参数,我们希望看到这些参数随着训练的进行而变化。在我们的案例中,我们记录了时期、训练和验证损失以及 AUC 分数。最后,为了开始不同超参数组合的扫描,我们将调用wandb.agent(),在这里我们传递配置和训练器函数的 sweep_id。我们还传递 count=6,以限制我们想要执行的运行次数。这仅在进行随机搜索而非网格搜索时使用。一旦训练开始,你可以在 W & B 宏伟的仪表盘中实时监控训练!🤌🏻
跳上你的 W&B 仪表板,看看神奇的展开!

W&B 仪表板的概述。作者图片
在这里,您可以看到扫掠中的每个管路都有一个唯一的名称,您可以轻松地对其进行重命名或删除。您还会注意到参数对于我们正在监控的指标的重要性(此处为 AUC 得分)。这是一个极其重要的特征,因为它告诉我们哪些超参数对我们的分数影响最大。在我们的例子中,我们可以看到辍学对 AUC 分数有很大的影响。当我们想要设置最佳超参数设置时,这是一条需要注意的关键信息。当我们向下滚动时,我们看到了扫描图,它显示了每个超参数组合如何产生特定的 AUC 分数。这是 W&B 在 Sweeps 中提供的我特别喜欢的图表类型!

扫描图表。太酷了!作者图片
这里,图表清楚地向我显示了产生最佳 AUC 分数的组合(黄色图)。如果你渴望用老派的方式查看数据,你还可以得到一个不错的表格格式!

扫地台。作者图片
除此之外,我们还获得了我们记录的度量的图表,并获得了所有不同运行的比较图。您甚至可以关闭某些运行的可视化,以比较其余的期望运行!

其他度量图。作者图片
**如果您已经被 4-5 行 wandb 所能做的事情弄得不知所措,请耐心等待,因为我将向您展示更多内容!还记得 **wandb.watch(型号)那条线吗?你认为我们为什么要观察这个模型?嗯,你可以去每次跑步,看看在训练中模型的梯度是什么样的!这对模型可解释性和可解释性研究人员来说极其重要!

模型梯度演化。作者图片
但就这样了吗?再说一次,不!您甚至可以使用 W&B 观察系统指标,它显示各种图表,如内存使用情况、CPU/GPU 使用情况、温度、磁盘使用情况等。

系统指标图。作者图片
如果你认为这些是你能绘制的唯一的库驱动图,你就错了。您可以混合匹配您记录的任何参数,然后轻松地绘制它们,以查看它们的比较。您可以根据需要灵活地制作图表,甚至使用 W&B 生成报告!然而,与团队共享它需要团队的 W & B。
它还记录它在训练脚本中看到的所有输出,以及通过版本控制记录代码和数据集。不仅如此,它还根据训练脚本自己生成一个 requirements.txt 文件。所以,现在你甚至不需要 pip 冻结,你可以直接下载这个文件,并与他人分享!

作者在黑白图像中自动生成的文件
wandb 库不断增加越来越多的特性,如表格等。因为这篇文章已经太长了,所以我把它留到以后讨论。因此,长话短说,训练信息现在在 W&B 的仪表板上有一个漂亮的庇护所,这将使我们能够随时复制实验。这样,我们现在可以使用最佳超参数组合来训练模型,然后使用派生的模型进行推理。从我的训练中,我能够得到一个非常好的模型,AUC 分数为 0.9459。使用该模型测试非常稳健的 AUC 分数,其中 27/28 个类别的标签式 AUC 分数大于 0.8!对于如此复杂的数据集来说,这的确是一个非常令人印象深刻的分类器。

标签式 AUC-ROC 评分。作者图片
结论

来源:男高音
唷,我们终于可以结束了!本教程展示了如何在多标签数据集上应用 SqueezeBERT,该数据集包含超过 50K 条评论和 28 个类。使用 PyTorch 和变形金刚库。我们还能够通过 wandb 库实时监控我们的培训。在公开您的项目后,您还可以通过共享“公开”项目链接与他人共享您的整个仪表板!这是我的仪表盘链接。在未来,我们可以使用 Streamlit 部署这个多标签情感分类器,并在此基础上构建一个 web 服务!
的全部代码可以在我的资源库 这里 中找到。
感谢您的耐心让我们走到这一步!如果你想了解更多关于 wandb 库和 HuggingFace 库的信息,我已经把它们链接到下面了,看看吧!
**https://wandb.ai https://docs.wandb.com https://huggingface.co https://www.youtube.com/c/AbhishekThakurAbhi
我看得出你是个有文化的人,能走到这一步!我很高兴你能走到这一步!别忘了关注我更多这样的 ML 相关文章!查看我的网站,关注我的 LinkedIn 、 GitHub 和 Instagram !✨**
AWS Sagemaker | MLOPS | Pytorch 中的多模型部署
原文:https://towardsdatascience.com/multi-model-deployment-in-aws-sagemaker-mlops-pytorch-baf0fa08be42?source=collection_archive---------12-----------------------
如果你曾经部署过一个计算繁重的人工智能模型,你可能知道它的部署价格。不一定是 AI 模型;它可以是在生产中全天候运行的任何模型。

solébikes 在 Unsplash 上拍摄的照片
我生产的 pytorch 型号很少,不管我使用什么平台,随着时间的推移,它都非常昂贵。所以我决定降低模型部署的成本。我发现 AWS sagemaker 有一个多模型部署选项。然而,这些文档并不是非常友好,而且经常令人困惑。所以我决定在这篇文章中多解释一些。
如果您正在通读这篇文章,我假设您知道 AWS sagemaker,并且能够在平台中部署模型。如果没有,请参考这篇文章来详细了解一下。
设置时需要的东西
- 具有 sagemaker 访问权限的 AWS 帐户
- ECR 码头集装箱入口
- Sagemaker 笔记本或本地 jupyter 笔记本环境
多模型如何工作?
当您使用 sagemaker 部署单个模型时,它将启动一个实例,启动一个 docker 容器,从 S3 桶中加载模型的 tar 文件,最后从创建的代码中创建一个端点。
- 当一个模型被调用时,它将请求路由到部署的端点后面的实例。
- 每个多模型实例包含处理所有请求的容器。传入的请求被路由到容器。
- 当请求到达容器时,它将模型下载到该实例上的容器内存中。
- 如果内存不足以容纳多个模型,它将卸载第一个或未使用的模型。
对于每个模型类型和库,多模型的底层 docker 容器和模型是不同的。关键是当有多个模型时,它会根据端点请求将模型加载到容器内存中。
不同类型的多模型部署:
- 使用预制集装箱
- 自定义集装箱相似输入
- 带有其他类型输入的定制容器——这通常不包含在 AWS 文档中。
使用预制容器—不复杂
只有当您有一个已经由 AWS 预构建的容器时,这种类型的方法才有帮助。这个链接包含可以在 sagemaker 中使用的容器列表。AWS 基于 xgboost 模型打造了一款笔记本。此链接详细介绍了所使用的代码。
- 从 sagemaker 中检索现有容器
- 准备和培训每个模型

3.使用 sagemaker 内置调用创建多数据模型端点。

点 S3 数据和训练好的模型

部署多个模型
4.最后,下面是从 API 调用中调用模型的代码。

优点/缺点:
- 除了数据准备和模型训练,多模型部署更加简单。
- 如果你仔细看看代码,你会发现我们是在用不同的名字部署相同类型的模型。这在大多数情况下是没有用的。
- 相似类型的输入被传递给不同的模型进行预测。如果你想节约成本,一般情况下是不会的。
- 容器已经预制好了。所以我们不能对它做任何定制。
自定义容器类似输入处理
上一节提到的方法对我们大多数人都不起作用。Sagemaker 提供了一个定制容器的选项。这是你可以定制的容器列表。在这种方法中,您可以部署多个模型,并根据需要定制容器。
然而,它不像前一个那样直截了当。它需要一些码头集装箱的工作。在幕后,sagemaker 使用开源工具多模型服务器。让我带你经历每一步。
作为第一步,我们需要有一个创建定制容器的模型和理由。很多时候,容器中构建的包不够用,会和其他包冲突。
- 创建一个包含所需包的 docker 文件

2.如果你注意到,有一个 python 入口点。这个入口点启动多模型服务器流程,该流程被包装在 sagemaker 中。

3.下一步,将多个模型上传到 S3 存储桶中,并作为常规流程创建端点。

如果您看到这个代码,您可能已经注意到模型是用 mxnet 创建的。这并不是非常流行,而且需要对 python 文件进行不同的更改。
从我的经验来看,我尝试过这种方法,部署模型有点乱。
优点/缺点:
- 上面提到的例子是在 mxnet 上。PyTorch/TensorFlow 没有很好的例子。
- 它仍然可以处理相似类型的输入。例如,您不能为另一个模型传递文本和图像。
- 当我们试图部署多个模型时,这个过程有点混乱。
具有不同输入处理的自定义容器
由于提到的两个选项都有一些缺点,我决定为我的项目做一点不同的选择。我不得不部署微调的 GPT-2 和巴特-大-cnn 模型。对于这个例子,我将使用预先训练的模型作为例子。
这两个预先训练的模型都可以作为拥抱脸模型管道的一部分。下面是我为简化和部署多个模型所遵循的步骤。
- 在这里,我们正在更新 sagemaker 的预构建容器,并根据我们的需要定制容器。

在第一行中,我们从 sagemaker 导入现有的容器。然后我们向它添加所需的包并对其进行定制。最后,我们将向 docker 容器添加一个入口点。
2.将所需的模型上传到 S3 存储桶。在这里,我们必须将这两个模型合并到 model.tar.gz 文件中。

3.现在,我们需要 app.py,来自 docker 容器的主入口点。它基本上是一个 flask API。

如果你看到上面的截图,我们使用两种不同的拥抱面部管道,创建不同的模型。为了调用模型,我们需要指定模型类型。在函数 run_model 中,我们会提到使用哪个模型。这里流程提到了模型类型。

4.其余的代码将不会花哨或任何不同。我们必须在大型服务器上部署这个模型。我使用 ml.c5.2xlarge 在一台服务器上部署这两种模型。当我们调用模型时,我们需要指定模型类型和输入。

准备端点的输入

汇总模型的输出
从上面的截图可以看出,我们可以通过在端点中传递模型类型(流程)来调用不同的模型。
优点/缺点:
- 我们可以为不同的模型传递不同类型的输入。输入不再限制我们。
- 模型操作过程是干净的,可以很容易地开发和维护。
- 部署这两种型号可以节省大量资金。
获取代码
请订阅我的 简讯 获取我的文章和其他更新的免费工作代码。
通过 Heroku 和 HTML 实现“多页”应用
原文:https://towardsdatascience.com/multi-page-apps-done-right-via-heroku-4537cd196f2?source=collection_archive---------24-----------------------

多梅尼科·洛亚在 Unsplash 上拍摄的照片
比方说,你想建立一个有多个不同页面的 Heroku web 应用程序——也许你有一些不同的可视化效果,你想让人们能够与之交互,但这并不完全适合一个页面。但是,当你在 Dash 或其他软件中构建一个普通的多页面应用程序时,你可能会发现你使用了比你真正需要的更多的内存,并且整个界面可能没有你想要的那么漂亮。
所以这篇文章会帮你解决两个相关的问题。首先,Heroku 自由层帐户在内存和内存块大小上有一些非常实际的限制,这可能会阻止你的应用程序一次做太多事情。第二,在 Dash 或 Flask 中使用 Python 代码设计应用程序可能有点痛苦。毕竟,这些工具必须同时完成应用程序和的实际工作。他们倾向于关注第一项任务,而让第二项任务变得更加棘手或混乱。
我将带大家了解的解决方案是托管多个独立的 Heroku 应用程序(不要担心,这不会让你付出任何额外的代价),并使用 HTML 在几个相连的网页上访问它们。这使我们能够削减内存和 slug 大小的要求,因为我们可以在我们不同的应用程序之间分割这些。这也意味着我们可以使用 HTML 和 CSS 的所有工具来流畅轻松地设计我们的应用程序。要查看这个解决方案的运行情况,您可以在philosophydata.com查看我的实现。
要跟进,你需要(1)一个功能正常的 Heroku 应用,(2)愿意学习一些超级基础的 HTML,(3)一个 Github 账户。
这个过程有三个核心步骤。创建你的网站。在您的网站中显示您的应用程序
3。托管你的网站
所以事不宜迟,我们开始吧!
1。创建您的网站
这可能是最恐怖的一步。你刚刚建立了一个完整的应用程序,你真的需要建立一个网站吗?你可能甚至不知道任何 HTML 或 CSS——这将是一场噩梦!
别害怕,我的朋友。这会花费你更多的时间,但这比尝试在 Dash 中设计你的应用要容易得多,它给你一个学习新技能的机会,并且它会给你带来更多完美的结果。
这样做的关键是找到一个你喜欢的现有 HTML 模板,并简单地根据你的需要修改它。你可以在 templated.co找到一个很棒的模板库。

这可能是你的应用程序
一旦你找到一个你喜欢的,只需下载带有模板的文件夹。如果你打开它,你会发现一些 HTML 页面,一个保存相关 CSS 和 Javascript 的 assets 文件夹(所有东西都已经为移动设备进行了样式化和优化,没有必要强调),以及一个保存图片等的文件夹。
此时,你只需要在空白处填入你喜欢的信息。打开 index.html 文件,将标题更改为您喜欢的标题,并给标签添加新标签。

您的可能看起来略有不同,但这些是您想要更改的部分
好了,现在你已经修改了标题和标签。要更改登录页面图像,您需要进入 style.css 文件(有时也称为 main.css)并找到它引用当前横幅图像的位置。用你选择的图像替换它,你就可以开始了。

以下是 5 分钟修改代码中一些单词的结果:

像冰一样凉。哇!
你可能想用更具描述性的标题重新标记一些 html 文件,一般来说只是浏览和添加新的页面,弄乱图像,诸如此类的事情。要在进行过程中查看更改,请在浏览器中打开 HTML 文件并刷新页面。有很多尝试和错误,但设计是有趣的!
我把实验留给你,但是如果你遇到任何困难,请随时联系我。现在让我们来看看好东西:在网页中设置你的应用程序。
2.通过 HTML 显示您的应用
如果你已经在 Heroku 上托管了你的应用,那么你也有了一个链接。从这里开始,通过 HTML 将应用程序的结果显示为页面中的页面是一件简单的事情。
以下是 HTML 的外观:
<iframe src=”YOUR HEROKU URL", width=100%, height=500></iframe>
你当然可以根据自己的需要调整宽度和高度。点击此处深入了解 iframe。
结果将是这样的:

HTML & CSS 设置标签和标题,但是搜索栏和按钮是 Heroku 应用程序
请注意,Heroku 应用程序本身根本不需要任何样式。但是它看起来仍然非常干净和清晰,因为 CSS,一种为设计风格而设计的语言*,正在做所有的工作。*
此时另一个很酷的技巧是多次显示同一个应用程序,按列或行排列。这意味着一个相对简单的应用程序可以在一个页面上多次使用,从而可以对输出进行比较。
这里有一个例子。首先是 HTML:
# adjusting the width makes these display as three columns, with some space between them<iframe src=”https://w2v-data.herokuapp.com/", width=300, height=450, scrolling=’no’></iframe><iframe src=”https://w2v-data.herokuapp.com/", width=300, height=450, scrolling=’no’></iframe><iframe src=”https://w2v-data.herokuapp.com/", width=300, height=450, scrolling=’no’></iframe>
然后结果是:

该应用程序返回指定作者和单词的单词向量相似性结果;有三个栏目,你可以比较三个不同的作者。
现在您的工具箱中已经有了这个工具,您可以在任意多个选项卡上使用它。这不应该占用你的应用程序的内存,因为每个应用程序都托管在一个单独的 dyno 上,而一个常规的多页面应用程序必须在一个 dyno 上保存所有的应用程序。我的实现能够托管 4 个不同的应用程序,有时在一个网页上可以托管三倍,同时永远不会突破 Heroku 50%的内存限制。使用通常的多页面方法,我很难加载多个应用程序。
现在我们有了一个漂亮的网站。让我们看看如何让全世界都知道这个。
3.托管您的网站
有许多托管服务,但有一个简单的是 Github Pages。每个 Github 帐户都可以免费托管一个网页。
要设置它,首先创建一个名为
git clone [https://github.com/*username*/*username*.github.io](https://github.com/username/username.github.io)
现在你有你的网站回购,你可以复制所有你一直在工作的文件,添加它们,并提交更改。
Github 会看到你创建了一个格式为
一旦你建立了回购,你基本上就完成了!干得好!如果你想添加最后一层润色,你可以为你的站点设置一个自定义域名,这样 URL 就不会指向 github.io 名称。这很容易做到,一旦你买了你想要的域名,你就可以按照这里的步骤来设置它。
现在你有了一个漂亮的多页面应用程序,具有出色的造型和低内存使用。精彩!
让我知道这是否有帮助,或者如果你遇到任何困难,我很乐意帮忙。关于我自己的 Github 网站是如何建立的,查看这里的回购。
使用机器学习和自然语言处理的多页文档分类
原文:https://towardsdatascience.com/multi-page-document-classification-using-machine-learning-and-nlp-ba6151405c03?source=collection_archive---------0-----------------------
一种对具有不同变化形状、文本和页面大小的文档进行分类的方法。

来源:https://unsplash.com/photos/5cFwQ-WMcJU
本文描述了一种新颖的多页文档分类解决方案,它利用高级机器学习和文本分析来解决抵押贷款行业的一个主要挑战。
摘要
即使在今天的技术时代,大部分业务都是使用文档完成的,涉及的文书工作数量会因行业而异。这些行业中的许多需要通过扫描文档图像 ( 通常包含不可选择的文本)来获得关键索引字段的信息,以操作他们的日常任务。
为了实现这一点,第一个主要任务是索引不同类型的文档,这有助于从各种复杂的文档中提取信息和元数据。这篇博文将展示如何利用高级机器学习和 NLP 技术来解决这个难题的主要部分,正式名称为文档分类。
介绍
在抵押贷款行业,不同的公司执行数千人的抵押贷款审核。
每个单独的审计都是针对各种文件进行的,这些文件作为一个捆绑包提交,称为贷款包。一个包是扫描页面的组合,可以从**(100–400 ~)页变化。包内有多个子组件,可能由(1–30 ~)页组成。这些子组件被称为文档或文档类别**。下表直观地说明了这一点。

第二行代表一个包,数字显示该包中存在的有序页面。第三行显示包中不同种类文档的出现。
背景和问题陈述
传统上,在评估贷款审计时,文档分类是人工工作的主要部分之一**。**抵押贷款公司大多将这项工作外包给第三方 BPO 公司,这些公司通过使用手动或部分自动化的分类技术(即规则引擎、模板匹配)来执行这项任务。当前实现所面临的根本问题是业务流程外包( BPO )人员必须手动查找和分类包中存在的文档。
尽管很少有第三方公司使用关键字搜索、正则表达式等来实现某种程度的自动化。这种解决方案的准确性和鲁棒性是有问题的,并且它们的人工工作量减少仍然不令人满意。 关键字搜索和正则表达式 意味着这些解决方案需要考虑所呈现的每个新文档或文档变体,并且还需要为此添加规则。这本身就变成了手工操作,只能实现部分自动化。系统仍有可能将文档类标识为“ Doc A ”,但实际上它是“ Doc B ”,因为两者中存在共同的规则。此外,身份识别也没有确定性。通常情况下,仍然需要手动验证。
有几百种文档类型,BPO 人员需要有一个“ 的知识库,知道某个文档是什么样子,同一文档有哪些不同的变体?”, 以便对文档进行分类。最重要的是,如果人工工作太多,人为错误往往会增加。
目标
文档分类解决方案应该能够显著减少人工工作量。它应该在最少人工干预的情况下实现更高水平的准确性和自动化
我们将在这一系列博客中讨论的解决方法不仅限于抵押贷款行业,它可以应用于任何需要扫描文档图像并对此类文档进行分类的地方。几个可能的行业有 金融机构、学术界、研究机构、零售店
文件的特点
为了做一个解决方案管道,第一步就是要知道什么是数据,它的不同特征是什么。由于我们一直在抵押贷款领域工作,我们将定义我们在抵押贷款行业中处理的数据的特征。
在一个*包中,*有许多类型的页面,但是一般来说,这些可以分为三种类型:
结构化| 一致的表单和模板

例如:调查、问卷、测试、索赔表
非结构化| 文本化,无格式和表格

例如:合同、信件、文章、笔记
半结构| 以上两者的杂交,可能有部分结构

例如:发票、采购订单、账单、eob
就文件而言,以下是在数据中观察到的特征。
- 包裹中的文件顺序不一致。例如,在一个包文档**“A”可能在文档“B”**之后,而在另一个包文档中则相反
- 同一个文档类有许多变体。一个文档类可以有不同的外观变化,例如,一个**文档类“A”**页面模板/格式可能因美国不同的州而异。在抵押贷款领域,这些表示相同的信息,但在格式和内容上有所不同。换句话说,如果“猫”是一个文件,不同品种的猫就是“变种”。
- 文件类型有不同种类的扫描变形,即噪声、2D 和 3D 旋转、扫描质量差、页面方向,这些会扰乱这些文件的 OCR。
解决方法
在这一节中,我们将抽象地解释我们的解决方案管道是如何工作的,以及每个组件或模块是如何组合在一起产生一个端到端的管道的。下面是解决方案的流程图。

解决方案|流程图
既然目标是识别包内的文档,我们就必须识别 一个文档内的什么样的特征,使它不同于另一个? 。在我们的例子中,我们认为文档中的文本是关键,因为直觉上我们人类也是这样做的。下一个挑战是找出文档在包中的位置。在多页文档的情况下,边界页(开始、结束)最有意义。因为使用这些页面,可以识别一系列文档。
机器学习课程
在机器学习方面,我们把这个问题当成了**分类问题。**我们决定在这里标识出 每个文档的第一页和最后一页 。我们将我们的 机器学习类 (ML 类)分为三种类型:
- 首页类:这些类是每个文档类的首页,负责标识文档的开始。
- 末页类:这些类是每个文档类的末页,负责标识文档的结尾。这些类仅适用于包含多页样本的文档类。
- Other Class: 这个类是一个单独的类,包含所有文档类的中间页面,合并成一个类。拥有这个类有助于后面阶段的管道,它减少了文档的中间页面被分类为同一文档的第一页或最后一页的情况,这在直觉上是可能的,因为在所有页面(如页眉、页脚和模板)之间可能存在相似性。这允许模型学习更健壮的特征。
下图展示了这些不同类型的 ML 类在包和文档方面的表现

这里 A、B 是文档 A、B 的第一个页面类。此外,A-last、B-last 是相同文档的最后一个页面类。任何文档类的所有中间页面都被视为另一个类
机器学习引擎
一旦定义了 ML 类,下一步就是准备用于训练机器学习引擎的数据集(数据准备部分将在接下来的章节中详细讨论)。下图解释了机器学习引擎的内部工作原理,是解决方案管道的一个更技术性的视图。

端到端分类周期。
让我们一步一步地描述解决方案的不同阶段。
第一步
- 包(pdf 格式)被分割成单独的页面(图像)
第二步
- 通过从图像中提取文本并生成文本文件的 OCR (光学字符识别)**、**处理各个页面。我们使用了最先进的 OCR 引擎来生成我们案例中的文本。有许多免费的在线 OCR 产品可以在这个步骤中使用。
第三步
- 对应于每个页面的文本然后被传递到机器学习引擎,在那里文本矢量器(Doc2Vec) 生成其特征向量表示,本质上是一个浮点列表。
第四步
- 然后将特征向量传递给分类器(逻辑回归)。分类器然后预测每个特征向量的类别。这些基本上是我们之前讨论过的 ML 类别之一(第一、最后或其他
此外,分类器返回所有 ML 类(图表最右边的部分)**的置信度得分。**例如让(D1,D2..)是 ML 类,那么对于单个页面,结果可能如下所示。

后加工
一旦整个包被处理,我们使用结果/预测来识别文档的边界。结果包含包中所有页面的预测类和预测的置信度得分。请参见下表

下面是使用来自机器学习引擎的输出来识别文档边界的简单算法和步骤。

文档范围识别|后处理算法
解决方案详情(深度挖掘 TL;博士)
数据准备
致力于开发一个端到端的文档分类管道。第一步,也可以说是最重要的一步是数据准备,因为解决方案和它使用的数据一样好。我们用于实验的数据是来自抵押贷款领域的文档。我们采用的策略可以以类似的方式应用于任何形式的文档数据集。以下是执行的步骤。
定义:文档样本 *是一个特定文档的实例。通常它是一个(****pdf)***文件,只包含该文档的页面。
第一步
- 第一步是决定,包中的哪些文档要被识别和分类?。理想情况下,应该选择包中存在的所有文档。一旦决定了文档类,我们就进入提取部分。在我们的例子中,我们决定分类(44)文档类。
第二步
- 为了获得数据集,我们收集了数百个包的 pdf,并从这些包中手动提取所选文档。一旦文档在包中被识别,该文档的页面被分离并以 pdf 文件的形式连接在一起。例如,如果我们在包的第 4 页到第 10 页发现了“ Doc A ”。我们将提取 6 页(第 4-10 页)并将其合并成一个 6 页的 pdf。这个 6 页的 pdf 构成了一个文档样本。为特定文档类别提取的所有样本都放在单独的文件夹中。下图显示了文件夹结构。我们为每个文档类收集了 300 多个文档样本。每个文档类都有一个惟一的标识符,我们称之为" DocumentIdentifierID "

第三步
- 下一步是应用 OCR 和从文档样本中的所有页面提取文本。OCR 遍历所有文件夹并生成 excel 文件,其中包含摘录文本和一些元数据。下面显示了 excel 文件的格式,每行代表一页

带有示例行的数据集表。
贷款号,文件名: 这些都是唯一的样本(pdf)标识符。表格中有两个(绿色、黄色)样本。
文档标识符 ID,文档名: 代表这些样本所属的文档类。
页数: 一个特定样本中的总页数。(两个样本都有 2 页)
页码: 是样本内每一页的有序页码。
***IsLastPage:***如果为 1,则表示该页面是该特定样本的最后一页。
页面文本: 是从特定页面的 OCR 返回的文本。
数据转换
一旦数据以上述格式生成,下一步就是转换它。在转换阶段,数据被转换/处理成对训练机器学习模型至关重要的格式。以下是应用于数据集的转换。
步骤 1 | 生成 ML 类
- 转换的第一步是生成第一页、最后一页和其他页面类。为此,使用了页码和页面列值。下面显示了所用逻辑的条件表示。

- 此外,下表代表各列。注意黄色栏,其中 6853 表示第一个页面类,6853-最后一个表示最后一个页面类,而中间页面被视为其他类

步骤 2 | 数据分割用于训练和测试管道
- 一旦步骤 1 完成,从那时起我们只需要两列“页面文本和“ ML 类”来创建培训管道。其他列用于测试评估。
- 下一步是拆分用于训练和测试管道的数据,数据的拆分方式是 80% 用于训练, 20% 用于测试。数据也是随机打乱的,但每一类都是分层的。更多信息请点击链接。
第三步| 数据清理和转换
- 包含每个页面的 OCR 文本的“页面文本”列被清理,该过程应用于训练和测试。以下是执行的过程。
- 大小写修正:所有文字转换成大写或小写。
- 非字母数字字符的正则表达式:删除所有非字母数字的字符。
- 单词标记化:所有单词都被标记化,这意味着一个页面文本字符串成为单词列表
- 停用词移除:停用词是在英语中过于常见的词,可能对单个文档的分类没有帮助。例如像“这个”、“是”、“一个”这样的词。这些词也可以是特定领域的。它可以用来删除许多不同文档中常见的冗余单词。即就金融或抵押而言,“价格”一词可出现在许多文件中。
下表显示了转换前后的情况

培训渠道
在前面的**机器学习引擎部分,**我们抽象地讨论了机器学习引擎的内部工作原理。两个主要组成部分是。
- **文本矢量器:**在我们的例子中,我们使用了 Doc2Vec
- **分类器模型:**使用逻辑回归器进行分类。
文本矢量器(Doc2Vec)
自从自然语言处理 (NLP)开始以来,就有了将文本转换成机器可以理解的东西的需求。这意味着,将文本信息转换成有意义的表示,通常称为数字向量(或数组)。研究界一直在开发不同的方法来完成这项任务。在我们的研发中,我们尝试了不同的技术,发现 Doc2Vec 是所有技术中最好的。
Doc2Vec 基于 Word2Vec 模型。Word2Vec 模型是一个预测向量空间模型。为了理解 Word2Vec,让我们从向量空间模型开始。
***向量空间模型(VSM):***将单词嵌入到连续的向量空间中,其中语义相似的单词被映射到附近的点
VSM 的两种做法:
- 预测方法: 根据学习到的小而密的嵌入向量(例如 Skip-Gram,CBOW)从其邻居预测一个单词。Word2Vec 和 Doc2Vec 就属于这一类机型
Word2Vec 型号
这是一个计算高效的预测模型,用于从原始文本中学习单词嵌入。 Word2Vec 可使用以下两种模型创建:
- Skip-Gram :围绕当前单词(目标单词)创建一个滑动窗口。然后使用当前单词来预测所有周围的单词(上下文单词)。(例如从“垫子”预测“猫坐在”)
- 连续词袋 ( CBOW ):在当前词(目标词)周围创建一个滑动窗口。然后从周围的单词(上下文单词)中预测当前单词。(如从“猫坐在”上预测“垫子”)
更多详情,请阅读这篇文章。它详细解释了它的不同方面。
Doc2Vec 型号
**这种文本矢量化技术在科研论文 句子和文档的分布式表示 **中有所介绍。此外,进一步的技术细节可在这里找到。
定义||it是一种从变长文本块中学习 定长特征向量表示 的 无监督算法 。然后,这些向量可以在任何机器学习分类器中使用,以预测类别标签。**
它类似于 Word2Vec 模型,只是它使用每个文本文件中的所有单词在一个矩阵中创建一个唯一的列(称为段落矩阵*)。然后,将训练单层 NN,如在 Skip-Gram 模型中看到的,其中输入数据都是当前单词的周围单词以及当前段落列,以预测当前单词。其余与 Skip-Gram 或 CBOW 模型相同。***

Doc2Vec |分布式单词包|来源:句子和文档的分布式表示
Doc2Vec 模型的优点:
- 在情感分析任务上,Doc2Vec 取得了新的最先进的结果,优于复杂的方法,在错误率方面产生了超过 16% 的相对改善。
- 在文本分类任务上,Doc2Vec 令人信服地击败了词袋模型,给出了大约 30% 的相对提升。
分类器模型(逻辑回归)
一旦文本被转换成矢量格式。机器学习分类器已经准备好学习不同文档类型的向量中存在的模式,并识别正确的区别。因为有许多分类技术可以在这里使用,我们尝试了最好的分类技术并评估了它们的结果。即随机森林、SVM、多层感知器和逻辑回归器。为获得最佳结果,每个分类器尝试了许多不同的参数。逻辑回归器被发现是所有这些模型中最好的。
培训程序
- 一旦数据被转换。首先,我们在训练分割上训练 Doc2Vec 模型(如数据转换一节中所讨论的)。-
- 训练完 Doc2Vec 模型后。训练数据再次通过它,但是这一次不训练模型,而是我们推断训练样本的向量。最后一步是将这些向量和实际的 ML 类标签传递给分类模型(逻辑回归)。
- 一旦根据训练数据对模型进行了训练,这两个模型都被保存到磁盘上,以便可以将它们加载到内存中,用于测试和最终的生产部署。下图显示了该协作方案的基本流程。

测试和评估管道
一旦训练了管道(包括 Doc2Vec 模型和分类器),下面的流程图显示了如何使用它来预测测试数据分割的文档类。****

转换后的测试数据通过经过训练的 Doc2Vec 模型,在这里提取并推断出测试数据中所有页面的矢量表示。然后通过分类器对这些向量进行分类,该分类器返回所有 ML 类别的预测类别和置信度得分。
对于机器学习引擎的详细评估,我们根据结果生成一个 excel 文件。下表显示了测试阶段生成的列和信息。

测试阶段生成的评估 excel 文件。
页面文本、文件名、页码: 这些 都是我们在数据准备阶段就有的相同的列,这些只是从源数据集中照原样取来的。
***ground,pred 😗** ground 显示该页面的实际 ML 类,而 pred 显示 ML 引擎预测的 ML 类。
已训练类列 :此部分中的列表示模型被训练的 ML 类以及这些类的置信度得分。
***MaxProb,Range 😗** MaxProb 显示“已训练的类”部分中任何列达到的最大置信度得分。参见红色文本,范围显示 MaxProb 落入的范围。
目前有三个等级的结果评估。
- 累积误差评价指标
- 混淆矩阵
- 班级水平置信度分数分析
累积误差评估度量
该评估计算两个指标,准确性和F1-分数*。更多细节请看这个博客。这些让我们对管道的优点有了一个抽象的认识。分数可以在(1-100)之间。其中较高的数字表示管道在文档分类方面的好坏。在我们的实验中,我们得到了下面的准确度和 f1 值。***

混淆矩阵
混淆矩阵是一个表格,通常用于描述一个分类模型(或“分类器”)对一组真实值已知的测试数据的性能。
本质上,它更容易理解:
- 哪些班级表现不好?
- 个别班级的准确率分数是多少?
- 哪些阶层互相混淆?
下图代表了我们测试后生成的混淆矩阵。这是一个嵌入链接,所以点击查看混淆矩阵。
混淆矩阵图
*****X 轴(真实标签)**和 *Y 轴(预测标签)上的值代表我们训练的文档类别。单元格内的数字显示属于左边和底部的类的测试数据集的百分比。
****对角线上的值代表预测类别正确的数据百分比。百分比越高越好。即,如果 0.99,则意味着该特定类别的 99%的测试数据被正确预测。所有其他单元格显示错误的预测,百分比显示某个类被另一个类混淆的程度。
可以看出,该模型能够以超过 90% 的准确度正确地分类大多数 ML 类别。
类别级置信度得分分析
虽然混淆矩阵给出了关于类混淆的细节,但是它不代表预测的置信度分数。也就是说
- “在对一个文档类进行预测时,模型的可信度如何?”
需要什么?
在理想情况下,模型在预测正确的 ML 类时应该具有高置信度,而在预测错误的 ML 类时应该具有低置信度。但是这不是严格的行为,并且取决于许多因素,即特定类的性能、文档类之间的实际领域相似性等。为了评估这种行为是否存在以及置信度得分是否可以作为真实预测的有用指标,我们设计了一种额外的评估方法。
接近
因为任务是减少人工工作,所以决定只选择具有高置信度的预测。这样错误的预测就不会发生(因为那些人不会有很高的信心)。其余的文档和页面将由 BPO 手动验证。
阈值
在该步骤中,计算类别的置信度得分并定义阈值,阈值是一个百分比,即 80%,75% ,其中基于以下条件确定。
- 什么是置信分值,其中,错误的预测是无关紧要的数字,而正确的预测是较高的数字。换句话说,这是关于 找到甜蜜点。
下图显示了真阳性(蓝线)和假阳性(红线)。

X 轴显示了 ML 类, Y 轴显示了特定类的测试数据的百分比,其被真阳性或假阳性所覆盖。
***例如:在 ML 类 **1330 的情况下,**真实预测几乎覆盖了该类的整个测试数据集的 70%。这意味着 ML 引擎能够正确预测 70% 的数据,置信度得分大于 *90%。此外,假阳性仅占测试数据集的 1%,这意味着只有 1% 的测试数据被预测为错误,置信度高于 90%。
虽然,由于阈值的原因,有时我们会丢失真正的阳性(当置信度得分小于阈值时)。但这并不像高置信度的误报那样糟糕。此类页面/文档将被手动验证。
***前面的图是用阈值( **90%及以上)制作的。**在 t he 以下图中,阈值为( *80%及以上)。注意,即使阈值降至 80% 假阳性也不会增加,而真阳性会显著增加。这意味着,在 90%和 80%阈值之间, 80% 是最佳的。

进行该分析时,检查所有水平,即 50%、60%、70%。使用该评估度量来选择最佳阈值。
解决方案功能
- 快速预测| 一个页面的分类时间在(~ 300ms )以下。如果算上 OCR 的时间,一页在 1 秒之内就可以很好的分类了。此外,如果采用多重处理,
- 高准确度| 当前的解决方案管道能够以高准确度和高置信度识别和分类文档。在大多数类别中,我们获得了超过 95%的准确率。
- 标记数据需求| 在我们的实验中,我们观察到管道可以很好地处理每个文档类的大多数 300 个样本。(就像我们在这些博客中讨论的实验一样)。但是这取决于文档类的变化和类型。此外,我们看到随着样本数的增加,准确度和置信度得分也在增加。
- 置信度得分阈值| 管道提供预测置信度得分,这实现了调整方法,并允许在真阳性和假阳性之间进行调整。
- ****多处理|doc 2 vec 实现允许多处理,而且我们的数据转换脚本是高度并行的。
结论
机器学习和自然语言处理已经在许多领域创造了奇迹,我们直接看到了它如何帮助减少人工劳动和自动化文档分类任务。求解不仅快,而且非常准确。
因为这一过程中使用的数据具有敏感性。代码库不可用。我会在一些虚拟数据的基础上重做代码,这将允许我把它上传到我的 github。请在 github 上关注我,了解更多更新。也看看我的一些其他项目;)
用 ARIMA、LightGBM 和 Prophet 进行多步时间序列预测
原文:https://towardsdatascience.com/multi-step-time-series-forecasting-with-arima-lightgbm-and-prophet-cc9e3f95dfb0?source=collection_archive---------1-----------------------
实践教程
使用 Python 对不同类型的时间序列进行建模,以比较模型算法

由马库斯·温克勒在 Unsplash 上拍摄
时间序列预测是数据科学领域中一个非常常见的话题。公司使用预测模型来更清楚地了解他们未来的业务。在开发时间序列预测模型时,选择正确的算法可能是最困难的决定之一。在本文中,我们在不同类型的时间序列数据集上比较了三种不同的算法,即 SARIMA 萨里玛、LightGBM 和 Prophet。SARIMA 萨里玛是最受欢迎的经典时间序列模型之一。 Prophet 是脸书在 2017 年开发的较新的静态时间序列模型。 LightGBM 是一种流行的机器学习算法,一般应用于表格数据,可以捕捉其中的复杂模式。
我们使用以下四种不同的时间序列数据来比较这些模型:
- 循环时间序列(太阳黑子数据)
- 没有趋势和季节性的时间序列(Nile 数据集)
- 趋势强劲的时间序列(WPI 数据集)
- 具有趋势和季节性的时间序列(航空数据集)
虽然我们将在所有四个不同的时间序列上尝试 SARIMA 萨里玛和 LightGBM,但我们将只在航空数据集上模拟 Prophet,因为它是为季节性时间序列设计的。
1.循环时间序列(太阳黑子数据)
周期性时间序列的上升和下降不是固定频率的,这不同于具有固定和已知频率的季节性时间序列。下面的数据集是来自国家地球物理数据中心的太阳黑子年度(1700-2008)数据。

太阳黑子数据集
首先,我们检查时间序列的平稳性。平稳性意味着时间序列不会随着时间的推移而改变其统计特性,特别是其均值和方差。具有周期行为的时间序列基本上是平稳的,而具有趋势或季节性的时间序列则不是平稳的(详见此链接)。我们需要平稳的时间序列来发展稳定的线性模型,如 ARIMA。
下面,我们将设置并执行一个显示自相关(ACF)和偏自相关(PACF)图的函数,同时执行扩展的 Dickey-Fuller 单元测试。

自相关(ACF)图可以用来判断时间序列是否平稳。这也有助于找到 ARIMA 模型中移动平均线部分的阶数。偏自相关(PACF)图有助于识别 ARIMA 模型中自回归部分的阶数。增强的 Dickey-Fuller 单元测试检验时间序列是否是非平稳的。零假设是序列是非平稳的,因此,如果 p 值很小,这意味着时间序列不是非平稳的。
上图中,Dickey-Fuller 检验 p 值不够显著(> 5%)。我们将取第一个差值,使级数更加平稳。

这一次,Dickey-Fuller 测试 p 值是显著的,这意味着该序列现在更有可能是平稳的。
ACF 图显示了正弦曲线模式,并且在 PACF 图中直到滞后 8 都有显著的值。这意味着 ARIMA(8,1,0)模型(我们取第一个差,因此 d=1 )。您可以在此链接中看到从 ACF/PACF 图确定 ARIMA 参数顺序的一般规则。下一步,我们将使用sktime包中的AutoARIMA,它会自动优化 ARIMA 参数的顺序。考虑到这一点,上面查找 ARIMA 参数的正确顺序的图形分析看起来是不必要的,但它仍然有助于我们确定参数顺序的搜索范围,并使我们能够验证 AutoARIMA 的结果。
在建模之前,我们将数据分为训练集和测试集。该系列的前 80%将作为训练集,其余 20%将作为测试集。
1.1 太阳黑子数据集上的 ARIMA
ARIMA 是最流行的时间序列预测模型之一,它在一个类似回归的模型中同时使用序列的过去值(自回归)和过去的预测误差(移动平均)。模型有三个不同的参数 p,d ,和 q 。 p 是自回归部分的阶次, d 是涉及的一阶差分的程度, q 是移动平均部分的阶次。我们需要找到这些参数的正确值,以获得我们的时间序列上最合适的模型。我们这里用的是[sktime](https://www.sktime.org/en/latest/api_reference/modules/auto_generated/sktime.forecasting.arima.AutoARIMA.html) 的 [AutoARIMA](https://www.sktime.org/en/latest/api_reference/modules/auto_generated/sktime.forecasting.arima.AutoARIMA.html),它是[pmdarima](http://alkaline-ml.com/pmdarima/)的包装器,可以自动找到那些 ARIMA 参数( p,d,q )。pmdarima是一个 Python 项目,它复制了 R 的 auto.arima 功能。你可以在链接中看到 auto.arima 是如何自动调整参数的。由于上面的分析建议 ARIMA(8,1,0)模型,我们将 start_p 和 max_p 分别设置为 8 和 9。

结果是AutoARIMA选择的参数与我们之前的预期略有不同。
接下来,我们将设置一个函数,在该函数下绘制模型预测并评估模型性能。我们使用平均绝对误差(MAE) 和平均绝对百分比误差(MAPE) 作为性能指标。MAE 对预测期内的绝对预测误差进行平均:

𝑡是时间,𝑦𝑡是𝑡的实际值,𝑦̂ 𝑡是预测值,𝑛是预测范围。
MAPE 是 MAE 的缩放度量,它是绝对误差除以实际𝑦:


黑子数据集上的 1.2 LightGBM
要使用 LightGBM 进行预测,我们需要首先将时间序列数据转换为表格格式,其中的特征是用时间序列本身的滞后值创建的(即 𝑦𝑡−1、𝑦𝑡−2、𝑦𝑡−3 、…)。由于模型只能预测一步预测,当我们创建多步预测时,预测值将用于下一步的特征,这称为多步预测的递归方法(您可以在本文中找到多步预测的不同方法)。软件包为我们提供了这些功能和一个方便的 API。在下面的create_forecaster函数中,make_reduction包装LGBMRegressor,并在我们拟合预测器时将输入时间序列转换成表格格式。
我们也在使用ForecastingGridSearchCV来寻找最好的滞后特征window_length。

下表总结了两种不同模型的结果。对于这个时间序列数据,LightGBM 的表现优于 ARIMA。

太阳黑子数据上的模型性能
2.没有趋势和季节性的时间序列(Nile 数据集)
尼罗河数据集包含从 1871 年到 1970 年的 100 年间在阿什万测量的尼罗河年流量。时间序列没有任何季节性,也没有明显的趋势。

虽然 Dickey-Fuller 测试表明它是稳定的,但在 ACF 图中可以看到一些自相关。我们正试图了解它的第一个不同之处。

这看起来比原来更稳定,因为 ACF 图显示立即下降,并且 Dicky-Fuller 测试显示更显著的 p 值。从这个分析中,我们期望 ARIMA 在 p 和 q 上具有(1,1,0),(0,1,1)或任何组合值,并且 d = 1 ,因为 ACF 和 PACF 在滞后 1 处显示了重要值。让我们看看 AutoARIMA 选择什么参数值。
2.1 尼罗河数据集上的 ARIMA

该模型按照预期选择了 d = 1 ,并且在 p 和 q 上都有 1。然后,我们创建一个带有评估的预测。

由于时间序列中没有明确的模式,该模型预测随着时间的推移,值几乎保持不变。
Nile 数据集上的 2.2 LightGBM
我们使用与前面的数据相同的函数来开发 LightGBM。

原来 LightGBM 创造了一个与 ARIMA 相似的预报。下面的汇总表显示这两种型号之间没有太大的区别。

Nile 数据的模型性能
3.趋势强劲的时间序列(WPI 数据集)
美国批发价格指数(WPI)从 1960 年到 1990 年有一个强劲的趋势,如下图所示。

我们取第一个差值,使它保持不变。

它仍然看起来不稳定,因为 ACF 随时间缓慢下降,Dicky-Fuller 也没有显示出显著的 p 值。因此,我们又多了一个不同点。

现在,随着 Dicky-Fuller 的显著值和 ACF 图显示快速下降,它看起来是稳定的。从这个分析中,我们期望 d = 2 因为it 需要二阶差分来使其稳定。由于 ACF 在滞后 1 时具有重要值,而 PACF 在滞后 2 之前具有重要值,我们可以预期 q = 1 或 p = 2 。
3.1 WPI 数据集上的 ARIMA
我们将时间序列分成训练集和测试集,然后在其上训练 ARIMA 模型。

正如前面的分析所证实的,该模型有二级差异。接下来,我们将创建一个预测及其评估。

3.2 WPI 数据集上的 LightGBM
我们用和以前一样的方式对 LightGBM 建模,看看它在这个时间序列上是如何工作的。

LightGBM 显然不太好用。由于回归树算法无法预测超出其在训练数据中看到的值,因此如果时间序列有很强的趋势,它就会受到影响。在这种情况下,我们需要在建模之前取消时间序列的趋势。sktime提供了一个方便的工具Detrender和PolynomialTrendForecaster,用于还原可包含在培训模块中的输入序列。
在将其纳入培训模块之前,我们将在下面演示PolynomialTrendForecaster,看看它是如何工作的。

线性去趋势
在上图中,你可以看到趋势预测者捕捉到了时间序列中的趋势。
接下来,我们使用TransformedTargetForecaster创建一个预测器,它包括包装PolynomialTrendForecaster的Detrender和包装在make_reduction函数中的LGBMRegressor,然后在window_length上用网格搜索训练它。

这一次,LightGBM 在 detrender 的帮助下预测超出训练目标范围的值。
下表总结了两种不同模型在 WPI 数据上的表现。LightGBM 的表现再次优于 ARIMA。

WPI 数据的模型性能
4.具有趋势和季节性的时间序列(航空数据集)
Box-Jenkins 航空公司数据集由 1949-1960 年间国际航空公司乘客的月度总数(千单位)组成。如下图所示,该数据具有趋势性和季节性。

首先,我们取一个季节差异(滞后 12)使其稳定。

随着 ACF 缓慢下降,它看起来仍然不是稳定的,所以我们对它进行额外的一阶差分。

现在,它看起来是稳定的,因为 Dickey-Fuller 的 p 值是显著的,ACF 图显示随着时间的推移快速下降。这一分析的结果表明,当 ACF 和 PACF 图在滞后 1 时显示显著值时,SARIMA 与 d = 1 和 D (季节差异的顺序) = 1.p 或 q 可以是 1。
4.1 航空数据集上的 SARIMA
接下来,我们将数据分为训练集和测试集,然后在其上开发 SARIMA(季节性 ARIMA)模型。SARIMA 模型具有 ARIMA 上空的附加季节参数(P,D,Q)。p、D 和 Q 分别代表季节自相关的阶、季节差异的程度和季节移动平均的阶。为了对 SARIMA 建模,我们需要指定sp参数(季节周期。在这种情况下,它是 12)在AutoARIMA上。

正如所料,创建的模型有 d = 1 和 D = 1 。接下来,我们创建一个带有评估的预测。

4.2 航空数据集上的 LightGBM
由于时间序列具有季节性,我们在 LightGBM 预测模块中增加了Deseasonalizer。由于季节性效应会随着时间的变化而变化,我们在Deseasonalizer模块上设置了“multiplicative”。

4.3 航空数据集上的预言者
Prophet 是脸书在 2017 年开发的一个时间序列预测模型,可以有效处理多个季节性(年、周、日)。它还具有整合节假日影响和在时间序列中实现自定义趋势变化的功能。由于我们的时间序列不需要所有这些功能,我们只是在打开年度季节性的情况下使用 Prophet。

下表比较了航空公司数据集上三种不同模型的性能指标。虽然这三款车型之间的性能差异不大,但 ARIMA 的表现略好于其他车型。

航空公司数据上的模型性能
结论
在这篇博文中,我们对不同类型的时间序列比较了三种不同的模型算法。除了季节性时间序列(航空公司)之外,LightGBM 的表现与 ARIMA 相当或更好。

通过时间序列数据集建立模型预测 MAE
由于 LightGBM 是一个非线性模型,它比线性模型有更高的过度拟合数据的风险。您可能希望在使用它时设置可靠的交叉验证。如果你的数据有许多不同的时间序列(例如,公司的股票价格或按产品的销售),机器学习方法也比线性模型有优势,因为你可以用一个机器学习模型预测多个时间序列(我们在这篇博客中没有挖掘这一优势。如果你感兴趣的话,请看一些来自 M5 游戏竞赛的实现。
机器学习方法的一个缺点是,它没有任何计算预测区间的内置功能,而大多数静态时间序列实现(即 ARIMA 或预言家)都有。您可能想编写自己的模块来计算它。
虽然 Prophet 在我们的数据中并不比其他人表现得更好,但如果您的时间序列有多个季节性或趋势变化,它仍然有很多优势。
您可以在下面的 Google Colab 链接或 Github 链接中看到完整的工作代码。
https://colab.research.google.com/drive/1Z4zNI_bVXoFQBsCHUtxBDCBno6yhXceB?usp=sharing https://github.com/tomonori-masui/time-series-forecasting/blob/main/multi_step_time_series_forecasting.ipynb
参考
[1] 用 sktime 预测— sktime 官方文档
[2]Python 中的时间序列分析
[3]light GBM 自回归器—使用 Sktime
[4] Rob J Hyndman 和 George Athanasopoulos,预测:原则和实践(第 3 版)——第 9 章 ARIMA 模型
2023 年 3 月 9 日-更新代码(包括链接的 Colab 和 Github)以使用当前最新的包
机器学习中的多任务学习
实践教程
基于神经网络的深度多任务学习

阿瑟尼·托古列夫在 Unsplash 上的照片
介绍
在大多数机器学习环境中,我们关心的是一次解决一个单个任务。不管任务是什么,问题通常都是用数据来解决单个任务或一次优化一个指标。然而,这种方法最终会遇到性能上限,通常是由于数据集的大小或模型从中学习有意义的表示的能力。
另一方面,多任务学习是一种机器学习方法,其中我们试图同时学习多个任务,同时优化多个损失函数。我们不是为每项任务训练独立的模型,而是让一个模型学习一下子完成所有的任务。在此过程中,模型使用不同任务中的所有可用数据来学习在多种上下文中有用的数据的通用表示。
多任务学习已经广泛应用于多个领域,如自然语言处理、计算机视觉和推荐系统。由于它能够有效地利用大量数据来解决相关任务,因此在行业中也经常被利用,例如在谷歌。
何时使用多任务学习
在进入如何实现多任务学习模式的细节之前,首先要了解多任务学习适合和不适合的情况。
通常,当任务具有某种程度的相关性时,应使用多任务学习。换句话说,当任务之间有潜在的原则或信息共享时,多任务学习可以提高绩效。
例如,涉及对动物图像进行分类的两个任务很可能是相关的,因为这两个任务都将涉及学习检测皮毛图案和颜色。这将是多任务学习的一个很好的用例,因为学习这些图像特征对于两个任务都是有用的。
另一方面,有时多任务培训会导致任务间的负迁移,其中多任务模式比等效的单任务模式表现更差。这通常发生在不同的任务彼此不相关的时候,或者在一个任务中学到的信息与在另一个任务中学到的信息相矛盾的时候。
构建多任务模型
现在我们知道了何时应该使用多任务学习,我们将通过一个简单的多任务模型架构。这将集中于神经网络架构(深度多任务学习),因为神经网络是迄今为止多任务学习中使用的最常见的模型类型。
学习共享表示
其核心是,深度多任务学习旨在学习产生通用的表示,这些表示足够强大,可以在不同的任务之间共享。这里我将重点讨论硬参数共享,其中不同的任务使用输入数据的完全相同的基本表示。

来源
正如我们所看到的,硬参数共享迫使模型学习一种中间表示,这种表示为所有任务传达了足够的信息。网络的特定任务部分都从最后一个共享层的相同基础表示开始。
多任务学习提高了这种表示的概化能力,因为学习多任务会迫使模型关注对所有任务都有用的特征。假设任务是相关的,对任务 A 重要的特征也可能对任务 c 重要,反之亦然;不重要的功能可能在所有任务中都不重要。
多任务学习也有效地增加了你的数据集的大小,因为你是在组合每个任务的数据集。通过向来自不同任务的训练集添加更多样本,模型将学会更好地忽略每个单独数据集中特定于任务的噪声或偏差。
针对多项任务进行优化
一旦决定了模型的架构,我们需要决定优化什么损失函数。
最简单的方法是最小化单个任务损失函数的线性组合。每个任务都会有自己单独的损失函数 L_i 。因此,在我们的多任务模型中,我们简单地对每个损失函数进行加权,并最小化这些加权损失的总和。

来源
寻找辅助任务
现在我们知道了如何构建多任务模型,我们需要确定如何将这种方法应用到给定的任务中。在许多情况下,看起来你真的只有一个任务要解决,如何将你的问题融入到多任务学习环境中并不明显。
如果您没有明确地拥有多个任务,您可以创建辅助任务,旨在解决与您的单个原始任务相关但不相同的问题。通过创建一个辅助任务,你仍然可以将多任务学习应用到你的单一主要任务中,并有望提高它的性能。
确定一个辅助任务通常是特定领域的,没有一个放之四海而皆准的方法。然而,它们有一些共同的一般原则。一般来说,辅助任务应该与主要任务相关,并且应该推动网络学习主要任务的重要特征。
例如,如果主要任务是对数据序列进行分类,我们可以创建一个辅助任务,用自动编码器重建序列。这个辅助任务明确地迫使网络学习序列编码器,该序列编码器产生足够信息的表示,以便能够重构原始序列。这很可能提高原始任务的性能,对序列进行分类,简单地通过产生序列的更多信息的中间表示。
多专家混合多任务学习
原文:https://towardsdatascience.com/multi-task-learning-with-multi-gate-mixture-of-experts-b46efac3268?source=collection_archive---------24-----------------------
谷歌的内容推荐神经网络模型

附身摄影在 Unsplash 上拍照
介绍
多任务学习是一种机器学习方法,其中一个模型学习同时解决多个任务。假设通过学习用相同的模型完成多个相关的任务,每个任务的性能将比我们在每个任务上训练单独的模型更高。
然而,这一假设并不总是正确的。简单的多任务学习方法没有考虑任务之间的关系和学习完成所有任务的权衡。
谷歌的多门专家混合模型 (MMoE)试图通过明确学习任务之间的关系来改善基线多任务学习方法。
结构
我们将讨论多任务学习的三种架构:1)共享底层模型,2)单门专家混合模型(MoE),以及 3)多门专家混合模型(MMoE)。前两个架构提供了上下文,并展示了最终 MMoE 架构的渐进步骤。

来源
共享底层模型
共享底层模式是最简单和最常见的多任务学习架构。该模型有一个单一的基础(共享的底层),所有特定于任务的子网都从这个基础开始。这意味着这种单一的表征用于所有的任务,个体任务没有办法相对于其他任务来调整它们从共享底层中得到什么信息。

来源
专家混合模型
专家混合架构通过创建多个专家网络并添加门控网络来加权每个专家网络的输出,对共享底层模型进行了改进。

来源
每个专家网络本质上都是一个唯一的共享底层网络,每个网络都使用相同的网络架构。假设每个专家网络能够学习数据中的不同模式,并专注于不同的事情。
门控网络然后产生加权方案,使得任务能够使用专家网络输出的加权平均值,以输入数据为条件。门控网络的最后一层是 softmax 层( g(x) ),用于产生专家网络输出的线性组合( y )。

来源
这种架构的主要创新是,该模型能够在每个样本的基础上不同地激活网络的部分。由于门控网络以输入数据为条件(由于门控网络作为训练整个模型的一部分而被训练),该模型能够学习如何基于输入数据的属性对每个专家网络进行加权。
多门专家混合模型(MMoE)
最后,我们继续讨论多门专家混合(MMoE)模型架构。

来源
MMoE 体系结构类似于 MoE 体系结构,只是它为每个任务提供了一个单独的门控网络,而不是为整个模型提供一个单独的门控网络。
这允许模型学习每个专家网络的每个任务和每个样本的权重,而不仅仅是每个样本的权重。这允许 MMoE 学习对不同任务之间的关系进行建模。彼此几乎没有共同点的任务将导致每个任务的门控网络学习使用不同的专家网络。
MMoE 的作者通过在具有不同任务相关性水平的合成数据集上比较共享底层、MoE 和 MMoE 架构来验证这一结论。

来源
首先,我们看到,相对于 MoE 和 MMoE 模型,共享底部模型在所有情况下都表现不佳。
接下来,我们可以看到,随着任务之间的相关性降低,MoE 和 MMoE 模型之间的性能差距增大。
这表明 MMoE 能够更好地处理任务互不相关的情况。任务多样性越大,MMoE 相对于共享底层或 MoE 架构的优势就越大。
Python 中的多任务处理:通过同时执行,将程序速度提高 10 倍
原文:https://towardsdatascience.com/multi-tasking-in-python-speed-up-your-program-10x-by-executing-things-simultaneously-4b4fc7ee71e?source=collection_archive---------2-----------------------
应用线程和进程加速代码的分步指南

帮助我们更快执行的工人大军(图片由 Brian McGowan 在 Unsplash 上提供)
本文着重于通过让程序同时做多件事来加速程序。当我们的程序等待 API 响应时,我们不必空闲,例如:我们可以在同一时间做其他事情!我们还将探讨如何应用更多的 CPU 来加快计算速度。在本文结束时,您将:
- 理解多任务处理的不同方式
- 知道何时应用哪种技术
- 能够通过使用代码示例来加速您自己的代码
在我们开始之前,我强烈建议看看下面的文章。它解释了 Python 是如何工作的,以及为什么它没有其他语言快。它还揭示了为什么 Python 一开始就不是多线程的?你会对我们在这篇文章中试图解决的问题有更好的理解。我们来编码吧!
线程和进程
Python 可以通过两种方式处理多任务:线程和多重处理。从表面上看,他们非常相似,但根本不同。在下面的部分中,我们将通过使用两个简单的比喻来检验这两者。我们的目标是了解线程和进程之间的区别,以便我们知道何时使用哪一个。
线程就像做早餐
让我们做一些早餐:我们需要一个煮鸡蛋,一些烤面包和一杯咖啡,所以我们有 4 个任务:
- 烤面包
- 烧开水
- 煮鸡蛋
- 打开咖啡机

这是我们试图在尽可能短的时间内完成的(图片由 Eiliv-Sonas Aceron 在 Unsplash 上拍摄)
你会怎么做?一种方法是顺序执行每个任务;首先烤面包,然后烧开水和一个鸡蛋,然后打开咖啡机。虽然这个过程很容易理解,但最终留给我们的只是一些冷吐司、一个冷鸡蛋和一杯热咖啡。或者,我们可以同时执行一些任务;我们将打开咖啡机和烤面包机,同时煮些水。
让我们用一些代码来模拟一下。
我们将按顺序(一个接一个)运行这段代码,如下所示:
toast_bread()
boil_water_and_egg()
make_some_coffee()
依次做早餐大约需要 17.5 秒。这涉及到很多等待!让我们使用线程进行多任务处理:
代码非常简单:我们将创建一些任务,并将它们添加到一个列表中。然后,我们将启动列表中的每个线程,并等待所有线程完成(这就是 t.join()所做的)。同时做早餐大约需要 8 秒钟!

我们做早餐的方式(图片由作者提供)
的主要收获是如果有很多等待(典型的 I/O 任务,比如下载数据、API 请求、写文件..)我们可以使用线程来执行多任务。在本文的后面,我们将研究为什么线程是 I/O 任务的最佳选择。
多重处理就像做作业一样
当我们用同样的早餐制作原则来做作业时,我们遇到了一个问题;做数学作业是一项需要持续关注的任务;我们不能开始它,然后等它结束!为了同时做多门功课,我们需要克隆自己,对吗?多重处理正是这样做的。

对焦和开始处理的时间(图片由 Annie Spratt 在 Unsplash 上拍摄)
让我们先把作业的例子翻译成一些代码。我们将通过一些 CPU 密集型处理来模拟做作业;将从 0 到 1 亿的所有数字相加:
这些都是 CPU 密集型函数;我们必须进行大量的计算。我们将首先顺序执行这些函数,然后线程化(与上一部分相同的代码),然后使用下面的代码来生成进程。请注意,代码看起来很像上一部分的线程代码。
基准:
- 依次:14.22 秒
- 使用线程:13.89 秒
- 使用进程:6.00 秒
您将会看到使用进程大大加快了执行速度!这是因为我们可以使用更多的 CPU。
在这个例子中我们要进行 3 亿* 3 次计算。线程化并不能提高它的速度,因为它仍然是一个必须执行 3 亿次计算的 CPU。然而,当使用进程时,我们会在不同的 CPU 上产生一个全新的 Python 实例。换句话说,我们将使用 3 个 CPU,每个 CPU 可以执行 1 亿次计算!
摘要
线程是用来做类似早餐的任务的:它需要大量的等待,因此一个“人”(或 CPU)可以同时做一些事情。过程是为了“思考-任务”;他们需要你在那里做重活。多重处理就像创造一个你自己的克隆体,这样它就可以在你工作的时候做其他的事情。
引擎盖下的线程和多重处理
现在我们对线程和进程的工作原理有了更清晰的理解,让我们来谈谈两者之间的区别。

让我们来看看他们是如何跑的(图片由埃里克·麦克莱恩在 Unsplash 上拍摄)
GIL——为什么线程更适合 I/O
如前所述,线程适用于 I/O 任务,而进程适用于 CPU 密集型任务。原因是 Python 臭名昭著的 GIL;全局解释器锁。这个锁确保 Python 是单线程运行的,阻塞了没有持有锁的其他进程。许多 I/O 进程在空闲时释放 GIL,使得线程化成为可能。查看 这篇文章 了解 Python 为什么应用 GIL。
在作业示例中,线程没有意义,因为所涉及的任务不是 I/O 任务。由于 GIL,任何时候只有一个线程可以执行,所以它不提供加速。当多重处理时,我们创建一个新的 Python 实例,它有自己的 GIL。这样,进程并行运行,大大加快了程序的执行速度。
进程不能共享资源
主要的一点是,进程不能共享资源,而线程可以。这是因为一个进程使用多个 CPU,而一个线程只是一个 CPU 在多个线程之间来回移动。
您可以将线程视为一个 CPU,它首先执行 thread1 中的几行代码,然后执行 thread2 中的几行代码,然后继续执行 thread3。然后,它执行线程 1 中的下一行,然后是线程 2,依此类推。线程并发执行多个任务*;一个在任务间切换的工人。对于用户来说,事情看起来好像是同时发生的,但从技术上来说并非如此。*
当您生成一个新进程时,会创建一个全新的 python 实例,并将其分配给不同的 CPU。这就是两个进程不能共享一个公共资源的原因。 中的流程运行并行*;有多个工作人员同时从事多项任务。*
开销
进程需要更多的时间来生成。这就是为什么家庭作业的例子不是快三倍,而是稍微慢一点;首先,我们必须生成进程,然后才能从并行性中获益。
更快的速度
在 Python 中,多任务处理可以解决很多速度问题,但有时这还不够。查看 这篇 或 这篇 文章,向您展示如何编译一小部分代码以获得 100 倍的速度提升。
* *
结论
在许多情况下,线程和多重处理可以用来加速代码的执行。在本文中,我们探讨了什么是线程和进程,它们如何工作以及何时使用它们。不要忘记查看本文 中关于如何应用池和基准的 !
如果你有建议/澄清,请评论,以便我可以改进这篇文章。同时,看看我的其他关于各种编程相关主题的文章,比如:
- Python 为什么慢,如何加速
- Python 中的高级多任务处理:应用线程池和进程池并进行基准测试
- 编写自己的 C 扩展来加速 Python x100
- cyt hon 入门:如何用 Python 执行>每秒 17 亿次计算
- 用 FastAPI 用 5 行代码创建一个快速自动归档、可维护且易于使用的 Python API
- 创建并发布你自己的 Python 包
- 创建您的定制私有 Python 包,您可以从您的 Git 库 PIP 安装该包
- 完全初学者的虚拟环境——什么是虚拟环境,如何创建虚拟环境(+示例)
- 通过简单的升级大大提高您的数据库插入速度
编码快乐!
—迈克
页(page 的缩写)学生:比如我正在做的事情?跟我来!
*https://mikehuls.medium.com/membership *
Python 中的多元异常检测
原文:https://towardsdatascience.com/multi-variate-outlier-detection-in-python-e900a338da10?source=collection_archive---------7-----------------------
能够检测数据集中异常值/异常值的六种方法

davisuko 在 Unsplash 上拍摄的照片
在我之前的媒体文章中,我介绍了五种不同的单变量异常值检测方法:分布图、Z 分数、箱线图、Tukey fences 和聚类。这突出了一个事实,即可以使用几种不同的方法来检测数据中的异常值,但每种方法都会导致不同的结论。因此,在选择使用哪种方法时,您应该注意数据的上下文,以及哪些领域知识会被归类为异常值。
然而,通常情况下,数据是从多个来源、传感器和时间段收集的,从而产生多个变量,这些变量可能会与您的目标变量相互作用。这意味着分析或机器学习方法通常应用于需要分析多个变量的情况。这意味着,由于这些变量之间的相互作用,能够检测出异常值往往比仅仅从单个变量中检测出异常值更为重要。因此,本文试图确定几种不同的方法来实现这一目的。
像以前一样,神奇宝贝数据集用于演示这些方法,数据来自 7 季的 801 只神奇宝贝。这将侧重于该数据集中的攻击和防御属性,以便能够检测任何潜在的异常,如下图所示。当然,通过这些方法在数据集中发现的任何异常实际上可能不是异常,但是我们可以根据这些结果做出选择。

作者图片
正如我们从该图中看到的,在该数据集中,防御和攻击之间通常存在正线性关系,但似乎有一些异常值。
箱线图和 Tukey 栅栏图
箱线图和 Tukey fences 图是能够从多变量数据集中检测异常值的第一种方法。虽然这些方法能够检测单个变量分布中的异常值,而不是它们之间的相互作用,但我们可以将此作为基线来与其他方法进行比较。我们可以首先把它想象成:
#create the plot
ax = sns.boxplot(data = pokemon[["attack", "defense"]], orient = "h", palette = "Set2")
#add labels
ax.set_xlabel("Value", fontsize = 20, labelpad = 20)
ax.set_ylabel("Attributes", fontsize = 20, labelpad = 20)
ax.set_title("Boxplot of pokemon Attack \nand Defense attributes", fontsize = 20,
pad = 20)
#edit ticks
ax.tick_params(which = "both", labelsize = 15)

作者图片
这表明在两种分布的上端都可能有异常值。为了提取这些值,我们可以使用 Tukey fences,其值高于上四分位数的上限加上 1.5 倍的四分位数间距,低于下四分位数的下限减去 1.5 倍的四分位数间距:
#create a function to calculate IQR bounds
def IQR_bounds(dataframe, column_name, multiple):
"""Extract the upper and lower bound for outlier detection using IQR
Input:
dataframe: Dataframe you want to extract the upper and lower bound from
column_name: column name you want to extract upper and lower bound for
multiple: The multiple to use to extract this
Output:
lower_bound = lower bound for column
upper_bound = upper bound for column"""
#extract the quantiles for the column
lower_quantile = dataframe[column_name].quantile(0.25)
upper_quantile = dataframe[column_name].quantile(0.75)
#cauclat IQR
IQR = upper_quantile - lower_quantile
#extract lower and upper bound
lower_bound = lower_quantile - multiple * IQR
upper_bound = upper_quantile + multiple * IQR
#retrun these values
return lower_bound, upper_bound#set the columns we want
columns = ["attack", "defense"]
#create a dictionary to store the bounds
column_bounds = {}#iteratre over each column to extract bounds
for column in columns:
#extract normal and extreme bounds
lower_bound, upper_bound = IQR_bounds(pokemon, column, 1.5) #send them to the dictionary
column_bounds[column] = [lower_bound, upper_bound]#create the normal dataframe
pokemon_IQR_AD = pokemon[(pokemon["attack"] < column_bounds["attack"][0]) |
(pokemon["attack"] > column_bounds["attack"][1]) |
(pokemon["defense"] < column_bounds["defense"][0]) |
(pokemon["defense"] > column_bounds["defense"][1])
]


作者提供的图片
我们从整个数据集中检测出 15 个潜在的异常值。正如我们所见,这导致了高于 180 攻击和/或高于 150 防御的异常值。因此,这表明识别这些异常值的线性截止值,但这是基于单个变量,而不是相互作用。
隔离林
能够处理这些变量之间相互作用的第一种方法是隔离林。这通常是一个很好的起点,尤其是对于高维数据集。它是一种建立在决策树基础上的树集成方法,就像随机森林一样,通过首先随机选择一个特征,然后在所选特征的最大值和最小值之间选择一个随机分割值来划分树。原则上,异常值没有常规观测值频繁,并且价值不同。因此,通过使用随机分区,它们应该被识别为更靠近树的根,需要更少的分裂。
为此,算法要求我们指定污染参数,该参数告诉算法预计有多少数据是异常的。在我们的例子中,根据 Tukey fences 分析,这可以设置为 0.02,表示我们认为 2%的数据可能是异常的,相当于 16 个点。这可以通过以下方式实现:
from sklearn.ensemble import IsolationForest#create the method instance
isf = IsolationForest(n_estimators = 100, random_state = 42, contamination = 0.02)
#use fit_predict on the data as we are using all the data
preds = isf.fit_predict(pokemon[["attack", "defense"]])
#extract outliers from the data
pokemon["iso_forest_outliers"] = preds
pokemon["iso_forest_outliers"] = pokemon["iso_forest_outliers"].astype(str)
#extract the scores from the data in terms of strength of outlier
pokemon["iso_forest_scores"] = isf.decision_function(pokemon[["attack", "defense"]])#print how many outliers the data suggests
print(pokemon["iso_forest_outliers"].value_counts())# Out:
1 785
-1 16
我们有 16 个异常值。然后,我们可以将其绘制为:
#this plot will be repeated so it is better to create a function
def scatter_plot(dataframe, x, y, color, title, hover_name):
"""Create a plotly express scatter plot with x and y values with a colour
Input:
dataframe: Dataframe containing columns for x, y, colour and hover_name data
x: The column to go on the x axis
y: Column name to go on the y axis
color: Column name to specify colour
title: Title for plot
hover_name: column name for hover
Returns:
Scatter plot figure
"""
#create the base scatter plot
fig = px.scatter(dataframe, x = x, y=y,
color = color,
hover_name = hover_name)
#set the layout conditions
fig.update_layout(title = title,
title_x = 0.5)
#show the figure
fig.show()#create scatter plot
scatter_plot(pokemon, "attack", "defense", "iso_forest_outliers",
"Isolation Forest Outlier Detection",
"name")

作者图片
我们可以看到,与 Tukey Fences 不同,每个变量的顶部或底部没有明显的分界线,左下角的点也被识别为异常值。我们还可以根据这些变量在决策树中的位置,看到它们的得分范围。
#create the same plot focusing on the scores from the dataset
scatter_plot(pokemon, "attack", "defense", "iso_forest_scores",
"Isolation Forest Outlier Detection Scores",
"name")

作者图片
我们可以看到哪些点可能被归类为异常值,位于中心的点得分较高,表明这些点是核心点,而位于外围的点得分较低,表明它们可能是异常值。
我们还可以使用单变量异常检测方法,通过识别分数结果中的异常值来选择污染百分比,如下所示:


作者提供的图片
这些方法可以用来建议在哪里设置污染参数的截止值,并且可以以相同的方式用于所有随后的算法,在这些算法中可以提取分数。
局部异常因素
另一种可以使用的算法是局部离群因子算法。这是一种计算方法,它通过检查一个点的相邻点来找出该点的密度,然后将其与其相邻点的密度进行比较。如果发现一个点的密度比其相邻点的密度小得多,表明是孤立的,那么这个点可以被识别为异常值。
该算法的优势在于,它将数据集的局部和全局属性都考虑在内,因为它关注的是样本相对于其相邻样本的孤立程度。为此,我们需要指定要比较密度的邻居数量(默认为 20)以及要使用的距离度量(默认为“minkowski ”,它概括了欧几里德距离和曼哈顿距离)。因此,这可以应用为:
#import the algorithm
from sklearn.neighbors import LocalOutlierFactor#initialise the algorithm
lof = LocalOutlierFactor(n_neighbors = 20)
#fit it to the training data, since we don't use it for novelty than this is fine
y_pred = lof.fit_predict(pokemon[["attack", "defense"]])#extract the predictions as strings
pokemon["lof_outliers"] = y_pred.astype(str)
#print the number of outliers relative to non-outliers
print(pokemon["lof_outliers"].value_counts())
#extract the outlier scores
pokemon["lof_scores"] = lof.negative_outlier_factor_#Out:1 767
-1 34
其中使用 20 个邻居的默认值给我们 39 个潜在的异常值。我们可以再次检查异常值和分数的分布:


作者提供的图片
同样,我们可以看到,与前面的算法类似,离群点是在点的主要质量的边缘上检测到的。这是因为强调了密度,位于主要区域之外的点很可能被识别为异常值,因为它们周围没有完全包围它们的点。如前所述,我们还可以使用单变量异常检测方法来分析分数,以调整原始算法中的超参数。
数据库扫描
类似地,DBScan 是另一种算法,它也可以基于点之间的距离来检测异常值。这是一种聚类算法,其行为与 LOF 不同,通过选择尚未分配给聚类的点,通过查看在给定距离内是否至少有给定数量的样本来确定它是否是核心点。如果是,那么它与由距离度量识别的在该点的直接到达范围内的所有点一起被指定为核心点。然后,对聚类内的每个点重复这一过程,直到识别出聚类的边缘,在该边缘处,在指定距离内不再有可被识别为核心点的点(在指定距离内具有最小数量的点)。
如果一个点不属于任何潜在的聚类,那么它被认为是异常值,因为它不适合现有的密度或点的聚类。这可以通过以下方式实现:
#import the algorithm
from sklearn.cluster import DBSCAN#initiate the algorithm
#set the distance to 20, and min_samples as 5
outlier_detection = DBSCAN(eps = 20, metric = "euclidean", min_samples = 10, n_jobs = -1)
#fit_predict the algorithm to the existing data
clusters = outlier_detection.fit_predict(pokemon[["attack", "defense"]])#extract the labels from the algorithm
pokemon["dbscan_outliers"] = clusters
#label all others as inliers
pokemon["dbscan_outliers"] = pokemon["dbscan_outliers"].apply(lambda x: str(1) if x>-1 else str(-1))
#print the vaue counts
print(pokemon["dbscan_outliers"].value_counts())# Out:
1 787
-1 14
结果如下:

作者图片
这里的关键区别是,只有两个属性的上边缘被选为该算法的异常值,类似于 Tukey Fences。这是因为它们将超出核心点的范围,而左下角的点将在核心点的最小距离内。
这样做的好处是,当特征空间中的值的分布无法假设时,可以使用它,它很容易在 Sklearn 中实现,并且理解起来很直观。但是,选择最佳参数通常是困难的,并且难以用于预测能力。
椭圆形信封
椭圆包络是另一种异常检测算法,但它假设数据的一部分是高斯分布。这是通过在给定数据集周围创建一个假想的椭圆区域来实现的,其中落在椭圆内的值被视为正常数据,落在椭圆外的值被视为异常值。
与隔离林一样,该模型的实现需要指定参数contamination来表明该模型预期有多少异常值。如前所述,我们可以将其设置为 0.02,这可以实现为:
#import the necessary library and functionality
from sklearn.covariance import EllipticEnvelope#create the model, set the contamination as 0.02
EE_model = EllipticEnvelope(contamination = 0.02)
#implement the model on the data
outliers = EE_model.fit_predict(pokemon[["attack", "defense"]])#extract the labels
pokemon["EE_outliers"] = outliers
#change the labels
pokemon["EE_outliers"] = pokemon["EE_outliers"].apply(lambda x: str(-1) if x == -1 else str(1))
#extract the score
pokemon["EE_scores"] = EE_model.score_samples(pokemon[["attack", "defense"]])
#print the value counts for inlier and outliers
print(pokemon["EE_outliers"].value_counts())#out:
1 785
-1 16
然后,我们可以将结果和分数可视化如下:


作者提供的图片
我们可以看到,围绕点分布中心(即中心质量)的椭圆已经确定。分数本身是负的 Mahalanobis 距离,它是点和分布(椭圆)之间的距离度量的倒数。因此,远离分布的点得到较低的分数。
与之前相比,由该算法检测到的点之间的主要区别在于,朝向中心左侧的点在这里没有被识别为异常值,这表明这些点已经被计算在椭圆内。
这种方法的困难包括变量分布的正态性要求,以及像以前一样我们不知道污染参数的精确值。然而,假设正态性,如前所述,可以对分数进行单变量分析以识别异常值。
合奏
最后,增强对这些方法的信心的一种方法是,不仅使用单一的一种方法,而且将所有方法的预测结合起来。这可以通过以下方式实现:
#extract the sum of the outlier count
pokemon['outliers_sum'] = (pokemon['iso_forest_outliers'].astype(int)+
pokemon['lof_outliers'].astype(int)+
pokemon['dbscan_outliers'].astype(int)+
pokemon['EE_outliers'].astype(int))
#print the value counts for each scale
print(pokemon["outliers_sum"].value_counts())# out:
4 758
2 24
0 9
-4 8
-2 2
其中在所有算法中只有 8 个被识别为异常值。因此,我们可以把这想象成:

作者图片
我们可以看到不同算法的预测重叠的地方。在这一点上,信任哪个算法是主观判断,是单个算法还是多个算法,这将取决于特定上下文中的领域专业知识。
在本例中,确定的八个神奇宝贝如下所示:

其中集合结果被视为关键度量。
除此之外,还可以使用其他算法,包括:一类 SVM、PCA 和自动编码器。当然,这种选择取决于领域、可用的工具和数据集的维度。然而,这些结果表明,并非所有算法都会产生相同的结果,并且需要一些主观判断。
可用代码:https://github.com/PhilipDW183/Outlier_detection
可用数据集:https://www.kaggle.com/rounakbanik/pokemon?select=pokemon.csv
https://philip-wilkinson.medium.com/membership [## scikit-learn 决策树分类器简介
towardsdatascience.com](/introduction-to-decision-tree-classifiers-from-scikit-learn-32cd5d23f4d)
用 Numpy 的 einsum 实现多类分类神经网络
原文:https://towardsdatascience.com/multiclass-classification-neural-network-implementation-using-numpys-einsum-3675a7e1e703?source=collection_archive---------31-----------------------
用 Numpy 的 einsum 实现两层多类分类神经网络。

鸢尾花——照片由希拉·斯威兹从 Unsplash 拍摄
我们都通过实现一个小的分类或回归问题来开始我们的机器学习之旅。今天我将使用 pure numpy 实现一个多类分类神经网络。我将使用虹膜数据集。下面是我在 colab 笔记本上的代码的链接。
https://colab.research.google.com/drive/1RacUOXLadYAHfq_VSLy8NDcMui_APlPI?usp=sharing [## 谷歌联合实验室
多类分类神经网络](https://colab.research.google.com/drive/1RacUOXLadYAHfq_VSLy8NDcMui_APlPI?usp=sharing)
我将创建一个 2 层神经网络。第一个隐藏层将使用leaky_relu激活函数,第二个隐藏层将使用softmax激活函数。
Softmax 函数返回属于每个类的概率。
softmax的导数实现起来有点棘手,因为它涉及到创建一个雅可比矩阵。出于所有实际目的,softmax将始终用于多类分类神经网络的最外层,因此计算其导数并不重要,只要我们能够获得成本函数 J 相对于 logits z 的导数即可。与计算 softmax 函数本身的导数相比,计算该导数相当简单。我在下图中展示了导数:

最外层中成本函数 J w r t logit z 的偏导数。
如果你还执着于得到 softmax 的导数,它的实现就写在下图所示的softmax_prime函数中。

实现神经网络的效用函数(图片归作者所有)
如上图所示,特别注意 softmax 和二进制化功能。
现在让我们来理解binarize功能。它转换标签 0,1,2 等。一个热编码数组,比如:
+=======+===========+
| Label | Binarize |
+=======+===========+
| 0 | [1,0,0,0] |
+ — — — -+ — — — — — -+
| 1 | [0,1,0,0] |
+ — — — -+ — — — — — -+
| 2 | [0,0,1,0] |
+ — — — -+ — — — — — -+
| 3 | [0,0,0,1] |
+ — — — -+ — — — — — -+
这很重要,因为在多类分类中,目标将属于数据中存在的多个类之一。此外,最终的 softmax 图层将返回记录属于这些分类标签的概率。因此,将类标签转换成一个热编码记录是很重要的。在实现中,binarize函数巧妙的利用了 numpy 的广播概念。
我们现在可以看代码了吗?
我在开始时使用了 numpy 的可怕的、臭名昭著的步进函数来创建数组的滑动窗口视图,该视图包含每层中的单元数。使用这个函数的风险更大,因为它在创建视图时不检查给定数组的边界,这很容易导致缓冲区溢出。尽管使用起来风险更大,但我还是用了,因为为什么不呢。在灵巧的手中,它就像一把瑞士军刀。但愿 tensorflow 有类似
as_strided函数的等价东西。
def murals_batch(units_count, x, y, lr, epochs, bias=False, _seed=42):
batch_size, ni = x.shape[-2:]
units_count.insert(0,ni)
units_count_arr = np.array(units_count)
L, = units_count_arr.shape # Number of layers + 1
# RED ALERT - `as_strided` function is like a LAND-MINE ready to explode in wrong hands!
arr_view = as_strided(units_count_arr, shape=(L-1,2), strides=(4,4))
# print(arr_view)
rng = np.random.default_rng(seed=_seed)
wghts = [None]*(L-1)
intercepts = [None]*(L-1)
# WEIGHTS INITIALIZATION
for i in range(L-1):
w_cols, w_rows = arr_view[i,:]
wghts[i] = rng.random((w_rows, w_cols))
if bias:
intercepts[i] = rng.random((w_rows,))
costs = np.zeros(epochs)
# Gradient Descent
for epoch in range(epochs):
# FORWARD PROPAGATION
# hidden layer 1 implementation, relu activation
h1a = np.einsum('hi,Bi -> Bh', wghts[0], x)
if bias:
h1a = h1a + intercepts[0]
h1 = relu(h1a)
# hidden layer 2 implementation, softmax activation
h2a = np.einsum('ho,Bo -> Bh', wghts[1], h1)
if bias:
h2a = h2a + intercepts[1]
hyp = softmax(h2a, _axis=1)
current_epoch_cost = -np.einsum('Bi,Bi', y, np.log(hyp))/batch_size
# print(current_epoch_cost)
costs[epoch] = current_epoch_cost
# BACKWARD PROPAGATION
# layer 2
dJ_dH2a = hyp - y
dJ_dW1 = np.einsum('Bi,Bj -> ij',dJ_dH2a, h1)/batch_size
# layer 1
dJ_dH1 = np.einsum('Bi,ij -> Bj', dJ_dH2a, wghts[1])
dJ_dH1a = dJ_dH1*relu_prime(h1a)
dJ_dW0 = np.einsum('Bi,Bj -> ij',dJ_dH1a, x)/batch_size
if bias:
dJ_dB1 = np.einsum("Bi -> i", dJ_dH2a)/batch_size
dJ_dB0 = np.einsum("Bi -> i",dJ_dH1a)/batch_size
# WEIGHTS ADJUSTMENT
wghts[1] = wghts[1] - lr*dJ_dW1
wghts[0] = wghts[0] - lr*dJ_dW0
if bias:
intercepts[1] = intercepts[1] - lr*dJ_dB1
intercepts[0] = intercepts[0] - lr*dJ_dB0
if bias:
return (costs, wghts, intercepts)
else:
return (costs, wghts)iris = load_iris()
x = iris.data
y = iris.target# NORMALIZE
x_norm = normalize(x)
x_train, x_test, y_train, y_test = train_test_split(x_norm, y, test_size=0.33, shuffle=True, random_state=42)# BINARIZE
y_train = binarize(y_train)
y_test = binarize(y_test)unit_per_layer_counts = [10,3]
costs, fw, fb = murals_batch(unit_per_layer_counts, x_train, y_train, lr=0.01, epochs=19000, bias=True)plt.plot(costs)def predict(x,fw,fb):
h1a = np.einsum(’hi,Bi -> Bh’, fw[0], x)+fb[0]
h1 = relu(h1a)
h2a = np.einsum(’ho,Bo-> Bh’,fw[1],h1)+fb[1]
return softmax(h2a)

分类交叉熵损失随时期的变化(图片归作者所有)
还要看看高效代码执行 19000 个纪元所花费的惊人时间!!!

执行 2 层多类分类神经网络 19000 个时期所花费的时间(图片归作者所有)
这种效率是由于 numpy 的爱因斯坦求和函数用于实现神经网络。我在之前的博客https://manishankar . medium . com/batch-gradient-descent-algorithm-using-numpy-einsum-f 442 ef 798 ee 2中已经详细讨论过了
请让我知道你对这篇博文和代码实现的看法以及你的评论。
我是 TCS 的机器学习工程师,我的(数字软件和解决方案)团队正在开发令人惊叹的产品。点击下面的链接,了解更多关于我们产品的信息:
https://www.tcs.com/dss
使用 Adam 优化器的多类分类神经网络
原文:https://towardsdatascience.com/multiclass-classification-neural-network-using-adam-optimizer-fb9a4d2f73f4?source=collection_archive---------15-----------------------
这是我的博客系列的继续,在我的博客系列中,我使用 numpy 的 einsum 来实现完整的模型。

爱德华·豪厄尔在 Unsplash 上拍摄的照片
我想以一种更实际的方式来看看 Adam 优化器和梯度下降优化器之间的区别。所以我决定改为实现它。
在这里,我采用了虹膜数据集,并实现了一个多类分类 2 层神经网络,就像我在以前的博客中所做的那样。这次唯一不同的是我用了 Adam 优化器,而不是梯度下降。
def leaky_relu(z):
return np.maximum(0.01*z,z)def leaky_relu_prime(z):
z[z<=0] = 0.01
z[z>0] = 1
return zdef softmax(z, _axis=0):
stable_z = z - np.max(z)
e_z = np.exp(stable_z)
return e_z/np.sum(e_z, axis=_axis, keepdims=True)def binarize(z):
return (z[:,None] == np.arange(z.max()+1)).astype(int)def pasapali_batch(units_count, x, y, lr, epochs, bias=False, _seed=42):
beta1 = 0.9
beta2 = 0.999
eps = np.nextafter(0, 1)
batch_size, ni = x.shape[-2:]
units_count.insert(0,ni)
units_count_arr = np.array(units_count)
L, = units_count_arr.shape # Number of layers + 1
# RED ALERT - `as_strided` function is like a LAND-MINE ready to explode in wrong hands!
arr_view = as_strided(units_count_arr, shape=(L-1,2), strides=(4,4))
# print(arr_view)
rng = np.random.default_rng(seed=_seed)
wghts = [None]*(L-1)
intercepts = [None]*(L-1)
M_W = [None]*(L-1)
M_B = [None]*(L-1)
V_W = [None]*(L-1)
V_B = [None]*(L-1)
# WEIGHTS & MOMENTS INITIALIZATION
for i in range(L-1):
w_cols, w_rows = arr_view[i,:]
wghts[i] = rng.random((w_rows, w_cols))
M_W[i] = np.zeros((epochs+1, w_rows, w_cols))
V_W[i] = np.zeros((epochs+1, w_rows, w_cols))
if bias:
intercepts[i] = rng.random((w_rows,))
M_B[i] = np.zeros((epochs+1, w_rows))
V_B[i] = np.zeros((epochs+1, w_rows))
# COSTS INITIALIZATION
costs = np.zeros(epochs)
# Gradient Descent
for epoch in range(epochs):
# FORWARD PROPAGATION
# hidden layer 1 implementation, relu activation
h1a = np.einsum(’hi,Bi -> Bh’, wghts[0], x)
if bias:
h1a = h1a + intercepts[0]
h1 = leaky_relu(h1a)
# hidden layer 2 implementation, softmax activation
h2a = np.einsum(’ho,Bo -> Bh’, wghts[1], h1)
if bias:
h2a = h2a + intercepts[1]
hyp = softmax(h2a, _axis=1)
current_epoch_cost = -np.einsum(’Bi,Bi’, y, np.log(hyp))/batch_size
# print(current_epoch_cost)
costs[epoch] = current_epoch_cost
# BACKWARD PROPAGATION
# layer 2
dJ_dH2a = hyp - y
dJ_dW1 = np.einsum(’Bi,Bj -> ij’,dJ_dH2a, h1)/batch_size
# layer 1
dJ_dH1 = np.einsum(’Bi,ij -> Bj’, dJ_dH2a, wghts[1])
dJ_dH1a = dJ_dH1*leaky_relu_prime(h1a)
dJ_dW0 = np.einsum(’Bi,Bj -> ij’,dJ_dH1a, x)/batch_size
# numerical optimization
beta1_denom = (1.0 - beta1**(epoch+1))
beta2_denom = (1.0 - beta2**(epoch+1))
if bias:
dJ_dB1 = np.einsum("Bi -> i", dJ_dH2a)/batch_size
dJ_dB0 = np.einsum("Bi -> i",dJ_dH1a)/batch_size
# MOMENTS ADJUSTMENT
M_B[0][epoch+1,:] = beta1 * M_B[0][epoch,:] + (1.0 - beta1)*dJ_dB0
M_B[1][epoch+1,:] = beta1 * M_B[1][epoch,:] + (1.0 - beta1)*dJ_dB1
V_B[0][epoch+1,:] = beta2 * V_B[0][epoch,:] + (1.0 - beta2)*dJ_dB0**2
V_B[1][epoch+1,:] = beta2 * V_B[1][epoch,:] + (1.0 - beta2)*dJ_dB1**2
# BIAS CORRECTION
mhat_b0 = M_B[0][epoch+1,:] / beta1_denom
vhat_b0 = V_B[0][epoch+1,:] / beta2_denom
mhat_b1 = M_B[1][epoch+1,:] / beta1_denom
vhat_b1 = V_B[1][epoch+1,:] / beta2_denom
# BIAS ADJUSTMENT with numerical stability
intercepts[1] = intercepts[1] - lr*mhat_b1/(np.sqrt(vhat_b1) + eps)
intercepts[0] = intercepts[0] - lr*mhat_b0/(np.sqrt(vhat_b0) + eps)
# MOMENTS ADJUSTMENT
M_W[0][epoch+1,:] = beta1 * M_W[0][epoch,:] + (1.0 - beta1)*dJ_dW0
M_W[1][epoch+1,:] = beta1 * M_W[1][epoch,:] + (1.0 - beta1)*dJ_dW1
V_W[0][epoch+1,:] = beta2 * V_W[0][epoch,:] + (1.0 - beta2)*dJ_dW0**2
V_W[1][epoch+1,:] = beta2 * V_W[1][epoch,:] + (1.0 - beta2)*dJ_dW1**2
# BIAS CORRECTION
mhat_w0 = M_W[0][epoch+1,:] / beta1_denom
vhat_w0 = V_W[0][epoch+1,:] / beta2_denom
mhat_w1 = M_W[1][epoch+1,:] / beta1_denom
vhat_w1 = V_W[1][epoch+1,:] / beta2_denom
# WEIGHTS ADJUSTMENT with numerical stability
wghts[1] = wghts[1] - lr*mhat_w1/(np.sqrt(vhat_w1) + eps)
wghts[0] = wghts[0] - lr*mhat_w0/(np.sqrt(vhat_w0) + eps) if bias:
return (costs, wghts, intercepts)
else:
return (costs, wghts)iris = load_iris()
x = iris.data
y = iris.target
#NORMALIZE
x_norm = normalize(x)
x_train, x_test, y_train, y_test = train_test_split(x_norm, y, test_size=0.33, shuffle=True, random_state=42)
#BINARIZE
y_train = binarize(y_train)
y_test = binarize(y_test)unit_per_layer_counts = [10,3]
costs, fw, fb = pasapali_batch(unit_per_layer_counts, x_train, y_train, lr=0.01, epochs=200, bias=True)plt.plot(costs)def predict(x,fw,fb):
h1a = np.einsum(’hi,Bi -> Bh’, fw[0], x)+fb[0]
h1 = relu(h1a)
h2a = np.einsum(’ho,Bo-> Bh’,fw[1],h1)+fb[1]
return softmax(h2a)
在代码中,不同之处在于我已经为每一层初始化了两个矩数组,并根据 Adam 优化算法更新了这些矩(或者我应该编写适应的 …)。
在正常的梯度下降优化器中,基于在同一时期中计算的梯度来调整权重。

梯度下降中的权重调整仅取决于当前梯度。—图片归作者所有
在 Adam optimizer 中,权重根据当前和先前时段中计算的梯度移动平均值进行调整。根据 Adam 算法的矩调整被计算为先前和当前梯度的移动平均,然后这些矩被用于更新权重。

Adam 优化器中的权重调整取决于当前和先前的梯度。—图片归作者所有
本文中,β1 = 0.9, m 根据公式更新:

自适应一阶矩公式根据作者拥有的论文图像
让我们对每个时期逐步展开上述公式。

三个历元中一阶矩 m 公式的扩展。—图片归作者所有
我们看到,在每个时期,先前的梯度被包括在更新中,但是分配给远离当前时期梯度的梯度的权重变得越来越小。这有助于向最小值移动,同时抑制搜索最小值时的梯度振荡。这给了我们穿越鞍点的速度。
让我们谈一谈二阶矩 v 。在权重调整期间,学习率除以的均方根v。这有助于调整每个重量的学习率。具有相对较大幅度的权重将具有较大的值 v ,因此在该方向上具有较小的学习步长。这有助于我们放慢速度,这样我们就不会超过最小值。
最后,我们来谈谈偏差校正部分。在原始论文中,他们给出了数学推导并给出了解释。对于门外汉来说,知道引入这种偏差校正在梯度稀疏时有所帮助就足够了,如果不进行校正会导致较大的阶跃。
让我们比较一下梯度下降优化器和 Adam 优化器方法中成本函数的收敛性。

使用梯度下降的成本函数衰减—图片归作者所有

使用 Adam optimizer 的成本函数衰减—图片归作者所有
Adam optimizer 仅用了 250 个历元就达到了最佳成本值,而梯度下降则用了 19000 个历元。让我想起了超级英雄闪电侠!!

梯度下降优化器完成 19000 个历元所用的时间为 2.98 秒-图片归作者所有

Adam optimizer 完成 250 个时期所用的时间大约为 87.3 毫秒—图片归作者所有
这是收敛速度的巨大进步。亚当不仅速度快。它也适用于稀疏和非常嘈杂的渐变。
请让我知道你对这篇博文和代码实现的看法以及你的评论。
我是 TCS 的机器学习工程师,我的(数字软件和解决方案)团队正在开发令人惊叹的产品。
从零开始的多类逻辑回归
原文:https://towardsdatascience.com/multiclass-logistic-regression-from-scratch-9cc0007da372?source=collection_archive---------6-----------------------
数学和梯度下降用 Python 实现

照片由 Amy Shamblen 在 Unsplash 上拍摄
多类逻辑回归也称为多项逻辑回归和 softmax 回归。当我们想要预测两个以上的类时,就使用它。很多人一直在使用多类逻辑回归,但并不真正知道它是如何工作的。所以,我将向你们展示数学是如何工作的,并使用 Python 中的梯度下降从头开始实现它。
免责声明:关于这个主题有各种各样的符号。我使用了我认为容易理解和形象化的符号。你可以在其他地方找到其他的符号,比如矩阵和向量被转置。
问题陈述

作者图片
假设我们有 N 个人/观测值,每个人有 M 个特征,他们属于 C 类。我们被给予:
- 矩阵𝑋是ℝ𝑁×𝑀.𝑋𝑖𝑗代表具有特征 j 的人 I
- 𝑌是ℝ𝑁.的一个载体属于 k 类的𝑌𝑖represents 人 I
我们不知道:
- 权重矩阵𝑊是ℝ𝑀×𝐶.𝑊𝑗𝑘表示特征 j 和类别 k 的权重
我们想找出𝑊,并使用𝑊来预测任何给定的观察值 x 的类成员
多类逻辑回归工作流
如果我们知道𝑋和𝑊(比如说我们给𝑊的初始值都是 0),图 1 显示了多类逻辑回归正向路径的工作流程。
- 首先,我们计算𝑋和𝑊的乘积,这里我们让𝑍=−𝑋𝑊.
有时人们在这里不包括负号。这里有没有负号并不重要。
有时我们还会添加一个偏差项。为简单起见,我们只看本文中的权重。
- 第二,我们取每行𝑍𝑖: 𝑃𝑖=softmax(𝑍𝑖)=𝑒𝑥𝑝(𝑍𝑖)/∑𝑒𝑥𝑝(𝑍𝑖𝑘).的 softmax𝑍𝑖的每一行应该是𝑋(i.e.、𝑋𝑖的每一行和𝑊.的整个矩阵的乘积现在𝑃的每一行加起来应该是 1。
- 第三,我们获取每行的 argmax,并找到概率最高的类。

*图一。多类逻辑回归正向路径(*图片由作者提供)
当我们一次只看一个观察值时,图 2 显示了多类逻辑回归前向路径的另一个视图:
- 首先,我们计算𝑋𝑖和 w 的乘积,这里我们让𝑍𝑖=−𝑋𝑖𝑊.
- 其次,我们对这一行𝑍𝑖: 𝑃𝑖=softmax(𝑍𝑖)=𝑒𝑥𝑝(𝑍𝑖)/∑𝑒𝑥𝑝(𝑍𝑖𝑘).取软最大值
- 第三,我们取这一行𝑃𝑖的 argmax,并找到概率最高的索引作为𝑌𝑖.

*图二。一行上的操作。(*图片作者)
可能性
回想一下,在问题陈述中,我们说我们得到了𝑌.所以对于一个给定的观察,我们知道这个观察的类别,这就是𝑌𝑖.𝑌𝑖给定𝑋𝑖和𝑊的似然函数是观测值 I 和𝑘=𝑌𝑖类的概率,是𝑍𝑖,𝑘=𝑌𝑖.的 softmax 而𝑌给出的𝑋和𝑊的似然函数是所有观测的产物。图 3 帮助我们理解这个从𝑌𝑖追溯到𝑊𝑘=𝑌𝑖.的过程

*图 3。计算可能性。(*图片由作者提供)
损失函数
接下来,我们计算损失函数。我们使用负对数似然函数,并根据样本大小将其归一化。这里需要注意的一点是

𝑇r 意味着主对角线上元素的总和。图 3 显示了这种计算。

*图四。矩阵计算。(*图片由作者提供)

我们经常在损失函数中加入一个𝑙2 正则项,并试图最小化组合函数。事实上,scikit-learn 的缺省设置使用了𝑙2 惩罚。𝑙1 正则化也是非常常用的。这里我们使用𝑙2 正则化。

梯度下降实现
现在我们已经计算了损失函数和梯度函数。我们可以用 Python 实现 loss 和 gradient 函数,实现一个非常基本的梯度下降算法。
我们在 iris 数据集上尝试了我们的模型。我们拟合模型,然后绘制损失与步骤的关系,我们看到损失函数随着时间的推移而下降。当我们查看我们数据的预测时,我们看到该算法正确地预测了大多数类。

这是多类逻辑回归的数学和梯度下降的基本概述。希望这篇文章对你有所帮助。
作者索菲亚·杨 2021 年 4 月 18 日
多维排列
多维标度的理论与应用。在 r 中有一个完整的例子。

多维缩放—照片由 Unsplash 上的石罩拍摄
多维排列
多维标度是一系列统计方法,侧重于根据距离创建项目映射。在多维标度中,不同类型的数据有不同的方法:
- 公制多维标度,也称为主坐标分析,是多维标度的一个子类型,处理数值距离,其中没有测量误差(每对项目正好有一个距离测量)。
- 非度量多维标度是多维标度的一个子类,处理项目之间的非数字距离,其中没有测量误差(每对项目只有一个距离度量)。
- 个体差异标度是一种多维标度,当您对项目之间的距离有多个(不同)估计值时适用。当多个个体各自给出所有对之间的距离的估计时,通常就是这种情况。
- 偏好的多维分析是一种多维标度,适用于您对项目之间的距离有多种(不同)估计的情况。该方法不适用于距离矩阵,而是适用于排名数据*(多个参与者各自从最好到最差排列项目)。*
多维标度最有趣的应用(从统计角度来说)是那些让多个参与者给出(稍微或非常)不同估计的应用。因此,我将在度量和非度量多维标度上讲得快一点,并在个体差异标度和偏好的多维分析上多花一点时间。
度量多维标度
第一类多维标度是度量多维标度,也称为主坐标分析。这种方法可以归结为根据观察值之间的距离来创建观察值的映射。
您可以将 Metric dimensional Scaling 视为一种数据可视化方法:输入一定数量的数据点之间的距离矩阵,该方法将输出一个显示这些观察结果的图表。
度量多维标度通常用于感知映射(基于不同于通常的距离度量创建地图)和产品开发。
数学上,度量多维缩放将输入距离矩阵转换为双中心距离矩阵,然后应用奇异值分解。由于这种方法中的统计分析非常简单,我更愿意链接到一篇深入介绍度量多维扩展的文章。
非度量多维标度
我也将简短介绍非度量多维标度,因为它与度量多维标度非常相似。公制和非公制 MDS 的目标完全相同。
区别在于数据的测量尺度。度量多维标度仅适用于间隔标度数据,而您需要非度量多维标度用于序数数据。由于多维标度经常应用于市场营销、心理学等领域,因此它经常受到序数数据的影响,如表达配对之间偏好的数据,或 1-7 个测量标度的数据。
在非度量多维标度中,您从相异矩阵而不是距离矩阵开始。当然,这两者背后的思想是相同的,除了不相似性不能用于计算事物。为了说明这一点,假设我说两种产品非常不同。我可以说他们的相似度是 1 到 10 分。然而,这并不意味着相似度为 2 的一对产品是第一对数据的两倍!
为了对这种类型的数据进行 MDS,我们需要一种不同的方法:我们将使用迭代优化方法和成本函数。我们从在地图上随机放置数据点开始。然后我们计算压力:数据点的原始相异点与它们在地图中的相异点之间的差异。最后,我们使用迭代优化算法来寻找最小化该压力的映射。

多维标度—应力公式
非度量多维标度是一个很好的数据可视化工具,可以让你做一些有趣的用例。在这篇文章中,您可以了解适合的用例类型。
前两种多维标度方法的一个大缺点是它们都从 1 个距离/相异矩阵开始。在现实生活的数据分析中,对距离进行多次估计要常见得多。例如,在一项产品开发研究中,你问人们如何看待你的产品,你可以很容易地采访 100 个人,最后得到 100 个不同的相异矩阵!
在本文接下来的部分,您将发现两种方法,它们允许您对产品的相似性以及个人观点和评估的差异进行建模。
个体差异量表
我们现在开始第三种多维标度方法,称为个体差异标度。这种方法在模型中加入了人类的异质性。这使得模型更加现实,因为人类并不以同样的方式看待世界。数据的变化往往比一般平均值更重要。
个体差异衡量是一个具有三个主要映射的模型:
- 一张个人感知图,用于研究中的每个个体参与者
- 组刺激空间是代表“最终”地图上产品的映射
- 主题空间具有一组用于每个个体的权重,其将每个个体映射转换成组刺激空间映射。
这意味着该模型确实允许人们对现实有不同的感知(他们每个人仍然有自己的地图),并且它将这些不同的感知转换成对总体感知的估计。
在以前的方法中,我们必须忽略人们意见不同的事实,并且我们必须定义一个单一的相异度矩阵。接受个体差异使得个体差异衡量更接近现实。
个体差异量表示例
在这个例子中,我们将使用 10 种麦当劳产品之间不同之处的模拟数据集。我们模拟了 5 个人的相异矩阵。分析的目标是:
- 创建群体刺激空间,以更好地了解产品的相似性
- 创建主题空间,了解人与人之间的差异
R 中的个体差异标度
让我们开始研究这个例子。我们将有 5 个不同的矩阵作为输入。五个相异矩阵已经被连接在一个数据文件中。您可以使用以下代码从我的 AWS S3 桶中获取这些数据:
多维缩放-导入数据
数据将如下所示:

多维标度—数据集
要使用 R 中的单个缩放函数,首先需要将这些数据转换成距离矩阵列表。您可以这样做:
多维标度-距离矩阵列表
现在,我们将使用smacof包来拟合个体差异缩放模型:
多维标度—拟合 indscal 模型
个体差异标度最有趣的地方是前面提到的图:
- 理解对产品相似性的总体认知的群体空间
- 理解为什么参与者对你的产品有这种感觉的主题空间。
群体空间
让我们从生成组空间开始。组空间是由 smacof 的 indscal 函数输出的标准图,如下所示:
多维标度—绘制组空间

多维标度—组空间
在这个群组空间中,您会看到一些非常有趣的麦当劳产品集群:
- 产品 3(巨无霸)、8(四分之一磅)和 2(三层芝士汉堡)
- 产品 9(双层芝士汉堡)和产品 10(双层麦当劳)
- 产品 7(烧烤牧场汉堡)和 1(汉堡)
- 产品 5(麦乐鸡)、4(麦香鸡)和 6(菲力鱼)
这是一个很好的分组,因为它很容易解释。似乎是:
- 第一组包含了最大的汉堡**
- 第二组包含双层汉堡**
- 第三组包含单个汉堡**
- 第四组包含不同动物的汉堡(鸡肉和鱼肉)**
我们可以使用这种聚类来解释已经创建的两个维度*:***
- 尺寸 1 从左边的大汉堡,到中间的双层汉堡,再到右边的单层汉堡。维度 1 明确代表 汉堡大小。
- Dimension 2 ranges 底部有非牛汉堡(低分),高分有牛汉堡。维 2 明确区分了牛肉和非牛肉。
主题空间
现在,对于个体差异衡量的第二个主图表,我们将着眼于主题空间。您可以使用以下代码来获取主题空间:
多维标度——主题空间
您将获得下图:

多维标度——主题空间
有趣的是,在主题空间中,有两个非常明显的集群:
- ***个体 1、2 和 3 在维度 2 上非常**高,在维度 1 上非常**低。*******
- 个体 4、5 和 6 是相反的:在维度 2 上非常低并且在维度 1 上非常高*。*******
我们由此得出的结论非常有趣。我们已经发现区分麦当劳产品的两个重要方面是大小(维度 1)和肉的类型(维度 2)。
主题空间给了我们额外的结论:有些人几乎只根据大小来区分麦当劳产品,而有些人几乎只根据肉的种类来区分。
总之,个体差异量表让我们深入了解产品之间的相似性,并解释了区分产品的因素。最重要的是,它允许我们对个人偏好建模,并理解人类感知的差异。这才是个体差异缩放的真正附加值!
偏好的多维分析
我将介绍的最后一种多维标度方法是偏好的多维分析。该方法还搜索表示二维平面上的项目的映射。和前面的方法一样,当有多人给你的项目评分时,也可以使用这种方法。然而,最大的区别在于,它对排名数据起作用,而不是对相异度的估计。
R 中偏好的多维分析
对于这个分析,我们需要一个等级数据集。我们将使用六名巴黎迪士尼乐园游客的模拟数据集,他们根据偏好对 14 个景点进行排名(1 个是最高偏好,14 个是最低偏好)。您可以使用以下代码导入数据:
多维缩放-导入数据
您将获得以下数据帧:

多维标度—数据集
这个数据还不能用于 R 的mdprep函数。您需要从数据框中移除第一列。您还需要转置数据:该函数期望“法官”在行中,项目在列中。您还需要添加一个名为“n”的列。只有当很多人给出完全相同的排名时,这个专栏才有用。如果是这种情况,您可以只指定一次该行,但是将值放在“n”中,作为它出现的次数。在我们的例子中,它们都恰好出现一次。
多维标度—准备数据
现在数据已经准备好了,我们可以继续使用pmr包来安装mdpref模型。您可以使用以下代码来实现这一点:
多维标度—拟合 mdpref 模型
拟合模型的内容将是items中每个项目的新坐标,ranking包含最后两列中裁判的新坐标。explain告诉我们该模型使用 2 个维度而不是全部数据解释了多少:我们看到 0.66 (66%),对于只有 6 个人的 14 个项目来说,这不是很大,但也不算太坏。

多维缩放-模型内容
下一步是最重要的:创建巴黎迪士尼乐园景点的地图。您可以使用以下代码创建它:
多维标度-绘制景点
您将获得下图:

多维扩展——吸引力
这张图表向我们展示了一些深刻的认识。首先,我们获得几组吸引力:
- 左上组:小世界、巴斯光年、白雪公主和小飞象
- ****右上组:料理鼠王、遥控赛车、玩具士兵、碾压
- ****底部中间组:奇兵、大雷山、星际之旅、超空间山
- ****局外人:恐怖之塔
了解了这些吸引人的地方,我们可以将这些群体解释如下:
- ****左上组:魔法王国 parc 的儿童景点
- ****右上组:华特·迪士尼影城公园的儿童景点
- ****底部中间组:魔幻王国公园的精彩景点
- ****恐怖之塔:华特·迪士尼影城帕洛阿尔托公园中唯一的强烈吸引力(数据集中唯一的一个)
这直接让我们能够解释我们的维度测量了什么:
- Dimension 1 左侧显示魔法王国景点,右侧显示华特·迪士尼影城景点。尺寸代表景点所在的 parc。**
- 维度 2 在顶部显示缓慢的吸引力,在底部显示强烈的吸引力。尺寸代表吸引力的缓慢度。**
由于这种分析,我们可以清楚地说,这两个维度是游客吸引力偏好的重要变量。
现在我们来看看各个评委(嘉宾)的评分有多大的不同。我们可以使用以下代码创建绘图:
多维标度——描绘个体
该图将如下所示:

多维尺度——个体
我们看到没有超级明显的客人群体。这意味着一些客人倾向于根据位置进行选择,一些客人倾向于根据强度进行选择,但在这两者之间没有一般的赢家:它们都很重要。
多维标度的结论
在本文中,您已经看到了多维伸缩的不同方法的概述。您首先看到了度量和非度量维度缩放,之后,您看到了两个深入的个体差异缩放和偏好多维分析的代码示例。
多维扩展是一系列方法,不同的用例需要不同的方法。我希望这篇文章能为您提供必要的信息,帮助您找到正确的方法,并在实践中将其应用于您的数据集。
感谢您的阅读,请不要犹豫,继续关注更多数学、统计和数据科学内容!
用于降维和数据可视化的多维标度(MDS)
通过 python 实现使用不同的距离方法解释和再现多维标度(MDS)
降维方法允许根据具有许多特征的数据集中的各种参数(例如相关性、距离、方差)之间的关系,在另一个轴上检查数据集。在这个阶段之后,可以使用监督或无监督学习方法轻松地对数据集执行分类等操作。此外,如果我们考虑具有 30 个特征的数据集,而不是进行 30 维可视化,则在可视化方面,根据它们之间的各种因素从不同方面考虑 30 个特征并使它们二维化会更容易。可以从不同的方面考虑降维方法对数据集的处理,即通过使用傅立叶变换将时间轴中给出的信号变换到频率轴,并有效地处理信号。本文从理论层面讨论了作为降维和可视化方法之一的多维尺度变换的各种类型,并介绍了它的应用领域。python 实现丰富了研究。
***Table of Contents* 1\. What is Multi-Dimensional Scaling?
2\. Goodness of fit — Stress —
3\. PCA vs MDS
4\. Different Distance Approaches on image dataset
- Euclidean Distance
- Manhattan Distance
- Chebyshev Distance
- Minkowski Distance
5\. Tutorials
- S curve
- Digits Dataset
6\. Metric MDS and Non-Metric MDS
7\. References**

索尔·阿尔维斯在 Unsplash上的照片
1.什么是多维标度?
多维缩放是对象集之间的距离或差异的可视化表示。[1]“对象”可以是颜色、面孔、地图坐标、政治信仰,也可以是任何一种真实的或概念上的刺激[2]。除了将差异解释为图上的距离,MDS 还可以作为高维数据的降维技术[3]。简而言之,MDS 的主要目的是在降维后保持这些不同。
在 Sklearn 库中提供的 MDS 中,距离默认设置为欧几里德距离。此外,可以调整和使用其他距离,如曼哈顿(见第 4 节)。

图一。数据集,按作者分类的图像
在下面的代码块中,使用图 1 和图 2(左)中所示的城市之间的航空公司距离创建了一个 6 x 6 数据集,并应用了 MDS sklearn 库中的基础版本。

图二。地图(左),代码块的输出(右),图片作者
结果如图 2(右)所示。另外,应力值为0.188。在进入压力的话题之前,让我们讨论一下得到的结果。最初,我们有一个 6×6 的矩阵,通过应用 MDS 将数据集的维数减少到 6×2,然后将其可视化。
如果考虑坐标平面,则(x,y)点的位置由参考(0,0)原点确定。任何(x,y)点相对于(0,0)点定位。另一方面,在 MDS,每个列对都是用指定的距离类型计算的。这就是为什么与 MDS 保持距离。当我们看图 2 时,可以看到埃尔祖鲁姆是离其他城市最远的。同样,在应用 MDS 后,彼此靠近的城市的位置也很接近,因此当我们查看 MDS 的结果时,会遇到类似的情况。从不同的角度查看数据集,同时保持数据之间的距离关系。
2.契合度——压力——
需要一个特定的表达式来确定在数据分析应用中需要降维到什么程度。在 PCA 中,累积方差是通过绘制一个 scree 图来确定的。在 MDS,距离是模型化的。因此,MDS 的最佳选择是基于实际距离与其估计值之间的差异。这种方法被称为压力。上面示例中绘制应力图的代码块及其输出如下:

图 3。代码块的输出-重音-,作者图像
结果如图 3 所示。在关于 MDS 的原始论文中,Kruskal (1964)根据他的经验给出了以下关于压力值的建议[4]:
0.2 – poor
0.1 – fair
0.05 – good
0.025 – excellent
0 – perfect
如今,学术研究认为,根据数据集的规模和数据的质量来遵循这一表格会产生误导。
3.PCA vs MDS
通过考虑 PCA 中的方差-相关值来执行通过保持 MDS 中的距离来执行的过程。使用欧几里德距离最小化线性距离类似于最大化线性相关性。因此,可以说 PCA 和 MDS 应用数据集的 2D 图形具有相似的特征。当然,这仅适用于使用欧几里得距离的 MDS。此外,可以根据项目应用不同的距离方法。(例如,对于大型数据集,欧几里德距离可能很弱。)PCA 已经应用于上面代码块中的数据集,结果如图 4 所示。

图 4。PCA 降维,作者降维
4.不同的距离方法
已经提到过,在 Sklearn 库中默认使用欧几里德距离。此外,通过设置dissimilarities = “precomputed”可以使用各种距离。在下面的代码块中,MDS 被应用于 sklearn 库中不同距离的fetch_olivetti_faces数据集,并在 2D 可视化。
欧几里德距离

图 5。MDS 使用欧几里德距离降低了维数,作者使用图像
曼哈顿距离

图 6。通过 MDS 和曼哈顿距离降低维度,通过作者降低图像维度
切比雪夫距离

图 7。通过 MDS 和切比雪夫距离降低维数,图片由作者提供
闵可夫斯基距离

图 8。MDS 使用闵可夫斯基距离降低维度,作者使用图像
查看上面的图像,可以看到每个结果都是根据基于距离的不同特征形成的。应该根据进行选择时所使用的数据集的结构来选择。例如,研究基因的生物学家应该选择和使用对数倍数变化,因为他们对基因的对数倍数变化感兴趣。
5.教程
S 曲线
3D S 曲线导入到下面的代码块中。它被渲染成 2D,并用主成分分析和 MDS 可视化。结果如图 9 所示。

图 9。MDS 的 S 曲线(左),PCA 的 S 曲线(右),图片作者
如果在项目中使用机器学习对数据集进行分类,那么在使用各种非监督学习方法或监督学习方法应用 MDS 后,分类过程可以很容易地完成。
数字
导入 Digits 数据集的前 5 个标签,即 0,1,2,3,4。通过分别应用 MDS 和 PCA 过程,数据集的形状从 901 x 64 转换为 901 x 2。然后进行可视化,结果如图 10 所示。

图 10。数字数据集的维数分别通过 MDS(左)和主成分分析(右)降低,图像通过作者
经过 MDS 过程,可以看出,特别是 2。第三。与 PCA 相比,组形成在更好的簇中。在这个阶段之后,各种机器学习过程的应用将给出有效的结果。
6.公制 MDS 和非公制 MDS
到目前为止,它一直专注于度量(经典)多维标度,也称为主坐标分析(PCoA)。在这种方法中,通过考虑特征之间的距离进行降维,并进行可视化。
非公制 MDS 适用于序数数据集。比如在市场调研时收集的调查数据中,比方说 10 以上的 X 牌车会给多少分。在这里,8 分意味着质量比 3 分好。标签 0–1–2–3–4 被用在上面的数字数据集中,但是两者都没有优势。在另一个例子中,当精神病患者被要求评价他们的情绪时,高分和低分意味着不同的东西。
简而言之,虽然度量 MDS 显示了线性关系,但非度量 MDS(也称为有序 MDS)是由一组仅取决于等级值的曲线来描述的。[5]通过设置metric = False,它可以用作 Sklearn 库中的非公制 MDS。
7.参考
[1]“多维标度:定义、概述、示例—统计方法。”https://www.statisticshowto.com/multidimensional-scaling/(2021 年 10 月 14 日访问)。
[2]“多维标度——约瑟夫·b·克鲁斯卡尔,米隆·威希——谷歌图书。”https://books . Google . de/books/about/多维 _Scaling.html?id = iTydugEACAAJ&redir _ ESC = y(2021 年 10 月 13 日访问)。
[3] A. Buja,D. F. Swayne,M. L. Littman,N. Dean,H. Hofmann 和 L. Chen,“多维标度下的数据可视化”,计算机学报图表。统计。,第 17 卷,第 2 期,第 444–472 页,2008 年 6 月,doi: 10.1198/106186008X318440。
[4] NCSS 有限责任公司,“435–1 多维标度”
[5]“多维标度:定义、概述、示例—统计方法。”https://www.statisticshowto.com/multidimensional-scaling/(2021 年 10 月 14 日访问)。
https://ibrahimkovan.medium.com/machine-learning-guideline-959da5c6f73d
多层感知器用一个真实的例子和 Python 代码解释:情感分析
原文:https://towardsdatascience.com/multilayer-perceptron-explained-with-a-real-life-example-and-python-code-sentiment-analysis-cb408ee93141?source=collection_archive---------0-----------------------
多层感知器是一种学习线性和非线性数据之间关系的神经网络

作者图片
这是致力于深度学习系列的第一篇文章,深度学习是一组可以追溯到 20 世纪 40 年代的机器学习方法。深度学习在过去几十年中因其在图像分类、语音识别和机器翻译等领域的开创性应用而受到关注。
如果你想看用现实生活中的例子和一些 Python 代码解释不同的深度学习算法,请继续关注。
这一系列文章重点关注深度学习 算法,这在过去几年中受到了很多关注,因为它的许多应用在我们的日常生活中占据了中心位置。从自动驾驶汽车到语音助手,人脸识别或者将语音转录为文本的能力。
这些应用只是冰山一角。自 20 世纪 40 年代初以来,研究和增量应用的漫长道路已经铺就。我们今天看到的改进和广泛应用是硬件和数据可用性赶上这些复杂方法的计算需求的顶点。
为什么深度学习正在改变游戏规则
在传统的机器学习中,任何建立模型的人要么必须是他们正在研究的问题领域的专家,要么与一个专家合作。如果没有这方面的专业知识,设计和工程特征将成为一个越来越困难的挑战[1]。机器学习模型的质量取决于数据集的质量,但也取决于特征对数据中模式的编码程度。
深度学习算法使用人工神经网络作为其主要结构。使它们区别于其他算法的是,它们在特征设计和工程阶段不需要专家的输入。神经网络可以学习数据的特征。
深度学习算法接受数据集,学习它的模式,它们学习如何用它们自己提取的特征来表示数据。然后,他们将数据集的不同表示组合成一个更抽象、更高级的数据集表示,每种表示都标识一个特定的模式或特征[1]。这种放手的方法,在特征设计和提取中没有太多的人工干预,允许算法更快地适应手边的数据[2]。
神经网络受大脑结构的启发,但不一定是大脑结构的精确模型。关于大脑及其工作方式,我们仍有许多不了解的地方,但由于其开发智力的能力,它一直是许多科学领域的灵感来源。尽管有神经网络是为了理解大脑如何工作而创建的,但我们今天所知的深度学习并不是为了复制大脑如何工作。相反,深度学习专注于启用学习多层次模式构成的系统[1]。
而且,正如任何科学进步一样,深度学习并不是从你在最近的文献中看到的复杂结构和广泛应用开始的。
这一切都始于一个基本结构,一个类似大脑的神经元的结构。
这一切都始于一个神经元
20 世纪 40 年代早期,神经生理学家沃伦·麦卡洛克和逻辑学家沃尔特·皮茨合作创建了一个大脑工作模型。这是一个简单的线性模型,在给定一组输入和权重的情况下,产生正或负的输出。

麦卡洛克和皮茨神经元模型。(图片由作者提供)
这种计算模型被有意称为神经元,因为它试图模仿大脑核心构建模块的工作方式。就像大脑神经元接收电信号一样,麦卡洛克和皮茨的神经元接收输入,如果这些信号足够强,就将它们传递给其他神经元。

神经元及其不同的组成部分。(图片来源)
神经元的第一个应用复制了一个逻辑门,其中有一个或两个二进制输入,以及一个布尔函数,该函数只有在给定正确的输入和权重时才会被激活。

AND 和 OR 逻辑门的例子(图片由作者提供)。
然而,这个模型有一个问题。它无法像大脑一样学习。获得期望输出的唯一方法是预先设置权重,权重在模型中起催化剂的作用。
神经系统是一个神经元网络,每个神经元都有一个胞体和一个轴突[……]在任何时刻,神经元都有一个阈值,兴奋必须超过该阈值才能启动冲动[3]。
仅仅十年后,Frank Rosenblatt 扩展了这个模型,并创造了一种算法,可以让 T2 学习权重以产生输出。
罗森布拉特以麦卡洛克和皮特的神经元为基础,开发了感知机。
感知器
虽然今天感知器被广泛认为是一种算法,但它最初是作为一种图像识别机器。它因执行类似人类的感知功能,看到并识别图像而得名。
特别是,人们的兴趣集中在一种机器的想法上,这种机器能够将直接来自光、声音、温度等物理环境的输入概念化。—我们都熟悉的“现象世界”——而不是需要人类代理的干预来消化和编码必要的信息。[4]
罗森布拉特的感知机依赖于一个基本的计算单元,神经元。就像以前的模型一样,每个神经元都有一个接收一系列输入和权重对的细胞。
Rosenblatt 模型的主要区别在于,输入被组合成一个 加权和,如果加权和超过一个预定义的阈值,神经元就会触发并产生一个输出。

感知器神经元模型(左)和阈值逻辑(右)。(图片由作者提供)
阈值 T 代表激活功能。如果输入的加权和大于零,神经元输出值 1,否则输出值为零。
二元分类感知器
有了这个由激活函数控制的离散输出,感知器可以用作二元分类模型,定义了线性决策边界。它找到分离的超平面,该超平面最小化错误分类的点和决策边界之间的距离【6】。

感知器的损失函数。(图片由作者提供)
为了最小化这个距离,感知器使用随机梯度下降作为优化函数。
如果数据是线性可分的,则保证随机梯度下降将在有限步内收敛。
感知器需要的最后一块是激活函数,这个函数决定神经元是否会触发。
最初的感知器模型使用了 sigmoid 函数,仅仅看它的形状,就很有道理!
sigmoid 函数将任何实数输入映射到值 0 或 1,并对非线性函数进行编码。
神经元可以接收负数作为输入,它仍然能够产生 0 或 1 的输出。

Sigmoid 函数(图片由作者提供)。
但是,如果你看看过去十年的深度学习论文和算法,你会发现它们大多数都使用校正线性单元(ReLU) 作为神经元的激活函数。

ReLU 函数。(图片由作者提供)
ReLU 越来越被采用的原因是它允许使用随机梯度下降进行更好的优化、更高效的计算并且是比例不变的,这意味着它的特征不受输入比例的影响。
把所有的放在一起
神经元接收输入并随机选取一组初始权重。这些组合成加权和,然后由激活函数 ReLU 确定输出值。

感知器神经元模型(左)和激活函数(右)。(图片由作者提供)
但是你可能想知道,感知器实际上不学习权重吗?
确实如此。感知器使用随机梯度下降来寻找,或者你可以说学习,最小化错误分类点和决策边界之间距离的权重集。一旦随机梯度下降收敛,数据集被一个线性超平面分成两个区域。
虽然据说感知器可以代表任何电路和逻辑,但最大的批评是它不能代表异或门、异或,如果输入不同,该门只返回 1 。
近十年后,Minsky 和 Papert 在 1969 年[5]证明了这一点,并强调了只有一个神经元的感知器不能应用于非线性数据的事实。
多层感知器
多层感知器就是为了解决这个限制而开发的。这是一个神经网络,其中输入和输出之间的映射是非线性的。
多层感知器具有输入和输出层,以及一个或多个隐藏层,其中许多神经元堆叠在一起。而在感知器中,神经元必须有一个施加阈值的激活函数,如 ReLU 或 sigmoid,多层感知器中的神经元可以使用任何任意的激活函数。

多层感知器。(图片由作者提供)
多层感知器属于前馈算法的范畴,因为输入在加权和中与初始权重相结合,并服从激活函数,就像在感知器中一样。但不同的是,每个线性组合都传播到下一层。
每一层都由向下一层提供计算结果,即数据的内部表示。这一直通过隐藏层到达输出层。
但是它有更多的含义。
如果算法只计算每个神经元中的加权和,将结果传播到输出层,并在那里停止,它将无法学习最小化成本函数的权重。如果算法只计算一次迭代,就不会有实际的学习。
这就是 反向传播【7】发挥作用的地方。
反向传播
反向传播是一种学习机制,允许多层感知器迭代调整网络中的权重,目标是最小化成本函数。
反向传播要正常工作有一个硬性要求。结合神经元中的输入和权重的函数(例如加权和)和阈值函数(例如 ReLU)必须是可微分的。这些函数必须有一个有界导数,因为梯度下降通常是多层感知器中使用的优化函数。

多层感知器,强调前馈和反向传播步骤。(图片由作者提供)
在每次迭代中,在通过所有层转发加权和之后,计算所有输入和输出对的均方误差的梯度。然后,为了将它传播回去,第一个隐藏层的权重用渐变的值更新。这就是权重如何传播回神经网络的起始点!

梯度下降的一次迭代。(图片由作者提供)
这个过程一直持续到每个输入-输出对的梯度收敛,这意味着与前一次迭代相比,新计算的梯度变化不超过指定的收敛阈值。
让我们用一个真实世界的例子来看看这一点。
使用感知器进行情感分析
你的父母在乡下有一个舒适的床和早餐,大厅里有传统的留言簿。欢迎每位客人在离开前写一张便条,到目前为止,很少有人离开时没有写一张短笺或励志名言。有些人甚至留下了家里的小狗莫莉的画像。
夏季即将结束,这意味着在假期工作开始前的清洁时间。在旧储藏室里,你偶然发现了一个盒子,里面装满了你父母多年来保存的留言簿。你的第一直觉?让我们什么都看!
读了几页后,你就有了更好的想法。为什么不试着了解客人留下的是积极的还是消极的信息?
你是数据科学家,所以这是二进制分类器的完美任务。
所以你随机挑选了一些留言簿,用作训练集,转录所有的信息,给它一个积极或消极情绪的分类,然后让你的堂兄弟也把它们分类。
在自然语言处理任务中,一些文本可能是不明确的,所以通常你有一个文本语料库,其中的标签是由 3 个专家同意的,以避免联系。

来宾留言示例。(图片由作者提供)
将最终的标签分配给整个语料库后,你决定将数据放入一个最简单的神经网络感知器。
但是在建立模型之前,你需要把自由文本转换成机器学习模型可以处理的格式。
在本例中,您使用术语频率—逆文档频率(TF-IDF) 将留言簿中的文本表示为一个向量。这种方法将任何类型的文本编码为每个单词或术语在每个句子和整个文档中出现频率的统计数据。
在 Python 中,你使用了 ScikitLearn 中的 TfidfVectorizer 方法,删除了英文停用词,甚至应用了 L1 规范化。
TfidfVectorizer(stop_words='english', lowercase=True, norm='l1')
用感知器进行二元分类!
为了实现这一点,您完全使用了感知器开箱即用和所有默认参数。

Python 源代码在语料库上运行感知器。(图片由作者提供)
在对语料库进行矢量化、拟合模型并对模型从未见过的句子进行测试后,您会发现该模型的平均准确率为 67%。

感知器模型的平均精确度。(图片由作者提供)
对于感知器这样简单的神经网络来说,这已经不错了!
平均而言,感知器会将你父母的客人写的每三条信息中的大约一条错误分类。这让你想知道,也许这些数据不是线性可分的,你也可以用稍微复杂一点的神经网络来获得更好的结果。
在这种情况下,多层感知器会如何表现?
使用 SckitLearn 的多层感知器,你决定保持简单,只调整几个参数:
- 激活函数:ReLU,用参数 activation='relu' 指定
- 优化函数:随机梯度下降,由参数 solver='sgd' 指定
- 学习率:反比例,由参数*learning _ rate =‘inv Scaling’*指定
- 迭代次数:20,由参数 max_iter=20 指定

Python 源代码在语料库上运行多层感知器。(图片由作者提供)
默认情况下,多层感知器有三个隐藏层,但是您想要了解每层中的神经元数量如何影响性能,所以您从每个隐藏层 2 个神经元开始,设置参数 num_neurons=2。
最后,为了查看每次迭代中损失函数的值,您还添加了参数 verbose=True。

具有 3 个隐藏层,每个隐藏层有 2 个节点的多层感知器模型的平均精度。(图片由作者提供)
在这种情况下,多层感知器有 3 个隐藏层,每个层有 2 个节点,性能比简单感知器差得多。
它在 24 次迭代中收敛得相对较快,但是平均精度不好。
当感知器平均每 3 个句子中有 1 个错误分类时,这个多层感知器是相反的,平均每 3 个句子中预测正确的标签 1。
如果你给神经网络增加更多的容量会怎么样?当每个隐藏层有更多的神经元来学习数据集的模式时,会发生什么?
使用同样的方法,你可以简单地改变参数 num_neurons 并将其设置为 5。
buildMLPerceptron(train_features, test_features, train_targets, test_targets, num_neurons=5)
向隐藏层添加更多的神经元无疑提高了模型的准确性!

具有 3 个隐藏层,每个隐藏层有 5 个节点的多层感知器模型的平均精度。(图片由作者提供)
您保持了相同的神经网络结构,3 个隐藏层,但是随着 5 个神经元的计算能力的增加,模型在理解数据中的模式方面变得更好。它收敛得更快,平均精度提高了一倍!
最后,对于这个特定的案例和数据集,多层感知器表现得和简单感知器一样好。但这绝对是一个很好的练习,可以看到改变每个隐藏层中神经元的数量是如何影响模型性能的。
这不是一个完美的模型,可能有一些改进的空间,但下次有客人留言说你的父母不确定这是积极的还是消极的,你可以使用感知器来获得第二种意见。
结论
与当前最先进的技术相比,第一个深度学习算法非常简单。感知器是一个只有一个神经元的神经网络,只能理解提供的输入和输出数据之间的线性关系。
然而,有了多层感知器,视野扩大了,现在这个神经网络可以有许多层神经元,并准备学习更复杂的模式。
希望你喜欢学习算法!
敬请关注本系列的下一篇文章,我们将继续探索深度学习算法。
参考
- 深度学习。自然 521,436–444(2015)
- 伊恩·古德菲勒、约舒阿·本吉奥和亚伦·库维尔。2016.深度学习。麻省理工学院出版社。
- 麦卡洛克,W.S .,皮茨,w.《神经活动中固有观念的逻辑演算》。数学生物物理学通报 115–133(1943 年)
- 弗兰克·罗森布拉特。感知器,一个感知和识别自动机项目。康奈尔航空实验室(1957 年)
- 明斯基法学硕士和 Papert S. A. 1969。感知器。麻省剑桥:麻省理工学院出版社。
- 加雷斯·詹姆斯,丹妮拉·威滕,特雷弗·哈斯蒂,罗伯特·蒂布拉尼。(2013)。统计学习导论:在 r .纽约的应用
- D.鲁梅尔哈特、g .辛顿和 r .威廉姆斯。通过反向传播误差学习表征。性质323(6088):533–536(1986)。
用于图像分类的多层感知器
没有回旋也没有关注。只是全连接层。

由乌列尔 SC 在 Unsplash 上拍摄的照片
介绍
卷积神经网络(CNN)已经帮助我们解决了图像分类等计算机视觉任务。它可以捕捉邻居信息,这要归功于它的卷积过程,其中它通过一个过滤器使用相乘矩阵的和。此外,使用 CNN 是因为它在其模型上具有权重共享机制,因此参数的数量将少于使用深度神经网络。
随着时间的进步,CNN 并不是做计算机视觉任务的唯一方式。主要用于自然语言处理任务的 transformer 体系结构可以在 ImageNet 数据集上以很高的性能完成图像分类任务。并且与 CNN 模型具有可比性[1]。
现在,Touvron 等人(2021)提出了一种仅使用全连接层的图像分类架构。它被称为剩余多层感知器(ResMLP)。它的伟大之处在于,该模型可以在 ImageNet-1k 训练数据上取得很好的结果[2]。
本文将向您解释 ResMLP 架构。此外,我将向您展示使用 PyTorch 实现这个模型。没有进一步,让我们开始吧!
建筑
那么这个模型是如何工作的呢?该模型的灵感来自于基于变形金刚模型[1]的 ViT 架构。
首先,ResMLP 模型将图像分成 N×N 个小块,其中 N 是 16。然后,每个面片将被展平为一个矢量,然后这些面片被独立地馈送到 ResMLP 层。
ResMLP 将采用大小为 d x (N x N)的矩阵 X,其中 d 是向量维数,N x N 是面片数。然后,那个矩阵会得到几次变换,直到得到一个与矩阵 x 大小相同的矩阵 Y,下面是得到矩阵 Y 的等式。


GELU 是激活层,aff 是通过重新缩放和移动图像来变换输入列的算子,B C 矩阵是模型可学习的权重。
在我们获得矩阵 Y 之后,该矩阵将被平均为 d 维向量,其中该向量将被用作线性分类器的特征并捕捉预测结果。该架构的概述可以在下图中看到。

架构(Touvron 等人(2021 年) )
实施
值得庆幸的是,作者还包括了 GitHub 上的实现,你可以看看这个链接。代码是使用 PyTorch 库实现的,并且已经打包成了一个包。你可以用 pip 下载,看起来是这样的。
**! pip install res-mlp-pytorch**
数据
我们将使用的数据来自 EPFL,名为 Food 5K。该数据集由 5000 幅图像组成,这些图像根据是否包含食物进行了分类。要下载数据集,可以从官网或者这个 kaggle 资源库下载。
导入库
在运行代码之前,我们需要导入库。这是完成这项工作的代码,
加载数据
PyTorch 的主要优点是模块化,因为您可以更容易地定制您的数据加载器,因此您可以更容易地将数据加载到模型中。下面是加载数据的代码。
训练模型
准备好数据后,现在可以使用 ResMLP 模型对数据进行建模。在这种情况下,我们将通过 20 个时期迭代建模过程。下面是训练模型的代码。
与 CNN 的比较
这是使用 ResMLP 模型的训练结果,
**# ResMLP
Best val loss: 0.2860
Best acc: 0.891**
基于训练结果,我们得到验证损失为 0.2789,准确率为 0.895。对于一个只使用前馈神经网络的模型来说,确实是一个不错的结果。但我们需要将其与现有模型进行比较。
为了确定该模型与其他模型的性能如何,我还使用 ResNet-18 和 ResNet-50 架构训练了 CNN 模型。这是结果,
**# ResNet-18
Best val loss: 0.0365
Best acc: 0.986** **# ResNet-50
Best val loss: 0.0245
Best acc: 0.993**
正如你所看到的,ResNet-18 实现了良好的性能。验证损失为 0.0365,准确率为 0.986。ResNet-50 取得了较好的效果。验证损失为 0.0245,准确率为 0.993。
结束语
从这些结果来看,我们可以说 ResMLP 仍然支持 ResNet 或类似的体系结构。但有了这个结果,我相信这个模型可以改进得更好,所以它可以像 CNN 一样有相当的表现,甚至更好。
以上是对 ResMLP 模型的解释和实现。我希望你现在能从深度学习的进展中得到启发。如果你以后喜欢我的文章,对我的文章感兴趣,请关注我的媒体。如有疑问,可联系我 LinkedIn 。
谢谢你看我的文章!
参考
[1] Dosovitskiy,a .,Beyer,l .,科列斯尼科夫,a .,Weissenborn,d .,Zhai,x .,Unterthiner,t .,Dehghani,m .,Minderer,m .,Heigold,g .,Gelly,s .,Uszkoreit,j .,& Houlsby,N. (2020)。一幅图像相当于 16x16 个字:大规模图像识别的变形金刚。ArXiv:2010.11929 [Cs]。http://arxiv.org/abs/2010.11929
[2]图夫龙,h .、博雅诺斯基,p .、卡隆,m .、科德,m .、埃尔-努比,a .、格雷夫,e .、朱林,a .、辛那夫,g .、维尔比克,j .、杰古,H. (2021 年)。具有数据有效训练的图像分类前馈网络。ArXiv:2105.03404 [Cs]。http://arxiv.org/abs/2105.03404
多语言亚马逊评论分类
原文:https://towardsdatascience.com/multilingual-amazon-reviews-classification-f55f8a650c9a?source=collection_archive---------29-----------------------
用数据做很酷的事情
介绍
NLP 将成为这十年中最具变革性的技术。变压器模型推动了 NLP 的发展。在这篇博客中,我们来看看转换器在多语言数据上的应用。现在,许多企业的业务范围遍及全球,并以多种语言收集数据。NLP 也在建立强大的处理多语言数据的能力。Transformer 模型现在可以用多种语言进行训练,并无缝地接收其中的文本。这太神奇了!

来源:免版税的 Unsplash—https://unsplash.com/photos/jhtPCeQ2mMU
在这篇博客中,我们使用了一个预先训练的多语言 XLM 罗伯塔模型,并在下游分类任务中对其进行了微调。XLM-罗伯塔已经接受了超过 100 种不同语言的训练!该模型能够在多种语言的产品评论数据集上提供最先进的性能。我认为这为企业整合来自不同国家的运营数据并产生有价值的见解提供了新的机会。
这个模型的代码可以在我的 Colab 笔记本这里找到。我也上传到我的 GitHub 这里。
这里的代码是使用 HuggingFace 和 Datasets 库运行分类的通用代码。它可以很容易地修改为许多其他数据集和涉及分类的问题。
数据集和预处理
对于这个博客,我们使用亚马逊多语言评论语料库。该数据集由亚马逊公开提供,包含 6 种不同语言(英语、日语、德语、法语、西班牙语和中文)的各种产品的客户评论。数据集库提供了一个简单的 API 来下载和访问数据集。
!pip install datasets
dataset = datasets.load_dataset('amazon_reviews_multi')
数据集在训练集中有 1.2MM 的评论,在验证集中有 30K 的评论。每篇评论都有标题、类别、正文和星级。星级从 1 到 5 不等,平均分布在 5 个等级中。

英语评论及其评级的示例如下:
Review: 'I’ve had good luck with this product when I’ve bought it in stores, but this particular one I bought though Amazon was watered down! The texture was much more viscous than any of the tubes I bought in person and it lasted me about half the time. You’re better off finding it in a shop than wasting your money on watered down face wash.'
Rating: 2
我们加载 XML-Roberta 模型,并用它来标记文本。文本的最大长度设置为 512 个标记。为了提高模型的准确性,我们将 review_body、review_title 和 review_category 连接起来。
由于实际数据集相当大,我们在 10%的训练样本上进行训练。数据集库中的shard功能使得对一小部分数据进行采样进行实验变得很容易。完成此操作的代码如下:
dataset = dataset.shuffle(seed=123)train_dataset = dataset["train"].shard(index=1, num_shards=10)val_dataset = dataset['validation'].shard(index=1, num_shards=5)
tokenizer,encode_plus 选项用于标记与审查主体、审查标题和审查类别相关联的文本。实际评论的星级从 1 星到 5 星不等。我们将其转换为范围从 0 到 4 的目标标签。
模特培训
为了简化训练过程,我们使用 HuggingFace 的训练器库。我们使用自动建模功能加载预训练的 XLM-罗伯塔基础模型。
from transformers import AutoModelForSequenceClassification, TrainingArguments, Trainerbatch_size = 8num_labels = 5model = AutoModelForSequenceClassification.from_pretrained('xlm-roberta-base', num_labels=num_labels)
为了实例化训练器,我们定义了两件事:
- 训练参数——这些参数包括输出目录的位置、学习速度、批量大小、次数等
- 评估的计算指标功能—我们可以定义一个评估指标,在培训期间在 val 集上运行。
数据集包还定义了许多自定义指标。我喜欢数据集库中的这一功能,因为它使得在数据集上计算 bleu、rouge 或自定义小队指标的范围变得非常容易。
我们首先从数据集库中加载我们选择的指标
from datasets import load_metricmetric = load_metric('accuracy')
f1_metric = load_metric('f1')
metric.compute()函数可用于获取结果。
import numpy as npfake_preds = np.random.randint(1, 6, size=(64,))fake_labels = np.random.randint(1, 6, size=(64,))metric.compute(predictions=fake_preds, references=fake_labels)f1_metric.compute(predictions=fake_preds, references=fake_labels, average='weighted')
为了在训练期间运行它,我们从 eval_pred 获取预测和标签,然后运行 metric.compute()。仅此而已!!
为了开始训练,现在我们可以将所有这些参数传递给训练者。
trainer = Trainer(model,args,train_dataset= encoded_train_dataset,eval_dataset=encoded_val_dataset,data_collator=SmartCollator(pad_token_id=tokenizer.pad_token_id),tokenizer=tokenizer,compute_metrics=compute_metrics)
该模型在 Colab 上的 P100 实例上训练大约需要 2 小时 20 分钟。这是针对训练数据集的 10%样本的。
该模型达到了 59.9%的准确率,比介绍该数据集的论文中报告的 59.2%的准确率略高。对完整数据集的训练应该会进一步改善结果。
我想谈谈其他一些可以用来提高训练速度/模型结果的很酷的东西
智能动态填充减少训练时间
这里的灵感来自于的作品。在训练期间,我们必须输入相同长度的样本。由于基础文本可以有不同的长度,典型的方法是将所有内容填充到最大长度。这通常在完整数据集级别完成,其中如果文本的最大长度是 512 个标记,则所有内容都被填充到该最大长度。然而,这是低效的。例如,一个有 20 个标记的句子也会被填充到 512 个标记。由于注意力在计算中是二次的,这将显著减慢对这种样本的训练。
这里可以做的一个优化是填充到批次中的最大长度,而不是数据集中的最大长度。我笔记本中的代码通过定义一个智能数据排序器来实现这一点。在数据排序器中,完成这一任务的主要模块是:
max_size = max([len(ex['input_ids']) for ex in batch])for item in batch:batch_inputs += [pad_seq(item['input_ids'], max_size, self.pad_token_id)]
这种优化减少了一半的训练时间!这对一小段代码非常有用。
使用 Optuna 的超参数优化

Unsplash 的免版税:https://unsplash.com/photos/4Ennrbj1svk
Optuna 是一个超参数搜索器,可以很容易地与教练类集成。它有效地搜索大空间的超参数,及早修剪没有希望的试验。
在我们的实验中,我们像以前一样定义训练参数和训练器,并让 Optune 在选定数量的试验中进行优化。
trainer.hyperparameter_search(n_trials=15, direction=”maximize”)
结论
这个博客展示了如何使用 Transformer 模型来处理多语言数据。我们使用 HuggingFace 库和它的许多特性来准备我们的数据,并用几行代码来训练它。经过训练的模型具有最先进的结果。代码是以非常模块化的格式编写的,可以用于任何类型的分类问题。我们还讨论了动态填充的功能,以显著加快训练速度,并研究超参数优化,以进一步改善结果。
我希望您尝试一下代码,并训练自己的模型。请在下面的评论中分享你的经历。
在深度学习分析,我们非常热衷于使用机器学习来解决现实世界的问题。我们已经帮助许多企业部署了创新的基于人工智能的解决方案。如果您看到合作的机会,请通过我们的网站这里联系我们。
参考
- XML-Roberta 论文
- 拥抱脸分类笔记本
- 拥抱脸
- Optuna
多语言剪辑,带 Huggingface + PyTorch Lightning
原文:https://towardsdatascience.com/multilingual-clip-with-huggingface-pytorch-lightning-9a060eee334d?source=collection_archive---------16-----------------------

openAI CLIP 算法(摘自官方博客)
这是 OpenAI 训练片段的演练。CLIP 旨在将图像和文本放入一个新的投影空间,这样只需查看点积,它们就可以相互映射。
同时训练图像模型和语言模型的情况并不常见!
传统上,像 imagenet 这样的训练集只允许将图像映射到一个类(因此是一个单词)。此方法允许您将文本映射到图像,但是如果需要的话,也可以用于将图像映射到文本。
然而,这篇特别的博客具体介绍了我们如何使用拥抱脸变形金刚和 pytorch 闪电在 colab GPUs 上进行训练。
完整的代码可以在 Google colab 中找到。
确认
keras 文档中的以下剪辑教程值得称赞。
关于常量,需要注意的重要一点是嵌入维数。我们将把 resnet 和 transformers 的输出投射到 512 维空间。
EMBED_DIM **=** 512
TRANSFORMER_EMBED_DIM **=** 768
MAX_LEN **=** 128 *# Maximum length of text*
TEXT_MODEL **=** "distilbert-base-multilingual-cased"
EPOCHS **=** 5
BATCH_SIZE **=** 64
数据
我们下载了 coco 数据集,每个图像包含 5 个标题,大约有 82k 的图像。我们取其中的 20%作为我们的验证集。
考虑到图像主干是使用 imagenet 训练的,我们使用 imagenet stats 对其进行规范化,如转换规范化步骤所示。我们还将图像的大小调整为 128x128,以确保它在合理的时间内进行训练。

模型
主要有两种型号,VisionEncoder和TextEncoder以 resnet18 和 distilbert 为骨干。为了让它支持多种语言,我们简单地选择了distilbert-multilingual模式,就是这样!你很快就会看到,没有必要专门训练非英语单词。
Projection模块从视觉和文本编码器中提取嵌入内容,并将其投射到 512 维空间中。
需要注意两件事:
- 我们已经冻结了文本和视觉编码器的主干,不再训练它们的重量。
- 对于两种编码器,最终输出被归一化为单位长度。
**class** **Projection(**nn**.**Module**):**
**def** __init__**(***self***,** d_in**:** *int***,** d_out**:** *int***,** p**:** *float***=**0.5**)** **->** **None:**
*super***().**__init__**()**
*self***.**linear1 **=** nn**.**Linear**(**d_in**,** d_out**,** bias**=False)**
*self***.**linear2 **=** nn**.**Linear**(**d_out**,** d_out**,** bias**=False)**
*self***.**layer_norm **=** nn**.**LayerNorm**(**d_out**)**
*self***.**drop **=** nn**.**Dropout**(**p**)** **def** **forward(***self***,** x**:** torch**.**Tensor**)** **->** torch**.**Tensor**:**
embed1 **=** *self***.**linear1**(**x**)**
embed2 **=** *self***.**drop**(***self***.**linear2**(**F**.**gelu**(**embed1**)))**
embeds **=** *self***.**layer_norm**(**embed1 **+** embed2**)**
**return** embeds **class** **VisionEncoder(**nn**.**Module**):**
**def** __init__**(***self***,** d_out**:** *int***)** **->** **None:**
*super***().**__init__**()**
base **=** models**.**resnet34**(**pretrained**=True)**
d_in **=** base**.**fc**.**in_features
base**.**fc **=** nn**.**Identity**()**
*self***.**base **=** base
*self***.**projection **=** Projection**(**d_in**,** d_out**)**
**for** p **in** *self***.**base**.**parameters**():**
p**.**requires_grad **=** **False** **def** **forward(***self***,** x**):**
projected_vec **=** *self***.**projection**(***self***.**base**(**x**))**
projection_len **=** torch**.**norm**(**projected_vec**,** dim**=-**1**,** keepdim**=True)**
**return** projected_vec **/** projection_len**class** **TextEncoder(**nn**.**Module**):**
**def** __init__**(***self***,** d_out**:** *int***)** **->** **None:**
*super***().**__init__**()**
*self***.**base **=** AutoModel**.**from_pretrained**(**TEXT_MODEL**)**
*self***.**projection **=** Projection**(**TRANSFORMER_EMBED_DIM**,** d_out**)**
**for** p **in** *self***.**base**.**parameters**():**
p**.**requires_grad **=** **False** **def** **forward(***self***,** x**):**
out **=** *self***.**base**(****x**)[**0**]**
out **=** out**[:,** 0**,** **:]** *# get CLS token output*
projected_vec **=** *self***.**projection**(**out**)**
projection_len **=** torch**.**norm**(**projected_vec**,** dim**=-**1**,** keepdim**=True)**
**return** projected_vec **/** projection_len**class** **Tokenizer:**
**def** __init__**(***self***,** tokenizer**:** BertTokenizer**)** **->** **None:**
*self***.**tokenizer **=** tokenizer **def** __call__**(***self***,** x**:** *str***)** **->** AutoTokenizer**:**
**return** *self***.**tokenizer**(**
x**,** max_length**=**MAX_LEN**,** truncation**=True,** padding**=True,** return_tensors**=**"pt"
**)**
削波损失函数
对于像我这样没有经历过对比损失的人来说,这是最有趣的部分。
我们知道我们希望相应图像和文本的向量对齐。这意味着点积必须尽可能接近 1。对于其他一切,我们需要把它推向 0。
因此,对于给定的标题,我们取所有图像的点积的软最大值,然后取交叉熵损失。类似地,对于给定的图像,我们对所有的标题重复这个过程。我们平均这两次损失。
就哪一个元素在一批中是真阳性而言,请记住,我们发送的是已经排列好的图像、标题对。因此,我们希望所有的对角线元素排成一行,而所有非对角线的元素我们希望推向零。
**def** **contrastive_loss(**logits**,** dim**):**
neg_ce **=** torch**.**diag**(**F**.**log_softmax**(**logits**,** dim**=**dim**))**
**return** **-**neg_ce**.**mean**()**
**def** **clip_loss(**similarity**:** torch**.**Tensor**)** **->** torch**.**Tensor**:**
caption_loss **=** contrastive_loss**(**similarity**,** dim**=**0**)**
image_loss **=** contrastive_loss**(**similarity**,** dim**=**1**)**
**return** **(**caption_loss **+** image_loss**)** **/** 2.0**def** **metrics(**similarity**:** torch**.**Tensor**)** **->** Tuple**[**torch**.**Tensor**,** torch**.**Tensor**]:**
y **=** torch**.**arange**(***len***(**similarity**)).**to**(**similarity**.**device**)**
img2cap_match_idx **=** similarity**.**argmax**(**dim**=**1**)**
cap2img_match_idx **=** similarity**.**argmax**(**dim**=**0**)** img_acc **=** **(**img2cap_match_idx **==** y**).**float**().**mean**()**
cap_acc **=** **(**cap2img_match_idx **==** y**).**float**().**mean**()** **return** img_acc**,** cap_acc
PyTorch 闪电模型
如果你以前没有使用过 pytorch lightning,它的好处是你不需要担心把它放在哪个设备上,记住要把优化器归零等等。所有这些都已经处理好了。只需简单地指定训练和验证步骤,以及优化器,您就可以开始了。
我真正喜欢的另一个好处是记录。你只需要写self.log("name", metric_to_track),默认情况下它会记录到 tensorboard,或者任何其他类型的日志程序。
**class** **Model(**pl**.**LightningModule**):**
**def** __init__**(***self***,** lr**:** *float* **=** 1e-3**)** **->** **None:**
*super***().**__init__**()**
*self***.**vision_encoder **=** VisionEncoder**(**EMBED_DIM**)**
*self***.**caption_encoder **=** TextEncoder**(**EMBED_DIM**)**
*self***.**tokenizer **=** Tokenizer**(**AutoTokenizer**.**from_pretrained**(**TEXT_MODEL**))**
*self***.**lr **=** lr
**def** **common_step(***self***,** batch**:** Tuple**[**torch**.**Tensor**,** List**[***str***]])** **->** torch**.**Tensor**:**
images**,** text **=** batch
device **=** images**.**device
text_dev **=** **{**k**:** v**.**to**(**device**)** **for** k**,** v **in** *self***.**tokenizer**(**text**).**items**()}** image_embed **=** *self***.**vision_encoder**(**images**)**
caption_embed **=** *self***.**caption_encoder**(**text_dev**)**
similarity **=** caption_embed **@** image_embed**.**T loss **=** clip_loss**(**similarity**)**
img_acc**,** cap_acc **=** metrics**(**similarity**)**
**return** loss**,** img_acc**,** cap_acc **def** **training_step(**
*self***,** batch**:** Tuple**[**torch**.**Tensor**,** List**[***str***]],** *****args**:** *list*
**)** **->** torch**.**Tensor**:**
loss**,** img_acc**,** cap_acc **=** *self***.**common_step**(**batch**)**
*self***.**log**(**"training_loss"**,** loss**,** on_step**=True)**
*self***.**log**(**"training_img_acc"**,** img_acc**,** on_step**=True,** prog_bar**=True)**
*self***.**log**(**"training_cap_acc"**,** cap_acc**,** on_step**=True,** prog_bar**=True)**
**return** loss **def** **validation_step(**
*self***,** batch**:** Tuple**[**torch**.**Tensor**,** List**[***str***]],** *****args**:** *list*
**)** **->** torch**.**Tensor**:**
loss**,** img_acc**,** cap_acc **=** *self***.**common_step**(**batch**)**
*self***.**log**(**"validation_loss"**,** loss**,** on_step**=True)**
*self***.**log**(**"validation_img_acc"**,** img_acc**,** on_step**=True,** prog_bar**=True)**
*self***.**log**(**"validation_cap_acc"**,** cap_acc**,** on_step**=True,** prog_bar**=True)**
**return** loss **def** **configure_optimizers(***self***)** **->** torch**.**optim**.**Optimizer**:**
vision_params **=** **{**"params"**:** *self***.**vision_encoder**.**projection**.**parameters**(),** "lr"**:** *self***.**lr**}**
caption_params **=** **{**"params"**:** *self***.**caption_encoder**.**projection**.**parameters**()** **,** "lr"**:** *self***.**lr**}**
**return** torch**.**optim**.**Adam**([**vision_params**,** caption_params**])**
火车
训练是直截了当的,如下面的五行所示。使用 16 位精度几乎使每个历元的训练时间减半,从 16 分钟减少到 9 分钟。请注意,添加半精度训练和梯度裁剪是多么容易。
还有一点要注意的是,我无法在 TPUs 上运行,所以如果有人知道我需要调整什么,请告诉我。设置tpu_cores=8不起作用。
model **=** Model**(**1e-3**)**
trainer **=** pl**.**Trainer**(**
max_epochs**=** EPOCHS**,**
gpus**=**torch**.**cuda**.**device_count**(),**
gradient_clip_val**=**1.0**,**
precision**=**16
**)**
trainer**.**fit**(**model**,** train_dl**,** valid_dl**)**
结果
我将第一批文本嵌入(在验证集中)与验证集的所有图像进行比较,取它们之间的点积。
similarity **=** caption_embed **@** image_embed**.**T
val**,** closest **=** similarity**.**topk**(**5**,** dim**=-**1**)**
draw_result(i, similarity_matrix)是一个方便的函数,它获取第 I 个标题和相似性矩阵,并绘制出五个最接近的图像以及真实图像。标题和图像之间的相似性显示在标题中。首先打印标题。
直方图以直方图的形式显示标题与所有图像的相似性。



下面是其中一个标题的谷歌翻译版本。
英文描述:“一只斑马低着头站起来,在泥地上吃草。”,翻译成西班牙语:“一个 cebra de pie con la cabeza gacha 和 comiendo hierba 在 el suelo de tierra。”

又是一个翻译版本,这次是法语版。英文说明:“一台笔记本电脑陈列在一个小木平台上。”根据谷歌翻译的意思是:“一个小平板上的便携式附件。”

下面的俄语翻译做得很糟糕,所以它显然不是防弹的。或者我需要多训练一段时间。英文说明:“一个摆满各种钟的商店。

最后,我检查一个单词版本。注意这只狗看起来确实有点像熊。也许它的名字叫熊?

无耻的自我推销
看这里我的关于机器学习和深度学习的课程(使用代码 DEEPSCHOOL-MARCH 获得 85 折优惠)。
用于文档分类和自然语言处理的多项式朴素贝叶斯
原文:https://towardsdatascience.com/multinomial-na%C3%AFve-bayes-for-documents-classification-and-natural-language-processing-nlp-e08cc848ce6?source=collection_archive---------9-----------------------

Arthur V. Ratz 的照片
多项式朴素贝叶斯在 Python 3.8、NumPy 和 NLTK 中的实现
介绍
朴素贝叶斯——一种用于构建数据分类模型的概率方法。它被公式化为几种方法,广泛用作基于距离的 K-Means 聚类和决策树森林的替代方法,并将概率作为数据属于特定类的“可能性”来处理。朴素贝叶斯的高斯和多项式模型是存在的。
多项式模型提供了对无法用数字表示的数据进行分类的能力。它的主要优点是大大降低了复杂性。它提供了使用小的训练集来执行分类的能力,而不需要不断地重新训练。
通过使用最新的 NumPy 和 NLTK 库,本文的读者将了解多项式朴素贝叶斯分类算法及其在 Python 3.8 中的实现。
多项式朴素贝叶斯分类器
多项式朴素贝叶斯广泛用于根据文档内容的统计分析将文档分配到类别中。它为“繁重的”基于人工智能的语义分析提供了一种替代方案,并大大简化了文本数据分类。
分类的目的是通过确定一个文档属于具有相同主题的其他文档类别的概率,将文本片段(即文档)分配到类别。
每个文档由多个单词(即术语)组成,这些单词有助于理解文档的内容。类是一个或多个文档的标记,指的是同一主题。
通过执行统计分析,测试文档的术语已经在特定类别的其他文档中出现的假设,来用现有类别之一标记文档。这增加了文档与已经分类的文档来自同一类别的可能性:
给定:𝑺想要分类的样品;
𝑺中的每个样本都被定义为出现在𝑪:类的一个或多个文档中的字符串
根据已经分类的𝐷文档执行分类:
为了执行分类,𝑺的术语由向量𝑾.表示𝑾的每个特征𝒘ᵢ是相应的𝒊-th 术语在𝑪.类文档中的出现频率每个特征向量𝑾 —一个术语/类别出现频率直方图:

词汇出现频率直方图|作者图片
通过估计𝒑(𝒘ᵢ | 𝑪) 𝒊=𝟏的概率来执行分类..𝒏认为,𝑺的术语出现在𝑪.的𝐷文献中
多项式模型仅依赖于从贝叶斯定理得出的基于概率的决策函数的评估。
贝叶斯概率𝒑(𝑪ₖ | 𝑾)计算如下:

贝叶斯概率公式
朴素贝叶斯背后的主要思想是,𝑾的所有要素都独立地影响𝑺属于𝑪ₖ.的概率
或者,后验𝒑(𝑪ₖ | 𝑾)表示为:

后𝒑(𝑪ₖ | 𝑾)公式|作者图片
估计的先验𝒑(𝐶ₖ)和可能性𝒑(𝑾 | 𝐶ₖ)成比例地对𝐶ₖ是𝑺.类的结果有贡献
然而,只有它的分子影响后验𝐏𝐫(𝐶ₖ | 𝑾).的估计尽管如此,证据𝒑(𝑾)同样规模的𝐏𝐫(𝐶ₖ | 𝑾)的每个𝐶ₖ,可能只是从估计中省略。
多项式模型用于文档分类,假设𝑺由特征向量𝑾.表示每个特征𝒘ᵢ是来自𝑺的𝒊-th 术语在文档𝐷中出现的计数(即频率),该文档已经被分配给类别𝐶.之一
原来,每个𝐶ₖ的后验𝐏𝐫(𝑾 | 𝐶ₖ)估计为:

多项式朴素贝叶斯分类器|作者图片
先验𝐏𝐫(𝑪ₖ)是一个商。哪个分子被估计为所有特征之和的阶乘∀ 𝑤 ₖ ᵢ ∈ 𝑾 𝒊=𝟏..𝒏.反过来,分母是所有特性的' 𝑤 ₖᵢ阶乘的乘积。
分子被评估为概率分布,这是𝑺在来自𝑪ₖ.类的文档𝑫中发生的所有可能结果的可能性这些结果的总量等于上面提到的∀ 𝑤 ₖᵢ ∈ 𝑾的特征总和的阶乘。
反过来,𝒑(𝑪ₖ的分母)被获得作为𝑾的特征在𝑫.的文档语料库中出现的所有可能结果的量这被确定为一个算术级数,作为每个特征的 𝑤 ₖᵢ之和获得!阶乘。
上面的计算非常类似于矢量𝑾.的勒让德质因数分解此外,它意味着多元二项式分布计算。当分母足够大时,𝒑(𝑪ₖ显著降低。这通常提供了排除出现在语料库𝑫.的所有文档中的特征的能力
𝑺给定𝑪ₖ的可能性是项的概率的乘积𝒑ₖᵢ在统计程度上 𝑤 ₖᵢ,拒绝零假设:

可能性𝒊-th 任期 𝑤 ᵢ属于𝐶ₖ类|图片由作者提供
上面的多项式模型被用于计算𝑺来自𝑪ₖ.的概率𝒑(𝑾 | 𝑪ₖ
虽然,在这种情况下,目标是计算𝑪ₖ是𝑺.类的概率通过在对数空间中表示多项式模型,可以容易地计算𝐏𝐫(𝐶ₖ | 𝑾:

对数空间中的多项式贝叶斯分类器
因为可能性𝒑(𝑾 | 𝐶ₖ)是概率𝒑ₖᵢ ≡ 𝒑( 𝑤 ᵢ | 𝐶ₖ)的乘积,所以它用对数标度表示。在这种情况下,𝒑(𝑾 | 𝐶ₖ)与统计模型所支持的最大可能程度成正比。
在上式中,𝗹𝗼𝗴(𝒑)是概率𝒑的自然对数,计算如下:

概率自然对数𝗹𝗼𝗴(𝒑) |图片由作者提供
𝒑's 对数总是𝟏.𝒑小于或等于𝟏.时的𝟎𝟎,和自然对数𝐥𝐧(𝒑),除非另有说明。
关于𝑆的阶级𝐶ₛ的决定是这样作出的:

基于概率的分类公式|作者图片
𝐶ₛ类被确定为现有的𝐶ₖ类之一,其𝐏𝐫(𝐶ₖ | 𝑾)的绝对值最大。因为𝐏𝐫(𝐶ₖ | 𝑾)总是负值,所以取其绝对值。
多项式贝叶斯分类算法
让𝑺 —一个输入字符串,𝑫 —一个𝒛-documents 语料库,𝑪 —一个𝒎-classes:集
计算样本𝑺的类别 𝑪ₛ 如下:
- 将样本𝑺分割成一组𝒏-terms
- 对于每个𝒌-th 级𝑪ₖ 𝒌=𝟭..𝒎做到了以下几点:
- 计算𝒏-features ∀𝒘ₖᵢ ∈ 𝑾的向量𝑾,其中𝒘ₖᵢ是相应的𝒊-th 项在𝑪ₖ.的文档中出现的频率
- 评估先前的𝒑(𝑪ₖ)作为文档出现在来自𝑪ₖ.类别的文档中的全概率
- 计算后验𝐏𝐫(𝑪ₖ | 𝑾)的方法是将先验𝒑(𝑪ₖ)加到每项𝒘ᵢ的总和上,给定𝑪ₖ,概率𝒑(𝒘ᵢ | 𝑪ₖ):

后𝐏𝐫(𝑪ₖ | 𝑾)公式|作者图片
3.确定𝑪ₛ为𝑺的一类,其中𝐏𝐫(𝑪ₖ | 𝑾) 𝒌=𝟭..𝒎是最高的:

𝑺计算的类𝑪ₛ|图片作者
该算法的复杂度 𝜎 被评估为:

多项式贝叶斯分类器复杂性( 𝜎 ) |图片由作者提供
𝒛—𝒏𝑫的文档总数—𝒎𝑺样本中的术语数—𝑪的类数
半监督学习
半监督学习提供了提高多项式模型性能的能力。此外,它允许通过基于已经分类的文档语料库训练模型来提高分类的质量。
半监督学习算法相当直观简单且公式化,例如:
- 使用上面讨论的多项式模型计算𝑺的𝑪ₛ等级。
- 将标有𝑪ₛ的𝑺加入𝐷.文献集
- 重新评估分类模型。
按照以下流程对每个新的𝑺.样本进行分类
半监督学习过程:

半监督学习过程|作者图片
如上所示,该过程提供了执行分类的能力,将样本分配到有限的类别集,类似于使用期望最大化算法(EM)。
使用代码
下面列出了 Python 3.8、NumPy 和 NLTK 库中实现多项式分类算法的代码:
输出:
结论
多项式贝叶斯模型是已知的 K 均值聚类和决策树算法的有效替代,能够对通常不容易量化的各种数据进行分类。例如,它可以用作基于人工神经网络的文本分类模型的一部分,该模型基于由多项式贝叶斯分类器推断的文本内容摘要来学习和预测文本的类别。
与用于基于内容的文本分类的类似人工智能和机器学习(ML)不同,多项式贝叶斯分类器完全是一种数据挖掘方法,允许预测引入模型的文本的类别,而无需连续训练。然而,为了防止多项式模型中遇到的早期收敛和冷启动问题,建议使用半监督学习算法来训练模型,以便更好地预测文本。这通常是通过训练模型并同时将其用于预测来完成的。
对于多项式贝叶斯分类器演示,请下载并运行下面的项目:
- Python 3.7 中的多项朴素贝叶斯分类器
连续多个贝叶斯测试
原文:https://towardsdatascience.com/multiple-bayesian-tests-in-row-2e4ad8fb5055?source=collection_archive---------22-----------------------

约翰·莫塞斯·鲍恩在 Unsplash 上拍摄的照片
在我之前的一篇文章中,我已经简要讨论了如何在给定两个测试的情况下看到一个事件的条件概率。在这种情况下,我们看到了被疾病影响的条件概率的例子,给出了两个积极的医学测试。
我开始思考,对于许多测试,我们如何计算测试结果和期望结果的所有可能组合。
奇怪的是,我在网上找不到关于这个特定话题的现成解决方案。我有点惊讶,考虑到贝叶斯定理最精确的元素之一,它给我们新的预测的能力给了我们新的更新信息。
因此,在这篇文章中,我想解决的只是,我算出了一些数学,所以你不必,我已经列出了公式。希望这能帮助处于相同情况的人。
要重温贝叶斯定理的基础,请参考我的另一篇文章。
公式
让我们继续之前看到的医学测试 T 的例子,对于疾病 D ,我们知道对其的敏感性和特异性。
假设检测结果为阳性 P(+),则受该疾病影响的概率 P(D)将由以下公式给出:

经典表示中的贝叶斯定理和 P(+)的展开式。(图片由作者提供)
这里,我们可以看到条件概率 P(D|+)由下式给出:
- P(+|D) 又名测试的 灵敏度 乘以
- 此类疾病的传播 P(D),这是 先验 值
- 除以概率,以检测疾病的阳性 P(+) ,而不管我们是否真的被感染,是真阳性,还是假阳性。
来自上述例子的公式由灵敏度乘以 P(D)加上, 特异性 P(+|D)乘以 1 减去先前的 给出
因此,为了计算条件概率,我们只需要敏感性、特异性和疾病传播的值。前两个是固定的和给定的,另一个经常变化,取决于遗传疾病的人群和时间/地点,如季节性流感等。
因此,计算给定多重测试的条件概率的技巧是根据测试结果乘以灵敏度和特异性。
例如,对于一系列 n 测试的阳性结果,我们可以使用公式:

多重贝叶斯定理的一般化版本(图片由作者提供)
然而,如果其中一个测试是阴性的,我们必须使用假阴性率,因此,1-Sens。并且,为了使用,用假阳性率 1-Spec 替换特异性的相同方法。
两个测试的所有可能示例如下:

连续两个贝叶斯定理(图片由作者提供)
相反 P(D)的值由 1 减去上述公式的结果给出,或由下式给出:

连续两个贝叶斯定理(图片由作者提供)
例子
在这里,我已经锻炼了一个例子,只有先验,敏感性和特异性。

两个贝叶斯定理的例子(图片由作者提供)
我们可以看到,如果我们只在一项测试中测试为阳性,我们有 66%会受到实际影响,33%不会。这在第二次阳性测试中发生了巨大的变化。现在我们可以确定医学结果了。
相反,给我们一个阴性测试,我们就非常确定我们的状况。
这个测试示例确保在测试完成时没有假阴性。
希望你喜欢这篇文章,并发现它很有帮助,如果是这样,请告诉我!
我打算在接下来的几天里在 Jupyter 笔记本上重现这个例子。萧然若不是跟我扯上关系,这里就只有叶了。
多重分派:一个强大的编程范例
原文:https://towardsdatascience.com/multiple-dispatch-a-powerful-programming-paradigm-8bc8fcd2c73a?source=collection_archive---------25-----------------------

美国宇航局在 Unsplash 拍摄的照片
通过朱莉娅的镜头讲述
Julia 是我一直最喜欢的编程语言之一。不仅像 Python 一样容易接,还拥有 c 的速度,不过据说 Julia 用户是为了性能而来,为了多次调度而留下来的。但是这个奇怪的特征是什么,是什么让它对新手和老手都有吸引力呢?
多重调度是如何工作的?
我们从回忆 Julia 的动态类型系统的样子开始。Julia 类型可以分为具体类型和抽象类型(也有复合类型和参数类型,但让我们暂时忽略它们)。抽象类型构成了具体类型可能适合的类型层次结构的主干。

图片作者。
在 Julia 中有许多表示数字的方法:一个给定的数字可能是实数或复数,有理数或无理数,有符号或无符号。它们被组织在一个层次结构中,上面的树中列出了其中的一个子集。叶节点是具体类型,而中间节点是抽象类型。事实上,在 Julia 中,具体类型可能不是子类型;它们的超类型总是抽象类型。这反映了行为的遗传而不是结构的遗传。关于 Julia 类型系统的更详细的概述,我们参考了用户手册。
我们还注意到了函数和方法之间的区别。一个函数可以有多种行为。根据定义,函数的一种可能的行为称为方法。关于 Julia 方法的深入讨论,请参见这里的。
现在,多重分派是一个执行规则,通过它,函数的行为由参数类型的组合和计数决定(根据它们的最高级别的特性)。换句话说,多分派根据函数输入类型的组合来决定执行哪种方法。
例如,我们可以定义以下内容:
函数遭遇是重载的,对于给定的输入组合,将根据输入类型选择期望的行为。在上面的代码中,游客是人的子类型,但是鹿对游客的反应与普通人不同。我们可以通过添加额外的输入参数,例如 foo,来轻松定义额外的和更复杂的函数行为。
基于所有输入类型的匹配在许多设置中是合理的,包括在数学中:
多重分派对于数学代码特别有用,在这种情况下,人为地认为运算“属于”某个参数比“属于”其他任何参数都没有什么意义:在
x + y中的加法运算属于x比属于y多吗?(朱莉娅手册)
我们在下面强调多重分派的两个具体好处。
表达性
实现多重分派的语言比不实现多重分派的语言具有更强的表达能力。在零分派下,对于任何允许的输入组合,一个函数只有一种行为。在单个分派下(函数在单个输入上分派),可能的行为或方法的数量与输入空间的大小成线性关系。在多重分派下,可能行为的数量取决于所有输入空间大小的乘积!换句话说,可以表达的行为数量大约为

这当然是一个很大的空间,并允许用户有足够的空间通过更高阶的特异性来控制功能行为。
代码重用
在我的最佳编程实践指南中,Kent Beck 和 Cynthia Andres 解释了极限编程,代码重用在软件设计中至关重要。通过尽可能地消除代码重复,它为我们程序员节省了宝贵的时间和精力。
多重分派如何促进从未见过面或说过话的作者编写的 Julia 包的共享、再利用和最终重用?在 Julia 中,人们可以简单地通过定义一个新的函数来构建一个预定义的类型和它的一系列行为,该函数对输入类型的不同组合或顺序进行操作。例如,如果我想扩展上面的 demo.jl 代码,我可以导入这个包并在现有类型的基础上构建,或者为相遇函数定义新的行为,如下所示。
保存和添加预定义的类型和功能很简单,比如 Person、Tourist 和 encounter。
提示:要从另一个包中扩展一个函数,必须显式地导入它。例如,要从线性代数模块中扩展行列式函数,必须首先在脚本的顶部添加下面一行
我们注意到,由于微妙的原因,静态类型语言中的结构继承和函数重载等熟悉的概念对于代码重用来说并不是最佳的。例如,在像 C++这样的静态类型系统中,重载函数的参数的静态类型用于确定函数行为,而不是编译器直到运行时才知道的更具体的类型。这使得很难有效地实施正确的行为。事实上,多重分派是 Julia 生态系统中不寻常的大量代码重用的主要原因。
摘要
多重分派通过对函数输入类型的组合进行分派来实现更高阶的表达能力。多重分派也提升了代码重用的两个属性:
- 我们可以定义现有操作可以应用的新类型
- 我们可以定义可应用于现有类型的新操作
这些使得用户可以很容易地在现有的包以及它们带来的功能和预定义类型上进行构建。事实证明,它还通过促进针对各种数据类型的编译代码的专门化和优化,提高了 Julia 的速度和性能。多重分派无疑是一个有价值的特性,它将 Julia 与其他科学计算语言(如 Matlab)区分开来,并且是 Julia 社区成员的福音。
参考
- 茱莉亚手册:https://docs.julialang.org/en/v1/manual/methods/
- JuliaCon 2019: 多次派遣的不合理效力作者 Stefan Karpinski
多元线性回归变得简单
原文:https://towardsdatascience.com/multiple-linear-regression-made-simple-d073e55e7d0c?source=collection_archive---------41-----------------------
学习如何在 R 中运行多元和简单的线性回归,如何解释结果以及如何验证应用条件

照片由王欣拍摄
介绍
R 记住描述性统计是统计学的一个分支,它可以描述你手头的数据。推断统计学(具有流行的假设检验和置信区间)是统计学的另一个分支,它允许进行推断,即基于样本得出关于总体的结论。统计学的最后一个分支是关于模拟两个或多个变量之间的关系。 1 描述和评估变量间联系最常用的统计工具是线性回归。
有两种类型的线性回归:
- 简单线性回归是一种统计方法,允许评估两个定量变量之间的线性关系。更准确地说,它使这种关系能够被量化,并对其重要性进行评估。
- 多元线性回归是简单线性回归的推广,这种方法可以评估一个响应变量(定量)和几个解释变量(定量或定性)之间的线性关系。
在现实世界中,多元线性回归比简单线性回归使用得更频繁。这主要是因为:
- 多元线性回归允许评估两个变量之间的关系,同时控制其他变量的效果(即去除效果)。
- 随着数据收集变得更加容易,在分析数据时可以包括和考虑更多的变量。
多元线性回归是一个如此强大的统计工具,我想把它展示出来,让每个人都理解它,甚至在必要的时候使用它。然而,如果不先介绍简单线性回归,我就不能写多元线性回归。
因此,在提醒了简单线性回归的原理和解释之后,我将说明如何在 r 中执行多元线性回归。我还将在多元线性回归的上下文中展示如何解释输出,并讨论其应用条件。然后,我将通过介绍与线性回归直接相关的更高级的主题来结束本文。
简单线性回归:提醒
简单线性回归是一种不对称程序,其中:
- 其中一个变量被认为是要解释的响应或变量。也称为因变量,在 y 轴上表示
- 另一个变量是解释变量,也称为自变量,表示在 x 轴上
简单的线性回归允许评估两个变量之间存在的线性关系,并量化这种联系。请注意,线性是线性回归中一个强有力的假设,因为它测试并量化了两个变量是否线性相关。
线性回归之所以成为强大的统计工具,是因为它允许量化当解释变量/自变量增加一个单位时,响应变量/因变量的变化量。这个概念是线性回归的关键,有助于回答以下问题:
- 在广告上花费的金额和一定时期内的销售额有联系吗?
- 在第一份工作中,学校教育的年数在经济上有价值吗?
- 烟草税的增加会减少它的消费吗?
- 根据面积不同,一套公寓最可能的价格是多少?
- 一个人对刺激的反应时间取决于性别吗?
简单线性回归可以被看作是对方差分析和学生 t 检验的扩展。方差分析和 t-检验允许根据定量变量对各组进行比较,2 组用于 t-检验,3 组或更多组用于方差分析。 2 对于这些测试,独立变量,即构成不同组进行比较的分组变量,必须是定性变量。线性回归是一种扩展,因为除了用于比较组外,它还用于定量自变量(这是 t 检验和 ANOVA 所不能做到的)。
在本文中,我们感兴趣的是评估一加仑燃料行驶的距离与汽车重量之间是否存在线性关系。对于这个例子,我们使用mtcars数据集(预加载在 R 中)。
该数据集包括 32 辆汽车的燃料消耗以及汽车设计和性能的 10 个方面: 3
mpg英里/(美国)加仑(一加仑≈ 3.79 升)cyl汽缸数量disp排水量(立方英寸)hp总马力drat后桥传动比wt重量(1000 磅,1000 磅≈ 453.59 千克)qsec1/4 英里时间(其中 1/4 英里≈ 402.34 米)vs发动机(0 = V 型,1 =直型)am变速器(0 =自动,1 =手动)gear前进档数量carb化油器数量
dat <- mtcarslibrary(ggplot2)
ggplot(dat, aes(x = wt, y = mpg)) +
geom_point() +
labs(
y = "Miles per gallon",
x = "Car's weight (1000 lbs)"
) +
theme_minimal()

作者的情节
上面的散点图显示,一加仑燃料行驶的距离和一辆汽车的重量之间似乎有一种的负相关关系。这是有道理的,因为汽车越重,它消耗的燃料就越多,因此一加仑汽油能行驶的里程就越少。
这已经很好地概述了这两个变量之间的关系,但以每加仑英里数为因变量、汽车重量为自变量的简单线性回归走得更远。当重量变化一个单位(本例中为 1000 磅)时,它会通过告诉我们平均距离变化了多少英里。由于回归线,这是可能的。
原则
简单线性回归的原理是,找到与观测值尽可能接近地通过的直线(即确定其方程),即成对(,易)形成的点集。
第一步,有很多潜在的线。其中三个被绘制出来:

作者的情节
为了找到尽可能靠近所有点的直线,我们取每个点和每条势线之间垂直距离的平方。注意,我们采用距离的平方来确保负间隙(即线下的点)不会被正间隙(即线上的点)补偿。最接近这组点的线是使这些平方距离之和最小化的线。
生成的回归线在下图中以蓝色显示,灰色虚线表示点和拟合线之间的垂直距离。每个观测点和由最小二乘法确定的拟合线之间的这些垂直距离被称为线性回归模型的残差并表示为ϵ.

作者的情节
根据定义,没有其他直线的点和直线之间的总距离更小。这种方法称为最小二乘法,或称 OLS 为普通最小二乘法。
等式
回归模型可以写成以下方程的形式:
Y=β0+β1X+ϵ
使用:
- 因变量 y
- x 独立变量
- β0 截距(x=0 时 Y 的平均值),有时也表示为α
- β1 斜率(X 增加一个单位时 Y 的预期增加量)
- ϵ残差(平均值 0 的误差项,描述模型未捕捉到的 y 的变化,也称为噪声)
当我们确定最接近所有点的那条线时(我们说我们用一条线来拟合观察到的数据),我们实际上是根据手头的数据估计未知参数 β0 和 β1。请记住,在您的几何课上,绘制直线只需要两个参数-截距和斜率。
这些估计值(以及之前散点图中显示的蓝线)可以通过以下公式手动计算:

其中 x 和 y 分别表示 x 和 y 的样本均值。
(如果你很难手工计算ˇβ0 和ˇβ1,看看这个闪亮的应用,它可以帮助你根据你的数据轻松找到这些估计值。)
系数ˇβ的解释
截距ˇβ0 是自变量 X 取值 0 时因变量 Y 的平均值。它的估计对评估两个变量之间是否存在线性关系没有兴趣。然而,如果你想知道当 x=0 时,Y 的平均值是多少,这是有意义的。 4****
另一方面,斜率ˇβ1 对应于当 X 变化一个单位时 Y 的预期变化。它告诉我们两个重要的信息:****
- 斜率的符号表示线的方向——正斜率(ˇβ1>0)表示两个感兴趣的变量之间存在正关系(它们以相同的方向变化),而负斜率(ˇβ1<0)表示两个变量之间存在负关系(它们以相反的方向变化)。
- 斜率的值提供变量 Y 的演变速度的信息,作为变量 x 的函数。斜率绝对值越大,x 的每个单位的 Y 的预期变化越大。然而,注意,大值不一定意味着关系具有统计显著性(关于关系的显著性部分有更多信息)。
这类似于相关系数,它给出了关于两个变量之间关系的方向和强度的信息。
为了在 R 中执行线性回归,我们使用了lm()函数(代表线性模型)。该函数要求先设置因变量,再设置自变量,用波浪号(~)隔开。
应用于我们的重量和汽车消耗的例子,我们有:
model <- lm(mpg ~ wt, data = dat)
summary()函数给出了模型的结果:
summary(model)##
## Call:
## lm(formula = mpg ~ wt, data = dat)
##
## Residuals:
## Min 1Q Median 3Q Max
## -4.5432 -2.3647 -0.1252 1.4096 6.8727
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 37.2851 1.8776 19.858 < 2e-16 ***
## wt -5.3445 0.5591 -9.559 1.29e-10 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 3.046 on 30 degrees of freedom
## Multiple R-squared: 0.7528, Adjusted R-squared: 0.7446
## F-statistic: 91.38 on 1 and 30 DF, p-value: 1.294e-10
在实践中,我们通常在解释系数之前检查应用条件*(因为如果不遵守这些条件,结果可能会有偏差)。然而,在本文中,我在测试条件之前给出了解释,因为重点是展示如何解释结果,而不是找到一个有效的模型。*
结果可总结如下(参见表格Coefficients中的Estimate栏):
- 截距ˇβ0 = 37.29 表示,假设汽车重量为 0 磅,我们可以预计平均油耗为 37.29 英里/加仑。这种解释是为了说明的目的,但作为一个汽车重量 0 磅是不可能的,这种解释没有任何意义。因此,在实践中,我们不会解释这种情况下的截距。参见部分中自变量以平均值为中心时截距的另一种解释。
- 斜率ˇβ1 =-5.34 表明:
- 重量和一辆汽车用一加仑油可以行驶的距离之间有一个负关系(鉴于之前散点图中各点的负趋势,这是意料之中的)。
- 但更重要的是,斜率为-5.34 意味着,重量增加一个单位(即增加 1000 磅),每加仑的英里数平均减少 5.34 个单位。换句话说,每增加 1000 磅,英里/加仑数平均减少 5.34 。
截距的另一种解释
截距的另一个有用的解释是当自变量以均值为中心时。在这种情况下,截距被解释为 X 值等于 X 平均值的个体的 Y 平均值。
让我们在实践中看到它。我们首先将wt变量置于平均值附近,然后使用这个新变量重新运行一个简单的线性模型:
dat_centered <- datdat_centered$wt_centered <- dat$wt - mean(dat$wt)mod_centered <- lm(mpg ~ wt_centered,
data = dat_centered
)summary(mod_centered)##
## Call:
## lm(formula = mpg ~ wt_centered, data = dat_centered)
##
## Residuals:
## Min 1Q Median 3Q Max
## -4.5432 -2.3647 -0.1252 1.4096 6.8727
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 20.0906 0.5384 37.313 < 2e-16 ***
## wt_centered -5.3445 0.5591 -9.559 1.29e-10 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 3.046 on 30 degrees of freedom
## Multiple R-squared: 0.7528, Adjusted R-squared: 0.7446
## F-statistic: 91.38 on 1 and 30 DF, p-value: 1.294e-10
根据结果,我们看到:
- 斜率没有改变,解释与没有居中的情况相同(这是有意义的,因为回归线只是简单地向右或向左移动)。
- 更重要的是,截距现在是ˇβ0 = 20.09,因此我们可以预计,一辆平均重量的汽车平均油耗为 20.09 英里/加仑(平均重量为 3.22,即 3220 磅)。
这种居中特别有趣:
- 当连续自变量的没有有意义的值为 0 时(这种情况下,重量为 0 磅的汽车没有意义),或者
- 解释截距的时候很重要。
请注意,不一定要仅围绕平均值进行居中。自变量也可以集中在数据范围内的某个值上。只要值有意义并且在数据范围内,居中的确切值并不重要(不建议将其居中在数据范围之外的值上,因为我们不确定该范围之外的两个变量之间的关系类型)。对于我们的例子,我们可能会发现选择最低值或最高值的权重是最佳选择。因此,由我们来决定解释截距最有意义的权重。
关系的重要性
如前所述,斜率的值本身并不能使评估线性关系的显著性成为可能。换句话说,不同于 0 的斜率不一定意味着它与 0 有显著不同,因此不意味着总体中的两个变量之间有显著关系。10°的斜率可能不显著,2°的斜率可能显著。
该关系的显著性还取决于斜率的可变性,斜率通过其标准误差来衡量,通常记为 se(ˇβ1)。
为了评估线性关系的重要性,我们不用过多的细节,将斜率除以其标准误差。该比率是测试统计值,遵循具有 N2 个自由度的学生分布: 5

对于双边检验,无效假设和替代假设是: 6
- H0:β1=0(两个变量之间没有(线性)关系)
- H1:β1≠0(两个变量之间存在(线性)关系)
粗略地说,如果该比率的绝对值大于 2,则斜率明显不同于 0,因此两个变量之间的关系很重要(在这种情况下,它是正还是负取决于估计值的符号ˇβ1)。
标准误和测试统计显示在表格Coefficients的Std. Error和t value栏中。
幸运的是,R 给出了一个更精确和更简单的方法来评估关系的重要性。该信息在Coefficients表的Pr(>|t|)栏中提供。这是测试的p-值。对于任何统计检验,如果p-值大于或等于显著性水平(通常α=0.05),我们不拒绝零假设,如果p-值低于显著性水平,我们拒绝零假设。如果我们不拒绝零假设,我们不拒绝两个变量之间没有关系的假设(因为我们不拒绝斜率为 0 的假设)。相反,如果我们拒绝没有关系的零假设,我们可以得出结论,这两个变量之间存在显著的线性关系。
在我们的示例中,p-value = 1.29 e-10<0.05,因此我们在显著性水平α=5%拒绝零假设。因此,我们得出结论,汽车重量和油耗之间有着显著的关系。
*提示:*为了确保我只解释有意义的参数,我倾向于首先根据 p 值检查参数的意义,然后相应地解释估计值。为了完整起见,请注意测试也是在截距上执行的。p-值小于 0.05,我们还得出截距明显不同于 0 的结论。
相关性并不意味着因果关系
请注意,两个变量之间的显著关系并不一定意味着一个变量对另一个变量有影响,或者这两个变量之间有因果关系!
X 和 Y 之间的显著关系可能出现在几种情况下:
- x 导致 Y
- y 导致 X
- 第三个变量导致 X 和 Y
- 这三个原因的结合
单靠一个统计模型无法建立两个变量之间的因果联系。证明两个变量之间的因果关系更加复杂,除其他外,还需要特定的实验设计、结果随时间的可重复性以及各种样本。这就是为什么你会经常读到“相关性并不意味着因果关系”和线性回归遵循同样的原则。
申请条件
线性回归不能用于所有情况。除了要求因变量必须是连续的定量变量外,简单线性回归还要求数据满足以下条件:
- **线性:**两个变量之间的关系应该是线性的(至少大致如此)。因此,在进行简单的线性回归之前,总是需要用散点图来表示数据。 7

作者的情节
- **独立性:**观察必须是独立的。通常是抽样计划和实验设计提供了这种情况的信息。如果数据来自不同的个人或实验单位,通常是独立的。另一方面,如果在不同时期测量相同的个体,数据很可能不是独立的。
- **残差的正态性:**对于大样本量,无论误差是否遵循正态分布(中心极限定理的一个结果,详见恩斯特和艾伯斯 ( 2017 ))置信区间和对系数的检验都(近似)有效!对于小样本量,残差应遵循正态分布。这种情况可以通过视觉测试(通过 QQ 图和/或直方图),或者更正式地测试(例如通过夏皮罗-维尔克测试)。
- **残差的同方差:**误差的方差应该是常数。当残差的离差随着预测值(拟合值)的增加而增加时,则缺少同方差。这种情况可以通过视觉测试(通过标绘标准化残差与拟合值)或更正式地测试(通过 Breusch-Pagan 测试)。
- 没有影响点:如果数据包含异常值,识别它们是至关重要的,这样它们就不会、自己影响回归的结果。请注意,例如,如果点在回归线的直线上,异常值本身不是问题,因为它不会影响回归线。如果它对估计值(尤其是回归线的斜率)有实质性的影响,那么它就成为线性回归中的一个问题。这可以通过识别异常值并比较有和没有潜在异常值的结果来解决(两种方法的结果是否相同?)
*提示:*我记得前 4 个条件,感谢首字母缩略词“LINE”,表示线性、独立性、正态性和方差相等。
如果不满足任何条件,测试和结论可能是错误的,因此最好避免使用和解释模型。如果是这种情况,通常可以通过变换(例如,对数变换、平方或平方根、Box-Cox 变换等)来满足条件。)的数据。如果没有帮助,可以考虑删除一些变量或添加其他变量,甚至考虑非线性模型。
请记住,在实践中,在根据模型得出任何结论之前,应验证应用条件。我在这里避免测试我们的数据的条件,因为它将在多元线性回归的上下文中详细讨论(参见本部分)。
形象化
如果你是博客的常客,你可能知道我喜欢画(简单而有效)可视化来说明我的统计分析。线性回归也不例外。
有许多方法可以可视化这两个感兴趣的变量之间的关系,但我目前发现的最简单的方法是通过同名包中的visreg()函数:
library(visreg)
visreg(model)

作者的情节
我喜欢这种简单的方法——只有一行代码。
但是,其他元素也可以显示在回归图上(例如回归方程和 R^2).这可以通过{ggpubr}包中的stat_regline_equation()和stat_cor()功能轻松完成:
# load necessary libraries
library(ggpubr)# create plot with regression line, regression equation and R^2
ggplot(dat, aes(x = wt, y = mpg)) +
geom_smooth(method = "lm") +
geom_point() +
stat_regline_equation(label.x = 3, label.y = 32) + # for regression equation
stat_cor(aes(label = ..rr.label..), label.x = 3, label.y = 30) + # for R^2
theme_minimal()

作者的情节
多元线性回归
现在,您已经理解了简单线性回归背后的原理,并且知道了如何解释结果,是时候讨论多元线性回归了。
我们还从多元线性回归的基本原理开始,然后展示如何解释结果,如何测试应用的条件,并以更高级的主题结束。
原则
多元线性回归是简单线性回归的推广,在某种意义上,这种方法可以通过其参数中的线性函数将一个变量与几个变量联系起来。
多元线性回归用于评估两个变量之间的关系,同时考虑其他变量的影响。通过考虑其他变量的影响,我们抵消了这些其他变量的影响,以便隔离并测量两个感兴趣变量之间的关系。这一点是与简单线性回归的主要区别。
为了说明如何在 R 中执行多元线性回归,我们使用与简单线性回归(mtcars)相同的数据集。下面是一个简短的预览:
head(dat)## mpg cyl disp hp drat wt qsec vs am gear carb
## Mazda RX4 21.0 6 160 110 3.90 2.620 16.46 0 1 4 4
## Mazda RX4 Wag 21.0 6 160 110 3.90 2.875 17.02 0 1 4 4
## Datsun 710 22.8 4 108 93 3.85 2.320 18.61 1 1 4 1
## Hornet 4 Drive 21.4 6 258 110 3.08 3.215 19.44 1 0 3 1
## Hornet Sportabout 18.7 8 360 175 3.15 3.440 17.02 0 0 3 2
## Valiant 18.1 6 225 105 2.76 3.460 20.22 1 0 3 1
我们已经看到,一辆汽车用一加仑油可以行驶的距离与其重量之间存在显著的负线性关系(ˇβ1 =-5.34,p 值< 0.001).
However, one may wonder whether there are not in reality other factors that could explain a car’s fuel consumption.
To explore this, we can visualize the relationship between a car’s fuel consumption ( 【 ) together with its weight ( 【 ), horsepower ( 【 ) and displacement ( 【 ) (engine displacement is the combined swept (or displaced) volume of air resulting from the up-and-down movement of pistons in the cylinders, usually the higher the more powerful the car):
ggplot(dat) +
aes(x = wt, y = mpg, colour = hp, size = disp) +
geom_point() +
scale_color_gradient() +
labs(
y = "Miles per gallon",
x = "Weight (1000 lbs)",
color = "Horsepower",
size = "Displacement"
) +
theme_minimal()

Plot by author
It seems that, in addition to the negative relationship between miles per gallon and weight, there is also:
- a negative relationship between miles/gallon and horsepower (lighter points, indicating more horsepower, tend to be more present in low levels of miles per gallon)
- a negative relationship between miles/gallon and displacement (bigger points, indicating larger values of displacement, tend to be more present in low levels of miles per gallon).
Therefore, we would like to evaluate the relation between the fuel consumption and the weight, but this time by adding information on the horsepower and displacement. By adding this additional information, we are able to 仅捕获英里/加仑与重量之间的直接关系(马力和排量产生的间接影响被抵消)。
这就是多元线性回归的全部意义!事实上,在多元线性回归中,因变量和解释变量之间的估计关系是一种调整的关系,即不受其他解释变量的线性影响。
让我们通过在线性回归模型中增加马力和排量来说明调整的概念:
model2 <- lm(mpg ~ wt + hp + disp,
data = dat
)summary(model2)##
## Call:
## lm(formula = mpg ~ wt + hp + disp, data = dat)
##
## Residuals:
## Min 1Q Median 3Q Max
## -3.891 -1.640 -0.172 1.061 5.861
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 37.105505 2.110815 17.579 < 2e-16 ***
## wt -3.800891 1.066191 -3.565 0.00133 **
## hp -0.031157 0.011436 -2.724 0.01097 *
## disp -0.000937 0.010350 -0.091 0.92851
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 2.639 on 28 degrees of freedom
## Multiple R-squared: 0.8268, Adjusted R-squared: 0.8083
## F-statistic: 44.57 on 3 and 28 DF, p-value: 8.65e-11
我们可以看到,现在,英里/加仑与重量之间的关系在斜率方面变弱了(现在β1= -3.8,而只考虑重量时β1= -5.34)。
重量对油耗的影响根据马力和排量的影响进行调整。这是在考虑了马力和排量的影响后,英里/加仑和重量之间的剩余影响。在本章节中有更详细的解释。
等式
多重线性回归模型由以下等式定义

它类似于简单线性回归方程,除了有多个独立变量(X1,X2,…,Xp)。
通过最小二乘法对参数β0,…,βp 的估计基于与简单线性回归相同的原理,但是应用于 pp 维度。因此,问题不再是找到最佳线(最靠近成对点(yi,xi)的线),而是找到最靠近坐标点(yi,xi1,…,xip)的 pp 维平面。这是通过 最小化平面上的点的偏差的平方和来实现的:

资料来源:Thaddeussegura
系数ˇβ的解释
最小二乘法导致系数的调整估计。术语“调整”是指在考虑了其他独立变量对因变量以及预测变量的线性影响后的**。**
换句话说,当其他解释变量(X2,…,Xp)的线性效应被移除时,系数β1 对应于 Y 和 X1 之间关系的斜率,既在因变量 Y 的水平也在 X1 的水平。
将重量、马力和排量作为独立变量应用于我们的模型,我们得到:
summary(model2)##
## Call:
## lm(formula = mpg ~ wt + hp + disp, data = dat)
##
## Residuals:
## Min 1Q Median 3Q Max
## -3.891 -1.640 -0.172 1.061 5.861
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 37.105505 2.110815 17.579 < 2e-16 ***
## wt -3.800891 1.066191 -3.565 0.00133 **
## hp -0.031157 0.011436 -2.724 0.01097 *
## disp -0.000937 0.010350 -0.091 0.92851
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 2.639 on 28 degrees of freedom
## Multiple R-squared: 0.8268, Adjusted R-squared: 0.8083
## F-statistic: 44.57 on 3 and 28 DF, p-value: 8.65e-11
表Coefficients给出了每个参数的估计值(列Estimate),以及参数无效值的 p 值(列Pr(>|t|))。
假设与简单线性回归相同,即:
- H0:βj=0
- H1:βj≠0
βj=0 的检验相当于检验假设:在所有其他条件相同的情况下,因变量是否与所研究的自变量相关联,也就是说,处于其他自变量的恒定水平。
换句话说:
- β1=0 的测试对应于测试假设:在马力和排量不变的情况下,油耗与汽车重量有关吗
- β2=0 的测试对应于测试假设:在重量和排量不变的情况下,油耗与马力相关吗
- β3=0 的测试对应于测试假设:在重量和排量不变的情况下,燃油消耗是否与排量相关
- (为了完整起见:β0=0 的测试对应于测试假设:当重量、马力和排量等于 0 时,英里/加仑是否不同于 0)
在实践中,我们通常在解释系数之前检查应用条件*(因为如果不遵守这些条件,结果可能会有偏差)。然而,在本文中,我在测试条件之前给出了解释,因为重点是展示如何解释结果,而不是找到一个有效的模型。*
基于我们模型的输出,我们得出结论:
- 在英里/加仑和重量之间有一个显著的负相关关系,在其他条件相同的情况下。因此,重量每增加一个单位(即增加 1000 磅),在马力和排量保持不变的情况下,平均每加仑行驶英里数减少 3.8 英里(p 值= 0.001)。
- 在其他条件相同的情况下,英里/加仑和马力之间存在显著的负相关关系。因此,在重量和排量不变的情况下,每增加一个单位的马力,一加仑汽油行驶的距离平均减少 0.03 英里(p 值= 0.011)。
- 当重量和马力保持不变时,我们不拒绝英里/加仑和排量之间没有关系的假设(因为 p 值= 0.929 > 0.05)。
- (为了完整性,但只有当它有意义时才应该被解释:对于重量、马力和排量= 0,我们可以预计一辆汽车平均油耗为 37.11 英里/加仑(p 值< 0.001). See a more useful interpretation of the intercept when the independent variables are centered in this 部分)。)
这就是如何解释定量自变量。解释定性独立变量略有不同,因为在其他条件相同的情况下,它量化了某一水平相对于参考水平的影响。因此,它根据因变量比较不同的组(由分类变量的不同水平形成)(这就是为什么线性回归可以被视为 t 检验和 ANOVA 的扩展)。
为了说明,我们根据重量(wt)和发动机的形状(vs)来模拟燃料消耗(mpg)。变量vs有两级:V 型参考级和直列发动机。 7
## Recoding dat$vs
library(forcats)
dat$vs <- as.character(dat$vs)
dat$vs <- fct_recode(dat$vs,
"V-shaped" = "0",
"Straight" = "1"
)model3 <- lm(mpg ~ wt + vs,
data = dat
)summary(model3)##
## Call:
## lm(formula = mpg ~ wt + vs, data = dat)
##
## Residuals:
## Min 1Q Median 3Q Max
## -3.7071 -2.4415 -0.3129 1.4319 6.0156
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 33.0042 2.3554 14.012 1.92e-14 ***
## wt -4.4428 0.6134 -7.243 5.63e-08 ***
## vsStraight 3.1544 1.1907 2.649 0.0129 *
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 2.78 on 29 degrees of freedom
## Multiple R-squared: 0.801, Adjusted R-squared: 0.7873
## F-statistic: 58.36 on 2 and 29 DF, p-value: 6.818e-11
基于我们模型的输出,我们得出结论:
- 对于 V 型发动机,重量增加一个单位(即增加 1000 磅),平均每加仑行驶里程数减少 4.44 英里(p 值= 0.013)。
- (为了完整性,但只有当它有意义时才应该解释:对于重量= 0 和 V 形发动机,我们可以预计汽车的平均油耗为 33 英里/加仑(p 值< 0.001). See a more useful interpretation of the intercept when the independent variables are centered in this 部分)。)
- 申请条件
对于简单线性回归,多元线性回归需要一些应用条件,以使模型可用,结果可解释。简单线性回归的条件也适用于多元线性回归,即:
因变量和自变量之间关系的线性8
- 观察值的独立性
- 残差的正态性
- 残差的同方差
- 无影响点 ( 异常值)
- 但是多元线性回归还有一个条件:
**无多重共线性:**当自变量之间存在强线性https://statsandr.com/blog/correlation-coefficient-and-correlation-test-in-r/相关性时,出现多重共线性,条件是模型中的其他变量。检查它是很重要的,因为当变量改变时,它可能导致估计参数的不精确或不稳定。可以通过研究每对独立变量之间的相关性来评估,或者更好的是,通过计算方差膨胀因子(VIF)来评估。相对于解释变量严格独立的情况,VIF 衡量估计回归系数的方差增加了多少。高 VIF 值是多重共线性的标志(根据域的不同,阈值通常为 5 或 10)。减少 VIF 最简单的方法是去除一些相关的独立变量,或者最终将数据标准化。
- 你会经常看到这些条件通过运行
plot(model, which = 1:6)得到验证,完全正确。然而,我最近从{performance}包中发现了check_model()函数,它同时测试所有这些条件(老实说,以一种更优雅的方式)。 9
应用在我们的model2上,以英里/加仑为因变量,重量、马力和排量为自变量,我们得到:
作者的情节
# install.packages("performance")
# install.packages("see")
library(performance)check_model(model2)

除了同时测试所有条件之外,它还提供了关于如何解释不同诊断图以及您应该期待什么的见解(参见每个图的副标题):
方差的同质性(右上图)受到重视
- 多重共线性(左中图)不是问题(对于 VIF,我倾向于使用阈值 10,并且它们都低于 10) 11
- 没有有影响的点(右中图)
- **由于 3 个点偏离参考线,残差的正态性(两个底部图)也不完美,但对我来说似乎仍然可以接受。在任何情况下,给定参数 12 的数量,并且给定与正态分布的小偏差,观察值的数量足够大,因此无论误差是否遵循正态分布,对系数的测试都(近似)有效**
- 线性(左上图)并不完美,所以让我们分别检查每个独立变量:
- 作者的情节
# weight
ggplot(dat, aes(x = wt, y = mpg)) +
geom_point() +
theme_minimal()

作者的情节
# horsepower
ggplot(dat, aes(x = hp, y = mpg)) +
geom_point() +
theme_minimal()

作者的情节
# displacement
ggplot(dat, aes(x = disp, y = mpg)) +
geom_point() +
theme_minimal()

似乎英里/加仑和马力之间的关系不是线性的,这可能是该模型的轻微线性缺陷的主要组成部分。为了提高线性度,可以移除变量或应用变换(例如对数和/或平方)。 13 如果这不能解决线性问题,可以考虑其他类型的模型。
为了简单和说明的目的,我们假设本文的其余部分是线性的。
当满足应用条件时,我们通常说模型有效。但并不是所有有效的型号都是好的型号。下一节讨论模型选择。
如何选择好的线性模型?
满足应用条件的模型是最低要求,但您可能会找到几个满足此标准的模型。因此,有人可能会问如何在所有有效的不同模型中做出选择?
选择一个好的线性模型的三个最常用的工具是根据:
与模型相关联的 p 值,
- 决定系数 R^2 和
- 阿凯克信息标准
- 这些方法将在接下来的章节中详细介绍。注意,前两个适用于简单和多元线性回归,而第三个仅适用于多元线性回归。
与模型关联的 p 值
在解释模型的估计值之前,最好先检查与模型相关的 p 值。该 p 值指示该模型是否比仅具有截距的模型更好。****
测试的假设(称为 f 检验)是:
H0:β1=β2=⋯=βp=0
- H1:H1:至少有一个系数β≠0
- 该 p 值可在
summary()输出的底部找到:
p 值= 8.65e-11。零假设被拒绝,因此我们得出结论,我们的模型比只有截距的模型更好,因为至少有一个系数β明显不同于 0。
summary(model2)##
## Call:
## lm(formula = mpg ~ wt + hp + disp, data = dat)
##
## Residuals:
## Min 1Q Median 3Q Max
## -3.891 -1.640 -0.172 1.061 5.861
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 37.105505 2.110815 17.579 < 2e-16 ***
## wt -3.800891 1.066191 -3.565 0.00133 **
## hp -0.031157 0.011436 -2.724 0.01097 *
## disp -0.000937 0.010350 -0.091 0.92851
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 2.639 on 28 degrees of freedom
## Multiple R-squared: 0.8268, Adjusted R-squared: 0.8083
## F-statistic: 44.57 on 3 and 28 DF, p-value: 8.65e-11
如果其中一个模型的 p 值> 0.05,这意味着您选择的变量没有一个有助于解释因变量。换句话说,你应该完全忘记这个模型,因为它不会比简单地取因变量的平均值更好。
决定系数 R^2
决定系数 R^2 是模型的拟合优度的度量。它衡量模型解释的总可变性的比例,或模型与数据的拟合程度。****
R^2 在 0 和 1 之间变化:
R^2=0:这个模型解释不了什么
- R^2=1:模型解释了一切
- 0
- the higher the R2, the better the model explains the dependent variable. As a rule of thumb, a R2> 0.7 表示模型 13 的良好拟合
- 注意,在简单的线性回归模型中,决定系数等于相关系数的平方:R^2=corr(X,Y)^2.
将英里/加仑作为因变量,重量、马力和排量作为自变量,应用于我们的model2,我们得到:
R^2 显示在summary()输出的底部,或者可以用summary(model2)$r.squared提取。
summary(model2)##
## Call:
## lm(formula = mpg ~ wt + hp + disp, data = dat)
##
## Residuals:
## Min 1Q Median 3Q Max
## -3.891 -1.640 -0.172 1.061 5.861
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 37.105505 2.110815 17.579 < 2e-16 ***
## wt -3.800891 1.066191 -3.565 0.00133 **
## hp -0.031157 0.011436 -2.724 0.01097 *
## disp -0.000937 0.010350 -0.091 0.92851
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 2.639 on 28 degrees of freedom
## Multiple R-squared: 0.8268, Adjusted R-squared: 0.8083
## F-statistic: 44.57 on 3 and 28 DF, p-value: 8.65e-11
该车型的 R2 为 0.8268,这意味着一加仑汽油行驶距离的 82.68%由汽车的重量、马力和排量决定。相对较高的 R2 意味着汽车的重量、马力和排量是解释一加仑燃料可以行驶的距离的良好特征。
注意,如果要比较自变量数量不同的模型,最好参考调整后的 R2 (=此处为 0.8083)。的确,在模型中加入变量并不能使 R2 降低,即使变量与因变量无关(所以在模型中加入变量时 R2 会人为增加,或者至少保持不变)。因此,调整后的 R2 通过惩罚额外的变量来考虑模型的复杂性(变量的数量),因此它是拟合优度和节俭之间的折衷。
吝啬
一个简约的模型(很少的变量)通常比一个复杂的模型(很多变量)更受青睐**。有两种方法可以从具有许多独立变量的模型中获得一个简洁的模型:**
我们可以反复删除与因变量最不相关的自变量(即在方差分析表中具有最高 p 值的那个),直到它们都与响应变量显著相关,或者
- 我们可以根据赤池信息标准(AIC)** 选择模型。AIC 表达了用尽可能少的系数拟合模型的愿望,并允许对模型进行比较。根据这个标准,最好的模型是具有最低 AIC 的模型。该标准基于拟合质量和其复杂性之间的折衷。我们通常从具有许多独立变量的全局模型开始,程序(称为逐步算法) 14 自动比较模型,然后根据 AIC 选择最佳模型。**
- 我们展示了如何在 r 中执行第二个选项。为了便于说明,我们从一个模型开始,该模型将数据集中的所有变量作为独立变量(不要忘记首先转换因子变量):
(公式mpg ~ .是将数据集中存在的所有变量视为自变量的捷径,除了已被指定为因变量的变量(此处为mpg)。
## vs has already been transformed into factor
## so only am is transformed here## Recoding dat$vs
library(forcats)
dat$am <- as.character(dat$am)
dat$am <- fct_recode(dat$am,
"Automatic" = "0",
"Manual" = "1"
)model4 <- lm(mpg ~ .,
data = dat
)model4 <- step(model4, trace = FALSE)
根据这一标准选择的型号如下:
使用自动程序时要小心,因为即使它是所选的最佳模型,它也是基于:
summary(model4)##
## Call:
## lm(formula = mpg ~ wt + qsec + am, data = dat)
##
## Residuals:
## Min 1Q Median 3Q Max
## -3.4811 -1.5555 -0.7257 1.4110 4.6610
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 9.6178 6.9596 1.382 0.177915
## wt -3.9165 0.7112 -5.507 6.95e-06 ***
## qsec 1.2259 0.2887 4.247 0.000216 ***
## amManual 2.9358 1.4109 2.081 0.046716 *
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 2.459 on 28 degrees of freedom
## Multiple R-squared: 0.8497, Adjusted R-squared: 0.8336
## F-statistic: 52.75 on 3 and 28 DF, p-value: 1.21e-11
在一个单一的标准(AIC 在这种情况下),但更重要的是;
- 它基于一些数学规则,这意味着不考虑行业知识或人类专业知识。
- 我相信这种模型选择的自动程序是一个很好的起点,但是我也相信最终的模型应该总是与其他模型进行检查和测试,以确保它在实践中是有意义的(应用常识)。
最后但同样重要的是,不要忘记验证应用条件,因为逐步程序不能保证它们得到遵守。
形象化
有许多方法可以将线性回归的结果可视化。我知道的最简单的两个是:
visreg()它在不同的图中说明了因变量和自变量之间的关系(每个自变量一个,除非你指定了你想要说明的关系):
- 作者的情节
library(visreg)
visreg(model4)

作者的情节

作者的情节

ggcoefstats()在一张图中显示了结果:
- 作者的情节
library(ggstatsplot)
ggcoefstats(model4)

在该图中:
当实线不与垂直虚线交叉时,估计值在 5%显著性水平上显著不同于 0(即 p 值< 0.05)
- furthermore, a point to the right (left) of the vertical dashed line means that there is a positive (negative) relationship between the two variables
- the more extreme the point, the stronger the relationship
- To go further
Below some more advanced topics related to linear regression. Feel free to comment at the end of the article if you believe I missed an important one.
Extract model’s equation
You can easily extract the equation of your linear model in LaTeX or directly in your R Markdown document thanks to the 【 function from the 【 package:
The function works for linear regression, but also for many other models such as ANOVA 、GLM、逻辑回归等)。
library(equatiomatic)
extract_eq(model4, use_coefs = TRUE)

自动报告
同名软件包中的report()功能允许根据最佳实践指南自动生成模型报告:
请注意,该函数也适用于数据框架、统计测试和其他模型。
library(report)report(model4)## We fitted a linear model (estimated using OLS) to predict mpg with wt, qsec and am (formula: mpg ~ wt + qsec + am). The model explains a statistically significant and substantial proportion of variance (R2 = 0.85, F(3, 28) = 52.75, p < .001, adj. R2 = 0.83). The model's intercept, corresponding to wt = 0, qsec = 0 and am = Automatic, is at 9.62 (95% CI [-4.64, 23.87], t(28) = 1.38, p = 0.178). Within this model:
##
## - The effect of wt is statistically significant and negative (beta = -3.92, 95% CI [-5.37, -2.46], t(28) = -5.51, p < .001; Std. beta = -0.64, 95% CI [-0.87, -0.40])
## - The effect of qsec is statistically significant and positive (beta = 1.23, 95% CI [0.63, 1.82], t(28) = 4.25, p < .001; Std. beta = 0.36, 95% CI [0.19, 0.54])
## - The effect of am [Manual] is statistically significant and positive (beta = 2.94, 95% CI [0.05, 5.83], t(28) = 2.08, p = 0.047; Std. beta = 0.49, 95% CI [7.59e-03, 0.97])
##
## Standardized parameters were obtained by fitting the model on a standardized version of the dataset.
预言
线性回归也经常用于预测目的**。可以用predict()函数计算出新数据的置信区间和预测区间。**
假设我们想预测一辆带手动变速器的汽车的英里/加仑数,该车重 3000 磅,在 18 秒内行驶 1/4 英里(qsec):
根据我们的模型,预计这辆车一加仑油可行驶 22.87 英里。
# confidence interval for new data
predict(model4,
new = data.frame(wt = 3, qsec = 18, am = "Manual"),
interval = "confidence",
level = .95
)## fit lwr upr
## 1 22.87005 21.09811 24.642# prediction interval for new data
predict(model4,
new = data.frame(wt = 3, qsec = 18, am = "Manual"),
interval = "prediction",
level = .95
)## fit lwr upr
## 1 22.87005 17.53074 28.20937
置信区间和预测区间的区别在于:
一个置信区间给出了一个新观察值的 Y 的平均值的预测值,而
- 一个预测区间给出了一个个体** Y 对于一个新观察的预测值。**
- 预测区间比置信区间宽,以说明由于预测个体反应而产生的额外不确定性,而不是给定 x 值的平均值****
线性假设检验
线性假设检验可以推广第节中提到的 f 检验,同时提供系数比较检验或系数线性组合相等检验的可能性。
例如,要测试线性约束:
H0:β1=β2=0
- H1:H1:不是 H0
- 我们使用
{car}包的linearHypothesis()函数如下:
我们拒绝零假设,我们得出结论,β1 和β2 中至少有一个不同于 0 (p 值= 1.55e-09)。
library(car)
linearHypothesis(model4, c("wt = 0", "qsec = 0"))## Linear hypothesis test
##
## Hypothesis:
## wt = 0
## qsec = 0
##
## Model 1: restricted model
## Model 2: mpg ~ wt + qsec + am
##
## Res.Df RSS Df Sum of Sq F Pr(>F)
## 1 30 720.90
## 2 28 169.29 2 551.61 45.618 1.55e-09 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
分类变量的总体效应
当自变量是 k 个类别的分类变量时,回归表提供 k1 p 值:
**变量vs和am有两个级别,因此回归输出中显示一个级别。变量cyl有 3 个级别(4、6 和 8),所以显示其中的 2 个。vs和am的总体效果在Pr(>|t|)栏中报告,但不包括cyl的**总体**效果,因为该变量有 2 个以上的级别。**
model5 <- lm(mpg ~ vs + am + as.factor(cyl),
data = dat
)summary(model5)##
## Call:
## lm(formula = mpg ~ vs + am + as.factor(cyl), data = dat)
##
## Residuals:
## Min 1Q Median 3Q Max
## -6.2821 -1.4402 0.0391 1.8845 6.2179
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 22.809 2.928 7.789 2.24e-08 ***
## vsStraight 1.708 2.235 0.764 0.45135
## amManual 3.165 1.528 2.071 0.04805 *
## as.factor(cyl)6 -5.399 1.837 -2.938 0.00668 **
## as.factor(cyl)8 -8.161 2.892 -2.822 0.00884 **
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 3.097 on 27 degrees of freedom
## Multiple R-squared: 0.7701, Adjusted R-squared: 0.736
## F-statistic: 22.61 on 4 and 27 DF, p-value: 2.741e-08
为了获得分类变量总体效应的 p 值,我们需要通过{car}包中的Anova()函数获得方差分析表: 15
从这张方差分析表中,我们得出结论:
library(car)
Anova(model5)## Anova Table (Type II tests)
##
## Response: mpg
## Sum Sq Df F value Pr(>F)
## vs 5.601 1 0.5841 0.45135
## am 41.122 1 4.2886 0.04805 *
## as.factor(cyl) 94.591 2 4.9324 0.01493 *
## Residuals 258.895 27
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
vs与mpg无明显关联(p 值= 0.451)
am和cyl与mpg显著相关(p 值< 0.05)- 互动
到目前为止,我们已经讨论了没有任何交互作用的多元线性回归。如果因素 A 对响应的影响取决于因素 B 所采取的水平,则因素 A 和 B 之间存在相互作用影响。****
在 R 中,可以按如下方式添加交互:
从输出中我们得出结论,重量和传输之间存在相互作用(p 值= 0.00102)。这意味着重量对一加仑行驶距离的影响取决于变速箱类型**。**
model6 <- lm(mpg ~ wt + am + wt:am,
data = dat
)# Or in a shorter way:
model6 <- lm(mpg ~ wt * am,
data = dat
)summary(model6)##
## Call:
## lm(formula = mpg ~ wt * am, data = dat)
##
## Residuals:
## Min 1Q Median 3Q Max
## -3.6004 -1.5446 -0.5325 0.9012 6.0909
##
## Coefficients:
## Estimate Std. Error t value Pr(>|t|)
## (Intercept) 31.4161 3.0201 10.402 4.00e-11 ***
## wt -3.7859 0.7856 -4.819 4.55e-05 ***
## amManual 14.8784 4.2640 3.489 0.00162 **
## wt:amManual -5.2984 1.4447 -3.667 0.00102 **
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## Residual standard error: 2.591 on 28 degrees of freedom
## Multiple R-squared: 0.833, Adjusted R-squared: 0.8151
## F-statistic: 46.57 on 3 and 28 DF, p-value: 5.209e-11
处理交互的最简单方法是可视化分类变量的每个级别的关系:
作者的情节
visreg(model6, "wt", by = "am")

我们看到,与自动变速器的汽车相比,手动变速器的汽车重量和英里/加仑之间的关系更强(斜率更陡)。
这是一个很好的例子,说明当研究两个变量(比如 X 和 Y)之间的关系时,如果也有可能与 X 和 Y 都相关的其他变量的数据,将它们包括在回归中并根据这些变量有条件地分析关系是很重要的。****
忽略一些应该包含在模型中的变量可能会导致错误和误导性的结论,直到关系完全颠倒(这种现象被称为辛普森悖论)。
摘要
在本文中,我们首先提醒一下简单线性回归,特别是它的原理以及如何解释结果。
这为更好地理解多元线性回归奠定了基础。在解释了其原理之后,我们展示了如何解释输出以及如何选择一个良好的线性模型。然后我们提到了几个可视化,并以更多的高级主题结束了这篇文章。
感谢阅读。我希望这篇文章能帮助你更好地理解线性回归,并给你信心在 r。
和往常一样,如果您有与本文主题相关的问题或建议,请将其添加为评论,以便其他读者可以从讨论中受益。
参考
奥斯汀,彼得 C,和 Ewout W Steyerberg。2015."线性回归分析中每个变量所需的受试者人数."临床流行病学杂志68(6):627–36。
恩斯特,安雅 F,卡斯帕 J 艾伯斯。2017.“临床心理学研究实践中的回归假设?对常见误解的系统回顾。”PeerJ 5: e3323。
詹姆斯、加雷思、丹妮拉·威滕、特雷弗·哈斯蒂和罗伯特·蒂布拉尼。2013.统计学习入门。第 112 卷。斯普林格。
有些人将回归分析视为推断统计学的一部分。这是真的,因为一个样本是用来评估一个群体中两个或更多变量之间的联系的。我倾向于将回归与推断统计区分开来,原因很简单:( I)回归通常在更广的范围内使用(用于预测分析等),并且因为(ii)线性回归的主要目标(参见本部分)不同于推断统计领域众所周知的置信区间和假设检验的目标。 ↩︎
- 形式上,ANOVA 也可用于比较 2 组,但实际上我们倾向于将其用于 3 组或更多组,将 t 检验留给 2 组。 ↩︎
- 关于数据集的更多信息可以通过执行
?mtcars找到。 ↩︎ - 请注意,当 X 不能等于 0 或在实践中没有意义时,最好避免解释截距。 ↩︎
- n 是观察次数。 ↩︎
- 也接受 0 以外的其他值。在这种情况下,测试统计变为 tn2 =ˇβa/se(ˇβ1 ),其中 a 为假设斜率。 ↩︎
- 请注意,可以通过两个变量的散点图或残差和拟合值的散点图来检查线性。在的部分可以看到更多相关信息。 ↩︎
- 您可以随时使用
relevel()功能更改参考电平。查看更多数据操作技术。 ↩︎ - 请注意,也可以对残差进行线性测试。 ↩︎
- 安装完
{performance}包后,您还需要手动安装{see}包。如果需要更多帮助,请参见如何安装 R 包。 ↩︎ - 我使用阈值 10 是因为,如James et al .(2013)所示,介于 5 和 10 之间的值表示中度相关,而大于 10 的 VIF 值表示高度和不可容忍的相关。 ↩︎
- Austin 和 Steyerberg ( 2015 )表明,在使用普通最小二乘法估计的线性回归模型中,每个变量有两个受试者往往能够准确估计回归系数。我们的数据集包含 32 个观察值,远远超过每个变量两个受试者的最小值。 ↩︎
- 如果您应用对数变换,请参见关于如何解释结果的两个指南:英文版和法文版分别为和。 ↩︎
- 请注意,高 R2 并不保证您选择了最好的变量或您的模型是好的。它只是告诉我们模型与数据吻合得很好。建议在比较模型时运用常识,不要只参考 R2(特别是当 R^2 接近时)。 ↩︎
- 主要有两种方法;向后向前。反向方法包括从包含所有可能相关的解释变量的模型开始,然后递归地去除降低模型的信息标准的变量,直到不可能降低为止。向前方法是向后方法的反向方法,从这个意义上说,我们从一个具有最低信息标准的单变量模型开始,并且在每一步都添加一个解释变量。默认情况下,R 中的
step()函数组合了向后和向前方法。 ↩︎ - 不要与
anova()函数混淆,因为它提供的结果取决于变量在模型中出现的顺序。 ↩︎ - 相关文章
手工假设检验
- R 中的方差分析
- R 中的离群点检测
- R 中的 Wilcoxon 检验:如何在非正态假设下比较两组
- R 中的相关系数和相关检验
- 原载于 2021 年 10 月 4 日 https://statsandr.comhttps://statsandr.com/blog/multiple-linear-regression-made-simple/。****
Originally published at https://statsandr.com on October 4, 2021.
多元线性回归 Python 101
原文:https://towardsdatascience.com/multiple-linear-regression-python-101-af459110a8af?source=collection_archive---------3-----------------------
入门
数据准备和预测建模分步指南

来源:Adobe 股票许可
开始使用 Python 构建您的第一个多元线性回归预测模型可能会令人生畏!这篇文章提供了一个基于 CRISP-DM 的实用工作流程、指南和示例代码。希望你会觉得有用,欢迎评论。
线性回归的概念性工作流程
数据挖掘的跨行业标准流程是描述数据科学生命周期的领先流程模型。这个项目在建立线性回归模型时遵循以下战术工作流程。该流程图对 CRISP-DM 流程的四个子任务进行排序,涵盖数据理解、数据准备、建模和评估。

来源:作者
流程中固有的简单想法包括:
- 在太多的概要分析和分析之前拆分您的数据
- 为每个特性迭代一组通用的数据准备任务
- 遍历包含不同特性的各种模型
- 使用函数在整个项目中保持数据转换的一致性和代码效率
现在让我们开始,一步一步地完成一个项目。
数据准备工作流程
我使用的是华盛顿金县的房屋销售数据集,它在 Kaggle 和数据科学训练营上很受欢迎。数据包含 21 列,横跨>20000 个在 2014–2015之间跨越 12 个月完成的西雅图大都会房屋销售交易。多元线性回归模型将使用普通最小二乘法(OLS)并预测连续变量“房屋销售价格”。
数据、Jupyter 笔记本和 Python 代码可在 my GitHub 获得。
步骤 1 —数据准备基础知识
为了开始了解我们的数据,该流程包括以下基本任务:
- 加载数据
- 组合和集成数据
- 转换数据类型
- 修复数据质量问题
- 删除重复项
- 使数据干净、格式化和可用的步骤
为了方便访问,我将我所有的库导入捆绑在一个地方。
下一步是加载我们的。csv 数据转换成熊猫数据帧,并采取窥视。

该文件包含住宅列表标准属性;我的笔记本里有一本数据字典。
乍一看,注意浴室中的小数值和 yr _ renovated 和滨水区中的“nan”null。下面使用 DataFrame.info(),我们注意到需要解决的数据类型和 null 问题。

因此,基于这一点和这里没有显示的几个 Series.value_counts(),我们可以进行调整以清理我们的房屋销售数据。
现在让我们使用 Dataframe.describe()来看看基本的统计数据和范围。

回顾上表可以看出:
- 价格:偏正,平均值高于中值。异常值为 770 万美元
- 卧室:异常值在 33
- Sqft_Lot:在 1.6M 处有明显的异常值,平均值接近中值的两倍。
- 海滨:人口稀少,很少有 1。
- yr _ revealed:75%的小部分房屋为 0。
经过一番考虑,我放弃了 33 间卧室的单个记录。除了这个记录,只有少数家庭有 8 或 9 间卧室,所以我只是把这个作为一个可能的数据输入错误。即使在几个属性上有其他异常值,我也没有过滤或删除任何其他记录。
处理重复数据、合并其他数据或任何其他常规数据准备任务都可以包含在此数据准备基础流程中。现在,数据是一致的,并且准备好被分割用于数据剖析、相关性分析和特征工程。
步骤 2——将数据分成训练集和测试集
对于将在新数据上使用的预测模型来预测生产,在分析和分析之前拆分数据有助于防止数据泄漏。
数据泄露:当你用来训练机器学习算法的数据恰好拥有你试图预测的信息。
鉴于这一分析是一次性的历史工作,我在做了相关分析后将数据分开。这意味着我使用整个样本开发特性。然而,我在第 2 步展示了分离逻辑,以与上面的最佳实践图保持一致。
上面的 Python 代码片段创建了 X(预测值)和 y(目标值)数据集。作为目标因变量,我包括价格,价格的对数和价格的 box-cox 变换。这三个目标选项是在模型开发过程中反复开发的。
步骤 3 —数据分析和转换
剖析和可视化探索数据有助于评估潜在的特征和关系。虽然任何类型的探索都是公平的游戏,但我反复关注每个预测者 X(例如卧室)的这些目标和目标 y(例如价格日志)。
- 检查一段时间内的变量趋势
- 确认线性关系(X-y)
- 调查异常值
- 评估变量分布、正态性和偏斜
- 用一键编码转换变量类
类别变量图
我创建了一个函数(称为 distplots,在我的 Jupyter 笔记本中可用)来绘制任何 X 预测值,包括直方图、散点图、箱线图和前 N 个累积值。我假设我的模型中的一个变量是卧室,因为这通常是买卖房屋时的区分描述符。

回顾输出,有几个观察结果:
- 剔除 33 间卧室的异常值后,只有 0.3%的家庭拥有 6 间以上的卧室
- 中位数逻辑上落在 3 到 4 间卧室之间
- 箱线图显示了随着卧室数量的增加,中值价格在增加
…但最终卧室和价格之间的相关性很弱,只有 0.32,没有影响我的模型性能,所以我没有考虑。
我还使用 distplots 函数来可视化其他变量。
平均居住面积趋势
如果你有基于时间的变量,分析趋势有助于看到方向或势头。例如,在过去的一个世纪里,家庭的平均居住面积增加了。由于一些老房子有更多的卧室,我绘制了第二个轴来查看每个卧室的居住面积。这里的代码是一个双轴条形图,带有使用 Seaborn 和 Matplotlib 库的趋势线。

这张图表显示了 20 世纪 70 年代西雅图地区居住空间的“起飞”。对于自 1970 年以来建造的房屋,我们看到居住面积增加了 32%,平均卧室增加了 9%,每间卧室增加了 21%。sqft_per_bedroom 功能作为 living_sqft 的补充功能制作了最终模型。

Pairplot 关联分析
另一种数据分析技术是直观地检查变量的交集以寻找关联。这里我使用了 Seaborn 的 pairplot,它为每个组合创建了一个散点图,在对角线上留下了一个直方图。

- 与价格成线性关系:等级、条件、浴室
- 价格不确定或非线性:建造年份、指标、卧室
一键编码
一种常见的方法是一键编码(OHE ),将分类变量转换为特征。我测试了几个 OHE 变量,如条件、等级、卧室和浴室,看看它们是否比连续变量表现得更好。我发现每一个都作为连续变量更好。
最后我得到了两个 OHE 变量:zipcode 和 grade_grp(将住宅建筑群从 3-13 简化为 4 类)。我用了熊猫。get_dummies()方法将要素透视到 OHE 列,删除第一个类以最小化纯多重共线性风险。

步骤 4 —相关性分析
下一步是查看与这些目标的相关性:
- 验证哪些预测要素(X)与目标(y)相关
- 检查多重共线性预测值之间的相关性
皮尔逊相关系数(r) —目标 y
根据预测值 price_log (y)检查每个要素(X)的皮尔逊相关系数。相关性将提供对在线性回归模型中表现良好的特征的洞察。我使用这些结果以及相关矩阵热图作为包含哪些功能的指南。

这四个标有星号的特征具有中等或强相关性,并构成了最终的 OLS 模型。
- 强相关性(0.6–0.8):sqft _ living,grade,sqft_above,zip_psf_median,sqft_living15
- 中度相关(0.4–0.6):浴室,景观
检查多重共线性
当自变量相关时,表明一个变量的变化与另一个变量的变化相关。相关性越强,改变一个变量而不改变另一个变量就越困难。—吉姆·弗罗斯特“回归分析”
查看预测值之间的相关性是避免选取具有可能导致模型无效的强多重共线性的要素的关键步骤。代码块创建变量对,并按照最高相关性对它们进行排序。

突出显示两种不同的场景(上面标有星号):
- sqft _ living with sqft_above 0.876 correlation-我尝试在我的模型#3 中纳入 sqft _ above,但由于多重共线性而放弃了这一点。
- Grade,Sqft_living 的相关系数为 0.76——即使高度相关,我也能够包含这两个特性,而不会出现重大问题。
这里的建议是高度重视使用皮尔逊或其他统计数据(如方差膨胀因子(VIF))的预测值相关性。
第五步——设计新功能
第一次通过数据集创建线性回归模型时,最初可能不需要任何新功能。但是在您的第一次模型迭代之后,潜在的转换包括:
- 添加一个混杂变量以减少模型偏差
- 将要素转换为人均要素或表示为比率
- 使用求逆、对数、平方根或 box-cox 对特征进行幂变换
- 添加多项式特征(例如 X)以更好地拟合曲率
- 添加一个交互项,梳理出两个预测值之间线性关系的变化
对于金县房价销售预测,我研究了除多项式以外的所有上述方法。对于我的基线模型#1,让我们先看看简单的未转换数据是如何工作的。
- 目标变量:价格(未转换)
- 预测变量(前 6 个相关性):sqft_living、sqft_above、sqft_living15、浴室、视图、等级(全部未转换)

呀!
虽然模型#1 注册了 0.577 的调整后 r 平方值和显著的 p 值,但正如总体模型残差的 QQ 图所示,它具有巨大的偏斜和多重共线性。
我们显然需要观察预测值分布,并考虑其他特征。
检查正常性并创建日志功能
给定非正态残差和偏斜的价格分布,我创建了 Python 函数 check_normality 和 create_log,它接受一个 dataframe 列,创建一个日志序列并显示双直方图进行比较。
使用这些函数,我将 King County 价格序列转换为 price_log。如下所示的对数序列更加标准化,偏斜度接近 0,峰度略低于 1。在转换之前,price 的偏斜度超过 4,峰度为 35,有近 200 个观察值超过第 4 个标准差。作为一个指南,理想的正态性应该是偏斜接近 0,峰度在 0-6 之间,3 是理想的。

线性回归假设不要求因变量或自变量具有正态分布,只要求正态模型残差。我选定 price_log 作为模型迭代 2 号到 8 号的因变量,因为它提高了性能并消除了剩余曲线偏差。
创建指标特征—改造和地下室
在对数据进行分析并尝试将翻新年份作为一个特征之后,我决定包含分类指示器特征 revenue _ ind 和 basement_ind,其中 1 表示“是”。
按邮编划分的每平方英尺价格中位数
将邮政编码纳入模型非常重要,因为不同地理区域的价格差异很大。在模型#6 中,我首先使用 70 个邮政编码作为一次性编码的分类变量,但是由于残差非常差,所以放弃了这个模型。相反,我添加了一个特征“每平方英尺的中值价格”,我将它应用于 zipcode 的数据。该特征的 box-cox 变换版本最终是重要的,因此被包括在最终的模型#8 中。
距离西雅图市中心几英里
该功能使用每个家庭的纬度和经度来测量到西雅图中心的距离。该功能实际上是在上述邮政编码每平方英尺价格之前创建的,基于这样的想法,即城市人口中心附近的价格通常较高。最初,这一功能非常重要,但一旦与邮政编码每平方英尺的中位数价格一起纳入,它就失去了大部分影响力。
第 6 步——选择特征
数据准备的最后一步是选择在回归模型的下一次迭代中包含哪些要素。这个实验过程是由你完成的数据分析和先前的模型结果决定的。

我的方法是制作一个仅包含所选特征的训练和测试数据的副本,这样我就有一组数据帧和模型结果专门用于每个模型迭代。
我在我的笔记本上保存了 8 个模型迭代,但是在特性、转换、缩放等方面至少运行了两倍多的实验。我认为我的四次迭代在统计上是有效的,而四次迭代由于性能、残差或多重共线性而被放弃。我停在了包含以下特征的 8 号模型上。

建模工作流
选择特性决策是我们从数据准备到建模工作流的转换点。在这一部分,我将只关注最终的模型#8 过程和结果。在下面的每个模型构建和模型预测部分,我都包含了相关的模型评估步骤。

来源:作者
步骤 7——特征缩放
在构建(拟合)线性回归模型之前,要素缩放步骤包括标准化(归一化)、居中或缩放要素所需的任何方法。您可能使用的常见 sklearn 函数包括 StandardScaler、MinMaxScaler 或 RobustScaler。对于我的最终模型,我没有缩放任何功能。
第 8 步——模型构建
函数 calc_sm_ols 接受 X 和 y 数据帧,并符合 statsmodel 普通最小二乘法(ols)回归模型。该函数打印模型摘要、均方根误差(RMSE)和平均绝对误差(MAE),并将 price_log 转换回可解释的形式。
让我们将模型 8 的特征与我们的训练数据相匹配,看看我们看起来如何。

黄色框中的统计数据是关键的性能组件:
- 调整后的 R 平方: 0.745 表示模型解释了 74.5%的方差。这是我的模型中最好的,我对残差感到满意。
- 总体 p 值: 0.00 表示我们可以拒绝零假设;在 alpha 为 0.05 的情况下,模型结果的 p 值非常显著。
- 赤池信息标准 ( AIC ): 3329 是我认为统计有效的四个模型中最低的,表示最好。
- **特征 p 值:**所有 10 个特征的 p 值都低于 0.05,因此我们可以拒绝任何特征的结果是由于随机性的观点。
- **偏斜:**模型在 0.041 处得分接近 0,表明残差分布接近正态。
- **峰度:**4.395 分在 0-6 的理想范围内,接近正态分布峰度 3。这表明残差分布尾部比正态分布稍重,但是可以接受。
- JB 和 Prob(JB): 基于接近 0 的概率(JB),这进一步支持了我们有回归误差的近正态分布。
- 条件编号: 629 可能表示要素之间存在多重共线性,但不足以对 OLS 汇总发出警告。Statsmodel 文档建议该值应低于 20。
- 平均绝对误差(MAE): $107,812 表示预测房价的平均误差。我把重点放在 RMSE 平均误差上,以减少对大的异常残差求平方的影响。请记住,房价从不到 10 万美元到超过 700 万美元不等。
因此,总的来说,整个模型结果看起来很可靠,不完美,但在给定的可用数据和我的时间分配下,这是我能想到的最好的结果。
总体 QQ 图
让我们使用函数 qqplot 来直观地检查整体模型残差。

当查看 QQ 图时,完全标准化的残差将沿着红色对角线。我们的两条尾巴都掉了,尤其是每一端的最后一点。这些表示模型预测不正确的异常值,会生成较大的 RMSE 值。在未来的迭代中,我会努力改进这一点。
特征残差图
我们还需要检查每个独立变量的残差分布,以确认每个变量都是同方差的。如果我们在残差中看到模式而不是随机性,我们的假设可能不可信,我们可能高估了拟合优度。函数 plot_residuals 循环遍历每个要素以绘制残差。
在 10 个特征中,9 个看起来很棒,1 个有趋势。让我们首先看一个例子,其中残差图在 sqft_living_log 特性上看起来很好。在右上方的框中,您可以看到残差的分布在 0 的上方和下方,几乎是随机的。这是我们希望看到的。

在我们的 zip_psf_median_box 特征中,残差确实显示出轻微的下降趋势。这可能表明这个特性需要一些调整,或者我们有一个混杂变量,我们应该在未来的迭代中合并。

K 重交叉验证
熟悉模型的最后一步是使用 sklearn.model_selection 中的 k-folds。我对我们的完整样本进行了 5 次分割,以确保原始数据集中的每个观察值都有机会反复出现在训练集和测试集中。

使用 5 k 倍的交叉验证导致平均 MAE 为 0.2123,而我们的 OLS 模型总结为 0.19907(在 price_log 上)。虽然 6.6%的差异比我想要的略高,但我相信这个模型是合理的。
我既使用了训练测试分割(80/20 ),也将 k 倍应用于整个样本。
步骤 9 —模型预测
现在,在最后一段,让我们使用我们的 X_test 和 y_test 数据,这些数据还没有暴露给我们的模型。下面的函数“预测”接受测试和训练数据来应用模型。输出包括用于评价的 RMSE 和 MAE 比较摘要。

这种比较看起来是可靠的,因为试验预测产生了 108,268 美元的平均寿命,与列车平均寿命相差不到 1%。我们准备进入最后的选择环节。
步骤 10 —型号比较&选择
我开发的 8 个正式模型中只有 4 个被认为是有价值的。推荐的模型#8 是迄今为止最好的,产生 10.8 万美元的 MAE 和 0.745 的调整 R 平方。与模型#2 相比,模型#8 代表了 MAE 中 38K 美元和调整后 R 平方中 0.174 美元的累积改善。

向企业描述这种多元线性回归模型的一个挑战是,我们有 10 个特征,并使用了几个对数转换。这使得可解释性变得困难。下面的可视化过滤了 7 个“平均”预测值,然后绘制了这个预测子集(244 个测试观察值)以显示价格和居住平方英尺之间的关系。

深入单个预测可以描述功能的相对影响。按照上面圈出的点来显示这个单独预测的组成。

给定对数性质和分类变量,特征的相对重要性将因每个家庭而异。但至少对于这个家,我们可以评估以下几点:
- 他们的邮政编码中的模型常数、居住平方英尺和每平方英尺价格驱动了 85%的预测价格。
- 其他特征等级,每间卧室的面积,条件和距离西雅图市中心的英里数贡献了另外的 15%。
这个特别的住宅“雨天天堂”没有我们模型中的四个特征(根据这个住宅销售的数据,所有特征都是 0)。
- 海滨:如果是真的,预计价格会增加 18.9 万美元以上
- 翻新:如果属实,预计价格将增加 54,000 美元
- 地下室:如果是真的,将增加 9K 美元的预测价格
- 等级 10(vs . 7):如果是真的,将会在预测价格上增加 24 万美元
希望这种分析能使模型更加真实,并告知相关的特性重要性和敏感性。
最后的想法
感谢你坚持看完这篇冗长的帖子!我想在一个地方布置一个端到端的多元线性回归工作流程。我推荐的型号 8 不错,但我对六位数的 MAE 感到失望。如果我有更多的时间,我会继续优化这些模型,首先从以下几个方面着手:
- 更深入地看看异常值,或者也许将豪华模型与核心模型分开。
- 使用纬度和经度来尝试隔离邻域,并在邻域级别生成相对于邮政编码的每平方英尺价格中值。
- 更详细地看多项式和相互作用项。
请留下你的评论或想法!
https://chuckutterback.medium.com/membership
多重测试——你如何调整?
原文:https://towardsdatascience.com/multiple-testing-how-should-you-adjust-41048eab4a3d?source=collection_archive---------10-----------------------
思想和理论
概述多重测试调整以及如何控制家族错误率(FWER)和错误发现率(FDR ),并附有实例

照片由 Riho Kroll 在 Unsplash 上拍摄
随着用于探索目的的大规模数据集的使用,多重检验调整越来越受欢迎。它现在是统计推断问题中的一个关键考虑因素。
多重测试问题的常见例子包括测试几个变量是否对给定的结果有影响,或者测试单个变量对无数结果的影响。例如,在全基因组关联研究(GWAS)中,研究人员对测试表型和数百万 DNA 核苷酸突变之间的关系感兴趣。
在本文中,我将讨论传统的多重测试的频率主义方法,其中心是 p 值。我们将回顾 Bonferroni 校正法、Holm 法和 Benjamini-Hochberg 法。其他流行的(有时更强大的)方法,如 Scheffé的,Tukey 的,和重采样方法将不包括在内。本文的结构是受 Tibshirani 等人的“统计学习介绍”(ISL)中的一章的启发。
(一)假设检验和 p 值:快速复习
(II)控制家庭误差率(FWER)
(三)控制误发现率
结束语将讨论如何进行多重检验,以及一般的假设检验。本文末尾还提供了 ISL 教科书中实验部分的链接,并附有 R 代码。
㈠假设检验和 p 值:快速复习
在开始多重测试之前,让我们快速回顾一下进行假设测试时要遵循的 4 个步骤。下面的表 1 提醒人们在检验假设时可能犯的两种错误(第一类错误或假阳性率,第二类错误或假阴性率)。**

表 1:假设检验中的第一类和第二类错误(作者)
(步骤 1)定义 H0(零假设)、置信水平和功效
传统上,我们希望得出一个置信度为 95%,统计能力为 80%的结论。“置信水平”是(1- 第一类误差),“统计功效”是(1- 第二类误差)。我们修正了I 型 误差 a t 5%并期望谎称一次发现 5% 。
(步骤 2)使用观察到的数据,构建一个检验统计量来检验假设
通常,这是基于 z 、 t 或卡方分布。
(步骤 3)计算 p 值
p 值是对我们在步骤 2 中构建的检验统计量的极端程度的度量,该检验统计量假设了无效的零假设。
(步骤 4)做出拒绝的决定,或者做出不拒绝零假设的决定
这个决定是基于 p 值和预定义的类型 I 错误率:如果 p 值低于 0.05,我们拒绝零假设。
使用这个框架测试大量的假设需要特别的考虑。因为我们将第一类错误固定在 5%,在规则性条件下,我们平均会错误地拒绝 5%的空值。这意味着如果我们同时测试 1000 个假设,我们预计会在 50 个中声称错误的发现。
这就是多重测试调整的重要性。
那么,我们如何调整多重测试呢?
(二)控制家庭误差率
当测试一个假设时,我们担心错误地拒绝一个零假设。当同时测试多个假设时,我们反而会担心错误地拒绝第一、第二… 或第 m 个无效假设。
我们将族式错误率(FWER)定义为出现至少一个类型 I 错误的概率。控制 FWER 最常用的两种方法是(Bonferroni 校正和(B) Holm 程序。
(一)邦费罗尼 的修正

Bonferroni 校正:推导(作者)
Bonferroni 校正是基于加法基本规则的一个不等式:

添加规则(按作者)
在上述推导中,我们寻找将 FWER 控制在 0.05 的量“alpha 倍数”,这导致简单地将 alpha 除以假设m的数量,因此在实践中,如果 p 值低于期望水平 alpha / m ,则认为 p 值是显著的,其中 m 是被测试的假设的数量。
Bonferroni 修正保证我们不会做出错误的肯定声明(第一类错误,),但这是以做出错误的否定声明(第二类错误)为代价的。当被检验的假设不独立时,由加法法则得出 P(A 或 B) < < P(A) + P(B)。因此,Bonferroni 校正通常需要比所需更大的实际 FWER,并且被认为是保守、,而不是一般的最佳方法。当 p 值不独立时,Tuckey 等其他方法可以提供更合适的替代方法。
使用 Bonferroni 校正的多重测试示例:
让我们假设我们同时运行了 5 个测试,并得出以下结果:

表 2:5 个同步假设检验的结果(按作者)
让我们将置信水平 alpha 设置为 0.05。
我们进行 5 次测试,所以 m=5
并且 Bonferroni 阈值是:a lpha /m = 0.05/5 = 0.01
因此,我们拒绝测试 2 的零假设,其中 p 值为<0.01
(B)霍尔姆程序
霍尔姆的程序也控制 FWER,但是通常会比邦费罗尼修正拒绝更多的无效假设,产生更少的类型 II 错误,并提供更大的能力。
霍尔姆的程序包括以下步骤:
- 指定 FWER alpha 级别
- 计算待测试的 m 个假设的 p 值
- p 值排序

有序 p 值
4.计算[1… m 中 j 的下列量

并将排序的 p 值与每个对应的 hj 进行比较。
5.寻找最小的 j 验证:

记为 j*

续例:使用霍尔姆程序的多重测试:

表 2:5 个同步假设检验的结果(按作者)
下表显示了执行霍尔姆程序的结果:

表 3:使用 ***霍尔姆程序*** 控制**前进**(作者)
使用霍尔姆的程序,我们拒绝了测试 2 和 4 的零假设。
请注意,在进行 Bonferroni 校正时,我们仅拒绝了测试 2 的空值。
Bonferroni 和 Holm 的程序不做任何假设(例如使用的测试统计、测试统计的独立性),可用于任何情况。
一般来说,当大量假设被检验时,控制 FWER 是有代价的。虽然分析师不太可能做出假阳性的断言,但他们也可能完全失去拒绝任何无效假设的能力:当测试太多假设时控制 FWER 会导致低统计能力。
为了获得新的科学发现,分析师可以容忍一些假阳性,并在后续研究中进一步调查这些发现。这就是控制错误发现率(FDR)发挥作用的地方。
(III)控制错误发现率(FDR)
错误发现率 (FD R )定义为错误发现比例的期望。
实际上,没有观察到错误发现比例 (FD P ),因为不知道一个给定的假设将是真还是假(否则,我们可能不必测试它)。
注意,FDR 也是(1-**)的期望*。*

将 FDR 控制在水平α保证了**平均来说,不超过α%的被拒绝的空值是假阳性*。实际上,这种程序没有标准的α水平。事实上, alpha 将取决于场景:FDR 方法本质上是探索性的,而不是验证性的。分析师愿意在犯一些错误的同时拒绝一些假设,并可以设计后续研究来证实他们的发现。如果数据收集和随访研究费用昂贵,那么 FDR 可能会低于或高于。*
(A)使用 Benjamini-Hochberg 程序 控制 FDR
- 指定控制 FDR 的 alpha 级别
- 计算要测试的 m 个假设的 p 值
- p 值排序

有序 p 值
4.计算[1…m**j的下列量

并将排序的 p 值与每个对应的 bj 进行比较。
5.寻找最小的 j 验证:

记为 j*

续例:使用 B-H 程序的多重测试:
在实践中,我们将不止对 5 个假设运行这个过程,但是为了一致性,我们保留相同的例子。
让我们想象一下,我们正在进行一项探索性研究,我们愿意在我们的发现中接受多达 5%的假阳性。

表 2:5 个同步假设检验的结果(按作者)

表 4:使用 Benjamini-Hochberg 程序控制 FDR (作者)
使用 B-H 的程序,我们拒绝了测试 2 和 4 的零假设。
在我们的示例中,只要 p 值(即测试的假设)是独立的(或轻度相关的),该程序保证平均不超过 5%的发现是假阳性,。
结束语和建议
基于 p 值的假设检验存在许多缺陷,以至于越来越多的人支持将统计显著性的阈值从 0.05 降低到 0.005。
假设检验通常依赖于理论分布,而理论分布有时是不可用的。当使用理论分布时,分析师需要确保模型假设不会被严重违反,因为这可能会使 p 值膨胀或收缩。例如,违反基本分布或协方差矩阵假设可能会导致测试统计数据膨胀和 p 值更显著。重采样方法可能是有用的,特别是当没有理论上的零分布可用时(例如,如果数据严重偏斜和零膨胀,以至于基于 t 统计的检验不再合适)。
通常,分析师需要定义分析是验证性的还是探索性的。目标是测试数千或数百万个假设以获得未探索领域的知识,还是测试 5 种干预措施中的一种是否对人群有影响?如果分析是探索性的,分析师可以专注于 FDR,并使用一些空间来做出发现。如果分析是确定性的,基于 Holm 程序、Tuckey 或 Scheffé方法的调整可能更合适。
无论是验证性的还是探索性的,评估被测试的假设的独立程度总是很重要的。如果假设是相关的,Bonferroni 调整要保守得多,甚至 Holm 的程序也只在满足独立性假设时保证 FDR。
最后,无论实验的性质如何,都需要重复和验证来加强对科学主张的信心,并且经常建议进行额外的数据收集和后续研究。统计分析应侧重于点估计和置信区间估计,并涉及元分析方法,以利用重复实验的结果来加强科学发现的可信度。
资料来源:
《统计学习导论》,提布拉尼等著
关于 R 代码请参考此链接*:**https://hastie.su.domains/ISLR2/ISLRv2_website.pdf(第 582 页)*
基于 R 的多时间序列预测和需求模式分类—第一部分
原文:https://towardsdatascience.com/multiple-time-series-forecast-demand-pattern-classification-using-r-part-1-31601158d33b?source=collection_archive---------11-----------------------
Syntetos Boylan Croston (SBC)类型,整齐的预测,需求模式

T 这里我们要重点介绍 时间序列预测 ( 利用统计/机器学习/深度学习模型预测未来值) & 需求模式分类 ( 根据数量和时间对产品进行分类)。
在这篇博客中,我将解释我们如何使用统计(经典模型)、机器学习&深度学习模型、时间序列特征工程&需求模式分类来拟合多个(1000+) 时间序列模型。该系列将具有以下 5 部分:
**第一部分:**数据清洗&需求分类。
**第二部分:**统计时间序列模型 (ARIMA、ETS、克罗斯顿等。)使用 fpp3(整齐预测)R 包。
**第三部分:**时间序列 特征工程 使用 timetk R 包。
**第四部分:**拟合 机器学习模型 (XGBoost,Random Forest 等。)&超参数调整 使用 modeltime & tidymodels R 包。
***第五部分:*拟合 深度学习模型(NBeats&DeepAR)&超参数调优使用 modeltime,modeltime.gluonts R 包。
我们开始吧!
PS:这不是解决这个问题的唯一方法。然而,这是解决这个问题的一种方法。
数据
我使用的数据来自 AnalyticsVidhya 的 食品需求预测黑客马拉松 。本次黑客马拉松的目标是预测送餐服务的每餐/中心套餐的订单数量。我们总共有 3548 份套餐/中心套餐(即 77 家中心& 51 份套餐),这意味着 将需要安装 3548 个时间系列型号 。这种技术在商业环境中也被称为 可扩展 预测。
让我们导入库。
**pacman::p_load(tidyverse, magrittr) # data wrangling packagespacman::p_load(lubridate, tsintermittent, fpp3, modeltime, timetk, modeltime.gluonts, tidymodels, modeltime.ensemble, modeltime.resample) # time series model packagespacman::p_load(foreach, future) # parallel functionspacman::p_load(viridis, plotly) # visualizations packagestheme_set(hrbrthemes::theme_ipsum()) # set default themes**
现在读取训练数据以拟合时间序列模型,并提交数据以预测未来值。
**meal_demand_tbl <- read.csv(unz("data/raw/train_GzS76OK.zip", "train.csv")) # reading train datanew_tbl <- read.csv("data/raw/test_QoiMO9B.csv") # the data need to forecastskimr::skim(meal_demand_tbl %>%
# remove id
select(-id) %>%
# make center & meal id factors
mutate(center_id = factor(center_id),
meal_id = factor(meal_id))) # summary of data**
数据预处理
在这个阶段,执行数据预处理步骤。该数据随后被转换为时间序列数据(即转换为 tsibble 对象:这是一种特殊类型的数据,用于处理 fpp3 包中的时间序列模型)。

一览表
上述总结表明,77 个中心出售 51 种膳食,这就产生了总共 3,548 个时间序列数据,每个时间序列数据包括 145 周。这里我们需要预测每份套餐的订单数量(num_orders)。此外,通过查看列complete_rate,我们可以看到变量中没有缺失值。
列week是从 1 到 145 的数字,所以我们需要将其改为日期。我们还将删除不需要预测的组合(餐饮/中心)。
**date_list <- tibble(id = seq(1, 155, 1),
week_date = seq(from = as.Date("2016-01-02"), by = "week", length.out = 155))master_data_tbl <- meal_demand_tbl %>%
left_join(date_list, by = c("week" = "id")) %>% # joining the date
inner_join(distinct(new_tbl, meal_id, center_id), by = c("meal_id", "center_id")) %>% # remove combos that did not want to forecast
select(week_date, num_orders, everything(), -c(week, id))**
现在让我们将训练和提交数据转换成完整的数据,即通过插入新的date行将不规则时间序列数据转换成规则时间序列数据。这些新创建的date行为num_orders &其他变量生成缺失值。因此,零是通过假设在这些特定的几周内没有销售发生而为变量num_orders 估算的,对于其他变量,我们用它们相应的上周值替换它们。
例如,下面的时间序列数据(表 1)显示,在第 4 周之后,直到第 7 周都有数据丢失。表 2 显示了缺失周(即第 5 周和第 6 周)的完整数据和新条目。

表 1

表 2
然后emailer_for_promotion & homepage_featured变量被转换成一个因子。
**master_data_tbl <- master_data_tbl %>%
as_tsibble(key = c(meal_id, center_id), index = week_date) %>%
## num_urders missing value imputation ----
fill_gaps(num_orders = 0, .full = end()) %>% # make it complete by max week dates
## X variables missing value imputation ----
group_by_key() %>%
fill_(fill_cols = c("emailer_for_promotion", "homepage_featured", "base_price", "checkout_price")) %>% # filling other variables
ungroup() %>%
## change variables to factor ----
mutate(emailer_for_promotion = factor(emailer_for_promotion),
homepage_featured = factor(homepage_featured))**
用submission文件进行类似的操作。
**## New Table (Submission file) data wrangling ----
new_tbl <- new_tbl %>%
left_join(date_list, by = c("week" = "id")) %>% # joining the date
full_join(new_data(master_data_tbl, n = 10), by = c("center_id", "meal_id", "week_date")) %>%
as_tsibble(key = c(meal_id, center_id), index = week_date) %>%
group_by_key() %>%
fill_(fill_cols = c("emailer_for_promotion", "homepage_featured", "base_price", "checkout_price")) %>% # filling other variables
ungroup() %>%
# change variables to factor
mutate(emailer_for_promotion = factor(emailer_for_promotion),
homepage_featured = factor(homepage_featured))**
时间序列食品数据可视化
图表 1:各中心的订单数量
**master_data_tbl %>%
# Randomly Pick 4 Centres
distinct(center_id) %>%
sample_n(4) %>%
# Joining the transaction data
left_join(master_data_tbl) %>%
group_by(week_date, center_id) %>% # aggregate to centres
summarise(num_orders = sum(num_orders, na.rm = T)) %>%
as_tsibble(key = center_id, index = week_date) %>%
fill_gaps(num_orders = 0, .full = end()) %>%
autoplot(num_orders) +
scale_color_viridis(discrete = T)**

上图显示, 中心#24 的前几周的交易都是 0;这些交易已被删除。但是,在此时间段之后,仍有连续的交易被包含在数据中以符合模型。
**master_data_tbl <- master_data_tbl %>%
filter(center_id != 24) %>%
bind_rows(master_data_tbl %>%
filter(center_id == 24 & week_date > as.Date("2016-07-16"))) # remove entries before 2016/07/16 for center 24**
图 2:按用餐 ID 排列的订单数量
**master_data_tbl %>%
# Randomly Pick 4 Meals
distinct(meal_id) %>%
sample_n(3) %>%
# Joining the transaction data
left_join(master_data_tbl) %>%
group_by(week_date, meal_id) %>%
summarise(num_orders = sum(num_orders, na.rm = T)) %>%
as_tsibble(key = meal_id, index = week_date) %>%
fill_gaps(num_orders = 0, .full = end()) %>%
autoplot(num_orders) +
scale_color_viridis(discrete = T)**

上面的图显示了引入新的膳食,使时间序列数据变短。因此,有一种可能性,这些类型的时间序列数据应分别对待交叉验证方法。

照片由雷切尔·帕克在 Unsplash 上拍摄
需求分类 SBC 方法。
N 现在我们将确定每一餐/中心套餐的需求模式类别(S 平稳、不稳定、不稳定&间歇) 。
为什么?
当你对现实世界的数据(即你的组织数据)进行预测时,你将会以一种零星的需求模式结束你的大部分产品。这些类型产品显示出较低的预测准确度,并且很难提高它们的准确度。这是因为它们的可预测性很低。那么我们能为这些类型的产品做些什么呢?我们必须计算安全库存值,而不是花时间增加他们的预测准确性。
此外,在大多数情况下,这些零星的产品不是高收入来源。这将有助于我们将预测项目分为两部分。对于常规需求,产品侧重于提高预测准确性,而零星需求产品计算安全库存。
此外,确定这些需求模式意味着不同的时间序列模型可以适用于他们。例如,Croston & SBA 适用于零星需求模式。
怎么会?
在 R 中我们可以使用 R 包 tsintermittent中的函数idclass。在函数idclass中,当参数type等于SBC时,会计算出以下两个值:
cv2—测量数量的变化p—测量需求间隔
基于这两个衡量标准,我们可以将需求模式分为 平滑(p<1.32&cv2<0.49)、不稳定*(p<1.32&cv2≥0.49)、起伏(p≥1.32【1.32】***
平滑:
平稳的需求模式显示了规则的需求和规则的时间。即这种类型的产品可以每天或每周出售。
飘忽不定:
**不稳定的需求模式显示出时间上的规律性,但是销售量变化很大。即,这种类型的产品可以每天或每周销售,然而,例如,某一天它可以销售 3 件,而另一天它可以销售 100 件。**
间歇:
间歇性需求模式在时间上表现出不规则性,在数量上表现出规则性。也就是说,这种类型的产品在第一周售出,然后在几周内不会售出,但最终会售出相同数量的产品。
块状:
起伏不定的需求模式表现出时间上的不规则性和数量上的不规则性。无论使用什么类型的时间序列模型,这种特殊类型的需求模式都很难预测。这类产品的解决方案是有一个安全库存。
开始编码吧!
首先,我们将较长格式的数据转换为较宽的格式,即创建 3,548 列(用餐总数/中心时间序列数据)。例如:

更长的格式

更宽的格式
**# make each combo ts as column
wide_dt <- .x %>%
transmute(week_date, id = paste0("x_", center_id, "_", meal_id), num_orders) %>%
pivot_wider(names_from = id, values_from = num_orders, values_fill = 0) %>%
arrange(week_date) %>% # arrange by week date
select(-week_date) %>%
data.frame()**
然后将idclass应用于转换后的数据帧。
**ts_cate_obj <- idclass(wide_dt, type = "SBC", outplot = "none")**
ts_cate_obj上面显示的是一个矩阵。我们现在将这个矩阵格式改为data.frame,然后应用临界值对需求模式进行分类。
**ts_categorization <- data.frame(id = row.names(t(wide_dt)),
cv2 = ts_cate_obj$cv2,
p = ts_cate_obj$p) %>%
separate(id, into = c("x", "center_id", "meal_id"), sep = "_") %>%
select(-x) %>%
mutate(demand_cate = case_when(p < 1.32 & cv2 < 0.49 ~ "Smooth",
p >= 1.32 & cv2 < 0.49 ~ "Intermittent",
p < 1.32 & cv2 >= 0.49 ~ "Erratic",
p >= 1.32 & cv2 >= 0.49 ~ "Lumpy")) %>%
mutate(center_id = as.integer(as.character(center_id)),
meal_id = as.integer(as.character(meal_id)))**
我们用一个柱状图来看上面分析的总结。

上图显示,在数据中,大多数时间序列的餐饮/中心组合属于平稳和不稳定。这意味着常规的时间序列模型如 ARIMA、ETS 等。会很适合。此外,先进的模型,如克罗斯顿和 SBA 已安装,以解决间歇性和起伏不定的需求模式。机器学习/深度学习模型也可以通过使用这些需求模式类型作为特征来拟合。 交叉验证方法已用于拟合无需求(即交易少于 20)组合。
在接下来的部分中,我们将进行特征工程,拟合时间序列模型,机器学习模型,深度学习模型,并比较它们的准确性,以找到合适的模型。
参考文献
北卡罗来纳州科伦茨,2014 年。R—Nikola OS kour entzes间歇需求预测包。Kourentzes.com。可查阅:<https://kourentzes . com/forecasting/2014/06/23/intermittent-demand-forecasting-package-for-r/>【2021 年 1 月 22 日获取】。
弗莱普。2021.需求分类:为什么可预测性很重要——Frepple APS。可在:https://frepple.com/blog/demand-classification/【2021 年 1 月 22 日获取】。
基于 R 的多时间序列预测和需求模式分类——第二部分
原文:https://towardsdatascience.com/multiple-time-series-forecast-demand-pattern-classification-using-r-part-2-13e284768f4?source=collection_archive---------15-----------------------
集合、ARIMA、ETS、动态调和回归、时间序列线性回归、STL 分解 ETS、霍尔特线性、霍尔特线性阻尼、简单指数平滑、漂移、季节性朴素、朴素、克罗斯顿、SBA

T 这是我之前博客的延续。在之前的博客中,我们看了如何执行基本的 数据预处理 &如何使用函数 *idclass*对时间序列 进行分类。该系列将具有以下 5 部件:
第一部分: 数据清理&需求分类。
第二部分: 拟合 统计时间序列模型 (ARIMA、ETS、CROSTON 等。)使用 fpp3(整齐预测)R 包。
第三部分: 时间序列 特征工程 使用 timetk R 包。
Part 4:Fit机器学习模型 (XGBoost,Random Forest 等。)**&超参数调优 使用 modeltime & tidymodels R 包。
第五部分:Fitdeep learning 模型(NBeats&DeepAR)&超参数调优使用 modeltime,modeltime.gluonts R 包。**
在这篇博客中,我将解释如何拟合***(ARIMA、ETS、分解模型等)。)*** 对一组时间序列数据***【3548 组】*** 并为每组选择合适的时间序列模型。**
我们开始吧!
工作流程图:

统计模型拟合工作流程
上图显示了为适应经典统计模型而执行的工作流程。在这个工作流程中,第一阶段( 读取数据,数据准备&需求分类 )在我的之前的博客中已经解释过了。这里我们将着重于拟合统计模型&计算RootMESquaredL对数 E rror (RMSLE)精度度量来选择合适的模型。
让我们导入库。
**pacman::p_load(tidyverse, magrittr) # data wrangling packagespacman::p_load(lubridate, tsintermittent, fpp3, modeltime, timetk, modeltime.gluonts, tidymodels, modeltime.ensemble, modeltime.resample) # time series model packagespacman::p_load(foreach, future) # parallel functionspacman::p_load(viridis, plotly) # visualizations packagestheme_set(hrbrthemes::theme_ipsum()) # set default themes**
步骤 01:为平稳和不稳定类型拟合时间序列模型。
我们已经确定了每个餐饮/中心组的需求类型。现在我们将只过滤 平滑&不稳定 需求类型餐/中心组时间序列数据。
**smooth_tbl <- master_data_tbl %>%
filter(demand_cate %in% c("Smooth", "Erratic"))**
现在,过滤后的数据被分为每一餐/中心组合的训练和测试(过去 10 周)数据。
**train_tbl <- smooth_tbl %>%
filter_index(. ~ "2018-07-23")test_tbl <- smooth_tbl %>%
filter_index("2018-07-30" ~ .)**
注意:这里我们的数据框架是一个 tsibble 对象,这是一种处理时间序列数据的特殊类型的数据框架。我们已经在 tsibble“master _ data _ TBL”中将“center&meal id”指定为关键变量。所以我们的 tidyverse 函数将适用于所有的膳食/中心组。例如,当我们应用函数“filter_index”时,它将按日期过滤所有组。

表 1: tsibble 数据
接下来,我们将为每一餐/中心组 拟合以下 11 个时间序列模型,即共拟合 34,265 个模型。
34265 款是怎么来的?
还有1839平滑&1276飘忽不定的类型餐/中心组,这使得总共有3115不同的组。我们将为每组配备 11 种不同的型号,总共有*3115 * 11 = 34265。*****
****Naive:Naive 模型将所有未来值设置为与上次观察值相同。
****季节性朴素模型:季节性朴素模型用于季节性数据。在这种情况下,每个未来值等于同一季节的最后值。
****漂移:漂移模型是朴素模型的变体,允许预测随时间增加或减少。
****简单指数平滑:简单指数平滑模型用于无法确定明确趋势或季节模式的情况。
****霍尔特线性模型:霍尔特线性模型是 SES 的扩展版本,允许根据趋势进行预测。
****阻尼霍尔特线性:阻尼加性趋势模型是霍尔特线性的扩展版本,允许趋势随着阻尼参数φ的变化而变化。
分解预测:分解模型用于使用季节和趋势分解使用黄土(STL) 方法分解时间序列。ETS 方法可用于预测经季节性调整的数据,之后季节性成分可添加到预测数据中。
ARIMA:ARIMA 模型解释了数据中的自相关性。这里我们将拟合一个自动 ARIMA 模型,该模型使用最小 AICC** 自动选择 ARMA 订单。**
动态调和回归: 动态调和回归模型允许包含其他数据,如基价、结账价、电子邮件促销&主页精选。
****时间序列回归:时间序列回归模型用于预测因变量 Y,假设它与其他自变量 x 具有线性关系,即在这种情况下,在预测订单数量与结帐价格、基础价格、促销电子邮件地址&主页特色具有线性关系时做出假设。
****集合:集合模型简单地同时使用几个不同的模型,并计算结果预报的平均值。例如,在这里,ARIMA、SES &分解模型被一起用来计算平均预测值。

图 1:带有预测值的拟合模型示例

图 2:带有预测值的拟合模型示例

**图 3:带有预测值的拟合模型示例**
现在,我们将使用 R 包fpp3将上述 11 个模型拟合到 R 中。这里我们对*对数转换后的订单数量施加一个正约束。你可以在 R 代码中看到我们增加了一个作为偏移量;这是为了克服 log(0) 无穷大的问题。*
这将需要更长的时间来运行,因为它适合 34k 模型。
***fpp3_model_table <- train_tbl %>%
model( ## Model 1: Naive ----
naive_mod = NAIVE(log(num_orders + 1)), ## Model 2: Snaive ----
snaive_mod = SNAIVE(log(num_orders + 1)), ## Model 3: Drift ----
drift_mod = RW(log(num_orders + 1) ~ drift()), ## Model 4: SES ----
ses_mod = ETS(log(num_orders + 1) ~ error("A") + trend("N") + season("N"), opt_crit = "mse"), ## Model 5: Holt's Linear ----
hl_mod = ETS(log(num_orders + 1) ~ error("A") + trend("A") + season("N"), opt_crit = "mse"), ## Model 6: Damped Holt's Linear ----
hldamp_mod = ETS(log(num_orders + 1) ~ error("A") + trend("Ad") + season("N"), opt_crit = "mse"), ## Model 7: STL decomposition with ETS ----
stl_ets_mod = decomposition_model(STL(log(num_orders + 1)), ETS(season_adjust ~ season("N"))), ## Model 8: ARIMA ----
arima_mod = ARIMA(log(num_orders + 1)), ## Model 9: Dynamic harmonic regression ----
dhr_mod = ARIMA(log(num_orders + 1) ~ PDQ(0,0,0) + fourier(K=6) + checkout_price + emailer_for_promotion), ## Model 10: TSLM ----
tslm_mod = TSLM(log(num_orders + 1) ~ checkout_price + base_price + emailer_for_promotion)) %>% ## Model 11: Ensemble Model ----
mutate(ensemble_sm_mod = combination_ensemble(arima_mod, ses_mod, stl_ets_mod))***
步骤 02:拟合间歇和波动类型的时间序列模型。
在这一步中,我们将过滤 间歇&块状 餐/中心组时间序列数据。
***inter_tbl <- master_data_tbl %>%
filter(demand_cate %in% c("Intermittent", "Lumpy"))***
现在,过滤后的数据被分为每一餐/中心组的训练和测试(过去 10 周)数据。
***train_tbl <- inter_tbl %>%
filter_index(. ~ "2018 W30")test_tbl <- inter_tbl %>%
filter_index("2018 W31" ~ .)***
接下来,我们将为每个餐饮/中心组 拟合以下 5 个时间序列模型,即共拟合 1,960 个模型。
1960 款是怎么来的?
还有 273 间歇& 119 块状类型的餐/餐群,这就使得总共有392 个不同的群。我们将为每组配备 5 种不同的型号,总共是392 * 5 = 1960。******
****简单指数平滑:参考上面的解释。
ARIMA: 参考上面的解释。
SBA:SBA 模型是克罗斯顿方法的另一个变体/改进版本。
****集合:在这里,克罗斯顿、SBA、ARIMA & SES 模型一起用来计算平均预报值。

图 4:带有预测值的间歇拟合模型示例
现在,我们将使用 R 包fpp3将上述 5 个模型拟合到 R 中。
*****fpp3_model_table <- train_tbl %>%
model(
## Model 1: Croston ----
crost_mod = CROSTON(log(num_orders + 1)), ## Model 2: SBA ----
sba_mod = CROSTON(log(num_orders + 1), type = "sba"), ## Model 3: SES ----
ses_mod = ETS(log(num_orders + 1) ~ error("A") + trend("N") + season("N"), opt_crit = "mse"), ## Model 4: ARIMA ----
arima_mod = ARIMA(log(num_orders + 1))) %>% ## Model 5: Ensemble ----
mutate(ensemble_inter_mod = combination_ensemble(crost_mod, sba_mod, ses_mod, arima_mod))*****
步骤 03:测试期间的预测。
我们现在有 36,225(即 34,265 + 1,960) 合身款。接下来,我们需要找出哪种模式适合每个用餐/中心组。为了做到这一点,我们必须在测试期间预测每个模型,并计算它们的准确性度量。
现在我们将对测试期进行预测。
*****forecast_tbl <- fpp3_model_table %>%
forecast(test_tbl, times = 0) %>%
as_tibble() %>%
select(week_date, meal_id, center_id, .model, fc_qty = .mean)*****
步骤 04:计算准确度。
在此阶段,我们将计算每个模型的精确度,以便为每个餐/中心组选择合适的模型。这里使用的精度度量是RootMeanSquaredL对数Eerror(RMSLE)。我选择 RMSLE 仅仅是因为它是本次竞赛用来评估的指标。然而,我个人更喜欢 RMSSE ,因为这曾在 M5 预测竞赛中使用过。****
可以通过使用 R 包Metrics中的函数rmsle在 R 中计算 RMSLE。
*****forecast_tbl <- master_data_tbl %>% as_tibble() %>% # change tsibble -> tibble select(week_date, center_id, meal_id, num_orders) %>% right_join(forecast_tbl, by = c("week_date", "meal_id", "center_id")) %>% # join forecast values mutate(fc_qty = ifelse(fc_qty < 0, 0, fc_qty)) # change negative & NA ordersaccuracy_tbl <- forecast_tbl %>% group_by(center_id, meal_id, .model) %>% summarise(accuracy_rmsle = Metrics::rmsle(num_orders, fc_qty)) # calculate RMSLE*****

表 2:精度表
第五步:选择合适的型号。
在这一阶段,我们将根据最小 RMSLE 为每一餐/中心组选择一个合适的模型。****
*****suitable_model_tbl <- accuracy_tbl %>%
ungroup() %>%
group_by(meal_id, center_id) %>%
filter(accuracy_rmsle == min(accuracy_rmsle)) %>%
slice(n = 1)*****
现在,我们有了一份适合每个餐饮/中心群体的模型列表。
例如,下表显示,对于中心 110 和膳食 2104,最合适的模型是季节性朴素模型(snaive_mod),而对于中心 67 &膳食 1885,最合适的模型是霍尔特线性模型(hl_mod)。这些相应的模型可用于每个组未来的订单数量预测。

*****表 3:合适的型号列表*****
步骤 06:准确性可视化和概述

图 5:精度密度图
上图显示了我们合适的模型的精确度的概况。它表明,对于大多数膳食/中心组,实现了高准确度(即小于 1 RMSLE)。然而,有一些膳食/中心组显示出较低的准确性;对于这些低精度组,可以拟合其他高级时间序列/机器学习模型来提高预测精度。此外,大多数低精度组是不稳定的/间歇的,这意味着对于这些类型的组,而不是拟合高级模型,重点应该是安全库存计算。

地块 6:合适的模型
上图显示了由最小 RMSLE 选择的适合模型的膳食/中心组的数量。结果表明,ARIMA、动态调和回归和时间序列回归模型是最适合膳食/中心组的模型。
因此,现在我们知道如何为一组时间序列数据拟合经典时间序列模型。然而,这在实际/商业世界中有一些缺点。那些缺点是什么?
- ****时间:当组数增加时,训练需要很长时间。比如我们这种情况,为了找到 3.5k 合适的模型,需要训练将近 35k 的模型。
- ****再现性:一旦你为一个团队找到了合适的模型,我们假设这个模型也将用于未来。实际上,情况并非如此,因为当添加新的事务时,最合适的模型可能会改变。
- ****没有可扩展性:没有一个全局模型。也就是说,我们必须为每一组设计不同的模型。
我们能做些什么来克服这些问题?
这些问题的答案是机器学习&深度学习模型。
在我的下一篇博客中,我将解释我们如何适应机器学习和深度学习模型,并进行特征工程。这个机器学习模型也大大提高了我们的准确性。
参考
Hyndman,R.J .,& Athanasopoulos,G. (2021) 预测:原则与实践,第三版,原文:澳大利亚墨尔本。OTexts.com/fpp3.于[2021 年 2 月 8 日访问]。
北卡罗来纳州科伦茨,2014 年。R-Nikola OS Kourentzes间歇需求预测包。Kourentzes.com。可在:<https://kourentzes . com/forecasting/2014/06/23/intermittent-demand-forecasting-package-for-r/>[2021 年 1 月 22 日访问]。
基于 PyCaret 的多时间序列预测
使用 PyCaret 预测多个时间序列的分步教程

py caret——Python 中的开源、低代码机器学习库
PyCaret
PyCaret 是一个开源的低代码机器学习库和端到端的模型管理工具,内置于 Python 中,用于自动化机器学习工作流。它因其易用性、简单性以及快速有效地构建和部署端到端 ML 原型的能力而广受欢迎。
PyCaret 是一个替代的低代码库,可以用来用几行代码替换数百行代码。这使得实验周期成倍地快速和有效。
py caret简单T2 好用。PyCaret 中执行的所有操作都顺序存储在一个管道中,该管道对于**部署是完全自动化的。**无论是输入缺失值、一键编码、转换分类数据、特征工程,甚至是超参数调整,PyCaret 都实现了自动化。
本教程假设您对 PyCaret 有一定的了解和经验。如果您以前没有使用过,没关系,您可以通过这些教程快速入门:
- PyCaret 2.2 已经发布——新功能
- 宣布 PyCaret 2.0
- 关于 PyCaret 你不知道的五件事
重述
在我的上一篇教程中,我已经演示了如何使用 PyCaret 通过 PyCaret 回归模块使用机器学习来预测时间序列数据。如果你还没有阅读,你可以先阅读使用 PyCaret 回归模块进行时间序列预测教程,然后再继续阅读本教程,因为本教程建立在上一篇教程中涉及的一些重要概念之上。
正在安装 PyCaret
安装 PyCaret 非常容易,只需要几分钟。我们强烈建议使用虚拟环境来避免与其他库的潜在冲突。
PyCaret 的默认安装是 pycaret 的精简版本,它只安装这里列出的硬依赖项。
**# install slim version (default)** pip install pycaret**# install the full version**
pip install pycaret[full]
当你安装 pycaret 的完整版本时,这里列出的所有可选的依赖项也会被安装。
👉PyCaret 回归模块
PyCaret 回归模块是一个受监督的机器学习模块,用于估计因变量(通常称为“结果变量”,或“目标”)和一个或多个自变量(通常称为“特征”,或“预测器”)之间的关系。
回归的目标是预测连续值,如销售额、数量、温度、客户数量等。PyCaret 中的所有模块都提供了许多预处理功能,通过设置功能为建模准备数据。它有超过 25 个现成的算法和几个图来分析训练模型的性能。
👉资料组
在本教程中,我将展示多时间序列数据预测的端到端实现,包括训练和预测未来值。
我使用了 Kaggle 的商店商品需求预测挑战数据集。该数据集有 10 个不同的商店,每个商店有 50 个项目,即五年(2013 年至 2017 年)共 500 个每日级别的时间序列数据。

样本数据集
👉加载并准备数据
**# read the csv file** import pandas as pd
data = pd.read_csv('train.csv')
data['date'] = pd.to_datetime(data['date'])**# combine store and item column as time_series**
data['store'] = ['store_' + str(i) for i in data['store']]
data['item'] = ['item_' + str(i) for i in data['item']]
data['time_series'] = data[['store', 'item']].apply(lambda x: '_'.join(x), axis=1)
data.drop(['store', 'item'], axis=1, inplace=True)**# extract features from date**
data['month'] = [i.month for i in data['date']]
data['year'] = [i.year for i in data['date']]
data['day_of_week'] = [i.dayofweek for i in data['date']]
data['day_of_year'] = [i.dayofyear for i in data['date']]data.head()

从数据中取样行
**# check the unique time_series**
data['time_series'].nunique()
>>> 500
👉可视化时间序列
**# plot multiple time series with moving avgs in a loop**import plotly.express as pxfor i in data['time_series'].unique():
subset = data[data['time_series'] == i]
subset['moving_average'] = subset['sales'].rolling(30).mean()
fig = px.line(subset, x="date", y=["sales","moving_average"], title = i, template = 'plotly_dark')
fig.show()

store_1_item_1 时间序列和 30 天移动平均值

store_2_item_1 时间序列和 30 天移动平均值
👉开始培训过程
现在我们已经准备好了数据,让我们开始训练循环。注意所有功能中的verbose = False,以避免训练时在控制台上打印结果。
下面的代码是我们在数据准备步骤中创建的围绕time_series列的循环。总共有 150 个时间序列(10 家商店 x 50 件商品)。
下面的第 10 行是过滤数据集的time_series变量。循环中的第一部分是初始化setup函数,然后是compare_models寻找最佳模型。第 24–26 行捕获结果,并将最佳模型的性能指标附加到一个名为all_results的列表中。代码的最后一部分使用finalize_model函数在整个数据集上重新训练最佳模型,包括测试集中剩下的 5%,并将包括模型在内的整个管道保存为 pickle 文件。
https://gist . github . com/moe zali 1/f 258195 ba1c 677654 abffb 0 D1 ACB 2c 0
我们现在可以从all_results列表创建一个数据帧。它将显示为每个时间序列选择的最佳模型。
concat_results = pd.concat(all_results,axis=0)
concat_results.head()

concat_results 中的样本行
培训过程👇

培训过程
👉使用训练好的模型生成预测
既然我们已经训练了模型,让我们使用它们来生成预测,但是首先,我们需要创建用于评分的数据集(X 变量)。
**# create a date range from 2013 to 2019**
all_dates = pd.date_range(start='2013-01-01', end = '2019-12-31', freq = 'D')**# create empty dataframe**
score_df = pd.DataFrame()**# add columns to dataset**
score_df['date'] = all_dates
score_df['month'] = [i.month for i in score_df['date']]
score_df['year'] = [i.year for i in score_df['date']]
score_df['day_of_week'] = [i.dayofweek for i in score_df['date']]
score_df['day_of_year'] = [i.dayofyear for i in score_df['date']]score_df.head()

score_df 数据集中的样本行
现在让我们创建一个循环来加载经过训练的管道,并使用predict_model函数来生成预测标签。
from pycaret.regression import load_model, predict_modelall_score_df = []for i in tqdm(data['time_series'].unique()):
l = load_model('trained_models/' + str(i), verbose=False)
p = predict_model(l, data=score_df)
p['time_series'] = i
all_score_df.append(p)concat_df = pd.concat(all_score_df, axis=0)
concat_df.head()

对 concat_df 中的行进行采样
我们现在将加入data和concat_df。
final_df = pd.merge(concat_df, data, how = 'left', left_on=['date', 'time_series'], right_on = ['date', 'time_series'])
final_df.head()

final_df 中的样本行
我们现在可以创建一个循环来查看所有的图。
for i in final_df['time_series'].unique()[:5]:
sub_df = final_df[final_df['time_series'] == i]
import plotly.express as px
fig = px.line(sub_df, x="date", y=['sales', 'Label'], title=i, template = 'plotly_dark')
fig.show()

store_1_item_1 实际销售额和预测标签

store_2_item_1 实际销售额和预测标签
我希望您会喜欢 PyCaret 的易用性和简单性。在不到 50 行代码和一个小时的实验中,我训练了超过 10,000 个模型(25 个估计值 x 500 个时间序列),并生产了 500 个最佳模型来生成预测。
即将推出!
下周我将写一篇关于使用 PyCaret 异常检测模块对时间序列数据进行无监督异常检测的教程。请在 Medium 、 LinkedIn 和 Twitter 上关注我,获取更多更新。
使用 Python 中的这个轻量级工作流自动化库,您可以实现的目标是无限的。如果你觉得这很有用,请不要忘记给我们 GitHub 库上的⭐️。
想了解更多关于 PyCaret 的信息,请在 LinkedIn 和 Youtube 上关注我们。
加入我们的休闲频道。邀请链接此处。
您可能还对以下内容感兴趣:
使用 PyCaret 2.0
在 Power BI 中构建您自己的 AutoML 使用 Docker
在 Azure 上部署机器学习管道在 Google Kubernetes 引擎上部署机器学习管道
在 AWS Fargate 上部署机器学习管道
构建并部署您的第一个机器学习 web 应用
使用 AWS Fargate server less
部署 PyCaret 和 Streamlit 应用
重要链接
文档
博客
GitHub
stack overflow
安装 PyCaret 笔记本教程 贡献于 PyCaret
想了解某个特定模块?
单击下面的链接查看文档和工作示例。
分类 回归
聚类
异常检测
自然语言处理 关联规则挖掘
多尺度金融信号处理
原文:https://towardsdatascience.com/multiscale-financial-signal-processing-324fa05c75a2?source=collection_archive---------17-----------------------
经验模态分解和希尔伯特谱分析

“一切都是能量”——照片由大流士·巴沙尔在 Unsplash 上拍摄
市场观察和实证研究表明,资产价格通常由多种因素驱动,从长期经济周期到短期的快速波动。这表明,金融时间序列可能嵌入不同的时间尺度。
另一方面,在金融时间序列中经常观察到非平稳、行为和非线性动态。这些特征很难被线性模型捕捉到,因此需要一种自适应的非线性分析方法。几十年来,基于短时傅立叶变换的方法已经得到发展并应用于非平稳时间序列,但在捕捉非线性动态方面仍然存在挑战,并且通常规定的假设使得这些方法不完全适应。这就需要一种自适应的非线性分析方法。
希尔伯特-黄变换(HHT)
自适应时间序列分析的另一种方法是希尔伯特-黄变换(HHT)。HHT 方法可以使用经验模态分解(EMD)将任何时间序列分解成具有非平稳振幅和频率的振荡分量。这种完全自适应的方法为原始时间序列提供了多尺度分解,从而提供了关于时间序列的更丰富的信息。随后使用希尔伯特变换提取每个分量的瞬时频率和瞬时幅度。分解到不同的时间尺度上,还允许重建到不同的分辨率,提供了一个平滑和过滤工具,是嘈杂的金融时间序列的理想选择。
EMD 是我们多阶段程序的第一步。对于在一段时间[0, T ]内观察到的任何给定时间序列 x(t) ,我们以迭代方式将其分解为有限的振荡分量序列 cⱼ(t) ,对于 j=1,…,n,加上一个称为剩余项的非振荡趋势:

为了确保每个 cⱼ(t) 具有适当的振荡特性,应用了 IMF 的概念。IMF 是允许良好行为和物理上有意义的希尔伯特变换的时间实函数。具体而言,每个国际货币基金组织由以下两个标准定义:
- 无本地振荡:极值的数量和过零点的数量必须相等或最多相差一。
- 对称:上包络定义的函数的最大值和下包络定义的最小值在任何时候都必须加起来为零T∈【0, T 。
此外,我们将互补集成经验模式分解方法应用于非平稳金融时间序列。这种噪声辅助的方法将任何时间序列分解成许多固有模式函数,以及相应的瞬时振幅和瞬时频率。
下面我们举例说明 S&P500 和 VIX 分解的本征模函数(IMF)和剩余项。

从标准普尔 500(log-price)2010 年 4 月 1 日至 2020 年 3 月 31 日的互补系综经验模态分解中提取的内禀模态函数(IMF)和残差项。最上面一行显示原始时间序列。倒数第二行显示相应时间序列的 IMF 模式。每个图的底部一行显示了时间序列的剩余项。

从 2010 年 4 月 1 日至 2020 年 3 月 31 日的波动率指数(VIX)的互补系综经验模式分解中提取的固有模式函数(IMF)和残差项。最上面一行显示原始时间序列。倒数第二行显示相应时间序列的 IMF 模式。每个图的底部一行显示了时间序列的剩余项。
这些模式对应不同的频率,从快速波动到长期趋势。这种分解允许我们 I)通过排除一些高频分量来平滑/过滤任何时间序列,以及 ii)使用分量子集来重建任何时间序列。

使用不同组分量的时间序列重建。
能量频谱
注意,每种模式(IMF)也对应不同的波动频率。上述分解允许我们在逐个模式(逐个频率)的基础上比较任何两个时间序列。
振荡实值函数可以看作复平面上的轨道在实轴上的投影。对于任何时间函数 X(t) ,希尔伯特变换由下式给出

因此 Y(t) 提供了 X(t) 的互补虚部,以在上半平面中形成解析函数,定义如下

在哪里


然后,瞬时频率被定义为相位函数的 2𝜋标准化变化率,也就是说,

将希尔伯特变换分别应用于每个 IMF 分量产生一系列分析信号

反过来,原始时间序列可以表示为具有时变振幅和频率的时间序列的稀疏谱表示:

此外,第 j 个分量的瞬时能量定义为

因此,对于每个时间序列,我们得到一个 能量-频谱图。

S&P500:瞬时能量 E(t)随着瞬时频率 f(t) 的增加而减小。

对 VIX 来说,瞬时能量 E(t)下降的速度比 S&P500 低得多。
S&P500 和 VIX 共享相似的瞬时频率,但是瞬时能量显著不同。

标准普尔 500 vs VIX:各模式的瞬时频率非常相似,这意味着它们都是由相似的波动频率驱动的,尽管幅度明显不同。
总之,这种方法的主要输出是一系列 IMF,以及时变瞬时幅度和瞬时频率。不同的模式组合允许我们使用不同时间尺度的成分来重构时间序列。使用希尔伯特谱分析,我们计算相关的瞬时能量-频率谱来说明原始时间序列中嵌入的各种时间尺度的特性。
多尺度信号处理也非常适合分析加密货币价格(参见这篇论文)。对于其他例子,如黄金(GLD)和国债,以及机器学习应用,我们建议读者参考下面的完整论文(或者可能是关于这个主题的未来帖子)。
免责声明:这不是投资建议。
参考
梁,赵(2021),特征生成与机器学习的金融时间序列分析与预测[ pdf ],随机模型在商业与工业中的应用
梁,赵(2021),自适应互补系综 EMD 与加密货币价格的能频谱pdf
谷歌学术 // 领英页面 // 首页
多元线性回归
原文:https://towardsdatascience.com/multivariant-linear-regression-e636a4f99b40?source=collection_archive---------15-----------------------
哦,男孩,同性恋

多元回归|照片由 paweczerwi ski 拍摄
这篇文章是我上一篇关于线性回归文章的延续。
重申我上一篇文章中关于最小二乘回归中的误差公式是很重要的。

最小二乘回归选择具有最低总 SSE 的线
这个误差平方和的和,就是这条线上的理论值和实际观测值之间的差异,也就是蓝点所在的点。这些差异的平方,然后将这些平方相加,总面积就是 SSE ,误差平方和。

平方和回归(SSR)
这里是平方和回归( SSR )。这基本上说明了方差的大小。我们在预测值之间看到的基本效果是什么,这个模型需要做多少工作。如果这是一个非常大的数字,它本身并不意味着模型是好是坏。但是如果这个 SSR 远大于它,或者回归的平方和远大于这些差异的平方和(SSE),那么它就是一个好模型。测量值介于预测值和平均值之间。
平方和 total ( SST )有时等于 SSR + SSE。有时主要是如果你使用最小二乘回归,首先,这是有意义的——我们在这里对平方求和,所以你是在做最小二乘回归。另一个更重要的事情是,这是人们总是忘记的,这个等式, SST = SSR +SSE 只对训练数据成立。对于测试数据来说是不成立的。上交所之所以可以任意放大测试数据。如果它变得太大,那么事情可能会出错。SSE 可以变得任意大,所以即使比 SST 大,它也可以变得比 SST 大很多。SST 仅基于训练数据,而 SSE 基于您的测试数据。

SSE 可以比 SST 大
因此,R 的值很可能是负值,我们都知道,如果它真的是某个东西的平方,R 不可能是负值。但是如你所见,这个公式不是任何皮尔逊 R 的平方,或者类似的东西。
线的另一种精度测量是使用均方根误差(RMSE)。用它来估计误差意味着我们比标准差多损失了一个自由度,所以我们把 RSE 写成:

根据 SSE 定义
# mean squared errormse = np.sum((y_pred — y_actual)**2)# root mean squared error, m is number of training samples
rmse = np.sqrt(mse/m)
# results.mse_resid
假设
以下是线性回归的一些重要假设。
- 因变量和自变量之间的线性关系
- 测量误差是随机的
- 残差是同方差的
主要假设是残差是同方差的。同方差意味着它们始终大致相同,这意味着残差不会突然变大。而事实往往不是这样,事情往往不是同质化的。那你会怎么做?好吧,如果你能做到这些技巧中的一个,比如改变 y 值,在做任何线性回归之前转换它们,那就太好了!否则,假设不成立时能做什么?这是一个肮脏的秘密:他们什么都不做。他们只是假装他们的残差是异方差的,尽管事实上他们是异方差的,这意味着误差始终是不一致的。

我们假设的同方差模型
名词(noun 的缩写)b:误差并不是正态分布的,而是均匀分布的,误差只需要始终大致相同。然而,它们确实需要以零为中心。
极端值

易受异常值影响:异常值是具有较大残差的观察值
线性回归容易受到异常值的影响。这里,你有一个很好的线性关系,这条线完美地穿过这些数据点。第二张图不是,因为右上角的数据点将回归线拉得很高。像这样的离群值真的是一个问题,你可以通过各种方式摆脱离群值,比如杠杆和库克距离。
线性回归根据 x 和 y 值的平均值拟合直线。它符合一条穿过两个值的平均值的线。这条线相对于每个点的拉力绕点旋转。离平均值越远的点在斜坡上的拉力越大——这就是杠杆作用。另一种量化每个点的“拉力”的方法是将线与没有每个点的数据拟合,并观察参数如何移动:这是库克距离。
系数和 p 值
当回归线是线性的时,回归系数是一个常数,代表一个变量作为另一个变量的函数的变化率——它是回归线的斜率。
- 系数的 p 值表明这些关系在统计上是否显著。
- t 统计是系数除以其标准误差。
检查残差
这很重要!你应该经常检查残差的趋势。

您应该看不到任何模式
右边的数据点比左边多,这没关系,这只是数据的形状。x 轴上的预测值和 y 轴上的残差,即预测值的误差。
- 残差=观察值(实际数据)-预测值(数据移至回归线的位置)
残差的正值(在 y 轴上)表示预测值过低,负值表示预测值过高。零表示猜测完全正确!
残差中不应该有模式!
问题是,总的来说,残差在右边比在左边更靠近这条虚线吗?或者更糟——虚线下面的残差比虚线上面的多吗?虚线是什么,在你有零残差的地方虚线是零。残差也是预测的 y 值和实际的 y 值之间的差异。它们应该都在零附近徘徊,因为有一个预测的 y 值的意义在于解释你的数据。而且预测的 y 值一般应该接近你的 y 值。
当不是这种情况时,你有残差。有时候,在你的残差中看到一个明显的趋势是一个机会。这意味着您可以在残差中使用该趋势,仅向残差添加另一个模型。
始终绘制残差图来检查趋势。检查残差与 y 的关系,确保它们总是正相关,相关性越高,拟合越差。原因是,如果残差与 y 高度相关,这意味着 y 越大,残差也越大。这实际上经常发生。检验残差的正态性也很重要。
残差为异方差的一个常见标志是“扇形”误差,即误差右侧比左侧大。

注意原始数据中的“扇形”, RHS 的误差比 LHS 大得多
同样,残差中不应该有明显的趋势。但是,通常残差可以被假定为是等方差的,而实际上并非如此,就像下面的扇形一样。第三个结果,非线性的,是一个非常糟糕的结果,不能假设是同方差的,误差趋势是抛物线。

这种非线性误差趋势很可能表明,数据与所用参数之间的关系没有得到最好的捕捉;残差中有信息。看起来我们实际上可以用多项式来拟合这些残差。或者,我们可以首先简单地将多项式添加到我们的线性模型中,这样做之后,总的来说我们会有更少的误差。
多元线性回归

所有这些回归在参数上都是线性的
正如我在上一篇文章中提到的,为什么不通过说指数是一个不同的变量,如 z,来使最后一个公式中的变量线性呢?这些是新的变量,然后我们有一个线性设置,方程变成了多元线性回归。

多个独立变量使这成为多元线性回归的一个例子
一个热编码与虚拟变量
这通常被误解。一种是一次性编码(OHE ),另一种是线性回归的虚拟变量编码方式。虚拟变量基本上和 OHE 一样,你只需要 n-1 个变量。性别不是 OHE 当你只有一个标有“性别”的列,1 代表“男性”,0 代表“女性”时,这实际上是一个虚拟变量。这不是一个热门的编码,因为没有女性一栏,只有男性一栏。所以这一栏不应该叫做性别,因为不存在一和零这样的性别,这一栏应该叫做是男性。
使用虚拟变量,您可以删除列,而使用 OHE,您永远不会删除列。这就是独热编码的定义。热门编码不是数据科学术语,而是来自一个非常不同的工程领域。传递虚拟变量在统计学上更有用,因为你不会有共线性。但是你可能不会有共线性,可能不是肯定的。使用 OHE 允许更好的模型解释。请记住,使用 OHE,您永远不会删除列,这样做是传递一个虚拟变量。
python 中的多元线性回归
下面这个例子,我们想预测股票价格指数,就像其他人一样。
名词(noun 的缩写)b:为了简单起见,这是一个大禁忌,这个模型只是使用测试数据进行拟合——没有训练/调整/测试分割。
# imports
from pandas import DataFrame
from sklearn import linear_model
import statsmodels.api as sm
from sklearn.metrics import mean_squared_error, r2_score
import matplotlib.pyplot as plt# create dataframe from stock data
Stock_Market = {‘Year’: [2017,2017,2017,2017,2017,2017,2017,2017,2017,2017,2017,2017,2016,2016,2016,2016,2016,2016,2016,2016,2016,2016,2016,2016],
‘Month’: [12, 11,10,9,8,7,6,5,4,3,2,1,12,11,10,9,8,7,6,5,4,3,2,1],
‘Interest_Rate’: [2.75,2.5,2.5,2.5,2.5,2.5,2.5,2.25,2.25,2.25,2,2,2,1.75,1.75,1.75,1.75,1.75,1.75,1.75,1.75,1.75,1.75,1.75],
‘Unemployment_Rate’: [5.3,5.3,5.3,5.3,5.4,5.6,5.5,5.5,5.5,5.6,5.7,5.9,6,5.9,5.8,6.1,6.2,6.1,6.1,6.1,5.9,6.2,6.2,6.1],
‘Stock_Index_Price’: [1464,1394,1357,1293,1256,1254,1234,1195,1159,1167,1130,1075,1047,965,943,958,971,949,884,866,876,822,704,719]
}# inputs
df = DataFrame(Stock_Market,
columns=[‘Year’,’Month’,’Interest_Rate’,’Unemployment_Rate’,’Stock_Index_Price’])# check that the data is well imported
df.head()

数据看起来导入得很好
确定感兴趣的变量,并对其进行缩放。扩展您的数据以确保安全!不管别人怎么说,如果你对数据进行缩放,你不会有不同的结果——你可以用数学方法证明这一点。我的陈述是真实的,只要你不考虑浮点的东西。如果你有非常大或非常小的数字,那么你有浮点不精确。这可能会导致很多问题。首先,如果不缩放,你会得到非常大的数字和非常小的数字。首先,如果你缩放一切,那么一切看起来都是一样的。一切都在负二和正二之间,一切都以零为中心。原因是大多数人会使用所谓的标准定标器来定标,也就是你的正态或高斯分布。你要做的是减去平均值,然后除以标准差。
缩放的另一个好处是,如果您使用迭代方法,您的数据通常会收敛得更快。但是,只要你有一个线性系统,就有一个数学证明(这是一个所谓的凸问题),如果是这样,在所有情况下都有一个特定的解决方案,不管数据缩放。原则上,如果你缩放,你不会得到更多或更少的精度——但是浮点不精确是一件要小心的事情!
我们保留“利率”和“失业率”以便进一步分析:
x = df[[‘Interest_Rate’,’Unemployment_Rate’]]
y = df[‘Stock_Index_Price’]# scale the data
from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
scaler.fit(x)
x = scaler.transform(x)from sklearn.linear_model import LinearRegression
regression_model = LinearRegression()
# Fit the data(train the model)
regression_model.fit(x, y)# predict
y_predicted = regression_model.predict(x)print(‘Intercept: \n’, regression_model.intercept_) # pull out intercept
print(‘Coefficients: \n’, regression_model.coef_) # pull out coeffeicients

该输出包括构建多元线性回归方程的截距和系数。
名词(noun 的缩写)b:我们对数据进行了缩放,所以上面的系数反映了这一点。尽管如此,高利率与股价上涨之间存在关联,而随着失业率下降,与价格上涨的关联效应较小。我们可以看到,缩放系数大致相同,因此可以直接比较。
Stock_Index_Price = (Intercept) + (Interest_Rate coef)*X1 + (Unemployment_Rate coef)*X2
插入新号码:
Stock_Index_Price = (1070.083) + (118.233)*X1 + (-80.815)*X2# prediction with sklearn
New_Interest_Rate = 2.75
New_Unemployment_Rate = 5.3
# create a scaled feature array and make the prediction
feature_array = scaler.transform([[New_Interest_Rate ,New_Unemployment_Rate]])
print (‘Predicted Stock Index Price: \n’, regression_model.predict(feature_array))

鉴于新的利率和失业率,我们预计股票会是这个价格
统计模型
相同的分析,但这次是用sm。我再次强调需要知道的更重要的包是sklearn
# with statsmodels
X = sm.add_constant(x) # adding a constant
model = sm.OLS(y, X).fit()
predictions = model.predict(X)
print_model = model.summary()
print(print_model)

Adj R 总是低于 R |由于多输入,我们失去了 2 个自由度
# Summary graphs:
import scipy.stats as stats
import statsmodels.api as statsmodels
from statsmodels.graphics.regressionplots import *
import seaborn as sns
import numpy as npsns.distplot(model.resid, hist=True)
plt.grid(True)
plt.xlabel(‘Residual’)
plt.ylabel(‘Frequency’)
plt.title(‘Residual Histogram’)
plt.show()# Residuals vs Fitted Values
residuals = model.resid # outlier_linear = name of linear model on #our dataset
fitted_vals = model.predict(X) # making predictions from our fit #modelplt.plot(fitted_vals, residuals, ‘o’) # plotting predictions from #fit model vs residualsplt.xlabel(‘Fitted Values’)
plt.ylabel(‘Residuals’)
plt.title(‘Residuals vs. Fitted Values (w/ 1 outlier)’)
plt.show()# create Q-Q plot to ensure normality
statsmodels.qqplot(residuals, stats.norm, fit=True, line=’45')# leverage Plot (Cook’s Distance)
influence_plot(model) # from statsmodels.graphics.regressionplots #import *

残差呈现合理的正态分布

残差图,无明显趋势——残差表现出合理的均方误差

正如所料,较大的残差=对拟合线的较大影响|残差被归一化(学生化)

数据呈正态分布,直方图已确认
结论
我们已经深入研究了线性模型,并运行了一些 Jupyter 笔记本代码单元来研究最小二乘线性回归。记住线性指的是参数——在上面的股票例子中,数据是多元的:我们使用利率和失业率来预测数字结果:给定这些输入的股票价格。强调了残差同方差的重要性,尽管是简短地强调了杠杆和异常值的重要性。
在我的下一篇文章中,我将扩展这些概念并探索使用 bootstrap 和回归模型!
在 Linkedin 上找到我
物理学家兼数据科学家——适用于新机遇| SaaS |体育|初创企业|扩大规模
多元自回归模型和脉冲响应分析
原文:https://towardsdatascience.com/multivariate-autoregressive-models-and-impulse-response-analysis-cb5ead9b2b68?source=collection_archive---------6-----------------------
实践教程
多元宏观经济分析的向量自回归模型和脉冲响应函数的计量经济学重点介绍。

由卢卡斯·布拉塞克在 Unsplash 上拍摄
考虑及时发现有意义模式的难度;例如,随着销售的发展,天气的变化,甚至一个人自己的睡眠周期。与其他数据类型相比,时间序列数据有其独特的考虑因素。为了消除歧义,随机性和周期性要求建模技术特别适合模式识别。单变量或单变量时间序列模型简单有效;然而,只有一个因变量的模型可能过于简化,需要强有力的假设,而这些假设在现实世界中可能是不可行的。这在宏观经济学中尤为重要,因为它几乎总是需要对多个因变量进行建模。因此,宏观经济学在很大程度上依赖于专门为多元时间序列数据开发的计量经济学模型。
多元自回归模型的使用并不仅限于经济学——它们在商业环境中非常有用,尽管主要用途是用于预测。另一方面,经济学家也对理解脉冲响应感兴趣,这需要超越预测来评估因果影响。通过我在世界银行的咨询工作,我发现实证宏观经济研究需要理解如何将多元自回归模型转化为脉冲响应函数。因此,本文的目的是介绍向量自回归(VAR) 模型和脉冲响应分析。其目的是为这个主题提供一个起点,可以有选择地扩展这个主题,为进一步的探索提供有用资源的建议。
首先,我介绍了将多元时间序列模型用于宏观经济学的背景知识。接下来,我将简要介绍 VAR 模型的设置和功能。接下来,我讨论脉冲响应分析,重点是脉冲响应函数的外推和对格兰杰因果关系的评估。最后,我给出了关于在 Python 和 R 中实现 VAR 模型的实际细节,并给出了解释最终脉冲响应函数的建议。
时间序列计量经济学
自回归是描述时变随机过程的术语。相应地,时间序列计量经济学提供了自回归统计模型来描述经济时间序列数据。当处理时间序列数据时,有两个统计特性需要考虑:平稳性和协整性。平稳性是指时间序列的一种属性,使得均值、方差和自相关结构将随时间保持不变。这意味着一个平稳的时间序列会随着时间的推移以一种保持一致的方式发生变化。需要明确的是,这并不意味着级数本身保持不变。更明确地说,一个平稳的时间序列在一段时间内具有恒定的方差,并且没有趋势或周期性波动,例如由季节性引起的波动。时间序列往往不是平稳的,这就需要对时间序列进行转换,以考虑平稳性的假设。转换方法包括对数据进行差分,或者对时间序列取对数或平方根。关于时间序列平稳性的更多细节,我建议这篇有帮助的中帖。
协整是一个时间序列集合的统计属性,在多元设置中特别相关。给定一组建模为方程组的非平稳时间序列,如果该系统的平稳线性组合(具有低阶积分)存在,那么组合的时间序列(多元时间序列)可以说是协整的。更具体地说,由于多变量模型有几个变量,所以这些变量的任意组合之间可能存在协整关系。在实际意义上,这个性质对于理解随机或确定性趋势是很重要的。从统计学的意义上来说,协整意味着一组时间序列的均值差异在一段时间内保持不变,而不提供方向性的指示。换句话说,当多变量时间序列被协整时,在变量的一个或多个组合之间存在长期的、统计上显著的关系。关于时间序列协整的更多信息,请看这篇简短的中期文章。
由于国家一级的经济观察往往频率较低,一个复杂的问题是宏观经济数据往往很少。例如,国内生产总值(GDP)通常作为年度数字发布。尽管如此,当我们的目标是理解一个经济体的整体行为时,我们很自然地会关注时间序列数据。因此,宏观经济学家在理解时间序列的统计特性方面有既得利益,这些特性根据序列的频率和持续时间而变化。作为免责声明,本简介并不是对时间序列的所有重要统计特性的全面回顾。例如,在宏观经济学中,条件异方差和随机波动的概念对于研究时间序列也很重要。在本文中,我主要关注平稳性和协整性,因为这两个统计特性对于理解向量自回归(VAR)模型的基本假设非常重要,这将在下一节中介绍。
向量自回归模型
对于多个时间序列,选择的计量经济模型是向量自回归(VAR)模型。在机器学习领域,该模型有时被称为 VAR 预测算法。基本的 VAR 模型可以描述为协方差平稳,这意味着我们假设时间序列过程是平稳的并且遍历时不变均值、方差、 ( Zivot & Wang,2003 )。VAR 模型的一个简化是,除了确定性回归变量(普法夫,2008 )之外,它只通过内生变量自身的历史来解释内生变量。使模型自回归的是每个时间序列被建模为过去值的函数,这意味着预测器是序列的时间延迟值,也称为“滞后”。在单变量自回归模型中,预测因子或滞后因子会影响因变量,但反之则不会;相反,在像 VAR 这样的多元模型中,变量(即时间序列)以双向关系相互影响。
由于多个时间序列相互影响,因此可以将它们建模为一个方程组,每个时间序列(变量)有一个方程。简而言之,VAR 中的每个时间序列都被建模为其自身的过去值和系统中其他时间序列的过去值的线性组合。从数学上讲,风险值过程可以用下面的等式来描述:

VAR 过程。来源: statsmodels 文档
从形式上看,VAR 过程对一个 T * K 多元时间序列 Yₜ 进行建模,其中 T 表示观测值的个数, K 表示变量的个数;其中 uₜ∼N(0,σᵤ) 和 Aᵢ 是一个 K * K 系数矩阵( statsmodels 文档)。在这里,“变量”一词可互换使用来指代单个时间序列,因此 K 是单个时间序列的数量,而 Yₜ 是该序列的集合。滞后数的范围从 1 到 p ,其中滞后被用作上述方程中的预测量, p 的值代表方程组的阶数。β值代表 Yₜ 的滞后系数,范围从 β ₁到 βₚ 。最后一项“*uₜ”*是误差项,可以认为是“白噪声”。
考虑下面的等式,它显示了最简单的场景,我们有一个滞后和两个时间序列: Y₁ 和 *Y₂.*目标是在时间 t 预测这些序列;因此,计算 Y₁,ₜ 需要 Y₁ 和 Y₂.的过去值同样,计算 Y₂,ₜ 需要两个系列的过去值。

双时间序列一阶 VAR 模型的方程组。改编自来源,图片由作者提供。
在上面的方程组中, Y₁,ₜ -₁ 是 Y₁ 的第一滞后, Y₂,ₜ -₁ 是 Y₂的第一滞后;这两个第一滞后都包含在每个方程中。由于每个系列只有一个滞后,这意味着方程是一阶的(即 p 为 1)。当 p 为 1 阶时,VAR 模型本身可以描述为一阶模型。对于 VAR 过程背后的直觉的极好概述,我推荐 Selva Prabhakaran 的这篇博客文章。
建立 VAR 模型时,滞后阶数 p 非常重要,由观测值数量 T 和方程数量 K 共同决定。在实践中,确定最佳滞后阶数 p 是通过迭代拟合具有递增滞后阶数的模型并选择具有最低 Akaike 信息标准 (AIC)分数的模型来实现的。还有其他指标可用于模型选择,但 AIC 评分被认为是最佳实践。要了解更多信息,我建议这篇中期文章涵盖 AIC 车型选择标准的一些细节。更深入的了解,建议参考柴田日泰的论文题目(柴田,1976 )。
基本风险值模型有多种变化,每种都有特定的变化,旨在克服基本模型的缺点。在没有外生变量( X) 的情况下,扰动方差-协方差矩阵包含了 Yₜ ( Baum,2013 )中变量间同期相关性的所有相关信息。在这种情况下,可以使用结构风险值(SVAR) 模型;当理论可以用来限制同期相关性时,这是有用的。或者,如果 Yₜ 中的变量不是协方差稳定的,但是它们的一阶差是,它们可以用向量误差校正模型 (VECM)建模。这是因为变量之间存在平衡关系,这可以使它们保持稳定而不产生差异。也就是说,当变量之间存在协整时,向量误差修正模型是合适的,因为这样就有可能处理非平稳(即不是协方差平稳的)多元时间序列通过纳入协整关系( Baum,2013 )。
本质上,模型的选择取决于可用于做出主观限制决策的领域知识的深度,以及时间序列的统计特性。尽管我有追求简单的本能,但我发现超越基本 VAR 模型是有实际理由的。或许甚至到了考虑其他宏观经济方法的地步,比如叙事方法或 T2 的乔莱斯基分解法。例如,未能考虑政府支出的预期变动,将导致与叙述方法 ( Ramey,2011 )不一致的风险值结果。
脉冲响应分析
虽然 VAR 模型也用于机器学习领域,但主要用途是用于预测。区分经济学,就是使用 VAR 模型分析脉冲响应和评估格兰杰因果关系。脉冲响应可以定义为系统对外部变化的反应。在宏观经济背景下,该系统是一个包含多变量自回归模型的方程组,外部变化被称为外生冲击。VAR 模型中的所有变量都是相互依赖的;因此,单个系数估计值提供的系统对冲击反应的信息有限( Zivot & Wang,2003 )。因此,需要脉冲响应,它提供了有关风险值模型动态行为的信息。随后,脉冲响应函数(IRF)的目的是描述风险值模型对一个或多个变量冲击的反应演变( Lütkepohl,2010 )。换句话说,IRF 让我们能够在一个方程组中追踪单个冲击的传递,这一特性使其有助于评估经济政策。
为了计算 IRF,我们以移动平均线的形式重写前面描述的 VAR 方程。这显示在下面的等式中:

脉冲响应函数。改编自来源,图片由作者提供。
这种形式是 VAR 模型的向量移动平均表示,其中所有过去的 Yₜ 的值都被替换掉了。重要的是,采用移动平均线可以消除短期波动,突出长期趋势。在上式中, X 表示一组可选的外生变量, Dᵢ 矩阵表示动态乘数函数,也称为传递函数。符号 𝚽 代表移动平均系数,其序列 𝚽ᵢ、代表层位 i 的脉冲响应函数。为了更深入地理解脉冲响应分析,我推荐一篇来自计量经济学杂志 ( Koop 等人,1996 )和另一篇来自经济快报 ( Pesaran & Shin,1998 )的文章。
如前所述,基本风险值模型的缺点是无法描述变量之间的同期关系。在脉冲响应分析中,如果需要描述这些关系,了解冲击对多变量系统的同期影响是很重要的。一般来说,这是通过生成正交脉冲响应函数以适当的顺序排列模型变量来实现的。更具体地,在从误差协方差矩阵的较低乔莱斯基分解获得误差之间的相关性的情况下,应用正交性。另一种方法是使用 SVAR 模型,其中主观限制用于直接模拟同期变量之间的关系。
因果关系在经济学中非常重要,对于时间序列数据,格兰杰因果关系是用于确定特定类型因果关系的统计测试。根据人们对因果关系的哲学定义,人们可能会认为格兰杰因果关系不是真正的因果关系,而仅仅是“预测性”的因果关系。格兰杰因果关系检验专门衡量一个时间序列使用另一个时间序列的先前值预测未来值的能力。准确地说,格兰杰因果检验是在一个变量是否预测另一个变量的背景下对时间关系进行检验(格兰杰,1969 )。作为一个警告,计量经济学家提到了后特设谬误,即假设仅仅因为一件事先于另一件事,这就是因果关系的充分证据,这是一个谬误。关于风险值模型,格兰杰因果关系允许的最大限度是声称一组变量在风险值方程中是格兰杰因果关系。对于测试格兰杰因果关系的简短教程,我建议这篇中期文章。更深入的了解,推荐詹姆斯·d·汉密尔顿的《时间序列分析》教材( 1994 )。
关于因果关系的进一步说明,Valerie A. Ramey 教授对宏观经济学中的因果关系做了一个出色而易懂的概述,我强烈推荐她关于主题的幻灯片。她提到,识别是将相关性转化为因果关系的关键,这一事实因需要解决动态、一般均衡效应和预期等问题而变得复杂。说到脉冲响应分析,除了基本的 VAR,还有两种常用的方法值得探讨:动态模拟( Romer & Romer,2010 )和 Jorda 局部投影( Jorda,2005 )。为了更好地理解脉冲响应在宏观经济学中的应用,我建议 Ramey 的两篇论文可以为脉冲响应分析的有用性提供背景:“好年景和坏年景下的政府支出乘数:来自美国历史数据的证据”( Ramey,2014 )和最近的“基础设施投资的宏观经济后果”( Ramey,2020 )。
IRF 模型的实现和解释
我发现宏观经济数据的最佳来源是国际货币基金组织(IMF)。这个组织共享从他们的网站上免费下载的数据,他们提供一个可以用 Python 或 R 访问的 RESTful API。对于 Python,他们建议使用 T2 的这个指南,对于 R,他们提供一个包“ imfr”来简化 API 的查询。就我个人而言,我更喜欢使用 R 包,因为它对于数据科学来说是最容易访问的。在 Python 中, statsmodels 库可用于构建 VAR 模型,并运行大量的统计测试。在 R 中,压缩性最强的包是 vars 库。
在建立风险值模型之前,测试单个时间序列的平稳性和多元时间序列的协整性是有帮助的。有几种单位根检验可用于检验平稳性,如增广迪基-富勒检验、 KPSS 检验和菲利普-佩伦检验。最常见的协整检验是约翰森检验,它允许多变量时间序列中有一个以上的协整关系。构建 VAR 模型的一个关键部分是通过优化模型选择指标(如阿凯克信息标准(AIC))来确定滞后阶数。幸运的是,使用 Python 库 statsmodels 很容易完成模型选择,该库有一个内置函数选择滞后顺序;这将根据所选指标的最佳分数来确定最佳延迟顺序。
解释脉冲响应函数(IRF)需要可视化,statsmodels 可以方便地绘制指定滞后长度的 IRF。绘制时,如下图所示,默认情况下,渐近标准差在 95%的显著性水平上绘制。

根据摩洛哥汇率和基于实验性文本分析的政策指数(标记为“GRI”)构建的 VAR 模型的脉冲响应函数图。图片作者。
在上图中,绘制了 10 个滞后的 IRF,在每个图中,蓝线代表 IRF,黑线代表置信区间。使用 statsmodels,还可以根据需要以正交或非正交形式绘制 IRF。为了测试脉冲响应函数中的预测误差,我们可以使用预测误差方差分解(FEVD) 。这种方差分解表示自回归中每个变量对其他变量贡献的信息量。本质上,这种分解决定了每个变量的预测误差方差中有多少可以由对其他变量的外生冲击来解释(佩萨兰&申,1998 )。
最后的想法
这篇文章概述了一个个人的旅程,是为了理解以政策为中心的实证宏观经济研究的实验结果而进行的。时间序列计量经济学是一门迷人的学科,我特别欣赏 Python 包 statsmodels 和 R 包 vars 提供的应用便利性。国际货币基金组织的数据 API 和这两个软件包使得玩弄宏观经济数据变得简单,这样我就能够在深入理论之前对这些方法建立一种直觉。数据科学工具的可访问性影响了我学习计量经济学的速度。因此,我非常感谢创建和维护这些工具的开源社区。
我希望这篇文章是对多元自回归模型和脉冲响应分析主题的有益介绍。一如既往,我欢迎反馈和问题,请随时通过 Linkedin 与我联系。
多元分式多项式:为什么这个没有被更多的使用?
原文:https://towardsdatascience.com/multivariate-fractional-polynomials-why-isnt-this-used-more-1a1fa9ead12c?source=collection_archive---------12-----------------------
提示和技巧
…为什么 R 和 Stata 得到了所有的乐趣?
如果你的数据有一些非常奇怪的形状,你会怎么做?像,真的很诡异?除了说“#@ & % it”并决定使用决策树之外?一旦你学会了非参数模型,你很容易认为参数模型太笨拙了,不能认真考虑更复杂的建模项目。您可能知道也可能不知道,像决策树、K-最近邻等非参数模型提供了一个显著的优势,因为它们不像参数模型那样对基础分布或方程做出假设。顾名思义——参数模型试图找到参数或一些最能描述数据的方程或直线,这不是一件轻而易举的事情!事实上,在野外数据很少采用漂亮、舒适的线性形式:

像这样的数据大多存在于入门课程和我的梦里。图 e 作者
更常见的是,您会得到这样的噩梦数据:

上帝啊。被作者诅咒的形象
您可能认为参数估计过于局限,注定产生不充分的结果。那么你如何处理这个问题呢?正如我在上面提到的,您可以完全避免参数化建模,但是这会带来一系列的问题。有时候,你真的必须接受现实,使用参数模型。因为你有一组创造性的数据点,你需要创造性地使用你的方法来建模。这就是多元分数多项式(MFP)的用武之地。
我在研究我之前的博客文章时偶然发现了这种技术(以及 R 和 Stata 中的函数),并最终阅读了太多关于它的内容,以至于我决定最好将其保存到自己的文章中。实际上,我很惊讶网络上没有更多关于它的信息——似乎大多数关于它的讨论或使用它的论文都局限于某些统计空间和医学科学社区。我认为,由于其巨大的潜力,它将被用于其他地方,或者至少作为一个 Scikit-learn 风格的实用程序。但是没有!甚至没有讨论它在 Python 中的使用,这让我感到非常困惑。
所以真的,所有这些的重点是把它作为一个强大的工具放在你的雷达上。MFP 算法将复杂的特征工程和选择一气呵成地结合在一起,以简洁、程序化和统计上合理的方式完成。谁还能要求更多?在这篇博文中,我将带您浏览我偶然发现的各种论文,并带您了解其总体思想,狂热地谈论它的力量,并指出您可能在哪里获得它以供使用。
但是在我进入 MFP 的细节之前,一个关于参数化建模的简单入门…
参数化建模
这背后的整个想法是,你需要得到某种线来拟合一堆数据点。这在线性回归中可能是最好的例证,线性回归由如下等式描述:

奥格。作者图
在 2D,您可以像这样对上面第一幅图中的数据进行图形化建模:

是的,这是一条线。图 e 作者
对于许多变量来说,这条线可能存在于比我们原始的 3D 大脑所能想象的更多的维度中,但它以同样的方式工作。
一个因变量是潜在的许多独立输入变量的输出。请注意,从现在开始,我将只谈论连续变量:那些可以在一个范围内取任何数值的变量。你开始的基本假设是,无党派人士(X,见上图)是一级的——也就是说,是一次幂。但是你不需要限制自己。为了使关系更加线性,独立变量有试验的余地,这就是特征工程的领域。你可以创建交互项,多项式特征,对数变换,任何原始数据的变换。sterling Scikit-learn,在其无限的效用中,有一些函数可以帮助:多项式特性,最显著的。对于门外汉来说,它使你能够在一行代码中,通过将所有的原件相乘或相乘来创建一堆新的特性。比如,如果你有特征 A,B,C,你可以得到 AB,AC,A,B*C,B,C 的 2 次多项式特征。
然而,除了前面提到的实用程序之外,以编程方式创建非线性特性并不容易,通常需要一定程度的领域专业知识来知道应该注意什么。但是这也需要一些猜测,而且不管你对这个领域了解多少,你都不太可能想到极其复杂和描述性的工程特性。从多项式特性的角度来看,仅仅制造一船的特性会导致其他问题,因为只有一小部分是真正重要的。这种散弹枪式的方法需要一些健壮的特征选择,以便去除你刚刚添加的所有不必要的复杂性,留下真正重要的复杂术语。
如果我们可以创造一些极其复杂的术语 和 来去除不必要的复杂性,会怎么样?
多元分数多项式(MFP)
据我所知,这项技术最早发表于 1994 年,来自《皇家统计学会杂志》,作者是帕特里克·罗伊斯顿和道格拉斯·g·奥特曼。在论文的总结中,他们描述了我在上面描述的一些相同的弱点,以及其他一些,给出了传统曲率解决方案的失败的完整描述。然后,他们提出了一个解决方案,即“扩展的曲线族……其幂项被限制在一个预定义的整数和非整数值的小集合中”。幂的选择使得传统多项式成为该族的子集这个列表包含了你可以为一个变量设计的最重要的特性。然后,他们继续说,这或多或少是在文献中出现了一段时间的东西的形式化,并断言强积金的灵活性和易用性。在论文的后面,他们提出了一种算法,用于迭代地为每个因变量选择该族中的幂。这就是我上面提到的关键特征选择组件的用武之地,因为它包括作为过程一部分的向后消除。这篇论文以医学中的几个例子作为结尾——也许这是一个有趣的预兆,预示着这种技术直到今天仍然存在于这个领域。我不会深入研究本文中的各种例子(以及下面的其他例子),但是请看看这种技术如何处理真实世界的数据。挺有意思的。此外,如果你渴望更多的例子,简单地搜索“多元分数多项式”就会从医学数据空间中得到大量的例子。
“曲线族”:特征工程变得更容易
我们将首先深入研究他们的“曲线族”的基本设置,然后深入研究他们提出的迭代算法。来自 Duong 等人(2014) 的这篇论文提供了一个比我能想到的任何东西都好得多的总结。有一个预定义的集合 S = {-2,-1,-0.5,0,0.5,1,2,3}包含了你的自变量的所有可能的幂(0 定义为 ln(X))。您的变量可以采用 xp(1 度)或 xp1+x^p2(2 度)的形式来表示不同的幂值(p、p1 和 p2),这些值取自集合 s。从技术上讲,这可以扩展到 2 度以上,但 Royston 和 Altman 认为这是不必要的。这两次分数多项式(FP)是这样构造的:
FP 度 1 带 1 次方 p:
y = β0 +β1X^p
(当 p=0 时):y= β0 +β1ln(X)
FP 度 2 带一对幂(p1,p2):
y = β0 + β1Xp1 + β2Xp2(适用于 p=0 的相同规则)
(当 p1=p2 时):y = β0 + β1Xp1 + β2Xp2*ln(X)
根据这种结构,FP1 有 8 种不同功率值的 8 种模型,FP2 有 36 种不同的模型——这 8 种值的 28 种组合和 8 种重复组合。总的来说,我们有 44 种可能的模型来拟合我们的数据。重要的是,要记住这些只是起点,系数 β 的值也会严重改变这些线的形状(见下文)。
那又怎样?仅仅基于方程式,可能很难看出其中的价值;我听到了。一个直观的演示可能最能解释为什么这 44 个模型如此有效。当你看下面的图表时,思考一下线迹有多复杂,仅仅通过盯着散点图来试图独自思考这个问题是多么令人头痛。

FP 1 次多项式,从 X=~0 到刚好高于 X=1。所有β-系数= 1。作者图,灵感来自 Duong 等人

FP 1 次多项式,从 X=1 到 X=5。所有β-系数= 1。作者图,灵感来自 Duong 等人

FP 次多项式。根据上述结构,元组表示 X 的幂。所有β-系数= 1。我花了很长时间做这个。作者图,灵感来自罗伊斯顿等人

形式为(-0.5,2)的 FP 次 2 多项式,具有不同的β-系数值。作者图,灵感来自罗伊斯顿等人
如果有希望的话,你会得出和我一样的结论,那就是在没有帮助的情况下想出这个几乎是不可能的!这就是特征工程的力量所在——这种方法为我们的独立变量提供了一组最具描述性的力量,以及将它们放在一起的结构。这就足够了,但是方法还附带了一个特性选择组件。
内置功能选择
同样,我将使用其他人的清晰解释来涵盖这一点,这些解释来自 Duong 和张等人(2016) 。张概述了一般程序,如何选择适当的程度和正确的权力,为您的自变量。它首先指出该过程有两个组成部分:
- 统计上无关紧要的变量的反向消除
- 变量 FP 度的迭代检验
相应地,对于上述两个部分,您需要两个显著性水平: α1 ,用于排除/包含变量,以及 α2 ,用于确定分数变换的显著性。注意 a1 和 a2 可以是(并且经常是)相同的值,但是这是你可以随意处理的。
因此,有了你的 α 值和连续变量列表,你就可以开始建立一个包含所有变量的多变量线性模型。或者,您可以对目标的每个变量进行单变量分析,并且只包括那些具有 p < 0.25 或 0.2 的变量,以帮助削减真正不重要的变量。然后,从第一个模型开始,按照 p 值递增的顺序组织所有变量。
接下来,您选择最高、最低的 p 值变量并开始封闭测试,该测试跟踪变量形式的变化如何影响整个模型的拟合(也就是它不是一个单变量模型)。这如下进行(来自 Duong 的关于统计测试的细节):
- 根据偏差(也称为模型误差),找到该变量的最佳拟合二次分数多项式(FP2)。使用具有 4 个自由度的卡方差检验,将其与零模型(变量不存在)进行比较。如果测试不显著(根据 α1 ),则删除变量,并得出变量对目标没有影响的结论。否则,继续。
- 使用具有 3 个自由度的卡方差检验,将步骤 1 中的 FP2 模型与线性模型(可变功率= 1)进行比较,以确定非线性。如果测试不显著(根据 *α2* ,变量是线性的,封闭测试结束。否则,继续。
- 类似于步骤 1,为变量找到最佳拟合的一次分数多项式(FP1)。使用 2 个自由度的卡方差检验与 FP2 模型进行比较。如果测试不显著(根据 α2 ,模型不会从额外的复杂性中受益,正确的模型是 FP1。
(关于我们为什么使用卡方差检验来评估这些模型的更多信息, 看这里 )

上述封闭测试算法的流程图。图由作者提供,灵感来自张等人
现在,一旦您对最低的 p 值变量执行了上述封闭测试,您就可以对先前在原始大线性模型中生成的有序列表中的下一个最高的 p 值变量进行同样的评估。先前 X 的 FP 形式被保留,但是 β 系数可以相应地改变。每隔一个变量继续这样,这标志着第一个周期。第二个周期是相同的过程,只是从第二低的 p 值变量开始,并保持先前低的 p 值变量的 FP 形式。这个循环过程迭代地继续,直到两个循环收敛,并且没有发生变化。

查找模型中所有变量的 FPs 的流程图。图由作者提供,灵感来自张等人
更复杂,对于顽固的复杂
如果你的数据不知何故是特别是香蕉,愿上帝保佑,你可以考虑用你漂亮的新 FPs 做其他事情。它们可能足够复杂,但是您可以考虑将 FPs 乘以一个多项式特性。类似地,您可能决定完全避开 MFP 判定算法(直接在上面),而简单地用 FP 集合 S (= {-2,-1,-0.5,0,0.5,1,2,3},其中 0 为 ln(X))中的幂的变量来构造多项式特征。考虑到包含负幂、ln 和平方根,与基多项式特性相比,这可能仍会为您提供相当多的额外建模能力。当然,以上任何一个决定都可能需要额外的特性选择。
缺点,因为不是一切都是完美的
据我所知,MFP 的主要缺点在于计算量太大。但我认为这并不十分令人惊讶,因为你需要经历多少步骤才能找到每个特征的理想 FP 形式——在一个典型的场景中,选择算法需要计算 44 个模型进行比较,每个特征的*,这可能需要相当长的时间。但放在上下文中,能够捕捉相似复杂程度的模型(例如,网格搜索随机森林或神经网络)可能不会好到哪里去。此外,对于其他一些模型,运行时间会减少,但最终会牺牲可解释性。MFP 似乎取得了很好的平衡。*
此外,MFP 似乎忽略了交互条款,这可能会削弱其作为工具的整体实力。如果两个变量有协同效应,MFP 将在算法执行时忽略它们。解决这个问题的一个简单方法是在开始时加入交互术语,但是增加特性会增加已经很昂贵的流程的运行时间,正如我刚才提到的。或者,正如我在上一节中所述,您可以完全放弃内置的选择算法,尝试一种多项式 Features 类型的工作流,只需使用 FP set S 中的幂。这肯定是一种可以尝试的方法,可以看出什么效果最好。
开门见山 Python 包在哪里?
谢谢你读到这里,我知道这是相当多的细节。不幸的是,我在另一边没有任何好消息给你。根据大量的搜索,似乎没有简单的方法可以将它集成到基于 Python 的数据项目中。我为所有没有回报的构建道歉——在许多方面,这篇文章表达了我自己的沮丧。正如我在引言中提到的,在医学统计学的范围之外,对这种非常强大的方法没有太多的讨论(如果有的话),尽管它的广泛用途是显而易见的。它确实作为函数存在于 R 和 Stata 中,但是除非我遗漏了某人晦涩的 GitHub repo,否则它不存在于任何 Python 包中,坦率地说,我觉得这很令人震惊。Python 中最接近于建模这种级别的曲线细节的是 Scikit-learn 的样条函数,它似乎做得很好——但它本身很难处理,而且 MFP 方法有明显的优势。
这种技术与统计世界的其他部分隔离开来真的很糟糕,如果幸运的话,这篇文章可能会激发一些改变。也许我会改变,写一个 Scikit-learn 风格的实用程序来执行 MFP 和增压我的参数模型,当我这样做时,我会确保链接下面的 GitHub repo。但与此同时,亲爱的读者,如果你现在迫切需要这个功能,希望你能够使用我在这篇文章中列出的资源作为起点,同时构建自己的功能。
参考文献:
罗伊斯顿博士和奥特曼博士(1994 年)。使用连续协变量的分数多项式回归:简约参数模型。皇家统计学会杂志。系列 C(应用统计学), 43 (3),429–467。https://doi.org/10.2307/2986270
Duong 和 d . Volding(2014 年)。模拟连续风险变量:分数多项式回归导论。越南科学杂志,1 (2),1–5。
张 Z. (2016)。回归模型的多元分式多项式方法。转化医学年鉴, 4 (9),174 页。https://doi.org/10.21037/atm.2016.05.01
Python 中的多元异常检测
原文:https://towardsdatascience.com/multivariate-outlier-detection-in-python-e946cfc843b3?source=collection_archive---------0-----------------------
Python 中的多元异常值和马氏距离
检测多元数据中的异常值通常是数据预处理中的挑战之一。有各种距离度量、分数和技术来检测异常值。欧几里德距离是最著名的距离度量之一,用于根据异常值到中心点的距离来识别异常值。还有一个 Z 值来定义单个数值变量的异常值。在某些情况下,聚类算法也是首选。所有这些方法都从不同的角度考虑异常值。基于一种方法发现的异常值可能不会被其他方法发现为异常值。因此,应该通过考虑变量的分布来选择这些方法和指标。然而,这也带来了对不同指标的需求。在本文中,我们将讨论用于检测多变量数据中异常值的距离度量,称为 Mahalanobis 距离。
马哈拉诺比斯距离
马哈拉诺比斯距离(MD)是一种有效的距离度量,用于计算点和分布之间的距离(参见)。它对多元数据非常有效,因为它使用变量的协方差矩阵来查找数据点和中心之间的距离(见公式 1)。这意味着 MD 基于数据点的分布模式来检测异常值,这与欧几里德距离不同。请参见图 1 以了解区别。

图 1 —欧几里得距离与马氏距离(图片由作者提供)
从图 1 可以看出,数据点分散在某个方向。虽然欧几里德距离可以将一些非离群点指定为这种分布中的离群点,但是 Mahalanobis 距离可以跟上它。从图 2 可以看出,非线性关系也是如此。

图 2—欧几里得距离与马氏距离(图片由作者提供)
这种差异的主要原因是协方差矩阵,因为协方差表明变量如何一起变化。在计算 n 维空间中的中心和点之间的距离时使用协方差提供了基于变化找到真实的阈值边界。从公式 1 中所示的 MD 的距离公式可以看出,协方差矩阵呈现为 C,并且其负一次幂已经取值。向量 Xpi 表示 n 维空间中观察值的坐标。例如,假设有一个包含三个变量的数据集,它的第一行和第二行可以表示为:Xp1:[13,15,23] and Xp2:[12,14,15]。但是,在识别异常值时,应该找到中心与每个观测值之间的距离,而不是找到每个点之间的距离。取每个变量的平均值就可以得到中心点。

公式 1 —两点之间的马氏距离
注意:与图 1 和图 2 中给出的示例数据不同,当变量大部分分散在一个圆中时,欧几里德距离可能是更合适的选项。
Python 的 Mahalanobis 距离
没错,是时候用 Python 求 Mahalanobis 距离了。如果你对 R 而不是 Python 感兴趣,可以看看我的另一篇文章。
在“Scipy”库中有一个计算 Mahalanobis 距离的方法。你可以从scipy.spatial.distance.mahalanobis.访问这个方法,你也可以在这里看到它的细节。在以下步骤中,我们将使用公式 1 中给出的公式创建自己的方法来计算 Mahalanobis 距离,而不是使用此方法。
我将使用名为“空气质量”的数据集,只使用变量“臭氧”和“温度”来检测异常值。或者,你可以从这里下载这个数据集或者使用你的数据集。
首先,我们应该导入必要的库和数据集。为了使数据集为计算做好准备,只需选择变量“臭氧”和“温度”。另外,不要面对任何错误,如果有错误,NA 值应该被丢弃。此外,我更喜欢使用 NumPy 数组,而不是使用 pandas 数据帧。所以我把它转换成了 NumPy 数组。
第二步,我们需要获得必要的值来计算中心和点之间的距离。这些是“臭氧”和“温度”变量之间的中心点和协方差矩阵。
第三步,我们准备寻找数据集中中心点和每个观察点之间的距离。我们还需要从卡方分布中找到一个临界值。卡方用于查找截止值的原因是,Mahalanobis 距离返回距离的平方(D)。在寻找截止值时,我们还应该将分位数取为 0.95,因为 0.95 以外的点(双尾)将被视为异常值。分位数越少意味着临界值越小。我们还需要卡方的自由度值,它等于我们的数据集中变量的数量,所以是 2。
最后,我们在索引[24,35,67,81]处有四个异常值。现在,让我们通过 make 和 plot 来更清楚地理解这个过程。
如图 3 所示,这些点位于椭圆之外,被检测为异常值。这个椭圆表示根据 MD 包裹非异常值的区域。

图 3 —温度—臭氧变量中的异常值(图片由作者提供)
下一步是什么?
本文讨论了马氏距离及其与欧氏距离的区别。我们还从头开始在 Python 上应用了 Mahalanobis 距离公式。如前所述,根据数据在 n 维空间中的分布选择距离度量非常重要。你也可以看看另一个叫做库克距离的距离度量。
如果您有任何问题,请随时留下您的评论。
如果您对如何使用 R 中的马氏距离来检测异常值感兴趣,可以查看我的另一篇文章。
基于变压器的多元时间序列预测
原文:https://towardsdatascience.com/multivariate-time-series-forecasting-with-transformers-384dc6ce989b?source=collection_archive---------0-----------------------
思想和理论
无图时空学习

时间自我注意(左)和时空自我注意(右)。将每个时间步长分割成独立的时间序列变量,可以让我们了解各个变量之间的注意力模式。[本文所有图片均由作者提供]
机器学习的许多现实应用涉及基于历史背景对一组相关变量的结果进行预测。我们可能希望预测连接道路上的交通状况、附近地区的天气或对类似产品的需求。通过对多个时间序列一起建模,我们希望一个变量的变化可以揭示相关变量行为的关键信息。多元时间序列预测(TSF)数据集有两个难点:我们需要学习时间关系以了解值如何随时间变化,以及空间关系以了解变量如何相互影响。
流行的 TSF 统计方法可能难以解释长的上下文序列并扩展到复杂的变量关系。深度学习模型通过利用大型数据集来预测遥远未来的罕见事件,从而克服了这些挑战。许多方法专注于学习跨长时间跨度的时间模式,并且基于递归或卷积层。在高度空间域中,图形神经网络(GNNs) 可以将变量之间的关系分析为连接节点的图形。该图通常是预定义的,例如交通预测中的道路和十字路口地图。
在这篇文章中,我们希望解释我们最近在混合模型上的工作,该模型纯粹从数据中学习跨空间和时间的图形。我们将多元 TSF 转换成一个超长序列预测问题,这个问题可以通过最近对 Transformer 架构的改进来解决。这种方法在从温度预测到交通和能源预测等领域都取得了有竞争力的成果。
这是我们的研究论文的非正式摘要, “用于动态时空预测的长程变压器”, 格雷斯比,王,齐,2021。论文可在 arXiv 上获得,复制实验和将模型应用于新问题所需的所有代码可在 GitHub 上找到。
变压器和时间序列预测
变压器是自然语言处理(NLP)任务的最新解决方案。它们基于多头自我关注(MSA)机制,在该机制中,沿着输入序列的每个标记与每一个其他标记进行比较,以便收集信息和学习动态上下文信息。变压器学习其输入之间的信息传递图。因为它们不按顺序分析它们的输入,变压器在很大程度上解决了在长期预测中阻碍递归神经网络(RNNs)的消失梯度问题。由于这个原因,Transformers 已经被应用于具有长历史信息的数据集,包括 TSF 。
多元 TSF 数据集通常按时间组织:所有 N 变量的值都表示为一个向量。然而,这仅允许变压器学习整个变量堆栈之间的关系。在复杂的多变量 TSF 问题中,每个变量与其历史以及其他变量历史中的不同事件都有意义的关系。对 TSF 数据的转换器的标准应用程序无法了解这一点,因为它将给定时间步长的每个变量的值视为其图形中的单个令牌;每个变量不能对它应该优先考虑的环境有自己的看法。这与 NLP 任务不同,在 NLP 任务中,变形金刚非常受欢迎,每个标记都代表一个统一的概念(一个单词)。
我们通过创建一个新的预测问题来解决这个问题,其中每个令牌代表每个时间步长的单个变量的值。然后,变压器可以随时关注任何变量的值,以便做出更准确的预测。这篇文章顶部的图表显示了这两种注意力的区别。
时空形成器
我们使用一种输入格式,其中在 T 时间步长的 N 变量被展平为一系列**(NxT)**令牌。每个变量的值用前馈层投影到高维空间。然后,我们添加关于对应于每个令牌的时间步长和变量的信息。时间和变量嵌入是随机初始化的,并与模型的其余部分一起训练,以改进我们对时间和空间关系的表示。我们希望预测的未来时间步长的值被设置为零,我们告诉模型哪些值在二进制“给定”嵌入中丢失了。不同的组成部分被相加和布局,使得 Transformer MSA 跨时间和可变空间构建一个时空图。嵌入管道如下图所示。

时空序列输入: (1)包含时间信息的多元输入格式。解码器输入丢失("?)将进行预测的值设置为零。(2)时间序列通过 Time2Vec 层,以生成表示周期性输入模式的频率嵌入。(3)二进制嵌入表示该值是作为上下文给出的还是需要预测的。(4)每个时间序列的整数索引被映射到具有查找表嵌入的“空间”表示。(5)用前馈层投影每个时间序列的 Time2Vec 嵌入和变量值。(6)值&时间、变量和给定的嵌入被求和并布局,使得 MSA 以更长的输入序列为代价处理跨时间和变量空间的关系。
标准转换器将每个标记与其他标记进行比较,以找到序列中的相关信息。这意味着模型的运行时和内存使用随着其输入的总长度成二次方增长。我们的方法通过使序列 N 比时间序列本身长一倍,大大夸大了这个问题。我们方法的其余部分处理工程挑战,使得在没有最高端 GPU/TPU 的情况下训练该模型成为可能。
在长输入序列应用中,高效变压器是一个活跃的研究领域。这些“远程转换器”看起来适合 GPU 内存中较长序列的梯度计算。他们经常通过添加启发法来使注意力图稀疏来做到这一点,但是这些假设在 NLP 之外并不总是成立的。我们使用执行者注意力机制,它用随机特征的内核线性近似 MSA。Performer 的效率足以适应数千个令牌的序列,并让我们在几个小时内在一个具有 10GB GPUs 的节点上训练我们的模型。
历史数据的上下文序列和我们想要预测的目标时间戳被转换成长的时空序列。基于执行器的编码器-解码器架构处理该序列,并预测每个变量在未来时间步长的值作为单独的令牌。然后,我们可以将预测重新堆叠为其原始格式,并进行训练以最小化预测误差指标,如均方误差。该模型还可以通过输出正态分布的均值和标准差来创建一系列预测,在这种情况下,我们训练以最大化地面实况序列的概率。完整的模型架构如下所示。

时空模型架构: 目标和情境嵌入通过一系列有效的注意层。“全局注意”模块查看每个变量的每个时间步长,而“局部注意”模块单独查看每个变量的时间步长。我们发现这在具有大的 N 的数据集中是有帮助的。
应用程序
我们将该模型与更标准的 TSF 和 GNN 方法进行比较。线性 AR 是一个基本的线性模型,被训练来进行自回归预测,这意味着它一次输出一个令牌,并将其输出作为下一次预测的输入。 LSTM 是一个标准的基于 RNN 的编解码器模型,没有注意。 LSTNet 是基于 Conv1D 层和 RNNs 的自回归模型,通过跳过连接来记住长期上下文。DCR nn是一个基于图形的模型,当预定义的变量图形可用时可以使用。像我们的方法一样, MTGNN 是一种 TSF/GNN 混合,它从数据中学习其图形结构,但不使用变压器进行时间预测。最后,我们包括了我们自己的模型的一个版本,该版本不将令牌分成时空图;每个变量的值照常堆叠在一起。这种"【太阳穴】消融是 告密者的替身,但它利用了我们其余所有的工程技巧和训练过程来隔离时空注意力的好处。
首先,我们来看一个天气预报任务。我们使用 ASOS 网络收集了来自德克萨斯和纽约机场的大量温度数据。这两个群体之间的地理分离使得空间关系更加重要,并且这些关系必须从经验中学习,因为我们不提供任何位置信息。我们预测未来 40、80 和 160 小时,并比较均方误差(MSE)、平均绝对误差(MAE)和均方根误差(RRSE)。这个实验集中在 TSF 模型上,因为没有可用的图表。

时空形成器优于基线,它相对于时间注意力版本的优势似乎随着我们预测的长度而增加。我们的目标是学习一个时空注意力图,我们可以通过可视化时空形成器的注意力网络来验证这是什么。注意力矩阵通过揭示每个标记给予整个序列的注意力来可视化 MSA 每行是一个令牌,该行的列显示该令牌对序列中其他令牌的关注,包括它自己。下图显示了时空模型和仅时间模型的气象站变量和注意力矩阵,其中深蓝色对应更多的注意力。标准的时间机制学习一种滑动的波状模式,其中每个令牌主要集中在它自己(沿着对角线)和序列的最末端。Spacetimeformer 将该序列展平为单独的变量,每个变量都有自己的记号子序列(用绿色箭头和变量形状表示)。这导致了“块结构”的注意力矩阵,其中一个变量的所有标记倾向于优先考虑其他变量子集的时间步长。我们可以解释这些模式来理解模型正在学习的空间关系。在这种情况下,该模型可以正确地将德克萨斯州和纽约站聚集在一起——如果放大,您可以在每个子序列中看到相同的波状时间模式。

接下来,我们看看交通和能源预测中的三个基准数据集。AL Solar 测量阿拉巴马州 137 个地点的太阳能输出,而 Metr-LA 和 Pems-Bay 数据集分别测量洛杉矶和旧金山周围 200 多个道路传感器的车辆速度。我们生成 4 小时太阳预报和 1 小时交通预报。结果如下表所示。
****
时空形成器在所有情况下学习精确的预测模型。交通结果很有趣,因为复杂的道路网络使这些问题成为图形神经网络研究中的常见基准,在图形神经网络研究中,地图可以变成图形,并提前提供给模型。我们的模型具有相当的预测能力,同时隐含地从数据中学习路线图。
如果你想将这种方法应用到新的问题中,模型和训练过程的源代码会在 GitHub 的 QData/spacetimeformer 上发布。在我们的论文中可以找到更多背景和相关工作的详细解释。
— — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — — —
杰克·格雷斯比,王哲,和齐。这项研究是由弗吉尼亚大学的 QData 实验室完成的。
基于 LSTM 的超前滞后时间步长多元时间序列预测
原文:https://towardsdatascience.com/multivariate-timeseries-forecast-with-lead-and-lag-timesteps-using-lstm-1a34915f08a?source=collection_archive---------10-----------------------
为什么是多元变量,它如何帮助做出更好的预测?
时间序列预测在大多数行业的决策中起着至关重要的作用。例如,预测运输公司要购买的集装箱数量可以为公司节省数百万美元。同样,预测特定产品类型的需求在定价中扮演着非常重要的角色,因此也是电子商务公司盈利的关键。
在大多数情况下,是企业或运营团队知道影响需求或供应的因素。简单地根据历史模式进行预测并不总是能得到想要的结果,或者并没有考虑到未来的前景。过去的错误有可能在未来的预测中重复。考虑影响因素并让团队有能力研究和理解它们对预测的影响总是好的。
这就是多元时间序列预测出现的时候。让我们用下面的图片来理解多元预测

图 1:具有滞后数据的多元时间序列预测(滞后=5 步)
图 1 描述了因变量 Y 在时间 t 的多变量时间序列预测,滞后=5。红色单元格是时间 t 时的预测值,该值取决于黄色单元格中的值(t-5 到 t)。这些是影响预测 Y at t 的独立变量。
我们可以将多元时间序列视为回归问题,其中独立变量是前一个滞后(直到 t-1)的特征,以及时间 t 的独立值。使用这种方法,对预测的控制远远超过了对前一个时间戳的控制。
下面是多元时间序列,它也考虑了铅值

图 2:具有超前和滞后特征的多元时间序列
从上图我们可以看出,除了滞后特性,lead=2 (t+2)时间步长也被认为是进行预测的时间步长。这使我们能够更好地控制影响预测的因素。在许多情况下,我们知道一些未来的因素也会影响我们当前的时间预测。通过这些方法,决策团队可以真正根据独立要素的各种输入值来模拟预测。
利用 LSTM 实现预测模型
现在让我们看看如何实现具有超前和滞后特性的多元时间序列。
- 用提前和滞后因子准备好数据
使用 LSTM 对时间序列执行回归任务的主要区别在于,在时间序列中,需要考虑超前和滞后时间戳数据。让我们定义一个函数,它可以基于作为参数的超前和滞后来做这件事
# convert series to supervised learning
def series_to_supervised(data, n_lag=1, n_lead=1, dropnan=True):
n_vars = 1 if type(data) is list else data.shape[1]
df = DataFrame(data)
cols, names = list(), list()
# input sequence (t-n, … t-1)
for i in range(n_lag, 0, -1):
cols.append(df.shift(i))
names += [(‘var%d(t-%d)’ % (j+1, i)) for j in range(n_vars)]
# forecast sequence (t, t+1, … t+n)
for i in range(0, n_lead):
cols.append(df.shift(-i))
if i == 0:
names += [(‘var%d(t)’ % (j+1)) for j in range(n_vars)]
else:
names += [(‘var%d(t+%d)’ % (j+1, i)) for j in range(n_vars)]
# put it all together
agg = concat(cols, axis=1)
agg.columns = names
# drop rows with NaN values
if dropnan:
agg.dropna(inplace=True)
return agg
上面的函数将数据转换成带有定制的 n_lag 和 n_lead 步骤的时间序列。该函数的输出包含滞后和超前步骤的数据,作为具有(t-n)或(t+n)时间戳的列
reframed = series_to_supervised(values, n_lag, (n_lead+1))#removing the future (t+n) dependent variable (Y)if n_lead>0:
reframed= reframed.drop(reframed.iloc[:,[i for i in range(df_no.shape[1]*(n_lag+1),reframed.shape[1],df_no.shape[1])]],axis=1)
上述代码有助于在训练模型时删除未来的 Y(t+n)。一旦我们放弃了未来的 Y,我们有了重构的数据,这就像训练 LSTM 回归问题一样简单。
# splitting reframed to X and Y considering the first column to be out target featureX=reframed.drop(['var1(t)'],axis=1)
Y=reframed['var1(t)']X_values=X.values
Y_values=Y.values#n_preduct being the test lengthtrain_X,train_Y = X_values[:(X_values.shape[0]-n_predict),:],Y_values[:(X_values.shape[0]-n_predict)]
test_X,test_Y = X_values[(X_values.shape[0]-n_predict):,:],Y_values[(X_values.shape[0]-n_predict):]#reshaping train and test to feed to LSTM
train_X = train_X.reshape((train_X.shape[0], 1, train_X.shape[1]))
test_X = test_X.reshape((test_X.shape[0], 1, test_X.shape[1]))
创建一个简单的 LSTM 模型
opt = Adam(lr=0.001, beta_1=0.9, beta_2=0.999, decay=0.01)
model = Sequential()
model.add(LSTM(100,return_sequences=True, input_shape=(train_X.shape[1], train_X.shape[2])))
model.add(Dropout(0.25))
model.add(LSTM(units=50,return_sequences=True))
model.add(Dropout(0.20))
model.add(LSTM(units=10,return_sequences=False))
model.add(Dense(units=1, activation='linear'))
model.compile(loss='mae', optimizer=opt)
一旦模型准备就绪,我们就可以根据训练数据训练模型,并在测试中对其进行测试。下面的代码显示了一些训练检查点,可以用来帮助训练一个好的模型。
#adding few model check points
es = EarlyStopping(monitor='val_loss', min_delta=1e-10, patience=10, verbose=1)
rlr = ReduceLROnPlateau(monitor='val_loss', factor=0.01, patience=10, verbose=1)
mcp = ModelCheckpoint(filepath="/test.h5", monitor='val_loss', verbose=1, save_best_only=True, save_weights_only=False)
tb = TensorBoard('logs')history = model.fit(train_X, train_Y, epochs=50, batch_size=10,callbacks=[mcp,rlr],validation_data=(test_X, test_Y), verbose=2, shuffle=False)
一旦模型被训练,我们就可以得到测试数据的预测
yhat = model.predict(test_X)
总结
在本文中,我们看到了什么是多元时间序列,以及如何使用超前和滞后数据进行预测。使用这种方法时,需要注意以下几点
- 随着 n_lead 和 n_lag 的增加,特定预测的特征数量也会增加。例如,如果我们在每个时间戳有 5 个独立的特征,并且我们认为 n_lag=5 并且 n_lead =2,那么重构后的所有特征将是 5+5*(n_lag)+5*(n_lead),这是 40 个特征的情况。
- 需要大量的训练数据,因为使用滞后和超前会减少训练行数。
- 每次我们改变 n_lead 和 n_lag 时,随着特征数量的增加或减少,必须明智地考虑 LSTM 模型结构以避免过度拟合。
使用 ConvLSTMs 生成音乐
原文:https://towardsdatascience.com/music-generation-with-convlstms-506fdce3b610?source=collection_archive---------18-----------------------
用可播放的人工智能生成的音乐文件

Rajesh Kavasseri 在 Unsplash 上的照片
这个项目是我的另一个项目的延续,因为我想到了一个产生更好结果的想法。我将使用 LSTMs,而不使用 GANs。这将使训练更快,并允许客观的评估函数来评估模型的性能。
在人类作曲家创作的音乐上训练,模型被训练使用过去的音符来预测下一个音符。这导致模型发现过去的音符和下一个音符之间的模式,这将允许原始音乐的生成
下面是我特别喜欢的一段摘录:
《LSTM 2》很有趣,因为它的结尾听起来像是要进入一个完美的节奏,和弦进行到 IV-V-I。在这个可能的序列之前的和弦也很有特点。
记住这段音乐,让我们开始构建程序:
数据预处理:
机器学习的第一步是数据预处理。对于这个项目,它包括两个步骤:
访问 Midi 文件:
我在网上找到了一个古典作品的数据集,是从一个在线网站上搜集来的。我把所有的 midi 文件提取出来,放在一个文件夹里。
将 Midi 文件转换成图像:
我找到了一个 github 页面,其中有两个程序使用 music21 库将 midi 文件转换成图像,然后再转换回来。
每个音符可以表示为一个白块。音块的高度决定了音高,长度决定了音符演奏的时长。
然后,我编写了一个脚本,将这两个程序与我的 midi 文件集成在一起,在不同的目录中创建新的图像:
import os
import numpy as nppath = 'XXXXXXXXX'os.chdir(path)
midiz = os.listdir()
midis = []
for midi in midiz:
midis.append(path+'\\'+midi)
这个脚本转到 midi 目录,然后将所有 midi 文件路径添加到一个列表中,供以后访问。
from music21 import midimf = midi.MidiFile()
mf.open(midis[0])
mf.read()
mf.close()
s = midi.translate.midiFileToStream(mf)
s.show('midi')
这个脚本打开第一个 midi 文件,并播放它以确保程序正常工作。如果在非交互环境中运行,这可能不起作用。
import os
import numpy as np
import py_midicsv as pmos.chdir(path)
midiz = os.listdir()
midis = []
for midi in midiz:
midis.append(path+'\\'+midi)
new_dir = 'XXXXXXXX'
for midi in midis:
try:
midi2image(midi)
basewidth = 106
img_path = midi.split('\\')[-1].replace(".mid",".png")
img_path = new_dir+"\\"+img_path
print(img_path)
img = Image.open(img_path)
hsize = 106
img = img.resize((basewidth,hsize), Image.ANTIALIAS)
img.save(img_path)
except:
pass
这个脚本使用 github 页面中的 midi2image 函数,并根据 midi 文件的路径转换所有的 midi 文件。它们也被重新成形为形状(106,106)。为什么?106 是程序的高度,因为这是 midi 文件中可能的音符数。此外,对于卷积转置,使用正方形要容易得多。
构建数据集:
import os
imgs = os.listdir()
pixels = []from PIL import Image
import numpy as npfor img in imgs:
try:
im = Image.open(img).rotate(90)
data = np.array(im.getdata())/255
pix = (data).reshape(106,106)
pixels.append(pix)
except:
pass
这个脚本遍历目录,并记录所有的图像数据。请注意,所有图像都需要旋转 90 度,因为这将允许 getdata 函数按时间顺序而不是间距顺序访问数据。
def split_sequences(sequence, n_steps):
X, y = list(), list()
for i in range(len(sequence)):
end_ix = i + n_steps
if end_ix > len(sequence)-1:
break
seq_x, seq_y = sequence[i:end_ix-1], sequence[end_ix]
X.append(seq_x)
y.append(seq_y)
return np.array(X), np.array(y)X = []
y = []
for i in range(len(pixels)):
mini_x,mini_y = split_sequences(pixels[i],10)
X.append(mini_x)
y.append(mini_y)
这个脚本从时间序列数据中构造 X 和 y 列表。使用变量 mini_x,使得 X 列表由 9 个音符的单独集合组成,y 列表由映射到每个 9 个音符的集合的单独音符组成。
X = np.array(X)
y = np.array(y)
当数据被输入到模型中时,将 X 和 y 列表转换成 NumPy 数组将不会出现错误。
X = X.reshape(len(X),1,9,106)
y = y.reshape((y.shape[0]*y.shape[1],y.shape[2]))
这个脚本重塑了 X 和 y 数组,使其适合 ConvLSTM。y 值将是 106 个 1 和 0 的值。1 表示将在该音高播放一个音符,而 0 表示在该时间步长内不会播放该音高的任何音符。
构建 ConvLSTM:
from keras.models import Sequential
from keras.layers import Dense
from keras.layers import Flatten
from keras.layers import Dropout,BatchNormalization
from keras.layers import LSTM,TimeDistributed
from keras.layers.convolutional import Conv1D,MaxPooling1Dmodel = Sequential()
model.add(TimeDistributed(Conv1D(filters=128, kernel_size=1, activation='relu'), input_shape=(None, 9, 106)))
model.add(TimeDistributed(MaxPooling1D(pool_size=2, strides=None)))
model.add(TimeDistributed(Conv1D(filters=128, kernel_size=1, activation='relu')))
model.add(TimeDistributed(MaxPooling1D(pool_size=2, strides=None)))
model.add(TimeDistributed(Conv1D(filters=128, kernel_size=1, activation='relu')))
model.add(TimeDistributed(MaxPooling1D(pool_size=2, strides=None)))
model.add(TimeDistributed(Conv1D(filters=128, kernel_size=1, activation='relu')))
model.add(TimeDistributed(Flatten()))
model.add(LSTM(128,return_sequences = True))
model.add(LSTM(64))
model.add(BatchNormalization())
model.add(Dense(106,activation = 'sigmoid'))
model.compile(optimizer='adam', loss='mse')
这是使用的完整模型架构。我发现这种型号非常通用。这个模型和我根据历史数据预测股票价格的模型是一样的。主要区别在于,最后一层使用具有 106 个节点的 sigmoid 函数,因为每个时间步长必须被描述为 106 个音符,以及它们是否将被播放。
model.summary()
当调用这个函数来查看模型架构时,我们得到这样的结果:
Model: "sequential_4"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
time_distributed_25 (TimeDis (None, None, 9, 128) 13696
_________________________________________________________________
time_distributed_26 (TimeDis (None, None, 4, 128) 0
_________________________________________________________________
time_distributed_27 (TimeDis (None, None, 4, 128) 16512
_________________________________________________________________
time_distributed_28 (TimeDis (None, None, 2, 128) 0
_________________________________________________________________
time_distributed_29 (TimeDis (None, None, 2, 128) 16512
_________________________________________________________________
time_distributed_30 (TimeDis (None, None, 1, 128) 0
_________________________________________________________________
time_distributed_31 (TimeDis (None, None, 1, 128) 16512
_________________________________________________________________
time_distributed_32 (TimeDis (None, None, 128) 0
_________________________________________________________________
lstm_6 (LSTM) (None, None, 128) 131584
_________________________________________________________________
lstm_7 (LSTM) (None, 64) 49408
_________________________________________________________________
batch_normalization_2 (Batch (None, 64) 256
_________________________________________________________________
dense_2 (Dense) (None, 106) 6890
=================================================================
Total params: 251,370
Trainable params: 251,242
Non-trainable params: 128
_________________________________________________________________
您可以摆弄模型中的超参数,看看它如何影响模型的结果。
model.fit(X,y,epochs = 100)
此脚本为 100 个时期训练模型。注意,不需要验证数据,因为模型实际上不需要 100%准确地预测下一个音符。它只是应该从源音乐中学习一种模式,以便创建一个似乎合理的下一个音符。
听证结果:
song_length = 106
data = X[np.random.randint(len(X))][0]
song = []
for i in range(song_length):
pred = model.predict(data.reshape(1,1,9,106))[0]
notes = (pred.astype(np.uint8)).reshape(106)
print(notes)
song.append(notes)
data = list(data)
data.append(notes)
data.pop(0)
data = np.array(data)
这个函数允许模型根据自己的音乐生成自己的音乐。它是这样工作的:
为了启动这一过程,模型需要从数据集中随机抽取一个实例来进行预测。数据列表的功能就像一个队列:每次做出一个新的预测,它都会被添加到列表的末尾。为了使输入数据的形状在每次迭代中都相同,有必要删除第一个实例。通过收集预测,它显示生成的片段完全是计算机生成的,不包含来自原始实例的任何信息。
new_image = Image.fromarray(np.array(song)).rotate(-90)
new_image.save('composition.png')
之后,我们会将图像向后旋转 90 度,以便 image2midi 功能可以正常工作。
image2midi('composition.png')
然后我们将图像转换成 midi 文件,我们可以使用下面的代码来听它(至少在 colab 笔记本中是这样的):
!apt install fluidsynth
!cp /usr/share/sounds/sf2/FluidR3_GM.sf2 ./font.sf2
!fluidsynth -ni font.sf2 composition.mid -F output.wav -r 44100
from IPython.display import Audio
Audio('output.wav')
让我们听听最初的结果:

没什么。
没有结果。
人为改变结果:
检查结果时,模型无法得出大于 0.6 的值。当 NumPy 向下舍入任何低于 0.6 的值时,没有实际的音符被演奏。我到处寻找改变模型架构的方法,但是网上什么都没有。
我想到的解决方案相当不正统:手动改变结果。
网络的问题在于预测值太小。如果我们给预测加上一定的值,它将能够显示实际的音符。
这怎么能不干扰模型的预测呢?这是因为我们可以将模型的预测可视化,而不是播放或不播放,而是这个音符与当前时间步长的吻合程度。这一点得到了以下事实的支持:该模型是用 MSE 损失函数训练的:预测值和实际值之间的差异很重要。由于保留了模型预测的初始值,模型的预测被简单地放大了。
下面是改进的生成函数:
song_length = 106
data = X[np.random.randint(len(X))][0]
song = []
vanish_proof = 0.65
vanish_inc = 1.001
for i in range(song_length):
pred = model.predict(data.reshape(1,1,9,106))[0]+vanish_proof
vanish_proof *= vanish_inc
notes = (pred.astype(np.uint8)).reshape(106)
print(notes)
song.append(notes)
data = list(data)
data.append(notes)
data.pop(0)
data = np.array(data)
如您所见,vanish_proof 和 vanish_inc 有两个新变量。Vanish_proof 是添加到所有预测中的值,vanish_inc 是 vanish_proof 增加的速率。这是必要的,因为网络的每个新预测将基于过去的预测。如果预测很小,这种效应会向前传播,使歌曲慢慢淡出。
这产生了更好的结果,就像本文开头显示的那样。这是我喜欢的另一段摘录:
结论:
我认为这篇文章最有见地的部分是人工改变结果,直接引出褪色的结果。我还没有真正在其他资源中看到过这种方法,我也不确定这种方法有多好或多有效。您可以尝试使用 vanish_proof 和 vanish_inc 参数。太高了,曲子被放错的音符打断了。
我的链接:
如果你想看更多我的内容,点击这个 链接 。
基于深度学习的音乐流派检测
原文:https://towardsdatascience.com/music-genre-detection-with-deep-learning-cf89e4cb2ecc?source=collection_archive---------6-----------------------
TensorFlow 模型如何用几行代码对音频文件进行分类。
无论你的音乐知识水平如何,都很难描述一种音乐类型是什么。为什么爵士乐听起来像爵士乐?你如何区分乡村音乐和迪斯科音乐?由于体裁没有一个系统的定义,不可能用**【if/else】这样的语句对体裁进行程序化的分类。这就是深度学习发挥作用的时候了。我们将会看到每一种音乐类型都有自己特定类型的声学特征,我们可以从中学习。让我们开始吧!

照片由 Mohammad Metri 在 Unsplash 上拍摄
介绍
这个项目的目标是对音频文件进行分类(在“”中)。wav"* 格式)分成 10 种音乐流派:布鲁斯、古典、乡村、迪斯科、hiphop、爵士、金属、流行、雷鬼、摇滚。为此,我们将使用 TensorFlow2/Keras 作为我们的深度学习框架,使用 Librosa 作为我们的音频预处理库。Librosa 是一个用于音乐和音频分析的 Python 包,它提供了创建音乐信息检索(M. I. R .)系统所必需的构件。*
为了训练我们的模型,我们将使用 GTZAN 数据集,该数据集由 10 个流派的集合组成,每个流派有 100 个音频文件,长度都为 30 秒。数据集在这里可用。
预处理、训练和推理步骤都是在 Jupyter 笔记本电脑上进行的,该笔记本电脑具有 Conda-Python3 环境,具有模块化块和功能,以实现更好的清晰度和可移植性。请在这篇文章的结论部分找到我的 GitHub 的链接。
声音和音频信号处理
一旦采样,声波就以一系列浮点值的形式驻留在你的硬盘上。其大小取决于采样率(通常为 44100Hz 或 22050Hz)和录制持续时间。这些时间序列可以很容易地加载到 Numpy 数组中进行处理。让我们使用 Librosa 可视化来自 GTZAN 数据集的一个音频文件:
*song, sr = librosa.load('GTZAN_Dataset/genres_original/rock/rock.00000.wav')librosa.display.waveshow(song)*

摇滚歌曲的声音振幅随时间(30 秒)变化的表示
通常使用短时傅立叶变换* (STFT)来更好地理解音频信号的定性行为。这是一个数学工具,允许我们分析频谱随时间的变化。以下是方法。*
首先,我们定义一个 frame_size (通常是一批 2048 个样本)。在每一帧上,通过应用离散傅立叶变换* (DFT)来计算(n 个频率仓的)频率向量。频率向量是我们的音频信号在频域中的即时表示。你可以把它看作是在一个给定的时间(例如一个给定的帧)所有频率区间上的能量分布的声音描述。*
第二,随着音乐播放,帧改变(下一帧与前一帧相距一个 hop_length )并且能量分布也随之改变。因此,我们通过对所有帧连续应用 DFT 来获得 STFT(或频谱图)的视觉表示,并在热图上表示它们,如下所示:
*def plot_spectrogram(Y, sr, hop_length, y_axis="linear"):
plt.figure(figsize=(10, 5))
librosa.display.specshow(Y, sr=sr, hop_length=1024, x_axis="time", y_axis=y_axis)
plt.colorbar(format="%+2.f")Y_log = librosa.power_to_db(np.abs(librosa.stft(song, hop_length=1024))**2)
plot_spectrogram(Y_log, sr, 1024)*

所有频率仓随时间(30 秒)的对数振幅变化
上图是我们的摇滚歌曲。这张热图显示了每个频段是如何随时间演变的:红色越多,能量越大,声音越大。请注意较高频率(约 10kHz 频段)如何在 0 至 30 秒内保持蓝色,因为这些频段的信号幅度较小。此外,均匀间隔的垂直红线出现在整个文件,这显然是所有的乐器跟着节拍。
想过摇滚到底是什么吗?
现在至少你知道它看起来像什么了!
但是我们的光谱图看起来仍然有些模糊,我们距离使它适合深度学习模型的摄取还有几步调整。你可能知道,我们人类听到的声音不是线性的:A1 (55Hz)和 A2(110Hz)之间的感知音高差异与 A4 (440Hz)和 A5 (880Hz)相同。这两个音程都是八度音阶,但是第一个八度音阶相差 55Hz,而另一个八度音阶相差 440Hz。梅尔标度定义了相对于感知频率均匀分布的频带。 Mel 滤波器组经过计算,对较低频率的区分度更高,对较高频率的区分度较低,就像人耳一样。通过执行如下加权鉴别来平滑谐波结构:

三角形 Mel 滤波器组
Mel 缩放后,最后一步是应用离散余弦变换*,即仅生成实值系数。作为最佳实践,我们通常保留前 13 个系数( n_mfcc= 13),称为*梅尔频率倒谱系数(mfcc)。它们描述了光谱形状的最简单方面,而高阶系数对训练来说不太重要,因为它们往往描述更多类似噪声的信息。
MFCC 是更大光谱结构的有用描述符,易于阅读并可由深度学习模型“解释”。让我们摘录我们摇滚歌曲中的 MFCC:
*mfcc_song = librosa.feature.mfcc(song, n_mfcc=13, sr=sr)plt.figure(figsize=(15, 8))
librosa.display.specshow(mfcc_song, x_axis="time", sr=sr)
plt.colorbar(format="%+2.f")
plt.show()*

13 乐队 MFCC 为一首 30 秒的摇滚歌曲
现在看起来很整洁!那么,我们这里到底有什么? mfcc_song 的形状返回(13,1293),例如对应于每个系数的 13 行和 1293 帧(每个帧 2048 个样本)持续 30 秒。这是一首摇滚歌曲的声音特征。为了学习摇滚流派,GTZAN 数据集还有 99 首歌曲要训练。让我们看看如何做到这一点。
准备训练、验证和测试数据集
浏览完所有歌曲后,我注意到它们的长度可能会在 30 秒左右略有不同。为了确保我们处理的文件具有相同的持续时间,我们将在 29 秒内裁剪所有文件。我们还设置采样率 sr = 22050Hz,并计算每个切片的样本数。
**# Sampling rate.* sr = 22050*# Let’s make sure all files have the same amount of samples, pick a duration right under 30 seconds.* TOTAL_SAMPLES = 29 * sr*# The dataset contains 999 files (1000–1 defective). Lets make it bigger.
# X amount of slices => X times more training examples.* NUM_SLICES = 10
SAMPLES_PER_SLICE = int(TOTAL_SAMPLES / NUM_SLICES)*
此外,我们需要生成更多的训练样本,因为 1000 首歌曲并不多。将每首歌曲分成 10 个子部分似乎是一个合理的大小,以便从它们的声学特征中保留足够的信息。
我们的数据集由 10 个文件夹及其流派名称组成,每个文件夹包含 100 首歌曲。所以标注过程很简单:文件夹名→标签。我们现在可以将这种分类公式化为监督问题,因为每首歌曲都有一个分配给它的流派标签。
所有 MFCCs 和相关标签将存储在单独的 json 文件中,并在 for 循环中处理,如下所示:
**# Let's browse each file, slice it and generate the 13 band mfcc for each slice.*
**for** i, (dirpath, dirnames, filenames) **in** enumerate(os.walk(source_path)):
**for** file **in** filenames:
song, sr = librosa.load(os.path.join(dirpath, file), duration=29)
**for** s **in** range(NUM_SLICES):
start_sample = SAMPLES_PER_SLICE * s
end_sample = start_sample + SAMPLES_PER_SLICE
mfcc = librosa.feature.mfcc(y=song[start_sample:end_sample], sr=sr, n_mfcc=13)
mfcc = mfcc.T
mydict["labels"].append(i-1)
mydict["mfcc"].append(mfcc.tolist())
*# Let's write the dictionary in a json file.*
**with** open(json_path, 'w') **as** f:
json.dump(mydict, f)
f.close()*
最后,通过Scikit-Learn train _ test _ split()以 20%的比率生成验证和测试数据集。
模型架构、训练和预测
我们将使用一个 TensorFlow2 框架来设计我们的卷积神经网络* (CNN),具有 3 层卷积和一个最终全连接层,具有 *softmax 激活*,具有 10 个输出(用于 10 个流派)。整体架构如下:*

使用 batch_size =32 训练超过 30 个时期需要在英特尔双核 i5* CPU 上花费几分钟,并在验证数据集上收敛到 77%的准确率。让我们用 Matplotlib 绘制度量曲线:*

结论
我在相同的数据上测试了其他模型架构,结果如下:
- 多层感知器→ 76%训练 acc 和 59%阀 acc
- 递归神经网络(LSTM 型)→ 90%训练 acc 和 76%阀 acc
本文中介绍的 CNN 架构最终获得了前所未有的最佳数据性能(77%)。为了减少方差,可以应用各种技术来微调模型,例如正则化、数据扩充、更多的漏失层等。让我知道你是否能拿出一个更好的分数,以及你实现它的方法。
请随意查看我的 GitHub 库上的模型基准。我的 Kaggle 上也提交了 CNN 的模型。感谢您的阅读!
使用卷积神经网络的音乐流派识别-第二部分
原文:https://towardsdatascience.com/music-genre-recognition-using-convolutional-neural-networks-part-2-f1cd2d64e983?source=collection_archive---------31-----------------------
了解如何使用 Streamlit 构建音乐流派识别应用程序,并将其部署在 Amazon EC2 上

照片由奥斯丁·尼尔在 Unsplash 拍摄
项目中任何不直接有助于尽快将有价值的软件交付给用户的角色都应该仔细考虑。—斯坦·英格·莫里斯巴克
你好,在本文的第一部分中我们开发了一个卷积神经网络来识别音乐类型。在这一部分中,我们将使用令人惊叹的 Streamlit 库开发一个 web 应用程序。
学习深度学习或者机器学习的人大多掌握了如何建立模型,训练模型却不知道如何部署模型(包括这个项目 xP 之前的我)。部署一个模型和训练它一样重要,通过部署你的模型,人们可以利用它。向你的朋友展示你为你的模型开发的应用程序,而不是在 jupyter 笔记本上运行你的模型,这不是很酷吗?。
我将使用 Streamlit 库来构建应用程序,然后将其部署在 Amazon EC2 实例上。
构建应用程序
首先,如果您没有安装 Streamlit,请在终端中使用以下命令进行安装
pip install streamlit
Streamlit 是一个非常易于使用的框架,它是专门为非 web 开发人员设计的。我只是要建立一个基本的 web 应用程序,你可以通过添加自己的风格和创造力来进一步改进它。
首先,创建一个 python 脚本,并在其中编写应用程序的所有代码。
import streamlit as stst.write("Music Genre Recognition App")
st.write("This is a Web App to predict genre of music")file = st.sidebar.file_uploader("Please Upload Mp3 Audio File Here or Use Demo Of App Below using Preloaded Music",type=["mp3"])
现在,要查看它在网页上的实际效果,你需要运行这个脚本。要运行,请在终端中键入以下命令。
streamlit run app.py

来源:作者图片
所以,这是一个非常基本的网络应用程序,它有一个文件上传程序来上传音乐的 mp3 文件。现在,我们需要编写代码从 file_uploader 获取文件输入,对其进行预处理,并使用我们的 CNN 模型给出预测。
首先,让我们导入所有我们需要的库
from PIL import Imageimport librosa
import numpy as np
import librosa.display
from pydub import AudioSegment
import matplotlib.cm as cm from matplotlib.colors import Normalize
现在,我们将创建一个将 mp3 音频文件转换成的函数。wav 文件,因为 Librosa 只支持。wav 文件。
def convert_mp3_to_wav(music_file):
sound = AudioSegment.from_mp3(music_file) sound.export("music_file.wav",format="wav")
如果你还记得的话,我们用 3 秒钟的音频片段训练我们的模型来识别流派。记住这一点,我们需要写一个函数,从我们的音乐中提取 3 秒钟的音频。
def extract(wav_file,t1,t2):
wav = AudioSegment.from_wav(wav_file)
wav = wav[1000*t1:1000*t2]
wav.export("extracted.wav",format='wav')
上述函数将在时间 t1 和 t2 之间分割出音频。
现在,最重要的部分是创建提取音频的 mel 声谱图。这个 mel 光谱图将被馈送到模型中进行预测。
def create_melspectrogram(wav_file):
y,sr = librosa.load(wav_file,duration=3)
mels = librosa.feature.melspectrogram(y=y,sr=sr)
fig = plt.Figure() canvas = FigureCanvas(fig)
p = plt.imshow(librosa.power_to_db(mels,ref=np.max))
plt.savefig('melspectrogram.png')
我们将使用上面生成的 mel 声谱图创建一个函数来预测音乐的流派。
def predict(image_data,model):
image = img_to_array(image_data)
image = np.reshape(image,(1,288,432,4))
prediction = model.predict(image/255)
prediction = prediction.reshape((9,))
class_label = np.argmax(prediction)
return class_label,predictionclass_labels = ['blues', 'classical', 'country', 'disco', 'hiphop', 'metal', 'pop', 'reggae', 'rock']
使用您在 GTZAN 数据集上训练的模型在此进行预测。如果你不知道如何在 Keras 中保存和加载模型,参考 如何保存和加载你的 Keras 深度学习模型(machinelearningmastery.com)。
class_label 是具有最高概率的标签,并且预测捕获所有类别(即流派)上的概率分布。
class_labels 是一个将类型映射到数字的列表(即列表的索引)
现在,我们将合并我们编写的所有函数,以在我们的 web 应用程序上显示最终输出。
if file is None:
st.text("Please upload an mp3 file")
else:
convert_mp3_to_wav(file)
extract_relevant("music_file.wav",40,50)
create_melspectrogram("extracted.wav")
image_data = load_img('melspectrogram.png',color_mode='rgba',target_size=(288,432))
class_label,prediction = predict(image_data,model)
st.write("## The Genre of Song is "+class_labels[class_label])
现在我们差不多完成了,我们只需要看看这个简单的网页在浏览器中的样子。请注意,您还可以使用 streamlit 向您的 web 应用程序添加其他功能,如侧栏、单选按钮、背景图像,您可以自己探索这些功能。

来源;作者图片
这是我们的应用程序的外观,它非常粗糙,但你可以自己探索如何添加更多的功能,这真的很容易。
现在我们已经构建了我们的应用程序,我们将使用 Amazon EC2 实例来部署它
在 Amazon EC2 上部署
- 在 Amazon Web Services 上创建一个免费帐户,填写所有详细信息,如姓名、地址、邮政编码等。您还需要提供您的借记卡信息,但他们不会收取任何费用。
- 创建帐户后,浏览到 AWS 管理控制台。

来源:作者图片
选择,用 EC2 启动虚拟机,之后选择亚马逊 Linux 2 AMI (HVM),SSD 卷类型
3.选择实例类型 t2.micro,现在只需转到下一步,直到配置安全组页面,这里我们需要创建一个自定义 TCP 端口,因为 Streamlit 在端口 8501 上工作。选择添加规则并在端口范围中写入 8501,在源中的任意位置选择**。**
4.完成后,它将提示您创建一个密钥对,因此选择创建一个新的密钥对,并为其命名,下载密钥对,安全地存储它,因为您将需要它来访问您的虚拟机。
5.现在,在您存储密钥对(*)的目录中打开一个终端窗口。pem 分机)。在终端中键入以下内容。
chmod 400 name.prem
这将为提供必要的权限。pem 文件
6.现在,ssh 进入您创建的 EC2 实例
ssh -i your_key_pair_name.pem ec2-user@your public dns address
您可以在 EC2 控制台中找到您的公共 DNS 地址。您已经成功启动了您的 Amazon EC2 实例。
7.安装我们的应用程序和模型所需的所有软件包。
sudo python3 -m pip install librosa llvmlite==0.33.0 numba==0.49.1
sudo yum install libsndfile
这将安装 librosa 和 libsndfile。现在,我们将需要安装 ffmpeg,它的安装有点复杂,所以参考这篇文章如何在运行 Amazon Linux 的 EC2 上安装 FFMPEG?| by Vivek Maskara | Medium 用于安装 ffmpeg。
安装 ffmpeg 后,要将其导出到我们的路径中,请使用
export PATH=$PATH:<path where you installed fffmpeg>
现在,安装 tensorflow
sudo python3 -m pip install --no-cache-dir tensorflow==2.2.0 grpcio==1.24
我们已经安装了所有需要的软件包。
8.创建一个 GitHub 存储库,将所有需要的文件添加到 GitHub 存储库中,包括模型权重、模型 json、应用程序的 python 脚本。使用下面的代码将这个存储库克隆到 amazon EC2 实例中。
git clone [https://github.com/username/repository_name.git](https://github.com/KunalVaidya99/Music-Genre-Classification.git)
9.将目录切换到您刚刚克隆的存储库,并运行包含 app 代码的 python 脚本
streamlit run script_name.py --server.port 8501
使用外部 URL 访问您的网络应用程序,瞧!您刚刚使用 Amazon EC2 实例部署了一个应用程序。
10.您将观察到,一旦您关闭终端,web 应用程序就变得不可访问,因此,即使在您关闭终端后,为了保持它的运行,我们需要使用 tmux。
tmux new -s StreamSession
这将创建一个 tmux 会话,并在该会话中输入第 9 点中提到的命令。完成此操作后,先按 Ctrl +b,然后按 d 从 tmux 会话中分离。现在,您可以随时访问 web 应用程序,甚至在关闭“终端”后也可以访问:)。
您刚刚部署了您的应用程序,现在任何人都可以访问和使用它,当然,您也可以向您的朋友展示它😃。
结论
这是使用卷积神经网络进行音乐流派识别的最后一部分。这是一个关于如何开发模型、训练模型、围绕模型构建应用程序然后部署它的端到端教程。希望对所有深度学习爱好者有所帮助。
以下是我开发的 Web 应用程序的截图。

来源:作者图片

作者图片
可以在 音乐-流派-识别-App Streamlit(ec2–3–134–82–239.us-east-2.compute.amazonaws.com)访问 Web App
请让我知道你是否觉得它有帮助。我总是喜欢诚实的反馈!
在这里找到链接到本文的 GitHub 知识库
kunalvaida99/Music-Genre-Classification:音乐流派识别 App,准确率 89%。(github.com)
参考
- 如何在 AWS Ec2 — JCharisTech 上部署 Streamlit 应用
- 如何在运行亚马逊 Linux 的 EC2 上安装 FFMPEG?|作者 Vivek Maskara | Medium
- API 参考— Streamlit 0.76.0 文档
Python 中的音乐
原文:https://towardsdatascience.com/music-in-python-2f054deb41f4?source=collection_archive---------2-----------------------
实践教程
音乐术语中的信号处理与乐理可视化
最近发现了这本书《T2 音乐》,作者是 Eli Maor,受到与一位对学习乐器感兴趣的朋友的对话的启发,我觉得用 Python 可视化一些数学和音乐概念会很有趣。
我绝不是一个有天赋的音乐家,也不是信号处理方面的专家,但我希望这篇帖子能帮助你理解一些使用音乐的数学概念,从不同的角度更多地欣赏音乐。上面提到的书是一本有趣的读物(首先,我不知道欧拉是由约翰·伯努利辅导的),这篇文章旨在提供一些使用 Python 的视觉和听觉帮助。
什么是声音?
如何发出声音?你可以拍手、敲门或唱歌来发出声音,所有这些动作都会引起某种形式的振动。振动通过介质(空气、水等)传播。)像浪潮一样涌入我们的耳朵。然而,我们不能听到所有的振动,因为有些频率不在我们的听觉范围内。我们认为高频率意味着高音调,不同的音符有不同的频率。下图显示了中音 C 和它正上方的 D 的声波。

正如你所看到的,D 波完成一个周期需要的时间更少,因此频率更高。
用 Python 发出声音
为了让声音有特定的音高,我们需要知道频率。维基百科有一个很棒的表格,将钢琴上的一个键映射到一个频率。一般来说,如果我们使用惯例,即中 C 以上的 A 具有 440Hz 的频率(备受争议的音乐会音高),我们可以使用以下公式导出任何音符:

其中 n 是键的秩(A4 是第 49 个键)。
这篇文章有关于如何计算音符频率和制造声波的示例代码。这里的代码非常相似,只是扩展到了 88 个键,而不是一个八度音阶:
上面的函数返回一个字典,该字典将一个音符名称映射到相应的赫兹频率。现在我们要做一波中 C:
在这里,持续时间以秒为单位,采样率决定声音的质量,振幅决定音量。44.1KHz(每秒 44,100 个样本)的采样速率对于消费音频来说非常常见。
使用上面的两个函数和 Scipy 的 wavfile 模块,我们可以创建一个 middle C 的. wav 文件。
这是输出音频。
制造逼真的声音
至少可以说,上面的音频很无聊,没有一个头脑正常的人会认为它是音乐。它缺乏许多我们认为乐器声音的特征,而且自始至终都太一致了。首先让我们将它与在真实钢琴上弹奏的中间 C 音进行比较。
同样的音符在钢琴上听起来和在小提琴上听起来不一样,因为音色(音质或特性)。当我们按下钢琴上的一个键时,它不仅仅产生一个声音(或一个波),而是产生许多其他我们称之为泛音(谐波)的声音。不同的乐器产生不同的泛音,因此产生的音色可以让我们在演奏同一个音符时区分不同的乐器。
再次使用 Scipy,我们将加载钢琴音频并使用 Matplotlib 绘制声波。

这看起来一点也不像第一部分中的纯正弦波,尽管它们应该是同一个音符!它不再平滑,大小也变了。
由于泛音的存在,波并不“平滑”。上面的声波实际上是频率是中间 C 的倍数(包括 1,对于音符本身)的波的组合。在这种情况下,中间 C 是我们的基本音符。那些泛音是什么音?结果是高于(C5)一个八度的 C 的频率是中间 C 的两倍,所以这是我们的候选之一。事实上,所有 C 的频率都是彼此的倍数。但是当我们按下中间的 C 键时,听起来并不像是同时按下所有的 C 键。这是因为泛音比基频要弱得多。要模仿钢琴的声音,我们需要知道如何将泛音应用到基础音上。
信号处理中从一个输入信号(我们的声波是信号的离散版本)中分离不同频率的信号的一种常见技术是傅立叶变换。如果你不熟悉的话,这里有一个由 3blue1brown 制作的关于傅立叶变换的美丽的可视化视频。现在,就把它想象成一个让我们通过频率来整理声波的操作。我们将对我们的钢琴音频应用快速傅立叶变换(FFT)并绘制声谱图。

频谱图显示最高峰(我们最突出的信号)略高于 250 赫兹。这是有意义的,因为中间 C 的频率约为 261.65Hz。下一个刚好高于 500Hz (C5 的频率约为 523.25Hz),依此类推。它们也是均匀分布的,这与我之前的主张相符,即泛音频率是基波的倍数。
下面这段代码计算了钢琴样本中每个泛音和基频之间的幅度比,这样我们就可以将其应用到纯正弦波中。
现在,我们将这些比率应用于我们的基本音符及其泛音。
我们越来越接近了,但声音始终太一致(音量保持不变)。当我们按下一个真正的钢琴键时,它开始很轻,然后很快变大,声音随着时间的推移而减弱。描述声音如何变化的一个模型是 ADSR(起音、衰减、延音和释音)。本质上,它把声音描述为经历四个阶段:倾斜的初始阶段,然后下降到一个较低的水平,在那里保持一段时间,然后减小到零。ADSR 对一些工具的建模比其他工具更好,但对我们来说应该足够了。下面是我用指数权重实现的 ADSR,让声音更加平滑和真实。当然,如果你愿意,你可以使用简单的线性砝码。
同一件乐器的声音在不同的房间会有所不同,因此 ADSR 阶段也可能有所不同。此外,如果我们知道声音如何在音乐厅中反弹,理论上我们可以建模并将其应用到我们的剪辑中,使其听起来像我们在音乐厅中演奏钢琴。更正式地说,当我们播放声音时,在给定位置的特定声学特征被称为脉冲响应(IRF),如果我们将 IRF 和输入声波卷积在一起,我们可以得到输出声音,就好像我们在所述位置播放一样。
把它放在一起
现在,我们将泛音和 ADSR 权重应用于上面生成的纯正弦波。
请注意,这里使用的泛音和振幅的特征是我所拥有的录音所特有的(一个小房间里的旧斯坦威)。摆弄泛音和 ADSR 参数,看看什么效果最好。最终的音频离真实的乐器还很远,但比我们开始的要好很多。
当然,作为奖励,还有用 Python 写的音乐“hello world,Twinkle Twinkle Little Star”。
结论
我掩饰了很多重要的,有时是复杂的概念,因为这是一个轻松的阅读。我无意用复杂的公式和代码来剥夺音乐的乐趣或吓跑音乐家,因为我只是试图用一种更系统的思维方式(数学)来描述我们所听到的。通过使用 Numpy 和 Scipy 的几行代码,即使您没有训练有素的音乐家耳朵和完美的音高,也可以轻松地分析声音。
你可以在我的 GitHub 上看到所有代码。
更多乐理在第二部。
Python 中的音乐第 2 部分
原文:https://towardsdatascience.com/music-in-python-part-2-4f115be3c781?source=collection_archive---------22-----------------------
实践教程
和谐与不和谐的形象化

作者图片
为了纪念世界钢琴日(一年中的第 88 天,因为一架标准钢琴上有 88 个键),这是对我之前关于 Python 中音频信号处理和分析的帖子的简短跟进。这更像是音乐理论,强调视觉和听觉,而不是编码。
在这篇文章中,我将探讨为什么我们会把某些声音与甜蜜和轻松联系在一起,而另一些声音会让我们感到紧张。我还会将上一篇文章中的延音踏板添加到我的合成钢琴上,让它听起来更真实。
和谐与不和谐
在上一篇文章中,我谈到了泛音的频率是基音的整数倍。例如,音乐会 A 的频率为 440 赫兹,第二泛音的频率为 880 赫兹(比 A 高一个八度)。大多数人可能会觉得两个 A 同时演奏的声音很悦耳。然而,如果我们只是同时按下任意两个键,那就没有必要了。原来这和我们结合的音符的频率有关。
完全一致
两个 A 或任何相隔一个八度的音符(简称“八度”)是音乐中完美和谐的一个例子。我们可以用 Matplotlib 看看 C4(中间 C)和 C5 波的组合是什么样子。

您可以看到,C4 完成一个周期(从峰值到峰值)所需的时间(几乎)是 C5 的两倍。这也使得两个音符的波峰彼此非常接近,在相同的地方形成一个新的波峰。你可以从输出音频中分辨出这是一个 C,尽管它听起来比中间的 C 高一点。
不完全和谐
当我们把一些其他的音符加在一起时,声波可能不会像八度音程那样整齐排列。如果我们把分数倍数的音符加在一起,而不是整数倍数,会怎么样?
下图显示了 C4(261.63 赫兹)和 E4(329.62 赫兹)的组合,其比率约为 4:5。换句话说,当 C4 完成 4 个周期时,E4 在同样的时间内完成了 5 个周期。在音乐中,这种特殊的组合是大三度的一个例子,而大三度又有不完美的和声。我们可以看到,峰值并不总是排成一行,但偶尔会排成一行。

输出听起来不再像 C 或 E,但我们仍然发现它是一个令人愉快或明亮的连贯音符。
不一致
如果我们有两个音符,它们的周期似乎不一致,那会怎么样?波峰没有排成一条线,它们最终互相抵消,形成了下面奇怪的波浪。

虽然有些地方 C 和 C 升(c4,或降 D)几乎排成一行,但这比上面的大三度例子要花更长的时间。这里,C (261.63Hz)和升 C(277.18Hz)之间的比率不像大三度那样接近一个很好的分数。我们经常将由此产生的声音与紧张和刺耳联系在一起,因为我们可以听到不同的频率。
延音踏板
在我看来,延音踏板给钢琴增添了魔力。它在手指移动时连接音符,并将声音混合在一起,从而允许连续音符之间的和谐(或不和谐)。
当您按下延音踏板时,它会按住所有弹奏的键,直到您释放它。除了我们在上一篇文章中制作的钢琴,我们将在每个小节的开头“按下”踏板,在结尾“释放”踏板,以便一个小节中的所有音符融合在一起。
我们还将修改获取歌曲数据的函数来使用踏板。
当然,在一个小节开始时必须踩下踏板或者在一个小节中只使用一次踏板并不是规则。我以这种方式编码,这样我们仍然可以得到美妙的回声效果,而不必指定我们需要使用踏板的确切位置。下面是有踏板和没有踏板的 c 大调音阶的比较。
将延音应用到我们的闪烁闪烁示例:
旋律:C-C-G-G-A-A-G .两个 C(G-G 和 A-A 也是)是一致的,C-G 是完美的五度,而 A-G 是大二度;所有这些都是和谐的例子。
结论
音乐中的和谐与不和谐会随着时间、环境和流派而变化。尽管如此,当有效使用时,它们增加了独特的特征并构建了叙事。钢琴上的延音踏板允许音符混合,这放大了它们之间的关系,并让钢琴家引导他们的观众通过作品。谢谢你到目前为止的阅读,希望你喜欢音乐!
所有代码都在 Github 上。
额外示例
摘自久石让的《空中楼阁中的天真*。这里有一些协和的例子(组成和弦的音符,以及旋律中音符的顺序)。*
节选自风之谷的风之谷的安魂曲,再次由美妙的久石让演唱。这里既有协和音也有不协和音,不协和音的使用使这首曲子听起来更神秘。
使用 Google Colab 上的 Spleeter 进行音乐源分离
原文:https://towardsdatascience.com/music-source-separation-with-spleeter-on-google-colab-9161dc72d1e8?source=collection_archive---------20-----------------------

凯利·西克玛在 Unsplash 上的照片
音乐录音可以附带各种器乐曲目,如主唱、钢琴、鼓、贝斯等。每首曲目被称为词干,对大多数人来说,当他们听一首歌时,孤立这些词干是很自然的。例如,如果你听旧时代摇滚,你会听到开头的钢琴梗、鲍勃·西格的主唱梗和鼓梗都在一起,你可以在整首歌中跟随每个梗。然而,不可能听到一个单独的词干而不被许多其他词干扭曲。这就是 Spleeter 发挥作用的地方…
什么是 Spleeter?
Spleeter 是音乐流媒体公司 Deezer 在 2019 年发布的一个源分离库。对于那些不熟悉 Deezer 的人来说,它与 Spotify 非常相似,大多在法国使用。Spleeter 是我们能够获得的最接近提取一首歌曲的单个曲目的工具,它主要被从事音乐信息检索的研究人员使用。Deezer 使用 Spleeter 进行他们自己的研究,他们希望发布一些可以让其他人以他们自己的方式使用的东西。
你能用 Spleeter 做什么?
除了研究,您还可以使用 Spleeter 做其他事情:
- 制作无伴奏合唱/乐器(也包括卡拉 ok)
- 用它来提取音响/乐器来创作你自己喜欢的歌曲版本(当然不是拷贝)
- 使用它来了解一首歌曲是如何从单个词干组合在一起的(源分离使词干听起来不那么失真)
- 播放你喜欢的艺术家的不同曲目;创建混搭
- 如果你演奏钢琴、鼓或低音乐器,你可以提取这些特定的音轨来清晰地听到和理解它们,并演奏/创作类似的东西
要跟随或自己执行这段代码,您可以在 Github 中找到我的 Spleeter Google Colab。我最初试图在 Jupyter 笔记本上执行这个,但出于我还不确定的原因,它没有工作,但它在 Google Colab 上工作得很好。如果您在 Python 应用程序上尝试这种方法,请让我知道它是否适合您,这样我就可以知道我错过了什么!
我的 Google Colab 代码的快速概述
我安装了一个导入的 3 个重要的库:youtube-dl、Spleeter 和 pydub。
- pydub:处理音频文件的模块;具体来说。wav 文件(Spleeter 将每个词干生成一个. wav 文件)
- youtube-dl:用于从 youtube 下载视频和音频文件
- 提取音乐的词干
您还想从 IPython.display 对象中导入“Audio ”,以便为您下载的歌曲创建一个 Audio 对象,并使用 display 函数来显示您的音频。
import spleeter
import youtube_dl
import pydub
import IPython.display as ipd
from IPython.display import Audio, display
from IPython.display import HTML
接下来,您必须创建一个变量来存储 YouTube 视频的参数,例如“bestaudio”特性和您希望它具有的输出名称。你可以在这里了解更多关于你可以得到的不同输出和 youtube-dl 库的文档。
ydl_args = {
'format' = 'bestaudio/best'
'outtmpl' = 'filename.mp3'
}
接下来,我使用名为 YouTube EDL 的 youtube_dl 实例来访问这些参数并下载它们。
ydl = youtube_dl.YoutubeDL(ydl_args)ydl.download(['url'])
下一步就是 Spleeter 的工作。更多关于它的文档信息,请点击这里。Spleeter 根据您想要提取的茎数提供了 3 个选项:2 茎、4 茎和 5 茎。
- 2 项=人声和伴奏
- 4 项=人声、鼓、低音和其他
- 5stems =人声、鼓、贝斯、钢琴和其他
我在我的项目中使用了 4 个词干,因为我知道我选择的歌曲主要是人声和鼓声。
!spleeter separate -p spleeter:4stems -o output/ entertainment.mp3
“-o”代表输出文件名,“-p”代表提供模型设置(4 项)。
当您执行 Spleeter 函数时,每个词干将被写成一个. wav 文件。因为我个人想处理. mp3 文件,所以我导入了一个名为“AudioSegment”的 pydub 实例,它的目的是读取。wav 音频文件。之后,我们可以将该文件导出为. mp3 文件。
from pydub import AudioSegment
sound = AudioSegment.from_wav("output/filename/vocals.wav")
sound.export("output/filename/vocals.mp3", format="mp3")
如果你对每一个干(人声、低音、鼓等)重复上述过程。),剩下要做的最后一件事就是使用 IPython.display(简称为 ipd)来显示你最终的词干。它应该是这样的:

作者图片
就这些了!你想怎么听和玩这些文件取决于你。我把我的词干上传到 Audacity 应用程序,因为它单独显示每个词干,它允许你打开或关闭某些词干,或者一起播放它们。如果您也想使用 Audacity,下面是它的样子:

作者图片
结论
我希望这为 Spleeter 做什么以及如何使用它提供了一个清晰的图像。如果你要发布任何你在 Spleeter 上使用过的东西(音乐/音频),确保得到所有者的许可!我非常喜欢这个项目,但是,我注意到 Spleeter 并没有被真正的讨论过,所以我这篇文章的部分意图是向每个人展示这是易于访问的,并传播这个信息。这也可以作为完美的数据科学和人工智能入门,供热爱错综复杂的音乐的初学者使用。
MusicAE:一个音乐家友好的机器学习工具
如何让音乐人在数字时代使用机器学习工具的概述和示例
由西奥·雅克努、塞缪尔·马尔茨和扎卡里·弗里德曼撰写
目前有哪些音乐 ML 工具?
在过去的几年中,已经发布了许多工具来将机器学习(ML)引入音乐世界。最值得注意的是,谷歌已经使用 Magenta 发布了几十个有趣的应用程序和原型,“这是一个开源研究项目,探索机器学习作为一种工具在创作过程中的作用。”在无数可用的 ML 工具中,有些工具可以增强艺术家的作品,但许多工具却试图完全取代艺术家。
Ai-Duet 让你弹奏虚拟钢琴,并自动与你一起二重奏。庆祝约翰·塞巴斯蒂安·巴赫是一个谷歌涂鸦(取代搜索页面上的谷歌标题的应用和动画),它允许你创作一首旋律,并产生巴赫风格的四声部对位法,同时不可否认地打破了巴赫的一些基本规则,他广泛地写了这些规则。更傻的是, Blob Opera 可以让你控制一群 Blob 形状的歌剧歌手,实时输入音高和元音。虽然这样的程序可能非常复杂,并且使用起来很有趣,但是它们的用例或多或少都是新奇的;没有一个严肃的艺术家会通过唱歌或模仿钢琴程序来创作音乐。然而,与此同时,另一种音乐 ML 正在崛起,它扩展了艺术家可用的工具,而不是完全取代它们。

Blob 歌剧歌手对他们的女高音的花腔表演感到惊讶| 谷歌艺术与文化
两个最流行的工具是音调转换和 NSynth 。Tone Transfer 允许音乐家上传在任何乐器(或非乐器)上演奏的简短样本,并输出在四种乐器之一上演奏的样本的重新解释:长笛、萨克斯管、小号或小提琴。令人惊讶的是,它很好地捕捉了原曲的音高和音调变化,但它失败的地方在于界面。该工具仅作为网络应用程序在线提供,它笨拙地要求艺术家一次上传和下载一个简短的样本,无法轻松连接到数字音频工作站(DAWs)等常见程序。另一方面,NSynth 提供了大量关于如何构建物理乐器的开源文档,具有标准接口,如乐器数字接口(MIDI)或 USB。NSynth 的问题是,即使你能组装它,它也只能作为独立的乐器工作,不能作为大多数音乐家已经拥有的合成器或 MIDI 控制器的软件来实现。

NSynth Super 在带有 MIDI 接口| 的 Raspberry Pi 上构建的组装 NSynth 示例
那么,音乐家获得机器学习的理想工具是什么呢?在我们上面展示的许多例子中,人工智能正在取代或试图取代音乐家。在某些情况下,它试图模仿一个作曲家,在其他情况下,一个表演者,但它往往达不到要求。我们认为当 ML 技术被用来增强艺术家的创作体验时,它是最成功的。**此外,该工具必须易于使用,使用直观,并适合音乐家的工作流程。**保证最后一点的最佳方式是在音乐家已经熟悉的平台上构建工具,在这个数字时代,黄金标准是数字音频工作站。如果机器学习方法可以像虚拟工作室技术(VST)一样以标准格式作为插件来实现,音乐家将能够将其无缝集成到他们的正常工作流程中。
音乐简介
宗旨
MusicAE 使用先进的机器学习技术来创建那些难以量化的声音元素的独特表示,并提供一个简单的界面来与它们进行创造性的交互。
它是如何工作的?
MusicAE 是神经网络驱动的合成器和效果工具,它使用自动编码器架构来学习某些声音的“指纹”,并在它们之间平滑地插值。自动编码器通过使用原始数据(如音频信号)作为输入来工作。他们学习使用更少的信息对数据进行编码或压缩,这一过程被称为降维、,然后对称地将数据重组为其原始形式。在解码器的中央,全部数据以尽可能少的数字表示。这被称为潜在空间、,代表输入数据的精华——“指纹”

自动编码器神经网络架构的图形表示|作者 Steven Flores
我们使用包含各种合成器、风琴和其他键盘乐器生成的音调的语料库来训练我们的自动编码器。经过几个时期的训练,自动编码器学会了如何有效地将声音压缩到一个较低维度的潜在空间,然后将其解码成几乎相同的声音。当你绕过编码器,简单地将一个数字向量引入潜在空间时,这个神经网络就变得有创造性了。网络将对潜在向量进行解码,就好像它是从声音中编码出来的一样,并将输出一个简短的音频样本,该样本不同于它所训练的任何声音。这就是音乐工具的用武之地。我们设想 MusicAE 的三种潜在用途:
- **合成器:**这就像上面描述的例子。自动编码器没有音乐输入,用户只需在 GUI 上或使用 MIDI 控制器定义一个潜在向量,解码器网络就会产生声音。对潜在空间的平滑变化对应于输出的平滑变化,为音乐家提供了一种全新的合成独特声音的方式。
- **效果:**在效果模式下,自动编码器使用音频流作为输入,解码器能够对其进行重建。通过引入潜在空间的小变化,声音以各种微妙的方式转变,就像通过失真、均衡器或其他效果一样。
- **混音器:**在这种模式下,两个并行网络同时对两个音频流进行编码。然后,就像推子可以让你在音轨之间平滑过渡一样,两个潜在空间混合在一起,解码为一个。用户不仅可以选择在混音中包含多少每个音轨,还可以控制潜在空间中每个维度的混音,从而产生创造性的混音。

MusicAE |作者提供的三种操作的摘要
与传统合成器不同,传统合成器专门控制音高、波形、起音、衰减、中心频率、谐波、等等,自动编码器学习声音的这些特征,并将其抽象到一个潜在的空间中,以一种无意识地组合它们的方式。在上面描述的三种用途中,艺术家发现了一个新的维度,或者几个维度,是他们习惯使用的声音和工具。不仅能够在两种声音之间淡入淡出,而且能够根据它们潜在的表现形式错综复杂地混合它们,这是目前没有其他工具提供的完全独特的体验。
示范
下面使用原型图形用户界面(GUI)显示了混音和合成器模式的示例。对于混音,水平滑块控制每个轨道组合到潜在空间中进行解码的量,而十个垂直滑块组合轨道的单个潜在尺寸。在这个原型 GUI 中,autoencoder 模型是预先训练好的,可以通过文件名加载,就像混音和效果中的输入音频文件一样。在演示视频中,一个输入声音是脉动合成器低音,另一个是风琴。注意每种声音的不同品质,如低音的脉动,是如何随着不同的潜在维度的变化而结合在一起的。在合成器的情况下,每个滑块确定潜在空间中一个维度的值,我们听到解码的输出。
朝着插件方向努力
正如我们在第一部分中所讨论的,音乐工具的质量,以及它对音乐家的用处,直接关系到它与他们的工作流程的契合程度。在为数不多的供音乐家使用的人工智能音乐程序中,没有一个充分考虑了他们参与创作过程的环境。今天,几乎所有的音乐都是用 DAW 处理的,无论录音本身是数字的,还是简单的母带。如果你想让音乐人使用你的 ML 工具,你必须让它和 DAWs 一起工作!
创建可在 DAW 中工作的程序的最常见方式是使用插件和广泛支持的平台,如 VST。它们允许音乐家和程序员创建自己的图形用户界面,可以直接与加载到 DAW 中的音乐进行交互,并可以创建效果、MIDI 控件,甚至整个虚拟乐器。像 VST 这样的平台得到了广泛的支持,并且与大多数 Daw 兼容。对于 MusicAE,我们希望插件能够直接从 DAW 读取和写入音频。理想情况下,这些操作应该像高亮显示轨道以选择输入并定义空白轨道作为输出一样简单,或者像软件乐器一样载入我们的合成器。控制潜在空间的滑块可以直接在屏幕上控制,或者映射到通过 DAW 连接的 MIDI 控制器上的模拟旋钮和滑块。
虽然我们的原始代码是使用 python 设计的,这使我们可以轻松访问最广泛使用的机器学习和神经网络库,特别是 TensorFlow,但 vst 和软件工具几乎普遍使用 C++编程语言编写。能够将经过训练的 autoencoder 模型移植到不同的语言中,并学会在 VSTs 的约束下工作,这已被证明是一个挑战,但我们已经能够复制我们的原型 GUI 的功能,现在正在寻找更直观的界面,以便在任何 DAW 中无缝工作。下面是基于我们现有 GUI 的模型,旁边是流行的 Mac OS 应用 GarageBand。

作者将 MusicAE 模型集成为基于 DAW 的 Mac OS 插件
结论
虽然在机器学习和音乐的交叉领域有很多令人兴奋的工作,但很少有开发者认真考虑过数字音乐人的需求。我们的 MusicAE 目标不仅仅是构建一个工具,为音乐家提供一种全新的与音乐互动的方式,而且还帮助他们使用他们熟悉的方法将其集成到他们的工作流程中。到目前为止,我们已经试验了不同的神经网络体系结构,并找到了合适的模型,我们已经在独立的 GUI 中测试了这些模型。我们现在着眼于将这一功能完全整合到 VST 插件中,使我们的工具与大多数 Daw 兼容,从而使音乐人也能使用它。

该项目由 Theo Jaquenoud、Samuel Maltz 和 Zachary Friedman 完成,是库珀联盟高级电气工程设计项目的一部分。我们要感谢我们的顾问塞缪尔·基恩教授向我们介绍了这个项目,还要感谢库珀联盟的校友、目前在伦敦玛丽女王大学数字音乐中心攻读博士学位的 T2·约瑟夫上校。他首先构思了这个想法,并为我们构建软件提供了很多基础。
2021 年您的数据团队必须优先考虑的事项
原文:https://towardsdatascience.com/must-have-priorities-for-your-data-team-in-2021-e4ac399ff1f3?source=collection_archive---------21-----------------------
意见
以及如何实现它们。

文章由 Unsplash 上的 Anastasia Petrova 提供。
2021 年,不仅仅是拥有“现代数据堆栈”它是关于用一种现代的方法处理你的数据。我们是这样到达那里的。
在过去的几周里,我与一些世界上最好的数据团队就他们 2021 年的优先事项进行了多次对话。随着许多团队专注于升级或扩展现有的基础架构,有两个“决心”让我印象深刻:
- 拉近工程和数据组织的距离
- 直接连接数据生产者和数据消费者
与许多其他优先事项不同,这两个优先事项显然不是技术性的,不仅需要更智能的工具(想想:增强分析、数据湖库和数据平台),还需要关于构建和扩展数据团队的全新思维。
先说事实。现代公司正在利用越来越多的数据来保持竞争优势和推动创新。因此,这些公司中越来越多的人正在使用和访问这些数据来驱动关键的业务功能。
为了使这种面向领域的数据基础设施成为现实,许多数据团队正在采用“数据网格”方法,这种方法为跨职能团队提供了支持“数据即产品”的选项,每个领域都处理自己的管道和分析。
“数据网格”的概念越来越流行,虽然我完全支持它,但事实是大多数组织在开始采用它时并没有为成功做好准备。为了大规模快速发展,工程团队和数据组织需要通力合作。对于许多公司来说,这两个领域各自为政,很容易将这种方法“结合”起来。
也就是说,如果你从上面的两个优先事项开始,你就已经成功了。以下是到达那里的方法:
对数据应用开发运维心态
首先,我们已经亲眼看到,将软件工程和 DevOps 概念引入数据团队和工作流是提高数据可靠性的可靠方法,从而提高整个组织的数据信任度。
具体来说,数据团队可以将数据可观察性最佳实践应用于管道中出现的数据事件的监控、警报和故障排除。更好的是,利用沿袭来映射上游和下游依赖关系可以使工程和数据团队在出现问题时更容易协作。
Pro-tip: 最有影响力:使用一个 SLA/ SLO 框架(结合端到端可观察性来监控!)以确保一致性和问责制。这篇文章由易贝的产品、数据服务主管撰写,对这一点有着深刻的见解。
投资数据发现
一旦团队明确了特定数据领域的所有权和责任,团队就必须转向数据发现来减少数据和见解操作上的摩擦。
数据发现让数据所有者对其作为产品的数据负责,并促进不同位置的分布式数据之间的通信。一旦数据被提供给给定的域并被其转换,域数据所有者就可以利用这些数据来满足他们的运营或分析需求。与数据目录不同,数据发现可以实时了解数据的当前状态,而不是理想状态或“编目”状态。
专业提示:为技术和非技术用户提供一个共享的自助服务平台,帮助他们找到所需的数据并更好地理解这些数据(数据来自哪里,是否是最新的,等等)。)对于增强数据消费者的能力至关重要。
通过采用 DevOps 最佳实践并投资于自动化、可扩展的数据发现,数据团队可以满足这些优先事项,甚至更多。通过正确的方法,更高的数据可靠性和协作指日可待。
这里还有一些资源,应该可以让您很好地了解如何构建您的现代数据团队:
- 博客: 数据可观测性:数据工程的下一个前沿
- 博客: 为什么数据测试不能防止数据管道破裂
- 案例研究: 如何解决大规模数据质量问题
有兴趣了解更多关于数据可观察性和发现之间关系的信息吗?伸出手去 斯科特 以及剩下的 蒙特卡洛 团队!
高效数据科学家必备的演示工具
原文:https://towardsdatascience.com/must-know-presentation-tools-for-the-effective-data-scientist-93e618ffc8c2?source=collection_archive---------21-----------------------
有影响力的数据科学
对于当今的数据科学家来说,传达一个连贯的、数据驱动的故事是最重要的技能,但也是最不发达的技能。更好的工具会有所帮助——现在就了解一种新的工具。

由路上车头拍摄
O 这些年来,我看到许多拥有博士学位的数据科学家花费数周或数月来构建高效的机器学习管道,这些管道(理论上)将带来现实世界的价值。不幸的是,如果这些劳动成果不能有效地传达他们工作的价值,它们就可能在藤蔓上死去,这种不幸我已经见证得太多了。我在这里分享一些具体的、可行的技巧,让你成为技术思想的有效交流者(文章即将发表)。然而,这篇文章将试图对有效的数据科学家的表示方法进行全面的回顾。如果你知道更多,请在下面的评论中分享!
表示方法
我将按照酷的程度依次介绍以下几种展示方法:
- PowerPoint + Excel
- 太美了。人工智能
- 比默胶乳
- RStudio/Markdown — ioslides
- RStudio/Markdown — RevealJS
- 白皮书
前两个选项是点击式图形用户界面(GUI)。
最后四种是编程语言——这意味着您使用编程语言(通常是多种语言:Markdown、HTML5,以及 LaTeX、R 和 Python 选项)来创建演示文稿。
每种类型都有利弊。
GUI 与编程利弊概述
GUI 优点:
- 快速采用。做一件简单的事情不需要很长时间。
- 个人演示模板。随着时间的推移,您可以构建一个漂亮的 PowerPoint 演示文稿,并以最小到中等的可变时间成本为每个新的演示文稿进行复制和修改。需要一个漂亮的标题幻灯片?用一台你花了很多时间完善和改变的旧电脑,收获你之前劳动的成果。
- 主幻灯片布局。通过仔细的思考和设计,主幻灯片布局可以使演示文稿的构建更加规范。
- 内容和定制。只要有足够的时间,几乎任何事情都可以在 PowerPoint 中完成:可播放的视频、嵌入的 Excel 表格、Excel 图表、彩色图像背景、幻灯片过渡、徽标、页脚等等。并且放置在幻灯片上向下到第 n 个像素。
GUI 缺点:
- 主幻灯片布局。仔细的思考和设计是罕见的。通常情况下,修改主幻灯片布局会有点麻烦,甚至非常麻烦。公司的主幻灯片布局可能特别烦人;我还没有看到一个设计良好的。此外,在我看来,主幻灯片布局中没有必要有 30-50 个幻灯片模板…这只会让事情变得更复杂。
- 内容限制。不是所有的事情都可以用 PowerPoint 轻松完成。例如,您不能包含由 R 代码片段生成的交互式数据可视化。但是您可以通过编程解决方案做到这一点。
程序化优点:
- 内容灵活性。可以包含视频、由 R 或 Python 代码片段动态生成的(交互式)数据可视化。结果看起来很“酷”,很先进。
- 灵活的专题定制。您可以通过预置的主题控制许多最重要的造型细节,只需更改一个选项的一个词(即“默认”为“月亮”或“米色”)。您还可以灵活地在层叠样式表(CSS)的粒度级别上进行修改,这是 HTML 的基石。
- 快速、集中的造型选项。例如,您可以只通过设置一个选项“smart: true”来强制输入正确的样式,该选项会自动将直引号转换为弯引号,将三个破折号转换为长破折号,将三个点转换为省略号。对于自动应用到整个演示文稿中所有内容的其他更改,还有许多其他选项。
- 数学。方案选项
纲领性缺点:
- 陡峭的学习曲线。学习 Markdown,LaTeX(如果需要的话),R,Python 都需要时间。然而,我认为数据科学家已经知道至少一种语言,如果不是所有这些语言的话,而且许多人精通所有这些语言。
- 模板开发的前期成本高。开发一个自定义模板需要很长时间。
- 难以用于复杂的视觉布局。想把一堆视觉效果,以一种非常特殊的方式互相重叠?使用 PowerPoint。然而,你真的应该在专业演讲中这样做吗?或者,在 PowerPoint/Paint/Paint 3D 中构建它,截取一个屏幕截图,然后使用 Markdown 将其插入到编程演示文稿中:)
PowerPoint + Excel
PowerPoint 既可以是简单的演示形式,也可以是复杂的演示形式。简单:在 Excel 中制作累积分布函数(CDF)折线图、数据字典、条形图等。然后复制粘贴到 PowerPoint 演示文稿中并分享。
复杂:
-屏幕记录显示器的自定义区域,播放网络图动画/交互式 Plotly 图,然后在幻灯片中插入视频
-缓慢且低效:编译 LaTeX 方程然后截图-复制-粘贴
-不可能:动态、可视化-生成 R 或 Python 代码片段
理想的用例条件:你需要一个快速的演示。你不需要提出许多或任何数学方程式。你的观众不会欣赏提高了的审美。您需要对可视对象的定位进行精细控制,或者您有一个复杂的幻灯片布局。对 R 或 Python 生成的数据可视化没有任何好处。您需要将它传递给其他想要编辑或使用您的演示文稿的演示者/合作者。
太美了。人工智能
一个漂亮、简单的 PowerPoint 版本。美丽的基本原则。我认为 PowerPoint 为用户提供了太多无用的选项,对于有效地讲故事来说,这些选项几乎没有任何价值。太美了。AI 引入了限制你的幻灯片布局的约束,但是对于那些已经被设计为自动拥有最佳和漂亮的空白,默认的配色方案。对于幻灯片过渡和其他常见功能,也存在类似的敏感约束。
最佳用例条件:面向高管观众或外部观众的高水平演示,这些观众喜欢华丽、美丽的简单性和巧妙的叙述。基本上,漂亮。艾创作出好看的现代艺术。
强烈推荐!通过我的推荐链接免费查看它们(每次点击我可以获得 1 美元的商店积分!):https://www.beautiful.ai/signup?affiliate = u 5 cjvzpocrojnwmq 4 apq 8 gtjsk 0
比默胶乳
如果你有一个 STEM 研究生学位,你应该知道这个用于大量数学演示的 LaTeX 热门课程。我曾经认为这很酷,但尽管我尽了最大努力,我还是觉得它丑得令人心烦意乱。
我仍然有这种感觉。跳过这种沟通方法,选择下面描述的后续选项,因为虽然教授和研究生可能不介意枯燥的演示主题,但至少有一位产品经理、副总裁和高管会介意,这会扼杀他们对你的演示的兴趣,并导致你失去任何获得认同和实施的机会。请记住,这些人在冒着名誉受损的风险向他们的老板提出这个想法之前,必须先相信你的异想天开、深奥难懂的想法。帮助他们理解你释放的价值,你将获得另一个你的想法的传播者,增加你的机器学习模型被更广泛采用的可能性。
最佳用例条件:你想要一个丑陋的演示,作为一个内部笑话,给一群博士听众,他们会在一瞬间充满渴望的遐想中笑着和/或赞许地点头。
RStudio/Markdown — ioslides
以前觉得这很酷。但是很难自定义配色方案。事实上,你甚至不能轻易改变特定幻灯片的背景颜色( 见 GitHub 问题 )。更多定制选项参见 RevealJS。
理想用例条件:无。请改用 RevealJS。
虽然用户报告可以在 StackOverflow 上为幻灯片设置自定义背景颜色,但我没有找到一个可行的交钥匙 CSS 解决方案,也不想花太多时间来解决这么简单的事情。
RStudio/Markdown — RevealJS
强烈推荐这个选项。建立一个好的第一次演示需要花费大量的时间。但是如果你花时间去做好它,你可以在未来通过使用你自己制作的模板,根据你通常的使用情况定制,更快地制作出漂亮的演示文稿。RevealJS 输出格式的另一个有趣动态是 2D 演示格式:不仅可以在幻灯片之间左右移动,还可以上下移动。这允许幻灯片的固有子集化,很像表示子要点。
理想的用例条件:受益于 Python 或 R 代码(或许还有其他代码)生成的交互式数据可视化的演示。).与你分享它的人不会想要/需要做出改变。轻松显示代码及其输出。将结果快速发布到网站上,以便更广泛地传播。
请参阅我在文章底部编写并分享的代码片段,开始阅读。也可以查看有用的文档:https://github.com/rstudio/revealjs#reveal-plugins
白皮书/书面备忘录
极其耗时——需要一周或更长时间才能有效地写作。然而,在可能和适用的情况下,我喜欢撰写和传播白皮书。杰夫·贝索斯不仅比我(或许还有你)富有几十亿,他还阐述了书面备忘录优于“幻灯片”/“幻灯片组”/“幻灯片组”/PowerPoint 的优点:
忽略烦人的视频剪辑,尤其是结尾的循环。不过总体来说视频不错。
理想的用例条件:
适合容易接受这个想法并且对 PowerPoint 没有强烈偏好的观众。一般来说,技术观众喜欢包含在白皮书/书面备忘录中的细节,而更多的执行观众倾向于喜欢带有视觉伴奏的华丽演说。
代码
您可以将下面的代码片段复制粘贴到。Rmd 文件并在 RStudio 中编译。要运行我的示例代码,您至少需要以下包:
install.packages("revealjs ",type = " source ")
install . packages(" network D3 ")
关于作者
安德鲁·杨是 Neustar 的 R&D 数据科学家经理。例如,Neustar 是一家信息服务公司,从航空、银行、政府、营销、社交媒体和电信等领域的数百家公司获取结构化和非结构化的文本和图片数据。Neustar 将这些数据成分结合起来,然后向企业客户出售具有附加值的成品“菜肴”,用于咨询、网络安全、欺诈检测和营销等目的。在这种情况下,Young 先生是 R&D 一个小型数据科学团队的实践型首席架构师,负责构建、优化和维护一个为所有产品和服务提供信息的系统,该系统为 Neustar 带来了超过 10 亿美元的年收入。在 LinkedIn 上关注 Andrew,了解数据科学的最新趋势!
更多热门文章:
- 隔离森林是目前最好的大数据异常检测算法
- 每个数据科学家都应该知道:偏差-方差权衡一般化是错误的
互信息:作为模仿的预测
原文:https://towardsdatascience.com/mutual-information-prediction-as-imitation-da2cfb1e9bdd?source=collection_archive---------24-----------------------
行业笔记
这是关于信息论及其与数据驱动企业和战略的关系的系列文章中的第二篇。虽然在每一部分都会有一些等式,但是对于那些对细节不太感兴趣而对含义更感兴趣的人来说,它们可以被忽略。第一篇关于 熵的文章可以在这里找到。
在《麦克白》第一幕中击败了挪威和爱尔兰的联军之后,这个有名无实的角色遇到了三个女巫。女巫们接着做了三个预言,麦克白将会
1.格拉米的塔那,他现在的头衔
2.考多的塔那,这是他最近击败的敌人的称号
3.最后是苏格兰国王
后来,他们又发表了两个更可怕的预言。他们的第四个预言是,没有男人(女人生的)会杀死麦克白,他们的第五个预言是,他将是安全的,直到附近的森林侵占附近的小山。在某种程度上,你可以破坏西方文学中最受欢迎的作品之一,麦克白最终被 Macduff 杀死,因为 Macduff 的军队使用了附近森林中的伪装来占领附近的山丘。就像从特尔斐到奥马哈的许多其他神谕一样,女巫们的预言实现了,这要归功于运气、推理和足够的变幻莫测的良好结合,这使得它很难出错。
当然,如今的高管们往往不太关心他们的竞争对手是如何诞生的,也很少有时间去猜谜语。相反,他们想要可操作的信息,清楚地说明不需要 5 个动作和全部角色来完成。通常,特别是在操作环境中,他们希望在仔细收集数据和严格测试模型之前得到答案。
互信息通过将推理重新想象为一个最优模仿的游戏,提供了一种形式上和概念上的方法来解决预测问题。它为我们提供了一种方式,既正式又本能地评估我们看到的信息的价值,并了解如何通过减少噪音来防止信息过载。在这里,我们将讨论如何利用信息进行有效的预测,以及这种方法令人惊讶的辅助效用。
隐藏信息——三种预测方式
回到女巫和她们的前三个预言。他们的第三个预言,麦克白将成为国王,是我们大多数人在说预言时想到的事情。他们所描述的事件正在未来发生,而绝不是既定的。如果真的发生了,那就证明了别人不具备的特殊知识或远见。这是基本的有线电视天气预报员、华尔街分析师和对方付费电话通灵师使用的占卜类型。
就我们的目的而言,我们需要一个更一般化的预测定义,一个包含多种情景的定义。当计算机看到一张狗的图像,并正确地将它标记为狗时,这是一种预测。同样,当调查者试图找出工厂事故的根本原因时,这也是一种预测。将所有这些联系在一起的线索是,他们试图理解对他们来说隐藏的信息。与其严格地试图理解近期未来,不如说预测的更好定义是类似于: **预测是从专业知识、直觉和观察中发现隐藏信息的任务。**我们将在本系列中继续这样做,让我们相信这是有意义的。同样,苏格兰戏剧中的预言:
1.格拉米的塔那:过去——恢复信息还是调查
2.考多的塔那:现在——推断信息和相互作用
3.苏格兰国王:未来——正确的预测
我们很容易将第三个预言视为预言。我们先检查一下。如果你在这个星球上的任何历史时期随机选择任何人(例如,你没有任何特殊信息),他们不太可能是 11 世纪的苏格兰人,更不可能是来自格拉米的贵族。为了正确地将某人归类为拥有贵族头衔,你至少需要一些特殊的信息,即使有了这些信息,你也可能会弄错。
同样地,女巫的第二个预言也是一个预言。大多数人在大多数时候都不是考多的塔那,虽然这个头衔正在授予麦克白,但女巫们对此并没有直接的了解。通过一些神秘主义,他们推断它正在发生,再次揭示隐藏的信息。
最后,让我们检查一下隐藏信息的定义是否符合我们的直觉。根据我们的直觉,预测通常是前瞻性的陈述。运用与之前相同的逻辑,我们看到大多数人,大多数时间都不是,也永远不会成为苏格兰国王。再一次,正确地将某人归类为未来的苏格兰国王需要相当多的特殊信息或者相当大的运气。
通过这三个例子,我们描述了一个一致的预测过程。首先要有特殊的信息或洞察力。其次,考虑这些信息的重要性和含义。最后对一个可能是错误的事件做一个可证伪的陈述。这非常符合我们上面的定义。
现在一个新的问题出现了。这个世界充满了信息,大多数信息都是无用的。了解皮奥里亚的天气不太可能让你洞察皇室血统。理想情况下,你希望依赖相关、可靠和高质量的信息,忽略不相关的信息。我们如何衡量我们所掌握的信息的质量?
模仿&互资
考虑下面的游戏:抛一枚硬币,无论它落在哪个面上,都抛第二枚硬币,直到它落在同一个面上,记录结果。这不是一个有趣的游戏。事实上,这个游戏和最初的掷硬币有相同的熵:1 比特。

掷硬币游戏的一些例子
我们要稍微改变一下游戏。我们现在不只是记录结果,而是试图预测结果。没有任何信息,有 50/50 的机会得到 HH 或 TT 结果。这是我们的新游戏:
1.抛第一枚硬币并隐藏结果
2.然后你可能知道宇宙过去或现在的任何一条信息,
3.猜测游戏的最终状态
4.翻转第二枚硬币,直到它与第一枚相符
我们的新目标是找到一些能告诉我们最终结果的数据。这里的解决方案很明显,揭开第一个翻转你就知道游戏的最终状态了。换句话说,最后一次翻转是对第一次翻转的完美模仿。或者,我们可以在游戏之外抛硬币,这不会告诉我们游戏的最终状态,也不会提供任何信息。
在这个翻转游戏中,我们要观察最终状态共享最大互信息的情况。互信息告诉我们一个事件在多大程度上减少了另一个事件的不确定性。它是一个观察到的信号模拟一个单独信号的程度,即使是不完美的模拟。第二次翻转是第一次的完美复制。游戏之外的硬币是独立的,因此不提供新信息。知道某人是 11 世纪的苏格兰人并不能保证他们会成为国王,但它提供了一些你原本不会得到的少量信息。相互信息由以下给出:

互信息 I(X;y);p(x,Y)是 X 和 Y 成员的联合概率,而 p(x)和 p(y)是它们各自的概率
不可否认,这一个比以前的文章中的方程更吓人,但同样直观。该等式表明,两个事件(X 和 Y)的互信息(I)与 X 和 Y 的每组结果 p(x,Y)的概率以及它们的独立概率 p(x)和 p(y)成比例。就像熵一样,这是一种形式化的方式来陈述我们已经知道的东西:两个事件越相互关联,它们告诉我们的关于彼此的信息就越多。如果我们考虑翻转游戏中的两个硬币,它们共享一个 1 比特的共同信息,因为它们是完全相关的。知道了第一次翻转的结果,就从一个有 1 比特熵的游戏中消除了 1 比特的不确定性。来自游戏之外的硬币提供了 0 比特的相互信息,并且知道这与游戏的结果无关。
策略分析
知识性模仿并不总是像我们的抛硬币游戏那样明显。考虑以下交易策略。

2016 年 4 月至 2021 年 4 月标准普尔 500 两种策略的回报
虽然这里的两个时间序列看起来并不完全相似,但它们确实拥有关于彼此的最大限度的相互信息。【2】
这两种策略将成为我们理解信息的常规策略,因此了解它们是什么是值得的。蓝色策略是一个简单的动量。一个简单的动量策略着眼于市场昨天做了什么,并假设它今天会做同样的事情。如果市场昨天上涨,那么你今天买入,如果下跌,就卖出。简单的均值回归——用橙色表示——正好相反:如果市场上涨,假设它被高估,今天就卖出;如果市场下跌,假设它被低估,现在就买入。了解一种策略的立场可以很好地告诉你另一种策略的立场。
从信息论的角度来看,动量和均值回复也是有趣的策略,还有另外一个原因。回想一下上一节,我们定义了一个叫做买入并持有的最小信息策略。这种策略仅仅是购买并持有(因此得名)一种证券【3】。

同样的策略以买入并持有为基准,这是一种信息含量较低的策略
虽然动量和均值回归是彼此的完美预测指标,但它们也与我们的默认策略“买入并持有”分享最少的共同信息。这三者,最小信息方法和它的信息对手形成了更广泛的基准策略的基础。新的、更奇特的方法应该改进这些简单的策略,并且可以被描述为具有动量或均值回复偏差。由于均值回归和动量都经历过表现过度和表现不足的时期,理想的奇异策略不会偏向任何一方。
关于策略的一个小插曲。在本系列中,我们将花大量时间讨论与股票投资相关的投资组合构建和交易策略。这主要是因为财务数据的可用性。从信息、概念和统计的角度来看,决定如何以及何时进出标准普尔 500 指数的头寸,以及如何以及何时让一群人参与广告活动,这两者之间几乎没有区别。在证券投资组合中分配一堆钱以获取回报(投资组合构建)和将一堆任务分配给人员或机器以达到 KPI 之间几乎没有区别。
优猜
任何策略都是买入并持有的完美预测器,但是没有一个最优的猜测策略可以从买入并持有来预测动量或均值回复策略。在这个系列中,“猜测”和“预测”将被区分开来,就像我们用通俗的说法区分两者一样。猜测是随意的,很快就能完成,而预测则更为周到,也更费时。更具体地说,猜测是一种预测,除了最多的结果概率之外,没有关于事件的迫切信息。我们将要讨论的两种猜测是
纯粹猜测:策略是从所有可能的结果中随机选择结果
最佳猜测:一种策略,它选择一个结果,并且在没有仔细研究紧急信息的情况下尽可能正确
我们之前遇到过纯猜测的。在前面的掷骰子游戏中,我们使用了一种纯粹的猜测策略,在 16.7%的时间里正确地猜测出公平和不公平骰子的结果。我们也遇到过最优猜测策略。美国大盘股往往会产生正回报,或者至少在过去 120 多年里一直如此。如果这就是所有的信息,你的最佳猜测策略是买入市场,然后离场。
正如您可能已经猜到的那样,最佳猜测与交互信息密切相关。让我们再想想我们的骰子。提醒一下,一个公平的骰子有 1/6 的机会出现在任何面上;不公平的一方有 90%的几率‘1’,2%的几率给对方面子。一个纯粹的猜测策略会产生相同的结果,但是对于不公平的骰子,似乎应该有一个更好的策略。
根据互信息方程(方程 4 ),使 X 成为掷骰子的结果,Y 成为我们猜测的结果。最佳猜测策略将最大限度地使真实结果与猜测一致。换句话说,最佳猜测最大化了掷骰子和猜测之间的互信息。不公平骰子的一个策略是总是猜“1”这是对我们看到的实际掷骰子的一个很好的模仿,你 90%的时候都是对的。
最佳猜测对于快速决策至关重要。这是真正专家的领域。真正的专家利用他们的经验来了解未知的概率,以及从轶事观察中捕捉信息的启发法。如何检验一个专家是否真的有专长,是以后的问题。最佳猜测对于低信息量决策也至关重要。风险资本和天使投资者对早期公司的真实信息知之甚少,但如果他们能找出平均产生回报的公司类型的子集,投资所有这些公司是有意义的。
信息过载&信息修剪
上述最优猜测策略的一个怪癖是,“总是猜 1”策略与“总是猜 2”策略具有相同的与不公平骰子的交互信息信息优先的方法认为,虽然 2 很少出现,但它提供的信息可以被更好地利用。例如,如果我们建立第二个规则,说‘每当我们猜 2,就把它改成 1’,我们会表现得很好。换句话说,互信息告诉我们什么时候数据是好的而模型是坏的。
如果互信息可以告诉我们什么时候数据是好的,它也可以告诉我们什么时候数据是坏的。猜想:应该忽略坏数据。顺便说一下,它也可以用来告诉我们数据是否冗余。“总是猜 1”策略不会给出“总是猜 2”策略不会给出的信息。简单的动量策略不能提供简单的均值回复策略不能提供的信息。我们可以忽略这两个策略中的一个,情况不会更糟。
然而在现实世界中,我们很少能找到完美的信息替代品,也找不到完全不相关的信息。甚至洛杉矶的雨也可以与交易行为【6】轻微相关。如果您将这一点与我们对信息力量的见解相结合,即更多的信息总是会减少不确定性,那么运营经理每天早上收到 50 页的图表和数字报告就不足为奇了。一个人不可能看懂的 50 页。我们需要一种方法来限制信息的摄入,以匹配我们消耗信息的带宽。
主成分分析(PCA)是一种传统的统计方法,可以将所有这些图表整理成可读性更强的图表。PCA 将你所有的图表合并成一小部分,这就是目标。不幸的是,这些图表是你最初 50 个图表的合成产物,与工厂车间没有直观的关系。更好的方法是删除冗余和不相关的信息,只保留相关和唯一的图表。
同样,我们有一个正式和直观的基于互信息的解决方案。在形式上,我们希望找到一组预测器,它们与目标具有最大的互信息,同时彼此之间的冗余最小。不太正式的是,我们希望找到模拟 KPI 但又不互相模拟的信号。这通常被称为最大相关性最小冗余(mRMR)【7】特征选择方法。编写或找到一个 mRMR 算法来修剪一份早间报告并不难。这也可以通过一些经验法则来完成:我的图表看起来是像我的目标正面朝上还是朝下(相关性),每个图表看起来不同还是一堆图表看起来相同(冗余),我能否在每个 KPI 的 1 页上适合所有图表(带宽保留)。有了这种策略,就有可能将大量信息压缩成一份简单易懂的 5 页报告。
基础知识
随着熵和互信息的牢固确立,我们现在拥有了信息理论的基石。我们已经看到了许多玩具、寓言和轶事,表明这种方法的独特力量和灵活性。在下一篇文章中,我们将使用我们的构建模块来建立一些公理和原则,使我们能够以更复杂的方式处理数据驱动的决策和信息风险。
莎士比亚的《麦克白》……我真的需要引用这个吗?
从雅虎财经检索的数据
【3】想一个股票或债券
更多关于金融市场严峻/令人愤怒的统计数据,请看塔勒布的《黑天鹅》或曼德尔布罗的《市场的不当行为》
【5】在撰写本文时,Andeerssen Horowitz 的投资组合中有超过 250 名活跃成员(2021 年 5 月 6 日,https://a16z.com/portfolio/))
【6】愚蠢相关性的例子【https://www.tylervigen.com/spurious-correlations
【彭海春,龙,丁,“基于互信息的特征选择:最大依赖、最大相关和最小冗余准则”,《IEEE 模式分析与机器智能汇刊》,第 27 卷第 8 期,第 1226-1238 页,2005 年。
免责声明:本文表达的观点仅代表作者个人观点,不一定代表任何雇主、组织或其附属机构的观点。
2021 道格拉斯·汉密尔顿
MVPs:构建数据产品的缓慢而昂贵的方法?
原文:https://towardsdatascience.com/mvps-the-slow-expensive-way-to-build-data-products-a95b166c10d3?source=collection_archive---------45-----------------------

佛朗哥·安东尼奥·乔瓦内拉在 Unsplash 上拍摄的照片。
有没有比 MVP 更快的方法来创建实际上可以使用、简单并且值得信赖的数据产品?
现在,让我们来谈谈 MVP(最小可行产品),以及为什么以这种方式构建数据产品对您的团队来说可能是一种非常昂贵、缓慢的学习方式。
很简单:MVP 听起来很棒。在专注于快速尝试事物、获得反馈和做出改进方面,他们可以是伟大的。
只不过大多数地方不这么做。
大多数地方只是继续增加名单上的下一个项目,从来没有减去或重新评估他们的 MVP 的价值。因为没有时间了,而用户/企业现在就需要它。
MVP 说“快!”这听起来不错。
说“我们不知道”是很棒的,因为它让你面对各种可能性。事实是,相对于 MVP,有一种更便宜、更快捷的方式来了解用户需要什么,以及你需要如何定位你的数据,以改变他们的生活,使他们变得依赖于你的见解和技术。
这叫做用户研究。
不太刺激?
你猜怎么着:你们大多数人做得很少。你们中的大多数人都没有常规的 it 程序,并且你们把大部分时间花在构建和猜测上,而不是去了解用户的问题空间、今天(或者明天)如何利用数据做出决策,以及你们的解决方案需要如何适应他们的生活。
(毕竟不是给你的——是给他们的。)
更快地学习对你的团队和组织来说是一个重要的竞争优势。
事实上,如果你的团队知道如何对数据做出反应,它(而不是人工智能)可能是你整个公司的头号竞争优势。)你在与用户交谈时捕捉到的。
看,你很聪明:你已经知道这可能是一个更好的方法。为什么?
不是猜测。
你一直在呼吁:你也和你的团队一起实践吗?
我们忘记了定性数据(也就是我们的研究发现)也是数据吗?
你知道快速学习意味着更少的时间浪费在错误的解决方案上。
当你的团队真正理解用户的问题空间时,他们最好的作品通常会出现。
UX 研究,尤其是定性的 2x1 或 1x1 类型的访谈/跟踪式人种学,是必不可少的如果你想停止猜测,并开始降低推出另一个不会被使用的数据产品的风险。
很有可能,你总是倾向于“构建一些东西”或者争论数据,或者构建你知道你将需要的基础设施,“不管前端会出现什么。”
因为建筑很容易测量。
因为做技术工作和扯皮数据、做模型感觉都是进步,一个人无法轻易衡量“听用户说话”的价值。
不习惯的那种。用户不信任,不关心,或者不能使用的那种,因为它太他妈复杂了。
那种从来没有被任何用户调查提前告知的。
我给大多数人的第一条建议是花更多的时间和你的用户在一起:观察、倾听、学习和询问。因为这是我们如何降低制造劣质产品的风险,并增加创新的竞技场。
这适用于你团队中的所有成员。
这些客户/用户暴露时间至关重要;如果你的团队中有 UX /设计人员,也不仅仅是他们。
事实上,如果你的员工中有试图“拥有”研究的设计/UX 专业人士,而不是打破墙壁让你的技术和数据团队成员来观察、促进和参与研究,他们就没有做好自己的工作。
好的设计从研究开始:从我们服务的使用我们数据产品的人开始。
这是关于了解他们的态度、关注点、决策过程、工作流程、观点和过去的行为,这样我们就可以利用现有的数据做得更好。
你只需要一个可重复的习惯和程序来做研究,包括你的整个团队。这是帮助你和你的团队创新的第一件事,并防止你依赖于“猜测、构建、获得幸运”的套路(这听起来就像我本周要演奏的爵士乐音乐会一样由数据驱动)。)
从长远来看(实际上可能不会太久),你的用户会感谢你——并且你会走上以人为中心的设计之路,而不是谈论它。
当用户高兴时,商业价值通常会很快跟上。
关于作者
Brian T. O'Neill 是一名咨询产品设计师,帮助公司创建创新的 ML 和分析解决方案。他是 Designing for Analytics 的创始人和负责人,主持 体验数据 播客,并为麻省理工学院沙盒创新计划提供建议。想在收件箱中获得更多关于创新数据产品设计的见解吗?现在就订阅。
🎧 | 网站 | LinkedIn | 推特:@rhythmspice
我作为数据科学家的 2021 年回顾
原文:https://towardsdatascience.com/my-2021-year-in-review-as-a-data-scientist-dd081343ccc7?source=collection_archive---------17-----------------------
阅读、写作、编码和职业思考

照片由史蒂夫·曾在 Unsplash 上拍摄
阅读
我开始这一年的目标是每周读一本书。生活和工作忙起来了,所以没发生那种事。
以下是我 2021 年读过的书的清单:

- 终有一死:医学和最终的意义
- 偷书贼
- 没有规则规则:网飞和文化再造
- 我们坚持的真理:美国之旅
- 凤凰计划:一部关于它的小说,DevOps,帮助你的生意
- 范围:为什么多面手会在专业领域取得成功
- 永远不要单独吃饭
- Bossypants
- 如何打破常规,找到你的目标,创造你应得的生活和事业
- 在 H Mart 哭泣:回忆录
- 每周 4 小时工作制:摆脱朝九晚五,住在任何地方,加入新富行列
- 厨房机密更新版:美食地下冒险
- 个人 MBA:掌握商业艺术
- 小人物
- 清单宣言:如何把事情做好
- 引爆点:小小的事情可以产生巨大的影响
- 优势:化逆境为优势
我的最爱?阿图尔·加万德的《凡人》。这是一本充满了感人、悲伤、令人心碎的生、死和死亡故事的书。这真是一本让我大开眼界的书。我获得了对长者更多的同情、尊重和理解。
如果你有任何书籍建议,请告诉我。我发现很难找到有趣的书来读。我喜欢自传,轻松的读物,好的小说,我不看惊悚小说。
写作
2021 年,我在 Medium 上写了 10 篇数据科学博客,连续三个月成为 Medium 上的顶级作家之一。查看它们:
实验:
- 在线实验技巧——方差缩减
可视化:
- 将 Python 可视化面板应用部署到谷歌云
- 使用 Python 中的 Datashader 实现大数据可视化
- Python 中的可视化和交互式仪表盘
- Python 中的实时仪表盘
数据访问:
- 使用 intake-salesforce 在 Python 中查询 Salesforce 数据
- Python 中查询 BigQuery 的 3 种方式
其他:
- 如何在 Github 上托管 Jupyter 笔记本幻灯片
- 从零开始的多类逻辑回归
- 数据科学家的测试——使用 pytest 和假设进行单元测试
面谈
- 从走向数据科学的采访
- 如何在 AnacondaCon 推进您的数据科学职业生涯
- 在UT Austin Master and PhD Quant Night分享我的数据科学经验
Anaconda 博客和提及
- Anaconda 博客:揭穿数据科学中最大的神话
- Anaconda 博客:成为一名数据科学家意味着什么?
- Anaconda 博客:如何成为数据科学家?
- 政治家篇
- ZDNet 文章
编码
我猜我在夏天没怎么用 Github:)

我不能谈论我在工作中做什么。但是我可以谈谈我的开源项目。2021 年,我创建了两个 Python 开源包: Intake-salesforce 和 Intake-stripe 。
Intake 是一个轻量级包,用于查找、调查、加载和传播数据。(【https://intake.readthedocs.io/en/latest/】)T21)。
进气-销售力和进气-条纹是两个进气驱动器,用于轻松加载销售力数据和条纹数据。它们提供了从各种来源和 API 加载数据的一致方式。如果你感兴趣的话,可以看看这些包装和我在 intake-salesforce 上的博文。
职业思想
科技就业市场今年火了。每家科技公司都在疯狂招聘,包括我的公司,科技公司的薪酬在过去的一年里飙升。我得到了很多机会,我一直在想我的职业生涯会是什么样的,什么样的工作最适合我。
让我们一起做一个练习:
首先,让我们列出你希望你的理想工作应该具备的五个最重要的品质(假设报酬和工作生活平衡合理)。
- 对我来说,最重要的五件事是有趣的项目、好同事、自主权、所有权和信任。
接下来,如果非要牺牲一个,你牺牲哪个?
- 这个很简单,我会牺牲有趣的项目。这就是生活的现实。没有人能一直做有趣的项目,即使你是首席执行官。此外,下班后你有做任何你感兴趣的事情的自由。
然后,再选一个来祭祀。
- 现在越来越难了。我会牺牲所有权。这个很难。因为没有了主人翁的感觉,内在动力就会少一些。但是我想外在的动力仍然会让我前进。
接下来你会选择牺牲什么?
- 现在真的很难。好同事、自主和信任绝对重要。往往这三件事是相互交织在一起的。当有信任时,自主就会发生;当周围有好人时,信任就会发生。我想在这三者中,我可以放弃自主性,成为工作机器的最坏情况。
现在还剩两个,对你来说最重要的是什么?
- 在好同事和信任之间,如果我必须选择,我会选择好同事。信任是可以建立的,但是你不能改变其他人。我感到非常幸运能和我欣赏、喜爱和关心的人一起工作。许多名字、美丽的脸庞和微笑现在浮现在我的脑海里。我的同事是我最好的朋友和家人。
你选择了什么?你目前的工作能满足你最大的愿望吗?如果没有,看看我们的职业页面,因为 Anaconda 太棒了。
这就是我 2021 年的回顾。期待 2022 年!
作者索菲亚杨 2021 年 12 月 21 日。
我的 6 部分强大的 EDA 模板,讲终极技能
原文:https://towardsdatascience.com/my-6-part-powerful-eda-template-that-speaks-of-ultimate-skill-6bdde3c91431?source=collection_archive---------19-----------------------
EDA —做得对…

照片由 玛丽泰勒 上 像素
你为什么卡住了?
当 Jupyter 笔记本的空白盯着你时,很难开始。您有一个包含数百个要素的数据集,但您不知道从哪里开始。你的直觉告诉你:“正常,从一个正态分布的特征开始”。一如既往…
你一头扎进数据中,从一个特征移动到另一个特征,直到你发现自己在森林里无目的地追逐野鹅。那么,是什么原因呢?
嗯,开始,说明你没有一个清晰的流程。许多人说探索性数据分析是任何项目中最重要的部分,会占用你大部分的时间。
这个社区充满了平庸的、无组织的、难以理解的 EDA。这就是为什么一个定义良好的、基于过程的方法会给你一个脱颖而出的机会。这告诉其他人,也许是潜在的雇主,你“知道你在做什么”,在处理看不见的数据时,你有自己的流程和规则。
还没有那个流程?别害怕,我掩护你。
今天我们就来说说我在做很多网络课程的时候学到的一个 EDA 模板。我使用 Google Playstore 数据集在 Kaggle 上做了一个 EDA 示例,该数据集包含超过 100 万个应用程序的信息。我建议您在一个单独的窗口中打开它,以充分利用这篇文章。如果你有一个 Kaggle 账户,你可以在这里访问笔记本或者从 GitHub 克隆回购。
剧透一下,我选择的样本数据集并没有我想象的那么有趣。但是我尽了最大努力让这篇文章尽可能的有知识性。当我解释模板的每一部分时,我会在我介绍的主题上链接我的其他文章,而不是太详细地解释它们。尽情享受吧!
https://ibexorigin.medium.com/membership
获得由强大的 AI-Alpha 信号选择和总结的最佳和最新的 ML 和 AI 论文:
https://alphasignal.ai/?referrer=Bex
#1.介绍数据集,清楚地陈述 EDA 的目标
任何 EDA 的第一部分都应该是提供任何必要的信息,以建立对数据集及其试图解决的问题的初步理解。
就像任何书面内容一样,你应该以这样一种方式写开头,让观众一直读到结尾。
即使你要展示代码,也不一定要令人厌烦。
在 Kaggle 上 OSIC 肺纤维化竞赛的笔记本中,我看到了一个构建良好 EDA 简介的优秀策略。笔记本以问题的背景信息开始,并说明解决问题的重要性。然后,它继续给出数据集的基本信息,它是如何收集的,以及笔记本试图实现的目标。
在写这一节的时候,不要把它变成文字墙。使用良好的格式和适当的视觉效果让你的 EDA 令人难忘。
在单独的子部分中,导入所需的库和模块。你可以通过导入有用的库,如tqdm, colorama,并根据你的喜好调整matplotlib的rcParams。如果您想了解更多关于完美项目设置的信息,我推荐您阅读我的相关文章:
#2.基础探索和预处理
在进行可视化之前,通常需要对数据集进行高级概述。在一个小的小节中,使用常用的pandas函数,如head、describe、info等,来了解你的数据。
这样,您可以识别违反数据约束的基本清理问题,如数据类型、唯一性和范围。
我的建议是先突出所有问题,分别处理。数据清理既紧张又无聊,所以发现一个问题并立即着手解决它会让这个过程变得更糟。
试着用一种清晰的心态去发现所有的问题,而不去担心如何解决它们。
我喜欢在一个单元格中记录所有问题,就像这样:

作者图片
这让我可以在解决问题时划掉每个问题。在修复每一个时,我通常遵循以下模式:

作者图片
我用一个标题声明这个问题,并在一个单元格中修复它。为了检查错误,我使用了assert语句,如果检查成功,则不返回任何输出。否则,它抛出一个AssertionError。
对于海量数据集,即使是最小的操作也可能需要很长时间。当你认为某件事花费的时间比预期的要长时,很可能你做得很慢。尝试寻找你正在做的事情的更快的方法。
在我为本文准备的示例笔记本中,我注意到pd.to_datetime仅仅将一列转换为datetime就花了将近 2 分钟来处理一百万行。我在 StackOverflow 上搜索了一下,发现为函数提供一个格式字符串可以显著减少执行时间:

作者图片
对于百万行数据集,该解决方案只需几秒钟,而不是几分钟。
我相信有很多这样的快速清洁技巧,所以一定要搜索它们。
#3.单变量探索
第三部分是视觉探索的开始。具体来说,单变量探索是关于可视化单个变量。
使用分布图,如直方图、 PMF 和 PDF 图,CDFs 帮助您识别每个数字特征的分布。当你继续建模时,你会对你的特征有更好的理解。
在这里,了解不同的概率分布是有帮助的,比如正态、泊松、二项式和许多其他的。
尤其是,拥有一个正态分布的变量是你所能期望的最好的事情。但在现实中,你会经常遇到这样疯狂的事情:

作者图片
对于分类要素,使用条形图和计数图来查看数据集中每个类别的比例。同样,这对于分类问题至关重要,因为在拟合模型之前,您可以计算诸如类别不平衡之类的指标。

作者图片
#4.二元探索
现在,开始一次看两个变量。探索分类和统计关系。
这是你创造散点图、箱线图和热图等视觉效果的技巧大放异彩的地方。尽管它们很简单,但要把它们做对可能很难。考虑一下这个:

作者图片
您可以看到,通过调整绘图参数,您可以获得截然不同的结果。
计算相关矩阵来识别数字特征之间的线性关系也是一个好主意。在研究相关性时,重要的是不要误解它。
一般来说,高的正负系数表明强的正/负线性关系,而接近 0 的系数可能表明非线性关系。找出关系的类型有助于回归任务。

在这一节中,您还应该继续探索发行版。现在,你开始将它们相互比较,而不是单独看:

作者图片
比较变量时,使用概率质量函数、概率密度函数或累积分布函数。直方图在这方面很糟糕,因为你无法避免宁滨偏差和数据模糊。
#5.多元探索
顾名思义,是时候将 3 个或更多的变量结合成一个单一的情节。在这一部分,你的想象力是极限。

作者图片

作者图片
不过,有几件事你必须记住。首先,不要在一个图中包含 4 个以上的变量。不必要的变量不但没有用,反而给图表增加了额外的难度,使它们难以解释。
永远记住,你的情节不需要看代码就能被每个人理解。这是有效可视化的关键。
如果你正在犹豫是用大小还是颜色来表示第三个变量,那么选择颜色。人类的眼睛自然更善于区分颜色,而不是标记大小的微小差异。
如果你在考虑使用支线剧情或者 FacetGrids,尽量保持支线剧情的数量尽可能的少。它们可能对你有用,但是观众通常没有足够的耐心去看每一个。所以,把关键的支线剧情从群体中分离出来,在更大的尺度上进行剧情设计。
#6.EDA 的结论
最后,在单独的部分总结探索的要点。这是非常有用的,因为一些 EDA 往往很长,读者可能会在读到结尾之前忘记关键点(包括你自己)。因此,在进入机器学习之前,简要提及关键发现可以确保一切都记忆犹新。
我对 3 年机器学习新手的建议
不要像我一样浪费几天,几周,几个月

照片由this is 工程 发自 像素
我在一家初创公司做了 3 年多的机器学习。我们筹集了一轮资金,开发了一些很酷的技术。但是在这个过程中浪费了很多时间。
当你的公司的未来依赖于它时,学习 ML 并不是掌握 ML 最轻松的途径。
在这个过程中,我犯了很多错误。以及一些成功的例子。
这是基于轶事经验的建议。
虽然最适合想解决实际问题的人,但它适用于所有初学者。
开始了。
远离无监督学习
离远点。这是一个巨大的时间浪费。
尽管几乎每一个给我建议的人工智能博士都给了我建议,但无监督学习的多次尝试没有带来任何价值。我想这方面有很多学术研究。否则我无法解释这个悖论。
无监督学习是在无标签数据上训练模型。它通常涉及集群。理论上,这可以揭露以前未知的模式。
相反,监督学习学习输入和标记输出之间的关系。这是通过了解什么特征与什么输出相关联来实现的。
在我们的例子中,无监督学习每次都不如人类的直觉。
因此,虽然这个领域可能有一些很酷的应用程序,但这绝对不是轻而易举的事情。等你在别处有了经验再来谈这个。
跳过神经网络
我见过神经网络优于传统模型,但收益很小,所需的努力很大。
神经网络会带来一些挑战,尤其是在你职业生涯的起步阶段。
- 迭代缓慢。你的学习曲线是你尝试新事物的速度的函数。神经网络通常比传统模型需要更长的训练时间。所以迭代的时间就少了。
- **需要大量数据以避免过度拟合。**通常,这需要从事业务足够长的时间来收集大量数据,而大多数公司并没有预先标记这些数据。
- 选项太多。虽然逻辑回归的超参数组合是有限的,但神经网络可以以无限不同的方式配置。这个兔子洞比解决方案更可能让你迷失和沮丧。
- 传统的 ML 模型通常表现良好。对于一个 MVP 来说,从 sklearn 插入一个现成的模型通常就足够了。虽然数周的神经网络调整可能会给 f1 带来一些额外的积分,但在开始时往往不值得。
- **很难找到良师益友。**神经网络是一种奇怪的野兽。几乎任何人都可以告诉你他们是如何在高层工作的。但是很少有人有使用它们解决实际问题的经验。因此,你可能只能靠自己了。
总之,我不反对神经网络。但是用它们从 90 到 100,而不是从 0 到 1。
将所有问题框架为二元分类
让你的模型尽可能容易学习。最简单的问题是二元分类。
一个二元分类模型输出 1 或 0。照片里要么有一只狗,要么没有。
相比之下,多类分类根据照片中是否包含狗、猫、鹦鹉或鸸鹋,返回 0、1、2 或 3。
一次又一次,并行运行多个二进制分类器比处理所有情况的单个多类模型有更好的结果。
最大的收获不是选择了正确的模型,而是用正确的方式构建了一个问题。
调整您的超参数
这可以带来巨大的不同。
超参数是模型级配置。比如学习率。
使用自动化工具。有几个(即:GridSearchCV,TPOT…)。
你没有时间手动调整你的模型。设置一些调优界限,把你的实验推到云端。
亲提示。 *编写您的代码来挽救错误并定期保存结果。当云中的一个实验在 3 天后崩溃,没有保存任何结果时,我已经丢失了太多次结果。*
默认的超参数很少是最佳的。调整它们。
给尝试的时间框架,而不是结果的时间框架
ML 不是软件工程。
你无法预测解决一个问题需要多长时间,甚至无法预测这个问题是否可以解决。你能做的就是预测一个实验需要多长时间。
前者最终会让你陷入困境。对于一个公司的业务部门来说,没有什么比一个经常低估时间需求的工程师更令人讨厌的了。
这是一个简单的观点,但如果你是在工作中学习 ML,这是很重要的一点。
永远永远永远记录你的实验
六个月后你会感谢自己的。
我建议注意:
- 型号/架构选择
- 超参数
- 数据的粗略描述(来源、大小、日期、特征……)
- 结果(即:精确度、召回率、f1…)。
- 指向数据快照的链接(如果可能)
- 评论和学习
不要想多了。电子表格非常适合这种情况。
最终,首席执行官或新的顾问会要求你尝试你已经尝试过的东西。但是你不会记得上次为什么失败了。能够查找和呈现过去的结果将会节省你大量的时间和烦恼。
写事后总结(以及偶尔的成功汇报)也会让你学到更多东西。它会帮助你看到模式,建立你的直觉。从长远来看,这是让你成为“高级人才”的原因。
结论
这些是我花了几年时间构建 ML 驱动的应用程序后的一些心得。
虽然我的经验几乎只在 NLP 领域,但没有理由不能应用到其他领域。
如果我留给你什么的话,我希望是这个:前沿技术可以产生伟大的成果,但你可能还没有准备好。遵循那些经过实践检验的真理。然后在需要的时候突破界限。
现在走出去,建立一些有用的技术。
我学习数据科学新工具的最佳方法
原文:https://towardsdatascience.com/my-best-method-to-learn-new-tools-in-data-science-31941c2fc810?source=collection_archive---------24-----------------------
以及我是如何练习的

由 Unsplash 上的 Element5 数码拍摄
近年来,数据科学经历了巨大的发展。数据收集、存储和处理方面的进步促进了这种增长。
利用数据创造价值的潜力吸引了许多行业。越来越多的企业在其运营中采用了以数据为中心的战略和流程。
不断增长的需求也促使开发人员和开源社区为数据科学创造新的工具。因此,在数据科学领域工作的人有许多库、框架或工具来完成他们的工作。
其中一些工具被设计成用不同的编程语言执行相同的任务。有些人比其他人更有效率。有些人专注于一项特定的任务。不可否认的事实是,我们有许多工具可供选择。
你可能会争辩说,最好坚持使用一种工具来完成特定的任务。然而,我更喜欢至少有几个选择。我还想做一个不同工具之间的比较。
在这篇文章中,我将尝试解释我是如何学习新工具的。我的策略是基于比较。我关注的是如何用不同的工具完成给定的任务。
这种比较让我看到了它们之间的不同和相似之处。此外,它有助于建立一种直觉,了解这些工具的创造者是如何处理特定问题的。
假设我对 Python 中的 Pandas 库很熟悉,并且想学习 r 中的 dplyr 库,我尝试对这两个库执行相同的任务。
假设我们有以下关于营销活动的数据集。

营销(作者图片)
我想创建一个新的列,包含支出金额和工资的比率。这里是如何使用熊猫和 dplyr 来完成的。
#pandas
marketing['spent_ratio'] = marketing['AmountSpent'] / marketing['Salary']#dplyr
mutate(marketing, spent_ratio = AmountSpent / Salary)
让我们再做一个比较熊猫和 SQL 的例子。假设我们有一个包含杂货及其价格的数据集。

(图片由作者提供)
我们想计算每个商店的平均商品价格。这个任务可以通过 Pandas 和 SQL 来完成,如下所示。
#Pandas
items[['store_id','price']].groupby('store_id').mean()
price
store_id
-------------------
1 1.833333
2 3.820000
3 3.650000 #SQL
mysql> select store_id, avg(price)
-> from items
-> group by store_id;+----------+------------+
| store_id | avg(price) |
+----------+------------+
| 1 | 1.833333 |
| 2 | 3.820000 |
| 3 | 3.650000 |
+----------+------------+
这种方法让我更容易学习语法和掌握概念。它还帮助我在学习新工具的同时实践我已经知道的东西。
学习软件库和框架的挑战之一不是记住语法,而是知道何时应用哪种方法或功能。通过与我已经知道的进行比较来学习,选择适合给定任务的函数对我来说变得更容易了。
我用这种方法学习几乎任何工具。让我们也来比较一下 Python 的两个不同的数据可视化库。我已经知道了 Seaborn,想学牛郎星。
回想一下文章开头的营销数据集。例如,我们将创建一个散点图,比较女性和男性的支出金额和工资。
以下是 Seaborn 的做法。
import seaborn as snssns.relplot(
data=marketing, x='AmountSpent', y='Salary',
kind='scatter', hue='Gender', aspect=1.5
)

(图片由作者提供)
下面是如何用 Altair 完成的。
import altair as altalt.Chart(marketing).mark_circle().encode(
x='AmountSpent', y='Salary', color='Gender'
).properties(height=300, width=450)

(图片由作者提供)
如果你继续以这种方式学习,你会发现这些工具比你想象的更相似。过了一段时间,学习一个新的开始变得有趣而不是一个挑战。
我还比较了 SQL 和 NoSQL。大多数关系数据库管理系统使用 SQL(结构化查询语言)来管理以表格形式存储数据的数据库。NoSQL 指的是非关系数据库设计。它仍然提供了一种有组织的方式来存储数据,但不是以表格的形式。
我在 MySQL 中有一个简单的表,在 MongoDB 中有关于汽车及其价格的相同数据。对于每个品牌,我想计算 2019 年制造的汽车的平均价格。
下面是我们如何用 SQL 执行这个计算。
mysql> select make, avg(price)
-> from car
-> where year = "2019"
-> group by make;+---------+------------+
| make | avg(price) |
+---------+------------+
| BMW | 53000.0000 |
| ford | 42000.0000 |
| hyundai | 41000.0000 |
+---------+------------+
带 MongoDB 的 NoSQL 版本如下。
> db.car.aggregate([
... { $match: { year: "2019" }},
... { $group: { _id: "$make", avg_price: { $avg: "$price" }}}
... ]){ "_id" : "BMW", "avg_price" : 53000 }
{ "_id" : "ford", "avg_price" : 42000 }
{ "_id" : "hyundai", "avg_price" : 41000 }
结论
我学习新工具的最好方法是比较。我挑战自己,通过使用新工具来完成我知道的工具可以轻松完成的任务。这个方法对我来说非常有效。
过了一段时间,尝试新的库和框架变得很有趣。我不断地为不同的任务选择工具。但是,不妨碍我尝试新的。
我强烈建议至少在一个新工具上尝试这种方法。我认为它会让你的学习之旅变得更加轻松有趣。
感谢您的阅读。如果您有任何反馈,请告诉我。
我学习新数据科学工具的最佳方式
原文:https://towardsdatascience.com/my-best-way-to-learn-a-new-data-science-tool-b55dc0e36767?source=collection_archive---------27-----------------------
您需要一种有效的方法来采用丰富的工具选择。

马库斯·斯皮斯克在 Unsplash 上的照片
数据科学是一个跨学科领域,涉及许多不同的领域。然而,学习数据科学主要有两个方面。一个是理论知识,一个是软件工具。
没有合适的工具,我们就无法将想法付诸行动。它们允许我们从数据中提取洞察力。随着数据量的增加,工具的复杂性和种类也在增加。
不幸的是,没有一种工具能满足我们所有的需求。为了保持竞争力和高效运营,我们应该采用新工具。在这篇文章中,我将解释我学习新工具的策略。
让我们从数据科学生态系统中两个非常流行的工具开始:Pandas 和 SQL。如果你打算从事数据科学家和数据分析师的工作,你应该掌握它们。
我有一个数据集,其中包括墨尔本一些房屋的几个特征及其价格。它来自 Kaggle 上的墨尔本房产数据集。

(图片由作者提供)
考虑这样一种情况,我们需要计算不同类型房屋的平均价格。
以下是我们对熊猫的做法:
melb.groupby("Type").agg(Avg_price = ("Price","mean")).round()

(图片由作者提供)
我们可以对 SQL 进行同样的操作,如下所示:
SELECT type, ROUND(AVG(price)) AS Avg_price FROM melb GROUP BY type; type | avg_price
------+-----------
t | 933735
h | 1242665
u | 605127
这是一个非常简单的例子,展示了我如何练习学习新工具。我试着把它们和我已经知道的进行比较。这个比较包括做几个例子。
这种学习方式有两个主要优点:
- 因为我是基于我已经知道的工具进行比较的,所以我从学习我经常做的操作开始。它节省了我很多时间。比一下子全学会效率高多了。
- 这让我有机会进行高水平的比较。我对软件工具是如何设计的以及某些操作是如何安排的有了全面的了解。
这种比较也使我们更容易熟悉语法。尽管不同的软件工具可能有不同的语法,但常见操作的语法结构遵循相似的模式。
让我们做一个稍微复杂一点的例子。这次我们将添加另一个工具,即 r 的数据表包。
我们将计算不同类型房屋的平均价格和距离。我们还将添加一个过滤操作,以便只包括拥有 3 个以上房间的房屋。
熊猫:
melb[melb.Rooms > 3].groupby("Type").agg(
Avg_price = ("Price","mean"),
Avg_distance = ("Distance","mean")
).round()

(图片由作者提供)
SQL:
SELECT
type,
ROUND(AVG(price)) AS Avg_price,
ROUND(AVG(distance)) AS Avg_distance
FROM melb
WHERE rooms > 3
GROUP BY type; type | avg_price | avg_distance
------+-----------+--------------
t | 1211218 | 11
h | 1548914 | 12
u | 1067696 | 10
数据表:
melb[Rooms > 3,
.(Avg_price = mean(Price), Avg_distance = round(mean(Distance))),
by=Type] Type Avg_price Avg_distance
1: h 1548914 12
2: t 1211218 11
3: u 1067696 10
它们都提供了完成相同任务的有效方法。它们执行过滤、分组和聚合等特定部分的方式可能会有所不同。语法之间也存在细微的差别。
你可能会争辩说,如果这些工具可以执行相同的操作,我们就不需要学习所有这些工具。选择可能不总是取决于你。如果您的公司使用 R 编程语言进行开发,那么您可能需要使用数据表包,而不是 Pandas。
在某些情况下,某个特定的工具比其他工具具有某些优势。例如,如果一家公司将数据存储在关系数据库中,用 SQL 执行数据分析和操作是最可行的解决方案。
数据科学生态系统中有丰富的工具可供选择。当您处理大规模数据时,像 Pandas 和 SQL 这样的传统工具可能不是最佳选择。在这种情况下,您可能需要使用 Spark 等支持分布式计算的工具。
结论
近年来,数据科学获得了极大的普及。促成这种流行的原因之一是软件工具的进步。多亏了这些工具,我们能够非常容易和快速地处理数据。
由于工具选择丰富,数据科学家面临着立即学习新工具的挑战。我认为通过比较学习是采用新工具的好方法。
感谢您的阅读。如果您有任何反馈,请告诉我。
作为一名自学成才的数据科学家,我最大的挑战是
以及我是如何克服它们的

菲利帕·罗斯-泰特在 Unsplash 拍摄的照片
伴随着互联网的兴起,自学也越来越多。在 21 世纪,自学从未像现在这样容易,这得益于通过互联网在全球范围内传播的大规模开放式在线课程(MOOCs)。这些课程越来越受欢迎,以至于有些人甚至开始质疑影响了企业界许多企业招聘策略的学术体系的可信度。
我个人已经承担了相当一部分 mooc——现在仍然如此。毫无疑问,我同意这些课程是无价的,至少其中一些是。没有双关的意思。
https://medium.com/analytics-vidhya/courses-to-learn-data-science-in-2021-a52e64344e5c
对于像我这样的大多数人来说,我们做的大多数事情都是因为我们不得不做;
当我在学校的时候,我做了我的家庭作业(我的一些老师不敢苟同),因为我不得不这样做。
我找了份工作,因为我不得不。
每次训练结束后,我都会清洗我的足球鞋,因为我的靴子太脏了——在大多数职业俱乐部,脏靴子是一种罚款。
我们都在生活中有要做某些事情,不做这些事情会带来不利于我们真正想要做的事情的后果。
自学打破了这个轨迹。没有人告诉我们做什么,如果我们不做,也不会有任何后果。我们学习新的科目通常是因为我们想学,而不是因为我们必须学。随之而来的是它自身的主要障碍,这些障碍阻止了许多人进行跳跃。
没有方向感
当我第一次踏上数据科学之旅时,我知道自己想要实现一些明确的目标。例如,我知道我想成为一名数据科学家,我知道我想用我学到的技能帮助人们,我知道我想擅长数据科学。本质上,我知道我想要什么,但我从来不知道如何到达那里,如何衡量我是否到达那里。
想想吧…
一个人究竟如何衡量自己是否是数据科学家?—我知道有些人会说“找到工作后你就是数据科学家”,但这是否意味着每个因 Covid 而失业的数据科学家都不再是数据科学家,因为他们没有工作?显然不是一个好的衡量标准。
我不知道一个优秀的数据科学家是什么样的,在我参加的任何课程中都没有教授过。所以,我会花更多的时间学习不同的课程,试图找到我所缺少的东西。我读了一本书又一本书,一旦读完,我就会意识到我所知甚少,因此促使我寻找新书。
这变成了一个恶性循环,我没有完成任何实际工作。
解决方案:我总是发现与在该领域做大事的更资深的人联系会有所帮助。对我来说幸运的是,我遇到了哈普瑞特·萨霍塔,从那以后我一直在关注他的工作和他所支持的领域里的人们。他还主持每周办公时间,在那里,一群各种水平的数据科学家聚在一起,谈论对我来说具有革命性意义的数据科学。
孤独
决定学习数据科学与我周围的人和我所习惯的大相径庭。我家里没有人有编程技能,我很确定我周围的每个人都会选择足球比赛而不是数学课。
当没人理解你的工作时,这是相当疯狂的…
鲍勃:嘿,伙计!你现在在做什么?
我:我是一名数据科学家。
鲍勃:
大多数对话都是这样进行的。
每当我与数据科学领域之外的人交谈时,我发现大多数时候我都在解释数据科学家到底是什么以及我们做什么。
再补充一点,每当我遇到问题时,绝对没有人可以让我大声抱怨或寻求帮助,他们离我至少有一个小时的路程(就旅行距离而言)。这并不是说在网上没有支持你的媒介,但有时,我觉得如果我有一个可以不时打电话和聊天的朋友会好得多。疫情也没帮上忙。
不要误解我。我在数据科学社区交了很多新朋友,但他们大多数生活在不同的国家,处于不同的时区。而且,在目前的气候下,我不可能赶飞机去看他们。
解决方案:对我帮助很大的是找到了新的爱好,我可以用它来将我的注意力从工作中分离出来,并以一种全新的思维重新审视它。我完全迷上了健美操。去年我开始做自由式健美操,从那以后我认识了很多新朋友——要么是想学一些技巧的人,要么是教我的人。不管怎样,只要有可能,和真人面对面交流总是很酷的。
其他数据科学家的恐惧
这听起来很傻…我害怕与其他数据科学家进行技术讨论。尤其是那些更资深的人。我总是认为他们比我更有资格(老实说,他们确实是),因此也更有资格…
另外,你听过一群数据科学家说话吗?我第一次听到的时候,我发誓他们说的是克林贡语。
我猜这种不安全感源于某种形式的冒名顶替综合症——一种你不相信自己像别人认为的那样有能力的表现。以前,我总是避免与高级数据科学家进行技术交流,在我不能这样做的事件中,我总是尽可能少说话(换句话说,什么都不说),即使我不同意他们说的一些事情。
解决方法:怀疑自己已经够糟糕了,但是拿自己和别人比,完全是对自己存在的不尊重。对此,我的最佳解决方案是把自己置于这样一种境地:我不得不与比我了解更多的人交谈。从字面上来说,变得舒服不舒服——阅读下面链接的文章获得更详细的分类。
</5-hacks-to-overcome-data-science-imposter-syndrome-47cc45032e96>
最后的想法
成为自学者的最大困难是克服你给自己设置的限制。你脑子里所有消极的自我对话。我坚信“任何人都可以学习任何他们想学的东西,如果他们决定去做的话”,但这首先要打破阻碍你进入下一个层次的精神障碍,这是第一步。
感谢您的阅读!在 LinkedIn 和 Twitter 上与我联系,了解我关于数据科学、人工智能和自由职业的最新帖子。
相关文章
</4-data-related-books-ill-be-reading-in-april-efd06b367e35> https://medium.datadriveninvestor.com/the-road-to-my-first-1000-month-from-blogging-891099362482
作为一名高中生,我在构建基于 NLP 的产品时遇到的挑战
原文:https://towardsdatascience.com/my-challenges-in-building-an-nlp-based-product-as-a-high-school-student-f0b8f2e85edc?source=collection_archive---------39-----------------------
以及我是如何设法解决这些问题,而没有花掉我根本不存在的积蓄中的一毛钱

文森佐·迪乔尔吉在 Unsplash 上拍摄的照片
介绍
作为一名 18 岁的学生和 NLP &深度学习的热情爱好者,学习曲线对我来说是真正无价的。我积累并获得了大量的技术技能。然而,这段旅程并不是一条直路;它充满了颠簸、弯路和碰撞。
对于这篇文章,我想强调我所面临的一些主要困难,特别是选择构建一个大量包含人工智能(确切地说,是深度学习和自然语言处理)的辅助项目。虽然这是一个被过度使用的时髦词,可能会给你那些一无所知的朋友留下深刻印象,但我决定利用 NLP &深度学习,因为我看到这是一种解决我试图解决的问题的合适方法。
然而,作为一名破产的学生,深度学习融入初创公司的成本非常高,这也于事无补。因此,本条的目的是:
- 帮助人们评估将深度学习模型融入他们的辅助项目的好处和挑战
- 向边项目布道者和独立黑客展示你如何通过一些巧妙的策略来克服这些挑战
事不宜迟,我们开始吧!
挑战 1:基础设施成本

照片由伊万·班杜拉在 Unsplash 上拍摄
这个问题可能是很多 AI 创业公司面临的最常见也是最麻烦的挑战。训练深度学习模型是一回事;确保它们在生产中正确快速地运行是另一个问题。对于深度学习模型,大多数时候需要 GPU 来获得快速响应时间,如果你不知道,GPU 服务器可能会变得极其昂贵,平均每小时执行 GPU 的成本为 0.8 美元。无法解决的问题,对吧?良好的..
我的解决方案:无服务器容器(GCP 云运行)

弗兰克·麦肯纳在 Unsplash 上拍摄的照片
是的,没错,无服务器容器。构建在 Knative 之上,Cloud Run 本质上允许你将所有代码&打包成一个无状态的容器,然后通过 HTTP 请求调用它。与云功能不同,云功能只是作为一个功能来部署,并且语言支持有限,您可以使用您选择的任何语言,并且可以通过简单地将代码封装到容器中来轻松部署。大概 Cloud Run 的伟大之处在于,你每月可以获得200 万个免费请求。多棒啊。
通过利用 Cloud Run,我能够轻松地将我的自然语言处理模型及其依赖项部署到 GCP 上,为自己节省了购买 GPU 硬件的巨大成本。
限制
当然,云跑也有弊端。首先,它是无服务器的,这意味着如果它在一段时间内不活动。这意味着,如果没有请求需要处理,云运行将在一段时间后终止未使用的容器,导致冷启动仍然可以。
此外,如果你正在将一个大型深度学习模型包装到一个容器中,这显然会导致延迟,特别是如果你在 CPU 上。
可能的解决方案
不要害怕,因为你总是可以尝试缓解这些问题!
对于冷启动问题,您可以设置一个云调度程序 cron 作业来定期 ping 端点。或者,您可以设置一个“热容器”,或者一个总是保持一个容器活动的选项。然而,这将产生额外的费用。
不幸的是,对于推理时间,您必须为运行云运行的 GPU 付费。因此,我真心推荐 Cloud Run 来建立一个激情项目/MVP,但是如果你真的对你的产品很认真,那么你将需要投入一些比索。
资源
- 谷歌云运行主页
- 比较谷歌的无服务器产品
- 优化谷歌云响应时间的 3 种方法
挑战 2:为工作寻找合适的模型

安德烈·德·森蒂斯峰在 Unsplash 上拍摄的照片
为了更好地展示我的产品,我的想法是让学生拍摄笔记的图像,让我的模型扫描文本并产生问题。我的挑战是找到一个好的模型来解决问题生成的挑战,这是 NLP 中相对未探索的领域,即使用 NLP 技术从文本块中生成问题的过程。
不幸的是,我找不到太多关于这个主题的博客或文章,也没有太多关于流行的 NLP 库(如 Huggingface)的工作。所以,朋友,你做了什么?
我的解决方案:研究论文
是的,我知道,枯燥冗长的论文充满了复杂的词汇和看起来复杂的公式。我不是说它们容易阅读。但是,我确实相信它是一种很好的技能,而且,像大多数技能一样,它可以通过练习和努力获得。
最初,我努力阅读关于自然语言处理的研究论文。然而,随着我开始阅读越来越多的论文,我开始进一步理解所解释的过程和方法,并开始实现论文中描述的问题生成模型的原型。我做到这一点的一个方法是阅读一段文字,并尝试用我自己的话总结它所描绘的内容。你也可以建立一个 NLP 总结模型来做这件事(看,有一个很酷的激情项目!)
这使我不仅能够实现论文中的数学并将其转换成代码,而且我还能够发现 Github 仓库中隐藏的宝石,其中一些研究人员已经编写了他们方法的概念证明,并允许我使用并进一步优化他们的代码以满足我的需求。多酷啊,对吧!
限制
当然,研究论文也有局限性,但这些局限性更容易减轻。
首先,值得注意的是,这些是研究人员,而不是软件工程师,因此坚实的原则和代码质量并不总是他们的主要关注点。
此外,一旦他们实现了概念验证,他们往往会离开代码,导致过时的代码&不能被正确使用的包。
最后,一些研究人员忽略了正确地记录他们的代码,所以可能很难在您的机器上得到一个工作的演示。
可能的解决方案
对于第一个问题,我发现这不是一个障碍,而是一个挑战,看看我能优化代码有多好,我鼓励你也这样做,因为这将帮助你提高你的编码技能,以及你消化他人代码的能力。
对于第二个问题,您总是可以创建一个虚拟环境,并使用旧的包和代码来启动和运行一个工作演示,稍后您可以相应地更新代码
对于第三点,为什么不通过扫描代码库来尝试自己记录代码呢!每一次挑战都是一次机会!
资源
- Arxiv
- 关于如何阅读研究论文的好文章
挑战三:模特培训

安妮·斯普拉特在 Unsplash 上的照片
我找到了一个日期设置,我相信我可以根据研究论文对我创建的现有模型进行微调和优化。然而,我很快意识到模特培训不是一个便宜的问题;事实上,这比我面临的任何问题都要昂贵。对我来说,期望的结果是能够在 GPU 上训练我的模型,并且不用花一分钱就能节省重量…
我的解决方案是:Google Colab
对于深度学习任务,尤其是训练 NLP 模型,Google Colab 是一个真正高超的工具。它本质上是一个托管的 Jupyter 笔记本,不需要设置,有一个优秀的免费版本,可以免费访问谷歌计算资源,如…GPU 和 TPUs!我以为我找到了 21 世纪的终极黑客;只需将模型的代码转储到 Colab 笔记本上,训练&在 GPU 上保存重量,并且不花一分钱!唉,要是有那么容易就好了!
限制
Colab 最大的限制可能是它在长时间不活动后会断开连接。它应该在 12 小时后断开连接,但我发现它在不活动 2 小时后就断开了我的连接。我想你不会想知道当它在本该节省重量的代码单元前断开我的模型时我是什么感觉!
可能的解决方案
通过创建 JavaScript 函数来模拟用户点击,有许多“黑客”方法来欺骗 Colab 认为你是活跃的。然而,即使使用了这些功能,我仍然无法连接。
我认为解决这个问题的三个办法是要么坚持 12 个小时,但我相信你有更好的事情去做!
你也可以使用 Kaggle,我在那里连续训练了我的模型 9 个小时。这可能是最好的解决方案,因为他们也有 GPU 支持。
最后,你可以随时升级到 Colab Pro,这对于他们提供的价格来说是一笔很大的交易!
资源
- 卡格尔
- Google Colab
- 使用 JavaScript 阻止 Google Colab 断开连接
结论
本文的目的不是向您展示我在尽一切可能避免支付额外资源方面有多吝啬,而是揭示我在构建基于 NLP 的辅助项目时所面临的一些有趣的挑战。我相信,这些方法肯定可以让那些为了不花太多钱而只是尝试和构建 NLP 辅助项目的人受益,但是,如果你更认真地构建一个产品,那么我建议你为额外的资源付费。
我希望你觉得我的发现很有见地,如果你在使用创新解决方案时遇到任何挑战,请在评论中告诉我!
直到下一次,和平!

照片由克里斯蒂安·威迪格在 Unsplash 上拍摄
我的干净整洁数据清单
原文:https://towardsdatascience.com/my-clean-and-tidy-checklist-for-clean-and-tidy-data-fbdeacb3736c?source=collection_archive---------45-----------------------
数据争论(获取、清理和转换数据)并不总是数据科学中有趣的部分,但它是在现实世界中处理数据的主要部分

穿着得体。来自 Pexels 的马蒂尔达·艾文的照片
在我们开始使用我们刚刚学习的令人敬畏的新算法之前,我们需要确保我们拥有数据,我们已经修改或删除了任何有问题的条目,并且我们已经将数据转换为我们可以使用的形式。有什么比使用一个方便的清单更好的方法呢?因为说真的,谁不喜欢清单呢?看,没有人。
我的清单由 3 个主要部分组成,文档部分贯穿所有部分:
- 首先,我们必须关注我们的外部资源,那些我们无法真正控制的东西,这样它们就不会阻碍我们前进。
- 接下来,我们需要了解我们面对的是什么。
- 之后我们就可以自由地做实际的工作了!
→一定要记得全程记录——把它当成你现在和未来的自己。

作者图片
外部因素

他们就在我们中间。迈克尔·赫伦在 Unsplash 上拍摄的照片
- 验证您的数据。在这个项目中,我的初始数据来自组织的另一个部门,他们已经做了一些计算。在工作了一段时间后,我注意到有些东西不对劲,这让我进行了一次彻底的 QA,在 QA 中我发现大多数计算基本上都是错误的。我应该从现实检查开始,但是我得到了教训——不要相信别人的数据!
- 验证您的系统。如果您的工作依赖于来自组织内部或(尤其是)外部的 API,您的第一步行动应该是检查它是否已启动并运行,您是否可以访问所有内容,等等。我们被延迟了一个月,因为一个合作伙伴试图让他们的 API 启动并运行,当你有一个客户截止日期时,这并不好玩。镜像之前的情绪——不要相信别人的系统。
- **重读你的资料。**如果你正在从其他人的笔记/笔记本/代码中学习,确保在你的项目工作一段时间后回到他们那里。一旦你更熟悉项目/数据/工具,你会注意到并理解你第一次错过的东西。
谅解

由 Rob Schreckhise 在 Unsplash 上拍摄
- 积累你的领域知识。很有可能你正在研究一个你不太了解的课题。对我来说,这是零售。什么是 UPC ?它看起来像什么,不同的数字是什么意思?不同的零售商如何组织他们的货架?
一些信息可以在维基百科中找到,在其他情况下,你需要钻研你的项目数据或相关数据(在我的例子中,是书架图片)。我发现熟悉这个领域在整个过程中帮助了我——清理数据、编写代码(和发现错误),以及向客户展示最终的解决方案。 - **了解你的交付成果是什么。**你试图用你的数据回答哪些问题?你打算怎么回答他们?您是要运行模型并在与客户的会议上展示结果,还是给他们发送 csv 文件,还是要编写一个将在生产中运行的算法?
这将帮助您决定如何处理您的数据——如何准备数据,如何处理丢失的数据(删除/完成数据),在所有转换后您希望数据采用何种格式。 - 探索数据。 这里的是一些让你入门的好代码行。每个(字符串)列中的唯一值是什么?每种有多少?查看来自列的随机数据样本,以了解数据的外观。
例如,我想保存一些带前导零的数字,所以我将它们作为对象存储。但是后来在这个过程中,我将数据转换为 int,并对我拥有的组的数量感到惊讶。事实证明,同一个数字可能有不同数量的前导零。哎呀。
进入正题

JESHOOTS.COM在 Unsplash 上拍照
- 清理那些数据。你需要处理缺失值、离群值、重复值等等。这本身就是一个完整的世界,这些是一个开始的好地方。
- **验证您的数据转换。当你转换或合并数据时,总是要停下来检查结果是否如你所愿。例如,结果数据帧具有预期的行数(而不是您认为它应该具有的行数的 3 倍)。举几个例子,手动检查结果是否正确。这很烦人,但比以后意识到错误要少得多。
合并时您可以使用的一个有用工具是指示器标志,它告诉您结果数据是从左侧、右侧还是两个数据帧合并而来。** - 构建一个小沙箱进行实验:当你试图对大型数据帧进行复杂运算时,不要试图在真实数据集上计算出你的算法。只是混乱又缓慢。就像你对代码进行单元测试一样,把一个小例子和相关的用例放在一起,然后继续工作。一旦成功了,就在真实数据上试试——正如我们之前提到的,确保验证你的结果。
证明文件

但是要让它可读。马克·拉斯姆森在 Unsplash 上拍摄的照片
**文档!**保留一个文档,在其中写下任何见解、想法、相关数据的链接等。你所遇到的。当你想最终得到一个演示文稿时,或者如果你想把项目交给某人,或者如果客户几个月后有问题时,它会很有帮助。使用笔记本上的 markdown 来解释你正在做的事情。既然都是商业逻辑,过几个月就会有人(或者未来的你)感谢你。
轮到你了
你可能会注意到,在所有这些艰苦的工作之后,我们差不多是 Kaggle 比赛开始的地方。它们是研究算法的一种很好的方式,但它们往往带有干净的数据,不太像现实世界中生产系统的模糊现实。
如果你想动手实践数据清理,这里有一个非常棒的数据集列表需要一些爱来为工作做准备,以及需要做些什么来让你前进的亮点。
让我总结一下
这是我们最后的清单
就像我们的数据一样整洁:
- 验证您的数据
- 验证您的系统
- 重读你的资料
- 建立你的领域知识
- 理解你的交付物是什么
- 探索数据
- 清理数据
- 验证您的数据转换
- 建造一个小沙箱进行实验
- 文档!
现在你的数据已经整理好了,你可以进入大多数人最喜欢的部分——算法。只是不要忘记,没有闪亮的算法会完全弥补糟糕的数据!
我目前最喜欢的 Power BI 功能
原文:https://towardsdatascience.com/my-current-favorite-power-bi-feature-d2dce14d007b?source=collection_archive---------34-----------------------
“在 Excel 中分析”选项改变了游戏规则

卢卡斯·布拉塞克在 Unsplash 上的照片
如果你是一名数据分析师,那么你可能有一个项目,其中每个人都希望以不同的方式查看相同的数据。当然,您可以用新的视图创建新的页面,但是在一天结束时,有人可能会带着一个稍微不同的用例回来,需要另一个图表或图形。报告开发会结束吗?
简介 Power BI 中的“在 Excel 中分析”功能。是什么让这个特性对最终用户和开发者如此重要?很高兴你问了。
临时数据透视表创建
使用 Power BI 报告背后的数据,最终用户可以在 Excel 中创建自己的数据透视表。这消除了诸如“您能把这两列添加到图表中吗?”除了包括所有数据列之外,该文件还包括所有计算的度量,这使得业务分析师或任何对 DAX 代码缺乏经验的人都可以轻松掌握分析的强大功能。
如果仪表板的用户需要一次性报告的数据,那么让他们使用生成的 Excel 文件会特别方便,因为开发人员不必创建只使用一次的新页面。
实时数据
可以刷新由此功能创建的 Excel 文件,以便从 Power BI 报告中获取最新数据。只需右键单击→刷新即可。因此,业务经理可以创建他或她理想的数据视图,当下一个数据刷新周期到来时,他或她可以刷新报告,使其包含最新和最好的数据。

照片由马库斯·温克勒在 Unsplash 拍摄
Excel 专业人员的熟悉程度
Excel 可能是商业专业人士使用起来最舒服的数据分析工具——至少对于小型数据集是这样。有些人可能不会很快使用 Power BI,或者他们可能已经花了很多时间在 Excel 文件中创建出色的分析。无论哪种方式,演示 Excel 中的分析功能都有助于那些不愿意学习新工具的人采用您的仪表板。
此外,用户可能希望将他们自己的数据合并到您的数据中,他们的数据可能在……您猜对了——Excel 中。此功能允许用户将其所有报告保存在一个文件中,或者使用 VLOOKUP 等公式将其数据集与 Power BI 仪表板背后的数据相结合。
您也可以制作数据透视图
在下面的截图中,您可以看到,一旦您单击插入数据透视图,您可以选择“使用外部数据源”,当您选择连接时,Power BI 连接已经可供您选择。

作者图片
提示和技巧
你准备好尝试这个功能了吗?以下是我在玩耍时学到的一些东西:
- 如果您希望某人能够使用 Power BI online 中的“在 Excel 中分析”按钮,您需要检查下面的第二个设置,以允许他们使用数据构建内容。

作者图片
- 如果您正在生成的 Excel 文件中设计您的数据透视表,您可以选中右下角的“延迟布局更新”(见下面的截图)。如果不这样做,文件将在每次对表进行更改后运行一次查询,如果添加一个包含大量唯一值的列/行,这可能需要一分钟左右的时间。

作者图片
- 您只能将 Power BI 度量放在数据透视表的“值”部分。因此,即使对于简单的公式,如 sum,您也需要创建一个度量值,以使数据透视表中的字段可求和。
- 保持表的组织性以及确保列名和度量值信息丰富非常重要,因为用户在使用文件时将能够看到所有的表和度量值。
感谢阅读!如果你有一个最喜欢的 Power BI 功能,或者你尝试了这个功能,请在评论中告诉我。
“我的数据漂移了。接下来是什么?”如何处理生产中的 ML 模型漂移?
原文:https://towardsdatascience.com/my-data-drifted-whats-next-how-to-handle-ml-model-drift-in-production-78719ef007b1?source=collection_archive---------6-----------------------
提示和技巧
可能步骤的介绍性概述。

图片作者。
“我有一个生产中的模型,数据正在漂移。怎么反应?”
这是我们经常遇到的问题。
这个 数据漂移 **可能是唯一的信号。**你在预测什么,但还不知道事实。模型输入和输出的统计变化是代理。数据发生了变化,您怀疑模型性能下降。
**其他情况,可以肯定知道。**您可以计算模型质量或业务指标。准确性、平均误差、欺诈率,应有尽有。性能变差了,数据也不同了。
接下来你能做什么?
下面是可能步骤的介绍性概述。
1.检查数据质量

图片作者。
如果你幸运的话,你有标签。应该盲目打“再培训”吗?
暂停一下可能是个好主意。
**数据漂移表示发生了变化。到底是什么?**来看看吧!
当我们谈到数据和预测漂移时,我们通常会想到模型相关性。这个模型仍然适合这个任务吗?是否在熟悉的环境中操作?
假设我们看到来自一个新地点的大量用户。他们有新的行为模式和特征。数据分布发生了变化。这就是我们所说的“真正的”数据漂移。
它可能会影响预测转化率或个性化营销提议的模型的性能。

样本特征分布图(1)。是的,我们画了这个,但是你显然可以使用开源 Python 库自动生成:)
但是假设在代码更新期间引入了一个 bug。因此,我们停止记录一些现有位置的数据。分布也会改变。

样本特征分布图(2)。图片作者。
那是伪装成数据漂移的数据质量问题。
数据输入错误、模式改变、上游模型问题——我们已经列出了数据可能出现问题的清单。
理想情况下,您应该对数据质量和完整性进行单独检查,并及时做出反应。您可以监控缺失数据、范围符合性等。
如果设置了这两种类型的监视,数据漂移和数据质量警报可能会同时触发。在这种情况下,数据质量检查具有优先权。
我们先把数据搞定。
您可以再次从数据源获取数据,重写连接器,解决日志问题,更新模式或特性转换代码,等等。
在这种情况下,不需要重新训练或更新模型。模型是好的,数据不是。
数据质量几乎总是首先要考虑的问题。
2.调查漂移

图片作者。
但是如果漂移是“真实”的呢?
该模型在动态环境中运行。事情变了。你可能会有数据漂移,概念漂移,或者两者同时存在。
下一步是试图找到现实世界的罪魁祸首来解释它。
首先,您可以绘制漂移要素的分布。

中的样本数据漂移报告明显在界面。(图片由作者提供)。
目标是理解和解释变化。
让我们回到用户突然涌入的例子。这是新的营销活动的结果吗?我们是否在新的地点推出了一款应用?还是单纯的垃圾流量?
当然,这可能会变得棘手。
我们并不总是能够接触到现实世界转变的“源头”。我们所拥有的特征通常是它的不完整表现。
例如,竞争对手的活动可能会影响用户行为。但是模型特征可能不包含此信息。
与领域专家合作可能有助于找到可能的解释。
个人特征也不总是能说明全部情况。例如,如果我们处理概念漂移,它们的分布可能保持相似。但是关系会发生变化:在特征之间,或者在特征和模型输出之间。
可以目测一下。
**首先可以看输出分布形状本身。**如果你有实际数据,你可以看到事实是如何变化的。如果你只有模型预测,你可以看看。

来自的样品目标分布测试显然是目标漂移报告。(图片由作者提供)。
你可以看一下输入和输出分布,然后就此打住。
或者,你可以继续挖。
例如,绘制单个特征和模型预测之间的相关性。我们并不总是期望线性关系,但这个简单的技巧往往可以提供有价值的见解。
不幸的是,没有简单方便的方法来绘制复杂的多维交互。

模型特征和目标值的相关图示例。(图片由作者提供)。
在上面的例子中,你可能会怀疑网页浏览量和转化率之间的突然变化是由垃圾邮件或低质量的流量引起的。
用例很重要吗?你或其他人能很好地解释这些数据吗?还有更多的东西可以看。
例如,您可以绘制成对特征相关性的变化。

样本成对特征相关图。(图片由作者提供)。
领域专家甚至可以用肉眼发现变化。例如,制造工程师可能会注意到在物理意义上不存在的相关性。只有特定的设备故障模式才能解释这一点。
你的用例可能有一些类似的东西。
如果可以的话,理解这个语境有助于通知下一步。
3.什么也不做

图片作者。
你对漂移有想法。哪些特征在漂移,有多少,有多强?也许,你甚至抓住了背后的过程。
下一步是什么?
你需要决定观察到的漂移是否真的有意义。这是你应该做出反应的事情吗?
在理想情况下,我们设置漂移检测阈值时会考虑到这一点。我们选择要查看的关键特性,选择合理的统计测试,并调整置信水平。我们希望对重要的事情保持警惕。
实际上,设置漂移监控通常是通过反复试验来完成的。
如果您在生产中收到漂移警报,这通常是一个学习练习。需要反应吗,还是假阳性?
你可能会看到漂移的来源,并决定接受它。
这里没有捷径—您需要来自了解用例及数据的数据科学家的专家判断。

图片作者。
也许标签很快就会到了。让我们等一会儿。
在那之前,你可以简单地决定接受较低的模型性能。对于不太重要的模型,这可能是一个理性的决定。
也许,胜负难料。让我们密切关注。
您可以决定在接下来的模型运行中更仔细地查看数据。您可以计划一些附加报告或添加一个仪表板来跟踪特定功能。
也许,这是一个错误的警报。让我们来调整监控。
您可以放弃通知。例如,如果您不想在下一次发生同样的事情时被打扰,您可以更改漂移警报条件或使用的统计测试。
在其他情况下,您可能会对模型对漂移的反应感到满意。
假设您看到某个特定类别在模型预测中变得更加普遍。但这与观察到的特征漂移非常吻合。
例如,获批贷款申请的增加是随着高收入申请人的增加。它可能看起来像特征和预测漂移,但与您预期的行为一致。
当然,事情并不总是这样。
如果您认为需要采取行动,让我们来看看这些选项。
4.如果可以的话,重新训练它

图片作者。
一条简单的出路。
这仅适用于新标签存在的情况。但往往你有,或者能得到它们!
在某些情况下,我们出于方便监视数据漂移。我们在机器学习服务中直接拥有了我们所需要的一切:进入的特征和模型预测。
地面实况标注或实际值可能会延迟一段时间到达,或者驻留在与预测不同的数据库中。
您可能需要一个单独的 ETL 作业来获取这些新数据。
如果您检测到漂移,是时候触发这个半人工数据准备过程了。导出,执行连接,并为模型重新训练进行清理。
在其他情况下,你可能需要先标记数据。
贴标机专家可能就在公司内部。但是他们的时间很宝贵,你不能要求他们一直查看模型预测。你按要求触发它。
有时,您会聘请外部团队或服务提供商。漂移检测表明是时候标记新一批数据了。让模特学习变化的图案!
5.校准或重建模型

图片作者。
对于那些幸运拥有这些标签的人来说,这是另一种选择。
如果你面临重大改变,幼稚的再培训往往是不够的。我们已经在过去的博客中讨论过这种差异。
**通过再培训,您可以重复现有的模型培训渠道。**您保留特征工程步骤、模型类型、超参数等等。您只需更改培训数据。
您可以向现有数据集添加一组新数据,或者仅对新数据集重新运行管道。或者做一些组合,新旧混合。旁注:你可以提前测试来选择最适合你的用例。
说这就是我们所做的。我们“重放”了模型训练。但是更新后的模型在验证集上的表现并不理想。
要重建或校准模型,您可以对培训管道进行更多更改。
您可以从头开始重新运行模型实验,测试新的模型架构、参数或特征处理方法。
你也可以测试一些更具体的想法来对抗漂移:
- 重新加权训练数据中的样本,给予最近的样本更大的重要性。目标是让模型优先考虑更新的模式。
- 确定模型失败的新细分市场,并为其创建不同的模型。考虑对不同的数据段使用几个模型的集合。
- 改变预测目标。例如,从每周预测切换到每天预测,或者用从“高”到“低”的分类来替换回归模型
- 选择不同的模型架构,以应对持续漂移。您可以考虑增量学习或在线学习,其中模型不断适应新数据。
- 应用领域适应策略。有许多方法可以帮助模型更好地推广到新的目标领域。
和数据科学一样,没有任何保证。
有时主要的概念漂移会完全破坏模型。想象一下,你在生产线上从事质量预测工作,它刚刚进行了一次改造。这使得模型学习过时了。
您可能需要等待收集更多新数据,直到您可以恢复机器学习系统。
如果机器学习不起作用,你还有什么其他选择?
可以考虑一个非 ML 的解决方案。其他健壮的东西:基于对因果关系的理解、过程的物理或专家知识。
6.暂停模型并使用回退

图片作者。
让我们画一幅画。
我们知道存在模型漂移。分布发生了变化,或者预测太离谱,或者两者兼而有之。但是我们没有更新模型所需的标签!
也许,他们将在 30 天后到达。或者我们有标签,但是数据量不足以更新模型。如果模型错误有风险或代价高昂,我们不能听之任之,容忍不可靠的预测。
这是一个激进的选择。
我们根本不用模型!
我们可以简单地关掉它。例如,在应用程序中隐藏推荐块。
在许多情况下,您可以有替代的回退策略。例如,如果模型没有响应,可以求助于它。

图片作者。
人类专家的决定就是其中之一。很有可能,一个 ML 模型被设计来帮助某人做可重复的决定。
但是如果模型没有帮助,你仍然可以依靠专家的判断。想象一下保险索赔处理、制造质量控制或销售线索评分。
你可以要求用户自己采取行动:就像 Twitter 在发现他们的图像裁剪算法可能有偏差时所做的那样。他们将控制权交还给用户。
**启发式和规则是另一种。**你通常可以设计一套规则,虽然不太精确,但比流氓模型更健壮。
例如,您可以简单地向所有客户显示最受欢迎的商品,而不是随机和不匹配的推荐。
**在某些领域,你可以切换到另一种模式类型。**你可以使用金融领域的经典评分模型。你可以使用统计模型来进行医院的病人分类。您可以在制造过程控制中使用第一原理物理模型。
如果您正在处理一个高风险的领域,最好从一开始就设计这样的回退。
7.找到表现不佳的部分

图片作者。
对于某些模型预测,您可能只会点击“暂停”。
通常,这种变化并不普遍。它可能只影响一部分人。或者,一个新出现的细分市场本身就是变化。
如果你能定义低性能的部分,这给了你新的选择。您不会完全停止模型应用程序。但是,您可以对某些数据输入进行限制。
即使你没有地面真相标签,这也是可行的。
这里有一个例子。
假设我们收到了营销个性化模型的数据漂移警报。我们开始调查。我们看到模型预测和一些特征的转变。
我们最喜欢的“位置”类别又有了不同的分布。这为我们提供了一个假设来测试:所有的变化都可以归因于新的区域吗?
一些位置类别是新的。有些在训练中很少见,但变得越来越频繁。
我们过滤我们的数据,看得更远。

图片作者。
也许这意味着丢弃大约 40%的数据。现在让我们看看还剩下什么!
事实证明,其余的分布是稳定的,类似于训练。按位置过滤有助于我们隔离所有新奇的东西!

图片作者。
然后,我们可以决定预测路线。
对于频繁的、已知的用户位置,我们可以依赖该模型。对于其余的,我们可以设计一些启发。在一个个性化的例子中,营销团队可能会提出人工策划的推荐,或者我们可以求助于“最受欢迎的”列表。
在制造过程控制示例中,我们可能会发现漂移与新的原材料供应商有关。
当生产批次被使用时,我们可以挑选出来。对于这些更复杂的情况,我们可以依靠运营商的决定。一旦收集到足够多的新标记数据,我们将更新模型。
当然,如果您能够访问新的标签,并且能够评估实际的模型质量,那么查找和定义低性能的部分将会更加可靠。
例如,您可以直接按性能对有意义的部分进行排名,或者探索具有最高模型误差的区域。下面是我们如何对一个员工流失模型进行分析的。
如果这不起作用呢?你可以在上面试试别的东西。
8.在模型之上应用业务逻辑

图片作者。
在这个选项中,您可以在模型预测的基础上进行调整,或者更改应用程序逻辑。
有不同的方法可以做到这一点。这种方法的缺点是很难推广。怀着最美好的愿望,我们可能最终只会让事情变得更糟。
由建模师和领域专家来决定。
这里有几个例子。
输出的手动校正。这在需求预测中很常见。
即使在一个“普通”的模型应用程序过程中,您也经常有一组针对特定项目、类别和区域的复杂业务规则。您可能习惯于调整模型预测来说明促销活动、营销活动和已知事件。
例如,DoorDash 依靠结合专家判断和机器学习进行需求预测。
鉴于数据或概念的漂移,您可以在模型输出的基础上应用新的修正。例如,对于给定的类别,您可以将模型输出更改 X%,或者设置最小或最大值,以确保业务流程不受影响。
慎用!
为分类问题设置新的决策阈值是可能的。

图片作者。
通常模型输出是一个概率。它说一个客户的流失率可能是 80%,另一个是 60%。
您可以在这里控制决策阈值。例如,只有当预测概率高于 50%、80%、甚至 95%时,您才可以决定分配标签“客户流失”。我们在教程中探讨了员工流失的含义。
如果您的数据正在漂移,您可能会决定改变这一点。例如,将阈值设置得比以前高。
你可以在假设模型校准良好的情况下做到这一点,并且可以依赖更高的预测概率。或者你可以通过限制联系的客户数量来限制浪费保留预算可能造成的损失。
在单独的工作流程中处理异常值是限制错误的另一种方法。
我们最近讨论了漂移和异常值检测之间的区别。离群值侧重于检测单个“不同”的数据点。
如果您在不断变化的环境中操作,您可能会决定即使存在漂移也要保持模型运行。相反,添加一个单独的工作流来尝试“捕捉”模型可能无法处理的最奇怪的输入。
例如,您可以设置一个检测器来发现最不同或最新颖的输入。然后,您可以发送它们进行人工处理。或者,在这种情况下,您做出了根本不显示模型预测的设计决定。
这种手动调整很少是精确的。小心地根据可用的基线来权衡风险和不利因素。
在我们总结这些选择之前,让我们走一个小弯路。
数据漂移:前传

图片作者。
我们以这个问题开始这个博客:当你注意到漂移时该怎么办?
在理想世界中,你最好反过来。先考虑可能的动作,再设计漂移检测框架。
没有一个单一的、普遍的“漂移”事件会发生在机器学习系统上,并促使你做出反应。模型创建者定义需要提醒的变化程度以及如何对其采取行动。
**首先,评估模型上下文。**数据背后的真实世界流程是怎样的?你到底期望它如何改变?
**接下来,决定模型有多重要。**你是在乎小波动还是只在乎大转变?什么是“重大”转变:一个模型失败会让你付出多少代价?
**然后,考虑选项。**你什么时候拿到新标签来更新型号?有没有可供选择的模型或系统可以让你做出决定?上面的想法适合维护工作流程吗?
之后,回过头来设计漂移检测工作流、指标和阈值。

图片作者。
例如,您可能根本不需要查看分布漂移。如果你得到真实标签的速度足够快,你可以直接计算模型质量。如果您主要关心损坏的输入,您可以进行基于规则的数据验证检查。
另一方面,如果您有延迟的基本事实、关键用例以及您可以很好解释的特性,您可能有一个详细的数据漂移检测仪表板和一整套统计测试要运行。
这里有很多变化。
你可以设计一个由重要性加权的单个特征组成的自定义漂移函数。或者,简单地看看你最关心的前 5 个特性。
你可以选择一个统计测试来更好地适应特定的特征分布。或者监控某些特征之间的相关性,因为这对领域专家来说是有意义的。
你可能不介意漂移的假阳性警报,因为模型是至关重要的。或者,您只想在它完全损坏时得到通知。
以及两者之间的一切。
总结
漂移监控系统的设计至关重要。确保您设置警报的方式与可能的操作一致。

图片作者。
如果检测到漂移,您可以采取以下措施:
- 检查数据质量。确保漂移是真实的。
- **调查。**探究数据变化,了解导致数据变化的原因。
- **什么都不做。**你可能会认为这是一个假警报,对模型处理漂移的方式感到满意,或者干脆决定等待。
- 如果可以的话,再培训一下。获取新的标签和实际值,并根据最新数据重新拟合相同的模型。根据需要放弃旧的。
- 如果需要的话,重建它。如果变化很大,您可能需要重建培训管道并测试新的模型架构。
- **使用回退策略。**不用机器学习做决定。
- **限制型号使用。**找出并排除表现不佳的细分市场。
- **添加自定义处理逻辑。**添加校正系数,更改决策阈值,审查异常值。请小心使用。
**有一个关于生产机器学习的问题?**加入我们的不和谐社区,在 #ds-ml-questions 频道提问。
本博客最初发表于https://evidentlyai.com。感谢我的联合创始人Emeli Dral共同撰写了这篇文章。
在 appeally AI,我们创建了开源工具来分析和监控机器学习模型。在 GitHub 上查看我们的 项目,喜欢就给它一颗星!
想留在圈子里吗?
- 报名 获取我们的快讯。
- 关注Twitter和Linkedin。
- 加入我们的 不和谐社区 来聊天联系吧。
我的数据科学实习故事
原文:https://towardsdatascience.com/my-data-science-internship-story-6ee0f10b5e0f?source=collection_archive---------41-----------------------
我在人工智能医疗创业公司工作 9 个月期间经历的事情
嗨,大家好!新年快乐。我想通过分享我最近在印度一家基于人工智能的医疗创业公司 Dozee 担任数据科学实习生的经历来开始这一年。我会试着涵盖一切,从我的日常安排到我的学习和整体经验。

赫梅尔森·科埃略在 Unsplash拍摄的照片
一些背景
大三快结束的时候,我被介绍到了深度学习和计算机视觉领域。最引起我注意的是它们在生物学和医学中的应用,以加速它们在过去几十年中相当饱和的增长。大二快结束的时候,我还挺服气的,去医学和 AI 的交汇点工作,进行最后一年的实习。我成功地在法国一家世界领先的医疗机器人研究所获得了一个职位,从事计算机视觉挑战方面的工作。但随后,COVID 击中了。旅行限制取消了我在这种环境下工作的目标。我感到崩溃,也有点焦虑,因为机会是完美的匹配。我不知道前方等待着我的是什么。
2020 年 4 月,我参加了 doze 的面试,一周后,我获得了作为数据科学实习生与他们的研究团队一起工作的机会。今天,我对自己的经历感到高兴,尤其是有可能体验到研究和生产两个方面。
如果你错过了一个你认为是宇宙所能提供的最完美匹配的机会,不要担心!角落里有一个同样好(如果不是更好)的机会在等着我们。你所要做的就是付出努力并发掘它。
每日最佳状态
首先,我想简单介绍一下我的日程安排。由于时间的灵活性,我每天上午 11 点左右开始工作,晚上 8 点结束。由于疫情限制我只能在家工作,我不必太担心午餐或晚餐的休息时间。

由卢卡斯·布拉塞克在 Unsplash 上拍摄的照片
这一天通常以上午 10 点整的 scrum 电话会议开始。在此期间,团队讨论了各自垂直行业的近期发展以及当天的计划提案。通常持续 10 到 15 分钟,这通电话无疑为一天定下了基调。在一些日子里,有高优先级的任务,而在其他日子里,都是关于即将到来的会议和讨论。
我把我的会议分成三部分(至少试图)。从上午 11 点到下午 2 点,我试着完成不需要太多时间的小而分散的任务。例如,根据新需求更新功能,完成部署算法的日常测试,或者为一些快速自动化任务编写脚本。然后我暂停了大约 340 分钟,又回到了 slack 上。在去吃午饭之前,我通常会在我们的 GPU 上运行一些耗时的任务。从下午 2:30 到 5:30,我把时间花在了一天的中心任务上。这些包括通读文章和研究论文,制定解决方案,以及收集为该任务构建 MVP 所需的所有信息。这是一天中最有趣的部分,因为我在这里发现和探索的最多。我通常在下午 6:00 到 8:00 之间的时间里,将前一个会话中开发的逻辑和想法转换为代码。这在大部分时间里都很简单。我试图在收工前准备好工作代码,并在第二天早上优化它。在此期间,整天都有项目讨论和 bug 修复的电话。然而,每天的时间表并不是那么整齐划一;有时候,我一天中有三分之二的时间都花在了修复 bug 上。
除此之外,我们每周就团队中正在进行的项目进行研究和开发讨论。我爱他们!这是一次广泛的开放式讨论,我们都在讨论如何用不同的方法克服项目中的障碍。这是长达 2-3 个小时的健康讨论,从构思新项目到优化已经开发的战略。
我喜欢每周 R&D 讨论会的想法。这是一个让每个人都参与不同项目进展的好方法,也给了每个人合作和贡献的机会。事实上,在我们 R&D 的一次讨论中,我正在进行的项目的一个非常基础的部分的想法成形了。
在彻底研究一个想法后,我们习惯性地召开演示会议,在会上我们阐述这个想法,它的表现,并仔细检查整合的可能性。它整合了项目的所有考虑因素。
项目分解
谈到这个项目,我会把它分成三个主要阶段:构思、测试、生产,每个阶段都比其他阶段更重要。

Jo Szczepanska 在 Unsplash 上拍摄的照片
思维能力
有些人可能会认为这是所有阶段中最重要的,因为想法将决定后面阶段的表现。作为一名新手实习生,这是我的第一个广泛的任务。我收到了问题陈述,我必须想办法解决它。刚开始还挺铺天盖地的!我记得花了几天时间阅读研究论文,试验现有的技术,以及围绕这个主题的其他先前的工作。有趣的是 Dozee 是基于心冲击描记术(BCG),一种通过测量身体对心脏射血的反应来监测生命体征的方法。围绕这个话题做的工作不多,更不用说我具体的项目陈述了。我不得不从过去关于心电图和活动描记术的工作中获得许多想法。如果你想知道你的智能手表是如何测量你的心率的,那就是腕动描记术,这是医疗保健和机器学习的一个有趣的融合领域。想到我们的想法可能是同类中的第一个,我继续前进,并推动我进行更深入的探索。
我遇到了两种层次的想法拒绝。第一个很容易被拒绝。在简单地分析了一个想法或方法及其与我们产品的兼容性之后,我们可以很容易地放弃这个想法。也许我们没有所需的输入,或者也许不可能实时想象。第二种想法似乎是可信的,即可以实现的想法。找到的唯一方法是继续研究这个想法。这需要时间,不要把时间浪费在不合适的想法上。在三个月的时间里,我研究了四个有希望的想法和策略。前三次完全失败了。我们取得的成绩低于我们现有的基准。最终的方法是一个超越了我们所有基准的方法,事实上,用一个相似的问题陈述击败了 BCG 的所有工作。
值得一提的是,如果我没有尝试前三种方法,我很难得出我们目前成功的战略。对每个想法的研究帮助我从不同的角度看待问题,并共同形成了对失败原因的概念。这也有助于我深入研究,理解我们到底在试图解决什么,为什么它很重要,更不用说这个问题的生物学方面了。
测试
有四种方法可以迭代地进入测试阶段。通过*迭代,*我的意思是第二种方法是基于第一种方法的学习而构建的,或者最终的方法是基于前三种方法的学习。这是一个耗时的阶段。它涉及生成数据点,手工制作特征,以及一起训练深度学习模型几个小时或几天。必须建立一个管道,将我得到的原始信号转换成可以用作模型输入的形式。为第一种方法构建管道是最具挑战性的,因为对于其他三种方法,可以修改和使用相同的管道。在我们团队其他数据科学家的指导下,我遵循的一般程序是:
a)构建最基本的基准架构。
b)使用这种架构试验不同的特征空间。
c)继续用最有前途的特征空间设计更好的架构。
这真的帮助我以积极和有组织的方式评估和比较特性、模型架构。我的前两种方法是基于图像作为特征的,我记得我急切地等待可视化和评估模型性能不佳的原因。在某一点上,我构建了如此多的具有不同特征空间的模型,以至于它们看起来不再像黑盒、,就好像我知道它们为什么会失败,以及什么会修复它们(在 60%的情况下,:D】*都是如此)。*这一阶段是该方法证明其艺术性能的倒数第二个挑战。通常会有一个赢家,一直延续到最后的状态。然而,如果它不符合服务器和生产标准,它仍然可以在最后阶段被拒绝。
生产
*圣杯。*在构建了具有传奇分数的最佳模型后,是时候将它放入您产品的工作管道中了。现在,许多因素开始发挥作用,例如计算成本、实时可行性(取决于产品)、存储需求(和成本)、满足服务器配置(或构建新实例的需求)等等。评估这些因素中每一个因素的模型性能是至关重要的。其中一个方面的失败可能意味着你将开始一个新的想法或者使用其他方法进入测试阶段。一般来说,这些都是可以解决的。优化您的架构并使其轻量级是相当容易的。我们通常使用修剪和量化等技术来显著降低计算需求。可能需要中间管道来使模型与正在运行的管道兼容。记住服务器配置和其他在构思阶段不能改变的因素可以节省你大量的时间。
满足所有的需求并不意味着你的模型已经可以部署了。在使您的代码可以投入生产和实际部署到所有用户之间,会进行大量的 beta 测试。内部用户收到新模型的更新,并进行一两个月的测试,每天记录性能。用户还会被审查他们对模型性能的体验。经过一段时间稳定的性能,没有代码中断之后,这个模型就可以发布给每个使用这个产品的人了。我还记得 pull 请求被发送以将我们的项目与主分支合并的那一天。这是一种奇妙的感觉。让我的作品在数千台设备上运行的感觉真是太棒了。这项工作花了将近 7 个月的时间才投入生产,每一分钟都是值得的。
我的总体经验
今天,每天有成千上万的数据点通过我们建立的管道传递,这让我欣喜若狂。我喜欢我被给予试验不同技术的自由,并且想出一个满足我们所有需求的方法。我喜欢被包括在测试和生产阶段,只是建立模型。我喜欢为其他正在进行的研究项目做贡献的机会。最后,我喜欢成为所有研究讨论、scrum 电话、每月团队会议的一部分,这让我有了一种全职数据科学家的感觉。
如果你想成为一名数据科学家、人工智能/人工智能工程师,如果你的目标和兴趣一致,那么与一家初创公司合作是不会错的。
随着医疗保健越来越重要,今天,我们看到更多这样的创业公司出现。在 Dozee 工作当然让我更接近我对医学和人工智能交叉的兴趣,该领域未来的潜力真的让我兴奋。
有兴趣了解更多我们的发现吗?我们的预印本现已发布:
非常感谢你的阅读。我希望你喜欢它。如果你觉得我错过了什么,或者如果你想知道更多,请在下面的评论中留下,如果 Dozee 团队有人正在阅读这篇文章;感谢给我这个机会!
在 AI 上看多了有交集?探索下面的文章。
https://medium.com/swlh/how-do-ai-based-fitness-trackers-work-ft-1d-cnns-e76f679bdee8 
作者图片(最近点击)
我 2021 年的数据科学之旅
原文:https://towardsdatascience.com/my-data-science-journey-in-2021-5f95b93f98e2?source=collection_archive---------12-----------------------
2022 年将会发生什么

GR Stocks 在 Unsplash 上拍照
我简直无法想象又到了一年一度的时候,每个人都试图吹嘘他们一年来的成就,并为来年制定他们大多无法实现的一厢情愿的计划。
我不是伟大传统的例外。
2021 年是孤独、挣扎、沮丧和精疲力竭的一年。同时,这也是学习、成长和成就的一年。在全球疫情的背景下,我以一个在三个月内找到工作的雄心勃勃的目标开始了这一年,但却以几乎三倍的时间结束了。幸运的是,事情进展顺利,我在沃尔玛全球科技得到了一份数据科学(统计学家)的工作。
数据科学就业市场现在很热,但竞争也很激烈。如果你正纠结于面试循环,请看看我的沃尔玛之旅: 我是如何在沃尔玛 获得一份数据科学工作的。
年度关键词是什么?
面试!
没错。整个 2021 年都是围绕着准备技术面试和面试后做事。因此,我将这篇文章的剩余部分分成四个部分,详细阐述我在每个阶段的成就。最后,我会列出 2022 年的几个计划。
第一季度:准备面试
在和业内朋友完成了几轮模拟面试后,我很快意识到了学术研究和真实行业工作的差距。我确定了一些需要培养的关键技能。SQL 和 Python 站在列表的顶端。为了快速发展我的编码技术,我在第一季度写了 20 多篇冗长的技术博客。
作为一名教育工作者,我坚信通过写作教学是最好的学习方式。如果您能够将核心数据科学概念分解成更小的模块,您就真正理解了这些概念。
教学重塑了我对数据科学和其他技术学科的整体看法。当然,学习应该是反复的,以小剂量进行。
如果你是一名数据科学家或者想进入这个领域,请查看这篇关于如何更有效地学习的文章:
在线写作的另一个好处是,它创造了一个奇妙的在线存在,并作为你的数据科学投资组合。在沃尔玛的现场面试中,所有的面试官都提到了我的技术博客文章。
写作时,我把质量放在首位。例如,我花了大约一年时间为下面这篇文章做研究,这篇文章记录了科技公司在实验策略中最常犯的错误:
第二节:学会破解面试
在接下来的几个月里,我参加了多家科技公司的面试。刚开始并不顺利,因为各种原因炸了几个面试。首先,我将技术面试视为学术讨论,而不是评估候选人能力的结构化方式,这是一个新手的错误。幸运的是,我很快意识到了不同之处,并采用了更具互动性的采访方式。
第二,我当时视野狭窄,未能从每次面试中反思哪些地方可以改进。回顾过去,我会把每一次面试都视为一次学习的机会,并接受自己的弱点。
第三,将拒绝视为个人行为只会带来伤害。如果你在面试循环中,试着接受一个否定的回答,并从拒绝中学习。要有弹性,因为从拒绝中恢复的速度决定了你能走多远。
我应对拒绝的策略是保持一种成长的心态,更加肯定自己的成长。帮我完成一个简单的任务。
例如,编写有效的代码曾经是我最大的弱点,我遵循这些技巧来提高我的 SQL 和 Python 编程技能:

照片由杰米街在 Unsplash
第三季度:广度>深度
7 月底,我收到了一份工作邀请,并与沃尔玛全球技术签订了合同。感到幸福和感激。这完全符合我在统计学、机器学习和实验&因果推理方面的学术背景。
签完合同后我做了什么?
我没有放松和无所事事,而是收集团队的反馈,并试图更好地了解他们的痛点。例如,他们会遇到哪些常见问题?他们从最终用户那里收到了哪些投诉和担忧?同样重要的是,他们想进入什么样的未来方向?他们希望关注哪些优先事项?
此外,我退出了其他科技公司的剩余面试(其中几家已经进入最后一轮),并从任何面试准备中解脱出来。从时间管理的角度来看,这是一个明智的决定,因为我有更多的时间阅读和研究。
利用额外的时间,我深入研究了其他科技公司如何解决具体技术问题的文献,并了解了其中的细微差异。事实上,我每天花大约 8 个小时,每周花 6 天时间(大约 50 个小时/周)研究相关主题。因此,到本季度末,我对实验和数据科学的理解显著提高。
上一季度:深度>广度
随着开学日期的迅速临近,我再次调整了阅读习惯。我没有广泛阅读各个领域的书籍,而是选择了几个主题,集中在精简的列表上。
到第三季度末,我已经很好地了解了其他科技公司是如何进行实验的,并确定了我下一步需要努力的关键领域。
深度和广度是不可能同时达到的。所以,最后一个季度我放弃广度换取深度。事实证明,专注服务于我的长期目标。
Medium 最近进化出了自己的 作家伙伴计划 ,支持像我这样的普通作家。如果你还不是订户,通过下面的链接注册,我会收到一部分会员费。
https://leihua-ye.medium.com/membership
2020 新年决心
没有完成所有的目标是正常的,一个经验法则是实现 80%的目标:高于目标意味着你把标准定得太低,低于目标意味着你高估了自己的能力。
我一生的座右铭是通过对我所能达到的目标不设上限来获得自由。
我将与我的数据科学社区分享以下 2022 年的具体计划:
- 练习相邻学习,获得更多的数据工程技能。 End **结果:**能够执行基本和中等水平的工程任务。
- 探索新的交流渠道,邀请人们加入我的 YouTube 频道。**最终结果:**征求他们的职业建议,并与数据科学社区分享。
- 在媒体和 LinkedIn 上继续写。**最终结果:**建立更有意义的联系。
- 多运动,保持健康,多运动。最终结果:完成另一场马拉松,增肌更多。
外卖食品
以下是我从 2021 年最大的收获。
- 学无止境。与任何技术领域一样,数据科学正在快速变化和发展。最近的趋势是转向工程,数据科学家必须执行许多工程任务。没有适当的 SQL、Python 和大数据培训,我们做不了多少事情。令我惊讶的是,数据科学家需要对数据管道有如此高的理解,这促使我学习更多关于数据工程的知识。
- 在学习中学会更好。我创造了“主动学习”这个术语,指的是通过理解基础知识来更有效地学习。我主动学习的秘密武器是通过反复阅读、写作和教学。
- 保持干劲!我是一个高成就者。例如,我同时完成了两个研究生项目(政治学博士和统计学硕士)。嗯,明显的缺点是它很容易导致倦怠。以下是我的应对策略。首先,走出我的舒适区,探索未知的领域,这就是为什么我开办了一个 YouTube 频道。第二,成长的心态帮助我放松自己。第三,稍事休息,神清气爽地回来。第四,举重有帮助。
喜欢读这本书吗?
请在 LinkedIn 和 Youtube 上找到我。
还有,看看我其他关于人工智能和机器学习的帖子。
我作为数据工程师和数据科学家的经历
原文:https://towardsdatascience.com/my-experience-as-a-data-engineer-vs-a-data-scientist-9ad0bcb26b61?source=collection_archive---------6-----------------------
如何确保成功过渡

来自 Pexels 的金德媒体的照片
当我第一次从数据工程师转变为数据科学家时,我天真地认为我只需要学习工作所需的数据科学技能。多年以后,现在看来显而易见的事情我当时并没有想到。今天,我想谈谈我一路走来遇到的挑战,以及如何做好准备以确保成功过渡到数据科学。
商业思维
作为一名数据工程师,很容易确定技术问题是否得到解决。代码要么执行了预期的行为,即将所有原始数据加载到数据库中,要么没有执行。我不能让代码只加载 90%的数据就声称这是成功的。
作为一名数据科学家,我的工作是帮助利益相关者解决业务问题。然而,由于缺乏数据或技术限制,我并不总是能够使用机器学习模型,必须找到替代方法来回答只能解决 90%问题的问题。这种心态的改变对我来说是一个困难的调整,因为当我处理技术问题时,我习惯了要么全有要么全无。
要点:在解决商业问题而不是技术问题时,改变你的思维模式。在数据科学中,不能 100%解决问题是可以接受的。
数据叙事
作为一名数据工程师,我很少做演示,即使做,也是技术性的。我也不经常与利益相关者交流,我的沟通技巧也不是最好的。
当我成为一名数据科学家时,我不得不开始向我的非技术利益相关者解释机器学习结果,并学习数据讲故事,以提供背景并显示业务影响。说发展我的沟通技巧很容易是骗人的,但最终,我成功了。
**要点:**与利益相关者交谈是数据科学家工作的重要组成部分。发展你的沟通技巧,特别是如果你是一个内向的人,不喜欢和别人交流。学习如何以清晰的方式向非技术观众解释结果。
利益相关者沟通
我发现在我成为数据科学家后,利益相关者向我这个数据工程师描述技术问题比业务问题更容易。例如,作为一名数据工程师,一个利益相关者会告诉我他们没有看到数据库中的最新数据。技术问题很清楚,对我来说研究根本原因并解决问题很容易。
作为一名数据科学家,并不总是清楚利益相关者的问题是什么。我经常需要会见利益相关者,以便更好地理解业务问题,因为有时他们要求的并没有解决真正的问题!这使我向数据科学的过渡更加困难,但随着时间的推移,我能够掌握这种“翻译技巧”,即提出引导性问题来解决真正的业务问题。
要点:调整你与技术利益相关者和业务利益相关者的沟通方式。不要表面上接受利益相关者的要求,因为他们的要求可能不能解决实际问题。询问后续问题或召开会议,以了解利益相关者的真正需求。
最后的想法
从数据工程师成功转型为数据科学家不仅需要学习该角色所需的软技能,还需要学习数据科学技能。如果你正在考虑从技术岗位转向数据科学岗位,我希望我的经历能帮助你更顺利地过渡。
感谢您的阅读,如果您想在这篇文章发表的媒体上无限制地获取故事,请考虑每月支付 5 美元注册成为会员。如果你订阅了这个 推荐链接 ,我就赚点小提成。
你可能也会喜欢…
https://medium.com/swlh/how-i-used-a-machine-learning-model-to-generate-actionable-insights-3aa1dfe2ddfd
我作为产品数据分析师的经历
原文:https://towardsdatascience.com/my-experience-as-a-product-data-analyst-3d01748bc6ea?source=collection_archive---------15-----------------------
支持产品如何让我成为更好的数据分析师

来自佩克斯的小伙子弗瑞的照片
在成为产品数据分析师之前,我的大部分数据工作都是支持市场营销的。你可能认为支持产品和其他部门是一样的,但是根据我的经验,我可以告诉你事实并非如此。今天,我想讨论一下我转变为产品数据分析师的经历,以及与产品打交道如何让我成为一名更好的数据分析师。
学习曲线
与通用的营销概念如 SEO 和 SEM 不同,每个产品都是不同的,并且有一个学习曲线让你了解产品的用途以及用户如何与之互动。分配入职所需的额外时间来学习用户流和用于事件跟踪的系统。当我开始担任产品分析师时,我花了几周时间来学习如何有效地使用产品分析软件,并找出映射到用户流中每个屏幕的事件名称。
用户体验
支持产品让你有机会在申请职位之前尝试一下。对产品或行业感兴趣将有助于你成为一名更有效的分析师,因为你可以联系用户体验,并在你的分析中考虑到这一点。我目前是一个移动应用程序的产品分析师,在我面试之前,我下载了这个应用程序来试试。这让我可以在面试中提出改进建议,以显示我事先对公司产品的积极研究。作为一名产品分析师,我可以自己使用该应用来评估新功能,并与用户体验相关联。
移动与网络
即使您以前是一名产品分析师,当您支持移动产品时,也需要考虑其他分析事项。
- 平台:当我们想到手机时,它只是一部电话。然而,从产品的角度来看,一个公司为 Android 或 iOS 平台开发一个应用程序。我的公司有 Alexa 技能的应用程序,成为另一个分析平台。还可以为 iPad 等平板电脑开发一款应用,这是又一个平台。可以首先在一个平台上开发功能,以评估用户反馈。作为一名产品分析师,我必须记住各个平台的特性,因为这可能会影响我的分析。平台和功能可用性只是产品分析中需要考虑的几个项目。
- 约束—一个网站的变更只需要在代码被批准后,由工程师进行部署即可。移动开发者必须制定发布时间表,所有修改必须在指定日期前完成,并在应用商店更新之前提交给苹果和谷歌审查。一个应用程序的漏洞修复不能像网站那样快速更新。如果你在等待一个代码变更被部署,考虑延迟,并相应地对你的任务进行优先级排序。
数据量
用户与产品的交互会产生大量的事件数据。想象一下,你在手机或网页上的每次打开、点击和输入都会被记录和保存。作为一名产品分析师,我经常被问到什么功能的使用会影响用户保持率和溢价转化。我必须使我的查询尽可能高效,因为我从数十亿行数据中查询,我犯的每个错误都意味着我在完成分析之前损失更多的时间。了解查询优化,以利用索引或分区,并在从大量数据中提取数据时估计更多的时间来完成分析。
A/B 测试
产品的一个常见 KPI 是用户保持率,而 A/B 测试通常用于产品变更以衡量影响。一个产品可以有许多团队支持产品的不同部分,同时运行所有的实验。请注意正在进行的其他实验,这些实验可能会影响您的实验 KPI,并希望您将大部分时间用于产品实验和测量。在成为产品分析师之前,确保你理解 A/B 测试的概念,因为你很可能会参与评估测试。
片段
KPI 通常因您支持的部门和业务而异。相反,产品有通用 KPI,这些 KPI 不会因公司而改变,比如用户保持率、每日活跃用户和流失率。产品目标可以是通过新功能或产品增强来吸引用户。我以前从不按用户细分来看 KPI,但在产品分析中,通常会按新用户和现有用户来划分用户,以评估产品变化是影响所有用户的行为,还是只影响单个用户。如果只有一个细分市场受到影响,产品经理可能会测试进一步的改进,看看保留率是否有所提高。产品分析师参与这些讨论,并评估这些后续实验,以衡量用户保留的影响。
漏斗分析
用漏斗分析来看产品交互是很常见的。例如,注册漏斗可以有多个屏幕,在帐户注册完成之前提示用户输入信息。更多的屏幕会增加摩擦,并可能导致帐户完成率下降。在漏斗的每个步骤中,我们都会评估掉线率,以确定漏斗的任何部分是否需要改进。在支持产品之前,我从不需要进行漏斗分析。现在,任何产品 KPI 调查都要求我考虑可能影响 KPI 的漏斗变化。例如,如果新用户突然减少,除了查看可能导致新用户减少的其他因素,我还需要检查注册流程中是否有产品变更。
结论
与产品打交道使我成为一名更好的数据分析师。我不再像在营销中那样关注一系列常见因素,而是学会了考虑漏斗变化和用户体验可能会如何影响产品结果。通过与产品经理在实验设计和产品计划规模方面的紧密合作,我提高了我的沟通技巧。因为不断的实验来改进产品,我有很多机会练习解释结果和用数据讲故事。转换到产品数据分析师的角色并不是一个无缝的过渡,但这绝对是一次值得尝试的经历,我希望你会发现这也让你成为一名更好的数据分析师。
你可能也会喜欢…
https://madfordata.medium.com/how-to-get-your-first-data-analyst-job-without-experience-7a9afc663439 [## 关于 A/B 测试,产品分析师应该知道什么
towardsdatascience.com](/what-product-analysts-should-know-about-a-b-testing-a7bdc8e9a61)
我在 Correlation One 面向所有人/女性的数据科学计划中的经历
原文:https://towardsdatascience.com/my-experience-with-correlation-ones-data-science-for-all-women-program-692b9c736b4?source=collection_archive---------8-----------------------
看看 Correlation 的一个免费数据科学训练营
在攻读化学博士两年后,我决定不想读完它。相反,我想从事数据科学/ML 工作。于是,我开始求职,上网络课程,做项目,搞人脉。通过网络,我了解到 DS4A/Women——这是一个为女性举办的为期 7 周的免费 数据科学训练营,号称可以与美国银行和 2 适马等公司直接建立联系。不用说,我非常感兴趣。在这里,我想分享一下我对该计划包括申请过程的经验。

照片由乔尔·穆尼斯在 Unsplash 上拍摄
评估
以下摘自网站:
对于从业者,将有一个核心数据科学技能的入门评估,如 Python、统计学和建模。
对于 DS4A/Women 计划,我得到了 60 分钟。对于 DS4A/Empowerment 项目,我认为你以前可以得到 90 分钟(根据我用疯狂的谷歌搜索技巧找到的这篇博客文章)。但是在网站上,现在也是 60 分钟。他们还给了我大约一周的时间来开始评估。
我花了一些时间冥想,思考我的人生决定,并像一个人一样,为即将到来的一切做好自我准备。当然,你也可以花时间温习 Python、统计学和建模。
我想我不能在这里安全地讨论具体的问题。总的来说,我感到惊喜。这些问题肯定是针对那些具有一些 Python 技能和数据处理经验的人的。我想说,如果你像我一样在 STEM,你有一个很好的机会。
面试
大约一个半星期后,我收到了面试邀请。面试官感谢我同意在周末和她见面。作为一名模范博士生,我不知道“周末”是什么。但我不想在面试时说漏嘴,说我什么都不知道,所以我只说了句‘没问题’。
我认为称之为采访让它听起来比以前更可怕。面试官超级冷淡。她问的大多是我的背景,为什么要从事数据科学方面的工作,为什么要申请 DS4A。还有一些行为问题。不过,它们在上下文中是有意义的。但是我还是没有做好准备,尽管我不应该做好准备,因为我也在准备工作面试!那次面试真的让我坐下来想出了一个行为问题的游戏方案。
上层社会
这些课程从美国东部时间周六上午 11 点到下午 5:30 开始,中间有休息时间,最后 2.5 小时是团队合作(为了你的顶点项目)。以下内容摘自网站:
培训包括 2021 年 9 月至 10 月为期四周的在线直播讲座,所有讲座都基于真实案例,并以可用的 Jupyter 笔记本形式提供。
我参加过机器学习的面授课程,以及一些网上课程。他们的一个共同点是,技术和概念通常是在真空中教授的,在漂亮的数据集上,除了教育目的之外,没有太多价值。有了 DS4A,每堂课都从业务环境开始,由教授或真正的数据科学家讲授,并洞察哪些技术是重要的!
此外,他们的 Jupyter 笔记本比我们从该计划中获得的课程数量还要多,每个笔记本都包含一个专注于一系列技能的案例研究。这是一个非常好的自学资源。
顶点工程
这部分很有趣。我看到许多新兵训练营宣传顶点工程是他们课程的一部分,但我经常对此感到困惑,因为如果每个校友都有相同的顶点工程,那还有什么意义呢?
DS4A 在 capstone 项目中非常自由。我们被分成几组。每个小组都有一名助教和一名导师,他们都是数据科学家。小组成员需要讨论并提出自己的想法,寻找数据,并在助教和导师的指导下用这些数据做一个项目。因此,您实际上获得了一个独特的项目和一个关于您在数据团队工作时的故事!
现在对于女子项目来说,日程有点紧。我们有 7 周时间,但我觉得更像是 6 周。前 2 周花在寻找数据和定义项目范围上。然后,花大约 3 周时间进行分析和建模。然后,我们有大约 1 周的时间来整理最终报告、datafolio 和仪表板(并非所有团队都有时间整理仪表板)。这肯定是可行的,因为我在大结局中看到了一些令人印象深刻的项目。但我要说,这高度依赖于你的团队的整体专业知识和时间投入。
社区
在过去的一年半时间里,我一直在慢慢训练自己,当主持人问“有问题吗?”时,不要因为沉默而退缩在变焦会议期间。但是每堂课和每次聚会的热情参与程度都令人震惊。这些女性绝对不会羞于提问,这让人耳目一新。我们被邀请到一个空闲频道,它在整个节目中一直很活跃。人们到处分享技巧、资源和机会。所有我有机会交谈过的女人都超级好又聪明!
就业机会和职业支持
每周,我们都有一个专门的现场会议,讨论求职的不同方面,从如何设计你的简历和 LinkedIn 页面,到如何建立关系网和协商你的薪水。更重要的是,我提到的数据科学家导师?他们亲自看了你的简历,并讨论你如何能做得更好!
此外,一旦你成为研究员,你就可以随时访问充满合作伙伴公司机会的工作板!这些公司在整个项目中亲自来和我们谈论在那里工作的感觉。在我的项目中,C1 还聘请了一名专门的招聘人员,与同事一起在这些机会中寻找合适的人选。
现在,我不会说这是有保障的,因为,当然,它不是。公司不会仅仅因为你是 C1 研究员就自动雇用你,但这个项目肯定会打开大门。
总结
如果你正在考虑,就去做吧!这是 facetime 之上的免费相关教育,面向招聘公司和各级数据科学家。到目前为止,他们提供了 4 个不同长度和目标学员的项目。例如,赋权计划为期 13 周,面向黑人、拉丁裔、LGBTQ+和其他代表性不足的群体。
坦率地说,有时我确实想知道我是否应该申请授权项目,因为它需要 13 周的学习。这是顶点工程的更多课程和更多时间。然而,我认为女子项目正适合我。7 周听起来不算多,但那就是加班 7 周,所以真的要看你的耐力了。
保持联系
我喜欢写关于数据科学和科学的文章。如果你喜欢这篇文章,请在 Medium 上关注我,加入我的电子邮件列表,或者成为 Medium 会员(如果你使用这个链接,我将收取你大约 50%的会员费),如果你还没有的话。下一篇帖子再见!😄
我在 HuggingFace 的 dataset-hub 上上传数据集的经验
原文:https://towardsdatascience.com/my-experience-with-uploading-a-dataset-on-huggingfaces-dataset-hub-803051942c2d?source=collection_archive---------18-----------------------
HuggingFace 的数据集库是一个单行 python 库,用于从 HuggingFace 数据集中心下载和预处理数据集。截至目前,该图书馆包含约 1000 个公开可用的数据集。

(来源:self)
在这篇文章中,我将分享我在 dataset-hub 上上传和维护数据集的经验。以下 meme 总结了使用数据集库背后的意图:

上传数据集的灵感(作者:self)
获取数据:
在 HuggingFace 人员的帮助和指导下,我能够将模型中心(类似于数据集,HuggingFace 托管 10,000 多个公开可用的模型)上可用信息的元数据下载到一个 csv 文件中。然后,我开始将它作为数据集上传到 dataset-hub。
安装数据集库:
$pip install datasets
添加数据集:
添加公共数据集有两种方式:
- 社区提供的 :数据集托管在数据集中心。它未经验证并在名称空间或组织下被识别,就像 GitHub repo 一样。
- 规范 :通过打开 PR(Pull Request) 到回购,数据集直接添加到 数据集回购。通常,数据是不托管的,必须经过 PR 合并过程。
因为我想托管数据,以及预处理脚本(被库称为“数据集加载脚本”),所以我选择上传数据集作为社区提供的数据集。
将 csv、json、xml 或任何其他格式的现有数据集转换为数据集有两个主要要求:
- 数据集加载脚本
- 数据集元数据
数据集加载脚本:
创建这样一个脚本有很好的文档可用。然而,我更喜欢通过使用与我相似的预先存在的数据集的脚本进行复制粘贴,这些脚本存在于数据集库中。例如:如果我的数据集是 csv 类型,我将从一个类似的 csv 类型脚本开始,并根据我的需要修改它。
该脚本主要需要定义三个组件:
- 关于列和数据类型的信息(称为特性
- 从(或本地文件)下载数据并指定培训/测试/验证分割的 URL
- 利用分割和特征产生 1 行数据
**1。_ info:**CSV 字段的标题需要用它们的数据类型来定义。我最后用了string、int32和large_string。f 特性中提供了受支持数据类型的描述。

数据类型很重要!(来源:self)
def _info(self):
return datasets.DatasetInfo(
description=_DESCRIPTION,
features=datasets.Features(
{
"modelId": datasets.Value("string"),
"lastModified": datasets.Value("string"),
"tags": datasets.features.Sequence(datasets.Value("string")),
"pipeline_tag": datasets.Value("string"),
"files": datasets.features.Sequence(datasets.Value("string")),
"publishedBy": datasets.Value("string"),
"downloads_last_month": datasets.Value("int32"),
"library": datasets.Value("string"),
"modelCard": datasets.Value("large_string"),
}
),
homepage=_HOMEPAGE,
license=_LICENSE,
citation=_CITATION,
)
列“标签”和“文件”是数组,因为它们可以是多个,由库通过dataset.features.Sequence.支持
2。URLs 本地文件:
下一步是定义一个 URL,在我的例子中是一个本地文件。因为数据只是探索性的,没有任何目标标签(不是专门用于训练的),所以不需要测试分割。因此,只有训练数据集分割会起作用。
_URL = "huggingface-modelhub.csv"
...
def _split_generators(self, dl_manager):
"""Returns SplitGenerators."""
data_file = dl_manager.download_and_extract(_URL)
return [
datasets.SplitGenerator(
name=datasets.Split.TRAIN,
gen_kwargs={
"filepath": data_file,
},),]
3。生成一行:
下一步是生成单行数据。在运行时,适当的生成器(如上定义)将从 URL 或本地文件中选取数据源,并使用它来生成一行。这里,由于数据格式是csv,我们可以使用 python 内置的 csv 模块及其函数csv.reader从文件中读取数据。
def _generate_examples(self, filepath):
"""Yields examples."""
with open(filepath, encoding="utf-8") as f:
reader = csv.reader(f)
for id_, row in enumerate(reader):
if id_ == 0:
continue
yield id_, {
"modelId": row[0],
"lastModified": row[1],
"tags": ast.literal_eval(row[2]),
"pipeline_tag": row[3],
"files": ast.literal_eval(row[4]),
"publishedBy": row[5],
"downloads_last_month": float(row[6]) if row[6] else 0,
"library": row[7],
"modelCard": row[8]
}
ast.literal_eval是一个将字符串中的数组解析成实际数组(list)的便捷函数。
该脚本可以这样测试:
**>>>** from datasets import load_dataset
**>>>** dataset = load_dataset('PATH/TO/MY/SCRIPT.py')
>>> dataset["train"] # To access train generator
>>> dataset["train"][0] #Access elements in dataset
脚本就绪后,我们继续添加数据集元数据,并准备好发布数据集。
数据集元数据:
文档详细解释了如何为共享准备数据集。为了添加元数据,可以通过datasets-cli使用一个 helper 命令,它是在我们安装datasets库时安装的。
datasets-cli test datasets/<your-dataset-folder> --save_infos --all_configs
运行上面的命令会生成一个文件dataset_infos.json,其中包含数据集大小、校验和等元数据。
上传数据集:
Huggingface 在幕后使用 git 和 git-lfs 将数据集作为存储库进行管理。首先,我们需要创建一个新存储库。

创建新的数据集 repo ( 来源
一旦存储库准备就绪,就可以应用标准的 git 实践了。即从您的项目目录运行:
$ git init .
$ git remote add origin https://huggingface.co/datasets/<user>/<repo>
$ git pull origin main
现在,我们已经将本地机器与存储库同步。下一步是添加以下文件并提交:
- 数据集文件(csv): 数据本身
- 数据集加载脚本:数据的加载器
- 数据集元数据:元数据,如大小、引用等
但是有一个条件!传统的 git 系统不适合处理大文件。这是通过 git-lfs (大文件存储)来管理的。我们不需要深入了解它是如何工作的,我们只需运行以下命令就可以将大文件推送到 repo:
$ git lfs install
$ git lfs track huggingface-modelhub.csv
$ git add dataset_infos.json huggingface-modelhub.csv huggingface-modelhub.py
$ git commit -m "Commit message"
$ git push origin main
文档也详细介绍了 lfs。因为这只是一个 git repo,所以也可以提交像 README 这样的任何其他文件。Dataset-hub UI 还提供了一种快速更新自述文件(称为 datasetCard)的方法
仅此而已。数据集应上传到数据集中心。要访问它,请运行:
>>> dataset = load_dataset("<user>/<repo>")
将更改推送到已发布的数据集:

维护数据集版本(来源:self)
由于数据集的 repo 可以使用 git 进行控制,我认为首先提交到一个dev分支,完整地测试它,然后执行到main的合并可能是一个好的实践。这将大大有助于“意外地”破坏一个已经存在且稳定的工作数据集。这非常类似于标准的基于 git 的软件发布哲学。
另一个优势是数据集版本也不一定需要连续(1、1.1、1.2 等)。不同的分支可以保存不同版本的数据集。
**>>>** dataset = load_dataset("<user>/<repo>",
**>>> ** script_version="dev") *# tag name, branch name, or commit hash*
卡住时该怎么办:

HuggingFace 有一个自主持的讨论平台(来源:self)
拥抱脸论坛非常活跃,团队+社区非常有帮助和支持。我个人觉得比 stackoverflow 更主动,反应更快。你也可以直接在 github 上打开问题,但是我更喜欢论坛,因为它们有点不正式。
没有问题是愚蠢的问题!永远不要害怕问。
那都是乡亲们!
我希望你喜欢这篇文章,并发现数据集库是有用的。我用加载脚本上传的数据集可以在这里查看,并用作 csv 格式的参考。
请在评论中分享你的观点。我也准备在推特上进行讨论。
祝你愉快。:)
我最喜欢的包管理器:Pip 和 Pkg 的比较
原文:https://towardsdatascience.com/my-favorite-package-managers-a-comparison-between-pip-and-pkg-56db9f3d6e6a?source=collection_archive---------49-----------------------
深入了解 Python 和 Julia 的包管理器。

图像来源
介绍
这里有很多工具可以改变编程语言的用法,并使其从众多竞争对手中脱颖而出。程序员会经常使用的一个很好的例子是包管理器,它会对他们的语言体验产生巨大的影响。编程世界中出现了许多非常酷和有趣的包管理解决方案,尤其是像 Python 和 Julia 这样的脚本语言。这两种语言都有非常酷的包管理器,分别是 PIP 和 Pkg,我认为它们对依赖关系管理和包安装经常遇到的许多问题有一些非常有趣的解决方案。今天我想后退一步,指出我喜欢这两者的一些特点,因为我认为它们在解决某些计算问题的方式上都很有趣。
概述(CLI)
当谈到像这样的包管理器时,首先要看的也是最明显的是两个包的命令行界面,或 CLI。CLI 是大多数用户与软件包管理器交互的方式,对于所讨论的编程语言的整体用户体验以及用户与软件包的交互方式来说,它无疑是一个重要的方面。从一开始,我们就看到了 PIP 和 Pkg 之间的巨大差异。
PIP 包管理器通常与 Python 编程语言本身分开安装。此外,它通常是在类 Unix 系统上从 Bash 调用的。通常,为了在全球范围内安装 Python 包,我们会运行如下代码:
sudo pip install numpy
另一方面,对于 Pkg,我们有两种选择。在 Julia 中,Pkg 包管理器通常在语言内部使用,而不是作为一个单独打包的应用程序。为此,Julia 包含了用于管理包的 Pkg REPL 和包 Pkg。这两者都包含在 Julia 的基础安装中,在它与语言的集成和 PIP 与 Python 的集成之间建立了明显的区别。这意味着我们通常会使用 Julia 来访问它,而不是 Bash 来调用应用程序。我们可以通过输入朱莉娅·REPL 并按下]来输入 Pkg REPL,或者用朱莉娅打电话给包裹。
juliajulia> ]pkg> add DataFramesjulia> using Pkgjulia> Pkg.add("DataFrames")
我认为这两种方法都很酷。我喜欢添加来自 Pkg REPL 的包裹。这方面另一个很酷的因素是能够在 Pkg 包中调用 using 并在 Julia 中创建一个完整的环境。它使得从包中创建环境变得相当方便,这是我喜欢的。我认为将它作为一个单独的应用程序调用的 PIP 解决方案也很方便,因为它确实使从您的终端快速添加一个包变得非常容易。当然,我不能说这在不像 Unix 的操作系统上是如何工作的,但是在我的 Linux 机器上,这无疑是一个很好的实现,也是用 Python 处理包的一个很好的方法。
虚拟环境
包管理器要考虑的另一个重要方面是如何管理单个项目的包。为此,我们使用了一种叫做虚拟环境的东西,它使得为任何包设置和加载依赖关系变得相对容易,而不管您当前正在使用的包需要什么版本,也不管它们是什么。对于 PIP,我们有 Pipenv。使用 Pipenv,它与 PIP 打包在一起,您可以使用
pip install -r requirements.txt
我认为这是一个非常好的解决方案,然而,在我看来,它确实有点落后于 Pkg 的方法。这是因为 Pkg 使用 Project.toml 文件自动化了创建虚拟环境的整个过程。与 Python 的方法相反,Julian 的包管理方法非常有组织性,并且普遍一致。使用 Pkg.activate()和 Pkg.generate(),我们可以更容易地创建新的环境,其中加载到该环境中的依赖项已经最终包含在该包中。
结论
不管你使用的是哪种高级编程语言,包管理器对于完成工作都非常重要。当然,并不是所有的包管理器都是平等的,肯定有一些东西是这个或那个包管理器更好的。我认为不管你使用什么样的包管理器,将包管理器相互比较并与每一个一起工作都是非常酷和有趣的。我认为这两种实现都有一些很棒的想法,而且两者都可以通过相互研究学到一些东西。
2021 年我最喜欢的 Python Web 框架
原文:https://towardsdatascience.com/my-favorite-python-web-frameworks-for-2021-a255fde2cd68?source=collection_archive---------6-----------------------
这是我最喜欢的 Python 编程语言 DS/ML web 框架的精选列表。

(src =https://pixabay.com/images/id-1729157/
介绍
在Python 的世界里,除了数据科学和后端网络开发之外,这种语言已经越来越少被使用。也就是说,数据科学领域有一部分恰好与使用 Python 进行 web 开发重叠。对于数据科学家来说,部署端点和 API 以便通过 HTTP 访问他们的数据是很常见的。如果您的数据科学领域碰巧更加面向软件,那么几乎可以肯定会出现这种情况。在这种情况下,通常这些端点将由数据科学家自己部署,因此学习如何部署这类端点无疑是成为成功的数据科学家的一项重要技能。
幸运的是,有了像 Python 的奇妙包索引这样的生态系统,有很多奇妙的选项可供选择。在大多数情况下,您可以找到您的产品想要或需要的特定特性,或者您喜欢使用的特定特性和代码风格。也就是说,下面的列表将是一个排名列表。当你知道第一名是什么时,你可能会非常惊讶!这当然只是我的个人观点,和我在这个时候的个人偏好。此外,这是从数据科学家的角度来看的。虽然我承认这些包有能力做其他事情,例如 Django 进行全栈 web 开发的能力可能会使它成为使用 Python 的 web 开发人员的一个更好的选择,但对于像我这样的数据科学家来说是一个更差的选择,这可能会影响我对它的排名。最后,如果你想阅读这篇文章更全面的更新版本的前身,这里有一个你可能感兴趣的链接!
</5-smooth-python-web-frameworks-for-the-modern-developer-47db692dfd52>
№10:涡轮
从我们的列表开始是非常值得一提的 TurboGears 框架。在我的上一篇文章“查看 Python web 框架”中,我提到了 TurboGears 以及为什么我喜欢这个包,现在它又回来了——这是完全合理的。与我通常选择的创建端点和仪表板的许多选项相反,TurboGears 是一个类似于 Django 的全栈框架。
让 TurboGears 令人敬畏的是,它像 Django 一样功能丰富,但往往比 Django 更轻便。虽然 Django 的重点是全栈,它的许多特性都被推送到使用该框架制作的每个应用程序中,但 TurboGears 不会先发制人地向您提供过多的特性。虽然对于全栈来说,这可能意味着在全栈应用程序中启动和运行某些功能可能需要更多的工作,但这也意味着在简单的返回和 API 方面,TurboGears 变得比 Django 更加通用。
我非常喜欢 TurboGears 的另一点是编程接口。许多 Python 的 web APIs 并没有在它们的 API 中使用令人难以置信的 Python 方法,但是 TurboGears 确实做到了这一点。界面非常 Python 化,非常面向对象。
№9:败家子
Black Sheep 是一个 web 框架,灵感来自于像 ASP.NET 和 Flask 这样的优秀工具,可以用来轻松地创建基于事件的 web 应用程序。尽管名为 Blacksheep,但它是一个非常普通的 web 框架,具有一些独特的特性,可以使它成为您下一个项目的资产。首先,Blacksheep 可以是同步或异步的,并且是非阻塞的,如果您碰巧正在寻找这种东西,这是很好的,因为这个列表中的许多其他框架不是这个就是那个。然而,Blacksheep 也允许您根据请求更改这一点。换句话说,您可以混合异步和同步操作。
使用 Blacksheep 的语法非常类似于 Flasks 的语法。大多数时候,语法最终只是略有不同——这并不奇怪,因为 web-framework 是建立在 Flask 之上的。话虽如此,我也认为这是一件好事,因为它很熟悉。
№8:龙卷风
Tornado 不仅是一个伟大的 web 框架,而且还是一个异步网络库。凭借这一特性,Tornado 能够为数千个开放连接提供服务。这是因为 tornado 使用非阻塞网络进出流。这使得 Tornado 成为那些希望在运行不同 web 服务器的同时将许多端点部署为同一服务器的一部分的人的非常合适的解决方案。
在语法方面,Tornado 采用了一种非常类似涡轮的路由方法。使用 Tornado,而不是把逻辑放在路线中,你通常会构建类来提供回报。
№7:金字塔
名单上的下一个是金字塔。Pyramid 是一个很棒的网络框架,因为它非常有效地在轻量级和功能丰富之间取得了一个稳固的平衡。当然,我相信这种平衡在某种程度上是一种光谱,但金字塔对我来说是一个合适的位置。我不能忍受某些框架的一点是,当你不需要它的时候,所有的膨胀都被扔进了你的项目,而金字塔在这种膨胀中取得了很好的平衡。同时不要将太多的特性混合在一起,以至于仅仅通过查看项目目录就感觉过载。
金字塔既可以是基本的,也可以是复杂的,这可能会吸引那些希望在未来扩展其端点的数据科学家。像前面提到的一些框架一样,Pyramid 采用非常 Pythonic 化的、面向对象的方法来构建全栈应用程序和端点。
№6:姜戈
说到 Python 的网络开发,Django 已经成为工作的同义词。这是有充分理由的,因为如果说 Django 有什么特质的话,那就是包容性。Django 打包了一些非常复杂的全栈 web 开发可能需要的所有工具。也就是说,Django 的最终目标是 web 开发人员,而不是数据科学家。
对于我所做的工作,Django 最大的缺陷仅仅是它不是针对我的。虽然这个列表强调了端点和仪表板,数据科学家的网络开发,我仍然认为 Django 必须被提及,因为它是一个典型的 Python 网络框架,在业界非常流行。我发现 Django 对您正在创建的应用程序做了很多假设,制造了很多额外的依赖项和代码,最终导致了很多膨胀,而您想要做的只是部署一个端点。此外,在制作仪表板之类的东西时,Django 仍然存在问题,因为它更像是一个后端工具。也就是说,Django 只是为了一个目的而创建的,而 Flask 服务于 Django 所缺乏的目的,所以这些属性对这个包都有意义。
№5: Jam.py
接下来是专门针对数据工程的 Python 编程语言的 web 框架。虽然这个网络框架肯定是直接针对数据科学家的,但它仍然相对通用,可以用于几乎任何事情。然而,Jam.py 真正的亮点在于创建分析仪表板。
与这个列表中的许多其他选项相比,Jam.py 是非常基于模板的。虽然这在某些情况下可能是负面的,例如端点,但它确实为使用该框架构建仪表板带来了显著的优势。这种基于模板的方法的伟大之处在于,它使得非常快速地构建像交互式仪表板这样的全栈应用程序变得容易得多。这意味着在大多数情况下,Jam.py 非常容易使用!
真正让 Jam.py 变得出色和独特的是,Jam.py 不仅仅是一个 Python web 框架,还是一个用于构建交互式仪表盘和基于 web 的演示的 Python web 开发集成开发环境(IDE)。IDE 是全浏览器的,基本工作原理是创建一个服务器,然后允许您在该服务器上进入一个仪表板构建应用程序。这使得完全有可能利用 Jam.py,即使你甚至不知道 Python!Jam.py 也是可扩展的,这意味着基本上任何 Javascript 或 Python 库的元素都可以轻松地实现到用 Jam.py 构建的仪表板中。Jam.py 对我来说是一个额外的加分,因为它是如此易于访问和使用,因为真的没有其他东西像它一样!
№4:虚线
在这个列表中排在第四位的是 Plot.ly Dash。有些人可能会惊讶地发现这个包在这个列表中排在这么靠前的位置,但是 Dash 确实是一个构建交互式仪表盘的好工具。当然,Dash 是一个如此神奇的工具的大部分原因是因为 Plot.ly 首先是非常棒的。
虽然 Dash 确实有其缺点,但在我看来,它确实是 Plot.ly 和那个令人惊叹的图形库的功能,它将这些交互式仪表盘带到了一个高点。Plot.ly 可视化很容易制作,而 Dash 框架使它们更容易呈现。Dash 是基于 Flask 的,所以它的大部分工作方式与人们通常对路由语法的预期完全相同。在我看来,Dash 最大的缺点就是缺乏可扩展性和定制性。虽然 Dash 支持 Plot.ly 可视化,但让其他东西工作,甚至定制用户界面可能会非常困难。有些方面甚至是不可能改变的,有点不幸。
3 号:烧瓶
如果有一个网络框架,我可以称之为“可靠”,那就是 Flask。虽然 Flask 可能有点简单,但这种简单也有利于它。作为终结点和类似事物的行业标准,Flask 实际上非常容易使用,并且能够快速响应给定的请求。
它也可以是多才多艺的。尽管它可能依赖于一些相对基本的模板引擎,但它最终仍然能够做很多事情,而这个列表中的其他 web 框架已经做了足够多的工作。你也可以在 Flask 中添加许多附加组件或额外的功能,因为你可能已经注意到许多这些包都是基于 Flask 的。如果您想从头到尾学习如何开发和部署 Flask endpoint,包括服务器管理,我实际上有一个很酷(也很经典!是两年前写的!)的文章,您可以在这里查看!:
№2: FastAPI
另一个我非常喜欢的 Python web 框架是 FastAPI。我喜欢 FastAPI 的一点是,它被创建得简单而轻量。与这个列表中的许多其他选项相比,FastAPI 更轻量级,响应速度更快。这当然是我在寻找一个好的数据科学网络框架时所寻找的东西。因此,我认为这是我个人的首选。我还认为这个 web 框架非常容易使用,在很多方面它都很像 Flask。
№1:埃米特
我最近偶然发现并爱上的一个 web 开发框架是 Emmett。Emmett 是一个很棒的 web 框架,因为它速度快,易于使用,最重要的是:
别挡路!
这并不是来自与我同名的框架的偏见,它只是一个方便易用的框架!在很多方面,Emmett 属于 Flask 一方,但用许多可选的内置功能进行了升级。最后,我认为埃米特是最好的选择之一!由于结合了必要的后端 web 开发特性(如身份验证)和前端特性(如高质量的模板引擎),甚至还有可能做一些前端和后端的事情。总的来说,这是一个很好的选择,我写了一整篇关于它有多好的文章,如果你愿意,你可以在这里阅读:
[## 用 Python 语言用 Emmett 进行优雅的 Web 开发
towardsdatascience.com](/elegant-web-development-with-emmett-in-python-612ed898a71e)
结论
数据科学的 web 开发方面通常是该领域中被低估的部分,尤其是对于入门级的初级数据科学家而言。然而,尽管忽略了这个主题,端点和仪表板是数据科学领域的一个非常重要的支柱。幸运的是,由于数据科学目前围绕着 Python,我们有大量非常好的选项来完成我们需要完成的工作!事实上有太多好的选择,很难把它们缩小。我还认为,最终这些可能会不时地为我切换,因为我可以互换使用它们,因为它们都有稍微不同的用例。
如果你没有看到一个你真正喜欢用来写东西的框架,我很乐意在回复中了解更多!请记住,这些只是我的主观喜好,其中一些是出于我喜欢在 web 框架中看到的主观原因。当然,你对此的想法可能会有所不同,所以我很想听听你对此的看法!非常感谢你阅读这篇文章,我希望它能鼓励一些人跳出框框思考如何创建你的下一个网络应用。
我最喜欢的管理、组织和阅读研究论文的工具
这样你就不会迷失在文件的海洋中。

在 Unsplash 上 Mari Helin 拍摄的照片
如果说我看得更远,那是因为我站在巨人的肩膀上*——艾萨克·牛顿。*
上面的引用首次出现在牛顿于 1675 年写给罗伯特·胡克的一封信中。从那时起,这句话就被用来象征科学的进步和发展。人们常说,了解机器学习领域最新动态的最佳方式之一是阅读研究论文。然而,这说起来容易做起来难。阅读研究论文并不是每个人都喜欢的。虽然许多人觉得它们令人生畏,但其他人觉得不可能跟上每天发表论文的剂量。 Arxiv —查找此类论文的必去网站之一,已提交超过190 万篇论文(截至 2021 年 10 月*)并且每天都在增加。更糟糕的是,还有一个` 怕漏了' 上的最新领域。如果你属于这个群体,不要担心,你不是唯一的一个。许多人都在同一条船上航行,以至于有一个完整的吴恩达关于阅读研究论文的讲座。*
吴恩达关于阅读斯坦福 CS230 课程研究论文的讲座
阅读研究论文确实是一门艺术,可以从一些方便的工具开始,随着时间的推移而发展。在这篇文章中,我想分享几个这样的工具,我用它们来组织我最喜欢的研究论文,并获得最新的信息。这不是一个详尽的列表,但对于那些研究和文献综述领域的新手来说,这是一个很好的起点。
1.ArXiv 理智保护者
在处理 ArXiv 的研究论文时,我使用 Arxiv 理智保护程序来保护我的理智。由 Andrej Karpathy 创建的网站 Arxiv Sanity Preserves 是一个帮助你找到你最喜欢的论文和该领域趋势的网络界面。该网站为你提供了一个搜索引擎来查找任何主题的论文。然后,您可以将喜爱的论文保存在您的库中,以便以后访问。根据您在网站上的搜索,该网站还将提供建议,这些建议将随着时间的推移而改进。这是 Karpathy 的一个很棒的介绍视频,解释了如何浏览网站。
Andrej Karpathy 关于使用 arxiv-sanity 的视频
根据作者的说法,目前的网站正在维持生命,他正在开发第二版。

图片作者|来源: Reddit
有趣的事实:以 CFOP Speedcubing 闻名于世的巴德梅菲斯托实际上是安德烈卡帕西😲
2.ArXiv 虚荣
Arxiv Vanity 是一个有用的网络界面,用于查看来自 arXiv 的研究论文。也就是说;它以更易读的格式呈现 arXiv 文件,令人赏心悦目。根据作者的说法:
arXiv Vanity 将 arXiv的学术论文呈现为响应性网页,这样你就不用眯着眼睛看 PDF 了。

Arxiv 虚空截屏|作者图片
3.相关论文
一篇研究论文不是孤立产生的。相反,每篇论文都与其他几篇论文有联系。读者会对论文中提到的参考文献和引文感兴趣。 Connected papers 是为解决这一特定问题而创建的可视化工具。它创建由与感兴趣的论文具有最强联系的论文组成的图。除此之外,它还展示了与论文相关的前期和衍生作品。

相关论文的截屏|作者图片
4.门德利咨询经理
我最初是在一个论坛上了解到 Mendeley 推荐经理的。Mendeley reference manager 是一款免费工具,可以帮助您在一个地方存储、组织和注释您的研究论文。笔记本设施使你能够在阅读时做笔记,也可以整理你在论文中的各种重点。下面的演示 gif 应该给你一个关于它的用法的好主意。

Mendeley Reference Manager 桌面应用的屏幕截图|作者图片
5.代码论文—方法语料库
我们都喜欢代码为的网站论文——这是一个免费开放的资源,包含机器学习论文、代码、数据集、方法和评估表。然而,我想指出网站上的方法部分,我发现它特别有用。

作者图片来自代码为的论文网站
这一部分包含了组织良好、分类有序的论文。每个类别还包括根据使用的方法类型分类的论文。例如,有一个计算机视觉类别,包含与计算机视觉技术相关的论文。然后,它被进一步分为 853 个子类别,每个子类别包含一个基于不同计算机视觉方法的论文集。

截屏来自代码为的论文网站|图片由作者提供
结论
我在上面文章中提到的工具和网站给了我很大的帮助。我现在在阅读论文时做笔记,然后用笔记来写文章和帖子。机器学习领域正在快速变化,每隔一天就会有新的研究和最先进的实现问世。然而,这种进步是由于前人不屈不挠的工作和知识。因此,阅读研究论文、理解、实施并以此为基础是研究和科学进步的真正本质,这让我们想起牛顿的名言——“如果我看得更远,那是因为我站在巨人的肩膀上”
我作为医疗保健数据分析师的头 100 天——我学到的经验
原文:https://towardsdatascience.com/my-first-100-days-as-a-healthcare-data-analyst-lessons-i-learned-c8422d463575?source=collection_archive---------8-----------------------

图片来源:作者
医疗保健是人类生活中发展最快、需求最大的领域之一。如今,随着技术的快速发展,医疗保健行业从未落后过。无论是计算机的发明,互联网的繁荣,还是现代的大数据和人工智能,医疗保健都是一个需要适应和发展的行业。
医疗行业对数据分析的需求是巨大的。
大数据改变了我们跨行业(包括医疗保健)分析、管理和利用数据的方式。随着世界范围内的数字革命,数据驱动的质量改进成为医疗保健转型的核心。
我认为,一个组织的医疗保健决策和分析的有效性取决于其利用这些数据的能力。这就是高效医疗保健数据分析师的价值所在(就是我!)
我获得了管理信息系统硕士学位,对金融分析有着强烈的热情,并在金融科技领域追求自己的职业生涯……直到我参加了一门医疗保健分析课程,迎来了真相时刻。医疗保健数据和分析鼓励我继续前进,在夜里 3 点,我以同样的热情工作了五个小时。进入医疗保健行业三个月后,我收到了芝加哥蓝十字蓝盾公司的录用通知!
我在 6 月份加入了这家公司,到目前为止,我学到了很多东西,更重要的是学到了很多东西!我过去在学校课堂上的经历(伊利诺伊大学芝加哥分校让我为“现实世界”做好了准备),在百事可乐两个不同团队的实习经历,团队项目一切都可以在今天使用。
以下是我在过去 100 天里学到的一些东西。
1.提高您的 SQL 技能
回到研究生院,当教授讲授实体关系图(ERD)时,我会最不注意,而今天当我每隔一天使用实体关系图时,我意识到保持强大的数据库知识基础是多么重要,而且至少要有从云存储到社交媒体帐户的 Spark 流的初级概念知识。
如果我的公司没有大数据查询,我的一天就是不完整的,当一天中使用五个不同的数据湖来获取和处理一天的数据时,您不可能不深入学习 SQL。
从理解左连接和内连接、子查询(查询中的查询)或创建临时表之间的区别和影响,当您作为数据分析师工作时,关于 SQL 的一切都很重要。
2.为你的工作建立商业敏锐性
在医疗保健行业,商业语言就像任何常用术语一样。不仅仅是行话,还有一个国家的保险历史、流程架构、资金流、数据流、责任和业务的利益相关者——你需要每个部分都参与到这个拼图中才能收工。
当我作为一名数据分析师加入我的公司时,前三周的培训只是习惯于业务语法、大量术语、业务资源、公司愿景和使命,最后是数据部分,这是你被聘用的原因。
你可能擅长编写 Python 代码、预测建模或 BI 工具,但如果你不了解业务所涉及的风险,一个数字上升或下降对你来说并不重要。与此相反,如果你知道你理解数据分析中的错误术语对业务意味着什么,你的故事就完成了。
流程中每一步的基本第一步是设置正确的上下文。从提出正确的问题到选择正确的数据,再到向最终消费者解释分析结果,一个好的故事需要商业头脑有目的地描绘出结果。
数据分析和商业敏锐性应该是赞美,而不是替代。
3.理解数据
如果你是一个面向业务的数据专家,你就是这个能够奇迹般地将来自两个不相交数据源的数据点组合起来并提供答案的向导。对于像这样的随机请求,如果您不太了解您的数据,那么您现在就陷入了一个试图找到一个数字的恶性循环中。
医疗保健数据非常庞大;从公共或私有数据、提供商数据、索赔数据、调查/营销数据、关于一般消费模式的信息、医疗保健提供系统生成的管理数据、医生笔记到保险数据等等。
每过一天,我都意识到了解我将使用的数据的显著特征对我来说是多么重要:数据来自哪里,谁拥有数据,多个数据集的用例,数据质量,每个数据集的不同数据点。
归根结底,每个数据问题都遵循标准的数据科学生命周期-从业务理解到数据清理和挖掘、数据探索、功能工程、预测建模和数据可视化,再到将分析与业务相关联-数据为您指路。
能够编写模拟现实世界流程的代码,并有切实的成果来解决重要问题。
比如,你想找到你的哪个客户有可能会再购买保险,如果他们不买,你想找到原因。为此,您需要您的数据点到位-人口统计信息(患者姓名、出生日期、性别、地址、受保护健康信息(PHI))-拉动保险信息、计划名称、共付额、保险日期、网络信息以及更多此类数据字段。
一个高效的医疗保健数据分析师可以定义一个良好的数据模型,该模型可以捕获所有需要的数据元素,并以一种有意义的方式将它们关联起来,以反映实际的工作流。
4.了解您的工具
作为一名应届毕业生开始一份工作,有一个不言而喻的语法——你应该知道工具,并具备所需的技能和能力。总会有一段培训期,但没人会教你大量的工作。你学习并适应。
我在研究生院的实习对我帮助最大。
实习开始让你准备好流行数据工具所需的面向业务的知识。
结构化查询语言(SQL)
一个实际操作的数据分析师必须能够通过结构化查询语言(SQL)来操作数据库,并从中清晰地表达出一份报告。当我说 SQL 时,认识到 SQL 有各种不同的方言,我一般指的是以 SQL 为语言核心通过代码处理数据库的能力。
MS Excel
怎么强调这些 excel 快捷方式对你的帮助都不为过。如果没有什么,数据透视表和宏的坚实基础是一个很好的起点。
我的日常工作主要涉及基本的 Excel 功能,如连接、查找和替换、过滤和排序、条件格式、索引匹配、删除重复项、逻辑函数、宏和数据透视表。
(舞台上由人扮的)静态画面
或者任何数据可视化工具…
我一直是 Tableau 的粉丝,能够有效地呈现数据,让外行观众以预期的方式进行解读,这是我认为一个普通分析师和一个杰出分析师的区别。
讲故事是业务流程中不可或缺的一部分——发出危险信号或使用数据提出建议,在不牺牲质量的情况下降低成本。
计算机编程语言
在开始新工作之前,至少掌握一门数据编程语言的中级到高级知识总是一个很好的起点。
5.PowerPoint 和沟通技巧
作为数据专业人员,没有人会花时间关注演示或沟通技巧。
如果你没有面对客户,你仍然会向你的团队做报告,向别人传达你的发现。确定你的听众,并根据他们的知识水平传达你的演讲。
我通常从声明任务的为什么开始。我的导师建议我总是从陈述你为什么做你所做的事情开始——这为根深蒂固的对话设置了前提,并设置了正确的背景。一两分钟的介绍,涵盖议程并直接进入任务的结果。一旦人们看到了结果,当你向他们讲述你是如何到达那里的以及克服了哪些挑战时,他们会更好地理解。
6.负责任的领导
除了掌握这些技能,还应该注重提高个人的责任感。表现出责任感的人会在组织中脱颖而出。
你在承担责任时的行为让你值得被认可。
就我个人而言,我总是努力使我的目标和价值观与组织的目标和价值观保持一致。你要对你工作的成败负责,因此,一个人应该计划你的工作对他人的影响。
这就是我的博客的结尾。感谢您的阅读!如果你也在医疗保健领域,请在评论中告诉我。我很想知道更多关于你的作品。
如果你喜欢阅读这样的故事,并希望支持我成为一名作家,可以考虑使用这个链接注册成为一名媒体会员(我可以赚取一小笔佣金,而不需要你额外付费!)
数据帐篷快乐!
Rashi 是一名来自芝加哥的数据奇才,他喜欢将数据可视化,并创造有见地的故事来传达隐含的见解。当她没有赶着赶上学校的最后期限时,她喜欢喝一杯好咖啡,写关于数据的博客……
