Будь ласка, використовуйте цей ідентифікатор, щоб цитувати або посилатися на цей матеріал:
https://er.chdtu.edu.ua/handle/ChSTU/6199Повний запис метаданих
| Поле DC | Значення | Мова |
|---|---|---|
| dc.contributor.advisor | Бойко, Володимир Володимирович | - |
| dc.contributor.author | Шулаков, Володимир Володимирович | - |
| dc.date.accessioned | 2025-11-25T17:08:10Z | - |
| dc.date.available | 2025-11-25T17:08:10Z | - |
| dc.date.issued | 2023-06-14 | - |
| dc.identifier.uri | https://er.chdtu.edu.ua/handle/ChSTU/6199 | - |
| dc.description.abstract | Важливість конфіденційності інформації на сьогодення продовжує зростати, а реальні дані, особливо табличні, які можуть бути використані для прикладного моделювання інформаційних та фізичних процесів, є конфіденційними, тому їх використання стає все більш складним. Застосування систем генерації синтетичних даних (СГСД) для публікування зовсім інших за значеннями, проте точних за статистичними характеристиками даних, набуває все більшої необхідності. Метою роботи є створення програмного продукту (модуля) з відкритим кодом для генерації синтетичних даних за допомогою нейронних мереж для подальшої побудови систем вищого рівня, зокрема, ERP-систем. Об’єкт дослідження: генерація синтетичних даних. Предмет дослідження: програмний модуль для генерації синтетичних даних. Методи дослідження: теоретичний метод (аналіз літератури щодо проблеми дослідження), штучні нейронні мережі та алгоритми генерації синтетичних даних. Роботу було апробовано: на II Всеукраїнській науково-практичній конференції з міжнародною участю «Актуальні завдання медичної, біологічної фізики та інформатики» (Вінниця, 7 квітня 2023 р.); на Днях студентської науки ЧДТУ-2023 (Черкаси, 19 квітня 2023 р.), за результатами якої було отримано почесну грамоту за І місце по секції «Комп’ютерні науки та системний аналіз». | uk_UA |
| dc.language.iso | uk | uk_UA |
| dc.subject | ГЕНЕРАЦІЯ СИНТЕТИЧНИХ ДАНИХ | uk_UA |
| dc.subject | НЕЙРОННІ МЕРЕЖІ | uk_UA |
| dc.subject | СТАТИСТИКА | uk_UA |
| dc.subject | ПРОГРАМНИЙ МОДУЛЬ | uk_UA |
| dc.subject | RUST | uk_UA |
| dc.subject | KOTLIN | uk_UA |
| dc.subject | JETPACK COMPOSE DESKTOP. | uk_UA |
| dc.title | Модуль генерації даних для ERP системи з використанням нейронних мереж | uk_UA |
| dc.type | Bachelor Thesis | uk_UA |
| Розташовується у зібраннях: | 122 Комп’ютерні науки (Комп’ютерні науки та прикладне програмування) | |
Файли цього матеріалу:
| Файл | Опис | Розмір | Формат | |
|---|---|---|---|---|
| Пояснювальна робота_Шулаков Володимир_КН-1901_2022-2023.pdf Restricted Access | 7.52 MB | Adobe PDF | Переглянути/Відкрити Запит копії |
Усі матеріали в архіві електронних ресурсів захищено авторським правом, усі права збережено.
Extracted text
МІНІСТЕРСТВО ОСВІТИ І НАУКИ УКРАЇНИ
ЧЕРКАСЬКИЙ ДЕРЖАВНИЙ ТЕХНОЛОГІЧНИЙ УНІВЕРСИТЕТ
Факультет інформаційних технологій і систем
Кафедра комп’ютерних наук та системного аналізу
Пояснювальна записка
до кваліфікаційної роботи
бакалавра
(освітньо-кваліфікаційний рівень)
на тему: «Модуль генерації даних для ERP системи
з використанням нейронних мереж»
Виконав: студент 4 курсу, групи КН-1901
спеціальності 122 «Комп’ютерні науки»
(шифр і назва спеціальності)
Освітня програма «Комп’ютерні науки та
(назва освітньої програми)
прикладне програмування»
Шулаков Володимир Володимирович
Керівник Бойко В.В.
(прізвище та ініціали)
Рецензент Богатирьов О.О.
(прізвище та ініціали)
Черкаси 2023 року
Бланк завдання на кваліфікаційну роботу бакалавра студенту
Черкаський державний технологічний університет
Факультет Інформаційних технологій і систем
Кафедра Комп’ютерних наук та системного аналізу
Освітньо-кваліфікаційний рівень Бакалавр
Спеціальність 122 – комп’ютерні науки
Освітня програма Комп’ютерні науки та прикладне програмування
ЗАТВЕРДЖУЮ
Завідувач кафедри КНСА
_______________ Юрій ТРИУС
«____» _____________ 2023 р.
ЗАВДАННЯ
на кваліфікаційну роботу бакалавра студенту
Шулакову Володимиру Володимировичу
(прізвище, ім’я, по батькові)
1. Тема роботи «Модуль генерації даних для ERP системи з використанням
нейронних мереж»
Керівник роботи. Бойко В.В., к.т.н., ст. викладач
(прізвище, ім’я, по батькові, науковий ступінь, вчене звання)
затверджені наказом університету від «24» лютого 2023 р. №43/04.
2. Строк подання студентом роботи «12» червня 2023 року
3. Вихідні дані до роботи:
Матеріали II всеукраїнської науково-практичної конференції з міжнародною участю
«Актуальні завдання медичної, біологічної фізики та інформатики».
4. Зміст пояснювальної записки (перелік питань, що їх належить розробити):
Вступ
4.1. Дослідження у сфері синтетичних даних.
4.2. Проєктування модуля генерації синтетичних даних.
4.3. Розробка модуля генерації синтетичних даних.
Висновки.
5. Перелік додатків (з точним зазначенням назв додатків):
5.1. Додаток А. Специфікація 482.ЧДТУ.31904-01.
1 . Інструкція користувача
5.2. Додаток Б. Програмний код модулю генерації синтетичних даних.
5.3. Додаток В. Інструкція користувача.
5 .4. Додаток Г. Публікація результатів.
6. Консультанти розділів роботи
Розділ Прізвище, ініціали та Підпис, дата
посада
завдання видав завдання прийняв
консультанта
Розділ 3. Розробка Сіньковський А.П.,
модуля генерації асистент кафедри
синтетичних даних комп’ютерних наук та
системного аналізу
7. Дата видачі завдання 20.12.2022 р.
КАЛЕНДАРНИЙ ПЛАН
№ з/п Назва етапів кваліфікаційної роботи бакалавра Строк виконання
Примітка
етапів роботи
1 Видача завдання на кваліфікаційну роботу бакалавра. до 20.12.2022
2 Аналіз літературних джерел, об’єкту та предмету
до 14.01.2023
дослідження.
3 Написання теоретичного розділу кваліфікаційної
до 12.02.2023
роботи бакалавра.
4 Написання аналітичного розділу (аналіз об’єкту й
до 15.03.2023
предмету дослідження).
5 Написання практичних розділів й висновків по роботі. до 24.04.2023
6 Передзахист кваліфікаційної роботи бакалавра на
25.05.2023
засіданні випускової кафедри.
7 Подання роботи завідувачу кафедри. до 12.06.2023
8 Захист кваліфікаційної роботи бакалавра. 14.06.2023
Студент _____________________________ Шулаков В.В.
(підпис)
Керівник роботи ____________________________ Бойко В.В.
(підпис)
РЕФЕРАТ
Кваліфікаційна робота бакалавра містить: 64 с., 35 рис., 7 таблиць, 22
використаних джерела, 4 додатки.
Актуальність теми. Важливість конфіденційності інформації на сьогодення
продовжує зростати, а реальні дані, особливо табличні, які можуть бути використані
для прикладного моделювання інформаційних та фізичних процесів, є
конфіденційними, тому їх використання стає все більш складним. Застосування
систем генерації синтетичних даних (СГСД) для публікування зовсім інших за
значеннями, проте точних за статистичними характеристиками даних, набуває все
більшої необхідності.
Метою роботи є створення програмного продукту (модуля) з відкритим кодом
для генерації синтетичних даних за допомогою нейронних мереж для подальшої
побудови систем вищого рівня, зокрема, ERP-систем.
Завдання кваліфікаційної роботи бакалавра:
– провести огляд існуючих систем генерації синтетичних даних;
– проаналізувати архітектуру систем генерації синтетичних даних;
– проаналізувати алгоритми генерації синтетичних даних;
– розробити методику порівняння табличних даних;
– спроєктувати та реалізувати програмний модуль генерації синтетичних
даних.
Об’єкт дослідження: генерація синтетичних даних.
Предмет дослідження: програмний модуль для генерації синтетичних даних.
Методи дослідження: теоретичний метод (аналіз літератури щодо проблеми
дослідження), штучні нейронні мережі та алгоритми генерації синтетичних даних.
Апробація результатів роботи. Роботу було апробовано: на II Всеукраїнській
науково-практичній конференції з міжнародною участю «Актуальні завдання
медичної, біологічної фізики та інформатики» (Вінниця, 7 квітня 2023 р.); на Днях
студентської науки ЧДТУ-2023 (Черкаси, 19 квітня 2023 р.), за результатами якої було
отримано почесну грамоту за І місце по секції «Комп’ютерні науки та системний
аналіз».
Публікації. Результати кваліфікаційної роботи бакалавра опубліковано у:
1) збірнику тез доповідей студентської науково-практичної конференції
ЧДТУ: Шулаков В.В., Бойко В.В., Сіньковський А.П. Інформаційна технологія
генерування синтетичних медичних даних на основі нейронних мереж [Електронний
ресурс]: Збірник тез доповідей студентської науково-практичної конференції ЧДТУ:
18–20 квіт. 2023 р. / [упоряд. : Єгорова О. В., Захарова О. В., Кисельов В. Б. та ін.]; М-
во освіти і науки України, Черкас. держ. технол. ун-т. Черкаси : ЧДТУ, 2023. С. 31;
2) матеріалах II всеукраїнської науково-практичної конференції з
міжнародною участю «Актуальні завдання медичної, біологічної фізики та
інформатики»: Володимир Шулаков, Артем Сіньковський, Юрій Триус.
Інформаційна технологія генерування синтетичних медичних даних на основі
нейронних мереж [Електронний ресурс]: Актуальні завдання медичної, біологічної
фізики та інформатики : матеріали II Всеукраїнської науково-практичної конференції
з міжнародною участю (Вінниця, 7 квіт. 2023 р.) / голов. ред. Вікторія Петрушенко;
заст. голов. ред. Олег Власенко. Вінниця : Едельвейс. С. 76-82.
Ключові слова: ГЕНЕРАЦІЯ СИНТЕТИЧНИХ ДАНИХ, НЕЙРОННІ
МЕРЕЖІ, СТАТИСТИКА, ПРОГРАМНИЙ МОДУЛЬ, RUST, KOTLIN, JETPACK
COMPOSE DESKTOP.
ABSTRACT
Bachelor’s thesis contains: 64 pages, 35 figures, 7 tables, 22 sources used, 4
attachments.
Relevance of the topic. The importance of information confidentiality continues to
grow today. Real data especially tabular data that can be used for applied modeling of
information and physical processes is confidential so their use is becoming increasingly
difficult. The use of synthetic data generation systems (SDGS) for publishing data that are
completely different in meaning but accurate in statistical characteristics is becoming
increasingly necessary.
Purpose of the study. The aim of the work is to create an open-source software
framework for synthetic data generation using neural networks for the further construction
of higher-level systems such as ERP systems. As a result, writing a bachelor’s thesis
contributes to mastering the methodology of creatively solving current problems of
scientific and applied orientation in building information systems based on the acquired
knowledge, skills and abilities.
Tasks of the bachelor’s thesis:
– review existing systems for generating synthetic data;
– analyze the architecture of synthetic data generation systems;
– analyze the algorithms for generating synthetic data;
– develop a methodology for tabular data comparison;
– design and implement a program module for generating synthetic data.
The object of research: synthetic data generation.
The subject of research: software module for synthetic data generation.
Research methods: theoretical method (analysis of the literature on the research
problem), artificial neural networks and algorithms for generating synthetic data.
Approval of the results of work: the work was approved: at the II All-Ukrainian
Scientific and Practical Conference with International Participation «Actual Problems of
Medical, Biological Physics and Informatics» (Vinnytsia, April 7, 2023); at the Student
Science Days of the Cherkasy State Technical University-2023 (Cherkasy, April 19, 2023),
which resulted in a honorary certificate for the first place in the section «Computer Science
and Systems Analysis».
Publications. The results of the bachelor’s thesis were published in:
1) conference proceedings of the scientific and practical student conference of the
ChSTU: Shulakov V.V., Boyko V.V., Sinkovskyi A.P. Information technology for synthetic
medical data generating based on neural networks [Electronic resource]: conference
proceedings of the scientific-practical student conference of the ChSTU: April 18-20, 2023
/ [compiled by Egorova O.V., Zakharova O.V., Kiselev V.B., et al]: Ministry of Education
and Science of Ukraine, Cherkassy State Technology University. Cherkasy: ChSTU, 2023.
P. 31;
2) the materials of the II All-Ukrainian Scientific and Practical Conference with
international participation «Actual Problems of Medical, Biological Physics and
Informatics»: Volodymyr Shulakov, Artem Sinkovskyi, Yuriy Tryus. Information
technology for synthetic medical data generating based on neural networks [Electronic
resource]: Actual tasks of medical, biological physics and informatics: materials of the II
All-Ukrainian scientific and practical conference with international participation (Vinnytsia,
April 7, 2023) / editor-in-chief Victoria Petrushenko; deputy editor-in-chief Oleg Vlasenko.
Vinnytsia: Edelweiss. P. 76-82.
Keywords: SOFTWARE MODULE, SYNTHETIC DATA GENERATION,
NEURAL NETWORKS, STATISTICS, RUST, KOTLIN, JETPACK COMPOSE
DESKTOP.
6
ЗМІСТ
ПЕРЕЛІК УМОВНИХ ПОЗНАЧЕНЬ, СИМВОЛІВ, СКОРОЧЕНЬ І ТЕРМІНІВ ........ 8
ВСТУП .................................................................................................................................. 9
1 ДОСЛІДЖЕННЯ У СФЕРІ СИНТЕТИЧНИХ ДАНИХ .......................................... 12
1.1 Теорія синтетичних даних ................................................................................... 12
1.1.1 Поняття синтетичних даних .......................................................................... 12
1.1.2 Статистичні характеристики табличних даних ........................................... 13
1.1.3 Головні проблеми генерації синтетичних даних ........................................ 16
1.2 Існуючі системи та методи генерації синтетичних даних ................................ 17
1.2.1 Методи, засновані на баєсових мережах: CLBN, PrivBN .......................... 17
1.2.2 Нейронні мережі CTGAN, TVAE, OCTGAN ............................................... 19
1.2.3 Системи tonic.ai, mostly.ai, syntho.ai ............................................................. 21
Висновки до розділу 1 ................................................................................................... 24
2 ПРОЄКТУВАННЯ МОДУЛЯ ГЕНЕРАЦІЇ СИНТЕТИЧНИХ ДАНИХ ............... 25
2.1 Постановка задачі ................................................................................................. 25
2.2 Вимоги до прикладної програми генерації синтетичних даних ...................... 25
2.3 Архітектура нейронної мережі ............................................................................ 25
2.4 Архітектура динамічної бібліотеки .................................................................... 33
Висновки до розділу 2 ................................................................................................... 38
3 РОЗРОБКА МОДУЛЯ ГЕНЕРАЦІЇ СИНТЕТИЧНИХ ДАНИХ ............................ 39
3.1 Технології та засоби розробки ............................................................................. 39
3.2 Розробка трансформатору вхідних даних для подачі в нейронну мережу ..... 44
3.3 Навчання нейронної мережі та її використання для генерації синтетичних
даних ................................................................................................................................ 46
3.4 Створення Kotlin-біндингів до основної бібліотеки ......................................... 49
3.5 Трансформатор рядків в унікальні числа ........................................................... 51
3.6 Створення прикладної програми генерації синтетичних даних ...................... 52
Висновки до розділу 3 ................................................................................................... 59
ВИСНОВКИ ....................................................................................................................... 61
7
СПИСОК ВИКОРИСТАНИХ ДЖЕРЕЛ ......................................................................... 63
Додаток А. Специфікація 482.ЧДТУ.31914-01 .............................................................. 65
Додаток Б. Програмний код модулю генерації синтетичних даних ............................ 67
Додаток В. Інструкція користувача ................................................................................. 79
Додаток Г. Публікація результатів досліджень ............................................................. 83
8
ПЕРЕЛІК УМОВНИХ ПОЗНАЧЕНЬ, СИМВОЛІВ, СКОРОЧЕНЬ І ТЕРМІНІВ
ERP – Enterprise Resource Planning (планування ресурсів підприємства);
СГСД – система генерації синтетичних даних;
МП – мова програмування;
НМ – нейронна мережа;
ОС – операційна система;
ШНМ – штучна нейронна мережа.
9
ВСТУП
Важливість конфіденційності інформації на сьогодення продовжує зростати, а
реальні дані, особливо табличні, які можуть бути використані для прикладного
моделювання інформаційних та фізичних процесів, є конфіденційними. Тому їх
використання стає все більш складним.
Багато цікавих даних, які можуть знадобитись для прикладного моделювання
фізичних систем, не можуть бути опублікованими для широкого загалу науковців. А
це означає, що використовувати реальні дані для розробки різноманітних ІС стає все
складніше і складніше.
Наприклад, щоб створити інтелектуальну ERP-систему для оцінювання рівня
ризику банкрутства підприємства, потрібно заздалегідь мати реальні дані для
тренування штучного інтелекту. Наразі такі дані не можна безпосередньо
використовувати з розумінь конфіденційності. Для вирішення даної проблеми можна
застосувати синтетичні дані, згенеровані за прикладом реальних, та досить гарно
відповідають статистичним характеристикам реальних даних, але самі значення не
схожі на реальні, чим і досягається збереження конфіденційності оригінальних даних.
Застосування систем генерації синтетичних даних (СГСД) для публікування
зовсім інших за значеннями, проте точних за статистичними характеристиками даних,
набуває все більшої необхідності.
Система генерації синтетичних даних – це програмне забезпечення, яке створює
нові дані шляхом імітації реальних даних. Синтетичні дані можуть бути створені для
різних цілей, таких як тестування та перевірка алгоритмів машинного навчання,
побудова моделей імітації, дослідження статистичних характеристик даних та інше.
Використання систем генерації синтетичних даних дозволяє отримувати дані без
прямого використання реальних даних.
Також синтетичні дані генеруються для задоволення конкретних потреб або
певних умов, які можуть бути не знайдені в оригінальних даних. Це може бути
корисно при проєктуванні будь-якого типу системи, оскільки синтетичні дані
використовуються як моделювання або як теоретичне значення, ситуація тощо. Це
10
дозволяє врахувати несподівані результати і мати базове рішення або засіб, якщо
результати виявляться незадовільними.
Такі провідні компанії з розробки СГСД, як «tonic.ai», «almost.ai», «syntho.ai»
мають свої власні рішення. З цього виникає проблема – ці рішення не мають
відкритого коду, тож неможливо упевнитись у тому, що дані, які користувач
відправляє їм на обробку, не використовуються з недобрими, або навіть злочинними
намірами.
Наразі, йде бурне дослідження в сфері фейкових даних. У 2019 році прикладом
цього стала розробка систем CTGAN та TVAE [1]. Ці системи обігнали традиційний
підхід генерування даних без штучного інтелекту. У 2022 році було представлено
OCT-GAN – це система, заснована на нейронних диференційних рівняннях, що
обійшла за продуктивністю систему TVAE та інші, однак відрив невеликий, а
складність OCT-GAN, у порівнянні з попередніми моделями, збільшилась у рази [2].
Метою роботи є створення програмного продукту (модуля) з відкритим кодом
для генерації синтетичних даних за допомогою нейронних мереж для подальшої
побудови систем вищого рівня, зокрема, ERP-систем.
Завдання кваліфікаційної роботи бакалавра:
– провести огляд існуючих систем генерації синтетичних даних;
– проаналізувати архітектуру систем генерації синтетичних даних;
– проаналізувати алгоритми генерації синтетичних даних;
– розробити методику порівняння табличних даних;
– спроєктувати та реалізувати програмний модуль генерації синтетичних
даних.
Об’єкт дослідження: генерація синтетичних даних.
Предмет дослідження: програмний модуль для генерації синтетичних даних.
Методи дослідження: теоретичний метод (аналіз літератури щодо проблеми
дослідження), штучні нейронні мережі та алгоритми генерації синтетичних даних.
У роботі представлено алгоритм генерування синтетичних даних, що
використовує нейронну мережу типу VAE (варіативний автокодувальник). Алгоритм
обернено в динамічну бібліотеку на мові програмування (МП) Rust, до якої можна
11
звертитись з будь-якої іншої МП через C ABI (application binary interface). Також
створено програму з графічним інтерфейсом для платформ Windows, MacOS, Linux,
що дозволяє тренувати нейронну мережу та генерувати з її допомогою будь-яку
кількість синтетичних даних.
Для розробки СГСД було використано сучасні мови програмування та
технології: Rust, Kotlin, Jetpack Compose Desktop. Обрані мови програмування та
технології забезпечують продуктивність, розширюваність та перспективність
застосування розробленої СГСД.
Програмний код розробленого алгоритму генерування синтетичних даних
наведено в додатку Б. Інструкція користувача щодо використання розробленої
програми генерування синтетичних даних наведена в додатку В.
12
1 ДОСЛІДЖЕННЯ У СФЕРІ СИНТЕТИЧНИХ ДАНИХ
1.1 Теорія синтетичних даних
Синтетичні дані можуть бути створені для різних цілей, таких як тестування та
перевірка алгоритмів машинного навчання, побудова моделей імітації, дослідження
статистичних характеристик даних та інше. Використання систем генерації
синтетичних даних дозволяє отримувати дані без прямого використання реальних
даних. Проєктування СГСД вимагає знання статистики даних, а також усвідомлення
головних проблем, що виникають при розробці таких систем.
1.1.1 Поняття синтетичних даних
Синтетичні (фейкові) дані – це інформація, яка штучно згенерована, а не
отримана з реальних подій. Зазвичай, створені за допомогою алгоритмів, синтетичні
дані можуть бути використані для перевірки математичних моделей і для навчання
моделей машинного навчання. Дані, згенеровані комп’ютерною симуляцією, можна
розглядати як синтетичні дані. Це охоплює більшість застосувань фізичного
моделювання, наприклад, авіасимулятори. Вихідні дані таких систем наближені до
реальних, але повністю згенеровані алгоритмічно. Синтетичні дані використовуються
в різних галузях як фільтр для інформації, яка в іншому випадку поставила б під
загрозу конфіденційність певних аспектів даних. У багатьох додатках з високою
важливістю інформації набори даних теоретично існують, але не можуть бути
оприлюднені широкому загалу; синтетичні дані обходять проблеми
конфіденційності, які виникають при використанні реальної інформації про
споживача без дозволу або компенсації [3].
Про табличні дані можна думати як про деяку кількість випадкових величин із
різноманітними невідомими закономірностями. Зазвичай табличні дані структуровані
за назвами стовпців, де кожен стовпець має дискретні чи неперервні дані. Дискретні
дані можна уявити як категорії, наприклад, стать, що може приймати значення
«чоловіча», «жіноча», «інша». Неперервні дані – це дійсні числа, такі як вік чи дохід.
13
У разі дискретних даних замість символьних рядків можуть бути цілі числа, де 1 може
позначати першу категорію, 2 – другу, і т.д. Цей випадок треба відрізняти від
неперервних даних.
1.1.2 Статистичні характеристики табличних даних
При порівнянні реальних та фейкових даних можна виділити дві метрики: l1-
відстань та матриця кореляцій Пірсона. Перша показує наскільки розподіли близькі
один від одного. Друга допомагає визначити схожість залежностей між даними.
Мангеттенська метрика (відстань l1) – метрика, запроваджена Германом
Мінковським. За цією метрикою, відстань між двома точками дорівнює сумі модулів
різниць їх координат. Назва «мангеттенська відстань» пов’язана з вуличним
плануванням Мангеттена, де вулиці перетинаються під прямими кутами.
Метрика l1 між двома векторами p, q в n-вимірному дійсному просторі з
заданою прямокутною системою координат – сума довжин проєкцій відрізка між
точками на осі координат [4], тобто
1(, ) =∑| − |, (1.1)
=1
де , – вхідні вектори;
– розмірність вхідних векторів.
Якщо вектори , нормалізовані, можна отримати нормалізовану відстань в
інтервалі [0; 1]:
1
1(, ) = 1(, ). (1.2)
Дану метрику можна використовувати для вирахування різниці між двома
дискретними розподілами випадкових величин, де кожне число у векторах , –
кількість потраплянь у певний інтервал у дискретному розподілі. Чим 1 ближче
до 0, тим ближче один до одного дискретні розподіли.
14
Щоб розрахувати l1-відстань між двома таблицями, можна взяти середнє l1-
дистанцій для кожного стовпця:
1
= ∑1( , ), (1.3)
=1
де , – вхідні вектори з -го стовпця першої та другої таблиць відповідно.
Інша важливою метрикою, або мірою залежності одних стовпців від інших, є
коефіцієнт кореляції Пірсона (позначають «r»). У статистиці коефіцієнт кореляції
Пірсона – це показник лінійної залежності між двома змінними та , який набуває
значень від −1 до +1 включно. Він широко використовується в науці для
вимірювання ступеня лінійної залежності між двома змінними. Показник був
розроблений Карлом Пірсоном (Karl Pearson) зі схожої ідеї, представленої Френсісом
Гальтоном у 1880-х рр. Коефіцієнт кореляції Пірсона між двома змінними дорівнює
коваріації двох змінних, або сумі добутків відхилень, поділеній на добуток їх
стандартних відхилень [5]. Даний коефіцієнт розраховують за формулою:
∑=1( − ?̅?)( − ?̅?)
= , (1.4)
√∑ ( − ?̅?)2∑=1 =1( − ?̅?)
2
де , – вибірки;
?̅?, ?̅? – вибіркові середні;
– довжина векторів вибірок.
Якщо вирахувати цей коефіцієнт для кожної комбінації стовпців з певної
таблиці, вийде кореляційна матриця. Для порівняння схожості залежностей даних з
двох таблиць з різними вибірками, достатньо вирахувати кореляційну матрицю для
першої і другої таблиць та знайти середню різницю:
1
= ∑∑||1 | − |2 ||,
2 (1.5)
=1 =1
де – розмір квадратної матриці;
15
1 – коефіцієнт кореляції Пірсона між стовпцями , першої таблиці;
2 – коефіцієнт кореляції Пірсона між стовпцями , другої таблиці.
Для розрахунку загальної метрики статистичної схожості таблиць можна взяти
середнє між та :
1
= ( + ). (1.6)
2
Якщо = 0, то таблиці статистично максимально схожі, а якщо = 1, то
таблиці максимально відрізняються за статистикою. Також, якщо обернути дану
метрику (1 − ), то її можна назвати «фактором реальності» синтетичних даних при
порівнянні реальних даних (перша таблиця) та згенерованих даних (друга таблиця).
Надамо приклад (таблиця 1.3) з даних, представлених на таблицях 1.1 та 1.2.
Для спрощення розглянемо тільки дискретні дані. Самі значення представлені
дискретними (натуральними) числами.
Таблиця 1.1 – Приклад даних для подачі в систему генерації даних
Стан здоров’я Зарплатня
3 2
1 4
2 3
1 1
Таблиця 1.2 – Приклад даних для подачі в систему генерації даних
Стан здоров’я Зарплатня
2 3
1 4
3 2
Дискретний розподіл поля «стан здоров’я» таблиці 1.1: {2,1,1}. В цій таблиці є
два записи, де стан здоров’я = 1; один запис зі значенням 2; один запис зі значенням
3. Дискретний розподіл поля «зарплатня» таблиці 1.1: {1,1,1,1}. Кожне значення
повторюється один раз (це обумовлено малим розміром таблиці).
Дискретний розподіл поля «стан здоров’я» таблиці 1.2: {1,1,1}. Дискретний
розподіл поля «зарплатня» таблиці 1.2: {0,1,1,1}. Значення 1 взагалі не присутнє в
ніякому записі в цій таблиці.
16
Спочатку робиться розрахунок характеристик по полям (таблиця 1.3), потім
рахуються коефіцієнти Пірсона (таблиці 1.4, 1.5).
Таблиця 1.3 – Підрахунок l1-відстані для окремих полів таблиць
Стан здоров’я Зарплатня
|2 − 1| + |1 − 1| + |1 − 0| + |1 − 1| +
1
|1 − 1| = 1 |1 − 1| + |1 − 1| = 1
1 1/3 1/4
Таблиця 1.4 – Коефіцієнти кореляції Пірсона для таблиці 1.1
Стан здоров’я Зарплатня
Стан здоров’я 1 −0.13
Зарплатня −0.13 1
Таблиця 1.5 – Коефіцієнти кореляції Пірсона для таблиці 1.2
Стан здоров’я Зарплатня
Стан здоров’я 1 −1
Зарплатня −1 1
Наприкінці вираховується фінальне значення відмінності (таблиця 1.6), яке
дорівнює 0.375, що означає, що дані таблиці статистично схожі на (1– 0.375) ∗ 100 ≈
62,5%.
Таблиця 1.6 – Підрахунок загальних параметрів таблиць.
1 1
( + )⁄2 ≈ 0.29
3 4
||1| − |1|| + ||−0.13| − |−1|| +
( )⁄4 ≈ 0.46
||−.013| − |−1|| + ||1| − |1||
(0.29 + 0.46)⁄2 = 0.375
1.1.3 Головні проблеми генерації синтетичних даних
Генерування даних однієї змінної не представляє великої проблеми – це можна
більш-менш вирішити за допомогою простих алгоритмів. Коли таблиця вміщає
багато змінних (стовпців), простим алгоритмом не можна обійтись, оскільки між
багатьма випадковими величинами практично завжди є умовні ймовірності.
По-перше, реалістичні табличні дані можуть містити як дискретні, так і
неперервні дані. Ці два типи не можна змішувати.
17
По-друге, дані можуть мати будь-які ймовірнісні розподіли. Наприклад, деяке
значення (категорія) в певних дискретних даних може мати абсолютну більшість
(ймовірність > 95%). Генератор даних повинен добре знаходити меншість категорій
та добре генерувати записи, де ці 5% категорій будуть присутні.
Це треба мати на увазі при проєктуванні СГСД.
1.2 Існуючі системи та методи генерації синтетичних даних
На сьогоднішній день існують деякі платформи та досить багато алгоритмів для
генерації синтетичних даних. Всі вони мають переваги та недоліки. Постає питання
про існування системи генерації синтетичних даних з найкращим компромісом.
1.2.1 Методи, засновані на баєсових мережах: CLBN, PrivBN
Баєсова мережа – це ймовірнісна графова модель (різновид статистичної
моделі), яка представляє набір випадкових змінних та їхніх умовних залежностей за
допомогою орієнтованого ациклічного графу. Наприклад, баєсова мережа може
представляти ймовірнісні зв’язки між захворюваннями та симптомами. Таку мережу
можна, наприклад, використовувати для обчислення ймовірностей наявності різних
захворювань за наявних симптомів [6].
Вершини в байєсовій мережі відображають випадкові змінні в байєсовому
розумінні. Вони можуть бути спостережуваними величинами, латентними змінними,
невідомими параметрами або гіпотезами. Ребра відображають умовні залежності, тоді
як вершини, які не зʼєднані (тобто немає шляху від однієї змінної до іншої),
відображають змінні, що є умовно незалежними одна від одної. Кожну вершину
повʼязано з функцією ймовірності, яка приймає певний набір значень батьківських
вершин як вхідні дані і видає ймовірність (або розподіл ймовірностей) змінної, яку
представляє ця вершина на виході. Наприклад, якщо батьківських вершин
представляють булевих змінних, то функцію ймовірності може бути представлено
таблицею 2 записів, по одному запису для кожної з можливих комбінацій істинності
18
або хибності її батьків. Схожі ідеї можуть застосовуватися до неорієнтованих та,
можливо, циклічних графів, таких як марковські мережі.
CLBN (Chow and Liu Beyesian Network) – у теорії ймовірностей і статистиці
дерево Чоу-Лю є ефективним методом побудови апроксимації другого порядку
спільного розподілу ймовірностей, вперше описаним у статті Чоу і Лю (1968) [7].
Метою такої декомпозиції, як і у випадку з подібними баєсівськими мережами
загалом, може бути або стиснення даних, або одержання нових даних.
Метод Чоу-Лю описує спільний розподіл ймовірностей як добуток умовного та
граничного розподілів другого порядку. Алгоритм Чоу-Ліу визначає, які умовні
ймовірності слід використовувати в апроксимації добутку. Загалом, якщо немає
взаємодій третього або вищих порядків, апроксимація Чоу-Ліу є наближенням і не
може відобразити повну структуру вихідного розподілу.
PrivBN – диференціально-конфіденційний метод для публікування даних
високої розмірності. Маючи набір даних D, даний метод спочатку будує баєсівську
мережу N, яка надає стислу модель кореляцій між атрибутами в D і дозволяє
наближено розподілити дані в D за допомогою набору P низьковимірних полів D.
Після цього вводиться шум в кожне поле в P в кожну границю в P для забезпечення
диференціальної конфіденційності, а потім використовує зашумлені границі та
баєсівську мережу для побудови наближеного розподілу даних у D. Наприкінці,
даний метод вибирає кортежі з наближеного розподілу для побудови синтетичного
набору даних, а потім випускає синтезовані дані. Інтуїтивно зрозуміло, що PrivBN
обходить «прокляття розмірності», оскільки вносить шум у низькорозмірні поля P
замість високорозмірного набору даних D. Побудова конфіденційних баєсівських
мереж виявилася складним завданням, тому розробники представили новий підхід,
який використовує сурогатну функцію для взаємної інформації для більш точної
побудови моделі. PrivBN значно перевершує попередні рішення з точки зору
точності.
Дані методи показали себе краще за попередні, втім вони використовують
дискретизацію вхідних даних, чим псується якість вихідного результату.
19
1.2.2 Нейронні мережі CTGAN, TVAE, OCTGAN
Нещодавні досягнення в галузі глибоких генеративних моделей відкрили
широкий спектр можливостей. Новітні моделі можуть вивчати розподіл ймовірностей
зображень та тексту і створювати високоякісні, реалістичні оригінальні результати.
Цей потенціал прискорив розвиток генеративних змагальних мереж.
Генеративні змагальні мережі (Generative adversarial networks, GANs) – це клас
алгоритмів штучного інтелекту, що використовуються в навчанні без учителя,
реалізовані системою двох штучних нейронних мереж, які змагаються одна з одною
в рамках гри з нульовою сумою. Вони були запроваджені Яном Ґудфелоу в 2014 році.
Ця методика дозволяє створювати фотографії та тексти, які для побіжного огляду
людиною виглядають як справжні та мають багато реалістичних елементів [9].
CTGAN (Conditional Tabular Generative Adversarial Network) – це метод для
моделювання розподілу табличних даних на основі GAN і регерування рядів з цього
розподілу [1]. У CTGAN застосовано модально-специфічну нормалізацію для
подолання негаусівського та мультимодального розподілу. Даний метод
використовує генератор умовних значень і навчання за допомогою вибірки для
роботи з незбалансованими дискретними стовпчиками. Використовуються повністю
зв’язні мережі та кілька новітніх методів для навчання високоякісної моделі. При
тренуванні вхідні неперервні дані спочатку декомпозуються за допомогою
варіативних гаусівських сумішей щоб розділити дані на режими (модалі) та потім
нормалізувати їх за середнім та середньоквадратичним значеннями для найкращого
сприйняття даних нейронними мережами. У CTGAN застосовано умовний
(conditional) генератор, щоб перевершити ліміт мереж GAN – погане вивчення даних,
статистичні характеристики яких представлені меншістю. Умовний генератор
створює умовні вектори, які представляються залежності між дискретними даними.
Вихідні дані, отримані з умовного генератору, мають бути оцінені за допомогою
«критика» – нейронної мережі, яка оцінює відстань між вивченим умовним
розподілом та умовним розподілом на реальних даних. Вибірка реальних навчальних
даних та побудова умовного вектора мають відповідати однаковим вимогам, щоб
20
допомогти «критику» оцінити відстань. Правильна вибірка умовного вектора і
навчальних даних допомагає моделі рівномірно дослідити всі можливі значення в
дискретних стовпцях.
TVAE (Tabular Variational Autoencoder) – ще одна генеративна нейромережева
модель. Дана модель простіша та елегантніша за CTGAN, а також має конкурентну
продуктивність.
У машинному навчанні варіаційний автокодувальник – це архітектура штучної
нейронної мережі, запроваджена Дідеріком Кінгмою та Максом Веллінгом, що
належить до сімейств ймовірнісних графових моделей та варіаційних баєсових
методів. Варіаційні автокодувальники призначено для стискання вхідної інформації
до обмеженого багатовимірного латентного розподілу, щоби відбудовувати її якомога
точніше (декодування) [10]. Незважаючи на те, що спочатку цей вид моделі був
розроблений для самонавчання, його ефективність була доведена і в інших галузях
машинного навчання, таких як напівавтоматичне та кероване навчання..
Зазвичай такі моделі навчаються за допомогою метаалгоритму очікування-
максимізації. Така схема оптимізує нижню межу вірогідності даних, яка зазвичай є
важкодоступною, і при цьому вимагає виявлення q-розподілів або варіаційних
апостеріорів. Ці q-розподіли зазвичай параметризуються для кожної окремої точки
даних в окремому процесі оптимізації. Однак варіаційні автокодери використовують
нейронну мережу як амортизований підхід для спільної оптимізації між точками
даних. Ця нейронна мережа приймає на вході самі точки даних, а на виході –
параметри варіаційного розподілу. Оскільки вона стискає відомий вхідний простір у
низьковимірний латентний простір, її називають кодувальником. Декодер – це друга
нейронна мережа цієї моделі. Це функція, яка трансформує латентний простір у
вхідний простір, наприклад, як засіб розподілу шуму.
Нейронні моделі CTGAN та TVAE за продуктивністю значно перевищують
баєсієвські мережі по типу CLBN та PrivBN.
OCTGAN – схожий на CTGAN метод, що покращує попередні алгоритми
шляхом створення генератору і дискримінатору на основі нейронних звичайних
диференціальних рівнянь (НОДР). Дискримінатор на основі НОДР виконує
21
класифікацію на основі траєкторії еволюції прихованого вектора, а не замість того,
щоб класифікувати за допомогою прихованого вектора лише на останньому шарі
мережі. Генератор також використовує шар звичайних диференціальних рівнянь на
самому початку своєї архітектури для перетворення початкового вхідного вектора
(тобто, конкатенації шумового вектору і вектору стану) в інший прихований
векторний простір, придатний для процесу генерації [2].
Порівняння моделей на базі штучного інтелекту представлено в таблиці 1.7.
Таблиця 1.7 – Порівняння моделей генерування даних на базі НМ
Параметр \ Мережа CTGAN TVAE OCTGAN
Складність середня відносно низька висока
Швидкість тренування задовільна задовільна низька
Якість генерації висока дуже висока найвища
1.2.3 Системи tonic.ai, mostly.ai, syntho.ai
Tonic.ai – це платформа SaaS, яка забезпечує зручний доступ до даних а
управління ними та дозволяє будь-кому створювати безпечні версії своїх
найважливіших наборів даних. Tonic поєднує статистичне моделювання даних та
перетворення даних самообслуговування, щоб дозволити всім, від фахівців з безпеки
до інженерів-програмістів, створювати сумісні портативні набори синтетичних даних
[11]. Головну сторінку сайту Tonic.ai представлено на рис. 1.1.
Рисунок 1.1 – Головна сторінка сайту Tonic.ai
22
Дана система підтримує підключення баз даних PostgreSQL та MySQL, а також
генерування даних через API.
Tonic надає різні ліцензійні плани для організацій різного розміру, які мають
більш-менш складні архітектури даних. Безкоштовної ліцензії немає.
Mostly.ai – аналогічна до Tonic.ai платформа SaaS, яка дозволяє генерувати
синтетичні дані безкоштовно до 100 тисяч рядків БД за день.
Компанія Mostly.ai активно досліджує усі сучасні методи генерування
синтетичних даних та розробляє власну унікальну комбінацію методів, щоб
забезпечити найкращі результати для клієнтів з точки зору точності,
конфіденційності та гнучкості. Їхній підхід продовжує випереджати інші рішення з
великим відривом, і вони не зупиняються на досягнутому, а продовжують активно
просувати платформу з командою експертів зі штучного інтелекту світового класу
[12].
Компанія використовує безпечне хмарне середовище AWS. Не зберігає жодні
завантажені дані. Після завершення завдання вихідний набір даних видаляється.
Згенеровані синтетичні дані поставляються з автоматично сформованим звітом про
конфіденційність і точність QA. Якщо синтетичні дані проходять перевірку
конфіденційності та точності, то можна безпечно використовувати їх без будь-яких
проблем із конфіденційністю. Головну сторінку сайту Mostly.ai представлено на
рис. 1.2.
Рисунок 1.2 – Головна сторінка сайту mostly.ai
23
Syntho.ai – організація, що займається технологіями обробки даних, що має
значний досвід у галузі синтетичних даних, створених штучним інтелектом, зі штаб-
квартирою в Амстердамі, Нідерланди. Була заснована у 2020 році з метою вирішення
глобальної дилеми конфіденційності та забезпечення економіки відкритих даних, де
дані можуть вільно використовуватися та поширюватися, а конфіденційність
гарантована. Пропонує генерацію синтетичних даних, що зберігають
конфіденційність, щоб розблокувати дані клієнтів та усунути законні проблеми
конфіденційності [13].
Можна легко підключитись до всіх найпопулярніших баз даних або легко
інтегрувати з існуючими інструментами автоматизації тестування та CI/CD. Syntho.ai
автоматично визначає правильні параметри для роботи з синтетичними даними,
зокрема типи даних, ідентифікаційні дані та зв’язки між таблицями. Можна додатково
оптимізувати ці налаштування відповідно до випадку використання. Після
завершення роботи можна проаналізувати конфіденційність та корисність
синтетичних даних у звіті про якість. Головну сторінку сайту Syntho.ai представлено
на рис. 1.3.
Рисунок 1.3 – Головна сторінка сайту syntho.ai
24
Платформи, описані вище, мають один великий недолік – вони не є open-source
проєктами, а, як відомо, в сучасному світі конфіденційність базується на прозорості
систем. Так, наприклад, у ОС Linux саме ядро та переважна більшість дистрибутивів
є open-source. Дана ОС вважаються найзахищенішою системою, тому що всі
вразливості швидко помічаються та виправляються різноманітними організаціями.
Попередньо представлені платформи заявляють на словах, що дані видаляються
після використання – це всього лиш обіцянка, яку дуже складно перевірити.
Враховуючи сказане, постає завдання створення реального open-source проєкту
генерації синтетичних даних саме з використанням нейронних мереж, адже на
практиці було досліджено, що традиційні методи генерують гірші дані.
Варто примітити, що вже існує сучасна open-source бібліотека «SDV» для
Python, яка вирішує задачу генерації даних з неймовірною кінцевою якістю з
використанням найсучаснішого алгоритму CTGAN. Але на то вона і бібліотека, що
треба використовувати програмний інтерфейс мови Python, щоб щось згенерувати, і
притому цей інтерфейс є неефективним, адже Python – інтерпретована мова. Інший
недолік – ліцензія цієї бібліотека, яка забороняє використання коду в комерційних
цілях для створення свого сервісу генерації.
Висновки до розділу 1
В цьому розділі розглянуто статистичні характеристики оцінки табличних
даних, а саме l1-відстань та коефіцієнт кореляції Пірсона, а також різноманітні методи
генерування синтетичних даних: CLBN, PrivBN, CTGAN, TVAE, OCTGAN.
Можна сказати, що мережа TVAE є найкращою за компромісом переваг та
недоліків. Вона елегантніша за CTGAN та більш підходить для вивчення розподілу
даних. Модель OCTGAN є найскладнішою, і тим більше приріст у продуктивності не
дуже великий (максимально граничний), а обчислювальна складність велика. Варто
зазначити, що якість згенерованих даних з TVAE вже досягає неймовірних
результатів, тому немає смислу гнатися за найкращою мережею. Виходить, є сенс у
розробці програмного модуля з власною реалізацією алгоритму TVAE.
25
2 ПРОЄКТУВАННЯ МОДУЛЯ ГЕНЕРАЦІЇ СИНТЕТИЧНИХ ДАНИХ
2.1 Постановка задачі
Для досягнення поставленої у роботі мети, потрібно вирішити такі задачі:
− Спроєктувати і розробити програмний модуль (бібліотеку), яка надає
можливість генерувати синтетичні дані за допомогою будь-якої мови програмування.
Інтерфейс передавання даних та саме опрацювання цих даних повинні бути
максимально ефективними. Програмний код цієї бібліотеки повинен стати відкритим.
− Розробити бібліотеку-біндинги до основної бібліотеки для користувачів, які
не бажають самостійно підключати низькорівневі бібліотеки до високорівневих МП.
− Для демонстрації роботи бібліотеки генерування синтетичних даних
створити прикладну програму з графічним інтерфейсом.
2.2 Вимоги до прикладної програми генерації синтетичних даних
Для демонстрації потенціалу розроблювальної бібліотеки потрібно розробити
прикладну програму, основне завдання якої – генерування нових даних при
завантаженні CSV-файлу вже існуючих (реальних) даних. Прикладна програма має
відповідати таким вимогам:
– робота на більшості сучасних ОС, таких як MacOS, Windows, Linux;
– можливість генерувати синтетичні дані в майбутньому без використання
реальних даних при кожному запуску програми;
– збереження згенерованих даних у CSV-файл.
2.3 Архітектура нейронної мережі
Запропонована раніше архітектура мережі TVAE базована, як зрозуміло з назви,
на VAE, де VAE – це тип нейронної мережі.
Різниця між TVAE і VAE в тому, що перша адаптована під табличні дані.
Мережа VAE (variational autoencoder) складається з кодера та декодера (рис. 2.1).
26
Кодер отримує вхідні дані і відображає їх у латентне представлення простору, яке є
простором нижчої розмірності, що представляє вхідні дані більш компактно і
змістовно. Декодер приймає це латентне представлення і транслює його назад у
первинний вхідний простір. У VAE цінова функція мінімізує помилку реконструкції
між початковим входом і виходом декодера, а також забезпечує відповідність
розподілу латентних змінних попередньо визначеному розподілу, як правило,
гаусівському розподілу. Це досягається додаванням розбіжності Кульбака-Лейблера
до цінової функції, що сприяє тому, щоб вивчений латентний розподіл відповідав
попередньо визначеному розподілу.
Рисунок 2.1 – Архітектура мережі VAE
Основною перевагою використання мережі VAE є те, що вона дозволяє
генерувати нові вибірки даних з вивченого представлення латентного простору.
Шляхом вибірки з латентного простору і подачі її через декодер можна генерувати
нові вихідні дані, подібні до оригінальних даних. Це робить VAE корисним для таких
завдань, як генерація зображень, виявлення аномалій і стиснення даних.
27
Розбіжність Кульбака-Лейблера (КЛ-розбіжність) є мірою того, наскільки один
розподіл ймовірності відрізняється від іншого, еталонного розподілу ймовірності. В
простому випадку нульова розбіжність Кульбака-Лейблера показує, що два
розглядані розподіли є ідентичними. Простими словами, дана розбіжність є мірою
несподіваності [10]. Для дискретних розподілів ймовірності, визначених на одному й
тому ж імовірнісному просторі, розбіжність Кульбака-Лейблера дорівнює:
()
(||) = ∑() log ,
() (2.1)
∈
де (), () – дискретні ймовірнісні розподіли, означені в просторі
елементарних подій .
Як зазначено раніше, у варіаційному автокодері цінова функція складається з
двох частин: ціни на відновлення та ціни на регуляризацію.
Ціна на відновлення використовуються для вимірювання різниці між вхідними
даними та вихідними даними, згенерованими декодером. У VAE декодер генерує дані
з розподілу ймовірностей, а не детермінований вихід. Тому ціна при реконструкції
зазвичай визначається як логарифмічна ймовірність вхідних даних з урахуванням
вихідного розподілу декодера. Зазвичай для цього використовують функцію ELBO
(evidence lower bound). ELBO надає нижню межу для логарифмічної правдоподібності
спостережуваних даних при істинному апостеріорному розподілі. ELBO визначається
так:
= E[log p(|)] − (q(|)|p()), (2.2)
де E[log p(|)] – ціна відновлення, що вимірює різницю між входом і виходом
нейронної мережі декодера у VAE; E – математичне очікування;
p() – розподіл латентного простору;
(q(|)|p()) заохочує наближення апостеріорного розподілу q(|) до
попереднього розподілу () над латентними змінними.
28
Ціна регуляризації використовуються для застосування попереднього
розподілу до латентних змінних, які вивчив кодер. У VAE кодер вчиться відображати
вхідні дані у прихований розподіл, який, як передбачається, слідує відомому
попередньому розподілу, наприклад, розподілу Гауса. Ціну регуляризації зазвичай
визначають як розбіжність Кульбака-Лейблера між вивченим латентним розподілом
і попереднім розподілом.
Загальна функція втрат у ШНМ є сумою цих двох втрат, зважених на
гіперпараметри, які визначають відносну важливість кожного члена. Метою навчання
ШНМ є мінімізація цієї функції втрат по відношенню до параметрів моделі за
допомогою стохастичного градієнтного спуску або відповідного алгоритму
оптимізації.
Виходить, що в архітектурі VAE кодер моделює q(|), а декодер p(|).
При проєктуванні кодувальника, автори TVAE використали наступну
архітектуру декодера [1]:
ℎ1 = ReLU(FC128→128()) ,
ℎ2 = ReLU(FC128→128(ℎ1)),
?̅? = tanh (FC128→1(ℎ2)),
?̂? ~ (?̅? , ), (2.3)
?̂? ~ softmax (FC128→m (ℎ2)) ,i
?̂? ~ softmax (FC128→|D |(ℎ2)) ,
{ p(|) = ∏=1 Ρ(?̂? = )∏
=1Ρ(?̂? = )∏=1Ρ(?̂? = ) ,
де FC→ – лінійний шар нейронної мережі, який приймає дані з X входів та
виводить дані в Y виходів (нейронів);
ReLU та softmax – активаційні функції нейронів;
– латентний простір;
(μ, σ) – розподіл гауса з математичним очікуванням μ та стандартним
відхиленням σ;
– навчальний параметр мережі;
29
– кількість модальностей (режимів) у неперервних даних;
|| – кількість категорій в дискретних даних;
– представлення одного табличного рядку (запису);
p(|) – змішаний розподіл вхідних даних ?̂? , ?̂? , ?̂?, які отримано з
оригінальних вхідних даних якщо дано латентні дані .
Архітектура кодера [1] дещо простіша:
ℎ1 = ReLU(FC|r |→128()) ,j
ℎ2 = ReLU(FC128→128(ℎ1)),
= FC128→128(ℎ2), (2.4)
1
= exp( 128→128(ℎ2)) ,2
{ q(|) ~ (, ),
де q(|) – умовний гаусовський розподіл латентного простору, якщо дано
вхідні (оригінальні) дані .
В оригінальному розподілі даних може бути декілька модальностей, що
називають «мультимодальністю розподілу». Це властивість розподілу ймовірностей
мати кілька мод, або піків, у своїй функції щільності (рис. 2.2). Іншими словами,
мультимодальний розподіл має більше одного значення, яке зустрічається з
найбільшою частотою.
Розподіл може бути унімодальним, бімодальним, тримодальним або мати ще
більше мод. Наприклад, розподіл, який представляє зріст групи людей, може бути
бімодальним, якщо є дві окремі групи людей, наприклад, дорослі та діти, кожна з яких
має свій пік у розподілі.
30
Рисунок 2.2 – Приклад мультимодального розподілу
Мультимодальні розподіли можуть виникати різними способами. Вони можуть
бути результатом поєднання різних груп населення або процесів, а можуть бути
наслідком наявності кількох основних факторів, які впливають на розподіл змінної.
Мультимодальні розподіли також часто спостерігаються в складних системах, таких
як розподіл рівнів експресії генів у біологічних системах.
При створенні архітектури нейронних мереж використовуються так звані
«функції активації». Це математичні функції, які використовуються в штучних
нейронних мережах для введення нелінійності у вихід нейрона або шару нейронів.
Вихід функції активації зазвичай використовується як вхід для наступного шару
нейронів у мережі.
Функція активації застосовується до зваженої суми входів нейрона, відомої як
потенціал активації. Функція активації вносить нелінійність, перетворюючи
потенціал активації в нове вихідне значення, як правило, в обмеженому діапазоні. Це
дозволяє нейронній мережі вивчати більш складні та нелінійні зв’язки між входами
та виходами.
До найбільш поширених функцій активації в нейронних мережах, як правило,
відносять такі:
− Sigmoid: ця функція виробляє сигмоїдальний, або S-подібний, вихід, який
знаходиться в діапазоні від 0 до 1. Використовується в задачах бінарної класифікації
та як плавне наближення до порогових функцій;
31
− ReLU (випрямлена лінійна одиниця): Ця функція встановлює вихід на нуль
для всіх від’ємних входів і залишає вихід незмінним для всіх додатних входів.
Зазвичай використовується в глибоких нейронних мережах завдяки своїй простоті та
ефективності для покращення продуктивності навчання;
− Tanh: генерує значення гіперболічного тангенсу вихідних даних в діапазоні
від -1 до 1. Вона схожа на сигмоїдну функцію, але має більш крутий градієнт, що
може зробити її більш ефективною в деяких випадках;
− Softmax: використовується у вихідному шарі нейронної мережі для створення
розподілу ймовірностей за кількома класами. Вона відображає вихід попереднього
шару в розподіл ймовірностей, який дорівнює 1.
В архітектурі TVAE застосовано модально-специфічну нормалізацію –
неперервні дані розбиваються на гаусовські модалі й відповідні неперервні значення
нормалізуються за середнім та середньоквадратичним значеннями кожної модалі.
Потім, при тренуванні, разом з неперервним (дійсним) числом з відповідного
стовпчика таблиці подається номер модалі. Таким чином нейронна мережа швидше
вчить неординарні ймовірнісні розподіли. Для реалізації такої нормалізації потрібно
застосовувати варіативні гаусовські суміші – досить комплексні та дорогі за
продуктивністю алгоритми.
Для спрощення моделі TVAE було вирішено замінити модально-специфічну
нормалізацію простою min-max нормалізацію, яка нормалізує дані за мінімальним та
максимальним значеннями з вхідної вибірки даних.
Тож, фінальна архітектура декодера буде такою:
ℎ1 = ReLU(FC128→128()) ,
ℎ2 = ReLU(FC128→128(ℎ1)),
?̂? ~ tanh(FC128→1(ℎ2)), (2.5)
?̂? ~ softmax (FC128→|D (ℎ
| 2)) ,
p( | ) = ∏
{ =1 Ρ(?̂? = )∏=1Ρ(?̂? = ) .
32
Постає питання трансформації дискретних та неперервних даних для подання в
нейронну мережу. Неперервні дані можна подавати як нормалізовані числа в
інтервалі [0; 1], а от дискретні (цілі числа) так представляти не можна, бо навчання
буде дуже складним, оскільки НМ сприймають добре тільки дійсні числа.
Нормалізація – це масштабування даних до нульового середнього значення та
одиничної дисперсії, часто використовуються в нейронних мережах, оскільки це
може покращити ефективність навчання та збільшити швидкість збіжності алгоритму
оптимізації. Є кілька причин, чому нормалізація даних корисна для нейронних мереж:
– допомагає уникнути числової нестабільності (це пов’язано з тим, що ваги в
нейронній мережі можуть стати дуже великими або дуже малими, що ускладнює
збіжність алгоритму оптимізації);
– зменшення впливу викидів і більш стійке тренування моделі до пропусків у
вхідних даних;
– допомагає покращити умови задачі оптимізації, зменшивши кількість умов у
вхідних даних (це може допомогти покращити продуктивність алгоритму оптимізації
та зменшити ризик надмірного перенавчання);
– може полегшити порівняння відносної важливості різних функцій: коли
вхідні дані не нормалізовані, ознаки з більшими значеннями можуть вважатися
нейронною мережею більш важливими, навіть якщо вони насправді не є більш
важливими для поставленої задачі.
Для того, щоб НМ добре сприймали дискретні числа, потрібно їх
трансформувати в унітарний код («гарячі» вектори).
«Гарячі» вектори (hot vectors), також відомі як однократне (one-hot) кодування,
є загальновживаним способом представлення категоріальних даних у машинному
навчанні. Вони добре підходять для категоріальних даних, оскільки забезпечують
простий і ефективний спосіб представлення категоріальних змінних у вигляді
числових значень, які можна використовувати як вхідні дані для алгоритмів
машинного навчання. Унітарні коди представляють категоріальну змінну у вигляді
двійкового вектора, де лише один елемент має значення 1, а всі інші – 0. Індекс
елемента, який має значення 1, представляє категорію, до якої належить змінна.
33
Наприклад, якщо в даних є 3 категорії, то їх можна позначити так: 001, 010, 100.
Виходить, що для вводу цих даних в НМ потрібно 3 нейрони, кожен з яких може
приймати значення 0 чи 1.
Якщо в даних є символьні рядки, то їх можна категоризувати шляхом
трансформування в цілі числа, наприклад «male» = 1, «female» = 2, а ці числа потім
будуть трансформовані в hot-вектори.
2.4 Архітектура динамічної бібліотеки
Перед розробкою програмного модулю визначимо вимоги до основної
бібліотеки:
– можливість підключення до більшості мов програмування;
– окремі функції для тренування та вибірки даних з НМ;
– серіалізація: збереження вагів та всіх необхідних даних в портабельний
формат для подальшого завантаження з серіалізованих даних та їх використання;
– можливість припинення тренування НМ у будь-який момент часу зі
збереженням результатів.
Один з найбільш потужних, якщо не єдиний, серед методів комунікації між
двома мовами програмування – C ABI. Двійковий інтерфейс прикладних програм C
(ABI) визначає, як компілятори C генерують об’єктний код, як компонувальники
об’єднують ці об’єкти у виконуваний код і як цей код взаємодіє з іншим кодом,
включаючи бібліотеки часу виконання та інтерфейси операційної системи. Іншими
словами, C ABI – це набір домовленостей про те, як функції та структури даних
представлені в машинному коді і як вони передаються між різними частинами
програми або різними програмами. C ABI визначає такі деталі:
– розмір, розташування та вирівнювання основних типів, таких як цілі числа,
числа з плаваючою комою та вказівники;
– угоди про виклик функцій, які визначають, як аргументи функції
передаються та повертаються між тим, хто її викликає, та тим, хто її отримує;
34
– угоди про імена, що використовуються для функцій, змінних та інших
символів;
– розміщення та пакування структур, що використовуються для структур,
об’єднань та інших складених типів;
– формат об’єктних файлів, включаючи формат таблиць символів, інформації
про переміщення та налагодження.
Завдяки визначенню стандартного ABI компілятори та C-бібліотеки можуть
безперешкодно працювати разом, дозволяючи коду, написаному різними мовами,
взаємодіяти один з одним. Це також забезпечує бінарну сумісність між різними
версіями бібліотеки, якщо вони відповідають тому самому ABI. C ABI важливий для
створення бібліотек і спільних об’єктів, які можуть бути використані кількома
програмами без необхідності перекомпілювати їх для кожної окремої програми. Він
також дозволяє взаємодіяти між програмами, написаними різними мовами, якщо ці
мови дотримуються одного і того ж ABI. Загалом, C ABI є важливим компонентом
мови програмування C, і він має вирішальне значення для забезпечення правильної
компіляції, компонування та виконання коду C на різних системах і платформах.
Багато сучасних мов, таких як C++, Rust та Swift, підтримують C ABI або
безпосередньо, або за допомогою спеціальних мовних конструкцій чи бібліотек. Це
дозволяє програмам, написаним цими мовами, викликати функції, визначені в
бібліотеках C, або виводити функції в код C для використання в інших програмах.
Для використання C ABI в таких мовах, як Python, Java та JavaScript, дещо
специфічне. Вони мають власні інтерфейси та угоди про виклик, які є специфічними
для відповідних середовищ виконання. У цих випадках застосовуються специфічні
для мови інструменти або бібліотеки, які надають механізми взаємодії, такі як JNI
(Java Native Interface) для Java або CPython’s C API для Python.
Об’єктний файл – це скомпільований файл, який містить машинний код, дані та
іншу інформацію, згенеровану компілятором або асемблером з вихідного або
асемблерного коду.
Є два види бібліотек з підтримкою C ABI: статичні та динамічні. Це два
поширені способи пакування та розповсюдження програмного коду.
35
Статична бібліотека – це набір об’єктних файлів, скомпільованих і
скомпонованих в один файл. Коли відбувається зв’язування програми зі статичною
бібліотекою, код з бібліотеки копіюється у виконуваний файл програми. Це означає,
що програма має весь код, необхідний для запуску, і вона не покладається на зовнішні
бібліотеки під час виконання. Статичні бібліотеки зазвичай швидше завантажуються
і виконуються, ніж динамічні, оскільки немає накладних витрат, пов’язаних з
пошуком і завантаженням коду зовнішніх бібліотек під час виконання програми.
Однак це також означає, що розмір виконуваного файлу може бути більшим, оскільки
він містить весь код бібліотеки, навіть якщо програма використовує лише невелику
його частину. Такі бібліотеки зазвичай використовуються, коли треба розповсюдити
один самостійний виконуваний файл, який можна запустити на будь-якій системі, не
вимагаючи зовнішніх залежностей.
Динамічна бібліотека – набір об’єктних файлів, які компілюються і
компонуються окремо від основної програми і завантажуються під час виконання,
коли вони потрібні програмі. Після зв’язування програми з динамічною бібліотекою,
програма містить лише посилання на бібліотеку. Це означає, що код бібліотеки
завантажується в пам’ять тільки тоді, коли він потрібен, і кілька програм можуть
спільно використовувати одну і ту ж копію бібліотеки в пам’яті. Динамічні бібліотеки
зазвичай менші за розміром, ніж статичні, оскільки їх використовують декілька
програм. Динамічні бібліотеки зазвичай використовуються, коли потрібно
розповсюджувати код, який можна використовувати у декількох програмах, або коли
треба зменшити розмір виконуваного файлу програми. Динамічні бібліотеки також
можна оновлювати незалежно від основної програми, що полегшує виправлення
помилок і додавання нових функцій без необхідності перекомпіляції всієї програми.
Варто зауважити, що такі мови, як Java, використовують віртуальну машину
для конвертації байт-коду в машинний код, тому не представляється можливим
під’єднувати до них статичні бібліотеки.
Серіалізація – це процес перетворення складної структури даних або об’єкта у
формат, який можна зберігати, передавати або використовувати в різних додатках,
платформах або системах. Серіалізований формат даних, як правило, являє собою
36
послідовність байтів або тексту, які можна легко проаналізувати і відновити у
вихідний об’єкт або структуру даних. Серіалізовані дані можна зберігати у файлах
або базах даних, їх можна отримати та відновити пізніше. Також можна надсилати
між різними процесами або системами як спосіб передавання повідомлень або обміну
даними.
Процес серіалізації зазвичай передбачає розбиття даних або об’єкта на ряд
примітивів, таких як байти або символи, а потім запис цих примітивів у файл,
мережевий потік або інший механізм зберігання.
Десеріалізація – це зворотний процес отримання серіалізованих даних і
відновлення початкової структури даних або об’єкта. Серіалізація може бути
виконана з використанням різних форматів, включаючи двійковий, XML, JSON та
інші. Формат, що використовується, залежить від вимог програми, системи або
платформи, що використовується, та інших факторів, таких як продуктивність, розмір
і безпека.
Для задоволення поставлених вимог, спроєктовано макети відповідних
функцій.
synth_net_fit – створює нову мережу і тренує її.
Вхідні дані:
– вхідні табличні дані;
– параметри тренування.
Вихідні дані: об’єкт нейронної мережі.
synth_net_sample – генерує нові дані з нейронної мережі.
Вхідні дані:
– об’єкт нейронної мережі;
– кількість табличних записів для генерування.
Вихідні дані:
– масив згенерованих табличних записів;
– фактор якості даних (відповідність статистичної характеристики даних),
вирахувану за формулою (1.6).
37
synth_net_create_snapshot – створює «знімок» нейронної мережі в
портабельному форматі даних.
Вхідні дані:
– об’єкт нейронної мережі.
Вихідні дані:
– об’єкт знімку НМ у вигляді масиву байтів чи символів.
synth_net_snapshot_destroy – вивільняє пам’ять знімку НМ.
Вхідні дані:
– об’єкт знімку нейронної мережі.
synth_net_create_from_snapshot – створює об’єкт нейронної мережі зі «знімку»,
де вже присутні натреновані ваги НМ.
Вхідні дані:
– об’єкт знімку НМ.
Вихідні дані:
– об’єкт нейронної мережі.
synth_net_destroy – вивільняє пам’ять об’єкту НМ.
Вхідні дані:
– об’єкт нейронної мережі.
Через те, що МП Kotlin в останні роки набирає оберти, потрібно зробити
додаткову бібліотеку-біндингів до C ABI. Дана бібліотека допоможе спростити
використання основного модулю у високорівневих програмах. Є один нюанс –
програми на мові Kotlin в основному працюють на JVM, і, як описано вище, JVM не
підтримує підключення бібліотек через C ABI, тому потрібно використати ще один
програмний інтерфейс, а саме JNA.
JNA (Java Native Access) – це бібліотека Java, яка дозволяє коду на Java
викликати та взаємодіяти з нативними бібліотеками та функціями, написаними
іншими мовами програмування через C ABI. Через те, що Java-бібліотеки працюють
на JVM, JNA можна використовувати і в мові Kotlin. JNA надає Java-інтерфейс, який
дозволяє розробникам визначати структуру та поведінку нативних бібліотек у Java-
коді, включаючи типи даних, сигнатури функцій та обробку помилок. Дана бібліотека
38
використовує рефлексію під час виконання для динамічної генерації коду, який
звертається до нативної бібліотеки, уникаючи необхідності для розробників писати
складний код JNI (Java Native Interface). Деякі з переваг використання JNA:
– незалежність від платформи, що дозволяє розробникам писати код, який
може працювати на різних платформах, не турбуючись про специфічні для кожної
платформи деталі;
– динамічне зв’язування нативних бібліотек, що означає, що зміни у нативному
коді можуть бути внесені без необхідності внесення змін до коду Java;
– відображення типів: автоматичне зіставлення типів даних Java з нативними
типами даних, зменшуючи складність виклику нативних функцій.
У бібліотеці з біндингами є сенс розробити трансформатор символьних рядків
в унікальні числа. Такий трансформатор повинен мати можливість для серіалізації,
щоб потім можна було трансформувати унікальні числа назад в рядки.
Основні методи трансформатору рядків:
– transform: перетворює рядки в цілі числа, приймає на вхід масив рядків та
повертає масив чисел;
– reverseTransform: перетворює цілі числа в рядки, приймає на вхід масив
чисел та повертає масив рядків.
Висновки до розділу 2
При розробці бібліотеки доцільно обрати таку мову програмування, яка
підтримує компіляцію коду в динамічну бібліотеку з C ABI. Динамічний тип
бібліотеки полегшує з’єднання з будь-якою сучасною МП. У цьому розділі успішно
спроєктовано нейронну мережу на основі спрощенної моделі TVAE, в якій модально-
специфічну нормалізацію замінено на простішу min-max нормалізацію, що
нормалізує дані за мінімальним та максимальним значеннями з вхідної вибірки даних.
Також у розділі створено макети функцій основної бібліотеки модуля генерування
синтетичних даних, що розробляється.
39
3 РОЗРОБКА МОДУЛЯ ГЕНЕРАЦІЇ СИНТЕТИЧНИХ ДАНИХ
3.1 Технології та засоби розробки
Вибір основних технологій розробки програмного продукту – дуже важливий
крок на початку цього процесу. Від цього залежить продуктивність, підтримуваність
та гнучкість кінцевої системи.
Спершу треба обрати платформу для реалізації нейронних мереж. Є дві основні
платформи: TensorFlow [15] і PyTorch [16]. Це потужні фреймворки з відкритим
вихідним кодом, які використовуються для побудови та навчання моделей глибокого
навчання. Ключові відмінності між ними:
− Простота використання: PyTorch зазвичай вважається простішим у
використанні та більш інтуїтивно зрозумілим, ніж TensorFlow, особливо для
дослідників, які тільки починають працювати з глибоким навчанням. PyTorch
побудований на основі динамічних обчислювальних графів, а це означає, що можна
змінювати структуру моделі «на льоту», що полегшує експерименти з різними
архітектурами. TensorFlow, з іншого боку, використовує статичні графіки обчислень,
які вимагають більшого попереднього планування і з якими може бути складніше
працювати.
− Популярність: TensorFlow є більш популярним, ніж PyTorch, з точки зору
використання та впровадження в промисловості. Частково це пов’язано з раннім
прийняттям TensorFlow компанією Google і сильною підтримкою, яку він отримав від
компанії. Проте, PyTorch набирає обертів в останні роки, особливо серед дослідників.
− Продуктивність: як TensorFlow, так і PyTorch добре оптимізовані для
прискорення за допомогою GPU (graphics processing unit), що дозволяє швидше
навчати моделі глибокого навчання. Однак, існують деякі відмінності в
продуктивності в залежності від конкретного випадку використання і
використовуваного обладнання.
− Спільнота та екосистема: TensorFlow має більшу і краще сформовану
спільноту, ніж PyTorch, а це означає, що для роботи з фреймворком доступно більше
40
ресурсів та інструментів. Однак PyTorch має зростаючу спільноту і багату екосистему
бібліотек та інструментів, особливо в дослідницькому середовищі.
Для створення невеликої нейронної мережі буде достатньо PyTorch, оскільки ця
платформа краще адаптована під дослідництво та має більш простий механізм роботи
з тензорами.
PyTorch підтримує широку палітру платформ та обчислювальних технологій,
як це видно на рис. 3.1.
Рисунок 3.1 – Скріншот зі сторінки https://pytorch.org/get-started/locally
Дуже добре, що PyTorch має реалізацію на МП C++, тому не має проблем з
під’єднанням до низькорівневих бібліотек.
Тепер можна обрати саме мову програмування для основної бібліотеку. Вибір
не великий: C++ чи Rust. Інші низькорівневі мови не мають таку велику підтримку,
як у цих мов. Мова про Python навіть не йде – тренування НМ буде швидким завдяки
PyTorch, але трансформування та передача даних будуть дуже повільними.
C++ – це високорівнева МП загального призначення, яку створив Б’ярн
Строуструп у 1983 році. Вона є розширенням мови програмування C і призначена для
системного програмування, вбудованих систем та інших додатків, які потребують
високопродуктивного коду.
Rust – це мова системного програмування, яку вперше представила у 2010 році
компанія Mozilla [17]. Вона розроблена, щоб бути швидкою, безпечною,
41
підтримувати паралельні обчислення, з акцентом на безпеку пам’яті та потокову
безпеку.
C++ та Rust є мовами програмування з різними філософіями проєктування та
сильними сторонами. C++ дає програмісту великий контроль над управлінням
пам’яттю, але це також означає, що в ній легко робити помилки і викликати помилки,
пов’язані з пам’яттю, такі як помилки сегментації або витоки пам’яті. Rust
використовує систему власності, запозичення та часу життя, яка гарантує безпеку
пам’яті та усуває ці типи помилок. Rust має вбудовану підтримку паралелізму через
модель власності, яка дозволяє безпечну та ефективну багатопоточність. C++ має
бібліотеку багатопоточності, але з паралелізмом у C++ може бути складніше
працювати і він більш схильний до помилок, таких як «стан гонитви» (race condition).
Rust має простіший і сучасніший синтаксис, ніж C++, з такими можливостями, як
зіставлення шаблонів, закриття та виведення типів. C++ має складніший синтаксис і
може бути складнішим для вивчення та читання. C++ відома своєю продуктивністю і
часто використовується у критичних до продуктивності програмах, таких як ігри або
високопродуктивні обчислення. Rust також розроблена для продуктивності, але її
фокус на безпеці та управлінні пам’яттю може іноді призводити до дещо
повільнішого коду.
Ясно, що мова Rust, в сучасному світі, краща для розробки низькорівневих
програмних модулів з підтримкою C ABI.
Для розробки прикладної програми обрано фреймворк Jetpack Compose Desktop
[18]. Це фреймворк для створення десктопних додатків на Kotlin, створений
компанією JetBrains. Він базується на популярній бібліотеці Jetpack Compose для
розробки інтерфейсу користувача для Android. За допомогою Compose Desktop
розробники можуть створювати десктопні додатки, які мають сучасний і дружній
користувальницький інтерфейс, з мінімальним шаблонним кодом. Він використовує
декларативну модель програмування, а це означає, що інтерфейс визначається
простим та інтуїтивно зрозумілим способом, без необхідності в складних макетах або
ієрархіях представлень. Головні особливості Compose Desktop:
42
спрощений процес розробки інтерфейсу користувача з акцентом на
читабельність коду та зручність обслуговування;
вбудована підтримка тем і стилів, що дозволяє створювати послідовний і
настроюваний дизайн інтерфейсу користувача;
гнучка та розширювана архітектура для легкого інтегрування з іншими
бібліотеками та фреймворками Kotlin;
крос-платформна модель розробки, яка дозволяє створювати додатки для
Windows, Mac і Linux, використовуючи єдину кодову базу.
Jetpack Compose Desktop знаходиться на ранній стадії розробки, але вже встиг
завоювати популярність серед Kotlin-розробників, які бажають створювати сучасні
десктопні додатки з оптимізованим процесом розробки.
Залишається обрати IDE (Integrated Development Environment). Аналіз
популярних IDE показує, що найкраща IDE для мови Rust – CLion [19], а для розробки
користувацького інтерфейсу – IntelliJ IDEA [20].
CLion – це інтегроване середовище розробки для низькорівневих мов
програмування, таких як C/C++ та Rust, розроблене компанією JetBrains (рис. 3.2).
Воно покликане допомогти розробникам ефективніше писати, налагоджувати та
підтримувати C++ код. CLion надає ряд функцій, які допомагають розробникам
писати високоякісний C++ код: аналіз коду та підсвічування помилок, що допомагає
виявляти помилки та потенційні проблеми до того, як вони стануть проблемами;
доповнення коду та підказки, які прискорюють написання коду, та автоматичне
доповнення для поширених шаблонів коду; інструменти рефакторингу коду, які
допомагають спростити та оптимізувати код, перейменовувати змінні, переносити
методи в інші файли тощо; інтеграція з популярними системами будування проєктів,
включаючи CMake, Gradle та Make. Також є вбудовані інструменти налагодження,
включаючи відлагоджувач та профілювальник, які допомагають розробникам
виправляти помилки та оптимізувати код. CLion включає інтеграцію з іншими
інструментами JetBrains, такими як платформа IntelliJ IDEA, що дозволяє
розробникам працювати з різними мовами та технологіями в одному IDE.
43
Рисунок 3.2 – Вигляд IDE CLion
IntelliJ IDEA – це IDE, розроблене компанією JetBrains, в першу чергу для
програмування на Java, Kotlin, JavaScript, TypeScript (рис. 3.3). Воно призначене для
надання широкого спектру інструментів і функцій, які допомагають розробникам
більш ефективно писати, налагоджувати та підтримувати код. Ця IDE дуже схожа на
CLion, але має акцент на такі інструменти, як Spring, Hibernate, Maven і т.д.
Рисунок 3.3 – Вигляд IDE IntelliJ IDEA
44
Для роботи з PyTorch в Rust існує пакет «tch», який надає біндинги до PyTorch
C++ API.
Для серіалізації даних можна використати формат JSON за допомогою пакетів
«serde» та «serde_json».
Для збереження бінарних даних (дані мережі з PyTorch) можна використати
кодування base64, яке реалізовано в пакеті «base64».
Всі ці Rust-пакети присутні на сайті https://crates.io (рис. 3.4).
Рисунок 3.4 – Основний сайт пакетів для мови Rust
3.2 Розробка трансформатору вхідних даних для подачі в нейронну
мережу
Як було зазначено раніше, «сирі» табличні дані потрібно перетворити в
зрозумілий для НМ формат. Для цього було розроблено структуру DataTransformer
(рис. 3.5), яка містить інформацію про дані зі стовпчиків (рис. 3.6) та кореляційну
матрицю первинних даних. Кореляційна матриця записана у порядковому форматі в
1D-векторі.
45
Рисунок 3.5 – Структура даних DataTransformer
Рисунок 3.6 – Структура даних ColumnInfo
Як можна бачити, структура ColumnInfo може бути двох типів: Discrete або
Continuous. Дискретні дані описуються унікальними категоріями, представленими
цілими числами, а також дискретним розподілом ймовірностей категорій. Неперервні
дані описуються мінімальним та максимальним значеннями, а також дискретним
розподілом ймовірностей. Цей розподіл поділено на √ інтервалів, де – кількість
чисел у вибірці первинних даних.
Крім того, DataTransformer помічено анотаціями Serialize та Deserialize. Це
означає, що можна всю структуру трансформеру вивантажити в портабельний формат
та завантажити з нього. Реальні макети основних функцій перетворювача на МП Rust
представлено на рис. 3.7.
Рисунок 3.7 – Головні методи структури DataTransformer
46
Як можна бачити, функція transform приймає індекс стовпчика та первинні дані
для трансформації та повертає тензор, який містить інформацію у форматі дійсних
чисел (float32), що НМ може зрозуміти. Функція inverse_transform також приймає
індекс колонки, але вхідні дані – це вихідні (синтетичні) дані з НМ, які
перетворюються в зручні для розробника структуровані дані.
Програмний код цих функцій представлено в додатку Б.
3.3 Навчання нейронної мережі та її використання для генерації
синтетичних даних
Перед початком тренування треба створити структуру даних мережі (рис. 3.8).
Ця структура включає сховище даних шарів нейронної мережі, пристрій обчислення
даних (може бути CPU або щось інше), мережа декодеру (рис. 3.9), розмір пакету
тренування, а також сам трансформатор даних, щоб трансформувати дані назад при
генеруванні даних. Дана структура створюється у двох випадках: виклик функції fit
(тренування) або завантаження з портабельного формату. Варто відмітити, що
структура не містить кодувальника (encoder), тому що він буде створений один раз
при тренуванні, а потім вже не знадобиться. Генерування даних відбувається саме
декодером.
Рисунок 3.8 – Структура даних основної генеративної мережі
Рисунок 3.9 – Структура даних мережі декодера.
47
У структурі декодера можна побачити параметри мережі, а саме: множина
лінійних шарів, а також параметр sigma, що відповідають архітектурі цієї мережі.
Щоб створити НМ, треба викликати функцію fit (рис. 3.10). Дана функція
приймає дані для кожної колонки таблиці, розмір тренувального пакету, device для
обробки даних, а також функцію, яка на вхід приймає номер епохи та значення цінової
функції після цієї епохи, а повертає значення bool, яке контролює припинення
тренування. Дана функція повертає Self – це структура НМ (див. рис. 3.8). У Rust
можна вказувати тип Self у методах структури замість назви самої структури.
Рисунок 3.10 – Функція тренування (створення) нової НМ
У навчанні нейронних мереж розмір пакета – це кількість навчальних
прикладів, які обробляються за один прохід. Під час навчання модель отримує пакет
навчальних даних і використовує його для обчислення градієнтів функції втрат щодо
параметрів моделі. Ці градієнти потім використовуються для оновлення параметрів
моделі, таким чином покращуючи продуктивність моделі на навчальних даних.
Розмір пакету є важливим гіперпараметром, який може впливати на
ефективність та результативність процесу навчання. Більший розмір пакету може
призвести до скорочення часу навчання, оскільки модель здатна паралельно
обробляти більше прикладів. Однак, більший розмір пакету може також вимагати
більше пам’яті, і це може ускладнити узагальнення нових даних. З іншого боку,
менший розмір пакету може вимагати більше ітерацій для збіжності, але це може
призвести до кращого узагальнення та стабільнішого навчання.
Автори TVAE використовують розмір пакету в 500 прикладів [1].
Рекомендована кількість нейронів для внутрішніх шарів НМ – 128, кількість
внутрішніх шарів – 2.
Програмний код тренування НМ представлено на рис. 3.11.
48
Рисунок 3.11 – Програмний код тренування НМ
Навчання НМ відбувається у двох циклах. Перший (loop) починає нову епоху
на кожній ітерації доти поки функція flow_control не поверне true для зупинення
навчання. Нова епоха означає випадкове перетасування всіх прикладів тренування.
Вкладений цикл (for) проходиться по масиву прикладів пакетами. В одній ітерації
тренування градієнт оптимізатора встановлюється на нулі, потів дані «проганяються»
через кодер (encoder), створюється випадковий латентний простір з поправками з
кодера, та ці латентні змінні завантажуються в декодер (decoder). Після цього
підраховується цінова функція (calc_loss), потім оптимізатор робить свою справу –
застосовує нові градієнти, вирахувані за допомогою алгоритму стохастичного
градієнтного спуску «Adam», беручи до уваги значення цінової функції.
49
Генерування даних з натренованої мережі представлено на рис. 3.12. Для цього
вираховується число кроків. Кількість згенерованих табличних записів за один крок
дорівнює розміру тренувального пакету. На кожному кроці генеруються випадкові
числа латентного простору в розподілі Гауса. Потім ці дані завантажуються в декодер
для генерування даних. Далі застосовується типова активаційна функція tanh для
нормалізації вихідних даних з НМ. EMBEDDING_DIM – розмір латентного простору.
Рисунок 3.12 – Програмний код вибірки нових даних з НМ
3.4 Створення Kotlin-біндингів до основної бібліотеки
Враховуючи те, що основна бібліотека буде open-source, слід зробити простий
спосіб використання її у системах вищого рівня на JVM, що є, наразі, популярною
платформою для програмування «бекенду». Для цього мова Kotlin підходить набагато
краща за Java через наступні причини:
вбудована функція null-безпеки, яка допомагає запобігти виключним
ситуаціям з нульовим вказівником. Ця функція дозволяє розробникам писати
безпечніший та надійніший код;
інтероперабельність: Kotlin повністю сумісний з Java, що означає, що
розробники можуть використовувати обидві мови в одному проєкті;
підтримка функціонального програмування: Kotlin надає такі можливості, як
лямбди, функції вищого порядку та незмінність, які дозволяють розробникам писати
код, який легше читати, писати та підтримувати;
50
сучасний синтаксис, який робить її більш читабельною та легшою для
розуміння, ніж Java. Він також надає такі можливості, як виведення типів, функції
розширення та класи даних, які допомагають розробникам писати код більш
ефективно.
Тому допоміжну бібліотеку краще писати на Kotlin. Біндинги (прив’язки) – це
спосіб взаємодії з бібліотеками C з коду іншої МП. Зазвичай це означає прив’язування
функцій з однієї мови програмування (Kotlin) до іншої (C). Приклад транслювання
(прив’язки) Kotlin-структур до структур C-репрезентації за допомогою JNA
представлено на рис. 3.13.
Рисунок 3.13 – Приклад прив’язки Kotlin-класів до C-структур
Можна побачити, що кожен клас-прив’язка наслідує клас Structure.
Прив’язка функцій проста (рис. 3.14).
Рисунок 3.14 – Приклад прив’язки Kotlin-методу до C-функції
Все це повинно бути обгорнуто у власний інтерфейс, який наслідує інтерфейс
Library з JNA (рис. 3.15). Як можна бачити, йде використання паттерну «Singleton»,
який забезпечує одноразове завантаження динамічної C-бібліотеки.
51
Рисунок 3.15 – Приклад власного інтерфейсу-обгортки C-бібліотеки
Як видно, використовується функція load, що завантажує бібліотеки з папки,
шлях до якої можна вказати за допомогою системної властивості JVM під назвою
«jna.library.path».
3.5 Трансформатор рядків в унікальні числа
Для спрощення використання низькорівневого функціоналу, потрібно створити
простий трансформатор рядків в унікальні числа. Варто нагадати, що для
елегантності, трансформатор в основній бібліотеці приймає дані тільки у двох
форматах: цілі та дійсні числа.
Реалізація цього перетворювача досить проста (рис. 3.16). Для цього потрібно
зберігати дані унікальних рядків і їх мапування назад в числа.
Рисунок 3.16 – Повна реалізація трансформатора рядків в числа та навпаки
52
3.6 Створення прикладної програми генерації синтетичних даних
Завершальний етап роботи над проєктом – створення прикладної програми для
демонстрації роботи основної бібліотеки. Як зазначено раніше, для створення даного
проєкту було використано фреймворк Compose Desktop Multiplatform.
В IDE вже є шаблон цього типу (рис. 3.17).
Рисунок 3.17 – Створення проєкту Compose Multiplatform в IntelliJ IDEA
Для роботи з серіалізацією (збереження даних НМ в файл та завантаження з
нього) використано бібліотеку «kotlinx-serialization-json» та відповідний плагін
«kotlinx-serialization». Для збереження згенерованих даних в файл застосовано
бібліотеку «kotlin-csv-jvm».
Біндинги до основної бібліотеки підключені наступним чином:
53
1. У папку «assets» в корні проєкту покладено відповідні збудовані файли
бібліотеки генерування даних в форматах dll та dylib для windows і macos відповідно
(рис. 3.18).
2. У папку «libs» в корні проєкту покладено бібліотеку біндингів до основної
бібліотеки (рис. 3.19).
Всі бібліотеки підключено в основному файлі build.gradle.kts (рис. 3.20).
Рисунок 3.18 – Вміст директорії «assets»
Рисунок 3.19 – Вміст директорії «libs»
Рисунок 3.20 – Підключення залежностей у Gradle
Для того, щоб програма могла знайти розроблений раніше модуль
synthetic_data, потрібно вказати шлях до неї за допомогою системного параметру
«jna.library.path». Оскільки проєкт буде пакуватися за допомогою Compose Desktop,
треба використати ту директорію, де буде знаходитись папка «assets» після
54
встановлення програми на комп’ютер користувача. Це робиться за допомогою
системної змінної «compose.application.resources.dir» (рис. 3.21).
Рисунок 3.21 – Функція main прикладної програми
Як було зазначено раніше, Jetpack Compose має реактивну модель відображення
інтерфейсу користувача. Це означає, що при зміні будь-якого «стану» інтерфейс буде
оновлено.
Для цього використовуються функції remember і mutableStateOf (рис. 3.22), що
є двома важливими концепціями, які використовуються для керування станом
компонованого інтерфейсу.
Функція «remember» дозволяє зберігати значення, що використовується при
рекомпозиції інтерфейсу. Це корисно, коли потрібно зберегти стан, який не повинен
бути втрачений при перемальовуванні інтерфейсу. Функція remember приймає
лямбду, яка повертає початкове значення стану і повертає посилання на це значення.
Функція «mutableStateOf» дозволяє створити об’єкт стану, що змінюється, який
можна оновлювати та спостерігати. Вона приймає початкове значення і повертає
пару, що містить поточне значення і функцію для оновлення цього значення. Функція
mutableStateOf часто використовується разом із функцією remember для керування
станом інтерфейсу Compose.
55
Рисунок 3.22 – Використання станів в Jetpack Compose
Потім ці стани можна використати як звичайні змінні. Основне завдання
реактивного інтерфейсу – це те, що для кожного представлення (view) створюється
окрема функція, всередині якої все описується (стани, функції, елементи інтерфейсу).
Простий приклад цього представлено на рис. 3.23.
Рисунок 3.23 – Власна реалізація елементу «Combobox» в Jetpack Compose
Результати розробки прикладної програми Synthetic Data Generator
представлено на рис. 3.24, 3.25, 3.26.
56
Рисунок 3.24 – Головне вікно програми Synthetic Data Generator
Рисунок 3.25 – Вікно тренування нейронної мережі
57
Рисунок 3.26 – Вікно генерування даних
З метою перевірки ефективності розробленої інформаційної технології було
проведено експерименти, зокрема на медичних даних, результати якого подано
нижче.
Параметри нейронної мережі:
– кодувальник: сумарно 4 шари по 128 нейронів;
– розмір латентного простору – 128 дійсних чисел;
– параметри декодувальника такі самі, як у кодувальника;
– оптимізатор «Adam» (стохастичний градієнтний спуск), швидкість навчання
(LR): 10-3 за ітерацію; штрафна вага (L2 penalty): 10-5;
– розмір пакету (batch_size): 500;
– одна епоха робить N/batch_size ітерацій, де N – кількість записів.
Масив даних: Brain Stroke Dataset [21]:
– N = 4982;
– кількість полів: 11;
– кількість епох: 1000 – співпадіння 94%;
– кількість епох: 3000 – співпадіння 98%, час тренування: ≈ 40 сек.
Початкові (реальні) дані та згенеровані (синтетичні) дані представлено на рис
3.27 та 3.28 відповідно.
58
Рисунок 3.27 – Реальні дані з набору «Brain Stroke Dataset»
Рисунок 3.28 – Згенеровані синтетичні дані з «Brain Stroke Dataset»
Масив даних: Diabetes Health Indicators Dataset [22]:
– N = 64000 (з 253680);
– кількість полів: 22;
– кількість епох: 500 – співпадіння 90%;
– час тренування: ≈ 4.5 хв.
Час генерації синтетичних даних обсягом 10000 записів: ≈ 1 сек. Початкові
реальні дані та згенеровані дані представлено на рис 3.29 та 3.30 відповідно.
59
Рисунок 3.29 – Реальні дані з набору «Diabetes Health Indicators Dataset»
Рисунок 3.30 – Реальні дані з набору «Diabetes Health Indicators Dataset»
Висновки до розділу 3
В цьому розділі розглянуто різноманітні аспекти та деталі розробки системи для
генерування синтетичних даних Synthetic Data Generator за допомогою спроєктованої
спрощеної моделі TVAE. Обрано мову програмування Rust для реалізації основного
модуля генерації даних через її новітність у багатьох напрямах, зокрема, швидкість
та надійність. Для створення демонстраційної програми обрано фреймворк Jetpack
Compose Desktop, який, в свою чергу, новітній у напрямі використання реактивного
програмування користувацьких інтерфейсів, що робить таку розробку ефективною та
60
досить простою. Розробка велась в IDE CLion та IntelliJ IDEA, що мають гарну
підтримку Rust та Compose Desktop відповідно. Розроблений алгоритм протестовано
на відкритих наборах даних, зокрема, «Brain Stroke Dataset» та «Diabetes Health
Indicators Dataset». Результати показали досить близькі до реальних статистичні
характеристики згенерованих синтетичних даних.
Розроблені проєкти доступні на платформі GitHub:
− Основна бібліотека: https://github.com/volodya7292/synthetic_data
− Kotlin-прив’язки: https://github.com/volodya7292/synthetic_data_kt
− Демонстраційна програма: https://github.com/volodya7292/synthetic-data-app
61
ВИСНОВКИ
Синтетичні дані можна використовувати для навчання моделей машинного
навчання в ситуаціях, коли реальних даних недостатньо або вони дорогі, коли
потрібно зберегти конфіденційність цих даних, що потенційно може зробити
машинне навчання більш доступним і економічно ефективним.
Для генерування синтетичних даних використовуються сучасні інформаційні
технології (системи генерації синтетичних даних (СГСД)), в основі яких лежать
статистичні методи, методи штучного інтелекту, зокрема нейронні мережі.
При виконанні кваліфікаційної роботи бакалавра досліджено потенціал
варіаційних автокодерів (VAE) у сфері генерації синтетичних даних. VAE – це
потужний інструмент машинного навчання, який дозволяє створювати високоякісні
синтетичні дані.
У реалізованих проєктах використано нейронні мережі для генерації
синтетичних даних для різноманітних застосувань, включаючи використання у ERP-
системах. Вдалося розробити програмний модуль, який може генерувати
високоякісні синтетичні дані, які практично не відрізняються від реальних даних.
Продуктивність цього модулю досить висока.
Окрім роботи з VAE, також використано інші технології та інструменти,
зокрема PyTorch, Rust та Kotlin. Це дало глибоке розуміння основних принципів і
методів, необхідних для побудови потужної системи для генерації синтетичних
даних.
Одним з ключових викликів при розробці системи було створення зручного
інтерфейсу для генерації синтетичних даних. Для вирішення цього завдання
використано фреймворк Jetpack Compose Desktop, який дозволив створити
елегантний і сучасний користувальницький інтерфейс, простий та зрозумілий
використанні.
У результаті роботи була розроблена інформаційна технологія генерування
синтетичних даних на основі нейронних мереж з відкритим кодом, яка може
інтегруватися з іншими інформаційними системами й успішно використовуватися,
62
зокрема, у фінансово-економічній сфері та сфері охорони здоров’я. Ефективність
запропонованої інформаційної технології була перевірена експериментальним
шляхом на відкритих базах даних. Як одна з перспектив – створення перетворювача
різноманітних вхідних типів даних, наприклад, часові дані або мультитипові дані,
щоб було ще легше інтегрувати розроблений генератор даних в системи вищих рівнів.
63
СПИСОК ВИКОРИСТАНИХ ДЖЕРЕЛ
1. Lei Xu, Maria Skoularidou, Alfredo Cuesta-Infante, Kalyan Veeramachaneni.
Modeling Tabular data using Conditional GAN. URL: https://arxiv.org/abs/1907.00503
(дата звернення: 15.03.2023).
2. Jayoung Kim, Jinsung Jeon, Jaehoon Lee, Jihyeon Hyeong, Noseong Park. OCT-
GAN: Neural ODE-based Conditional Tabular GANs. URL:
https://arxiv.org/abs/2105.14969 (дата звернення: 15.03.2023).
3. Synthetic data. URL: https://en.wikipedia.org/wiki/Synthetic_data (дата
звернення: 17.03.2023).
4. Мангеттенська метрика. URL: https://uk.wikipedia.org/wiki/Мангеттенська
_метрика (дата звернення: 17.03.2023).
5. Коефіцієнт кореляції Пірсона. URL: uk.wikipedia.org/wiki/Коефіцієнт_
кореляції_Пірсона (дата звернення: 17.03.2023).
6. Баєсова мережа. URL: https://uk.wikipedia.org/wiki/Баєсова_мережа (дата
звернення: 17.03.2023).
7. C Chow and Cong Liu. Approximating discrete probability distributions with
dependence trees. URL: https://cs.nyu.edu/~roweis/csc2515-2006/readings/chowliu.pdf
(дата звернення: 17.03.2023).
8. Jun Zhang, Graham Cormode, Cecilia M Procopiuc, Divesh Srivastava, and
Xiaokui Xiao. Privbayes: Private data release via bayesian networks. URL:
https://dl.acm.org/doi/pdf/10.1145/3134428 (дата звернення: 17.03.2023)
9. Генеративна змагальна мережа. URL: https://uk.wikipedia.org/wiki/
Генеративна_змагальна_мережа (дата звернення: 17.03.2023).
10. Варіаційний автокодувальник. URL: https://uk.wikipedia.org/wiki/
Варіаційний_автокодувальник (дата звернення: 15.04.2023).
11. Розходження Кульбака-Лейблера. URL: uk.wikipedia.org/wiki/
Розходження_Кульбака_—_Лейблера (дата звернення: 17.03.2023).
12. About Tonic. URL: https://docs.tonic.ai/app (дата звернення: 06.02.2023).
64
13. About us – Mostly.ai. URL: https://mostly.ai/about-us (дата звернення:
06.02.2023).
14. Introduction – Syntho.ai. URL: https://www.syntho.ai/about-syntho (дата
звернення: 06.02.2023).
15. Tensorflow. URL: https://www.tensorflow.org (дата звернення: 05.05.2023).
16. PyTorch. URL: https://pytorch.org (дата звернення: 05.05.2023).
17. Rust. URL: uk.wikipedia.org/wiki/Rust_(мова_програмування) (дата
звернення: 05.05.2023).
18. Compose Multiplatform UI Framework. URL:
https://www.jetbrains.com/lp/compose-multiplatform/ (дата звернення: 05.05.2023).
19. CLion. URL: https://www.jetbrains.com/clion/ (дата звернення: 05.05.2023).
20. IntelliJ IDEA. URL: https://www.jetbrains.com/idea (дата звернення:
05.05.2023).
21. Brain Stroke Dataset. URL: https://www.kaggle.com/datasets/
jillanisofttech/brain-stroke-dataset (дата звернення: 30.03.2023).
22. Diabetes Health Indicators Dataset. URL: https://www.kaggle.com/datasets/
alexteboul/diabetes-health-indicators-dataset (дата звернення: 30.03.2023).
65
ДОДАТОК А
Затверджую
Зав. кафедри КНСА,
______________ Триус Ю.В.
«____»____________2023 р.
МОДУЛЬ ГЕНЕРАЦІЇ ДАНИХ ДЛЯ ERP СИСТЕМИ
З ВИКОРИСТАННЯМ НЕЙРОННИХ МЕРЕЖ
Специфікація
482.ЧДТУ.31914-01 01 01
Листів 1
Розробник ____________________ Шулаков В.В.
Керівник ____________________ Бойко В.В.
Черкаси – 2023
66
482.ЧДТУ.31914-01
Позначення Найменування Примітка
Документація
482.ЧДТУ.31914-01 12 01 Програмний код модулю
генерації синтетичних даних
482.ЧДТУ.31914-01 34 01 Інструкція користувача
482.ЧДТУ.31914-01 90 01 Публікація результатів
досліджень
67
ДОДАТОК Б
МОДУЛЬ ГЕНЕРАЦІЇ ДАНИХ ДЛЯ ERP СИСТЕМИ
З ВИКОРИСТАННЯМ НЕЙРОННИХ МЕРЕЖ
Програмний код модулю генерації синтетичних даних
482.ЧДТУ.31914-01 12 01
Листів 11
Розробник ____________________ Шулаков В.В.
Черкаси – 2023
68
tvae.rs
mod data_transform;
pub mod input;
use crate::tvae::data_transform::{ColumnTrainInfo, DataTransformer};
use crate::tvae::input::{ColumnDataRef, SampledColumnData};
use crate::utils;
use base64::Engine;
use std::io::Cursor;
use tch::nn::{Adam, Module, OptimizerConfig};
use tch::{nn, Device, Tensor};
const JSON_DATA_TRANSFORMER_FIELD: &str = "data_transformer";
const JSON_BATCH_SIZE_FIELD: &str = "batch_size";
const JSON_NN_DATA_FIELD: &str = "nn_data";
const LOSS_FACTOR: f32 = 2.0;
const L2_SCALE: f64 = 1e-5;
const EMBEDDING_DIM: i64 = 128;
const COMPRESS_DIMS: [i64; 2] = [128, 128];
const DECOMPRESS_DIMS: [i64; 2] = [128, 128];
struct Encoder {
seq: nn::Sequential,
fc1: nn::Linear,
fc2: nn::Linear,
}
impl Encoder {
pub fn new(vs: &nn::Path, data_dim: i64, compress_dims: &[i64], embedding_dim:
i64) -> Self {
let vs = vs / "encoder";
let mut seq = nn::seq();
let mut curr_dim = data_dim;
for (i, dim) in compress_dims.iter().enumerate() {
seq = seq
.add(nn::linear(
&vs / format!("seq{}", i),
curr_dim,
*dim,
Default::default(),
))
.add_fn(Tensor::relu);
curr_dim = *dim;
}
let fc1 = nn::linear(&vs / "fc1", curr_dim, embedding_dim,
Default::default());
let fc2 = nn::linear(&vs / "fc2", curr_dim, embedding_dim,
Default::default());
Self { seq, fc1, fc2 }
}
pub fn encode(&self, input: &Tensor) -> (Tensor, Tensor, Tensor) {
let feature = self.seq.forward(input);
let mu = self.fc1.forward(&feature);
let log_var = self.fc2.forward(&feature);
let std = (0.5_f32 * &log_var).exp();
(mu, std, log_var)
69
}
}
struct Decoder {
seq: nn::Sequential,
sigma: Tensor,
}
impl Decoder {
pub fn new(vs: &nn::Path, embedding_dim: i64, decompress_dims: &[i64], data_dim:
i64) -> Self {
let vs = vs / "decoder";
let mut seq = nn::seq();
let mut curr_dim = embedding_dim;
for (i, dim) in decompress_dims.iter().enumerate() {
seq = seq
.add(nn::linear(
&vs / format!("seq{}", i),
curr_dim,
*dim,
Default::default(),
))
.add_fn(Tensor::relu);
curr_dim = *dim;
}
seq = seq.add(nn::linear(
&vs / "seqLast",
curr_dim,
data_dim,
Default::default(),
));
let sigma = vs.entry("sigma").or_var(&[data_dim], nn::Init::Const(0.1));
Self { seq, sigma }
}
pub fn decode(&self, input: &Tensor) -> (Tensor, Tensor) {
let out = self.seq.forward(input);
(out, self.sigma.shallow_clone())
}
}
fn calc_loss(
recon_x: &Tensor,
x: &Tensor,
sigmas: &Tensor,
mu: &Tensor,
log_var: &Tensor,
output_info: &[ColumnTrainInfo],
factor: f32,
) -> (Tensor, Tensor) {
let mut st = 0_i64;
let mut loss = vec![];
for column_info in output_info {
for span_info in column_info.output_spans() {
let ed = st + span_info.dim as i64;
if span_info.activation != "softmax" {
let x_slice = x.slice(1, Some(st), Some(st + 1), 1);
70
let recon_x_slice = recon_x.slice(1, Some(st), Some(st + 1), 1);
let std = sigmas.get(st);
let eq = x_slice - recon_x_slice.tanh();
loss.push(
(eq.pow_tensor_scalar(2) / 2 /
std.pow_tensor_scalar(2)).sum(tch::Kind::Float),
);
loss.push(std.log() * x.size()[0]);
} else {
let x_slice = x.slice(1, Some(st), Some(ed), 1);
let recon_x_slice = recon_x.slice(1, Some(st), Some(ed), 1);
loss.push(recon_x_slice.cross_entropy_loss::<&_>(
&x_slice.argmax(Some(-1), false),
None,
tch::Reduction::Sum,
-100,
0.0,
));
}
st = ed;
}
}
assert_eq!(st, recon_x.size()[1]);
let kld = -0.5_f32
* (1_i32 + log_var - mu.pow_tensor_scalar(2) -
log_var.exp()).sum(tch::Kind::Float);
let s = loss.iter().sum::<Tensor>() * factor as f64 / x.size()[0];
let kld_norm = &kld / x.size()[0];
(s, kld_norm)
}
pub struct TVAE {
vs: nn::VarStore,
device: Device,
decoder: Decoder,
batch_size: usize,
transformer: DataTransformer,
}
fn next_multiple_of(n: usize, multiple: usize) -> usize {
assert!(multiple > 0);
((n + multiple - 1) / multiple) * multiple
}
pub type DoStop = bool;
pub type Realness = f32;
pub type CorrelationRealness = f32;
impl TVAE {
/// `flow_control`: Fn(epoch, loss) -> DoStop
pub fn fit<F: Fn(usize, f64) -> DoStop>(
data: &[ColumnDataRef],
batch_size: usize,
device: Device,
flow_control: F,
71
) -> Self {
let vs = nn::VarStore::new(device);
assert!(data.len() > 0);
let transformer = DataTransformer::prepare(data);
let n_rows = data[0].len() as i64;
let train_data = Tensor::cat(
&data
.iter()
.enumerate()
.map(|(i, v)| transformer.transform(i, v))
.collect::<Vec<_>>(),
1,
)
.totype(tch::Kind::Float);
let data_dim = transformer
.train_infos()
.iter()
.map(|v| v.total_dim())
.sum::<i64>();
let encoder = Encoder::new(&vs.root(), data_dim, &COMPRESS_DIMS,
EMBEDDING_DIM);
let decoder = Decoder::new(&vs.root(), EMBEDDING_DIM, &DECOMPRESS_DIMS,
data_dim);
let mut optimizer = Adam::default().wd(L2_SCALE).build(&vs, 1e-3).unwrap();
let mut epoch = 0;
loop {
let shuffle_perm = Tensor::randperm(n_rows, (tch::Kind::Int64, device));
let curr_train_data = train_data.index(&[Some(shuffle_perm)]);
let mut total_loss = 0.0;
let mut loss_count = 0.0;
for batch_start in (0..n_rows).step_by(batch_size) {
optimizer.zero_grad();
let batch = curr_train_data.slice(
0,
Some(batch_start),
Some((batch_start + batch_size as i64).min(n_rows)),
1,
);
let real = batch.to(device);
let (mu, std, log_var) = encoder.encode(&real);
let eps = std.randn_like();
let emb = &eps * &std + μ
let (rec, sigmas) = decoder.decode(&emb);
let (loss1, loss2) = calc_loss(
&rec,
&real,
&sigmas,
&mu,
&log_var,
&transformer.train_infos(),
LOSS_FACTOR,
72
);
let loss = &loss1 + &loss2;
total_loss += loss.double_value(&[]);
loss_count += 1.0;
optimizer.backward_step(&loss);
let _ = decoder.sigma.data().clamp_(0.01, 1.0);
}
let loss = total_loss / loss_count;
if flow_control(epoch, loss) {
break;
}
epoch += 1;
}
Self {
vs,
decoder,
batch_size,
device,
transformer,
}
}
pub fn save(&self) -> serde_json::Value {
let transformer_data = self.transformer.save();
let batch_size = serde_json::Value::Number(self.batch_size.into());
let mut nn_data = Vec::with_capacity(1024 * 1024);
self.vs.save_to_stream(&mut nn_data).unwrap();
let nn_data_base64 = serde_json::Value::String(
base64::engine::general_purpose::STANDARD_NO_PAD.encode(nn_data),
);
let mut map = serde_json::Map::new();
map.insert(JSON_DATA_TRANSFORMER_FIELD.to_owned(), transformer_data);
map.insert(JSON_BATCH_SIZE_FIELD.to_owned(), batch_size);
map.insert(JSON_NN_DATA_FIELD.to_owned(), nn_data_base64);
serde_json::Value::Object(map)
}
pub fn load(device: Device, data: serde_json::Value) -> Self {
let data = data.as_object().unwrap();
let transformer_data = data.get(JSON_DATA_TRANSFORMER_FIELD).unwrap();
let batch_size = data.get(JSON_BATCH_SIZE_FIELD).unwrap().as_u64().unwrap();
let nn_data_base64 = data.get(JSON_NN_DATA_FIELD).unwrap().as_str().unwrap();
let transformer = DataTransformer::load(transformer_data);
let nn_data = base64::engine::general_purpose::STANDARD_NO_PAD
.decode(nn_data_base64)
.unwrap();
let data_dim = transformer
.train_infos()
.iter()
.map(|v| v.total_dim())
73
.sum::<i64>();
let mut vs = nn::VarStore::new(device);
let decoder = Decoder::new(&vs.root(), EMBEDDING_DIM, &DECOMPRESS_DIMS,
data_dim);
vs.load_from_stream(Cursor::new(nn_data)).unwrap();
Self {
vs,
device,
decoder,
batch_size: batch_size as usize,
transformer,
}
}
pub fn sample(&self, samples: usize) -> (Vec<SampledColumnData>,
CorrelationRealness) {
let n_steps = next_multiple_of(samples, self.batch_size) / self.batch_size;
let n_columns = self.transformer.train_infos().len();
let mut generated_columns =
Vec::<SampledColumnData>::with_capacity(n_columns);
let mut raw_data = Vec::with_capacity(n_steps * self.batch_size);
for _ in 0..n_steps {
let mut noise = Tensor::zeros(
&[self.batch_size as i64, EMBEDDING_DIM],
(tch::Kind::Float, self.device),
);
let _ = noise.normal_(0.0, 1.0);
let (fake, _sigmas) = self.decoder.decode(&noise);
let fake = fake.tanh();
raw_data.push(fake.detach().to(Device::Cpu));
}
let generated = Tensor::cat(&raw_data, 0);
let mut start_idx = 0;
for (i, train_info) in self.transformer.train_infos().iter().enumerate() {
let end_idx = start_idx + train_info.total_dim();
let generated_column = generated.slice(0, None, Some(samples as i64),
1).slice(
1,
Some(start_idx),
Some(end_idx),
1,
);
let inverse_data = self.transformer.inverse_transform(i,
&generated_column);
let col_info = &self.transformer.column_infos()[i];
let realness = 1.0 - col_info.calc_l1_distance(&inverse_data);
generated_columns.push(SampledColumnData::from_regular(inverse_data,
realness));
start_idx = end_idx;
}
let real_corr_mat = self.transformer.correlation_matrix();
let generated_corr_mat = utils::calc_correlation_matrix(
&generated_columns
74
.iter()
.map(|v| v.data_as_ref())
.collect::<Vec<_>>(),
);
let corr_realness = 1.0
- real_corr_mat
.iter()
.zip(&generated_corr_mat)
.map(|(v1, v2)| (v1.abs() - v2.abs()).abs())
.sum::<f32>()
/ real_corr_mat.len() as f32;
(generated_columns, corr_realness)
}
}
data_transformer.rs
use crate::tvae::input::{ColumnData, ColumnDataRef};
use crate::utils;
use serde::{Deserialize, Serialize};
use tch::Tensor;
pub struct SpanInfo {
pub dim: i64,
pub activation: &’static str,
}
pub struct ColumnTrainInfo {
output_spans: Vec<SpanInfo>,
}
impl ColumnTrainInfo {
pub fn output_spans(&self) -> &[SpanInfo] {
&self.output_spans
}
pub fn total_dim(&self) -> i64 {
self.output_spans.iter().map(|v| v.dim).sum::<i64>()
}
}
#[derive(Serialize, Deserialize)]
pub enum ColumnInfo {
Discrete {
unique_categories: Vec<i32>,
pdf: Vec<usize>,
},
Continuous {
min: f32,
max: f32,
pdf: Vec<usize>,
},
}
impl ColumnInfo {
pub fn calc_l1_distance(&self, data: &ColumnData) -> f32 {
match (self, data) {
75
(
ColumnInfo::Discrete {
unique_categories,
pdf: real_pdf,
},
ColumnData::Discrete(data),
) => {
let data_pdf = utils::calc_discrete_pdf(unique_categories, data);
utils::l1_distance_between_pdfs(&data_pdf, real_pdf)
}
(
ColumnInfo::Continuous {
min,
max,
pdf: real_pdf,
},
ColumnData::Continuous(data),
) => {
let n_buckets = real_pdf.len();
let data_pdf = utils::calc_continuous_pdf(*min, *max, data,
n_buckets);
utils::l1_distance_between_pdfs(&data_pdf, real_pdf)
}
_ => panic!("Invalid combination"),
}
}
}
#[derive(Serialize, Deserialize)]
pub struct DataTransformer {
column_infos: Vec<ColumnInfo>,
correlation_matrix: Vec<f32>,
}
impl DataTransformer {
pub fn prepare(columns: &[ColumnDataRef]) -> Self {
if columns.is_empty() {
return Self {
column_infos: vec![],
correlation_matrix: vec![],
};
}
let n_rows = columns[0].len();
if columns.iter().any(|v| v.len() != n_rows) {
panic!("All columns must have the same number of rows");
}
let column_infos: Vec<_> = columns
.iter()
.map(|column| match column {
ColumnDataRef::Discrete(data) => {
let mut uniques = data.to_vec();
uniques.sort_unstable();
uniques.dedup();
let pdf = utils::calc_discrete_pdf(&uniques, data);
ColumnInfo::Discrete {
unique_categories: uniques,
pdf,
}
}
76
ColumnDataRef::Continuous(data) => {
let filtered: Vec<_> = data.iter().cloned().filter(|v|
v.is_finite()).collect();
let min = filtered
.iter()
.cloned()
.min_by(|a, b| a.total_cmp(&b))
.unwrap_or(f32::NAN);
let max = filtered
.iter()
.cloned()
.max_by(|a, b| a.total_cmp(&b))
.unwrap_or(f32::NAN);
let n_buckets = (data.len() as f64).sqrt().max(1.0) as usize;
let pdf = utils::calc_continuous_pdf(min, max, data, n_buckets);
ColumnInfo::Continuous { min, max, pdf }
}
})
.collect();
let correlation_matrix = utils::calc_correlation_matrix(columns);
Self {
column_infos,
correlation_matrix,
}
}
pub fn train_infos(&self) -> Vec<ColumnTrainInfo> {
self.column_infos
.iter()
.map(|data| match data {
ColumnInfo::Discrete {
unique_categories, ..
} => ColumnTrainInfo {
output_spans: vec![SpanInfo {
dim: unique_categories.len() as i64,
activation: "softmax",
}],
},
ColumnInfo::Continuous { .. } => ColumnTrainInfo {
output_spans: vec![SpanInfo {
dim: 1,
activation: "tanh",
}],
},
})
.collect()
}
pub fn column_infos(&self) -> &[ColumnInfo] {
&self.column_infos
}
pub fn correlation_matrix(&self) -> &[f32] {
&self.correlation_matrix
}
pub fn save(&self) -> serde_json::Value {
serde_json::to_value(&self).unwrap()
77
}
pub fn load(data: &serde_json::Value) -> Self {
Self::deserialize(data).unwrap()
}
pub fn transform(&self, column_index: usize, data: &ColumnDataRef) -> Tensor {
let column_info = &self.column_infos[column_index];
let n_rows = data.len() as i64;
match (data, column_info) {
(
ColumnDataRef::Discrete(data),
ColumnInfo::Discrete {
unique_categories, ..
},
) => {
let n_uniques = unique_categories.len() as i64;
let data_tensor = Tensor::of_slice(data);
let uniques_tensor = Tensor::of_slice(&unique_categories);
let data_x_uniques = data_tensor.broadcast_to(&[n_uniques, n_rows]);
let uniques_x_data = uniques_tensor.broadcast_to(&[n_rows,
n_uniques]);
let hot_vectors = data_x_uniques.transpose(0,
1).eq_tensor(&uniques_x_data);
let transformed = hot_vectors.totype(tch::Kind::Int8);
transformed
}
(ColumnDataRef::Continuous(data), ColumnInfo::Continuous { min, max, ..
}) => {
let range = max - min;
let filtered = Tensor::of_slice(data);
let normalized = (filtered - *min as f64) / range as f64;
let transformed = normalized.reshape(&[data.len() as i64, 1]);
transformed
}
_ => panic!("Invalid column data type"),
}
}
pub fn inverse_transform(&self, column_index: usize, data: &Tensor) -> ColumnData
{
let n_rows = data.size()[0];
match &self.column_infos[column_index] {
ColumnInfo::Discrete {
unique_categories, ..
} => {
let mut out_data = Vec::with_capacity(n_rows as usize);
let category_indices = data.argmax(Some(1), false);
for idx in category_indices.iter::<i64>().unwrap() {
let inverse = unique_categories[idx as usize];
out_data.push(inverse);
}
ColumnData::Discrete(out_data)
}
78
ColumnInfo::Continuous { min, max, .. } => {
let mut out_data = Vec::with_capacity(n_rows as usize);
let range = max - min;
let clamped = data.clamp(0.0, 1.0);
let tensor_inverse = clamped * range as f64 + *min as f64;
for i in 0..n_rows {
let value = tensor_inverse.double_value(&[i]) as f32;
out_data.push(value)
}
ColumnData::Continuous(out_data)
}
}
}
}
79
ДОДАТОК В
МОДУЛЬ ГЕНЕРАЦІЇ ДАНИХ ДЛЯ ERP СИСТЕМИ
З ВИКОРИСТАННЯМ НЕЙРОННИХ МЕРЕЖ
Інструкція користувача
482.ЧДТУ.31914-01 34 01
Листів 3
Розробник ____________________ Шулаков В.В.
Черкаси – 2023
80
1. Підготувати реальні дані: наприклад, можна завантажити дані з сайту
kaggle.com. Обрати формат CSV та завантажити їх на персональний комп’ютер.
2. Запустити програму Synthetic Data Generator.
81
3. Для створення нової моделі натиснути кнопку «New Model». Для генерації
даних з вже створеної моделі натиснути «Open Model» та обрати файл моделі.
Тренування моделі (New Model):
1. Натиснути кнопку «Open .CSV dataset» та обрати реальні дані.
2. Обрати колонки з таблиці для тренування.
3. Обрати кількість епох.
4. Натиснути кнопку «Train». Обрати назву файлу для збереження даних моделі.
5. Якщо немає часу чекати, натиснути кнопку «STOP AND FINISH».
6. Програма автоматично перейде до вікна генерування даних.
82
Генерування даних (Open Model):
1. Ввести кількість табличних записів для генерування.
2. Натиснути кнопку «GENERATE» та вибрати назву csv-файлу для збереження
згенерованих файлів.
3. Перевірити якість даних в спливаючому вікні.
83
ДОДАТОК Г
МОДУЛЬ ГЕНЕРАЦІЇ ДАНИХ ДЛЯ ERP СИСТЕМИ
З ВИКОРИСТАННЯМ НЕЙРОННИХ МЕРЕЖ
Публікація результатів досліджень
482.ЧДТУ.31914-01 90 01
Листів 8
Розробник ____________________ Шулаков В.В.
Черкаси – 2023
84
URL: https://dspace.vnmu.edu.ua/handle/123456789/6161
85
86
87
88
89
90
91