预测性能漂移:另一面的硬币
我们知道原因,接下来讨论类型
·
关注 发表在 Towards Data Science ·7 min 阅读·2023 年 2 月 2 日
–
预测性能漂移的两个方面(作者提供的图片)
机器学习的世界发展如此迅速,在不到二十年的时间里,我们已经进入了下一个阶段。模型已经建立,现在我们需要知道它们是否在短期、中期和长期内提供准确的预测。构建模型时,我们口袋里有很多方法、理论方法、思潮、范式和数字工具。现在,我们希望更好地理解预测性能随时间变化的风险。
有人可能认为,模型的预测性能是由测试集的规定质量来保证的,测试集是一组在模型训练过程中未参与的样本,但在那时进行测量或观察。现实是,尽管模型可能在基于该测试集的情况下以某种(希望非常令人满意的)预测性能交付,但随时间推移的观测将不会完全具备与训练集和测试集相同的属性或趋势。请记住,模型是机器,而不是思考实体。如果它们看到一些新的、未曾训练过的东西,它们无法对这些新事物进行批判。我们,人类,可以。
预测性能漂移:A 面
我们对预测性能的思考方式涉及将影响模型的变异分布分开。有输入特征的分布P(X)、标签的分布P(Y),以及给定输入的标签的条件分布P(Y|X)。这些组件中的任何变化都是模型在预测中产生更多不准确结果的潜在原因。考虑到这一点,如果我们监控这些组件并且它们中的某些部分发生变化,那么就需要进行深入检查,看看发生了什么,并在必要时修复。
预测性能漂移:B 面
虽然之前的内容无可争议,但我们也可以从预测性能漂移的另一个维度来观察:漂移的类型。当我们观察预测性能时,可以区分不同类型的漂移。那这样做有什么用呢?实际上,这不仅能检测到某些变化,还能更容易地知道如何修复这些变化。
漂移的类型大致可以分为四种:偏差、斜率、方差和非线性。虽然这些名称可能更具体地指向线性回归模型,但它们也适用于一般的机器学习模型。这些类型的更通用名称可以是常量偏移、相对于预测边界的旋转、与边界收敛的分散以及边界形状的变化。我们可能会同意,后者的名称较长且书写和讨论起来比较费时,因此我们坚持使用前者的名称。在这样做的同时,也不要将任何类型与模型的性质混淆。任何四种类型的漂移都可能发生在任何类型的模型中。
回归模型的漂移类型(作者提供的图片)
通过检查不同类型的漂移,不仅可以检测到某些变化,还能更容易地知道如何修复这些变化。
B 面:四种类型
偏差漂移指的是恒定的偏移。在回归模型中,如果我们观察到我们的预测值与观察值之间存在恒定差异,则发生了偏差漂移。在分类模型中,如果每个类别的特征有恒定的偏移,这也可能是特定于类别的。这种漂移可能仅仅是由于总体均值的变化,而特征之间及其对目标变量的影响没有其他变化。由于是简单的变化,可以通过重新调整模型的偏差或样本中的均值来轻松修复。
斜率漂移发生在回归函数或决策边界相对于中心的旋转时。这种漂移在涉及从不同角度拍摄但本质上相同的图像的学习任务中非常常见。根据旋转的程度以及旋转是否是分类任务特定的,斜率漂移可以通过简单调整旋转或完全重新训练模型来修复。
线性分类器的漂移类型示例(图片作者提供)
方差漂移,顾名思义,是指离散度的增加。如果离散度推动样本越过任务边界,它将对预测性能造成损害。在回归的情况下,方差漂移表现为更大的残差。在分类的情况下,这种漂移可能是每个类别中原始特征离散度的扩展。同样,这种漂移也可能是特定于类别的。这种漂移可能由不同原因造成,它可能对应于靠近决策边界的更大采样或输入特征的不均匀偏移。如果是由于采样问题,这个问题可以通过使用更能代表当前采样的更新数据重新计算模型来解决。
非线性分类器的漂移类型示例(图片作者提供)
最后一种漂移称为非线性漂移。虽然从展示的示例中看起来非常具体和系统化,这种漂移可能包含前面提到的三种类型的所有组合。著名的教学案例涉及使用 2 个输入特征来分隔 2 个类别。在二维空间中,这两个类别可能通过一个线性函数来分隔,但 2 个点的简单旋转会引发非线性变化。如果这些类别不再能通过一条直线来分隔,那么问题就变成了非线性问题。正如其他情况一样,对于分类任务,这种漂移也可能是特定于类别的,如上面线性分类的示例所示。由于这是最复杂的漂移类型,可能在模型的预测质量中观察到的非线性模式可能总是需要对模型进行重新训练。
从线性到非线性分类的示例(图片由作者提供)
何时预测性能会退化?
漂移并不总是有问题。让我们稍微深入一下。X的中心发生变化而Y的中心没有变化,或者反之,将导致偏差漂移。然而,如果X和Y都按照模型函数的要求移动,那么预测性能将不会漂移,因为模型将继续做出准确的预测。谈到斜率时,虽然回归任务中旋转的空间较小,但在分类任务中,只要这种旋转不与决策边界重叠,类别可以旋转而不影响性能。这可能是一个非常理论性的事实,在实践中的发生率较小,但事情就是这样。
方差漂移对于分类问题可能比斜率漂移更现实。只要方差的扩散没有与决策边界重叠,模型就能处理方差漂移。当数据点开始穿越决策边界时,预测性能会受到影响。回归模型也是如此,尽管与斜率漂移的情况类似,对于方差扩展的容忍度非常小,必须考虑预测性能的退化。
非线性漂移是最不允许的。这些变化可能总是会有问题。模型可以对许多与之前漂移相对应的变化保持鲁棒性,但它们是为了处理特定任务和特定形状而设计的。这个形状是我们创建模型以预测的概念的表现,如果这个形状开始改变,那么整个概念可能也在改变。因此,在非线性变化不是问题的情况下很难思考。
让我们继续抛硬币
因此,我们现在有了一种新的视角来思考模型预测性能的退化。我们不仅配备了原因,还配备了类型,当我们监控模型的预测质量时,可以继续抛硬币。这样,我们不仅能获得什么和何时发生了变化的信息,还能了解为什么发生变化以及如何发生变化。
我们是否最终会形成“谁”和“哪里”的定义?
准备应对气候变化与 AI 助手
通过对话简化复杂数据
·
关注 发表在 Towards Data Science ·13 分钟阅读·2023 年 11 月 26 日
–
使用 OpenAI 的 ChatGPT 和 Dall-E-3 生成的图像
摘要
在这篇文章中,我们探讨了如何使用来自 Probable Futures API 和新的 OpenAI Assistants API 创建一个对话式 AI 代理。这个 AI 代理能够回答有关气候如何影响特定地点的问题,并且还能进行基本的数据分析。AI 助手非常适合这样的任务,为非技术用户提供了展示复杂数据的有希望的途径。
我最近和一个邻居聊起了气候变化可能对我们产生的影响,以及如何最好地准备房屋以应对极端天气事件。有一些很棒的网站提供了与此相关的地图信息,但我想知道是否有时候人们只是想问像“我的家会如何受到气候变化的影响?”和“我可以怎么做?”这样的问题,并获得一个简明的总结和如何准备的建议。所以我决定探索一下最近几周发布的一些 AI 工具。
OpenAI 的助手 API
由大型语言模型如 GPT-4 驱动的 AI 代理正在成为人们通过对话与文档和数据互动的一种方式。这些代理解读人们的提问,调用 API 和数据库以获取数据,生成并运行代码进行分析,然后将结果呈现给用户。像 langchain 和 autogen 这样出色的框架正在引领潮流,提供了轻松实现代理的模式。最近,OpenAI 也加入了这个行列,推出了 GPTs 作为一种无代码的创建代理的方法,我在 这篇文章 中探索了这一点。这些设计得非常好,为更广泛的受众打开了大门,但它们确实有一些限制。它们需要一个带有 openapi.json 规范的 API,这意味着它们当前不支持 graphql 等标准。它们也不支持注册功能的能力,这对于无代码解决方案来说是可以预期的,但可能限制了它们的能力。
介绍 OpenAI 最近的另一个发布——助手 API。
助手 API(测试版)是一种编程方式来配置 OpenAI 助手,支持功能、网页浏览和从上传文档中检索知识。与 GPTs 相比,功能是一个很大的区别,因为这些功能使得与外部数据源的互动变得更加复杂。功能是指大型语言模型(LLMs)如 GPT-4 被告知某些用户输入应导致调用代码函数。LLM 会生成一个 JSON 格式的响应,包含调用函数所需的确切参数,然后可以用于本地执行。要详细了解它们如何与 OpenAI 一起工作,请参见 这里。
气候变化综合 API——Probable Futures
为了能够创建一个 AI 代理来帮助准备应对气候变化,我们需要一个好的气候变化数据来源和一个 API 来提取这些信息。任何这样的资源都必须采用严格的方法来结合气候模式(GCM)预测。
幸运的是,Probable Futures的团队做得非常出色!
Probable Futures提供了与气候变化预测相关的各种资源
Probable Futures 是“一个非营利气候素养倡议,提供在线实用工具、故事和资源,面向所有人,无论身在何处”,他们提供了一系列基于 CORDEX-CORE 框架的地图和数据,这是对来自 REMO2015 和 REGCM4 区域气候模型的气候模型输出的标准化。[附注:我与 Probable Futures 没有关联]
重要的是,他们提供了一个GraphQL API来访问这些数据,我在请求 API 密钥后可以访问。
根据文档我创建了函数,并将其保存在文件assistant_tools.py
中…
pf_api_url = "https://graphql.probablefutures.org"
pf_token_audience = "https://graphql.probablefutures.com"
pf_token_url = "https://probablefutures.us.auth0.com/oauth/token"
def get_pf_token():
client_id = os.getenv("CLIENT_ID")
client_secret = os.getenv("CLIENT_SECRET")
response = requests.post(
pf_token_url,
json={
"client_id": client_id,
"client_secret": client_secret,
"audience": pf_token_audience,
"grant_type": "client_credentials",
},
)
access_token = response.json()["access_token"]
return access_token
def get_pf_data(address, country, warming_scenario="1.5"):
variables = {}
location = f"""
country: "{country}"
address: "{address}"
"""
query = (
"""
mutation {
getDatasetStatistics(input: { """
+ location
+ """ \
warmingScenario: \"""" + warming_scenario + """\"
}) {
datasetStatisticsResponses{
datasetId
midValue
name
unit
warmingScenario
latitude
longitude
info
}
}
}
"""
)
print(query)
access_token = get_pf_token()
url = pf_api_url + "/graphql"
headers = {"Authorization": "Bearer " + access_token}
response = requests.post(
url, json={"query": query, "variables": variables}, headers=headers
)
return str(response.json())
我有意排除了datasetId
以检索所有指标,这样 AI 代理就能拥有广泛的信息供其使用。
该 API 非常强大,既可以接受城镇和城市,也可以接受完整地址。例如…
get_pf_data(address="New Delhi", country="India", warming_scenario="1.5")
返回包含位置气候变化信息的 JSON 记录…
{'data': {'getDatasetStatistics': {'datasetStatisticsResponses': [{'datasetId': 40601, 'midValue': '17.0', 'name': 'Change in total annual precipitation', 'unit': 'mm', 'warmingScenario': '1.5', 'latitude': 28.6, 'longitude': 77.2, 'info': {}}, {'datasetId': 40616, 'midValue': '14.0', 'name': 'Change in wettest 90 days', 'unit': 'mm', 'warmingScenario': '1.5', 'latitude': 28.6, 'longitude': 77.2, 'info': {}}, {'datasetId': 40607, 'midValue': '19.0', 'name': 'Change in dry hot days', 'unit': 'days', 'warmingScenario': '1.5', 'latitude': 28.6, 'longitude': 77.2, 'info': {}}, {'datasetId': 40614, 'midValue': '0.0', 'name': 'Change in snowy days', 'unit': 'days', 'warmingScenario': '1.5', 'latitude': 28.6, 'longitude': 77.2, 'info': {}}, {'datasetId': 40612, 'midValue': '2.0', 'name': 'Change in frequency of “1-in-100-year” storm', 'unit': 'x as frequent', 'warmingScenario': '1.5', 'latitude': 28.6, 'longitude': 77.2, 'info': {}}, {'datasetId': 40101, 'midValue': '28.0', 'name': 'Average temperature', 'unit': '°C', 'warmingScenario': '1.5', 'latitude': 28.6, 'longitude': 77.2, 'info': {}}, {'datasetId': 40901, 'midValue': '4.0', 'name': 'Climate zones', 'unit': 'class', 'warmingScenario': '1.5', 'latitude': 28.6, 'longitude': 77.2, 'info': {'climateZoneName': 'Dry semi-arid (or steppe) hot'}}, {'datasetId': 40613, 'midValue': '49.0', 'name': 'Change in precipitation “1-in-100-year” storm', 'unit': 'mm', 'warmingScenario': '1.5', 'latitude': 28.6, 'longitude': 77.2, 'info': {}}, {'datasetId': 40701, 'midValue': '7.0', 'name': 'Likelihood of year-plus extreme drought', 'unit': '%', 'warmingScenario': '1.5', 'latitude': 28.6, 'longitude': 77.2, 'info': {}}, {'datasetId': 40702, 'midValue': '30.0', 'name': 'Likelihood of year-plus drought', 'unit': '%', 'warmingScenario': '1.5', 'latitude': 28.6, 'longitude': 77.2, 'info': {}}, {'datasetId': 40704, 'midValue': '5.0', 'name': 'Change in wildfire danger days', 'unit': 'days', 'warmingScenario': '1.5', 'latitude': 28.6, 'longitude': 77.2, 'info': {}}, {'datasetId': 40703, 'midValue': '-0.2', 'name': 'Change in water balance', 'unit': 'z-score', 'warmingScenario': '1.5', 'latitude': 28.6, 'longitude': 77.2, 'info': {}}, {'datasetId': 40201, 'midValue': '21.0', 'name': 'Average nighttime temperature', 'unit': '°C', 'warmingScenario': '1.5', 'latitude': 28.6, 'longitude': 77.2, 'info': {}}, {'datasetId': 40205, 'midValue': '0.0', 'name': 'Freezing days', 'unit': 'days', 'warmingScenario': '1.5', 'latitude': 28.6, 'longitude': 77.2, 'info': {}}, {'datasetId': 40301, 'midValue': '71.0', 'name': 'Days above 26°C (78°F) wet-bulb', 'unit': 'days', 'warmingScenario': '1.5', 'latitude': 28.6, 'longitude': 77.2, 'info': {}}, {'datasetId': 40302, 'midValue': '24.0', 'name': 'Days above 28°C (82°F) wet-bulb', 'unit': 'days', 'warmingScenario': '1.5', 'latitude': 28.6, 'longitude': 77.2, 'info': {}}, {'datasetId': 40303, 'midValue': '2.0', 'name': 'Days above 30°C (86°F) wet-bulb', 'unit': 'days', 'warmingScenario': '1.5', 'latitude': 28.6, 'longitude': 77.2, 'info': {}}, {'datasetId': 40102, 'midValue': '35.0', 'name': 'Average daytime temperature', 'unit': '°C', 'warmingScenario': '1.5', 'latitude': 28.6, 'longitude': 77.2, 'info': {}}, {'datasetId': 40103, 'midValue': '49.0', 'name': '10 hottest days', 'unit': '°C', 'warmingScenario': '1.5', 'latitude': 28.6, 'longitude': 77.2, 'info': {}}, {'datasetId': 40104, 'midValue': '228.0', 'name': 'Days above 32°C (90°F)', 'unit': 'days', 'warmingScenario': '1.5', 'latitude': 28.6, 'longitude': 77.2, 'info': {}}, {'datasetId': 40105, 'midValue': '187.0', 'name': 'Days above 35°C (95°F)', 'unit': 'days', 'warmingScenario': '1.5', 'latitude': 28.6, 'longitude': 77.2, 'info': {}}, {'datasetId': 40106, 'midValue': '145.0', 'name': 'Days above 38°C (100°F)', 'unit': 'days', 'warmingScenario': '1.5', 'latitude': 28.6, 'longitude': 77.2, 'info': {}}, {'datasetId': 40202, 'midValue': '0.0', 'name': 'Frost nights', 'unit': 'nights', 'warmingScenario': '1.5', 'latitude': 28.6, 'longitude': 77.2, 'info': {}}, {'datasetId': 40304, 'midValue': '0.0', 'name': 'Days above 32°C (90°F) wet-bulb', 'unit': 'days', 'warmingScenario': '1.5', 'latitude': 28.6, 'longitude': 77.2, 'info': {}}, {'datasetId': 40305, 'midValue': '29.0', 'name': '10 hottest wet-bulb days', 'unit': '°C', 'warmingScenario': '1.5', 'latitude': 28.6, 'longitude': 77.2, 'info': {}}, {'datasetId': 40203, 'midValue': '207.0', 'name': 'Nights above 20°C (68°F)', 'unit': 'nights', 'warmingScenario': '1.5', 'latitude': 28.6, 'longitude': 77.2, 'info': {}}, {'datasetId': 40204, 'midValue': '147.0', 'name': 'Nights above 25°C (77°F)', 'unit': 'nights', 'warmingScenario': '1.5', 'latitude': 28.6, 'longitude': 77.2, 'info': {}}]}}}
创建一个 OpenAI 助手
接下来,我们需要使用测试版 API 构建 AI 助手。在文档和非常有用的OpenAI Cookbook中有一些很好的资源。然而,由于这是新推出的测试版,信息还不是很多,所以有时需要一些试错过程。
首先,我们需要配置助手可以使用的工具,比如获取气候变化数据的功能。参考文档…
get_pf_data_schema = {
"name": "get_pf_data",
"parameters": {
"type": "object",
"properties": {
"address": {
"type": "string",
"description": ("The address of the location to get data for"),
},
"country": {
"type": "string",
"description": ("The country of location to get data for"),
},
"warming_scenario": {
"type": "string",
"enum": ["1.0", "1.5", "2.0", "2.5", "3.0"],
"description": ("The warming scenario to get data for. Default is 1.5"),
}
},
"required": ["address", "country"],
},
"description": """
This is the API call to the probable futures API to get predicted climate change indicators for a location
""",
}
你会注意到我们为每个参数提供了文本描述。根据实验,这似乎是助手在填充参数时使用的,所以要尽可能清晰,并记录任何特性,以便 LLM 能够调整。从这些定义工具…
tools = [
{
"type": "function",
"function": get_pf_data_schema,
}
{"type": "code_interpreter"},
]
你会注意到我保留了 code_interpretor,赋予助手运行数据分析所需代码的能力。
接下来,我们需要指定一组用户指令(系统提示)。这些指令在将助手的性能调整到我们的任务上时至关重要。通过一些快速实验,我得出了这一组…
instructions = """
"Hello, Climate Change Assistant. You help people understand how climate change will affect their homes"
"You will use Probable Futures Data to predict climate change indicators for a location"
"You will summarize perfectly the returned data"
"You will also provide links to local resources and websites to help the user prepare for the predicted climate change"
"If you don't have enough address information, request it"
"You default to warming scenario of 1.5 if not specified, but ask if the user wants to try others after presenting results"
"Group results into categories"
"Always link to the probable futures website for the location using URL and replacing LATITUDE and LONGITUDE with location values: https://probablefutures.org/maps/?selected_map=days_above_32c&map_version=latest&volume=heat&warming_scenario=1.5&map_projection=mercator#9.2/LATITUDE/LONGITUDE"
"GENERATE OUTPUT THAT IS CLEAR AND EASY TO UNDERSTAND FOR A NON-TECHNICAL USER"
"""
你可以看到我添加了指令,要求助手提供如网站等资源,帮助用户为气候变化做准备。这有点‘开放’,对于生产环境的助手,我们可能希望有更严格的筛选。
一件现在可能的奇妙事情是,我们还可以就一般的语气提供指令,在上面的例子中请求输出对非技术用户清晰。显然,所有这些需要一些系统化的提示工程,但有趣的是,我们现在在某种程度上通过劝说来‘编程’。😊
好的,现在我们有了工具和指令,让我们创建助手……
import os
from openai import AsyncOpenAI
import asyncio
from dotenv import load_dotenv
import sys
load_dotenv()
api_key = os.environ.get("OPENAI_API_KEY")
assistant_id = os.environ.get("ASSISTANT_ID")
model = os.environ.get("MODEL")
client = AsyncOpenAI(api_key=api_key)
name = "Climate Change Assistant"
try:
my_assistant = await client.beta.assistants.retrieve(assistant_id)
print("Updating existing assistant ...")
assistant = await client.beta.assistants.update(
assistant_id,
name=name,
instructions=instructions,
tools=tools,
model=model,
)
except:
print("Creating assistant ...")
assistant = await client.beta.assistants.create(
name=name,
instructions=instructions,
tools=tools,
model=model,
)
print(assistant)
print("Now save the DI in your .env file")
上述假设我们在.env
文件中定义了密钥和代理 ID。你会注意到代码首先检查是否存在代理,使用.env
文件中的ASSISTANT_ID
并进行更新,否则它会创建一个全新的代理,生成的 ID 必须复制到.env
文件中。否则,我会创建大量的助手!
一旦创建了助手,它将在OpenAI 用户界面上可见,可以在Playground中进行测试。由于大多数功能调用相关的开发和调试实际上涉及调用代码,我发现 Playground 对这次分析并不是特别有用,但它设计得很好,可能在其他工作中会有用。
对于这次分析,我决定使用新的GPT-4-Turbo模型,将model
设置为“gpt-4–1106-preview”。
创建用户界面
我们想创建一个完整的聊天机器人,因此我从这个chainlit 食谱示例开始,稍微调整了一下,将代理代码分离到一个专用文件中,并通过……
import assistant_tools as at
Chainlit 非常简洁,用户界面易于设置,你可以在这里找到应用的代码。
尝试我们的气候变化助手 AI 代理
把这些放在一起——见代码这里——我们用简单的chainlit run app.py
启动代理……
让我们询问一个位置……
注意到上述我故意拼写错误了 Mombasa。
然后代理开始工作,调用 API 并处理 JSON 响应(这大约花了 20 秒)……
根据我们的指令,它最终完成了……
但这是否正确?
让我们调用 API 并查看输出……
get_pf_data(address="Mombassa", country="Kenya", warming_scenario="1.5")
这会查询 API……
mutation {
getDatasetStatistics(input: {
country: "Kenya"
address: "Mombassa"
warmingScenario: "1.5"
}) {
datasetStatisticsResponses{
datasetId
midValue
name
unit
warmingScenario
latitude
longitude
info
}
}
}
这给出了以下内容(缩略显示)……
{
"data": {
"getDatasetStatistics": {
"datasetStatisticsResponses": [
{
"datasetId": 40601,
"midValue": "30.0",
"name": "Change in total annual precipitation",
"unit": "mm",
"warmingScenario": "1.5",
"latitude": -4,
"longitude": 39.6,
"info": {}
},
{
"datasetId": 40616,
"midValue": "70.0",
"name": "Change in wettest 90 days",
"unit": "mm",
"warmingScenario": "1.5",
"latitude": -4,
"longitude": 39.6,
"info": {}
},
{
"datasetId": 40607,
"midValue": "21.0",
"name": "Change in dry hot days",
"unit": "days",
"warmingScenario": "1.5",
"latitude": -4,
"longitude": 39.6,
"info": {}
},
{
"datasetId": 40614,
"midValue": "0.0",
"name": "Change in snowy days",
"unit": "days",
"warmingScenario": "1.5",
"latitude": -4,
"longitude": 39.6,
"info": {}
},
{
"datasetId": 40612,
"midValue": "1.0",
"name": "Change in frequency of \u201c1-in-100-year\u201d storm",
"unit": "x as frequent",
"warmingScenario": "1.5",
"latitude": -4,
"longitude": 39.6,
"info": {}
},
.... etc
}
]
}
}
}
进行抽查时,似乎代理完美地捕捉了它们,并向用户提供了准确的总结。
通过指令改善可用性
AI 代理可以通过一些指令来改进其信息呈现方式。
其中一项指令是始终生成一个返回到 Probable Futures 网站的地图可视化链接,点击后会转到正确的位置……
代理始终生成一个 URL,将用户带到正确的地图可视化上,以便在可能的未来网站上进行查询。
另一个指令要求代理始终提示用户尝试其他变暖情景。默认情况下,代理会生成预测全球温度升高 1.5 摄氏度的结果,但我们允许用户探索其他——有些令人沮丧的——情景。
分析任务
既然我们给了 AI 代理代码解释器技能,它应该能够执行 Python 代码来进行基本的数据分析。让我们尝试一下。
首先我询问了气候变化如何影响伦敦和纽约,代理提供了总结。然后我问了……
结果是代理使用代码解释器生成并运行 Python 代码以创建一个图表……
AI 代理能够使用从 API 提取的气候变化数据进行基本的数据分析任务。
不错!
结论与未来工作
通过使用 Probable Futures API 和 OpenAI 助手,我们能够创建一个对话界面,展示人们如何能够询问有关气候变化的问题并获得如何准备的建议。代理能够进行 API 调用以及一些基本的数据分析。这提供了另一种气候意识的渠道,这可能对一些非技术用户更具吸引力。
我们当然可以开发一个聊天机器人来确定意图/实体,并编写代码处理 API,但这需要更多工作,并且在 API 发生更改或添加新 API 时需要重新审视。此外,大型语言模型代理在解释用户输入和总结方面表现良好,并且可以运行代码和进行基本数据分析。我们的特定用例似乎特别适合 AI 代理,因为任务范围有限。
不过也有一些挑战,这个技术有点慢(查询完成大约需要 20-30 秒)。此外,LLM 令牌费用未在本文中分析,可能会很高。
也就是说,OpenAI Assistants API 仍处于测试阶段。此外,代理没有经过任何调整,因此通过进一步的工作,额外的常用功能、性能和成本可能会针对这一令人兴奋的新技术进行优化。
参考文献
本文基于 Probable Futures 提供的数据和其他内容,Probable Futures 是 SouthCoast Community Foundation 的一个项目,部分数据可能由 Woodwell Climate Research Center, Inc.或 The Coordinated Regional Climate Downscaling Experiment (CORDEX)提供
这个分析的代码可以在这里找到。
你可以在这里找到更多我的文章。
用故事地图互动展示空间数据 #2
原文:
towardsdatascience.com/presenting-spatial-data-interactively-with-story-maps-2-17363598208f
通过互动地图讲述故事和洞察
·发表于 Towards Data Science ·7 分钟阅读·2023 年 8 月 22 日
–
由 Sutan.co.uk 定制的故事地图(作者,2023)
演示链接:
由 Sutan.co.uk 提供的雅加达通勤故事。我们如何在雅加达通勤?使用 HTML、CSS 和 JavaScript 进行呈现…
jakarta.sutan.co.uk](https://jakarta.sutan.co.uk/?source=post_page-----17363598208f--------------------------------)
仓库链接:
[## GitHub — sutanmufti/story-maps
通过在 GitHub 上创建账户来为 sutanmufti/story-maps 的开发做贡献。
github.com](https://github.com/sutanmufti/story-maps?source=post_page-----17363598208f--------------------------------)
相关文章:
通过响应式地图讲述故事/洞察。用地图讲故事!
towardsdatascience.com
空间数据科学与制图
作为空间数据专家和数据科学家,我的工作是从(地理)空间数据中生成洞察。自然,最终的结果是将这些洞察可视化,以便人们理解这些洞察是什么。这需要沟通技巧。虽然普通的数据可视化围绕图表和图形展开,但空间数据可视化则围绕地图展开。制图师,负责制作地图的人员,本质上就是完成这项工作的人。
然而,我感觉“制图学”和“数据科学”之间的界限正在消退或融合。90 年代由 ESRI(环境系统研究所)推动的 GIS 的显著性,将信息技术整合到了制图学领域。这意味着地图不再是手工绘制的;相反,地图是基于你提供的表格绘制的。这是一个强大且新颖的范式!每个表格都可以通过一系列规则转换为地图。
“我们对制图学的定义将其描述为‘地图制作的艺术、科学和技术’,这一定义展示了这一学科的广度。” — 英国制图学会
什么使空间数据分析不同于数据分析?空间数据分析具有我称之为“空间属性”的东西;一个几何列。了解更多关于空间数据结构的信息,请参阅我的其他文章:
空间数据表达指南
towardsdatascience.com
从静态地图到互动地图
在互联网出现之前,空间数据分析的输出是静态地图。然而,随着 IT 技术的进步,我们现代的互联网浏览器能够处理互动性。这利用了 Javascript,一种使网站具有互动性的编程语言。这也惠及了制图界;我们可以将地图呈现为互动网页。看看 Google Maps 或 Openstreetmap。
好消息是,在这个开源和现代环境中,每个人都可以访问构建这种互动地图的工具。
本文
我想分享如何制作带有互动地图的空间数据可视化。更确切地说,它是一个故事地图;一个与段落相关的逐页地图。我希望这能激励人们生成更多互动地图,并作为点燃新颖空间数据可视化创意的灯塔。
我不会深入讲解代码。然而,我会讨论使事物互动的基本理念,并突出代码片段。你可以在 GitHub 仓库 这里 查看代码。
请注意,我将讨论的范围限制在使用 Javascript 处理的互动性。我不会讨论使用 CSS 的样式和使用 HTML 的布局。
雅加达故事地图(来源:作者,2023)
引入 Javascript 的互动性
虽然 Python 主要用于 GIS、数据科学和空间数据科学,但我们使用 HTML、CSS 和 Javascript 开发网站。要了解更多关于这些语言及其在空间数据科学中角色的信息,请阅读我的其他文章。
Spatial Data Science: Javascript & Python
从数据工程到全栈应用,你需要掌握的语言。入门以及精选学习资源……
towardsdatascience.com
你正在阅读的这篇文章的亮点是充分利用 JavaScript。处理用户输入或使我们的网站发生变化并活跃使用 JavaScript。
使用地图绘制库
我们不从零开始编码;使用模块/库是不可避免的。在地图绘制的情况下,经典的地图绘制库是 Leaflet(如前面文章中的链接所讨论的)。在上面的演示链接中,我使用 leaflet 来显示和动画化地图。
## Leaflet - an open-source JavaScript library for interactive maps
Leaflet 是一个现代的、轻量级的开源 JavaScript 库,用于移动友好的交互式地图。
请注意,这并不是唯一的地图绘制库,例如,Openlayers、Google Maps Javascript API、ArcGIS Javascript API、Mapbox 等!但我喜欢 leaflet,因为它重量轻,非常简单,并且最有可能解决 80%的问题。此外,它们的文档也很直接,并附有示例。
技术理念
在跳入代码高亮之前,我想先讨论一下代码试图实现的思想。代码只是实现目的的工具;我们必须先了解目的,然后再进行编码。
每页地图
故事地图由包含相关地图的页面组成,以说明页面内容。每个页面通常具有不同的地图。这意味着当激活页面时(发出事件),会调用一个函数。这是人们在学习 JavaScript 时通常会学到的内容。
页面(来源:作者,2023 年)
这是每个页面按钮的最简单伪代码
const pageButtonOne = document.querySelector('#pagebutton')
pageButtonOne.addEventListener('click',handlePageOne)
function handlePageOne(){
// remove the map layers
// adds the relevant layers
// zoom to the view
}
这就是处理事件(例如用户点击)的方式;每个按钮都有一个事件处理函数。Leaflet 中这样代码的一个简单示例可以在以下页面找到(演示链接):
页面响应性演示(来源:作者,2023 年)
## story-maps/pagebuttonexample.html at main · sutanmufti/story-maps
通过在 GitHub 上创建账户,为 sutanmufti/story-maps 的开发做出贡献。
实际的 Javascript 代码在以下代码块中:
let map = L.map('map').setView([51.505, -0.09], 13);
L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', {
maxZoom: 19,
attribution: '© OpenStreetMap'
}).addTo(map);
const pageone = document.getElementById('pageone')
const pagetwo = document.getElementById('pagetwo')
pageone.addEventListener('click', ()=>{
map.flyTo([51.510, -0.08], 10);
// add or remove layers
})
pagetwo.addEventListener('click', ()=>{
map.flyTo([52.545, -0.091], 8);
// add or remove layers,
// or other animation functions that reponse to the interactions
})
上述代码捕获了“点击”事件并运行相应的箭头函数(这是一个 JavaScript 的特性)。
处理地理空间数据
一张地图由数据图层组成。每个“图层”是一个包含几何列的表。这就是我们显示地理空间数据的方式。我还有另一篇文章讨论空间数据结构。
空间数据科学:数据结构
一个阐述空间数据的指南
towardsdatascience.com
我用 QGIS 处理、分析、绘制和操作地理空间数据。它是一个免费开源软件,旨在向每个人提供 GIS(地理信息系统)访问权限。作为免费软件,它竟然如此强大,实在令人印象深刻!
QGIS 用户界面(来源:作者,2023)
你可以用 QGIS 做什么?你可以进行空间数据分析和生成地图图层。它可以连接到如 Postgresql 这样的企业数据库,并管理系统中的空间数据。然后,进行空间 SQL 并根据 SQL 分析生成图层。
空间数据科学:空间查询
用 SQL 回答每一个“哪里”问题;附示例
towardsdatascience.com
这些分析(无论是来自 QGIS 工具箱还是空间 SQL 分析)会导出为适当的格式;最有可能是 GeoJSON 格式。这是一种符合 GeoJSON 标准的 JSON 格式,用于标准化地理空间数据序列化。这确保了数据的互换属性,允许 ogr2ogr 或 Leaflet 等多种软件处理空间数据。
使用 Python 扩展
当你想确保可重复性时,你可以使用 Python 控制台并生成你的脚本。QGIS 的 API 使用起来非常直接,还有一个 cook book。
Python 控制台(QGIS)
将 GeoJSON 与 Leaflet 集成
这些输出然后被添加到 leaflet 中,如下代码所示。对每个图层都要这样做。
async function loadGeojsonToMap(){
// using http request to obtain the geojson file from qgis
const resp = await fetch('./data.geojson')
const geojsonfeature = await resp.json()
const mapLayer = L.geoJSON(geojsonFeature)
mapLayer.addTo(map) // map was instanciated in the previous code block
return mapLayer
}
const mapLayer = await loadGeojsonToMap();
结论
制图是视觉艺术和数据科学的产物。计算机技术的进步使得制图变得互动。这样的互动,如果设计得当,可以生成丰富插图的叙事,允许读者进行探索。首先,我们使用 QGIS 或其他 GIS 软件分析并生成图层。然后,我们通过 Leaflet(HTML、Javascript 和 CSS)提供分析结果。最棒的是,这完全免费,每个人都可以做到。你也可以做到这一点。
如果你喜欢空间和网络数据科学内容,请关注我。我撰写与城市规划、基础设施发展和交通背景相关的高级空间数据分析内容。
使用网络地图展示空间数据
原文:
towardsdatascience.com/presenting-spatial-data-with-web-maps-4069c01e26ac
深入探讨地图图块、基础地图、地图图层和矢量数据
·发布于 Towards Data Science ·15 分钟阅读·2023 年 8 月 15 日
–
爱尔兰西部历史泥炭沼泽地图,Corine 土地覆盖数据 2000
github.com/mmc1718/webmap-ireland
🗺 内容
-
简介
- 网络地图的工作原理
-
准备基础地图
-
创建矢量图块集
-
识别所需的更改
-
修改图块集
-
-
样式化基础地图 - 创建 JSON 样式文件
- 提供样式化的地图图块
-
创建网络地图 - 加载基础地图
- 在地图上叠加数据
制作地图的方式有很多种。我们可以使用桌面 GIS 软件,如 QGIS 或 ArcGIS,使用 Web 框架,如 Leaflet 或 Mapbox GL JS,或者使用传统的墨水和纸张来完成。
网络地图是展示空间数据的绝佳选择,因为它们易于分享且具有互动性。现在有许多工具可以使创建网络地图的过程变得简单,同时让我们完全控制地图的各个元素。
我将介绍使用开源软件和开放数据源创建您自己的地图的完整过程,包括如何使用OpenStreetMap和Natural Earth数据创建自定义矢量基础地图,使用Tileserver提供图块,将基础地图加载到网页上,并使用Maplibre在地图上叠加空间数据。
作为示例,我将创建一个展示十年期间泥炭沼泽损失的爱尔兰泥炭沼泽地图。我已准备好来自 Corine 土地覆盖清单的数据,使其可以在网络地图上显示。最终结果将是一个在浏览器中可查看的美观的爱尔兰沼泽地图。
你可以在这里查看已部署的最终地图版本: marymcguire.dev/ireland-bog-map
这个项目的代码库可以在这里找到:
[## GitHub - mmc1718/webmap-ireland: 显示爱尔兰泥炭沼泽的网络地图
显示爱尔兰泥炭沼泽的网络地图。通过创建账户来为 mmc1718/webmap-ireland 的发展做贡献…
github.com](https://github.com/mmc1718/webmap-ireland?source=post_page-----4069c01e26ac--------------------------------)
Docker 将是这个项目的主要依赖,因此如果你还没有安装,你需要先安装它才能继续。假设你具有一些基本的 GIS 概念知识,基本的终端使用、读取 JSON 文件的能力,并能够跟随使用 JavaScript 和 HTML 的简单示例。
网络地图如何工作
数字地图由两个主要部分组成:基础地图和数据层。如果你还不熟悉网络地图,它们与此无异。
都柏林的三个网络地图视图,地图数据版权归 OpenStreetMap 所有
数据层通常是制作地图的根本原因。基础地图为我们提供了一个基础地图,以便我们在其上叠加数据。样式设计对基础地图很重要,以支持数据层而不分散注意力,并且对数据层也很重要,使数据可读并传达关于其价值的附加信息(较暗或较亮的颜色,或较大或较小的符号传达不同的意义)。
通常,网络基础地图以一组单独的地图瓦片的形式出现,这些瓦片拼接在一起形成一张地图。
图示摘自**《空间数据基础设施的 Web 地图瓦片服务:管理与优化》**,该章节在 CC BY 许可证下发布,详细信息请参见www.intechopen.com/chapters/38302#F1
重要的是,这些地图瓦片被分类为不同的缩放级别,这意味着完整的地图瓦片集实际上包括了许多版本的地图——每个缩放级别一个。当你在网络地图(或“滑动地图”)上放大或缩小时,地图特征会变得更加详细或不那么详细,有些特征只有在达到某些缩放级别时才会出现。在后台,页面会加载与当前查看的缩放级别和地图部分相对应的新瓦片。
将这些瓦片拼凑在一起,就形成了一个瓦片金字塔。我们将其视为金字塔,因为缩放级别越高,覆盖整个区域所需的瓦片就越多。Maptiler 提供了一个很好的可视化图示,你可以在开始使用网络地图时参考。
在处理地理空间数据时,你可能听说过矢量数据和栅格数据之间的区别。地图切片也不例外;单个切片可以是矢量切片,通常是 .pbf 文件,也可以是栅格 (.png) 文件。
我将创建一个存储在 Mapbox 的 MBTiles 格式中的矢量切片集作为基础地图。我更喜欢矢量,因为它们提供了更多的灵活性。
准备基础地图
创建矢量切片集
第一步是创建基础地图切片集。有许多现成的基础地图可用,但我希望地图将重点放在爱尔兰岛上,排除大不列颠。这并不常见,因此我将制作自己的基础地图。
我使用Planetiler来创建矢量切片基础地图。Planetiler 是一个创建矢量地图切片集的工具,速度非常快。甚至还有一个 Docker 镜像,这使得使用 Planetiler 生成完整的切片集变得简单。Planetiler 使用 OpenStreetMap 数据来创建地图切片,而对我来说,爱尔兰和北爱尔兰的数据提取已经可用。
运行以下命令将下载所需的数据,并使用 OpenMapTiles 架构作为默认值生成切片集:
docker run --rm -v "$(pwd)/data":/data ghcr.io/onthegomap/planetiler:latest --download --area=ireland-and-northern-ireland --minzoom=4
注意 minzoom 参数设置为 4。这是因为我们的地图仅关注爱尔兰,所以不需要在爱尔兰过于小的非常低的缩放级别生成切片。缩放级别 4 是我们需要的最低级别,设置限制将节省切片生成时间。
Planetiler 完成的时间将根据你的硬件而有所不同,但可能需要一个小时或更长时间。让进程运行,等待的同时做些其他事情。
一旦切片完成,我们就可以查看它们。我使用Tileserver来完成这项工作。同样,我们可以使用 Docker 来运行此步骤,从而节省时间。确保在与切片相同的目录中运行此命令,或者使用 mbtiles 的完整路径运行。
docker run --rm -it -v $(pwd):/data -p 8080:8080 maptiler/tileserver-gl --mbtiles=./ireland_and_northern_ireland.mbtiles
我们的切片现在可以在 http://localhost:8080 上查看。
默认情况下,Tileserver 允许我们查看原始切片数据或使用基本样式的样式化切片。这对于检查我们的切片是否符合预期并识别任何问题非常有用。
即使原始数据没有样式,功能也会根据它们所在的数据层进行颜色编码,并且旁边有一个图例显示详细信息。这在样式化时需要特别注意。
确定所需的更改
查看切片数据揭示了切片本身存在的一个直接问题。
使用 Tileserver GL 查看未样式化的矢量切片集
尽管我们仅使用了爱尔兰和北爱尔兰的数据来生成地图,但大不列颠的轮廓仍然可见。这是因为 Planetiler 生成了填充水域的海洋多边形,使得陆地部分为空。我们看到的是大不列颠岛应在的位置的海洋空洞——如果你点击岛屿,你会发现没有特征,连陆地都没有。
我们该如何处理切割海洋效果?我们无法填补空洞,但对我们来说重要的是,我们可以对背景和陆地边界进行样式设置。通常这会通过给陆地周围的海洋设置样式来完成,以使背景填充陆地。我们需要反过来做,为爱尔兰岛包括一个陆地多边形,以对背景进行样式设置和对比。
在创建样式文件之前,需要做一些更改。首先,我们需要用额外的特征来补充数据——爱尔兰的陆地面积。
切片集还有一个稍微不那么明显的问题,但在放大时会看到。
注意到县界(粉色)在缩放级别 4 之后消失。在缩放级别 1-4 之间,Planetiler OpenMapTiles 模式使用 Natural Earth 的数据而非 OSM 来生成地球的低分辨率地图。缩放级别 4 之后,使用的是 OpenStreetMap 的数据。由于我们从缩放级别 4 开始生成切片,这一点很明显。
由于 Planetiler OpenMapTiles 模式适用于整个星球,不可避免地在国家之间存在一些不一致性。爱尔兰县界被从 OSM 数据中过滤掉就是这些不一致性之一。我们的地图特别是关于爱尔兰的,因此我们希望把它做对。
除了不一致性外,县界在爱尔兰地图上是一个重要特征,我们希望在所有缩放级别中都包括它。
简单的解决方案是从缩放级别 4 开始,将 Natural Earth 的县界添加回切片集中。
修改切片集
Tippecanoe 是一个非常有用的工具,用于处理 MBTiles 切片集。它允许我们从 GeoJSON 数据创建 mbtiles,并将 MBtiles 文件合并在一起。我们只需要所需的额外数据(GeoJSON 格式),就可以将所需的特征合并到切片集中。
[## GitHub - felt/tippecanoe: 从大型 GeoJSON 特征集合中构建矢量切片集。
从大型 GeoJSON 特征集合中构建矢量切片集。- GitHub - felt/tippecanoe: 从大型 GeoJSON 特征集合中构建矢量切片集…
为了获取表示爱尔兰的多边形——或更可能是多多边形,感谢其岛屿——我转向 OpenStreetMap,特别是Overpass Turbo。不立即清楚我们需要哪个特征(爱尔兰岛?爱尔兰和北爱尔兰?),因此需要一些探索。
通过在openstreetmap.org上使用‘查询特征’工具,我能够找到两个关系,当它们结合时,结果正是我所需要的特征。通过检查标签,我可以创建一个查询以供 Overpass 使用。
使用Overpass Turbo获取两个特征如下所示:
Overpass Turbo 的截图,显示了爱尔兰和阿尔斯特的关系
查询:
[out:json][timeout:25];
(
relation["ref"="IE0"];
relation["boundary"="historic"]["name"="Ulster"];
);
out body;
>;
out skel qt;
导出按钮允许我们将结果保存为 GeoJSON 文件。剩下的就是合并这两个特征,以便得到一个 MultiPolygon。
县界线可以从Natural Earth 网站下载。实际上,Planetiler 已经为你下载了 Natural Earth 数据的副本,但由于我们只需要国家边界,单独下载此提取文件会更简单。文件名为Admin 1 — States, Provinces,下载链接如下:
你可以在这个项目的GeoJSON 文件示例笔记本中找到准备两个 GeoJSON 文件的例子。
一旦 GeoJSON 文件准备好后,你可以从 GitHub 克隆Tippecanoe repo,并使用 Docker 构建镜像或从源代码安装(请参阅 README 获取说明)。我用于将数据合并到我的 tileset 中的命令如下:
# convert the files to mbtiles
tippecanoe -o ireland_outline.mbtiles -f ireland.geojson
tippecanoe -o ireland_counties.mbtiles -f counties.geojson
# merge the mbtiles into the main tileset
tile-join -o "ireland_1.mbtiles" --name="OpenMapTiles" -pk -f ireland_and_northern_ireland.mbtiles ireland_outline.mbtiles
tile-join -o "ireland_final.mbtiles" --name="OpenMapTiles" -pk -f ireland_1.mbtiles ireland_counties.mbtiles
使用 Tileserver 预览合并新数据后的新图块时,我们注意到了一些变化。
提示:运行 Tileserver 时,请确保使用的是正确的 mbtiles 文件,即你使用 Tippecanoe 创建的文件。运行 Tileserver 时使用参数--mbtiles
来指定文件。
未样式化的矢量数据视图,包括爱尔兰和县界层
图例提供了一种快速验证我们新图层是否存在的方法。我们可以在这里看到两个新图层;‘ireland’和‘counties*’*。
样式化基础地图
创建 JSON 样式文件
现在我们已经准备好基础 tileset,准备开始样式化。当制作样式时,我选择突出自然特征如山脉,减少对如高速公路等人造基础设施的关注,以保持沼泽主题。
如果你对地图样式不熟悉,它们是包含有关如何样式化切片集中矢量数据的信息的 JSON 文档。 Maputnik 是一个样式编辑器,我们可以用来创建和编辑样式,它允许我们在工作时预览样式和原始数据。它是 Mapbox 样式编辑器的一个开源替代品,可以使用公开的 Docker 镜像运行。
[## GitHub - maputnik/editor: 一个开源的视觉编辑器,用于’Mapbox Style Specification’
一个开源的视觉编辑器,用于’Mapbox Style Specification’ - GitHub - maputnik/editor: 一个开源的视觉…
github.com](https://github.com/maputnik/editor?source=post_page-----4069c01e26ac--------------------------------)
一旦 Maputnik 打开,打开一个空白样式。
首先,我们需要将其连接到我们的切片集。这是通过‘数据源’选项卡完成的。你需要复制你的 TileJSON 文件的链接 —— 使用 Tileserver 可以查看 —— 并将其粘贴到 #OpenMapTiles
活跃的数据源中。TileJSON 文件是一个包含有关你的切片集信息的 JSON 元数据文件,包括源 URL。
Maputnik 截图,显示了 Sources 窗口
如果一切正常,你应该能够将视图从‘地图’切换到‘检查’,并在屏幕上看到你的数据。数据将类似于使用 Tileserver 查看的原始数据。在你创建了一些样式图层之前,你的‘地图’视图将只显示一个空白屏幕。
在 Maputnik 中切换地图和检查视图
指定特征样式的方式是通过样式图层完成的。这些图层可以在 Maputnik 的侧边面板中编辑,并将被转换为我们最终样式 JSON 文件中的‘layers’字段中的 JSON 对象。点击‘添加图层’按钮开始操作。
第一个创建的图层通常是背景。之后,根据需要添加图层以定位不同的特征。注意图层的顺序会有所不同 —— 特征将根据样式顺序显示在彼此的上方或下方。
样式图层影响哪些特征取决于其源(特征所在的数据层,例如运输或土地使用)以及我们设置的过滤器。你可以在创建图层时指定这一点。
例如,我们可以过滤边界样式图层,以仅影响边界源图层中的陆地边界,通过使用 maritime 标签忽略海洋边界:
在 Maputnik 中查看的县界样式选项
在 JSON 中,这个字段最终变成如下:
"filter": [
"all",
["!=", "maritime", 1],
[">", "admin_level", 2],
["<", "admin_level", 8]
],
在底部的侧边面板中,你可以看到原始 JSON 的样子。
请注意,多个样式表可以影响相同的特征。这使得能够渲染相同特征的不同版本,例如一个点特征可以同时显示为标签和图像。
使用 Maputnik 创建的样式遵循 Mapbox 样式规范,因此如果有疑问,你可以 查看文档。
你可以在 这里 查看我最终的样式。
在线有大量关于制图的提示信息。Esri 发布了 一份在线资源列表,这是一个很好的起点。
请注意,我为样式添加了一个额外的远程字体和精灵(徽标或图像)源。这可以在 Maputnik 的源窗口中完成,我们在这里添加了我们的图块源。我使用的源是 https://orangemug.github.io/font-glyphs/glyphs/{fontstack}/{range}.pbf
用于字体和 https://openmaptiles.github.io/osm-bright-gl-style/sprite
用于精灵。(查看 Orange Mug 的自定义字体库 github.com/orangemug/font-glyphs
)
提供样式地图图块
一旦你的样式准备好,从 Maputnik 下载它并保存在与你的 MBTiles 图块集相同的目录中。我们需要为 Tileserver 创建一个配置文件,以便在提供图块时它可以找到你的自定义样式。
创建一个名为 config.json 的新文件,并确保其包含如下内容。
{
"options": {
"paths": {
"root": "",
"fonts": "/usr/src/app/node_modules/tileserver-gl-styles/fonts",
"styles": "styles",
"mbtiles": ""
}
},
"styles": {
"natural": {
"style": "ireland.json",
"tilejson": {
"bounds": [
-16.325684,
47.960502,
-5.053711,
56.872996
]
}
}
},
"data": {
"v3": {
"mbtiles": "ireland.mbtiles"
}
}
}
这个配置文件将加载一个名为 ireland.mbtiles 的 mbtiles 文件(使用在选项 -> 路径 -> mbtiles 下定义的路径)。Tileserver 将查找在样式目录(选项 -> 路径 -> styles)中名为 ireland.json 的样式文件来提供服务。请注意,字体、样式和 mbtiles 的路径假定是相对于根目录的。
在包含新配置文件的目录中重新启动 Tileserver 将使我们新的样式在 localhost:8080 上可供查看。
提示:如果你希望你的图块在 localhost 之外的地方可用,你需要在配置文件中包含‘domains’。详细信息请参见 Tileserver 文档。
创建 Web 地图
加载基础地图
我们已经有了基础地图和样式。现在剩下的就是将数据叠加到地图上。我有两个关于爱尔兰沼泽的数据文件。我准备数据的步骤可以在 GitHub 上查看。
我使用 Maplibre 来创建我的地图。Maplibre 是 Mapbox GL JS 的开源替代品,非常相似。我们可以直接在 HTML 文件中添加 JavaScript 脚本,或者使用 JavaScript 文件并导入它。
按照Maplibre 文档中的示例,首先创建一个 HTML 文件。包含一个<script>
标签,标签内的代码将使用新创建的自定义底图创建地图实例。
<div id="map"></div>
<script>
const map = new maplibregl.Map({
container: "map",
// stylesheet location
style: "http://localhost:8080/styles/natural/style.json",
center: [-8, 53], // starting position [lng, lat]
zoom: 7, // starting zoom
minZoom: 6,
maxBounds: [
[-17.7, 46.6],
[2.7, 58.6],
],
});
</script>
注意,我在‘style’字段中包含了由 Tileserver 提供的样式文件链接。
设置边界意味着用户将无法将地图平移到定义的边界框坐标之外,这很有用,因为我们的数据仅限于地图上的一个小区域。默认情况下,可以平移整个世界,这在我们的情况下并不是我们想要的。可以使用这个方便的工具找到自定义边界:norbertrenner.de/osm/bbox.html
。
要查看我们的页面,我们需要一个 Web 服务器。我使用VSCode 的 Live Server 扩展来快速查看开发中的网页。它可以免费安装,并且意味着你只需点击一下按钮即可提供 Web 应用。加载页面将显示我们的底图:
作者样式的爱尔兰底图
在地图上叠加数据
接下来,我们将数据图层添加到地图中。同样,Maplibre 网站提供了许多示例,涵盖了创建地图时可能遇到的各种场景。
要加载 GeoJSON 文件,我们首先需要将文件路径添加为源。定义文件源后,我们可以将具有样式选项的图层添加到地图上。这一切都在一个函数中完成,该函数在地图的“加载”事件触发时运行(这就是map.on(“load" …)
的意思)。我将新函数命名为setUpMap
并将其作为第二个参数传递给map.on(“load")
。
function setUpMap() {
map.addSource("peat_bogs", {
type: "geojson",
data: "data/ireland_peat_bogs_2012.geojson",
});
map.addSource("lost_bogs", {
type: "geojson",
data: "data/peat_bog_loss.geojson",
// required for smooth rendering while zooming
tolerance: 0,
});
map.addLayer({
id: "peat_bogs",
type: "fill",
source: "peat_bogs",
paint: {
"fill-color": "#92a94a",
"fill-opacity": 0.4,
},
});
map.addLayer({
id: "lost_bogs",
type: "fill",
source: "lost_bogs",
paint: {
"fill-color": "#744333",
"fill-opacity": 0.4,
},
})
};
map.on("load", setUpMap);
每个图层使用不同的颜色进行样式设置,并且稍微透明,以便基础地图中的地点标签和细节能够显示出来。我使用了这个方便的工具来选择和比较颜色。
我还使用了一个名为 maplibre-gl-legend 的插件,插件可以在这里找到:github.com/watergis/maplibre-gl-legend
。以下代码定义了图例:
const legendInfo = {
lost_bogs: "Lost Peat Bog (Derived from Corine Land Cover (CLC) 2000, Version 2020_20u1)",
peat_bogs: "Peat Bog (Corine Land Cover (CLC) 2012, Version 2020_20u1)",
};
map.addControl(
new MaplibreLegendControl.MaplibreLegendControl(legendInfo, {
showDefault: true,
showCheckbox: true,
onlyRendered: false,
reverseOrder: true,
title: "Peat Bog loss in Ireland Between 2000 and 2012",
}),
"top-right"
);j
图例还提供了一种方式,使我能够为数据源包含署名,而无需在其他地方添加这一内容。
查看最终结果,我们可以看到一个完整的沼泽地地图,附带互动图例:
爱尔兰 2000–2012 年间泥炭沼泽损失的地图,附带图例
感谢阅读!如果你觉得这篇文章有用,可以考虑点赞、关注作者或在 GitHub 上给项目加星👏
贝叶斯深度学习入门
原文:
towardsdatascience.com/primer-on-bayesian-deep-learning-d06e0601c2ae
概率深度学习
·发表于 Towards Data Science ·阅读时间 8 分钟·2023 年 2 月 1 日
–
介绍
本文属于“概率深度学习”系列。该系列每周涵盖概率方法在深度学习中的应用。主要目标是扩展深度学习模型以量化不确定性,即了解它们不知道什么。
贝叶斯深度学习是一个新兴领域,它将深度学习的表达能力和表征能力与贝叶斯方法的不确定性建模能力相结合。这两种范式的整合提供了一个原则性框架,用于解决深度学习中的各种挑战,如过拟合、权重不确定性和模型比较。
在本文中,我们提供了对贝叶斯深度学习的全面介绍,涵盖其基础、方法论和最新进展。我们的目标是以清晰易懂的方式展示基本概念和思想,使其成为研究人员和实践者的理想资源,特别是那些对该领域较新的人员。
迄今为止已发布的文章:
-
从头开始在 TensorFlow Probability 中进行最大似然估计
-
从头开始在 TensorFlow 中进行概率线性回归
-
确定性与概率深度学习
-
从头开始使用 TensorFlow 实现朴素贝叶斯
-
贝叶斯深度学习
图 1:今日格言:层层深入 (source)
确定性、概率性和贝叶斯深度学习
深度学习在计算机视觉、自然语言处理和游戏等多个应用领域取得了显著成功。尽管如此,传统的深度学习模型本质上是确定性的,并且在其预测中提供了有限的不确定性量化能力。为了解决这个问题,贝叶斯深度学习和概率深度学习作为重要范式出现,允许将不确定性融入深度学习模型中。
贝叶斯深度学习和概率深度学习代表了将不确定性融入深度学习模型的重要范式。这些方法相比传统的确定性深度学习具有若干优势,包括能够提供不确定性估计以及在存在数据分布外数据时进行稳健推理的能力。
在贝叶斯深度学习中,模型参数被视为随机变量,并对其施加先验分布。这个先验代表了关于模型参数的先验知识,例如它们的期望值或分布形状。然后,通过贝叶斯推理更新参数的后验分布,使用数据形成一个后验分布,这代表了我们在数据给定下对参数的更新信念。这将导致模型参数的分布以及模型预测中的不确定性度量。
概率深度学习则将数据生成过程建模为概率函数。给定一个输入,模型预测一个输出分布,从而允许量化预测中的不确定性。这种方法在输出空间复杂的问题中尤为有用,例如图像生成或语音合成。在这些情况下,将数据生成过程建模为概率函数可以捕捉数据中的复杂模式,并生成高质量的输出。
贝叶斯深度学习和概率深度学习都是重要且活跃的研究领域,最近在贝叶斯神经网络、变分推断和深度生成模型等技术方面取得了进展。尽管这些方法具有潜在的好处,但由于高维模型和深度学习中通常使用的大量数据所带来的计算和统计困难,这些方法仍然具有挑战性。尽管如此,近年来该领域已经取得了显著进展,贝叶斯深度学习和概率深度学习的未来研究充满了兴奋和潜力。
贝叶斯学习:概述
在这篇文章中,我们考虑了用于统计推断的贝叶斯框架,其中我们用符号𝑃表示概率密度。
贝叶斯方法提供了一种统一的统计推断方法,我们的目标是计算给定一些观察数据的模型参数分布。在神经网络的背景下,这对于估计权重参数的不确定性尤为有用,因为我们可以计算给定训练数据的这些参数的后验分布。这是通过应用贝叶斯定理实现的,该定理指出:
其中:
-
𝐷:观察数据,以𝑥和𝑦值对表示,例如 𝐷={(𝑥1,𝑦1),…,(𝑥𝑛,𝑦𝑛)}
-
𝑤:模型权重的值
-
𝑃(𝑤):先验密度,表示在观察到数据之前对模型权重分布的初始信念
-
𝑃(𝐷|𝑤):给定权重𝑤时观察到数据的似然
-
𝑃(𝑤|𝐷):模型权重的后验密度,通过贝叶斯定理将观察到的数据和先验信念结合计算得出。
归一化项 ∫𝑃(𝐷|𝑤′)𝑃(𝑤′)d𝑤′=𝑃(𝐷) 是与𝑤无关的,用于归一化后验密度。
贝叶斯定理使我们能够将观察到的数据与我们的先验信念结合,以获得模型参数的后验分布,提供它们不确定性的概率表示。在神经网络背景下实施贝叶斯学习可能很具挑战性,因为计算归一化常数的复杂性。为了克服这一点,使用了各种近似方法,例如变分贝叶斯。这些方法旨在有效地估计神经网络权重的后验分布。
变分贝叶斯推断
在变分贝叶斯方法中,我们使用第二个函数来近似后验分布,这个函数称为变分后验。这个函数由一组参数来表征,记作𝜃,可以通过优化来学习。关键思想是选择一个变分后验的函数形式,使其能够有效地优化,以尽可能接近真实的后验分布。
一种常见的评估近似质量的方法是使用变分后验和真实后验之间的 Kullback-Leibler (KL) 散度。KL 散度是衡量两个概率分布之间差异的指标,当两个分布相等时,其值为 0。
给定观察数据 𝐷,变分后验 𝑞(𝑤|𝜃) 和真实后验 𝑃(𝑤|𝐷) 之间的 KL 散度定义为
当将观察数据视为常量时,我们可以将这个表达式简化为仅依赖于 𝜃 和 𝐷 的函数:
第一项测量变分分布和先验分布之间的差异,第二项是变分后验下的期望负对数似然。
这个函数,称为变分下界,可以针对 𝜃 进行优化,以获得对真实后验的最佳近似。优化可以使用基于梯度的方法进行,使变分贝叶斯能够扩展到大型复杂模型。
贝叶斯神经网络的反向传播方案
在这一部分,我们描述了一种用于贝叶斯神经网络的反向传播算法,允许我们将权重不确定性纳入模型。我们的方法涉及引入一个密度为 𝑞(𝑤|𝜃) 的变分后验,其中 𝑤 是网络中的一个权重,𝜃 是一组可训练的参数。这个后验表示了我们对真实权重后验的近似,真实权重后验由观察数据和我们对权重的先验信念(通过先验密度 𝑃(𝑤) 编码)决定。
我们的目标是更新变分后验,使其准确地逼近真实的权重后验,这一目标通过证据下界(ELBO)来量化。为此,我们最小化变分后验和先验之间的 ELBO,公式如下:
其中 𝐷 是训练数据。然而,这个表达式涉及对权重 𝑤 的积分,这可能在计算上不可行。为了解决这个难题,我们将 ELBO 重写为期望:
为了进行反向传播,我们需要对 𝐿(𝜃|𝐷) 关于 𝜃 进行求导。然而,这很具有挑战性,因为期望的底层分布依赖于 𝜃。我们通过重参数化技巧克服了这个问题,这使得我们能够通过期望进行求导。
重参数化技巧:贝叶斯神经网络中随机梯度优化的关键技术
重新参数化技巧是一种数学技术,用于优化具有随机参数的模型。其目标是将依赖变量𝑤转换为具有固定分布且与模型参数𝜃无关的随机变量𝜖。这种转换使得可以独立于𝜃计算𝑓(𝑤;𝜇,𝜎)对𝑞(𝑤|𝜇,𝜎)的期望,从而实现模型的高效优化。
例如,考虑一个高斯分布𝑞(𝑤|𝜇,𝜎),其中𝜃=(𝜇,𝜎)。通过变量变换𝑤=𝜇+𝜎𝜖,其中𝜖∼𝑁(0,1),我们得到
然后可以轻松计算关于𝜇和𝜎的导数,并通过蒙特卡罗采样来估计期望。
重新参数化技巧是贝叶斯神经网络中高效优化的关键工具,并且在现代深度学习研究中得到了广泛应用。它使得可以对具有随机参数的模型进行基于梯度的优化,从而实现高效的训练和推断。
最终连接
贝叶斯深度学习提供了一种将不确定性融入深度学习模型的框架。通过将神经网络权重视为随机变量,我们可以捕捉到内在不确定性和认识不确定性,从而提供更稳健和可靠的预测。变分贝叶斯推断提供了一种学习网络权重后验分布参数的方法,这些参数可以通过反向传播方案进行优化。
重新参数化技巧(reparameterization trick),这是贝叶斯神经网络中用于随机梯度优化的关键技术,使我们能够通过将随机变量转换为可微分的形式来有效地估计梯度。这一技巧使得可以使用标准的基于梯度的优化算法来学习网络权重后验分布的参数。
需要注意的是,尽管内在不确定性(aleatoric uncertainty)考虑了数据中的固有噪声,但认识不确定性(epistemic uncertainty)则捕捉了我们对基础模型知识的不了解。在贝叶斯深度学习中,认识不确定性通过对网络权重施加先验分布并通过变分推断过程用观察数据更新它来建模。这导致了一个更灵活和信息量更大的模型,因为网络可以适应新数据并融入有关基础关系的额外信息。
结论
总之,贝叶斯深度学习提供了一种将不确定性融入深度神经网络预测的框架。通过将网络权重视为随机变量并建模其后验分布,我们可以获得比传统确定性深度学习方法更稳健和信息量更大的模型。
重参数化技巧与基于梯度的优化算法相结合,使得深度模型中的可扩展贝叶斯推断成为可能。因此,贝叶斯深度学习受到越来越多的关注,并在包括主动学习、分布外检测和不确定性感知强化学习等各种应用中取得了成功。这个领域仍然相对较新,仍有很大的探索和创新空间。
在未来,我们期望贝叶斯深度学习在人工智能和机器学习的发展中扮演越来越重要的角色。
关于我
连续创业者和 AI 领域的领导者。我为企业开发 AI 产品,并投资于专注于 AI 的初创公司。
创始人 @ ZAAI | LinkedIn | X/Twitter
参考资料
[1] — Coursera: Deep Learning Specialization
[2] — Coursera: TensorFlow 2 for Deep Learning 专业化课程
隐私政策
最后更新于 2024 年 9 月
我们收集、存储和使用个人信息的方法
信息贡献者提供给我们的
·发表于 Towards Data Science ·1 分钟阅读·2023 年 3 月 7 日
–
图片由 Annie Spratt 拍摄,来源于 Unsplash
我们是一个利用 Medium 平台将数据专业人士汇聚在一起的出版物,旨在分享和扩展他们对数据科学、机器学习及相关主题的知识。
随着 TDS 多年来的不断发展,我们的工作流程和编辑实践也发生了变化。我们希望向贡献者明确这些变化如何可能影响他们与我们分享的个人信息,因此我们制定了一份隐私政策,详细阐述了我们的方法。
您可以在此处找到最新版本:隐私政策 — 2024 年 9 月。
请注意,Medium 自身有其隐私政策,您可能还想查看一下 — 您可以在这里找到它。
如果您有任何问题,您可以在此页面找到所有联系信息。您还可以在这里浏览我们隐私政策的旧版本:2023 年 3 月、2022 年 8 月、2020 年 7 月。
私有 GPT:在企业数据上微调 LLM
原文:
towardsdatascience.com/private-gpt-fine-tune-llm-on-enterprise-data-7e663d808e6a
用数据做一些酷炫的事情
·发表于数据科学前沿 ·阅读时间 9 分钟·2023 年 7 月 5 日
–
照片由Robynne Hu提供,来源于Unsplash
介绍
在大数据和先进人工智能的时代,语言模型已成为能够处理和生成类人文本的强大工具。像 ChatGPT 这样的大型语言模型是能够在许多主题上进行对话的通用机器人。然而,LLM 也可以在特定领域的数据上进行微调,使它们在特定领域的企业问题上更加准确和切题。
许多行业和应用将需要经过微调的 LLM。原因包括:
-
从在特定数据上训练的聊天机器人中获得更好的性能
-
像 ChatGPT 这样的 OpenAI 模型是一个黑箱,公司可能会犹豫通过 API 分享他们的机密数据。
-
ChatGPT API 的费用对于大型应用程序可能会很高。
微调 LLM 的挑战在于该过程尚不明确,而且训练一个没有优化的十亿参数模型所需的计算资源可能非常昂贵。
幸运的是,很多关于训练技术的研究已经完成,这使我们现在能够在较小的 GPU 上微调 LLM。
在本博客中,我们将介绍一些用于微调 LLM 的技术。我们将使用 Colab GPU 在金融数据上训练Falcon 7B 模型!这里使用的技术是通用的,可以应用于其他更大的模型,如 MPT-7B 和 MPT-30B。
在深度学习分析中,我们过去 6 年一直在构建定制的机器学习模型。如果你对为你的应用程序微调一个 LLM 感兴趣,联系我们。
QLoRA
QLoRA,即“量化低秩适配”,呈现了一种结合量化和低秩适配的方法,以实现 AI 模型的高效微调。以下将详细解释这两个术语。
QLoRA 减少了微调 LLM 所需的内存,同时在性能上与标准的 16 位模型微调模型没有任何下降。这种方法使得 70 亿参数的模型可以在 16GB GPU 上微调,330 亿参数的模型可以在单个 24GB GPU 上微调,以及 650 亿参数的模型可以在单个 46GB GPU 上微调。
这意味着大多数公司现在可以以较小的成本拥有微调后的 LLM 或本地模型。
QLoRA 由两种技术组成:
-
LoRA — 低秩适配
-
量化
LoRA
LoRA,即低秩适配器(LoRA),是小型可训练参数集,注入到 Transformer 架构的每一层中进行微调。在微调过程中,原始模型权重被冻结且未更新,这些注入的权重在微调时会更新。这大大减少了下游任务的可训练参数数量。在随机梯度下降期间,梯度通过被冻结的预训练模型权重传递到适配器。因此,只有这些具有小内存占用的适配器在训练过程中被更新。
量化
接下来,我们将讨论量化。要理解量化,首先需要了解通常用于存储模型权重的不同数据类型。
-
FP32 — 完整精度权重,占用 4 字节内存
-
FP16/BF16 — 半精度权重,占用 2 字节内存
-
INT8 — 数据类型包括一个 8 位表示,可以存储 2⁸ 个不同的值(在 [0, 255] 或 [-128, 127] 的范围内用于有符号整数)
-
FP8 和 FP4,分别代表浮点 8 位和 4 位精度。它们是小型浮点值家族的一部分。
使用量化,模型需要的内存比存储原始模型所需的内存要小得多。例如,一个 8 位量化模型只需原始模型大小的 1/4。
那么,如何将原始的 32 位权重存储在像 INT8 或 FP8 这样的 8 位数据类型中呢?
量化意味着将值从一种数据类型“舍入”到另一种数据类型。它通过将较大的值压缩到具有较少位数的数据类型中来工作,但会有少量的精度损失。
在 QLoRA 的情况下,模型权重存储在 4 位浮点数据类型中。但为了尽量减少总体精度损失,所有矩阵运算都在 16 位浮点中执行。计算完成后,权重会被存储/量化回来,然后再存储。
总结一下,QLORA 使用一种存储数据类型(通常是 4 位 NormalFloat)和一种计算数据类型(16 位 BrainFloat)。存储数据类型在进行前向和后向传递时被反量化为计算数据类型。权重仅在需要时才被解压,因此在训练和推理过程中内存使用保持低位。更多详细信息可以在这里找到。[4]
那么 QLoRA 微调的准确性影响如何?
QLoRA 调优在各种实验中显示出与 16 位微调方法相匹配的效果。此外,使用 QLoRA 微调 LLaMA 模型的 Guanaco 模型在OpenAssistant 数据集(OASST1)上是最先进的聊天机器人系统,并且在 Vicuna 基准测试中接近 ChatGPT。
HuggingFace 对微调的支持
HuggingFace 发布了几个可以轻松微调 LLM 的库。
这些包括:
-
PEFT 库:HuggingFace 发布了一个关于参数高效微调(PEFT)的库,该库支持 LORA。
-
量化支持——许多模型可以使用 bitsandbytes 模块以 8 位和 4 位精度加载。以 4 位加载模型的基本方法是调用 from_pretrained 方法时传递参数 load_in_4bit=True。
-
Accelerate 库——Accelerate 库具有许多功能,能够轻松减少模型的内存需求
-
监督微调训练器——SFT 训练器是大规模 LLM 的监督微调训练类。
现在我们结合所有技术在金融数据集上训练 Falcon 7B 模型。
在 Alpaca Finance 数据集上训练 Falcon 7B
我们成功地在 Google Colab 上微调了最近发布的 Falcon-7B 模型,使用Alpaca-Finance数据集。这个数据集包含了大约 70K 的金融数据点。这是一个开源数据,存储在 HuggingFace 数据集中心,并可以直接从中心加载。
数据是金融问题及其答案。数据快照如下:
Alpaca Finance 数据集。图片来源:作者
这个微调的代码可以在这里查看。
首先,我们从 HuggingFace 加载一个预训练的 Falcon-7B 模型。我们使用 AutoModelForCausalLM,通常用于加载像 GPT 这样的自回归语言模型。注意,我们将存储类型设置为 4 位,将计算类型设置为 FP-16。
加载 Falcon 7B 模型的代码片段。图片来源:作者
现在,我们将创建 Adapters(记住!额外的层添加到 Transformers 模块中,将保存我们的微调权重)。我们将根据建议,将它们添加到线性层以及 Transformers 模块的查询值对中,以获得最佳的微调模型精度。下面可以在 target_modules 参数中注意到这一点。
PEFT 配置。图片由作者提供
最后,我们将整合一切,同时初始化 SFTTrainer 类。我们还会提供我们的数据、适当的令牌、格式化函数和 max_seq_length。
模型训练大约需要一个小时。我使用了 WandB 进行日志记录。更多细节请查看 Colab Notebook 这里。
训练损失在 100 步后相当低。
训练损失 — 财务 Alpaca 数据集。图片由作者提供
使用 Alpaca Finance 数据集进行推理
Colab Notebook 还包含推理代码。我发现模型在回应问题时表现良好。对于我关于退休所需收入的问题,它有一个较长的生成。它涵盖了一些关于 401K 和其他养老金计划的经验法则。它强调了了解退休收入以给出答案的必要性,最后建议我咨询财务规划师!
响应不如 chatGPT 那样干净,但很高兴看到一个 7B 模型可以相对容易地训练和运行。
### Instruction:
how do I figure out how much money I need to retire?
### Response:
There are a lot of assumptions embedded in any formula, so it's hard to say for
sure what you'll need per month, but some ideas to get started would be:
around 80k per year if you have a very high net worth or 180k per year if you
have a very low net worth. It sounds like your income is well above this number
, especially if you have a lot of assets. To really know though, you'll need
to do a full financial analysis. There are several factors that aren't
included in this equation, the most important being what kind of retirement
plan you have. If you have a 401k, the rule of thumb is ~$60k per year will
cover a typical retirement. If you have a pension, the rule of thumb is ~$10k
per year will cover a pension. These are rough guidelines, and there are lots
of other factors that go into it, such as whether you have another source of
income, what kind of investments you have, etc. A financial planner should
be able to help you with this. I will say that $150k per year is pretty high
even if you have a lot of assets; the average American family makes less than
$45k per year. If you don't have a retirement plan or your plan just isn't
enough to support you in retirement, then yes, you should consider
supplementing your income. However, before you make that decision,
you need to be honest with yourself and ask if you live beyond the basic necessities.
If you have the same quality of life that you had when you were making
half of what you are now, then yeah, you might want to look at
我尝试的另一个问题是关于投资组合多样化的。再次,来自一个小模型的响应质量让我感到惊讶!
### Instruction:
Why should portfolios be diversified?
### Response:
Because the performance of different assets is not identical. Even if they are
correctly classified as "equities" or "bonds", different stocks will behave
differently under different economic conditions. And even if you have the
same general view of the market (eg, it's overvalued), you will do better
if you own a broad basket of stocks rather than one that is too focused on
either the rising or falling side. Also, different markets interact with each
other, and crises in one affect others. If everyone rushes into safe havens
in a panic, for instance, then those safe havens will rise and you'll lose
money. Conversely, if everyone thinks a bubble is about to burst, then you
want to be buying up the shares before everyone else catches on and they
start declining. A single individual or small group can be right that the
stock market is overvalued, but if everyone thinks that way, it's time to
sell - and the sale will be greater the earlier you join it. Also, some
investments inherently involve more risk than others. Gambling stocks, for
instance, would probably be a bad idea during an industrywide gambling slump.
And some forms of investment, such as short
结论
在自定义数据集上微调 LLMs 变得更加容易,这可以让人们使用自己的“私有 GPT”模型。这些自定义模型可以在商业 GPU 上本地托管,并具有类似 ChatGPT 的界面。
深度学习分析是一个值得信赖的定制机器学习模型提供商,适用于多种使用场景。我们拥有全球客户基础,与组织合作开发符合其独特需求的定制解决方案。如果您希望利用语言模型(LLMs)的力量进行应用,欢迎与我们联系。请通过 info@deeplearninganalytics.org 与我们的团队联系,探索更多可能性。我们期待进一步讨论您的项目。
参考文献
[1] huggingface.co/blog/falcon
[2] huggingface.co/blog/hf-bitsandbytes-integration
[5] www.promptengineering.org/exploring-qloras-potential-for-accessibility-and-innovation/
[6] blog.gopenai.com/paper-review-qlora-efficient-finetuning-of-quantized-llms-a3c857cd0cca
[7] QLoRA 论文 -: arxiv.org/abs/2305.14314
[8] LoRA 论文 -: arxiv.org/abs/2106.09685
[9] huggingface.co/docs/trl/main/en/sft_trainer
专业级 GPU 系统 vs 消费级 GPU 系统用于深度学习
原文:
towardsdatascience.com/pro-gpu-system-vs-consumer-gpu-system-for-deep-learning-a62bec69f557
硬件
为什么你可能会考虑使用专业级 GPU
·发表于 Towards Data Science ·阅读时间 21 分钟·2023 年 4 月 19 日
–
本文将使用的专业工作站。图片来源于 Exxact Corporation ,授权给 Michael Clayton
在训练神经网络,特别是深度神经网络时,系统中拥有一个 GPU(或显卡)几乎是必需的。与 CPU 相比,即使是相当普通的 GPU,其训练速度的差异也是天壤之别。
……但你什么时候可能会考虑跳入专业级而非消费级 GPU 的领域?训练和推理速度有很大的差别吗?还是说其他因素使得转向专业级 GPU 更具吸引力?
介绍
本文的目的是让你了解作为普通消费者(或刚开始从事机器学习/深度学习)的 GPU 与高端系统中使用的 GPU 之间的主要区别。这些系统可能用于开发和/或推理高级深度学习模型。
除了作为一个有趣的练习来理解前沿专业设备和消费级硬件在纯处理速度上的区别外,它还将突出一些消费级 GPU 及其相关系统在处理前沿深度学习模型时存在的其他限制。
你提到的“专业”或“消费级”GPU 指的是哪些 GPU?
“现实世界”的差异将在文章的其余部分中讨论,但如果你想要一个明确的技术区分,包括示例显卡和规格,那么这一部分应该能涵盖。
正如我在之前的文章中详细说明的那样,NVIDIA 是目前深度学习和神经网络 GPU 的唯一明智选择。这主要是由于它们与 TensorFlow 和 PyTorch 等平台的更全面集成。
因此,在制造商的规格方面区分专业级和消费级显卡是相对直接的。
从以下页面中了解当前的 NVIDIA 消费级显卡批次:
探索 NVIDIA GeForce 显卡。RTX 40 系列、RTX 30 系列、RTX 20 系列和 GTX 16 系列。
…以及专业级 GPU:
了解 3D 艺术家、建筑师和产品设计师如何利用 NVIDIA RTX ™ 和 Omniverse ™ 的强大功能…
还有一些 GPU,主要用于数据中心,超越了上述的专业级显卡。A100 是一个很好的例子:
NVIDIA EGX ™ 平台包括优化的软件,提供基础设施上的加速计算…
你还可以了解 NVIDIA 认证的专业数据中心和工作站系统中使用的系统规格和 GPU:
[## NVIDIA 认证系统
NVIDIA 认证系统程序汇集了业界最完整的加速工作负载性能集合…
计划
我通常认为实际演示(或实验)是说明一个观点的最佳方式,而不仅仅依赖于制造商提供的规格和统计数据。
有鉴于此,虽然文章将讨论相关统计数据,但它还将直接比较三种不同的 GPU(专业级和消费级),在相同的深度学习模型上进行不同层次的比较。
这应该有助于突出在考虑是否需要专业级 GPU 时哪些因素重要,哪些因素不重要。
GPU 规格 — 概述
对于这项实验,将会有三种不同的显卡,但有四个比较级别:
-
NVIDIA RTX 1070(基础)
-
NVIDIA Tesla T4(中端)
-
NVIDIA RTX 6000 Ada(高端)
-
2 x NVIDIA RTX 6000 Ada(双高端!)
那么,这些不同的显卡在原始规格方面如何比较?
不同显卡的比较 — 表格由作者提供
注意: 我在上面的表格中包含了 RTX 4090,因为它是当前消费级显卡的巅峰,可能是与 RTX 6000 Ada 最直接的比较对象。我将在整篇文章中参考 4090 作为比较点,尽管它不会出现在基准测试中。
如果上面的表格只是一些没有意义的数字,那么我推荐我的上一篇文章,它介绍了一些术语:
加快训练速度,迭代更快
towardsdatascience.com
专业系统
从各个角度展示了本文使用的专业工作站。图片由 Exxact Corporation 提供,授权给 Michael Clayton
制作这样的文章的一个问题是,你需要访问专业级系统,因此主要障碍之一就是……成本。
幸运的是,有些公司会提供设备试用机会,以便你可以查看它是否满足你的需求。在这个特定的案例中,Exxact 足够友好地允许远程访问他们的一台设备,以便我可以进行所需的比较。
…工作站的价值大约为 25,000 美元
为了强调这些系统可能的成本,我估计我所获得访问权限的工作站价值大约为25,000 美元。如果你想更深刻(或者更惊讶?)了解这些内容,可以查看 配置器,看看实际能达到什么水平。
顺便提一下,如果您正在认真考虑这一水平的硬件,您也可以申请远程“试用”:
[## NVIDIA H100 试用 | Exxact
使用最新 NVIDIA 技术试用您的应用程序,注册以远程访问我们设备齐全的 GPU 服务器……
对于感兴趣的人,这些是“专业”系统的完整规格:
专业系统的规格 — 作者提供的表格
注意: 您可以参考本文中包含两张金色 GPU 的黑色计算机机箱的任何图片,因为这些是上述系统的实际照片。
有趣的是,拥有高端系统不仅仅是将您能找到的最佳显卡装入当前系统中。其他组件也需要升级。系统 RAM、主板、CPU、冷却系统以及电源,当然还有电力。
竞争者
本文中将用作基准的 NVIDIA GeForce GTX 1070 FTW — 作者提供的图片
NVIDIA GeForce GTX 1070
在底部的是 GTX 1070,这对于大多数人来说很容易获得,但仍然显著 比 CPU 更快。它还具有 8GB 的相当数量的 GPU RAM。一个不错的简单消费级基准。
NVIDIA Tesla T4
Tesla T4 可能是一个奇怪的补充,但有几个原因。
首先要注意的是,Tesla T4 实际上是一款专业显卡,只是几代之前的产品。
从处理速度来看,它大致相当于 RTX 2070,但其 GPU RAM 是 16GB 的双倍。这额外的 RAM 使其在此测试中处于中端范围。目前一代的消费级显卡通常有这种范围的 RAM(RTX 4070 [12GB] 和 RTX 4080 [16GB]),因此它在 GPU RAM 方面代表了消费级显卡。
最终原因是您可以在 Colab 免费访问这些显卡。这意味着任何阅读本文的人都可以亲自动手运行代码以查看效果!
专业 GPU — NVIDIA RTX 6000 Ada
这张图片展示了在本文中使用的专业工作站中安装的两张 NVIDIA RTX 6000 Ada 显卡。图片来源于 Exxact Corporation ,由 Michael Clayton 授权
毋庸置疑,RTX 6000 Ada 无论在规格还是价格上都是一款令人印象深刻的显卡。MSRP 为 6800 美元,绝对不是便宜的显卡。那么,如果你可以用仅仅 1599 美元(MSRP)购买 RTX 4090,为什么还要买一张(或者更多!?)RTX 6000 Ada 呢?
RTX 4090 的 RAM 只有 RTX 6000 Ada 的一半,且功耗比 RTX 6000 Ada 高出 50%
我在桌子上放了一张 RTX 4090 来尝试回答这个问题。这有助于展示消费者显卡和专业显卡之间最明显的两个区别(至少从规格上来看):
-
可用的 GPU RAM 数量
-
使用中的最大功耗
RTX 4090 的 RAM 只有一半,且 功耗高出 50% 比 RTX 6000 Ada。这绝非偶然,随着文章的深入将会显现出原因。
此外,考虑到 RTX 4090 更高的功耗,值得注意的是 RTX 6000 Ada 的速度仍然快了大约10%。
这些额外的 RAM 和降低的功耗真的有区别吗?希望比较能在文章后面帮助回答这个问题。
还有其他不太明显的优点吗?
是的,获得专业级显卡确实有一些额外的好处。
可靠性
图片来源于 WikiImages,来自 Pixabay
NVIDIA RTX 专业显卡经过广泛的专业应用认证,经过领先的独立软件供应商(ISVs)和工作站制造商的测试,并由全球支持专家团队提供支持。
从本质上讲,这意味着显卡在软件(驱动程序)和硬件层面上可能更可靠、更抗崩溃,如果遇到问题,还有一个广泛的专业网络可以解决问题。这些因素在企业应用中显然非常重要,因为时间就是金钱。
想象一下运行一个复杂的深度学习模型几天,然后由于崩溃或错误丢失结果。接着还需要花费更多的时间来解决问题。真是不妙!
这种安心感是否成为额外的理由来支付更多的费用?这真的取决于你的优先级和规模…
规模
照片由 Daniele Levis Pelusi 在 Unsplash 上提供
如果你在设计一个具有最佳 GPU 处理能力的计算机系统,那么可能需要多个 GPU。显然,系统中可容纳的 GPU 数量主要取决于主板插槽的数量以及机箱内的物理空间限制。
然而,还有其他直接与 GPU 相关的限制因素,这也是消费级 GPU 和专业 GPU 在设计上开始偏离的地方。
考虑到专业主板可能有四个双槽 GPU 的插槽(如本文中的专业系统)。理论上,你可以将 4 x RTX 6000 Ada GPU 装入系统中没有任何问题。然而,你只能在同一主板上装入 2 x RTX 4090。为什么?因为 4090 是三槽显卡(约 61mm 厚),而 6000 是双槽显卡(约 40mm 厚)。
消费级 GPU 的设计并没有考虑到相同的限制(即高密度构建),因此随着规模的扩大,它们的实用性开始下降。
冷却
接着讨论可能的尺寸问题……即使消费级显卡采用相同的双槽设计,还有其他问题。
专业级 GPU 通常配备有冷却系统(吹风机类型),这些系统设计用于从前到后通过显卡抽取空气,并且有封闭的罩子将空气直接排出机箱(即没有热空气在机箱内部循环)。这允许专业 GPU 紧密堆叠在机箱内,同时仍能高效地进行自我冷却,对机箱内其他组件或 GPU 的影响最小。
两张显卡,一张采用‘吹风机’冷却系统,另一张采用更常见的风扇冷却系统。图片由Nana Dua拍摄,发布在Pexels上。注释由作者提供。
消费级 GPU 通常使用从上/下方的风扇冷却。这不可避免地意味着 GPU 的热空气在机箱内会有一定程度的循环,因此需要优秀的机箱通风。
然而,在多个 GPU 的机箱中,其他显卡的紧密接近会使风扇冷却变得非常无效,并且不可避免地导致 GPU 和其他接近组件的温度不理想。
总的来说,专业显卡设计为紧凑高效地打包到系统中,同时保持冷却和自我封闭。
准确性
图片由Ricardo Arce拍摄,发布在Unsplash上。
这实际上与深度学习并不特别相关,但专业级 GPU 通常具有 ECC(错误更正码)RAM。在需要高精度(即,位翻转的潜在随机错误低)的处理过程中,这将非常有用。
然而,深度学习模型有时会调优为较少的数值精度(半精度 8 位计算),因此这对正在运行的计算可能并不会造成实际问题。
尽管如果这些随机位翻转导致你的模型崩溃,那么这也可能值得考虑。
深度学习模型
图片来源:Pixabay
对于深度学习模型,我希望它既先进又领先行业,并对 GPU 具有较高要求。同时,它也必须在难度上具有可扩展性,因为测试中的 GPU 具有广泛的能力范围。
一个专业级模型,适用于专业级显卡
要使模型符合行业标准,就排除了从头开始构建模型的可能性,因此在此比较中,将利用迁移学习使用现有的、经过验证的模型。
大数据
为确保输入数据的重量,分析将基于图像,特别是图像分类。
可扩展性
最终标准是可扩展性,有一组特定的模型完全符合这一标准……
EfficientNet
EfficientNet 由一系列图像分类模型(B0 到 B7)组成。每个模型都变得越来越复杂(也更准确)。随着你在模型系列中的进展,它也有不同的预期输入形状,从而增加了数据输入大小。
不同 EfficientNet 模型的比较——数据来源于EfficientNet: Rethinking Model Scaling for Convolutional Neural Networks——表格由作者提供
这有两个方面的效果:
-
随着你在不同的 EfficientNet 模型中进展,模型参数会增加(即,对 GPU 处理要求更复杂、更高的模型)。
-
需要处理的原始数据量也会增加(从 224x224 像素到 600x600 像素不等)。
最终,这为加载 GPU 提供了广泛的可能性,包括处理速度和 GPU RAM 需求。
数据
本文中使用的数据集是一组图像,描绘了游戏石头剪子布中手势的三种可能组合。数据¹。
来自数据集的四个示例,分为三种不同的类别。合成图像由作者提供。
每张图片为 PNG 格式,尺寸为 300(宽)像素 x 200(高)像素,全彩色。
原始数据集总共包含 2188 张图片,但为了这篇文章使用了一个较小的选择,共包含 2136 张图片(每个类别 712 张)。从原始数据集中略微减少总图片数,是为了平衡类别。
本文使用的平衡数据集可以在这里找到:
## notebooks/datasets/rock_paper_scissors at main · thetestspecimen/notebooks
这些数据集是原始“石头剪子布”数据集的一个选择,详细信息请见参考部分…
测试
如前所述,EfficientNet 提供了各种不同的级别,因此为了测试,以下将在每个 GPU 上运行:
-
EfficientNet B0(简单)
-
EfficientNet B3(中等)
-
EfficientNet B7(高强度)
这将测试显卡的速度能力,由于每个模型的总体参数不同,也会测试内存需求的范围,因为输入图像大小也会有所不同。
EfficientNet 模型将解锁所有层,并允许进行学习。
三个最终模型:
EfficientNetB0
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
input_layer (InputLayer) [(None, 224, 224, 3)] 0
data_augmentation (Sequenti (None, 224, 224, 3) 0
al)
efficientnetb0 (Functional) (None, None, None, 1280) 4049571
global_avg_pool_layer (Glob (None, 1280) 0
alAveragePooling2D)
output_layer (Dense) (None, 3) 3843
=================================================================
Total params: 4,053,414
Trainable params: 4,011,391
Non-trainable params: 42,023
_________________________________________________________________
EfficientNetB3
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
input_layer (InputLayer) [(None, 300, 300, 3)] 0
data_augmentation (Sequenti (None, 300, 300, 3) 0
al)
efficientnetb3 (Functional) (None, None, None, 1536) 10783535
global_avg_pool_layer (Glob (None, 1536) 0
alAveragePooling2D)
output_layer (Dense) (None, 3) 4611
=================================================================
Total params: 10,788,146
Trainable params: 10,700,843
Non-trainable params: 87,303
_________________________________________________________________
EfficientNetB7
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
input_layer (InputLayer) [(None, 600, 600, 3)] 0
data_augmentation (Sequenti (None, 600, 600, 3) 0
al)
efficientnetb7 (Functional) (None, None, None, 2560) 64097687
global_avg_pool_layer (Glob (None, 2560) 0
alAveragePooling2D)
output_layer (Dense) (None, 3) 7683
=================================================================
Total params: 64,105,370
Trainable params: 63,794,643
Non-trainable params: 310,727
_________________________________________________________________
速度测试
GPU 的速度将通过其完成一个周期的速度来评估。
更具体地说,每个显卡上将运行至少两个周期,第二个周期将用于判断处理速度。第一个周期通常会有额外的加载时间,因此不适合作为一般执行时间的参考。
第一个周期的运行时间仅供参考。
GPU 内存测试
为了测试 GPU 内存的极限,每个显卡的批量大小和每个 EfficientNet 模型(即 B0、B3 或 B7)已调整到尽可能接近该显卡的极限(即尽可能填满 GPU 内存)。
实际的峰值 GPU 内存使用情况也会披露,以便进行比较。
代码
一如既往,我将所有的 Python 脚本(GTX 1070 和 RTX 6000 Ada)以及笔记本(Tesla T4)都提供在 GitHub 上:
[## notebooks/pro-vs-consumer-graphics-card at main · thetestspecimen/notebooks
你现在无法执行该操作。你在另一个标签或窗口中登录了。你在另一个标签中登出了…
github.com](https://github.com/thetestspecimen/notebooks/tree/main/pro-vs-consumer-graphics-card?source=post_page-----a62bec69f557--------------------------------)
如果你愿意,也可以直接在 Colab 上访问 Tesla T4 的笔记本:
EfficientNetB0:
EfficientNetB3:
EfficientNetB7:
结果
照片由 Pixabay
EfficientNet B0
EfficientNet B3
EfficientNet B7
注意: 在第一轮训练中,我列出了括号中的秒数。这是第一轮和第二轮训练之间的时间差。
讨论 — 执行速度
图片由 Arek Socha 提供,来自 Pixabay
第一个要查看的项是执行速度。
对于这个特定的数据集,EfficientNet B0 对任何显卡都没有造成太大的挑战,所有显卡都在几秒钟内完成了一轮训练。
然而,需要记住的是,本文章使用的数据集很小,实际上,两块 RTX 6000 Ada 显卡在执行速度上大约比 GTX 1070(和 Tesla T4)快17 倍。对于 EfficientNet B3(快 8 倍)和 B7(快 11 倍),情况也基本相同。
区别在于,当将其视为执行时间时,这种速度的减慢会随着模型的复杂性增加而变得更具障碍。
例如,在这个非常小的数据集上,使用 GTX 1070 执行一个周期大约需要 15 分钟。与一对 RTX 6000 Ada 的 1 分钟多一点相比。
…情况更糟。
扩展规模
让我们现实一点。没有模型会在一个周期内收敛。对于像 EfficientNet 这样的模型,四百可能是一个更合理的数字。
这将是使用像 GTX 1070 这样的 GPU 需要4 天的差距,而在双 RTX 6000 Ada 设置下仅需几个小时(准确地说是 6.5 小时)。然后考虑到实际数据集不只有 2188 张图片,它可能有数百万张(作为参考,ImageNet有刚刚超过1400 万张图片)。
行业进展
另一个需要记住的是行业进展。EfficientNet 已经有几年历史了,情况已经发生了变化。
举个小例子,NoisyStudent在标准 EfficientNets 的基础上增加了一种叫做 EfficientNet-L2 的变体,并表示:
由于模型大小较大,EfficientNet-L2 的训练时间大约是 EfficientNet-B7 的五倍。
…所以如果你需要保持在前沿,速度确实很重要。
那么这对专业图形卡与消费者图形卡意味着什么呢?
事实是,如果你仅仅看执行速度,专业和消费者 GPU 之间几乎没有区别,如果你对比相同条件下的产品。RTX 4090 的速度几乎和 RTX 6000 Ada 一样。
RTX 4090 的速度几乎和 RTX 6000 Ada 一样。
目前为止,这个小实验仅仅说明了速度非常重要,因为行业标准模型的复杂性发展很快。老一代图形卡已经明显较慢。要跟上,就需要至少保持在硬件的前沿。
…规模在回答这个问题时确实很重要。
…但随着进展速度的加快(只需看看 GTP-3 和 GTP-4 的迅速发展),如果你想保持在前沿,即使是 RTX 4090 或 RTX 6000 Ada 级别的单个 GPU 也可能不够。如果是这样的话,专业级图形卡在构建系统时优越的散热、较低的功耗和更紧凑的尺寸就是一个显著的优势。
本质上,规模在回答这个问题时非常重要。
然而,速度只是一个方面。现在让我们转到 GPU RAM,这里情况会有些更有趣…
讨论 — GPU RAM
GPU RAM 在某些情况下是一个重要的考虑因素,甚至可能是是否可以使用某些模型或数据集的实际限制因素。
让我们看看一对 RTX 6000 Ada 的全面表现:
两张 RTX 6000 Ada GPU 正在运行深度学习模型。图片由作者提供
你可能会注意到上面的图片中两个 GPU 的 GPU RAM 都达到了 100%。然而,这并不是真实的使用情况:
默认情况下,TensorFlow 会映射几乎所有 GPU 的 GPU 内存(受
[*CUDA_VISIBLE_DEVICES*](https://docs.nvidia.com/cuda/cuda-c-programming-guide/index.html#env-vars)
)到进程中。这是为了通过减少内存碎片来更有效地利用设备上相对宝贵的 GPU 内存资源。
限制
绝对的限制被 GTX 1070(它有 8GB GPU RAM)所凸显,它只能以批量大小为 1 运行 EfficientNet B7(即它每次只能处理 1 张图片,然后更新模型参数并将下一张图片加载到 GPU RAM 中)。
这会引发两个问题:
-
由于频繁的参数更新和更频繁地将新数据加载到 GPU RAM 中(即更大的批量大小本质上更快),你会失去执行速度。
-
如果输入图像尺寸再大一点,模型将无法运行,因为它无法将单张图像放入 GPU RAM 中。
即使是 Tesla T4,它拥有不算差的 16GB GPU 内存,在 EfficientNet B7 上也只能处理批量大小为 2 的任务。
如前所述,16GB 的 GPU RAM 是大多数当前一代消费级 GPU 的良好代表,只有 RTX 4090 拥有 24GB。因此,如果你处理的是内存密集型原始数据,这对消费级 GPU 来说是一个相当显著的缺点。
此时,为什么所有专业 GPU 相比于消费级 GPU 拥有如此大量的 RAM 突然变得清晰。正如执行速度讨论中提到的,EfficientNet 已经不再处于前沿,因此今天的现实可能比这篇文章中的测试所述还要苛刻。
系统密度
关于 GPU RAM 的另一个考虑因素是系统密度。
例如,我可以访问的系统有一个可以容纳 4 张双高显卡的主板(我也见过最多可安装 8 张显卡的系统)。这意味着如果你的系统对 GPU RAM 有优先需求,那么专业 GPU 就是不二选择:
4 x RTX 6000 Ada = 192GB GPU RAM 和 1200W 功耗
4 x RTX 4090 = 96GB GPU RAM 和 1800W 功耗
(……正如我在文章前面提到的,RTX 4090 是一款三槽 GPU,所以这甚至不切实际。实际上,只有两张 RTX 4090 显卡才能实际适配,但为了方便比较,我们假设它是可行的。)
这不是一个小差异。要匹配 RTX 6000 Ada 系统的 GPU RAM,你将需要两个分别消耗至少 三倍功率 的系统。
要匹配 RTX 6000 Ada 系统的 RAM,你将需要两个分别消耗至少三倍功率的系统。
别忘了,因为你需要两个独立的系统,你还需要额外支付 CPU、电源、主板、冷却、机箱等费用。
关于系统 RAM 的一个附注…
你是否注意到在专业系统中,CPU 上下各有 8 根 64GB 的系统 RAM?图片来自 Exxact Corporation ,由 Michael Clayton 授权使用
还值得指出的是,重要的并不仅仅是 GPU RAM。随着 GPU RAM 的增加,你需要同步增加系统 RAM。
你可能会注意到,在 Tesla T4 的 Jupyter notebooks 中,我已注释掉了以下优化:
train_data = train_data.cache().prefetch(buffer_size=tf.data.AUTOTUNE)
val_data = val_data.cache().prefetch(buffer_size=tf.data.AUTOTUNE)
这是因为,对于 EfficientNet B7,如果启用这些设置,训练将崩溃。
为什么?
因为“.cache()”优化将数据保留在系统内存中,以便有效地传递给 GPU,而 Colab 实例只有 12GB 的系统内存。这还不够,即使 GPU RAM 峰值为 9.9GB:
这个 [.cache()] 会将一些操作(如文件打开和数据读取)从每个训练周期中省略。
然而,专业系统有 8 根 64GB 的系统 RAM,总计 512GB 的系统 RAM。所以即使两张 RTX 6000 Ada GPU 合计有 96GB 的 GPU RAM,系统 RAM 仍有足够的余量来处理大量缓存。
结论
图片来自 Exxact Corporation ,由 Michael Clayton 授权使用
那么,专业级显卡在深度学习中是否优于消费级显卡?
如果钱不是问题,那么是的,它们更好。
这是否意味着你应该放弃考虑用于深度学习的消费级显卡?
不,并不是。
这完全取决于具体的需求,通常情况下,更是规模。
大型数据集
如果你知道你的工作负载将是内存密集型的(例如大型语言模型、图像或视频分析),那么同一代和处理速度的专业显卡通常拥有大约两倍的 GPU RAM。
这完全取决于具体的需求和规模。
这是一个显著的优势,特别是考虑到与消费级显卡相比,实现这一点不需要额外增加能源需求。
较小的数据集
如果你的 RAM 需求不高,那么问题就更复杂了,涉及到可靠性、兼容性、支持、能源消耗,以及额外 10% 的速度是否值得那显著的价格上涨。
规模
如果你打算投资重大基础设施,那么可靠性、能源消耗和系统密度可能从低优先级变为相当重要的考虑因素。这些都是专业 GPU 擅长的领域。
相反,如果你需要一个较小的系统,并且高 GPU 内存需求并不重要,那么考虑消费者级别的显卡可能会有利。与大规模相关的因素,如可靠性和能源消耗将变得不那么重要,系统密度也不会成为问题。
最终结论
总的来说,这是一个平衡的过程,但如果我必须选择两个因素来总结选择消费者级 GPU 与专业级 GPU 之间最重要的因素,那就是:
-
GPU 内存
-
系统规模
如果你有较高的 GPU 内存需求,或需要配备多个 GPU 的大型系统,那么你需要一个专业级的 GPU/多个 GPU。
否则,大多数情况下,消费者级别可能会是一个更好的选择。
如果你觉得这篇文章有趣或有用,记得关注我,或订阅我的通讯以获取更多类似的内容。
如果你还没有,可以考虑订阅 Medium。你的会员费不仅直接支持我,还支持你阅读的其他作者。你还将获得 Medium 上每一篇文章的完全无限制访问权限。
使用我的推荐链接注册将为我带来少量佣金,但不会影响你的会员资格,所以如果你选择这样做,谢谢你。
[## 通过我的推荐链接加入 Medium - Mike Clayton
阅读 Mike Clayton 的每一篇文章(以及 Medium 上的其他成千上万的作者)。你的会员费直接支持…
medium.com](https://medium.com/@maclayton/membership?source=post_page-----a62bec69f557--------------------------------)
参考文献
[1] Julien de la Bruère-Terreault, 石头剪子布图像 (2018), Kaggle, 许可协议:CC BY-SA 4.0
使用回归进行二元事件的概率预测
原文:
towardsdatascience.com/probabilistic-forecasting-of-binary-events-using-regression-4f8a8022ec37
使用累积分布函数预测极端值的概率
·发布于Towards Data Science ·阅读时间 5 分钟·2023 年 3 月 8 日
–
图片由Silas Baisch提供,Unsplash上的照片
在本文中,我们将探讨时间序列中二元事件的概率预测。目标是预测时间序列超过临界阈值的概率。
你将学习如何(以及为什么)使用回归模型来计算二元概率。
介绍
首先,为什么你会使用回归来计算二元概率而不是分类器?
二元事件的概率预测通常被视为分类问题。但回归方法可能更为合适,原因有两个:
-
对点预测和事件概率的兴趣;
-
超过阈值的变动。
对点预测和事件概率的兴趣
有时你可能想要预测未来观测值的数值以及相关事件的概率。
例如,在预测海洋波高的情况下。海洋波是一个有前景的清洁能源来源。短期点预测对于估算从这一来源可以生产多少能源非常重要。
但是,大浪可能会损坏波能转换器——这些设备将波能转换为电力。因此,预测波高超过临界阈值的概率也很重要。
因此,在预测海洋波高的情况下,最好使用一个模型来计算两种类型的预测。
超过阈值的变动
时间序列中的二元事件通常由超越——当时间序列超过预定义阈值来定义。
在某些情况下,最合适的阈值可能会根据不同的因素或风险情况而改变。因此,用户可能会对估计不同阈值的超越概率感兴趣。
分类模型在训练过程中固定了阈值,在推断过程中不能更改。而回归模型则独立于阈值构建。因此,在推断过程中,你可以同时计算多个阈值的事件概率。
如何从预测中计算二元概率
照片由Jamie O’Sullivan拍摄,来源于Unsplash
那么,你如何使用回归模型来估计二元事件的概率呢?
让我们继续讨论关于预测海洋波浪高度的示例。
数据集
我们将使用从爱尔兰海岸上的一个智能浮标收集的时间序列[1]。
import pandas as pd
START_DATE = '2022-01-01'
URL = f'https://erddap.marine.ie/erddap/tabledap/IWaveBNetwork.csv?time%2CSignificantWaveHeight&time%3E={START_DATE}T00%3A00%3A00Z&station_id=%22AMETS%20Berth%20B%20Wave%20Buoy%22'
# reading data directly from erdap
data = pd.read_csv(URL, skiprows=[1], parse_dates=['time'])
# setting time to index and getting the target series
series = data.set_index('time')['SignificantWaveHeight']
# transforming data to hourly and from centimeters to meters
series_hourly = series.resample('H').mean() / 100
海洋波浪平均高度的每小时时间序列。水平虚线表示一个关键安全阈值。图像由作者提供。
超越概率预测
我们的目标是预测一个大波的概率,我们将其定义为超过 6 米的波浪。这个问题是超越概率预测的一个特定实例。
在上一篇文章中,我们探讨了超越概率预测背后的主要挑战。通常,这个问题通过两种方法之一来解决:
-
一个概率二元分类器;
-
一个预测集成。概率是根据预测超过阈值的模型比例计算的。
在这里,你将了解第三种方法。这种方法基于一个预测模型,但不一定是一个集成模型。像 ARIMA 这样的模型也可以。
使用累积分布函数
假设预测模型做出了一个“y”的预测。然后,假设这个预测遵循均值为“y”的正态分布。当然,分布的选择取决于输入数据。在这里,我们为了简化,选择正态分布。在平稳性条件下,标准差(“s”)可以使用训练数据来估计。
在我们的示例中,“y”是模型预测的波浪高度。“s”是训练数据中波浪高度的标准差。
我们通过累积分布函数(CDF)得到二元概率预测。
什么是累积分布函数(CDF)?
当在值 x 上进行评估时,CDF 表示随机变量取小于或等于 x 的值的概率。我们可以取补充概率(1 减去该概率)来获得随机变量超越 x 的概率。
标准正态分布的累积分布函数。图片由作者提供。
在我们的案例中,x 是表示超越的感兴趣的阈值。
这里是如何使用 Python 进行这项操作的一个片段:
import numpy as np
from scipy.stats import norm
# a random series from the uniform dist.
z = np.random.standard_normal(1000)
# estimating the standard dev.
s = z.std()
# fixing the exceedance threshold
# this is a domain dependent parameter
threshold = 1
# prediction for a given instant
yhat = 0.8
# probability that the actual value exceeds threshold
exceedance_prob = 1 - norm.cdf(threshold, loc=yhat, scale=s)
预测大波浪
让我们看看如何使用 CDF 来估计大波浪的概率。
首先,我们使用自回归构建预测模型。
# using past 24 lags to forecast the next value
N_LAGS, HORIZON = 24, 1
# the threshold for large waves is 6 meters
THRESHOLD = 6
# train test split
train, test = train_test_split(series_hourly, test_size=0.2, shuffle=False)
# transforming the time series into a tabular format
X_train, Y_train = time_delay_embedding(train, n_lags=N_LAGS, horizon=HORIZON, return_Xy=True)
X_test, Y_test = time_delay_embedding(test, n_lags=N_LAGS, horizon=HORIZON, return_Xy=True)
# training a random forest
regression = RandomForestRegressor()
regression.fit(X_train, Y_train)
# getting point forecasts
point_forecasts = regression.predict(X_test)
然后,我们可以使用 CDF 将点预测转化为超越概率。
import numpy as np
from scipy.stats import norm
std = Y_train.std()
exceedance_prob = np.asarray([1 - norm.cdf(THRESHOLD, loc=x_, scale=std)
for x_ in point_forecasts])
该模型能够有效地检测到大波浪的发生:
在最近的一篇论文中,我将这种方法与分类器和集成方法进行了比较。基于 CDF 的方法带来了更好的预测。你可以在参考文献[2]中查看详细信息。实验的代码也可以在Github上找到。
关键要点
-
从预测模型中获取二值概率是理想的,尤其是在点预测也有用或超越阈值发生变化的问题中;
-
你可以使用预测模型通过 CDF 获得二值超越概率;
-
使用 CDF 是一种与使用分类器或回归集成相竞争的替代方案。
感谢阅读,下次故事见!
相关文章
参考文献
[1] 爱尔兰波浪浮标 (许可证:知识共享署名 4.0)
[2] Vitor Cerqueira 和 Luis Torgo。“通过回归预测显著波高的超越概率。” arXiv 预印本 arXiv:2206.09821 (2022)。
使用 TensorFlow 进行概率逻辑回归
原文:
towardsdatascience.com/probabilistic-logistic-regression-with-tensorflow-73e18f0ddc48
概率深度学习
·发表于 数据科学前沿 ·阅读时间 9 分钟·2023 年 1 月 25 日
–
介绍
本文属于“概率深度学习”系列。该系列每周介绍深度学习中的概率方法。主要目标是扩展深度学习模型以量化不确定性,即了解它们不知道的内容。
在这篇文章中,我们将介绍概率逻辑回归的概念,这是一种强大的技术,允许在预测过程中纳入不确定性。我们将探讨这种方法如何在数据噪声大或模型过拟合的情况下提供更稳健和准确的预测。此外,通过在模型参数上引入先验分布,我们可以对模型进行正则化,防止过拟合。这种方法是进入贝叶斯深度学习激动人心的世界的绝佳起点。
已发布的文章:
-
从零开始的最大似然估计在 TensorFlow Probability 中
-
从零开始的 TensorFlow 概率线性回归
-
确定性与概率深度学习
-
从头开始使用 TensorFlow 实现朴素贝叶斯
-
使用 TensorFlow 进行概率逻辑回归
图 1:今天的座右铭:直线可以分隔更多的事物,比我们想象的要多 (source)
与往常一样,代码可以在我的 GitHub 上找到。
初步工作
在我们系列文章的前一篇中,我们从头开始构建了朴素贝叶斯算法,并使用它根据选择的特征对葡萄酒样本进行分类。这一次,我们将使用概率逻辑回归方法。由于我们已经采用了端到端的方法,我将跳过大部分探索性数据分析部分和类先验分布定义。
唯一需要注意的是我们为此模型选择的特征有所不同。
图 2:根据酒精和色调的目标样本分布。
我们将使用色调和类黄酮作为自变量。注意这些特征在分隔目标变量方面比酒精和色调更有效。
图 3:根据类黄酮和色调的目标样本分布。
概率逻辑回归
逻辑回归是一种广泛使用的二元分类统计方法,用于建模二元响应变量与一个或多个预测变量之间的关系。逻辑回归可以用来建模二元结果的概率作为预测变量的函数。传统的逻辑回归模型是一个确定性模型,假设预测变量与响应变量之间的关系是固定且已知的。然而,在许多实际应用中,预测变量与响应变量之间的真实关系是不确定的,因此使用概率方法更为合适。
概率逻辑回归模型通过概率框架建模预测变量与二元响应变量之间的关系,并能够考虑数据和模型参数的不确定性。这是通过对模型参数施加概率分布来实现的,而不是假设固定值。通过这种方式,与传统逻辑回归模型相比,概率逻辑回归模型可以提供更准确的预测和更好的不确定性量化。
最流行的逻辑回归概率模型之一是贝叶斯逻辑回归模型。这些模型基于贝叶斯定理,该定理指出,给定数据的模型参数的后验概率与数据给定参数的似然和参数的先验概率的乘积成正比。通常,贝叶斯逻辑回归模型使用共轭先验分布来对模型参数进行建模,这允许对后验分布进行封闭形式的解法。这使得计算响应变量给定预测变量的概率成为可能,这被称为后验预测分布。
似然
在本节中,我们介绍了一种在逻辑回归的概率方法中计算类别条件密度的方法。我们的方法基于均值的最大似然估计,其由以下公式给出
其中 𝑋(𝑛)𝑖 是第 n 个样本的第 i 个特征,𝑌(𝑛) 是第 n 个样本的目标标签,𝑘 是类别标签,𝛿(𝑌(𝑛)=𝑦𝑘) 是一个指示函数,如果 𝑌(𝑛)=𝑦𝑘 则等于 1,否则为 0。
为了估计标准差 𝜎𝑖,我们将通过从数据中学习这些参数来实现,而不是使用封闭形式的解法。我们通过实现自定义训练循环来实现这一点,该循环通过最小化数据的平均每个示例负对数似然来优化标准差的值。
我们的函数根据上述方程计算类别条件高斯分布的均值 𝜇𝑖𝑘。然后,它使用 MultivariateNormalDiag 创建一个多变量高斯分布对象,将均值设置为 𝜇𝑖𝑘,将尺度设置为 TensorFlow 变量。
该函数运行一个自定义训练循环,循环次数为指定的纪元,其中计算平均每个示例的负对数似然。接下来,梯度被传播,尺度变量相应地更新。在每次迭代中,尺度变量的值和损失都会被保存。
它返回一个包含三个对象的元组:损失值、每次迭代的尺度变量和最终学习到的批量 MultivariateNormalDiag 分布对象。
def train(x, y, scales, optimiser, epochs):
estimated_scales = []
n_classes = np.unique(y).shape[0]
n_features = x.shape[1]
counts = np.zeros(n_classes)
mean_cond_class = []
std_feature_given_class = []
for c_k in range(n_classes):
mean_cond_class.append(np.mean(x[np.squeeze(y==c_k)], axis=0))
mean_cond_class = np.asarray(mean_cond_class, dtype=np.float32)
x_c = np.concatenate((x,y.reshape(-1,1)), axis=1)
mv_normal_diag = tfd.MultivariateNormalDiag(loc=mean_cond_class,scale_diag=scales)
x = np.expand_dims(x , 1).astype('float32')
for i in range(epochs):
with tf.GradientTape() as tape:
tape.watch(mv_normal_diag.trainable_variables)
predictions = - mv_normal_diag.log_prob(x)
p1 = tf.reduce_sum(predictions[np.squeeze(y==0)][:,0])
p2 = tf.reduce_sum(predictions[np.squeeze(y==1)][:,1])
loss = p1 + p2
grads = tape.gradient(loss, mv_normal_diag.trainable_variables)
opt.apply_gradients(zip(grads, mv_normal_diag.trainable_variables))
estimated_scales.append(mv_normal_diag.trainable_variables[0].numpy())
print('Step {:03d}: Loss: {:.3f}: Scale1: {:.3f}: Scale2: {:.3f}'.format(i, loss, mv_normal_diag.trainable_variables[0].numpy()[0], mv_normal_diag.trainable_variables[0].numpy()[1]))
estimated_scales = np.asarray(estimated_scales)
return estimated_scales, mv_normal_diag
让我们创建要训练的变量。
scales = tf.Variable([1., 1.], name='scales')
opt = tf.keras.optimizers.Adam(learning_rate=0.01)
epochs = 100
我们现在准备开始训练过程。
scales_arr, class_conditionals_binary = train(x_train, y_train, scales, opt, epochs)
-----
Step 000: Loss: 290.708: Scale1: 0.990: Scale2: 0.990
Step 001: Loss: 288.457: Scale1: 0.980: Scale2: 0.980
Step 002: Loss: 286.196: Scale1: 0.970: Scale2: 0.970
Step 003: Loss: 283.924: Scale1: 0.960: Scale2: 0.960
Step 004: Loss: 281.641: Scale1: 0.950: Scale2: 0.950
Step 005: Loss: 279.348: Scale1: 0.940: Scale2: 0.940
[...]
最后,我们可以检查模型如何区分我们的葡萄酒类别。
图 4:类别条件密度轮廓。
使用我们在前一篇文章中定义的函数,我们可以为测试集生成预测。在上面的图中,我们可以看到类别被很好地分开,因此我们从模型中获得了良好的准确率。
predictions = predict(prior_binary, class_conditionals_binary, x_test)
accuracy = accuracy_score(y_test, predictions)
print("Test accuracy: {:.4f}".format(accuracy))
---------
Test accuracy: 0.92
为了定量评估我们的概率逻辑回归模型的性能,我们绘制了决策区域。这些区域由分隔两个类别的边界定义,为模型区分类别的能力提供了见解。我们的分析表明,模型能够有效地分隔数据集中的两个类别,这从视觉上明显的区域可以看出。然而,需要注意的是,决策边界被限制为线性,这是逻辑回归模型的假设。
plt.figure(figsize=(9, 5))
plot_data(x_train, y_train)
x0_min, x_0_max = x_train[:, 0].min()-0.5, x_train[:, 0].max()+0.5
x1_min, x_1_max = x_train[:, 1].min()-0.5, x_train[:, 1].max()+0.5
contour_plot((x0_min, x0_max), (x1_min, x1_max),
lambda x: predict(prior_binary, class_conditionals_binary, x),
1, label_colors, levels=[-0.5, 0.5, 1.5],
num_points=200)
plt.title("Training set with decision regions")
plt.show()
图 5:类别条件决策区域。
逻辑回归中的缺失链接
在本节中,我们将上述类别条件密度的定义与逻辑回归联系起来。我们展示了预测分布 𝑃(𝑌=𝑦0|𝑋) 可以写作
其中 𝑃(𝑋|𝑌=𝑦0) 和 𝑃(𝑋|𝑌=𝑦1) 是类别条件密度,𝑃(𝑌=𝑦0) 和 𝑃(𝑌=𝑦1) 是类别先验。
这个方程可以重新排列为 𝑃(𝑌=𝑦0|𝑋)=𝜎(𝑎),其中
是 sigmoid 函数,以及
是对数赔率。
通过我们额外的建模假设,即共享协方差矩阵 Σ,可以使用高斯概率密度函数显示 𝑎 实际上是 𝑋 的线性函数,
其中
这个线性函数 𝑎=𝑤𝑇𝑋+𝑤0 解释了逻辑回归的决策边界为何是线性的。可以看出,参数 𝑤 和 𝑤0 是类别条件密度 𝑃(𝑋|𝑌=𝑦0) 和 𝑃(𝑋|𝑌=𝑦1) 以及类别先验 𝑃(𝑌=𝑦0) 和 𝑃(𝑌=𝑦1) 的函数。这些参数通常通过最大似然估计,如我们在前面章节中所做的那样。
生成逻辑回归模型
在本节中,我们使用前面章节中推导出的方程来直接参数化生成逻辑回归模型的输出伯努利分布。具体来说,我们使用先验分布和类别条件分布来计算权重和偏置项 𝑤 和 𝑤0。
为了实现这一点,我们编写了一个新函数,该函数以先验分布和类别条件分布作为输入。该函数使用这些分布的参数来计算权重和偏置项,根据前面章节中推导出的方程。
函数的输入是对两个类别的先验分布和类别条件分布。
然后,函数使用这些输入来计算权重和偏置项,如下所示
该函数返回𝑤和𝑤0,这可以用来直接参数化生成逻辑回归模型的输出伯努利分布。这允许对模型参数及其与先验和类条件分布的关系有更直接和透明的理解。
def get_logistic_regression_params(prior, class_conditionals):
cov = class_conditionals.covariance()[0]
cov_inv = tf.linalg.inv(cov)
mu0 = class_conditionals.parameters['loc'][0]
mu1 = class_conditionals.parameters['loc'][1]
w = np.matmul(cov_inv,(mu0-mu1))
w0 = - 0.5 * (np.matmul(tf.transpose(mu0), np.matmul(cov_inv, mu0)))\
+ 0.5 * (np.matmul(tf.transpose(mu1), np.matmul(cov_inv, mu1)))\
+ np.log(prior.parameters['probs'][0] / prior.parameters['probs'][1])
return w, w0
w, w0 = get_logistic_regression_params(prior_binary, class_conditionals_binary)
我们现在可以使用这些参数制作轮廓图,以显示我们逻辑回归模型的预测分布。
fig, ax = plt.subplots(1, 1, figsize=(9, 5))
plot_data(x_train, y_train, alpha=0.35)
x0_min, x0_max = x_train[:, 0].min()-0.5, x_train[:, 0].max()+0.5
x1_min, x1_max = x_train[:, 1].min()-0.5, x_train[:, 1].max()+0.5
X0, X1 = get_meshgrid((x0_min, x0_max), (x1_min, x1_max))
logits = np.dot(np.array([X0.ravel(), X1.ravel()]).T, w) + w0
Z = tf.math.sigmoid(logits)
lr_contour = ax.contour(X0, X1, np.array(Z).T.reshape(*X0.shape), levels=10)
ax.clabel(lr_contour, inline=True, fontsize=10)
contour_plot((x0_min, x0_max), (x1_min, x1_max),
lambda x: predict(prior_binary, class_conditionals_binary, x),
1, label_colors, levels=[-0.5, 0.5, 1.5],
num_points=200)
plt.title("Training set with prediction contours")
plt.show()
图 6: 我们逻辑回归模型预测分布的密度轮廓。
我们的方法是否完全是贝叶斯的?
上述方法可以被视为一种贝叶斯推断形式,因为它涉及通过先验分布引入关于模型参数的先验知识,并通过类条件分布使用观测数据更新这些知识。这是贝叶斯推断的一个关键方面,旨在将关于模型参数的先验知识和不确定性融入推断过程。
在贝叶斯推断中,目标是计算给定观测数据下模型参数的后验分布。上述方法可以看作是一种近似贝叶斯推断,因为它涉及使用类条件密度和先验分布的最大似然估计来计算模型的权重和偏置。同时,该方法通过共享协方差矩阵来引入不确定性,这作为正则化项。
值得注意的是,上述方法并非完全贝叶斯,因为它没有提供模型参数的后验分布的封闭形式。相反,它使用基于最大似然估计的近似。
结论
在这篇文章中,我们提出了一种概率方法来处理预测过程中的内在不确定性。通过在模型参数上引入先验分布,我们的方法对模型进行正则化,防止过拟合。我们展示了如何使用 TensorFlow Probability 实现该方法以及如何分析其结果。
值得注意的是,尽管我们的方法包含了贝叶斯原则,但它并不是完全贝叶斯的方法,因为我们没有模型参数的完整后验分布。然而,考虑到预测过程中的内在不确定性,已经使我们对预测过程更有信心。
保持联系: LinkedIn
参考文献和材料
[1] — 葡萄酒数据集
[2] — Coursera: 深度学习专项课程
[3] — Coursera: TensorFlow 2 深度学习专项课程
[4] — TensorFlow 概率指南和教程