将神经网络表示为 API 的资源 JSON?

问题描述 投票:0回答:1

我想将神经网络表示为 REST API 可用的资源。每当客户想要创建神经网络模型时,他们都可以 POST 神经网络的 JSON 表示形式,该表示形式应该是一个

Model
对象,其中包含
Layer
对象列表。

A

Model
的主要数据是
Layer
对象列表,其中每个
Layer
可以是完全连接的
Linear
层、某种激活层(如
ReLU
)或其他有效的 PyTorch 网络像
Conv2d
一样分层。然而,每一层都需要一组不同的参数。
Linear
层需要节点数量,
Conv2d
需要内核大小和通道数,而
ReLU
实际上不需要任何东西。

我的问题是,我应该如何表示这个

Layer
列表,特别是在 FastAPI 中?

我有两个想法,但我都犹豫不决。

想法 1:带有
params
变量的枚举参数

在这里,我们有一个代表一切的

Layer
模型,其中“类型”由
type
变量处理。

from enum import Enum
from pydantic import BaseModel

class LayerType(str, Enum):
    linear = "Linear"
    relu = "ReLU"
    conv2d = "Conv2d"
    # ... other layer types

class Layer(BaseModel):
    type: LayerType
    params: dict # ?

class Model(BaseModel): # main representation of the neural network
    name: str
    layers: list[Layer]

有了这个想法,客户端将指定一个 JSON 对象列表,指定

type
并在
params
中填写正确的信息。然而,这会使类型检查变得极其乏味,因为我必须自己验证
params
和/或指定一个长
@model_validator()
方法,对吗?

想法 2:为每个层类型建立单独的 API 模型

相反,我可以为每个图层类型指定一个模型,如下所示:

from pydantic import BaseModel
from typing import Union
from typing_extensions import Self

class LinearLayer(BaseModel):
    size: int

    @model_validator(mode='after')
    def check_params(self) -> Self:
        assert self.size > 0 and self.size < 256, "`size` must be between 1 and 255, inclusive."
        return self

class DropoutLayer(BaseModel):
    dropout_prob: float = 0.5

    @model_validator(mode='after')
    def check_params(self) -> Self:
        assert self.dropout_prob >= 0.0 and self.dropout_prob <= 1.0, \
        "`dropout_prob` must be between 0.0 and 1.0, inclusive."
        return self

class Conv2DLayer(BaseModel):
    num_channels: int
    kernel: int | None = 3

    @model_validator(mode='after')
    def check_params(self) -> Self:
        assert self.num_channels > 0 and self.num_channels < 256, \
                "`num_channels` must be in the interval [1, 255]"
        assert self.kernel > 0 and self.kernel < 4, \
                "`kernel` must be in [1, 3]"
        return self

Layer = Union[LinearLayer, DropoutLayer, Conv2DLayer] # ***

class Model(BaseModel): # main representation of the neural network
    name: str
    layers: list[Layer]

在这两个想法之间,这个想法似乎肯定要好得多,因为我可以检查每个字段并限制每个图层类型的范围。一个小问题是,如果我想要几个不同的

Layer
,则
Union
类型需要非常长的
Layer
。但我对这个想法的主要问题是让它正确匹配每一层。如果我有一个简单的 API POST 方法,例如:

from fastapi import FastAPI
from .schemas import * # the Model schema and layer stuff

app = FastAPI()
@app.post("/models/")
async def create_model(model: Model) -> Model:
    return model

并使用这个简单的 JSON 测试此 POST 方法:

{
  "name": "test_model",
  "layers": [
    {
      "size": 0
    },
    {
      "dropout_prob": 0.5
    },
    {
      "num_channels": 0
    }
  ]
}

我收到以下回复:

{
  "name": "test_model",
  "layers": [
    {
      "dropout_prob": 0.5
    },
    {
      "dropout_prob": 0.5
    },
    {
      "dropout_prob": 0.5
    }
  ]
}

我认为我不理解 FastAPI 如何将 JSON 对象与相应的 pydantic BaseModel 相匹配的基本原理。这是我第一次使用 REST API。

我可以使用某种 pydantic 继承/多态性来解决这个问题吗?或者我应该去实施想法#1而不是#2?我没有太多运气浏览 FastAPI 和 pydantic 文档,也没有找到任何提及这样的情况。

python-3.x rest fastapi pydantic
1个回答
0
投票

您的最终解决方案将取决于您的最终需求。

如果您只想存储模型层的数据,并且除了验证之外不需要任何特定的业务逻辑,第二个解决方案看起来不错,您只需要填充response_model即可。

但是,如果您需要为不同类型的模型添加不同的业务逻辑,那么通过一个端点来执行此操作会很不舒服,并且您可能需要为不同的模型类型提供单独的端点:

POST /layers/linear
POST /layers/conv2d
...

这个解决方案会更加宁静

在这两种情况下,我建议您了解 FastAPI 如何生成 /docs 端点,这可能有助于更好地理解请求和响应输出

© www.soinside.com 2019 - 2024. All rights reserved.