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


#21

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


#22

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

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


#23

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


#24

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

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


#25

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


#26

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


#27

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

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


#28

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


#29

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


#30

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


#31

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


#32

Смотрите, когда вы применяете 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.

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

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


#33

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


#34

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

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


#35

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

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


#36

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

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

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


#37

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


#38

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

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


#39

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


#40

Только сейчас заметил что там в ключах написано 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.

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