Дешифровка из json

А что за ошибка?

Кстати, посмотрел на место ошибки через Вашу программу и понял, что руки у меня не из того места. Мой код выдаёт ошибку там, где её в JSON нет.

Я выше привёл ссылку на файл JSON и программу. При считывании данных 4-х мерного массива вылетает исключение “Преобразование невозможно”. Это происходит в том месте, где JSON не повреждён. Даже не знаю, в чём дело…

Смотрите, когда вы применяете as для 2 несовместимых типов, которые нельзя друг в друга перевести - as возвращает nil, без исключения. Но если тип в который вы преобразуете value-тип (передаётся не по ссылке а по значению, как Decimal) - вы получаете исключение, типа “невозможно преобразовать”.

Чтоб непонятных ошибок не было - могу порекомендовать нормально пользоваться исключениями:

{$Reference 'System.Web.Extensions.dll'}
uses System.Collections.Generic;
Uses System.Web.Script.Serialization;
Uses System;

type
  ObjArr = array of System.object;
  Dic = Dictionary<string, object>;
  
  CannotConvertException = class(Exception)
    
    public constructor(tf,tt:System.Type) :=
    inherited create($'Failed to convert from <{tf}> to <{tt}>');
    
  end;

procedure SaveWeights(bw: System.IO.BinaryWriter; o: System.object);
begin
  
  var a1 := o as ObjArr;
  if a1 = nil then raise new CannotConvertException(o.GetType,typeof(ObjArr));
  
  bw.Write(a1.Length);//если не записать длинны массивов тут - вам придётся потом её от руки вводить
  
  foreach var o1 in a1 do
  begin
    var a2 := o1 as ObjArr;
    if a2 = nil then raise new CannotConvertException(o1.GetType,typeof(ObjArr));
    
    bw.Write(a2.Length);
    
    foreach var o2 in a2 do
    begin
      var a3 := o2 as ObjArr;
      if a3 = nil then raise new CannotConvertException(o2.GetType,typeof(ObjArr));
      
      bw.Write(a3.Length);
      
      foreach var o3 in a3 do
      begin
        var a4 := o3 as ObjArr;
        if a4 = nil then raise new CannotConvertException(o3.GetType,typeof(ObjArr));
        
        bw.Write(a4.Length);
        
        foreach var o4 in a4 do
          if o4 is Decimal then
            bw.Write(single(Decimal(o4))) else
            raise new CannotConvertException(o4.GetType,typeof(Decimal));
      end;
    end;
  end;
  
end;

procedure SaveBiases(bw: System.IO.BinaryWriter; o: System.object);
begin
  var a1 := o as ObjArr;
  if a1 = nil then raise new CannotConvertException(o.GetType,typeof(ObjArr));
  
  bw.Write(a1.Length);
  
  foreach var o1 in a1 do
    if o1 is Decimal then
      bw.Write(single(Decimal(o1))) else
      raise new CannotConvertException(o1.GetType,typeof(Decimal));
end;

begin
  
  var json := ReadAllText('weights.json');
  var jss := new JavaScriptSerializer();
  jss.MaxJsonLength := json.Length;//зачем делать integer.MaxValue, если мы уже знаем максимальную длинну
  var o := jss.DeserializeObject(json);
  var Head: Dic := o as Dic;
  if Head = nil then raise new CannotConvertException(o.GetType,typeof(Dic));
  var Res := new System.IO.BinaryWriter(System.IO.File.Create('Colorize.PABCModel'));
  
  (** )//это если вам вдруг понадобится key, а не только value
  
  foreach var kvp: KeyValuePair<string, object> in Head do
  begin
    writeln($'Saving weights of {kvp.Key}');
    SaveWeights(Res, kvp.Value);
  end;
  
  (**)
  
  foreach var value:object in Head.Values do//каждое value это не C++ подобный массив, хранящий
                                            //только ссылку на первый элемент. Оно хранит ещё и свой
                                            //размер, поэтому кроме него самого ничего передавать не надо
                                            //тем более, писать размеры от руки... Это ведь костыль!
    SaveWeights(Res,value);
  
  (**)
  
end.

Таким образом если что то идёт не так - вы получаете подробную информацию в окне ввода. (Да, исключение пишет не только в “Список ошибок”, а ещё и подробную инфу в “окно ввода”).

И я вам ещё убрал передачу размеров массивов, потому что это совершенно не необходимо и костыль.

В bw записывались данные массивов, а не длина. А так, вроде бы понял. У Вас код без ошибок работал? Я пытаюсь разобраться в коде, но не совсем его понимаю… :frowning:

Я понимаю, но как программа которая будет читать данные узнает размерность массивов?

Давайте по мелким частям, приведите пример что не понятно, или не совсем понятно.

Размерность массивов фиксирована, так как она является гиперпараметром нейросети.

В JSON лежит набор 4-х мерных массивов и 1-мерных массивов. Как сохранить содержимое каждого массива в отдельный файл.

Тогда почему вы эти размерности вводили от руки а не программно брали из этих “гиперпараметров”? Это свидетельствует от костыльном программировании, если размеры массивов хранятся в отдельном файле - загружайте сначала тот отдельный файл и берите из него программно, не руками.

Так же и с 4-х и 1-о мерными массивами, по хорошему где то должно хранится какие ключи соответствуют 4-х мерным, а какие 1-о мерным.

Ну или в крайнем случае просто смотрим на тип первого элемента каждого массива. Если это массив - значит вся структура 4-х мерный массив. Если это Decimal - то это 1-о мерный массив. Но это тоже костыль.

Размерности не лежат в отдельном файле, они прописаны в другой программе. Как реализовать сохранение то? Там Decimal.

Тогда эта другая программа должна передавать вам параметры о размерах. Значит делайте .dll, в которой функции будут принимать это параметрами.

Где там? И - и что?

Зачем? Я же говорю, нужно просто извлечь значения элементов массивов из JSON и сохранить их в бинарный файл в виде Single. Размерности мне уже и так известны.

Только сейчас заметил что там в ключах написано bias или weight после точки в каждом. В таком случае можно так:

{$Reference 'System.Web.Extensions.dll'}
uses System.Collections.Generic;
Uses System.Web.Script.Serialization;
Uses System;

type
  ObjArr = array of System.object;
  Dic = Dictionary<string, object>;
  
  CannotConvertException = class(Exception)
    
    public constructor(tf,tt:System.Type) :=
    inherited create($'Failed to convert from <{tf}> to <{tt}>');
    
  end;

procedure SaveWeights(fname:string; o: System.object);
begin
  
  var bw := new System.IO.BinaryWriter(System.IO.File.Create(fname));
  
  var a1 := o as ObjArr;
  if a1 = nil then raise new CannotConvertException(o.GetType,typeof(ObjArr));
  
  //bw.Write(a1.Length);
  
  foreach var o1 in a1 do
  begin
    var a2 := o1 as ObjArr;
    if a2 = nil then raise new CannotConvertException(o1.GetType,typeof(ObjArr));
    
    //bw.Write(a2.Length);
    
    foreach var o2 in a2 do
    begin
      var a3 := o2 as ObjArr;
      if a3 = nil then raise new CannotConvertException(o2.GetType,typeof(ObjArr));
      
      //bw.Write(a3.Length);
      
      foreach var o3 in a3 do
      begin
        var a4 := o3 as ObjArr;
        if a4 = nil then raise new CannotConvertException(o3.GetType,typeof(ObjArr));
        
        //bw.Write(a4.Length);
        
        foreach var o4 in a4 do
          if o4 is Decimal then
            bw.Write(single(Decimal(o4))) else
            raise new CannotConvertException(o4.GetType,typeof(Decimal));
      end;
    end;
  end;
  
  bw.Flush;//вообще не обязательно
  bw.Close;
  
end;

procedure SaveBiases(fname:string; o: System.object);
begin
  
  var bw := new System.IO.BinaryWriter(System.IO.File.Create(fname));
  
  var a1 := o as ObjArr;
  if a1 = nil then raise new CannotConvertException(o.GetType,typeof(ObjArr));
  
  //bw.Write(a1.Length);
  
  foreach var o1 in a1 do
    if o1 is Decimal then
      bw.Write(single(Decimal(o1))) else
      raise new CannotConvertException(o1.GetType,typeof(Decimal));
  
  bw.Flush;//вообще не обязательно
  bw.Close;
  
end;

begin
  
  var json := ReadAllText('weights.json');
  var jss := new JavaScriptSerializer();
  jss.MaxJsonLength := json.Length;//зачем делать integer.MaxValue, если мы уже знаем максимальную длинну
  var o := jss.DeserializeObject(json);
  var Head: Dic := o as Dic;
  if Head = nil then raise new CannotConvertException(o.GetType,typeof(Dic));
  var Res := new System.IO.BinaryWriter(System.IO.File.Create('Colorize.PABCModel'));
  
  foreach var kvp:KeyValuePair<string,object> in Head do
  begin
    var key := kvp.Key;
    var t := key.Skip(key.LastIndexOf('.')+1).JoinIntoString;
    case t of
      'weight':SaveWeights(key,kvp.Value);
      'bias':SaveBiases(key,kvp.Value);
      
      else raise new System.NotSupportedException($'cann''t load {t}');
    end;
  end;
  
end.

Если вам в программировании приходится вводить значения от руки - значит вы что то делаете не так))

Теперь совсем непонятно. Значения никто с клавиатуры не вводит, они ПРОПИСАНЫ В КОДЕ! Итак, имею программу, которая выводит ключи(имена массивов):

{$reference System.Web.Extensions.dll}
uses System.Collections.Generic;
Uses System.Web.Script.Serialization;
Uses System;
type
  Dic = Dictionary<string, object>;
Type
  ObjArr = array of object;
Type WType = array of array of array of System.Decimal;
Begin
var json := ReadAllText('weights.json'); //всё в виде текста
  var jss := new JavaScriptSerializer(); //штука которая сделает из текста объект
  jss.MaxJsonLength:=System.Int32.MaxValue;
  var Head: Dic := jss.DeserializeObject(json) as Dic; //получаем объект. И да, можно в 1 строчку всё написать ;)
  //Console.WriteLine(Single(Decimal(((((Head['1.weight'] as objArr)[2] as ObjArr)[0] as ObjArr)[1] as ObjArr)[8])).ToString());
  //Console.WriteLine(Decimal((Head['1.bias'] as objArr)[0]).ToString());
  Var arr:=new String[Head.Keys.Count];
  Head.Keys.CopyTo(arr, 0);
  For Var i:=0 to arr.Length-1 do
  Console.WriteLine(arr[i]);
  End.

Теперь, сохранить значения(и ничего более) каждого массива в отдельный бинарный файл…

Ну так последняя моя программа именно это и делает. Там проверяется тип является ли массив 4-х или 1-о мерным по ключу (у 4-х мерных вроде .weigth в названии, а может наоборот). И дальше их записывает каждый в отдельный файл.

Не совсем, там не только пронумеровано, но и что-то наподобие этого: 13.0.0.4.weight. К тому же она зачем то сохраняет размеры.

13.0.0.4.weight потому что оно так называется в словоре. Дайте ему своё название если хотите, оно передаётся параметром fname в функции которые считывают объект и сохраняют его в файл. Я сейчас сделал что передаёт вместо названия ключ словаря.

А размеры уже не сохраняет, я закомментировал это.

Вот проблема: Ошибка при приведении Double в Decimal. Переписал так, чтобы читал и Double и Decimal и переводил сразу в Single. Результат - bw.Write(single((o4))) - заданное приведение не является допустимым.

Попробовал запустить Вашу программу - Exception. Я уже ничего не понимаю.

Важно не то что ошибка а какая). У меня пишет:

0.CannotConvertException: Failed to convert from <System.Double> to <System.Decimal>
   в 0.Program.SaveWeights(String fname, Object o) в D:\1Cергей\Мои программы\Test\0\0.pas:строка 52
   в 0.Program.$Main() в D:\1Cергей\Мои программы\Test\0\0.pas:строка 101
   в 0.Program.Main()

То есть ошибка тут:

        foreach var o4 in a4 do
          if o4 is Decimal then
            bw.Write(single(Decimal(o4))) else
            raise new CannotConvertException(o4.GetType,typeof(Decimal));

Значит входной тип был real а не Decimal.

Вот как раз для того чтоб понимать это и нужны исключения. Попробуйте их читать))

Я посмотрел в отформатированном варианте - там большая часть значения Decimal, но вперемешку с ними встречаются Double. Значит надо оба уметь читать, это не сложно добавить.

Дальше следующая проблема, под ключом “2.weight” сохранён 1-о мерный массив:

"2.weight" : Arr : [
			0 : System.Decimal(0.36821678280830383)
			1 : System.Decimal(0.11325336992740631)
			2 : System.Decimal(0.16857537627220154)
			3 : System.Decimal(0.0031781825236976147)
			4 : System.Decimal(0.37329545617103577)
			5 : System.Decimal(0.032075829803943634)
			6 : System.Decimal(0.36210280656814575)
			7 : System.Decimal(0.23077285289764404)
			8 : System.Decimal(2.4315297603607178)
			9 : System.Decimal(0.5644348859786987)
			10 : System.Decimal(0.2736789286136627)
			11 : System.Decimal(0.5671119689941406)
			12 : System.Decimal(0.6046656966209412)
			13 : System.Decimal(0.4039381146430969)
			14 : System.Decimal(0.37746092677116394)
			15 : System.Decimal(0.0011575500248000026)
			16 : System.Decimal(0.0007204473367892206)
			17 : System.Decimal(-0.002729201689362526)
			18 : System.Decimal(0.6900720000267029)
			19 : System.Decimal(0.13734953105449677)
			20 : System.Decimal(0.10661596059799194)
			21 : System.Decimal(0.4918227195739746)
			22 : System.Decimal(2.8141560554504395)
			23 : System.Decimal(0.14301876723766327)
			24 : System.Decimal(0.0006540906615555286)
			25 : System.Decimal(0.3235042989253998)
			26 : System.Decimal(0.5100299119949341)
			27 : System.Decimal(0.2883673906326294)
			28 : System.Decimal(1.045630693435669)
			29 : System.Decimal(0.4360208809375763)
			30 : System.Decimal(0.22335734963417053)
			31 : System.Decimal(-0.4508594274520874)
		]

То есть, моя догадка что weight-ы это 4-х мерные массивы а bias-ы это одномерные была не верна…

В таком случае немного более криво получается, ну да ладно. Вот то что получилось. Вот это у меня уже без ошибок работает.

1 лайк

Удалось это добавить! :slight_smile:

Вот чуть-чуть не успел сказать, что следующий за weight/bias группой weight/bias группа - это shift и scale для instanceNormalization, то есть - одномерный массив. :wink: Спасибо! Очень помогли! :blush: