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

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

Оригинальный Typefi Engine был написан на языке программирования Java. Оказывается, это было на самом деле ужасное решение, поскольку Java не имеет прямого доступа к объектной модели InDesign. В то время мы обошли это, написав плагин C++ InDesign, чтобы предоставить API для использования программой Java.

После нескольких лет этого безумия мы решили, что хватит, и переписали движок с нуля на JavaScript — языке с прямым доступом к объектной модели InDesign. Размер нашей программы резко сократился, и с тех пор прогресс был намного быстрее.

Теперь мне ясно, что JavaScript — лучший язык для автоматизации InDesign.

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

Резкий рост машинного обучения в последние годы заставил меня задуматься, можем ли мы применить эти современные методы здесь, в Typefi.

Что, если вместо того, чтобы тщательно создавать вручную шаблоны Typefi, вы могли бы просто показать компьютеру существующую публикацию, и он автоматически создал бы для вас соответствующий шаблон?

Что, если бы вы могли научить компьютер автоматически размечать новый контент, чтобы он соответствовал корпусу уже размеченного контента?

Предварительный выбор правильного языка для этой работы окажет огромное влияние на успех таких проектов.

Поэтому я ловлю себя на том, что задаюсь вопросом: машинное обучение — какой язык мне следует использовать?

Кандидаты

Мои три языка-кандидата:

  1. Октава (MATLAB). Это язык, который Эндрю Нг выбрал для своего отличного курса Машинное обучение в Стэнфорде. Эндрю заявил, что это было тщательно обдуманное решение, основанное на его опыте, согласно которому учащиеся быстрее учатся на этом языке высокого уровня.
  2. Питон. Кажется, это самый популярный выбор для машинного обучения в промышленности.
  3. JavaScript. Поскольку Typefi Engine уже написан на JavaScript, мы могли бы напрямую применять наши алгоритмы машинного обучения, если бы пошли по этому пути.

Один из первых сюрпризов, который вы испытываете, когда начинаете копаться в методах машинного обучения (текущего поколения), заключается в том, что под капотом они в основном просто применяют линейную алгебру. Ничего необычного или сложного — просто старые добрые матрицы и векторы из школьной математики.

Я до сих пор с нежностью вспоминаю свой учебник по линейной алгебре из Maths II на последнем курсе (более 25 лет назад?!). Он назывался Матрицы и векторы и имел мягкую зеленую обложку с желтоватой бумагой внутри.

Итак, мой главный вопрос теперь звучит так: «Линейная алгебра — какой язык мне следует использовать?»

Эксперимент

Я реализовал типичную задачу машинного обучения на каждом языке.

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

Посмотрим, как это получилось. Мы сравним три ключевые части решения на каждом языке.

1. Обработка обучающих данных

Предположим, что обучающие данные были загружены в переменную data. Этот код разделяет данные на два вектора-столбца и подсчитывает количество обучающих примеров m.

Октава

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

питон

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

питон

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

питон

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; 
}

Результаты очень похожи на функцию стоимости. Октав самый элегантный. С Python все в порядке, если не считать этого надоедливого вызова функции dot(). А JavaScript — это полный бардак.

Вывод

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

Python близок к Octave по лаконичности. Однако у него есть и другие причины. Это основной язык программирования с огромной пользовательской базой и обширной поддержкой библиотек — это делает его идеальным выбором для машинного обучения в промышленности.

JavaScript — неуклюжий выбор для выполнения линейной алгебры и машинного обучения. Это не помешало мотивированным людям идти вперед и делать это в любом случае, поэтому ваш пробег может варьироваться.

Таким образом, для нас это жеребьевка между Octave и Python для создания, обучения и тонкой настройки наших моделей машинного обучения. Мы будем избегать JavaScript, если сможем.

На данном этапе нет возможности использовать JavaScript для движка Typefi, поэтому, если когда-нибудь возникнет необходимость встроить код машинного обучения непосредственно в движок, мы столкнемся с головоломкой. Я подозреваю, что нам придется портировать наш код Octave/Python на JavaScript в самую последнюю минуту.

Однако, как только наша команда инженеров начнет работать в области машинного обучения, вполне возможно, что мы найдем лучшее решение. Мы с нетерпением ждем возможности попробовать!

О Бене Хаузере

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

Сильные стороны Бена включают гибкую разработку программного обеспечения, автоматизированное модульное тестирование, машинное обучение и генетические алгоритмы. Он имеет степень бакалавра компьютерных наук Университета Южного Квинсленда.

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