понедельник, 3 октября 2011 г.

Элитные языки программирования

На этот пост меня подвигли рассуждения Майкла Фогуса (автора "The Joy of Clojure") о том, что такое "программерская элита".
Фогус очень разумно объясняет, что не язык программирования является признаком "элитности" программиста. Даже Java может казаться сложной и непонятной тем, кто ее не знает. И даже Haskell будет простым и удобным тем, кто разобрался с этим языком.
Более того, действительно крутые программисты, с которыми Фогусу довелось иметь дело, работали даже с таким позорным убожеством, как С++, и тем не менее умудрялись быть очень-очень продуктивными.
Элитными могут быть, пожалуй, только программерские задачи, решить которые может лишь очень малый процент программистов. Т.е. не мега-навыки делают программиста "элитой", а, в первую очередь, крайне сложные проблемы, которые этот программист решает. Само собой, для решения сложных задач нужно иметь уровень профессионализма намного выше среднего, а также подходящие инструменты.
Выбор языка программирования для решения нетривиальных задач -- это уже следствие сложности самих задач. Язык программирования тогда "подходит" для задачи, когда упрощает ее решение, а не тогда, когда конкретный программист знает язык лучше или любит его больше. Clojure -- один из таких языков. Его "функциональность", макросы, гомоиконный синтаксис, STM, REPL, Swank -- все это предназначено для упрощения решения именно сложных задач.
Впринципе, согласен с тем, что написал Фогус. Clojure не просто располагает к экспериментированию, а прямо навязывает такой "экспериментаторский" стиль разработки. Вспомните, как нас учили программировать в школе и универе? Задачи часто решались на бумажке, нередко рисовали блок-схемы алгоритмов. Везде, где я работал, любили UML и много "планировали" перед тем, как приступить к кодированию. В Clojure -- все наоборот. Думать заранее, конечно, тоже нужно, но конспектируется "планируемое" решение не в блок-схемах или псевдокоде, а прямо на Clojure.
Т.е. вначале может и возьмешь бумажку с ручкой, но уже через минуту думаешь: "Какого чёрта?", открываешь Emacs, Slime REPL и пробуешь все уже там. Заработало? Сохраняешь в файл с исходником. Не заработало? Пробуешь дальше, не перезагружая программу. Все вычисленные данные, все переменные -- все остается; прямо в работающей программе можно переопределить любые функции и значения. Создается ощущение, что попал не просто в debug-режим, а будто только в таком режиме и работаешь.
На днях я экспериментировал с алогритмом Support Vector Machine, хотел научить классификатор по набору прилагательных от личать хорошие отзывы от плохих. С ресурса kinopark.by скачал все комментарии ко всем фильмам и рассортировал на два кластера: положительные отзывы и отрицательные (суммарно получилось окло 18Мб чистого текста). Выбрал все прилагательные (без окончаний) из каждого комментария; каждый комментарий закодировал дескриптором, содержащим 1 или 0 для каждого прилагательного из всего множества. Получился вектор размерностью около 10000. Вот такие-то вектора и скармливал классификатору на обучение.
Все это я делал только исключительно на Clojure. Все это, включая загрузку и разбор страниц с комментариями, заняло около 300 строк кода. За все время мне ни разу не потребовался дебаггер, и даже в голову не пришло писать unit-тесты (зачем, ведь функция каждый раз "пробуется" в REPL-е перед попаданием в исходник). И я не могу себе представить инструмента, более удобного для таких задач, чем Clojure.

Использованные библиотеки:
1.clj-http;
2.clj-tagsoup;
3.libsvm.

18 комментариев:

  1. По статье согласен. Заинтересовал момент работы с Clojure. Насколько реально для вас сделать небольшой скринкаст и показать в действии робочий процес

    ОтветитьУдалить
  2. хотелось бы посмотреть исходники и попробовать реализовать тот же функционал, но на другом языке =)

    ОтветитьУдалить
  3. Кстати, да, классная идея! А то мне все никак не удается объяснить непосвященным, что ж там такого крутого в этих лиспах! :-) Попробую сделать скринкаст.

    ОтветитьУдалить
  4. unel: зачем тебе исходники, если будешь писать на другом языке? :-) Пиши с нуля, ощутишь в полной мере, насколько сложно работать в традиционном стиле (write-compile-fix-compile-run-debug) по сравнению с тем, что есть в Clojure (Emacs, Slime, Swank).

    ОтветитьУдалить
  5. Если будешь делать скринкаст, то используй, пожалуйста, утилиту, чтоб на экране отображались вводимые комманды, а то после vim очень непривычен Emacs, хотелось бы посмотреть какие комбинации использует про. Заранее спасибо))

    ОтветитьУдалить
  6. ОК, сделаю файл субтитров, на которых будут все команды Emacs.

    ОтветитьУдалить
  7. Алекс Отт тоже как-то писал об удобностях имакс и кложуры, но на скринкаст наверное пока нету времени.

    ОтветитьУдалить
  8. У меня времени свободного тоже нет совсем. Именно поэтому лучше уж я один раз запишу скринкаст и раздам всем желающим, чем каждый раз буду устраивать live-сессию для демонстрации :-)

    ОтветитьУдалить
  9. Оки, оки - заранее огромнейшее спасибо! Будем ждать

    ОтветитьУдалить
  10. Вот скринкаст работы с ClojureScript REPL (http://vimeo.com/29535884)

    Вот скринкасты про кложуру (решение небольших задачек): http://vimeo.com/channels/fulldisclojure

    А вот screencast про Clojure/SLIME, etc. http://vimeo.com/9770382

    ОтветитьУдалить
  11. Посмотрел последний скринкаст. В принципе -- да, то, что надо.
    Раз необходимость в демонстрации отпала, то свое запишу попозже, при случае.

    ОтветитьУдалить
  12. А можно немного подробней непосредственно про классификацию прилагательных? Как отбирали среди всех слов именно прилагательные, как отсекали окончания, какой в итоге получился результат?

    ОтветитьУдалить
  13. Ну тут уже не все так грандиозно. Алгоритм машины опорных векторов я вообще приплел сюда только по той причине, что на лекциях, которые я посещаю (A.I. Creates), дали задание "пощупать" SVM. На простых примерах libSVM отработал нормально, решил скормить ему задачку посложнее. На векторах большой размерности время обучения SVM уходит в бесконечность. Мое терпение лопнуло через пару часов обучения, и я не дождался результата.
    Если бы задача была не изучить SVM а действительно классифицировать отзывы, то, на мой взгляд, здесь проще взять Fuzzy Logic и посчитать, какая гипотеза более вероятная: положительная или отрицательная.
    Окончания тоже выбирал "на глаз" из учебника. Если нужно более точно определить часть речи, то предлагаю обратиться к "Методам и алгоритмам трансляции естественно-языковых запросов к базе данных в SQL-запросы" Л.В.Найхановой и И.С.Евдокимовой. Там есть таблицы окончаний и еще много интересного. Книга доступна для скачивания в какой-то из электронных библиотек.

    ОтветитьУдалить
  14. Ну, на самом деле, определение части речи зависит не только и не столько от окончаний, сколько от статистики их использования в контексте других слов. Хорошим мультиязычным инструментом для этого является TreeTagger (http://www.ims.uni-stuttgart.de/projekte/corplex/TreeTagger/). Это если будет интересно развить наработку :)

    А для SVM попробуйте линейное ядро - обычно количества прилагательных хватает, чтобы однозначно построить гиперплоскость без порождения дополнительных атрибутов. А время обучения чуть-чуть больше, чем для того же байевского классификатора (я так понимаю, вы его подразумеваете под нечёткой логикой).

    ОтветитьУдалить
  15. А есть где-нибудь толковое описание алгоритма TreeTagger?

    ОтветитьУдалить
  16. Ну, вот то, что есть прямо у них на странице:
    http://www.ims.uni-stuttgart.de/ftp/pub/corpora/tree-tagger1.pdf
    http://www.ims.uni-stuttgart.de/ftp/pub/corpora/tree-tagger2.pdf
    Из похожего есть ещё Stanford Dependency Parser (http://nlp.stanford.edu/software/lex-parser.shtml). А там уже литературы по теме сколько вагон и маленькая тележка.

    ОтветитьУдалить
  17. Слушай, оставь контакт (е-мэйл или скайп). Хотелось бы пообщаться вне блога.

    ОтветитьУдалить
  18. andrei.zhabinski на гугломыле. В скайпе я реагирую гораздо медленней.

    ОтветитьУдалить