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

Хорошо. Видимо, действительно непонятно объяснил. По ссылке, приведённой мной несколькими сообщениями выше, можно найти реализацию нейросети (да, она запускается, обучается и работает в обычном браузере, т. к. написана на JavaScript). Там же есть возможность сохранения всех данных полученной нейросети в JSON - код. Он содержит размеры и типы слоёв, веса и другую информацию. Ещё там есть обученная сеть, которую я и сохранил в файл вышеописанным способом в надежде использовать её в Паскале. Задача-извлечь из этого JSON массивы с весами, причём только их(веса). Массивы (а их там несколько, причём многомерных и достаточно больших) дают некислое количество чисел в строковом представлении, переводить которые в двоичный формат вручную нет никакого желания(да и возможности тоже). А посему мне нужно как-то программно пропарсить эти массивы, чтобы автоматически сохранить их в бинарный файл.

С помощью отформатированного варианта узнаём какой структуры всё это (где as Dic, где as ObjArr и т.п.). Затем получаем System.Decimal числа и простым преобразованием типов переводим их в real/single/что_вам_удобнее. Ну и сохраняете полученное как то в бинарный файл. Всё это в отдельной программе/модуле, чтоб можно было много раз использовать, если снова понадобится.

Я одного понять не могу, что из этого не само собой разумеется?

Непонятно то, что код почему-то не видит сами массивы. И то, почему именно System.Decimal. Почему не Single сразу?

А как вы представляете лучше?

Потому что json. Ведь в файле оно записано десятичной дробью, вот его и читает в соответствующий тип.

Итак, как и обещал, предлагаю реализацию свёрточной нейросети для распознавания рукописных цифр на чистом PascalABC.NET. MnistNet.zip (141,7 КБ) При реализации активно использовалась DL библиотека ConvNetCS, а точнее её исходный код при реализации самого приложения и бинарная версия при обучении модели. Репозиторий ConvNetCS на GitHub.

2 лайка

Удалось раздобыть json-версию данных нейросети для окрашивания. Как их извлечь? Notepad++ сошёл с ума от размера файла. Архив: https://yadi.sk/d/N-XjaQ3v3YhfS2 Пароль к архиву: PABCSoft_WD1

Вот я обновил JSONR.pas, теперь он может читать и такие большие документы. Ноутпед не знаю, но виндовский блокнот у меня мин повис и таки открыл отформатированный с этой прогой результат.

Но это только чтоб узнать структуру этого файла. Всё равно руками всё не перепишете))

1 лайк

Скорее беритесь за IDE Паскаля, все ошибки с форматированиями уйдут в следующей же сборке! :smile: Я написал программу для чтения массивов, но там какая-то непонятная ошибка. JSON Convert.pas (2,4 КБ) . Может и в самом JSON ошибка, но делал не я, это перевод с Torch.

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

Кстати, посмотрел на место ошибки через Вашу программу и понял, что руки у меня не из того места. Мой код выдаёт ошибку там, где её в 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.

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