Блоґ одного кібера

Історія хвороби контуженого інформаційним вибухом

Posts Tagged ‘робота

Як написати собі маленький Google Translate на Grammatical Framework за 15 хв

with 3 comments

І з якістю трохи кращою ніж в Google Translate, але з обсягом можливих перекладів набагато меншим.

Що я роблю на роботі

Зараз я працюю в Zalando – компанії яка продає різний крам в купу країн Європи. Відповідно, треба підтримувати сайт для багатьох країн. Я працюю у відділі який займається інструментами що допомагають створювати наповнення сайту. Процес додавання нового вмісту на сайт дуже трудомісткий, бо потрібно сфотографувати продукт, на столі, на моделі, перевірити розміри, матеріали і інструкції з догляду, написати опис, заголовок і т.п.. А потім ще перекласти це на 8 мов і кілька локалей, бо деякі країни DACH (німецькомовний простір) не входять до єврозони і використовують швейцарські франки наприклад. Це велика купа часу, і великий простір для автоматизації.

Google Translate не підходить, тому що наприклад ми продаємо скатертини, і німецький менеджер пише “Tischdecken” (множина від Tischdecke), Google Translate думає що це дієслово (бо є таке дієслово), і перекладає “Set the table” (накривайте столи!).

Насправді задача стоїть простіша ніж повний машинний переклад, бо дані отримуються не якоюсь конкретною мовою, а набором параметрів – “що”, “коли”, “яке”, “для кого”, “по чому” і т.п.. Тобто якщо я отримую що=взуття, яке=класичне коли=зима для кого=хлопчики, по чому=<100 € то я (в сенсі моя програма), пише:

'de_CH': 'Klassische Winterschuhe für Jungs - unter CHF 107',
'de_DE': 'Klassische Winterschuhe für Jungs - unter 100 €',
'en_GB': 'Classic Winter Shoes for Boys - under £85',

Все просто – перекладаємо слова за словником і підставляємо в шаблон для конкретного набору компонентів. З німецькою лише один виклик – з’єднання слів. Тобто якщо я з’єдную Winter з чимось – просто конкатеную, а от коли Fruhling – додаю між Fruhling і тим словом яке приєдную ще “s”. Є ще інші правила.

Складність з’являється в слов’янських мовах. Там є відмінювання прикметників у множині.

На роботі я багато думаю як би то не робити роботу яку вже хтось зробив за мене

Програмісти ліниві створіння. Нащо писати код якщо код вже є написаний на StackOverflow і Github? Треба лише знати де той код лежить. І я почав собі шукати як інші люди це робили. До того як з’явилось машинне навчання і статистичні методи, всі пробували підхід на основі правил. І проміжної мови.

З проміжною мовою процес розбивався на дві частини – спершу проводився синтаксичний аналіз тексту на мові джерела, тоді з побудованого представлення тексту на проміжній мові (зазвичай дерево) будувався текст іншою мовою.

Моя задача вже простіша, бо ніякого синтаксичного аналізу робити не треба, треба просто згенерувати текст 8-ма мовами на основі структури даних. Це як два пальці відрендерити контекст за допомогою шаблону, ми веб-деви тільки таке й робимо.

Тільки виявляється що шаблон для генерації простого заголовку на зразок: “Mindestens 500 € sparen – Premium Herbstmode” або “Premium Herbstmode unter 999 €” займає мало не ввесь екран.

Я подумав що може нарешті настав той момент коли я на Кубику не даремно вчився, пам’ятаю Opa Chomsky Style, і треба застосувати якусь контекстно-вільну граматику, чи яка там наступна за потужністю, бо контекстно-вільна здається не потягне слов’янську морфологію.

І раптом в своїх пошуках надибав Grammatical Framework. Це штука яку вже більше ніж 20 років пишуть на Хаскелі, значить люди вміють точно більше ніж я. І автор приходить з доповіддю в Google, в якій кілька слайдів на початку – суцільний тролінг команди Google Translate:

Але мене підкупило речення з їх сайту, http://www.grammaticalframework.org/

GF is easy to learn by following the tutorial. You can write your first translator in 15 minutes.

До роботи!

Ну що ж, заводимо таймер, і спробуємо наприклад написати перекладач, що може перекласти набір шахматних словосполучень /(black|white) (queen|king)/ з англійської на українську. Шахмати, тому що одягом я й на роботі можу зайнятись, а блог я пишу вдома і хочеться трохи змінити контекст.

Для початку варто встановити компілятор/інтерпретатор gf. На сторінці завантаження є пакети для різних ОС, з інструкцією про те як їх поставити. Ще можна поставити плагін підсвітки синтаксису для вашого редактора, перелік є на цій сторінці.

Hello, world!

Тепер, в файлі Chess.gf пишемо таку граматику:

-- це, до речі, коментар
abstract Chess = {

  flags startcat = Piece ;

  cat PieceType ; Color ; Piece;

  fun
    piece : Color -> PieceType -> Piece ;
    Black, White : Color;
    Queen, King: PieceType;
}

abstract означає що ми описуємо граматику проміжної мови, тобто не якоїсь конкретної людської, а мови якою ми описуватимемо сенс тексту.

flags startcat = Piece ; означає що початковою категорією (коренем дерева) буде фігура.

cat перелічує можливі категорії (типи вузлів дерева).

Секція fun описує функції. Функції описують як будується дерево, і можуть бути будь-якої арності, в тому числі нулярні. Наприклад тут функції Black та White не приймають аргументів але повертають колір. (По суті – константи). Зате функція piece приймає дві категорії і повертає категорію для фігури.

Тепер про те як перетворити дерево на послідовність токенів якоюсь мовою. Це називається лінеаризацією, і описується конкретною граматикою:

concrete ChessEng of Chess = {

  lincat Piece, Color, PieceType = {s : Str} ;

  lin
    piece color type = {s = color.s ++ type.s} ;
    Black = {s = "black"} ;
    White = {s = "white"} ;
    Queen = {s = "queen"} ;
    King = {s = "king"} ;
}

Зберігаємо її в файлі ChessEng.gf. Для GF важливо щоб кожна граматика була в своєму файлі і щоб він називався так само як називається граматика, інакше він дає помилки.

lincat описує які типи токенів будуть відповідати категоріям абстрактної категорії. В нашому випадку це структура з одним полем типу Str, тому що пізніше нам знадобляться інші поля.

lin описує функції лінеаризації типи яких були описані в абстрактній граматиці. Для унарних функцій – як власне пишеться слово, для інших – як скомбінувати інші лінеаризації.

Щоб потестувати як все працює, запускаємо команду gf Chess*, яка завантажує обидві граматики і починає інтерактивну сесію.

Тут ми можемо наприклад перевіряти наші речення на правильність:

Chess> parse "black queen"
piece Black Queen

Chess> parse "snow queen"
The parser failed at token 1: "snow"

Правильно, ми тут пишемо граматику для шахмат а не для казок. Ще можна попросити згенерувати випадкове дерево:

Chess> generate_random
piece White Queen

Або його лінеаризацію:

Chess> generate_random | linearize
black queen

Або всі можливі речення:

Chess> generate_trees | l
black king
black queen
white king
white queen

Морфологія

Тепер давайте зробимо переклад на українську! Для цього треба описати граматику української. Додаємо переклад з англійської в файл ChessUkr.gf:

concrete ChessUkr of Chess = {

  lincat Piece, Color, PieceType = {s : Str} ;

  lin
    piece color type = {s = color.s ++ type.s} ;
    Black = {s = "чорний"} ;
    White = {s = "білий"} ;
    Queen = {s = "королева"} ;
    King = {s = "король"} ;
}

Завантажуємо і тестуємо перекладач в консолі:

Chess> parse -lang=ChessEng "black king" | linearize -lang=ChessUkr
чорний король

Дуже добре!

Chess> parse -lang=ChessEng "white queen" | linearize -lang=ChessUkr
білий королева

От засада! Навіть Google Translate вміє краще. Треба ввести в українську граматику поняття роду. Для цього до іменників треба додати інформацію про те якого вони роду, а кольори мають описувати як вони відмінюються залежно від роду:

concrete ChessUkr of Chess = {

  param Gender = Masc | Fem ;
  lincat Piece, PieceType = {s : Str; gender : Gender} ;
  lincat Color = {s : Gender => Str} ;

  lin
    piece color type = {s = color.s ! type.gender ++ type.s; gender=type.gender} ;
    Black = {s = table {
        Masc => "чорний";
        Fem => "чорна"
    } };
    White = {s = table {
        Masc => "білий";
        Fem => "біла"
    } };
    Queen = {s = "королева"; gender=Fem} ;
    King = {s = "король"; gender=Masc} ;
}

Тут нам і стало в нагоді те що наші категорії – це структури, а не просто рядки. Чіпляємо до кожної фігури інформацію про стать.

Gender => Str задає тип таблиці (це майже як функція, тільки описується чимось типу хеша). Кожен колір тепер – це таблиця. Операція “!” – це вибір значення з таблиці.

Тестуємо нову граматику української:

Chess> generate_trees | l
black king
чорний король
black queen
чорна королева
white king
білий король
white queen
біла королева


Chess> parse -lang=ChessUkr "білий король"
piece White King

Chess> parse -lang=ChessUkr "біла король"
The parser failed at token 2: "\1082\1086\1088\1086\1083\1100"

Хаха, він думає що ми неправильно вжили слово король, бо після біла може стояти лише королева. Але помилку знайшов!

Рефакторинг

Накодили, потестували, тепер пора зробити код гарнішим.

Писати словник в форматі:

    White = {s = table {
        Masc => "білий";
        Fem => "біла"
    } };

Коли ми можливо захочемо додати ще слів які можливо матимуть ще середній рід і множину – доволі трудозатратно. Але ми можемо написати функцію. Точніше оператор, бо в GF функцією ми вже назвали штуку яка будує дерево.

Ще нам знадобиться згадати школу, в якій нам казали що прикметники твердої групи в чоловічому роді завжди закінчуються на -ий, а в жіночому на -а.

Тоді ми можемо додати в українську граматику такий оператор:

  -- hard adjective
  oper ha: Str -> {s: Gender => Str} = \stem -> {
      s = table {
          Masc => stem+"ий";
          Fem => stem+"а"
      }
  };

Він приймає рядок і повертає токен в якого s – це таблиця що відображає рід на написання.

І тепер ми можемо швидко додавати багато прикметників:

    Black = ha "чорн";
    White = ha "біл";
    Fast = ha "швидк";
    Defenceless = ha "беззахисн";

і навіть швидко додати множину для всіх якщо додамо закінчення рід. Правда колір певне варто перейменувати в прикметник, а рід – в граматичну категорію. Але іменування – це вже складніша проблема програмування.

Тепер додайте решту слів і маєте свій перекладач!

Written by bunyk

2 Лютого, 2020 at 19:14

Опубліковано в Кодерство

Tagged with ,

Теорія взаємодії процесів (насправді про IT-Arena)

with 8 comments

Я не дуже хотів йти на Львів ІТ арену, бо то настільки понтово що задорого. Крім того на вузькоспеціалізованих конференціях на зразок PyCon я мало що розумію, навіть якщо сам доповідаю. 🙂 Хоча, знаєте, ото щойно передивився одну доповідь – і ніби все зрозумів (а що ще краще, виявляється що викладені там ідеї я зараз використовую в Angular, хоч і забув про них). Крім того, нащо йти на платну конференцію, якщо ти навіть не встигаєш читати всі блоги і дивитись всі безкоштовні відео доповідей з інших конференцій в інтернеті?

Але я пішов, і не пожалів. Познайомився з Естер Дайсон. Вона великий фанат здорового способу життя, і інвестор в наш проект.

Пішов на доповідь про мікросервіси оцього чоловіка. Там дізнався що всі системи які містять багато взаємодіючих компонентів можна описувати наприклад пі-численням. Але так як книжки з пі-числення страшенно дорогі, ось вам безкоштовна про математичну теорію названу “Взаємодія послідовних процесів”, і написана не аби-ким, а Сером Чарлзом Ентоні Річардом Гоаром. Тепер залишилось знайти час прочитати.

А ще поміж іншим дізнався про те що програмне забезпечення це лайно (точніше завжди знав), але існує стрібна куля. Називається LangSec, коли ми вхідні параметри описуємо якоюсь формальною мовою. Чим це відрізняється від Логіки Хоара і наприклад статичної типізації з алгебраїчними типами даних – ще треба подумати.

А ще зустрів хлопців з Quintagroup, вони зразу такі “О, це ти той пітонщик з SoftServe що пише на Zope”. Я такий – той, але вже не пітонщик і не з SoftServe. 🙂 Зараз вони багато працюють над проектом Prozorro, і шукають нових людей. Тому якщо знаєте Pyramid (чи який там фреймворк у https://github.com/openprocurement), шукаєте роботу – напишіть їм.

Written by bunyk

1 Жовтня, 2016 at 23:51

Складний і нудний текст?

with one comment

Кому він потрібен?

Зі складними текстами зустрічались напевне всі – від школярів до пенсіонерів. Всі напевне читали всілякі інструкції, чи договори під час відкриття рахунку в банку (хто взагалі тих юристів вчить таке писати?), чи щось подібне. Деякі нудні тексти ніхто не читає, як от ліцензія при інсталяції програмного забезпечення. Деякі, як от інструкції – треба читати, бо інші джерела інформації про якусь нову штуковину якою вам потрібно навчитись користуватись – відсутні.

Я от коли наткнувся на довжелезну 160-ти сторінкову інструкцію з встановлення та використання одного модуля системи яку ми підтримуємо, впав в легкий транс. Інструкція з інсталяції на 5 сторінок – це ще нічого, її можна просто бездумно виконувати крок за кроком, якщо все звісно пройде добре. Але от біда якщо треба читати 15 сторінок аби зрозуміти що то взагалі за штука і нащо ми її ставимо. Я почав гуглити “How to read boring manual”, і знайшов “Boring machine operators manual” – інструкцію для оператора бурильної (нудної 🙂 ) машини.

До речі, сумний факт – нудних текстів на світі більше ніж цікавих. Тому що цікавий текст дуже й дуже важко написати, і мало хто це вміє. А не кожен виробник бурильних машин може найняти Роджера Желязни чи Ніла Стівенсона аби ті написали інструкцію. Цей текст, напевне, теж нудний, але без ваших підказок мені важко буде зробити його цікавішим.

La Touche Lennui 1893
Прочитати решту цього запису »

Written by bunyk

11 Жовтня, 2014 at 21:20

Вступ до PowerShell

with 2 comments

Зробив на роботі доповідь по PowerShell:

І не переживайте, там з восьмої хвилини в конференції нарешті настає тиша. 🙂

Презентація зроблена на основі вікіпідручника з PowerShell, який порізано на слайди і показано за допомогою deck.js (хоча про технологію якось іншим разом).
Прочитати решту цього запису »

Written by bunyk

11 Серпня, 2014 at 22:58

Опубліковано в Кодерство, Конспекти

Tagged with , ,

Люблю секторні діаграми

leave a comment »

В понеділок. Особливо, якщо зайвих секторів вона не містить. Тоді наочність очевидна:

pie_chart

А інші – якось не вражають. 🙂

Written by bunyk

23 Грудня, 2013 at 10:56

Опубліковано в Всяке, Нещоденник

Tagged with , ,