Хорошо. Видимо, действительно непонятно объяснил. По ссылке, приведённой мной несколькими сообщениями выше, можно найти реализацию нейросети (да, она запускается, обучается и работает в обычном браузере, т. к. написана на 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.
Удалось раздобыть json-версию данных нейросети для окрашивания. Как их извлечь? Notepad++ сошёл с ума от размера файла. Архив: https://yadi.sk/d/N-XjaQ3v3YhfS2 Пароль к архиву: PABCSoft_WD1
Вот я обновил JSONR.pas, теперь он может читать и такие большие документы. Ноутпед не знаю, но виндовский блокнот у меня мин повис и таки открыл отформатированный с этой прогой результат.
Но это только чтоб узнать структуру этого файла. Всё равно руками всё не перепишете))
Скорее беритесь за IDE Паскаля, все ошибки с форматированиями уйдут в следующей же сборке! Я написал программу для чтения массивов, но там какая-то непонятная ошибка. 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 записывались данные массивов, а не длина. А так, вроде бы понял. У Вас код без ошибок работал? Я пытаюсь разобраться в коде, но не совсем его понимаю…
Я понимаю, но как программа которая будет читать данные узнает размерность массивов?
Давайте по мелким частям, приведите пример что не понятно, или не совсем понятно.
Размерность массивов фиксирована, так как она является гиперпараметром нейросети.
В 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.
Если вам в программировании приходится вводить значения от руки - значит вы что то делаете не так))