Я использую Поставщик типа JSON для загрузки созданного мной файла 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 typeTensor
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 toRuntime.BaseTypes.IJsonDocument
at runtime.
Как я могу заставить поставщика типов создавать унифицированный тип? В качестве альтернативы, как мне самому объединить их, чтобы сделать компилятор счастливым?
Я согласен с тем, что предполагаемый тип не так хорош, как мог бы быть. Поставщик типа XML имеет статический параметр 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
Немного более изящной версией было бы использование статических ограничений членов для доступа к свойству JsonValue
в функции processWeight
(чтобы вы могли просто вызвать nn.Conv1.Weight |> processWeight
), но я не хотел усложнять образец.
Интересно, что я не подумал передать
JsonValue
правильному конструктору. Спасибо.