Бен Хаузър, вицепрезидент по инженерството

Изборът на език за програмиране за проект е важно решение със значителни последствия. Най-подходящият език може да намали времето и усилията, необходими за решаването на определен проблем.

Оригиналният Typefi Engine е написан на езика за програмиране Java. Оказва се, че това всъщност е било ужасно решение, тъй като Java няма директен достъп до InDesign Object Model. Преодоляхме това навремето, като написахме плъгин на C++ InDesign, за да изложим API за използване от Java програмата.

След няколко години на тази лудост решихме, че е достатъчно и пренаписахме двигателя от нулата в JavaScript – език с директен достъп до обектния модел на InDesign. Размерът на нашата програма се сви драстично и напредъкът е много по-бърз оттогава.

Сега ми е ясно, че JavaScript е най-добрият език за автоматизиране на InDesign.

Машинно обучение

Драматичният възход на машинното обучение през последните години ме кара да се чудя дали можем да приложим тези модерни техники тук, в Typefi.

Какво ще стане, ако вместо щателно ръчно изработване на шаблони на Typefi, можете просто да покажете на компютъра съществуваща публикация и той автоматично ще създаде съответстващ шаблон за вас?

Какво ще стане, ако можете да обучите компютър автоматично да маркира ново съдържание, за да съответства на корпус от вече маркирано съдържание?

Предварителният избор на правилния език за тази работа ще има драматичен ефект върху успеха на такива проекти.

Така че установявам, че си задавам въпроса: Машинно обучение — какъв език да използвам?

Кандидатите

Моите три кандидат-езика са:

  1. Октава (MATLAB). Това е езикът, избран от Андрю Нг за неговия отличен курс „Машинно обучение“ в Станфорд. Андрю заяви, че това е било внимателно обмислено решение въз основа на неговия опит, че учениците учат по-бързо този език на високо ниво.
  2. Python. Това изглежда е най-популярният избор за машинно обучение в индустрията.
  3. JavaScript. Тъй като Typefi Engine вече е написан на JavaScript, бихме могли да приложим нашите алгоритми за машинно обучение директно, ако тръгнем по този път.

Една от първите изненади, които изпитвате, когато се задълбочите в (текущото поколение) техники за машинно обучение, е, че под капака те до голяма степен са само приложена „линейна алгебра“. Нищо фантастично или трудно - само добрите стари матрици и вектори от гимназиалната математика.

Все още си спомням с известно умиление моя учебник по линейна алгебра от Математика II в моята последна година (преди повече от 25 години?!). Наричаше се Матрици и вектори и имаше флопи, зелена корица с жълтеникава хартия вътре.

Така че моят заглавен въпрос сега стана: Линейна алгебра - какъв език трябва да използвам?

Експериментът

Приложих типичен проблем с машинно обучение на всеки език.

Частите от линейната алгебра бяха направени с помощта на numpy за Python и mathjs за JavaScript.

Да видим как се получи. Ще сравним три ключови части от решението на всеки език.

1. Обработка на данни за обучение

Да приемем, че данните за обучението са заредени в променливата data. Този код разделя данните в две векторни колони и отчита броя на примерите за обучение m.

октава

X = data(:, 1); 
y = data(:, 2); 
m = length(y);

Python

X = data[:, 0:1] 
y = data[:, 1:2] 
m = len(y)

JavaScript

var X = math.subset(data, math.index(math.range(0, m), 0)); 
var y = math.subset(data, math.index(math.range(0, m), 1)); 
var m = math.size(y)[0];

Можете да видите, че Octave и Python изглеждат доста сходни. Сложната част от решението на Python беше да се използва индексиране на части (0:1 вместо 0), за да се поддържат масиви с ранг 2. Решението на JavaScript е много подробно в сравнение.

2. Функция на разходите

Сега нека разгледаме типична линейна регресионна функция на разходите за всеки език.

октава

function J = computeCost(X, y, theta) 
  h = X * theta; 
  err = h - y; 
  J = 1 / (2 * m) * err' * err; 
end

Python

def computeCost(X, y, theta): 
  h = np.dot(X, theta) 
  err = h - y 
  return 1.0 / (2.0 * m) * np.dot(err.T, err)

JavaScript

function computeCost(X, y, theta) { 
  var h = math.multiply(X, theta); 
  var err = math.subtract(h, y); 
  return 1 / (2 * m) * math.multiply(math.transpose(err), err); 
}

Решението Octave е невероятно сбито и елегантно.

Решението на Python се доближава. Използваме типа данни array на numpy за разлика от неговия тип данни matrix („както се препоръчва“). Единственият недостатък на това е, че трябва да прибегнем до извикването на функция dot(), за да извършим матрично умножение. Това донякъде замърсява нещата и е малко дърпащо.

Още веднъж решението на JavaScript е доста грозно. Всяка матрична операция изисква извикване на функция: multiply(), subtract(), transpose().

3. Градиентно спускане

октава

function theta = gradientDescent(X, y, theta, alpha, num_iters) 
  for iter = 1:num_iters 
    h = X * theta; 
    err = h - y; 
    theta_change = alpha / m * (X' * err); 
    theta = theta - theta_change; 
  end 
end

Python

def gradientDescent(X, y, theta, alpha, num_iters): 
  for i in range(0, num_iters): 
    h = np.dot(X, theta) 
    err = h - y 
    theta_change = alpha / m * np.dot(X.T, err) 
    theta = theta - theta_change 
return theta

JavaScript

function gradientDescent(X, y, theta, alpha, num_iters) { 
  for (var i = 0; i < num_iters; i++) { 
    var h = math.multiply(X, theta); 
    var err = math.subtract(h, y); 
    var theta_change = math.multiply(alpha / m, math.multiply(math.transpose(X), err)); 
    theta = math.subtract(theta, theta_change); 
  } 
  return theta; 
}

Много подобни резултати на функцията на разходите. Octave е най-елегантният. Python е ОК, освен това досадно извикване на функция dot(). И JavaScript е гореща бъркотия.

Заключение

Octave има най-простия и чист синтаксис за изпълнение на линейна алгебра. Това е чудесен избор за учене, изучаване и прототипиране на проблеми с машинното обучение.

Python е близо до Octave по сбитост. Има и други неща за него обаче. Това е масов език за програмиране с огромна потребителска база и масивна поддръжка на библиотеки — това го прави предпочитаният избор за машинно обучение в индустрията.

JavaScript е тромав избор за извършване на линейна алгебра и машинно обучение. Това не е спряло мотивираните хора да „продължат напред“ и „да го направят така или иначе“, така че вашият пробег може да варира.

За нас, тогава, това е хвърляне между Octave и Python за изграждане, обучение и фина настройка на нашите модели за машинно обучение. Ако можем, ще избягваме JavaScript.

На този етап няма начин да се използва JavaScript за Typefi Engine, така че ако някога се наложи да вградим код за машинно обучение директно в Engine, тогава ще се сблъскаме с главоблъсканица. Подозирам, че ще трябва да пренесем нашия Octave/Python код към JavaScript в последния момент.

Въпреки това, след като нашият инженерен екип започне работа в пространството за машинно обучение, е много възможно да намерим по-добро решение. Очакваме с нетърпение да го пробваме!

Относно Бен Хаузър

Бен Хаузър управлява инженерния екип на Typefi и отговаря за продуктовата архитектура на Typefi. Той има над 20 години опит в създаването на компютърен софтуер и преди това е заемал ключови позиции в сектора на ценните книжа и инвестиционното банкиране в Австралия и Лондон.

Силните страни на Бен включват гъвкаво разработване на софтуер, автоматизирано тестване на модули, машинно обучение и генетични алгоритми. Има бакалавърска степен по компютърни науки от Университета на Южен Куинсланд.

Първоначално публикувано в www.typefi.com на 22 февруари 2018 г.