【问题标题】:JsonProvider gives me different types that I cannot unifyJsonProvider 给了我无法统一的不同类型
【发布时间】:2018-09-13 00:09:54
【问题描述】:

我正在使用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 }

错误 FS0438 重复方法。删除元组、函数、度量单位和/或提供的类型后,方法 Unify1 与 Tensor 类型中的另一个方法具有相同的名称和签名。

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

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"

此变体无法编译,因为

错误 FS3062 不允许使用提供的类型 JsonProvider<...>.Weight 进行此类型测试,因为此提供的类型将在运行时擦除为 Runtime.BaseTypes.IJsonDocument。

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

【问题讨论】:

    标签: json types f# f#-data


    【解决方案1】:

    我同意推断的类型并不像它应该的那样好。 XML type provider 有一个静态参数 Global,它根据名称统一整个文档中的 XML 元素——因此推测 JSON 提供程序可以做类似的事情(但更棘手,因为我们必须识别这两条记录仅基于它们的字段或父元素中使用的标签是“相同的”...)。如果您有兴趣为 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 |&gt; processWeight),但我不想让示例过于复杂.

    【讨论】:

    • 有趣的是,我没有想到将JsonValue 传递给正确的构造函数。谢谢。
    猜你喜欢
    • 2015-07-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-03-11
    • 1970-01-01
    • 2019-04-15
    • 2021-03-10
    相关资源
    最近更新 更多