JsonProvider 给了我无法统一的不同类型

JsonProvider gives me different types that I cannot unify

我正在使用 JSON type provider 加载我创建的 JSON 文件。类型提供者的最小输入如下所示:

{
  "conv1": {
    "weight": {
      "shape": [ 64, 3, 7, 7 ],
      "data": [ 1e-30, -0.01077061053365469 ]
    }
  },
  "bn1": {
    "eps": 1e-05,
    "weight": {
      "shape": [ 64 ],
      "data": [ 1e-30, 0.2651672959327698 ]
    },
    "bias": {
      "shape": [ 64 ],
      "data": [ 1e-30, 0.24643374979496002 ]
    }
  }
}

虽然两个 weight 部分具有相同的形状和类型,但类型提供程序为我提供了两个不同但等效的类型:

type Weight =
    inherit IJsonDocument
    new : shape: int [] * data: float [] -> Weight
    member Data : float []
    member JsonValue: JsonValue
    member Shape: int []

type Weight2 =
    inherit IJsonDocument
    new : shape: int [] * data: float [] -> Weight2
    member Data : float []
    member JsonValue: JsonValue
    member Shape: int []

首先,这不太好,但也许它无法弄清楚它们的意思是一样的。所以我坐下来尝试编写一个函数来统一两者,以便我可以从那里继续——我失败了。

我的第一个方法是使用重载:

type Tensor = {
    Data:single[]
    Shape:int list
} with
    static member Unify1 (w:NN.Weight) = { Data = w.Data |> Array.map single; Shape = w.Shape |> Array.toList }
    static member Unify1 (w:NN.Weight2) = { Data = w.Data |> Array.map single; Shape = w.Shape |> Array.toList }

Error FS0438 Duplicate method. The method Unify1 has the same name and signature as another method in type Tensor once tuples, functions, units of measure and/or provided types are erased.

然后我尝试了这样的手动类型测试:

let unify2 (o:obj) =
    match o with
    | :? NN.Weight as w -> { Data = w.Data |> Array.map single; Shape = w.Shape |> Array.toList }
    | :? NN.Weight2 as w -> { Data = w.Data |> Array.map single; Shape = w.Shape |> Array.toList }
    | _ -> failwith "pattern oops"

此变体无法编译,因为

Error FS3062 This type test with a provided type JsonProvider<...>.Weight is not allowed because this provided type will be erased to Runtime.BaseTypes.IJsonDocument at runtime.

如何让类型提供者生成统一的类型?或者,我将如何在让编译器满意的同时自己统一它们?

我同意推断的类型并不像它应该的那样好。 XML type provider 有一个静态参数 Global,它根据名称统一了整个文档中的 XML 元素——所以据推测,JSON 提供程序可以做类似的事情(但它是更棘手,因为我们必须根据它们的字段或父元素中使用的标签来确定两条记录是 "the same"...)。如果您有兴趣为 F# Data 做贡献,请打开一个问题来讨论这个!

与此同时,我认为一个合理的解决方法是获取底层 JsonValue 然后将其包装回提供的 Weight 类型。这将适用于两个 weight 记录,因为它们具有相同的字段:

type NN = JsonProvider<"""{ ... }""">

let processWeight (js:JsonValue) = 
  let w = NN.Weight(js)
  w.Data, w.Shape

let nn = NN.GetSample()
nn.Conv1.Weight.JsonValue |> processWeight
nn.Bn1.Weight.JsonValue |> processWeight

更高级的版本是使用静态成员约束来访问 processWeight 函数中的 JsonValue 属性(这样你就可以调用 nn.Conv1.Weight |> processWeight) , 但我不想让样本太复杂。