tag:blogger.com,1999:blog-240042322024-03-18T02:48:23.048-07:00Sergey Zelenin's blogСергей Зеленинhttp://www.blogger.com/profile/01667820453041864348noreply@blogger.comBlogger47125tag:blogger.com,1999:blog-24004232.post-74971708647246972952015-08-08T05:11:00.001-07:002015-08-08T05:11:56.096-07:00Мое Online обучение<div dir="ltr" style="text-align: left;" trbidi="on">
<h2 style="text-align: left;">
Курсы, которые я прошел</h2>
<div>
Однажды мне очень сильно повезло и у меня появилось 9 относительно спокойных месяцев, когда выходные и нерабочие вечера чаще проводишь дома и никаких дальних поездок, курортов и прочих развлечений даже не планируешь. Я решил потратить это время с максимальной пользой для себя, тем более что после этого 9-ти месячного периода жизнь кардинально изменилась и свободного времени заметно поубавилось. Но это уже другая история :).</div>
<div>
Я решил испопробовать массовое онлайн обучение (или <a href="http://en.wikipedia.org/wiki/Massive_open_online_course" target="_blank">MOOC</a>) что называется "по максимуму". Всего удалось пройти чуть более 10 курсов, и, после двухгодичного перерыва многое стало забываться, посему и решил немого вспомнить как это было. Сейчас пока первые пару курсов "вспомнил", потом буду докидывать еще.</div>
<div>
<h4 style="text-align: left;">
Стратегия прохождения</h4>
Я записывался на несколько курсов одновременно и не очень сопротивлялся соблазну подписываться на "все и сразу". Это же нереально круто, когда преподы из самого <a href="https://www.stanford.edu/" target="_blank">Standford</a>, <a href="http://www.princeton.edu/main/" target="_blank">Princeton</a> или <a href="http://www.berkeley.edu/" target="_blank">Berkeley</a> выкладывают свои курсы онлайн абсолютно бесплатно, причем программа курса зачастую такая же как и для студентов именитых университетов! Естественно, что пройти "все и сразу" невозможно и после первых недели-двух я определялся стоит ли продолжать слушать курс или лучше переключиться на другие. В процессе такого "естественного отбора" я обнаружил, что моя пропускная способность - это 2, максимум 3 параллельных курса, которые я планировал пройти до конца.<br />
<h4 style="text-align: left;">
Конспектирование </h4>
Оказалось очень удобно после 3х лет пролистать конспект и вспомнить детали, просмотреть слайды и видео лекций. Кстати, на экзаменах конспект тоже иногда помогал. В качестве инструмента конспектирования и использовал <a href="https://www.xmind.net/">XMind</a>. В mind map я приатачивал слайды и видео лекций, иногда вставлял свои пометки. Примерно так:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh_GmpYfUa9AYCf6kggu0fIaSUXX0b5kmcxJgq9mHMeShZhbD9rBXvCCFB41ZkrV88uEH2DkYnXm-oBvOekLrccDomFRN1cFF8UhDUMUQli_ghBQaaPPQP1v-8eqAyF84Lgeu0BIw/s1600/mind+map.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="400" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh_GmpYfUa9AYCf6kggu0fIaSUXX0b5kmcxJgq9mHMeShZhbD9rBXvCCFB41ZkrV88uEH2DkYnXm-oBvOekLrccDomFRN1cFF8UhDUMUQli_ghBQaaPPQP1v-8eqAyF84Lgeu0BIw/s400/mind+map.png" width="370" /></a></div>
<br />
<br />
Дальше я буду описывать курсы, которые прошли такую фильтрацию.<br />
В разделе "о чем курс" сугубо моя личная отсебятина, которая не всегда совпадает с официальной версией.</div>
<br />
<h4 style="text-align: left;">
Heterogeneous Parallel Programming</h4>
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEistMTfqhHMdhczUxicuInaJPWjxzbzkaAW-pi9nIrRVECunl0mXGNHLNDlnvkgwcyovQIhXSLQQrShjYMJ7o65fJOF_5J_40s1gPPUO-WTvvwsKs2Z9TmnQ9EOfKA01HCqWrrkbg/s1600/heterogenous.png" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEistMTfqhHMdhczUxicuInaJPWjxzbzkaAW-pi9nIrRVECunl0mXGNHLNDlnvkgwcyovQIhXSLQQrShjYMJ7o65fJOF_5J_40s1gPPUO-WTvvwsKs2Z9TmnQ9EOfKA01HCqWrrkbg/s1600/heterogenous.png" /></a><br />
Страница курса: <a href="https://www.coursera.org/course/hetero">https://www.coursera.org/course/hetero</a><br />
Университет: <a href="http://illinois.edu/" target="_blank">University of Illinois at Urbana-Champaign</a><br />
Сложность: 3<br />
<br />
Первый курс, который я прошел на курсере, поэтому он возглавляет мой список самых интересных курсов. Причем очень даже заслуженно :)<br />
<h4 style="text-align: left;">
О чем курс</h4>
Мы как-то очень привыкли к старым добрым однопоточным машинам, прототипом которых была <a href="https://ru.wikipedia.org/wiki/%D0%9C%D0%B0%D1%88%D0%B8%D0%BD%D0%B0_%D0%A2%D1%8C%D1%8E%D1%80%D0%B8%D0%BD%D0%B3%D0%B0">машина Тьюринга</a>. Тьюринговская лента (или "нить" - thread) всего одна и поэтому нет проблем с переплетениями нескольких лент во время работы. По сути первые компьютеры и были такими себе одноленточными реализациями машины Тьюринга. Потом, в процессе эволюции, возникла необходимость делить процессорное время между несколькими операторами, для чего увеличивали мощьность процессорных устройств, повышали частоту, появились RISC процессоры, конвейерная обработка команд, процессорный кеш и много-много других усовершенствований, о которых 99.99% людей, пользующихся компьютерами даже не догадываются. В общем, однопоточные процессоры делались все сильнее, быстрее, совершеннее и отлично подходят для задач, где надо выполнять действия последовательно. Но что если такой подход неэффективен для ряда задач? Что если заменить несколько "одноленточных" суперпроцессоров на несколько тысяч не таких супер, но все же реально параллельных процессоров? Какие задачи тогда можно решать быстрее?<br />
<b>Задача из курса.</b><br />
Предположим есть очень длинная палка колбасы (несколько км длиной), за которой стоит длинная очередь в сотни (а может и тысячи) людей. Каждый человек говорит какой длины кусок ему надо отрезать.<br />
Традиционное последовательное решение было бы таким: каждый по очереди подходит и ему отрезают кусок такой длины, какой скажет. Понятно, что для очереди длиной, например, в миллиард, мы бы занимались этим очень долго.<br />
Можно ли сразу у всех спросить длину отрезка, нанести разметку и, имея несколько тысяч ножей, сразу разрезать так как надо?<br />
В этом курсе мы практиковались в решении подобного рода задач с использованием GPU, узнали много нового о многопроцессорной архитектуре. Задания надо было реализовывать на C с использованием <a href="http://en.wikipedia.org/wiki/CUDA" target="_blank">CUDA</a> SDK. У кого не было возможности отлаживать код локально (т.к. надо GPU от NVIDIA), те могли загружать код прямо из браузерного окна редактирования и дебажить способом просмотра логов. Форум был очень активен и там часто можно было найти подсказки на решения проблем. Очень жаль, что этот курс был всего один раз и непонятно будет ли повторяться, но есть <a href="https://www.udacity.com/course/cs344" target="_blank">похожий курс на Udacity</a>.<br />
<br />
<br />
<br />
<h4>
Discrete Optimization </h4>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh336yxiutE5WInfQAr1JUm3aQZdedYoTcFSPziDB-5_gcDPwWPqSyR26umbItk3yVsBVXMZ05xPaSEFGUaNsLe0xsHNHPH2ls6r7gGzwlIdf9B66XB7nA5AdD540_nmfhkvxLSbA/s1600/discrete-optimization.png" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh336yxiutE5WInfQAr1JUm3aQZdedYoTcFSPziDB-5_gcDPwWPqSyR26umbItk3yVsBVXMZ05xPaSEFGUaNsLe0xsHNHPH2ls6r7gGzwlIdf9B66XB7nA5AdD540_nmfhkvxLSbA/s1600/discrete-optimization.png" /></a></div>
<br />
Страница курса: <a href="https://www.coursera.org/course/optimization" target="_blank">https://www.coursera.org/course/optimization</a><br />
<br />
Университет: <a href="http://www.unimelb.edu.au/" target="_blank">The University of Melbourne</a><br />
Сложность: 4.9<br />
<br />
Как только я посмотрел интродактори видео я понял, что будет очень интересно и не ошибся. Профессор оказался еще тем гиком. Я вот даже не поленился добавить это интродактари видео:<br />
<div class="separator" style="clear: both; text-align: center;">
<iframe allowfullscreen='allowfullscreen' webkitallowfullscreen='webkitallowfullscreen' mozallowfullscreen='mozallowfullscreen' width='320' height='266' src='https://www.blogger.com/video.g?token=AD6v5dy1NEPnr4nJ1RJGqcgHoMGLafMvCmbvXAOoOYWX0sYYsJPIbb5hsGt7fwpA2Uxli3aa-zmShMnSDm0' class='b-hbp-video b-uploaded' frameborder='0'></iframe></div>
<br />
<div>
<br /></div>
<h4>
О чем курс</h4>
<div>
Сколько надо цветов, чтобы раскрасить карту так, чтобы соседние государства не были окрашены в одинаковый цвет? А если государств 1000 или 10000? </div>
<div>
По какому маршруту должна ездить фура чтобы развезти продукты по нескольким магазинам и потратить как можно меньше горючего/времени? А если магазинов 1000, а фур всего 10?</div>
<div>
Классическая задача комивояжера - как найти оптимальное решение для 10 городов? А для 100? А для 1000? А вообще возможно ли его найти за относительно небольшое время? А если нет, то как можно максимально приблизиться к оптимальному решению?</div>
<div>
Вот такие задачи мы учились решать в рамках курса. Оказывается есть довольно много инструментов, которые успешно применяются для задач оптимизации. Это и <a href="http://en.wikipedia.org/wiki/Constraint_programming">constraint programming</a> и <a href="https://ru.wikipedia.org/wiki/%D0%9B%D0%BE%D0%BA%D0%B0%D0%BB%D1%8C%D0%BD%D1%8B%D0%B9_%D0%BF%D0%BE%D0%B8%D1%81%D0%BA_(%D0%BE%D0%BF%D1%82%D0%B8%D0%BC%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D1%8F)">алгоритмы локального поиска</a> (local search), и <a href="https://ru.wikipedia.org/wiki/%D0%9B%D0%B8%D0%BD%D0%B5%D0%B9%D0%BD%D0%BE%D0%B5_%D0%BF%D1%80%D0%BE%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5">линейное программирование</a> (linear programming), и целочисленное программирование. (integer programming)<br />
Практические задачи были разделены на 5 уровней сложности. Для каждого уровня свой набор входных данных. Например, для задачи комивояжера для уровня 1 было до 10 городов, для 2го от 10 до 50, для 3-го от 100 до 200, 4й -500 - 1000 и для 5-го более 50тыс. Если первые пару уровней обычно можно было решить простым перебором, то начиная с 3го все становилось очень интересно. Естественно, чтобы получить сертификат нужно было решить все задачи как минимум 3го уровня сложности, а для сертификата с отличием (with distinction) - 4го и 5го.<br />
<b>Попробовал тулы</b><br />
- SCIP optimizer <a href="http://scip.zib.de/">http://scip.zib.de/</a>. Простой и быстрый оптимизатор, оптимизирует задачи с помощью constraint programming и linear-integer programming. Поддерживает стандарт языка <a href="http://zimpl.zib.de/">ZIMPL</a>. Очень хорошо ломает мозг.<br />
- GLPK solver: <a href="http://www.gnu.org/software/glpk/">http://www.gnu.org/software/glpk/</a>. Изначально я пробовал его для некоторых задач, но потом перешел на SCIP, не помню почему<br />
- Много кодил на Java :). Задачу комивояжера, например, оптимизировал с помощью алгоритма <a href="https://en.wikipedia.org/wiki/Travelling_salesman_problem">K-opt</a> (local search) и <a href="https://en.wikipedia.org/wiki/Simulated_annealing">Simulated annealing</a> для поиска глобального минимума.<br />
<br />
<br /></div>
</div>
Сергей Зеленинhttp://www.blogger.com/profile/01667820453041864348noreply@blogger.com11tag:blogger.com,1999:blog-24004232.post-58878090969934313812015-04-28T21:50:00.000-07:002015-08-08T05:33:23.070-07:00Парадокс дней рождения<div dir="ltr" style="text-align: left;" trbidi="on">
<div class="separator" style="clear: both; text-align: center;">
</div>
<br />
<br />
<br />
<br />
Недавно, проходя мимо доски с именинниками вспомнил про так называемый "парадоск дней рождения". Подобно <a href="http://szelenin.blogspot.com/2014/01/blog-post.html" target="_blank">парадоксу Монти Холла</a>, этот "парадокс" на самом возникает из-за неправильного решения, которое подсказывает интуиция подзабывших теорию вероятности людей.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgfIFaLsZWTJUqlRZ92El1oU2rysfQXwsYDeuMnefzODxXpApJnENVZuCqaHBcfPfHSDoCoaXjaLfPrUexrSwmybRwCv-253ev1BndbB6WIGxS7vR4L-4ocw9Xvd0I__k1JsqNU3A/s1600/tort.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="171" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgfIFaLsZWTJUqlRZ92El1oU2rysfQXwsYDeuMnefzODxXpApJnENVZuCqaHBcfPfHSDoCoaXjaLfPrUexrSwmybRwCv-253ev1BndbB6WIGxS7vR4L-4ocw9Xvd0I__k1JsqNU3A/s200/tort.png" width="200" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<h3 style="text-align: left;">
Задача</h3>
Допустим в компании работает 25 человек. Какая вероятность того, что у двух людей из этой компании день рождения в один и тот же день?<br />
<h4 style="text-align: left;">
</h4>
<h4 style="text-align: left;">
Интуитивное решение</h4>
Ход мыслей может быть примерно такой: 25 человек, у каждого день рождения 1 раз в 365 дней. Поскольку надо 2 совпадения, то вероятности перемножаются, и, поскольку это надо повторить для всех, то еще умножить на 25:<br />
\(\frac{25}{365} * \frac{25}{365} * 25\), то есть \(\frac{25}{365}\).<br />
<br />
<h4 style="text-align: left;">
</h4>
<h4 style="text-align: left;">
Правильное решение </h4>
Правильное решение и математическое доказательство хорошо описаны в вики, см. <a href="https://ru.wikipedia.org/wiki/%D0%9F%D0%B0%D1%80%D0%B0%D0%B4%D0%BE%D0%BA%D1%81_%D0%B4%D0%BD%D0%B5%D0%B9_%D1%80%D0%BE%D0%B6%D0%B4%D0%B5%D0%BD%D0%B8%D1%8F" target="_blank">Парадокс дней рождения</a>, его я здесь повторять не буду. Я же напишу програмку, которая покажет кто прав - интуиция или теория вероятности.<br />
<h3 style="text-align: left;">
Пишу код</h3>
Для подобных экспериментов, результаты которых можно сразу же опубликовать мне очень нравится Python notebook. Его можно установить себе на компьютер, но я воспользуюсь бесплатным сервисом от <a href="https://www.wakari.io/">https://www.wakari.io</a><br />
<br />
В wakari.io создал новый файл, вставил ячейку (cell) типа Code и в ней заимпортил NumPy и вывел номер версии:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgsu0cMo1E5FTPn4wGG9U0HhWzlPYdSOWl2EMILufqQSbvFpoAcZKv2oVZ3ypOrbE60n1-9yFnWaIPkihyphenhyphenBJyLzw6g8UKXjkNHQFXV0dOjbbTyf-4l4BFlAS9SwdQ95C8efGouSIg/s1600/1.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="72" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgsu0cMo1E5FTPn4wGG9U0HhWzlPYdSOWl2EMILufqQSbvFpoAcZKv2oVZ3ypOrbE60n1-9yFnWaIPkihyphenhyphenBJyLzw6g8UKXjkNHQFXV0dOjbbTyf-4l4BFlAS9SwdQ95C8efGouSIg/s1600/1.PNG" width="640" /></a></div>
<br />
Затем создал функцию, которая создает массив размером n, каждый элемент которого случайное число от 1 до 365:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh5wolwrk8zNDXtSMBPHmdSc3xUPpZLe9PsHSXW8LGaRTphBCBJ-NrloZUSRLgbs9cMD41dFNqraz9zSlcr63U2IUbPRXsd2gDsKdDDn7pJpbUKMuIA7qoxWIgnoVMs0QB_h4GKiQ/s1600/2.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="48" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh5wolwrk8zNDXtSMBPHmdSc3xUPpZLe9PsHSXW8LGaRTphBCBJ-NrloZUSRLgbs9cMD41dFNqraz9zSlcr63U2IUbPRXsd2gDsKdDDn7pJpbUKMuIA7qoxWIgnoVMs0QB_h4GKiQ/s1600/2.PNG" width="640" /></a></div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
Эта функция проверяет есть ли в массиве birthdays элементы, которые повторяются больше чем 1 раз:</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi_E_fw_h5LeRg40a7PA5ybSX96X-s4L14exj209ZqqyLlL0n60sMNXGki-fVaVudu7vv_NhF0IJvjLyqOIp_Zra6AgqfHHJfrATtOgNnC4zK3iSt5QG_SBgptvFs-EKt_pZ_Wm4A/s1600/3.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="84" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi_E_fw_h5LeRg40a7PA5ybSX96X-s4L14exj209ZqqyLlL0n60sMNXGki-fVaVudu7vv_NhF0IJvjLyqOIp_Zra6AgqfHHJfrATtOgNnC4zK3iSt5QG_SBgptvFs-EKt_pZ_Wm4A/s1600/3.PNG" width="640" /></a></div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
Запускает эксперимент несколько раз и возвращает количество успешных экспериментов (количество совпадений > 1)</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjTRKXbmHYd7C3kLMVpmgPa1pmjmvZ7DJq-vS54bJAQvBKFR6dmMddM6O6Gt2-ARjUF8E2do0FaEw0uxlbiT3J1Rf5fy4Ii0LK6HS3PpCTbER3DARFQXtsiSBZSvNS7MpRIoKg5dw/s1600/4.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="82" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjTRKXbmHYd7C3kLMVpmgPa1pmjmvZ7DJq-vS54bJAQvBKFR6dmMddM6O6Gt2-ARjUF8E2do0FaEw0uxlbiT3J1Rf5fy4Ii0LK6HS3PpCTbER3DARFQXtsiSBZSvNS7MpRIoKg5dw/s1600/4.PNG" width="640" /></a></div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
Запускаю эксперимент и получаю вероятность того, что в группе из 23 человек хотя бы у двух совпадут дни рождения:</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgahSnviP2tWwyniThE8yHuKbhTZ8YexfjNvKlrdSVZu8HxS8qpfcGqg_OKqpz8WR5oPBtfn5lh49YPm52gU75OjQOepee1xQnqbS72Ga2F9sCtqvrEyZ7c-bczbyVYMZplve9Ezw/s1600/5.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="48" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgahSnviP2tWwyniThE8yHuKbhTZ8YexfjNvKlrdSVZu8HxS8qpfcGqg_OKqpz8WR5oPBtfn5lh49YPm52gU75OjQOepee1xQnqbS72Ga2F9sCtqvrEyZ7c-bczbyVYMZplve9Ezw/s1600/5.PNG" width="640" /></a></div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
Вероятность более 50%, то есть интуитивное решение не является правильным.</div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<a href="http://wakari.io/">Wakari.io</a> очень прикольный сервис, он позволяет публиковать свои поделки. Этот код целиком можно глянуть тут: <a href="https://www.wakari.io/sharing/bundle/szelenin/birthdays">https://www.wakari.io/sharing/bundle/szelenin/birthdays</a><br />
<br />
<h4 style="text-align: left;">
Ссылки на использованные материалы</h4>
- <a href="https://www.wakari.io/">Wakari.io</a> - online сервис для анализа данных на Python.<br />
- <a href="https://ru.wikipedia.org/wiki/%D0%9F%D0%B0%D1%80%D0%B0%D0%B4%D0%BE%D0%BA%D1%81_%D0%B4%D0%BD%D0%B5%D0%B9_%D1%80%D0%BE%D0%B6%D0%B4%D0%B5%D0%BD%D0%B8%D1%8F" target="_blank">Парадокс дней рождения</a> на википедиит<br />
- Мой код <a href="https://www.wakari.io/sharing/bundle/szelenin/birthdays">https://www.wakari.io/sharing/bundle/szelenin/birthdays</a><br />
<br />
<br /></div>
Сергей Зеленинhttp://www.blogger.com/profile/01667820453041864348noreply@blogger.com1tag:blogger.com,1999:blog-24004232.post-31159275082617856712015-04-17T12:35:00.003-07:002015-06-06T13:50:00.513-07:00Softmax Regression Pylearn2<div dir="ltr" style="text-align: left;" trbidi="on">
На установленном ранее pylearn2 пробую пройти туториал. Начну с softmax регрессии.<br />
ссылка на туториал:<br />
<a href="http://nbviewer.ipython.org/github/lisa-lab/pylearn2/blob/master/pylearn2/scripts/tutorials/softmax_regression/softmax_regression.ipynb">http://nbviewer.ipython.org/github/lisa-lab/pylearn2/blob/master/pylearn2/scripts/tutorials/softmax_regression/softmax_regression.ipynb</a><br />
<br />
<h3 style="text-align: left;">
Пытаюсь установить ipython</h3>
В принципе туториал можно запустить локально. Надо только ipython установить:<br />
Захожу на запущенную Vagrant виртуалку и запускаю команду<br />
<br />
<span style="font-family: Courier New, Courier, monospace;">sudo pip install "ipython[all]"</span><br />
<br />
Нельзя инсталить без [all]:<br />
<span style="font-family: Courier New, Courier, monospace;">sudo pip install ipython</span><br />
и вот почему: <a href="http://stackoverflow.com/questions/24995438/pyzmq-missing-when-running-ipython-notebook">http://stackoverflow.com/questions/24995438/pyzmq-missing-when-running-ipython-notebook</a><br />
<br />
дальше перехожу в папку с туториалом:<br />
<span style="font-family: Courier New, Courier, monospace;">cd /home/vagrant/pylearn2/pylearn2/scripts/tutorials/</span><br />
<br />
и запускаю ipython'овский notebook:<br />
<span style="font-family: Courier New, Courier, monospace;">sudo ipython notebook</span><br />
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
ноутбук запустился в аутентичном текстовом браузере w3m :)<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEizXf_UcUtDDAEG0Yjqi6_WQFZ0yhwXtSLrw37i7hkJaoOJovTH1ZEjcidAEoAPNuWnv01xj2f5Y3dqa1qa8ZTNHl6FMVRXgVXAa330COnKmyQKLxh1HHcRgsY0yKlVR2r5aSjp0Q/s1600/1-textbrowser.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="398" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEizXf_UcUtDDAEG0Yjqi6_WQFZ0yhwXtSLrw37i7hkJaoOJovTH1ZEjcidAEoAPNuWnv01xj2f5Y3dqa1qa8ZTNHl6FMVRXgVXAa330COnKmyQKLxh1HHcRgsY0yKlVR2r5aSjp0Q/s1600/1-textbrowser.PNG" width="640" /></a></div>
<br />
Немного побаловался, но в хроме все-таки удобнее было бы работать :). Для этого надо "перебросить" порт, который внутри виртуальной машины "наружу", чтобы я мог с хост системы зайти браузером.<br />
-Смотрю какой порт использовал ipython :<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhbd6El9nLLQTyWeXOWqrGC7mlDhxtKFx269070Hx5mxlvSjYXNLZlnqz4PNbXekMK_9tnf6RBbTvIFN7y56E6hqxt76z3sHR7Ez6AkkmzFit0flg-AJflZpRaNppW3GHPnlTC36A/s1600/2-port.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhbd6El9nLLQTyWeXOWqrGC7mlDhxtKFx269070Hx5mxlvSjYXNLZlnqz4PNbXekMK_9tnf6RBbTvIFN7y56E6hqxt76z3sHR7Ez6AkkmzFit0flg-AJflZpRaNppW3GHPnlTC36A/s1600/2-port.PNG" /></a></div>
Добавляю в "forwarded_port" в Vagrant файл (сразу за config.vm.box):<br />
config.vm.network "forwarded_port", guest: 8888, host: 8888<br />
И перегружаю vagrant (команда vagrant reload):<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgK8AUFH3b92jkenM3Xri1acU93A-e-sEBWrMnbi0TKGS86oKx_B_uIGTATbQzx67vZLt9eLsUQvgI9VdGzSRHUZOFOeBois-56glf84jjeFGWeHfZxFjkx6QKCf3Tg1Lx1lB4fGA/s1600/3-forwarded.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="220" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgK8AUFH3b92jkenM3Xri1acU93A-e-sEBWrMnbi0TKGS86oKx_B_uIGTATbQzx67vZLt9eLsUQvgI9VdGzSRHUZOFOeBois-56glf84jjeFGWeHfZxFjkx6QKCf3Tg1Lx1lB4fGA/s1600/3-forwarded.PNG" width="400" /></a></div>
<br />
не заработало... :(<br />
<b><br /></b>
<h4 style="text-align: left;">
<b>Update</b></h4>
Проблема решилась после того как сделал uninstall ipython (тот, который не [all])<br />
<span style="font-family: Courier New, Courier, monospace;">sudo pip uninstall ipython</span><b> </b><br />
<br />
и заново проинсталил ipython[all]:<br />
<span style="font-family: Courier New, Courier, monospace;">sudo pip install ipython[all]</span><br />
<h3 style="text-align: left;">
Оставлю пока ipython, делаю в отдельном файле train.py.</h3>
<br />
<pre style="background-color: #f7f7f7; border-radius: 2px; border: none; box-sizing: border-box; color: #333333; font-size: 14px; line-height: 17.0000591278076px; overflow: auto; padding: 0px; word-break: break-all; word-wrap: break-word;"><span class="kn" style="box-sizing: border-box; color: green; font-weight: 700;">import</span> <span class="nn" style="box-sizing: border-box; color: blue; font-weight: 700;">os</span>
<span class="kn" style="box-sizing: border-box; color: green; font-weight: 700;">import</span> <span class="nn" style="box-sizing: border-box; color: blue; font-weight: 700;">pylearn2</span>
<span class="n" style="box-sizing: border-box;">dirname</span> <span class="o" style="box-sizing: border-box; color: #666666;">=</span> <span class="n" style="box-sizing: border-box;">os</span><span class="o" style="box-sizing: border-box; color: #666666;">.</span><span class="n" style="box-sizing: border-box;">path</span><span class="o" style="box-sizing: border-box; color: #666666;">.</span><span class="n" style="box-sizing: border-box;">abspath</span><span class="p" style="box-sizing: border-box;">(</span><span class="s" style="box-sizing: border-box; color: #ba2121;">'/vagrant/ml_invsetigaiton/pylearn2_softmax'</span><span class="p" style="box-sizing: border-box;">)</span>
<span class="k" style="box-sizing: border-box; color: green; font-weight: 700;">with</span> <span class="nb" style="box-sizing: border-box; color: green;">open</span><span class="p" style="box-sizing: border-box;">(</span><span class="n" style="box-sizing: border-box;">os</span><span class="o" style="box-sizing: border-box; color: #666666;">.</span><span class="n" style="box-sizing: border-box;">path</span><span class="o" style="box-sizing: border-box; color: #666666;">.</span><span class="n" style="box-sizing: border-box;">join</span><span class="p" style="box-sizing: border-box;">(</span><span class="n" style="box-sizing: border-box;">dirname</span><span class="p" style="box-sizing: border-box;">,</span> <span class="s" style="box-sizing: border-box; color: #ba2121;">'sr_dataset.yaml'</span><span class="p" style="box-sizing: border-box;">),</span> <span class="s" style="box-sizing: border-box; color: #ba2121;">'r'</span><span class="p" style="box-sizing: border-box;">)</span> <span class="k" style="box-sizing: border-box; color: green; font-weight: 700;">as</span> <span class="n" style="box-sizing: border-box;">f</span><span class="p" style="box-sizing: border-box;">:</span>
<span class="n" style="box-sizing: border-box;">dataset</span> <span class="o" style="box-sizing: border-box; color: #666666;">=</span> <span class="n" style="box-sizing: border-box;">f</span><span class="o" style="box-sizing: border-box; color: #666666;">.</span><span class="n" style="box-sizing: border-box;">read</span><span class="p" style="box-sizing: border-box;">()</span>
<span class="n" style="box-sizing: border-box;">hyper_params</span> <span class="o" style="box-sizing: border-box; color: #666666;">=</span> <span class="p" style="box-sizing: border-box;">{</span><span class="s" style="box-sizing: border-box; color: #ba2121;">'train_stop'</span> <span class="p" style="box-sizing: border-box;">:</span> <span class="mi" style="box-sizing: border-box; color: #666666;">50000</span><span class="p" style="box-sizing: border-box;">}</span>
<span class="n" style="box-sizing: border-box;">dataset</span> <span class="o" style="box-sizing: border-box; color: #666666;">=</span> <span class="n" style="box-sizing: border-box;">dataset</span> <span class="o" style="box-sizing: border-box; color: #666666;">%</span> <span class="p" style="box-sizing: border-box;">(</span><span class="n" style="box-sizing: border-box;">hyper_params</span><span class="p" style="box-sizing: border-box;">)</span>
<span class="nb" style="box-sizing: border-box; color: green;">print</span> <span class="n" style="box-sizing: border-box;">dataset</span></pre>
<br />
<br />
Результат:<br />
<br />
<span style="font-family: Courier New, Courier, monospace;">vagrant@vagrant-ubuntu-trusty-64:/vagrant/ml_invsetigaiton/pylearn2_softmax$ sudo python train.py</span><br />
<span style="font-family: Courier New, Courier, monospace;">OpenBLAS : Your OS does not support AVX instructions. OpenBLAS is using Nehalem kernels as a fallback, which may give poorer performance.</span><br />
<span style="font-family: Courier New, Courier, monospace;">!obj:pylearn2.datasets.mnist.MNIST {</span><br />
<span style="font-family: Courier New, Courier, monospace;"> which_set: 'train',</span><br />
<span style="font-family: Courier New, Courier, monospace;"> start: 0,</span><br />
<span style="font-family: Courier New, Courier, monospace;"> stop: 50000</span><br />
<span style="font-family: Courier New, Courier, monospace;">}</span><br />
<div>
<br /></div>
<div>
Что это было?</div>
<div>
грубо говоря прочел YAML файл, в котором было описание датасета из набора MNIST. Этот набор поставляется с pylearn2. Всего в наборе 60000 записей, но я передал гиперпараметр, который обозначает, что стопнуть обучение нужно на 50000</div>
<div>
<br /></div>
<div>
короче, скопировал я все степы, запустил, но выдалась такая ошибка:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhSksfMSOCbeLf3nzSmD1C3MBVAy3Jjle_aZt8kkJISUNkgWnXQ4yG0C5xqCy4PNUi48JuA6YmENtD9UH41XtUtUd-O12lbeknY3zP8UTfh1GLr8GvvPJDCwuTfkrDhdW7YCIjA5g/s1600/6-nosuchfile.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhSksfMSOCbeLf3nzSmD1C3MBVAy3Jjle_aZt8kkJISUNkgWnXQ4yG0C5xqCy4PNUi48JuA6YmENtD9UH41XtUtUd-O12lbeknY3zP8UTfh1GLr8GvvPJDCwuTfkrDhdW7YCIjA5g/s1600/6-nosuchfile.PNG" /></a></div>
<br />
оказывается надо самому скачать MNIST датасет. Хорошо, что pylearn2 предоставляет для этого скрипт. в папке ~/pylearn2/pylearn2/scripts/datasets запускаю python download_mnist.py:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhP3qdR7tiQLeDTTsJhaNcGSqX389buS7q49diaIyCxL92lUfOLi6-YduARcGbzQaG85pcv9dcp5bdoEfs3WmRwALDRkGasEZdbSzA6AAGzAg3uLD4J-bmSveTPpROr625wGafhbA/s1600/7-downloadmnist.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhP3qdR7tiQLeDTTsJhaNcGSqX389buS7q49diaIyCxL92lUfOLi6-YduARcGbzQaG85pcv9dcp5bdoEfs3WmRwALDRkGasEZdbSzA6AAGzAg3uLD4J-bmSveTPpROr625wGafhbA/s1600/7-downloadmnist.PNG" /></a></div>
<br />
<br />
Запускаю опять, модель в процессе обучения печатает как хорошо она обучается:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiSJm1Iu1DKA2TOaMN8JncdXlRF19BThylufhGT2KD3hSZ2Qbo3SR5Zk2sl_-pj4CyT35dzYj89RGJx4OJmXylGmXmCIgY-hdDSxD8Da-ciqsm_KHI_CKW8btLpyUJi5l8GpT8uqA/s1600/8-learnin.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiSJm1Iu1DKA2TOaMN8JncdXlRF19BThylufhGT2KD3hSZ2Qbo3SR5Zk2sl_-pj4CyT35dzYj89RGJx4OJmXylGmXmCIgY-hdDSxD8Da-ciqsm_KHI_CKW8btLpyUJi5l8GpT8uqA/s1600/8-learnin.PNG" /></a></div>
<br />
<br />
Теперь чтобы расшифровать результаты запускаю скрипт print_monitor.py, который находится в /home/vagrant/pylearn2/pylearn2/scripts и передаю ему сгенерированный файл с моделью:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjJLmO-LUZGUMOymYquuaAABe4FuQ9AraLBZL_2ogLelVAJ-TimXAmyIq4uXPdzpeXUon_HfYRvGC-OijyQQM6H9-CBuIFeTN7X490BvyUo6Pn04WEoGh7BjVZD26mh6j7BgBoeug/s1600/9-print_monitor.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjJLmO-LUZGUMOymYquuaAABe4FuQ9AraLBZL_2ogLelVAJ-TimXAmyIq4uXPdzpeXUon_HfYRvGC-OijyQQM6H9-CBuIFeTN7X490BvyUo6Pn04WEoGh7BjVZD26mh6j7BgBoeug/s1600/9-print_monitor.PNG" /></a></div>
<br />
<br />
Процент неправильно классифирированых записей из тестового набора 7.68 (test_y_misclass: 0.0768)<br />
<br />
<br /></div>
</div>
Сергей Зеленинhttp://www.blogger.com/profile/01667820453041864348noreply@blogger.com0tag:blogger.com,1999:blog-24004232.post-55365887364755025752015-03-29T14:29:00.000-07:002015-03-29T14:29:31.637-07:00Ставлю IntelliJ Idea + Oracle JDK на Ubuntu в Vagrant (VirtualBox)<div dir="ltr" style="text-align: left;" trbidi="on">
<div>
<h3 style="text-align: left;">
Изучаю Pylearn2 в Идее</h3>
<br />
В один прекрасный момент мне надоели эти юниксовые редакторы текста для редактирования и отладки исходников во время прохождения туториала по Pylearn2. Хочу поставить графическую оболочку и там редактировать питоновские файлы. Может даже и задебажить или пробраузить код.</div>
<div>
<br /></div>
<div>
<h4 style="text-align: left;">
Ставлю графическую оболочку xfce4.</h4>
</div>
<div>
Загуглил и пошел инсталлить по первой же ссылке:<br />
<a href="http://stackoverflow.com/questions/18878117/using-vagrant-to-run-virtual-machines-with-desktop-environment">http://stackoverflow.com/questions/18878117/using-vagrant-to-run-virtual-machines-with-desktop-environment</a></div>
<div>
Выполнил все инструкции, перезапустил виртуалку (vagrant reload). В появившемся окне VirtualBox'a залогинился как vagrant/vagrant.<br />
<br /></div>
<div>
<h4 style="text-align: left;">
Ставлю Oracle JDK 8</h4>
<div>
Опять же загуглил и нашел чудовую пошаговую инструкцию как инсталлить ораковую ждк на убунту (14.10)</div>
<a href="http://www.wikihow.com/Install-Oracle-Java-JDK-on-Ubuntu-Linux">http://www.wikihow.com/Install-Oracle-Java-JDK-on-Ubuntu-Linux</a></div>
<div>
По шагам все сделал почти по инструкции за исключением путей</div>
<div>
Перезапускаю</div>
<div>
<br /></div>
<div>
<h4 style="text-align: left;">
Ставлю идею</h4>
<a href="http://tutorialforlinux.com/2013/02/24/linux-debian-wheezy-xfce-howto-install-gettingstarted-latest-intellij-idea-community-edition/">http://tutorialforlinux.com/2013/02/24/linux-debian-wheezy-xfce-howto-install-gettingstarted-latest-intellij-idea-community-edition/</a></div>
<div>
Тоже по инструкции.</div>
<div>
<br /></div>
<div>
Из UXTerm (можно и из XTerm) набираю idea <Enter>: </div>
<div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiOL_LpXQFnj2A1sPiEe0QwHYRg7SC3xld5rfhfS0hLrpARABp6h1A00-QcoNF2VxmB9j5OXVxo1b75vOtu31tDQoph6uo1d2Gq8e4NL32AVlIUWwLKYylnFUjwbeufM9_7S3WnSQ/s1600/4-uxterm.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiOL_LpXQFnj2A1sPiEe0QwHYRg7SC3xld5rfhfS0hLrpARABp6h1A00-QcoNF2VxmB9j5OXVxo1b75vOtu31tDQoph6uo1d2Gq8e4NL32AVlIUWwLKYylnFUjwbeufM9_7S3WnSQ/s1600/4-uxterm.PNG" /></a></div>
<br /></div>
<div>
Вау, красота то какая!</div>
<div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiKvcr7sEp5i_ikyiJSPPWvvX_4AMFyWzhA22yKDePAF6Q70BlTWvLz0rN_muJ7CTiAyND5codb2mNkH6KZYTD7H7Q0jCYy7T_m8sxyihMcxc6tygJHt3Gruu6_HiMjFj11k6fLHQ/s1600/5-krasota.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiKvcr7sEp5i_ikyiJSPPWvvX_4AMFyWzhA22yKDePAF6Q70BlTWvLz0rN_muJ7CTiAyND5codb2mNkH6KZYTD7H7Q0jCYy7T_m8sxyihMcxc6tygJHt3Gruu6_HiMjFj11k6fLHQ/s1600/5-krasota.PNG" height="514" width="640" /></a></div>
<br /></div>
<div>
<br /></div>
<br />
<div>
</div>
<br />
<div style="-webkit-text-stroke-width: 0px; color: black; font-family: 'Times New Roman'; font-size: medium; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; line-height: normal; orphans: auto; text-align: left; text-indent: 0px; text-transform: none; white-space: normal; widows: 1; word-spacing: 0px;">
<div style="margin: 0px;">
Все, теперь ставлю Python plugin и дальше буду работать в нормальной IDE :)</div>
</div>
</div>
Сергей Зеленинhttp://www.blogger.com/profile/01667820453041864348noreply@blogger.com0tag:blogger.com,1999:blog-24004232.post-30325776215958317932015-03-28T12:57:00.001-07:002015-03-28T12:58:06.753-07:00Установка Pylearn2 с помощью Vagrant и puppet<div dir="ltr" style="text-align: left;" trbidi="on">
Добрался я наконец-то до компа, но не так чтобы для работы, а подтянуть свои практические знания по Machine learning на python. В последнее время в тренде всякие диплернинги (<a href="http://deeplearning.net/">http://deeplearning.net</a>), особенный пинок в этом направлении дала возможность обучения нейронных сетей с использованием GPU, что на порядки увеличивает процесс обучения.<br />
<br />
Далее я буду пытаться утановить Pylearn2 со встроенной поддержкой Theano. Забегая вперед скажу, что сначала я описываю процесс наступления на грабли, а в конце привел исправленный процесс. Так что нетерпеливым можно сразу туда :). <br />
<br />
<h3 style="text-align: left;">
Итак, начнем.</h3>
Я взял инструкцию по установке Pylearn2 отсюда: <a href="http://deeplearning.net/software/pylearn2/">http://deeplearning.net/software/pylearn2/</a><br />
<br />
<h4 style="text-align: left;">
1. Устанавливаю Vagrant</h4>
Актуальная версия 1.7.2<br />
<a href="https://www.vagrantup.com/downloads.html">https://www.vagrantup.com/downloads.html</a><br />
скачал, перезапустил комп<br />
<h4 style="text-align: left;">
2. Устанавливаю VirtualBox</h4>
<a href="https://www.virtualbox.org/wiki/Downloads">https://www.virtualbox.org/wiki/Downloads</a><br />
и VirtualBox Extension Pack (там же)<br />
<h4 style="text-align: left;">
3. Устанавливаю pylearn2 из образа</h4>
клонирую репозиторий с настройками Vagrant<br />
<span style="font-family: Courier New, Courier, monospace;">git clone git@github.com:ironchief/pylearn2_vagrant.git</span><br />
и в папке pylearn2_vagrant стартую Vagrant:<br />
<span style="font-family: Courier New, Courier, monospace;">vagrant up</span><br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg8-r1x-prJJ0IsYWIGpgIwLceeY3Ci4ohCsuMAHCIfl-vq80jRjOmYWeLOe6cECs9R19hqLMN6TBgy5EXtbYc3zof5v14J-ei9WWSw_KTLfiQbwRA6u0cN5NDWDD2IETYp3SJbfA/s1600/2-error.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg8-r1x-prJJ0IsYWIGpgIwLceeY3Ci4ohCsuMAHCIfl-vq80jRjOmYWeLOe6cECs9R19hqLMN6TBgy5EXtbYc3zof5v14J-ei9WWSw_KTLfiQbwRA6u0cN5NDWDD2IETYp3SJbfA/s1600/2-error.PNG" /></a></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
Скачивание образа застопорилось на 12%, поэтому я скачал образ вручую. В файле Vagrant указан url на образ:<br />
<span style="font-family: 'Courier New', Courier, monospace;">config.vm.box_url = "http://files.vagrantup.com/precise64.box"</span><br />
<div>
эту строку я заменил на:</div>
<div>
<span style="font-family: Courier New, Courier, monospace;">config.vm.box_url = "C:/workspace/bin/images/precise64.box"</span></div>
<div>
<br /></div>
<div>
Опять запустил vagrant up</div>
<div>
Упало с непонятной ошибкой. </div>
<div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg8-r1x-prJJ0IsYWIGpgIwLceeY3Ci4ohCsuMAHCIfl-vq80jRjOmYWeLOe6cECs9R19hqLMN6TBgy5EXtbYc3zof5v14J-ei9WWSw_KTLfiQbwRA6u0cN5NDWDD2IETYp3SJbfA/s1600/2-error.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg8-r1x-prJJ0IsYWIGpgIwLceeY3Ci4ohCsuMAHCIfl-vq80jRjOmYWeLOe6cECs9R19hqLMN6TBgy5EXtbYc3zof5v14J-ei9WWSw_KTLfiQbwRA6u0cN5NDWDD2IETYp3SJbfA/s1600/2-error.PNG" /></a></div>
<br /></div>
<div>
Следую рекоментациям открыть машину в GUI. Переключаюсь в VirtualBox, где появилась новая машина и пытаюсь ее стартовать:</div>
<div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiP2QoWa1boj6TQpuV0cCQSjD5LSWfEnsZ7sKxmC39y9ROcyb4WGLcHMHRTC4nY4tdsA24kwv-hunHHDC8p5lBET9U1h445Nm4aNgkcFS7URDbhpxxAx3iiGClBNrTTZ9kmBmqMDw/s1600/3-start+from+ui.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiP2QoWa1boj6TQpuV0cCQSjD5LSWfEnsZ7sKxmC39y9ROcyb4WGLcHMHRTC4nY4tdsA24kwv-hunHHDC8p5lBET9U1h445Nm4aNgkcFS7URDbhpxxAx3iiGClBNrTTZ9kmBmqMDw/s1600/3-start+from+ui.PNG" height="296" width="400" /></a></div>
<br />
Включаю виртуализацию в биосе, и опять запускаю vagrant up:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhaKIxVJMvbmG30ms3jL2a7DqvNA9IvKeFuK6N1qToKIxfStgFqgcVNaS4utlkPySNQGjy_PU3OSXQKYAOBbDOJM5F3yGCLQzMTLEN5yN3s2-_3HQnGhSDMl6oMQb15zIV5voGJ_w/s1600/4-vagrant+up.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhaKIxVJMvbmG30ms3jL2a7DqvNA9IvKeFuK6N1qToKIxfStgFqgcVNaS4utlkPySNQGjy_PU3OSXQKYAOBbDOJM5F3yGCLQzMTLEN5yN3s2-_3HQnGhSDMl6oMQb15zIV5voGJ_w/s1600/4-vagrant+up.PNG" /></a></div>
<br />
puppet долго конфигурирует систему...<br />
и в результате выдал маловразумительную ошибку :(<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjXg7hWDl2Fr0qJ_UxykgWe3Ruj9k_6Lw7kJhe2v_Hama6DKD6iM2m0YX_A3liywyzCud_lhHIfmywtKIPHtsag06y74DfoczYMNatpHE-QbJ2IzhstKVgrpBUYhImPTtqVds866Q/s1600/5-error.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjXg7hWDl2Fr0qJ_UxykgWe3Ruj9k_6Lw7kJhe2v_Hama6DKD6iM2m0YX_A3liywyzCud_lhHIfmywtKIPHtsag06y74DfoczYMNatpHE-QbJ2IzhstKVgrpBUYhImPTtqVds866Q/s1600/5-error.PNG" /></a></div>
<br />
понять что произошло помог лог файл pip.log, который лежит в папке pylearn2_vagrant. Там вот такая ошибка:<br />
<span style="font-family: Courier New, Courier, monospace;"><i>numpy.distutils.system_info.NotFoundError: no lapack/blas resources found</i></span><br />
<span style="font-family: Courier New, Courier, monospace;"><i><br /></i></span>
Гугл в помощь, на stackOverflow нашел интересный пост: <a href="http://stackoverflow.com/questions/7496547/does-python-scipy-need-blas">http://stackoverflow.com/questions/7496547/does-python-scipy-need-blas</a><br />
<br />
В puppet манифест (pylearn2_vagrant/manifest/default.pp) в раздел package добавил liblapack-dev :<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg05YTcii1idzKEt25FFOZxXbjHgZ6hQl0jzACKiHH4qwo7DAmpAJcTAtbuKp9v3diK7iXWaazclontvfFSZlsTu3iWmvZHy7FXFcL4JRl7oWDvJsM_pwAoRSJgRMCpl4DbInqGeA/s1600/6-libpack-dev.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg05YTcii1idzKEt25FFOZxXbjHgZ6hQl0jzACKiHH4qwo7DAmpAJcTAtbuKp9v3diK7iXWaazclontvfFSZlsTu3iWmvZHy7FXFcL4JRl7oWDvJsM_pwAoRSJgRMCpl4DbInqGeA/s1600/6-libpack-dev.PNG" height="320" width="225" /></a></div>
<br />
и опять запустил vagrant с командой provision :<br />
<span style="font-family: Courier New, Courier, monospace;">vagrant provision</span><br />
опять долго инсталлируется ...<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj2azHRFJje1AbliRC4vLH1o_BXwij2UWH9Hrz_LuCC97NHtAvc1Rmt05Xygt0OM2YTUyPhrRs2T47GzXtiG9GpdR6QNBcYtb8s7InSnuDVoz8nkHE3AmeCJYedFf55hWHUA-FfDQ/s1600/8-vagrant+provision.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj2azHRFJje1AbliRC4vLH1o_BXwij2UWH9Hrz_LuCC97NHtAvc1Rmt05Xygt0OM2YTUyPhrRs2T47GzXtiG9GpdR6QNBcYtb8s7InSnuDVoz8nkHE3AmeCJYedFf55hWHUA-FfDQ/s1600/8-vagrant+provision.PNG" /></a></div>
<br />
<h3 style="text-align: left;">
День 2</h3>
На следующий день я решил начать все сначала. Но перед экспериментами я форкнул себе репозиторий <a href="https://github.com/ironchief/pylearn2_vagrant">https://github.com/ironchief/pylearn2_vagrant</a>, склонировал уже свой репозиторий (<a href="https://github.com/szelenin/pylearn2_vagran">https://github.com/szelenin/pylearn2_vagran</a>t) в новую папку и в Vagrant файле поставил версию последней убунты:<br />
<span style="font-family: Courier New, Courier, monospace;">config.vm.box = "ubuntu/trusty64"</span><br />
<span style="font-family: Courier New, Courier, monospace;"><br /></span></div>
<div>
и запустил команду <span style="font-family: Courier New, Courier, monospace;">vagrant up</span> из новой папки pylearn2_vagrant:</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjZBsq0w49apfHXJbadQxzapVa0NeY9S71YiWK4O4d1sTLQ2fnOf6ukWhERkZFwemVMsuNH80btRE_S_vkwCKixfLq-yvXNlI-08N5ZvDm7Jjo3ctZOuDirL7YI32t33UpnxH7WcQ/s1600/9-ubuntu14.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjZBsq0w49apfHXJbadQxzapVa0NeY9S71YiWK4O4d1sTLQ2fnOf6ukWhERkZFwemVMsuNH80btRE_S_vkwCKixfLq-yvXNlI-08N5ZvDm7Jjo3ctZOuDirL7YI32t33UpnxH7WcQ/s1600/9-ubuntu14.PNG" /></a></div>
<div>
и о чудо! все проинсталлилось с первого раза!</div>
<div>
<br /></div>
<h3 style="text-align: left;">
Финальная версия</h3>
<div>
Итого чтобы поставить pylearn2 (в моем случае на винду) надо</div>
<div>
<h4 style="text-align: left;">
1. Установить Vagrant</h4>
актуальная версия 1.7.2<br />
<a href="https://www.vagrantup.com/downloads.html">https://www.vagrantup.com/downloads.html</a></div>
<div>
<h4 style="text-align: left;">
2. Установить VirtualBox</h4>
<a href="https://www.virtualbox.org/wiki/Downloads">https://www.virtualbox.org/wiki/Downloads</a><br />
и VirtualBox Extension Pack (там же)</div>
<h4 style="text-align: left;">
3. Склонировать форкнутый репозиторий </h4>
<div>
Пулреквест отошлю чуть позднее</div>
<div>
<span style="font-family: Courier New, Courier, monospace;">git clone git@github.com:szelenin/pylearn2_vagrant.git</span></div>
<h4 style="text-align: left;">
4. Запустить Vagrant</h4>
<div>
Переходим в папку <i>pylearn2_vagrant</i> и запускаем команду (желательно из вменяемой консоли, я делал это из git bash)</div>
<div>
<span style="font-family: Courier New, Courier, monospace;">vagrant up</span></div>
<div style="text-align: left;">
<span style="font-family: inherit;"><b>* Возможно прийдется включить поддержку виртуализации в BIOS.</b></span></div>
<h4 style="text-align: left;">
5. Enjoy</h4>
<div>
Дальше можно приконектиться по ssh (я использую XShell) порт 2222, пользователь vagrant/vagrant</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEierljSbAA8asvqDBpM3GqK3q-W7upgTqGfOZFmqVR4nfLvi0oMRYrME2KR_5b8IQUzuXjqI49vwU-wszTop0rqJ77ZYbPuNdJAmNXr47ZPCJMMgcMc63l4D1E4NT6K1rhZ10gPdA/s1600/10-vagrantprops.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEierljSbAA8asvqDBpM3GqK3q-W7upgTqGfOZFmqVR4nfLvi0oMRYrME2KR_5b8IQUzuXjqI49vwU-wszTop0rqJ77ZYbPuNdJAmNXr47ZPCJMMgcMc63l4D1E4NT6K1rhZ10gPdA/s1600/10-vagrantprops.PNG" height="556" width="640" /></a></div>
<div>
<br /></div>
<h3 style="text-align: left;">
Используемые материалы</h3>
<div>
1. Vagrant: <a href="https://www.vagrantup.com/downloads.html">https://www.vagrantup.com/downloads.html</a></div>
<div>
2. VirtualBox: <a href="https://www.virtualbox.org/wiki/Downloads">https://www.virtualbox.org/wiki/Downloads</a></div>
<div>
3. Puppet провижнер для Vagrant и сам Vagrant файл: <a href="https://github.com/ironchief/pylearn2_vagrant">https://github.com/ironchief/pylearn2_vagrant</a> </div>
<div>
4. Мои дополнения для последней версии ubuntu: <a href="https://github.com/szelenin/pylearn2_vagran">https://github.com/szelenin/pylearn2_vagran</a>t</div>
<div>
<br /></div>
</div>
Сергей Зеленинhttp://www.blogger.com/profile/01667820453041864348noreply@blogger.com1tag:blogger.com,1999:blog-24004232.post-5888465208156398282014-12-17T00:14:00.001-08:002014-12-23T13:23:15.575-08:00Поиск среди миллиарда слов. Считаем количество N-Gram.<div dir="ltr" style="text-align: left;" trbidi="on">
В <a href="http://szelenin.blogspot.com/2014/12/blog-post.html" target="_blank">прошлом посте</a> я обещал расшарить результаты своего ночного кодинга, так что это вроде как продолжение (практическая часть). Здесь уже будет технический хардкор на Java.<br />
<br />
<h3 style="text-align: left;">
Оцениваем сложность по пямяти</h3>
Для начала надо было придумать как посчитать сколько раз встречаютсы все эти "the", "the cat", "the cat likes" и т.д. Уникальных слов будет много, а уникальных bi-,tri-, и.т.д -gramm будет еще больше. Для каждой n-gramm'ы нужно хранить сколько раз она встречается в тексте. Чтобы примерно оценить масштаб проблемы я предварительно спросил у гугла "<a href="https://www.google.com.ua/search?sourceid=chrome-psyapi2&rlz=1C1ASUM_enUA558UA558&ion=1&espv=2&ie=UTF-8&q=how%20many%20words%20in%20english%20language" target="_blank">сколько слов в английском языке</a>?"<br />
Гугл говорит, что более 1млн:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgfitRLzDl72A6qA8XU3vu6ddRKtzUFdjIMoFNpAoD0LcQd46hu8Gbr7jH7f_sMZI_d2h_oGRgyPF9euOVYkRs5-G0TILwV-0yuakSsktpkwyrF-C5wrt0kSJvtP03Ufte2o7PCsQ/s1600/1.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgfitRLzDl72A6qA8XU3vu6ddRKtzUFdjIMoFNpAoD0LcQd46hu8Gbr7jH7f_sMZI_d2h_oGRgyPF9euOVYkRs5-G0TILwV-0yuakSsktpkwyrF-C5wrt0kSJvtP03Ufte2o7PCsQ/s1600/1.PNG" height="124" width="640" /></a></div>
<br />
Если считать только количество unigram (уникальных слов), то это не проблема хранить 1млн счетчиков в памяти, но как только понядобятся 2-,3-..грамы, то требования к памяти совсем другие. Для bigram в худшем случае понадобится хранить \(1млн^2\) каунтеров, для trigram \(1млн^3\) и т.д.<br />
Хотелось бы обойтись без всяких DB/NoSQL и прочих Hadoop'ов, поскольку это сильно утяжелит решение. Я начну с чего-то простого, но достаточно эффективного по памяти.<br />
<br />
<h3 style="text-align: left;">
Структура данных Trie</h3>
Попробую использовать структуру данных <a href="http://en.wikipedia.org/wiki/Trie" target="_blank">Trie</a> (хороший пример можно также посмотреть <a href="http://www.toptal.com/java/the-trie-a-neglected-data-structure" target="_blank">здесь</a>). В моем случае Trie будет все тем же деревом, где каждый узел представляет слово, а дочки-возможные варианты слов, которые следуют за ним.<br />
<br />
<b>Поясню на примере</b>.<br />
Возьмем предложения <i>"The dog likes the cat"</i> и <i>"the cat hates the dog"</i>.<br />
Вот как будет выглядеть Trie после того, как посчитаю количество биграм для этих 2х предложений:<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEijDiICjA8HGBw3_LzntAcgG6aJ9RaaHuKov9roV6nqAHqOWUiU6cDL-49lzNUqewZGTTF4tF-2IXwyZL0koUC_5uJT165yNk1EbmbQE0Yzo04M9zjBLfFDYdV4djb8mKuEuHatrg/s1600/2.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEijDiICjA8HGBw3_LzntAcgG6aJ9RaaHuKov9roV6nqAHqOWUiU6cDL-49lzNUqewZGTTF4tF-2IXwyZL0koUC_5uJT165yNk1EbmbQE0Yzo04M9zjBLfFDYdV4djb8mKuEuHatrg/s1600/2.PNG" height="204" width="640" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
По дереву легко можно найти, например, что слово "the" встречается 4 раза, а "the dog" - 1 раз. Слово "dog" - 2 раза, а "dog likes" - 1 и т.д. При увеличении "N-грамности" дерево будет содержать новые уровни. Количество уровней в trie будет равно числу N в N-Gram'е (рутовую ноду я не считаю). </div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<h3 style="clear: both; text-align: left;">
Пишу код</h3>
<div>
Сам код здесь не буду постить, для этого есть гитхаб:<a href="https://github.com/szelenin/kaggle/tree/master/billion-word-imputation">https://github.com/szelenin/kaggle/tree/master/billion-word-imputation</a>. Укажу лишь, что но начал я с такого теста:</div>
<script class="brush: java" type="syntaxhighlighter">
<![CDATA[
@Test
public void shouldInsertFromSentence() {
Model model = new Model();
model.put("the dog likes the cat");
assertEquals(2, model.count("the"));
}
]]>
</script>
<br />
<div>
А закончил таким:
</div>
<script class="brush: java" type="syntaxhighlighter">
<![CDATA[
@Test
public void shouldCountSeveralNGramsAtOnce() {
Model model = new Model(3);
model.put("the dog likes the cat");
model.put("the cat likes the cat");
assertEquals(2, model.count("cat", "_stop_"));
assertEquals(2, model.count("*", "the"));
assertEquals(2, model.count("*", "*", "the"));
assertEquals(2, model.count("the", "cat", "_stop_"));
assertEquals(4, model.count("the"));
assertEquals(3, model.count("the", "cat"));
assertEquals(2, model.count("likes", "the", "cat"));
}
]]>
</script>
<br />
Все слова приводятся к нижнему регистру, знаки препинания и прочие нетерминальные символы я решил отбросить.
<br />
<br />
<h4 style="text-align: left;">
Пробный пуск
</h4>
<div>
Для начала я запустил подсчет уникальных слов, чтобы получить общую статистику по тренировочному файлу. Вот что выдала после 2х с небольшим минут.<br />
<br />
<span style="font-family: Courier New, Courier, monospace;">Lines read: 30301028. Unique words: <b>1026207</b>, total words: 693467918</span><br />
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
Количество уникальных слов практически такое же как выдал гугл. Так что, думаю, все возможные слова, которые есть в английском языке присутствуют :)<br />
<br />
<h4 style="text-align: left;">
1й запуск для подсчета триграм</h4>
</div>
<div>
Я особенно не расчитывал, что моя считалка посчитает количество всех возможных триграм в тексте, просто интересно через сколько времени закончится память :). Память закончилась практически сразу и JVM вывалился с жуткой ошибкой:</div>
<div>
<br /></div>
<div>
<div>
<span style="font-family: Courier New, Courier, monospace;"># A fatal error has been detected by the Java Runtime Environment:</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">#</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"># EXCEPTION_ACCESS_VIOLATION (0xc0000005) at pc=0x000000006f533c0b, pid=4768, tid=6776</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">#</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"># JRE version: Java(TM) SE Runtime Environment (8.0_20-b26) (build 1.8.0_20-b26)</span></div>
<div>
<br /></div>
</div>
<h4 style="text-align: left;">
2й запуск</h4>
<div>
На второй запуск я выделил 12Гб под JVM и запустил jvisualvm чтобы посмотреть как кушается память. Проработала программка 4минуты и вывалилась с такой же ошибкой. Но все таки успела прочитать 3.6 млн строк, в которых было 83.5 млн слов, из которых почти 400 тыс уникальных, что в общем-то неплохо для 4х минут работы :) :</div>
<div>
<br /></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">Lines read: 3648300. Unique words: 398484, total words: 83500620</span></div>
<div>
<br /></div>
<div>
<div style="text-align: center;">
<b>Вот как заполнялась память</b></div>
</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgIgDzNpJC30zZUVGocZeNMz37Mn3MoGHPwdOj7hr-EFnhNjIQimp37_3GetcdOLRGBrSOBDp2wVqQEVSetGKpN-0Q21qMt6m0eADd2hrqxul3RZNmfJZiRhI6aDjcVdBQLbofqwQ/s1600/3_not_optimized_heap.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgIgDzNpJC30zZUVGocZeNMz37Mn3MoGHPwdOj7hr-EFnhNjIQimp37_3GetcdOLRGBrSOBDp2wVqQEVSetGKpN-0Q21qMt6m0eADd2hrqxul3RZNmfJZiRhI6aDjcVdBQLbofqwQ/s1600/3_not_optimized_heap.PNG" height="368" width="640" /></a></div>
<br />
<div style="text-align: center;">
<b>А вот какие обьекты наиболее активно ее наполняли:</b></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjCfNYRB9lykU-LyI9fjDH_lakNtiAZO0qKsL-QBwNsHbAwMTn3SUzM8M4SS8v_LsNBvGs-w247MyBqgzAmwgVvSNkGBO4Znl2UFrTLln3NDJMG0teoA7tf0hWx1Vpk4ZnCAd0XYw/s1600/3_not_optimized.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjCfNYRB9lykU-LyI9fjDH_lakNtiAZO0qKsL-QBwNsHbAwMTn3SUzM8M4SS8v_LsNBvGs-w247MyBqgzAmwgVvSNkGBO4Znl2UFrTLln3NDJMG0teoA7tf0hWx1Vpk4ZnCAd0XYw/s1600/3_not_optimized.PNG" height="345" width="640" /></a></div>
<div>
<br /></div>
<div>
Глядя на количество инстансов HashMap я решил немного сэкономить и чуть оптимизировать код. Дело в том, что в каждой ноде я по умолчанию создавал мапу для ее детей:</div>
<script class="brush: java" type="syntaxhighlighter">
<![CDATA[
private Map<String, Node> children = new HashMap<String, Node>();
]]>
</script>
<br />
<div>
<br /></div>
<div>
Но для последнего уровня дерева детей не предполагается, поэтому можно мапу не создавать.</div>
<h4 style="text-align: left;">
<br /></h4>
<h4 style="text-align: left;">
3й запуск</h4>
<div>
Сделал создание мапы с детьми lazy и запустил еще раз. На этот раз работало 30 минут, после чего я убил процесс, т.к. дожидаться правильного OutOfMemory совсем не хотелось. Перед смертью считалка отчиталась, что прочитала 7.1 млн предложений из 16.2млн слов, из которых 541тыс уникальных.<br />
<br />
<span style="font-family: Courier New, Courier, monospace;">Lines read: 7119000. Unique words: 541538, total words: 162929078</span><br />
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<br />
<div style="text-align: center;">
<span style="font-family: inherit;"><b>Вот график поедания памяти</b></span></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjXIcLCpX19DYisMqsTmqtZluoqfUPnBCk8axJX-yKi_j_ykTehGk1GzpSBCJKNT26tWjzDV3iPCzQ9zh3kxHgJgAO-CI6jVtuBch_WTMmO8MlxXrVEnvN-v7M_teRueRifcYwuRg/s1600/4_optimized_heap.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjXIcLCpX19DYisMqsTmqtZluoqfUPnBCk8axJX-yKi_j_ykTehGk1GzpSBCJKNT26tWjzDV3iPCzQ9zh3kxHgJgAO-CI6jVtuBch_WTMmO8MlxXrVEnvN-v7M_teRueRifcYwuRg/s1600/4_optimized_heap.PNG" height="442" width="640" /></a></div>
<br />
<div style="text-align: center;">
<b> А вот ее содержимое</b></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEifvhROsQWT53XzoaKjkhza8xGW17jGMjX9gWLa7FlfUhPaMepEnXSOz_zITTEJb1f8PZvubkR7hm_BenVuVaE4b_hqXvxjO0kh11PxR19PHNmgsW8G8A9uDi1_7mq3HQHPRZhpXg/s1600/4_optimized.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEifvhROsQWT53XzoaKjkhza8xGW17jGMjX9gWLa7FlfUhPaMepEnXSOz_zITTEJb1f8PZvubkR7hm_BenVuVaE4b_hqXvxjO0kh11PxR19PHNmgsW8G8A9uDi1_7mq3HQHPRZhpXg/s1600/4_optimized.PNG" height="440" width="640" /></a></div>
<span style="font-family: inherit;"><br /></span>
<br />
<h3 style="text-align: left;">
<span style="font-family: inherit;">Выводы</span></h3>
</div>
<div>
Считалка свободно просчитывает 4млн предложений за 4минуты. В тренировочном наборе всего 30млн. Можно, конечно, придумывать новые способы хранения счетчиков, подключать внешние хранилища данных, и т.д. но это может превратиться в бесконечный процесс. Хотелось бы закончить с начальной версии модели, а затем ее усовершенствовать в плане производительности. Поэтому я разобью тренировочный набор на 10 частей и буду работать с ними отдельно. Еще надо найти где пропущено слово и, самое сложное, какое слово вместо него нужно добавить. Так что буду отлаживать модель на одном из 10 наборов или на каждом по очереди, а затем будем думать как обьеденить.<br />
<br />
<h3 style="text-align: left;">
PS</h3>
</div>
<div>
Как говорится "быстро сказка сказывается, но не быстро дело делается". На следующий день я бодренько поправил код, который разбивает тренировочный набор на 10 частей, считает каунтеры и сохраняет расчитанные значения в файле. Запустил прогу и через 30 минут она вывалилась с таким же ACCESS VIOLATION как и в 1й раз. Вот так съедалась память во время работы:</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjg7IkTuO5X5ZEiEAtRf6t8NCTQyZKLdJ0buPkjI0rOy02tfNwjwqMEkpHZg8gBCiG2RoFoA5Irc80b1Cdby3Whw71jX5vY6j2k2njnwHkGGZPSHyEbO-JO_yC9jbgh9sRq5EJ6BQ/s1600/5_partitioned_heap_big.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjg7IkTuO5X5ZEiEAtRf6t8NCTQyZKLdJ0buPkjI0rOy02tfNwjwqMEkpHZg8gBCiG2RoFoA5Irc80b1Cdby3Whw71jX5vY6j2k2njnwHkGGZPSHyEbO-JO_yC9jbgh9sRq5EJ6BQ/s1600/5_partitioned_heap_big.PNG" height="472" width="640" /></a></div>
<div>
<br /></div>
<div style="text-align: left;">
Это довольно странно, т.к. после каждой просчитанной порции тренировочного файла GC должен был убрать все что с этой порцией связано. Так что я ожидал увидеть несколько "холмов" в графике. </div>
<div style="text-align: left;">
Ради эксперимента я сказал JVM использовать Garbage Collector GC1 (-XX:+UseG1GC) и запустил считалку опять. На этот раз она упала при сериализации модели, так что я поменял стандартную Java сериализацию на <a href="https://github.com/EsotericSoftware/kryo" target="_blank">kryo</a> и запустил еще раз. В этот раз все прошло как и ожидалось:</div>
<div style="text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhmlbPhpb_sywncOCMWfU2owi94cA0CmOblrPNU99EV_sEMFiO4sSEc_TLp5eXEqQA-BqYgXctMrzIzJrDzupI1W0qtzNUqwV9h1LlgmRhevCs6YHQ0fBB_jLMz2g1jIjS7LpvWnQ/s1600/7_finished_heap.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhmlbPhpb_sywncOCMWfU2owi94cA0CmOblrPNU99EV_sEMFiO4sSEc_TLp5eXEqQA-BqYgXctMrzIzJrDzupI1W0qtzNUqwV9h1LlgmRhevCs6YHQ0fBB_jLMz2g1jIjS7LpvWnQ/s1600/7_finished_heap.PNG" height="458" width="640" /></a></div>
<div style="text-align: left;">
<br /></div>
<div style="text-align: left;">
График показывает, что GC корректно убивает ненужные объекты. Тогда почему это не работало раньше, с Garbage collector'ом, по умолчанию? </div>
<div style="text-align: left;">
По умолчанию применяется Concurrent Mark&Sweep GC, который разделяет объекты на "короткоживущие" и "долгоживущие". Область короткоживущих чистится часто, а долгоживущие чистятся гораздо реже. Если JVM положил объект в "долгоживущую" область, то потом его оттуда сложно будет вытащить. По крайней мере в моем случае похоже, что все попытки почистить долгоживущую область заканчивались крашем JVM. GC1 разделяет весь хип на блоки размером от 1 до 32мб. Каждый блок может быть "короткоживущим" или "долгоживущим". Если в блоке все объекты уже не используются, то блок освобождается целиком. Подробнее про устройство GC1 можно посмотреть <a href="http://www.infoq.com/articles/G1-One-Garbage-Collector-To-Rule-Them-All" target="_blank">здесь</a>.<br />
<br /></div>
<h4 style="text-align: left;">
Ресурсы</h4>
<div>
1. Мой код на гитхабе <a href="https://github.com/szelenin/kaggle/tree/master/billion-word-imputation">https://github.com/szelenin/kaggle/tree/master/billion-word-imputation</a></div>
<div>
2. Описание с структуры Trie на википедии: <a href="http://en.wikipedia.org/wiki/Trie">http://en.wikipedia.org/wiki/Trie</a></div>
<div>
3. Описание Trie с примерами : <a href="http://www.toptal.com/java/the-trie-a-neglected-data-structure">http://www.toptal.com/java/the-trie-a-neglected-data-structure</a></div>
<div>
4. Как устроен GC1 : <a href="http://www.infoq.com/articles/G1-One-Garbage-Collector-To-Rule-Them-All">http://www.infoq.com/articles/G1-One-Garbage-Collector-To-Rule-Them-All</a><br />
5. Kryo serialization framework : https://github.com/EsotericSoftware/kryo</div>
</div>
Сергей Зеленинhttp://www.blogger.com/profile/01667820453041864348noreply@blogger.com19tag:blogger.com,1999:blog-24004232.post-20608186458235116922014-12-11T14:51:00.000-08:002015-04-10T13:48:22.544-07:00Поиск среди миллиарда слов<div dir="ltr" style="text-align: left;" trbidi="on">
Ничто так не настраивает на продуктивную работу, как день проведенный в совещаниях. Вот и я, после дня проведенного в бесконечных трындежах, решил сделать что-то полезное. Как только дома все уснули я решил покодить. Нашел интересный контест на <a href="https://www.kaggle.com/" target="_blank">Kaggle</a>, <a href="https://www.kaggle.com/c/billion-word-imputation" target="_blank">Billion Word imputation</a>. <br />
<br />
<h3 style="text-align: left;">
Суть задачи состоит в следующем:</h3>
Есть два файла. В первом - текст, состоящий большой кучи осмысленных предложений (на инглиш оф коз). Размер текстового файла примерно 3.86Гб и в нем находится примерно 1миллиард слов. Это тренировочный набор (training data).<br />
Второй файл чуть поменьше (40мб). В нем из каждого предложения удалили по 1 слову. Это тестовый набор (test data).<br />
Задача: вставить пропущенное слово в тестовом наборе. Тексты никак друг с другом не связаны.<br />
<br />
<h3 style="text-align: left;">
Как собираюсь решать</h3>
<div>
На тренировочных данных нужно построить модель, с помощью которой можно предсказать пропущенные слова в тестовом наборе. Какую модель будем строить?<br />
Когда-то давно я прошел очень интересный курс по <a href="https://class.coursera.org/nlangp-001" target="_blank">Natural Language Processing</a> на Coursera от <a href="https://www.coursera.org/columbia" target="_blank">Columbia University</a>. Основным элементом языковых моделей, которые мы рассматривали были так называемые <a href="http://en.wikipedia.org/wiki/N-gram" target="_blank">N-Gram</a>'ы. N-Gram - это последовательность N слов в предложении. Если N=1, то такую N-Gramm'у называют Unigram (т.к. одно слово), если 2-Bigram, 3-Trigrams и т.д.<br />
<br /></div>
<div>
<b>Например</b>, если разложить предложение <i>"The dog likes the cat" </i>на N-Gramm'ы, то получится:</div>
<div>
- Пять Unigram: The, dog, likes, the, cat</div>
<div>
- 6 Bigram: * The, The dog, dog likes, likes the, the cat, cat _STOP_</div>
<div>
- 6 Trigram: * * The, * The dog, The dog likes, dog likes the, likes the cat, the cat _STOP_</div>
<div>
Здесь символ * и _STOP_ - это служебные символы, означающие начало и конец предложения.</div>
<div>
<br /></div>
<h3 style="text-align: left;">
Что можно сделать с этими N-Gramмами?</h3>
<div>
Можно посчитать вероятность, с которой встречается в тексте каждая N-Gram'ма. В простейщем случае (Unigram) вероятность слова w будет такой:</div>
<div>
$$p(w)=\frac{count(w)}{N}$$</div>
<div>
где N-общее количество слов в тексте, \(count(w)\) - сколько раз встречается слово w.</div>
<div>
Например, в предложении "the dog likes the cat" \(p(the)=2/5\)</div>
<div>
<br /></div>
<div>
Если N > 1 (bigram, trigram и т.д.) то немного сложнее. Здесь мы должны считать условную вероятность \(p(w_n|w_{1},w_{2}...w_{n-1})\). Это еще называют правдоподобием (likelihood). Т.е. это вероятность того, что слово \(w_n\) встретится за словами \(w_{1} w_{2}...w_{n-2}w_{n-1}\).<br />
<br />
В случае с биграммами мы будем "заглядывать" на одно слово назад, в случае с триграммами на 2 и т.д.<br />
Вот как считаются вероятности для биграм <br />
$$p(w_i|w_{i-1})=\frac{count(w_{i-1},w_i)}{count(w_{i-1})}$$<br />
Где \(count(w_{i-1},w_i)\) - сколько раз встречаются вместе слова \(w_{i-1} w_i\), а \(count(w_{i-1})\) - частота появления слова \(w_{i-1}\)<br />
Точно так же для триграм<br />
$$p(w_i|w_{i-2},w_{i-1})=\frac{count(w_{i-2},w_{i-1},w_{i})}{count(w_{i-2},w_{i-1})}$$<br />
<b>Например</b>, вероятность того, что <i>likes </i>встретится сразу за <i>"the dog":</i><br />
$$p(likes|the, dog)=\frac{count(the, dog, likes)}{count(the, dog)}$$<br />
<br />
<h3 style="text-align: left;">
Как использовать расчитанные вероятности?</h3>
</div>
<div>
Допустим, что я как-то посчитал сколько раз встречается в тренировочном тексте все "the", "the dog", "the dog likes", "likes the cat", "cat _STOP_" и т.д. Как это можно использовать, чтобы найти пропущенное слово?</div>
<div>
<br /></div>
<div>
Поясню на примере.</div>
<div>
Допустим, что в предложении "the dog likes the cat" удалили слово likes: "the dog <strike>likes </strike>the cat". Можно предположить, что likelihood для биграммы "dog the" будет очень маленьким, так же и для триграмм "the dog the" и "dog the cat". В идеале хотелось бы чтобы эти все 3 вероятности были бы нулевыми: </div>
<div>
\(p(the|dog)->{min}\)</div>
<div>
\(p(the|the,dog)->{min}\)</div>
<div>
\(p(cat|dog,the)->{min}\)</div>
<div>
<br /></div>
<div>
То есть нам надо пробежаться по предложению ("the dog the cat") и посчитать правдоподобия для всех bi-,tri-(возможно 4,5,6)-Gram в этом предложении. Если правдоподобие всех (или большинства) N-Gram минимально в одном и том же месте, то предполагаем, что в это место нужно вставить слово. </div>
<div>
Какое? тут уже надо искать слово, которое максимизирует правдоподобие (maximum likelihood) в заданном месте предложения. То есть если мы нашли, что слово удалили между "dog" и "the", то имеет смысл поискать что чаще всего встречалось после "the dog", "dog" и перед "the cat", "the". Это пока первоначальная гипотеза как можно найти слово, наверняка с реализацией она претерпит существенных изменений как это обычно бывает. </div>
<div>
<br /></div>
<div>
Если есть мысли\предложения по поводу модели и реализации, то велкам. Про то, как я начал считать каунтеры расскажу в следующем посте. Там тоже интересно получилось :).</div>
<div>
<br /></div>
<h4 style="text-align: left;">
Ресурсы</h4>
<div>
<a href="https://www.kaggle.com/" target="_blank">Kaggle</a>, <a href="https://www.kaggle.com/c/billion-word-imputation" target="_blank">Billion Word imputation</a> contest</div>
<div>
Курс <a href="https://class.coursera.org/nlangp-001" target="_blank">Natural Language Processing</a> на <a href="https://www.coursera.org/" target="_blank">Coursera</a></div>
<div>
<br /></div>
</div>
Сергей Зеленинhttp://www.blogger.com/profile/01667820453041864348noreply@blogger.com0tag:blogger.com,1999:blog-24004232.post-41042496315895090922014-04-08T00:16:00.001-07:002016-03-22T03:52:54.497-07:00Бомбим недругов с помощью A* search<div dir="ltr" style="text-align: left;" trbidi="on">
Примерно год назад я прошел интересный онлайн курс от университета Berkley по основам искуственного интеллекта. Курс так и называется <a href="https://www.edx.org/course/uc-berkeleyx/uc-berkeleyx-cs188-1x-artificial-579">Artificial Intelligence (код CS188.1x)</a>. Было весело, лектор много юморил и в шутливой форме доходчиво объяснил много полезных алгоритмов и подходов к решению задач ИИ. В качестве лабораторных надо было написать бота для старой доброй игры Pacman, что было очень забавно.<br />
Одним из первых базовых алгоритмов мы рассматривали A* search. Его я и применю в реализации бота для нашей игрушки <a href="http://codenjoy.com/portal/?page_id=421">bomberman</a>. Кстати, программу, которая управляет персонажем игры правильнее называть агентом, а не ботом ;)<br />
<br />
<div>
<h3>
Вкратце что из себя представляет A* search.</h3>
</div>
<div>
<div>
Поиск А* - это алгоритм нахождения оптимального пути в графе. Хорошо подходит для поиска оптимального маршрута из пункта А в пункт Б. Я воспользуюсь известным примером "путешествие по Румынии" (tour by Romania), чтобы в 2х словах пояснить как это работает.</div>
<div>
<div class="separator" style="clear: both; text-align: center;">
<a href="http://web.archive.org/web/20140606105751/https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjf_8whx66VQhNY2WxNRmqyLdwU_skmx8X3lqIugsDYZpQmZGOGSp0Q4959sOMUbAgcoTA_Y7TYxEgxyV813OdeBHReIKvESPRgESJJty2z49daGHXNlHevkRD7YOoTse4ivLlMig/s1600/output_zp7Eac.gif" imageanchor="1" style="color: #02689b; margin-left: 1em; margin-right: 1em;"></a></div>
Задача. Нам нужно на йти кратчайший маршрут из города Арад (Arad) в Бухарест (Bucharest). Можно перебрать все варианты, но в случае большой карты это займет много времени.</div>
</div>
<div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgSTyIxfvWyfuqnxj20kx0-oQ3Z_DsZKJlzyIZnQaJDVWqqDiVwiLiEV60_Li-_3pdzFIkRQjhF2z_4Bu7fjwJyTwMUV9NqfoEZERDhQc-vVyrYMt7VPO1TDHybklKgz6ayvddBVw/s1600/1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="207" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgSTyIxfvWyfuqnxj20kx0-oQ3Z_DsZKJlzyIZnQaJDVWqqDiVwiLiEV60_Li-_3pdzFIkRQjhF2z_4Bu7fjwJyTwMUV9NqfoEZERDhQc-vVyrYMt7VPO1TDHybklKgz6ayvddBVw/s320/1.png" width="320" /></a></div>
<div>
<br /></div>
<div>
Для реализации поиска с помощью A*, нам понадобится расчитать две величины для каждого пункта:</div>
<div>
1. <b>Эвристика</b>. Расстояние от текущего города до Бухареста по прямой. На карте это расстояние не указано, оно есть в полной версии <a href="https://dl.dropboxusercontent.com/u/22607711/A-Star-Search-Example.pdf">здесь</a>.</div>
<div>
2. <b>Стоимость</b>. Реальный пройденный километраж. Он обозначен на карте числами между пунктами.<br />
<br /></div>
</div>
<div>
<h3 style="text-align: left;">
Алгоритм</h3>
<div>
<br /></div>
<div>
1. Берем все соседние с Арадом пункты (Zerind, Sibiu,Timisoara). Эти пункты называются successors.</div>
<div>
2. Вычисляем эвристику для каждого пункта. Допустим для Zerind значение эвристики равно 374, для Sibiu 253, для Timisoara 329.</div>
<div>
3. Складываем значение эвристики со стоимостью и помещаем successor в сортированную коллекцию. Эта сортированная коллекция в A* имеет название "фринж" (fringe). Т.е. соседние с Арадом пункты разместятся во фринже в таком порядке : Sibiu (253 + 140 = 393), Timisoara (329 + 118 = 447), Zerind (374 + 75 = 449).</div>
<div>
4. Берем из фринжа первый пункт (Sibiu) и повторяем все, начиная с п.1 до тех пор пока мы не достигнем Бухареста или фринж не опустеет. Состояние "мы в Бухаресте" есть наша цель или по научному "goal state".</div>
</div>
<div>
<div>
<br /></div>
<div>
Здесь можно посмотреть пошаговое вычисление маршрута в задаче "tour by Romania": <a href="http://bit.ly/1hy3ezD">http://bit.ly/1hy3ezD</a></div>
</div>
<div>
<div>
<br /></div>
<div>
<b>Важные свойства эвристики</b></div>
<div class="separator" style="clear: both; text-align: center;">
<b></b></div>
<div>
1. Эвристика должна быть "допустимой" (admissible). </div>
<div>
\(\forall N, h(N) \leq C(N)\) </div>
<div>
где </div>
<div>
\( h(N) \) - значение эвристики в пункте n</div>
<div>
\( C(N) \) - реальная стоимость из пункта n в goal state</div>
<div>
<br /></div>
<div>
2. Эвристика должна быть консистентной (consistent)</div>
<div>
\( h(N) \leq C(N,P) + h(P) \) и \( h(G) = 0 \)</div>
<div>
где </div>
<div>
\( h \) консистентная эвристика </div>
<div>
\( N \) любой пункт в графе </div>
<div>
\( P \) пункт, предидущий N </div>
<div>
\( C(N,P) \) стоимость перехода из пункта P в пункт N </div>
<div>
<br /></div>
<h3 style="text-align: left;">
Реализация агента</h3>
<div>
<br /></div>
<div>
Перед тем как сесть ваять код нужно ответить на несколько вопросов</div>
<div>
- какие пункты считать "соседними" (successors) ?</div>
<div>
- что будет нашей целью поиска (goal state) ?</div>
<div>
- какую эвристику использовать ?</div>
<div>
- как расчитывать стоимость передвижения?</div>
<div>
<br /></div>
<div>
<b>Определяем соседей (successors)</b></div>
<div>
Бомбермен может двигаться и ставить бомбы. Он так же может пропустить ход. </div>
<div>
Successor - это состояние игрового поля после того, как бомбермен сделает одно движение или поставит бомбу (или же пропустит ход). Так же нужно учитывать, что бомба взрывается через 5 секунд и ее осколки разлетаются в радиусе 3х шагов. </div>
<div>
Для состояния доски я создал класс <a href="http://bit.ly/1ep5WaJ">GameState </a>(<a href="https://github.com/szelenin/bomberman">ссылка на репозиторий</a>). GameState может вернуть список доступных действий для бомбермена (метод getLegalActions) и вернуть новое состояние после того, как пойдет бомбермен (метод generateSuccessor(Action action))</div>
<div>
<br /></div>
<div>
<b>Каким будет Goal state?</b></div>
<div>
Поскольку A* - это алгоритм поиска, то надо определить что мы ищем. Я буду искать маршрут, где конечный пункт - забомбленый чертик (мы его зовем chopper).</div>
<div>
<br /></div>
<div>
<b>Какую эвристику использовать?</b></div>
<div>
Если я хочу забомбить ближайшего ко мне чертика, то в качестве эвристики для простоты можно использовать <a href="https://ru.wikipedia.org/wiki/%D0%A0%D0%B0%D1%81%D1%81%D1%82%D0%BE%D1%8F%D0%BD%D0%B8%D0%B5_%D0%B3%D0%BE%D1%80%D0%BE%D0%B4%D1%81%D0%BA%D0%B8%D1%85_%D0%BA%D0%B2%D0%B0%D1%80%D1%82%D0%B0%D0%BB%D0%BE%D0%B2">manhattan distance</a> от бомбера к ближайшему чертику. Здесь, правда, пришлось немного пошаманить, т.к. после того, как бомбермен поставил бомбу пришлось быстро от нее убегать. Код эвристики реализовал в классе <a href="http://bit.ly/1itV4ut">NearestChopperToBombHeuristic</a> </div>
<div>
<br /></div>
<div>
Как расчитывать стоимость передвижения?</div>
<div>
Вообще расчет стоимости в данном случае - это задача не совсем тривиальная, т.к. необходимо учесть несколько параметров:</div>
<div>
- "стоимость" установки бомбы. Т.е. должно быть выгоднее ставить бомбу ближе к чоперу</div>
<div>
- передвижение бомбера (чтобы как минимум соответствовать эвристике)</div>
<div>
- опасность быть убитым осколками</div>
<div>
Можно еще учесть массу других параметров, например, таких как опасность быть съеденым или стоимость разбомбленной стенки и т.д. Но чем больше параметров, тем сложнее их использовать в общем подсчете стоимости поскольку приходится уравновешивать взаимоконфликтные вещи. Например уровень опасности быть съеденым запросто может конфликтовать со стоимостью установки бомбы. Ведь для того, чтобы поставить бомбу рядом с чертиком нужно рискнуть и подойти очень близко.</div>
<div>
В общем со стоимостью пришлось немного поэкспериментировать. Пришлось даже написать небольшую песочницу, которая показывает найденный маршрут. Подсчет стоимости реализован в классе <a href="http://bit.ly/1iu5vhE">Problem</a>, см. метод cost.</div>
<div>
<br /></div>
<div>
Что получилось</div>
<div>
<br /></div>
<div>
Нагляднее всего продемонстрируют картинки. Вот пара вариантов расчета из песочницы:</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg0uieyRrx_Uedwfx85fPXVUyutwo0CxKf_6HSAnyD3jTXDBp-ClKUn_oAHzAbiwXt4LFnEBUXiX0UFjbc_RD6X7YQdrFm0gKtTVeIj19dmShAYdV_EL1UDGSmyzUnP_lYnp3mJjw/s1600/bomber1.gif" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg0uieyRrx_Uedwfx85fPXVUyutwo0CxKf_6HSAnyD3jTXDBp-ClKUn_oAHzAbiwXt4LFnEBUXiX0UFjbc_RD6X7YQdrFm0gKtTVeIj19dmShAYdV_EL1UDGSmyzUnP_lYnp3mJjw/s320/bomber1.gif" width="320" /></a></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjM4DJSmiBmcpO2ftaPG5BqUmkk8JLQn4lftQ6D-Pj6DCAF0CmIYKSe2RZyOWTTDSmlKLP7w71IQQOI1VDtDtva6REeF8b6KutDyXEpYBFzrpj59VQ4bUbr4bq8ysEEunkzim9P4A/s1600/bomber2.gif" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjM4DJSmiBmcpO2ftaPG5BqUmkk8JLQn4lftQ6D-Pj6DCAF0CmIYKSe2RZyOWTTDSmlKLP7w71IQQOI1VDtDtva6REeF8b6KutDyXEpYBFzrpj59VQ4bUbr4bq8ysEEunkzim9P4A/s320/bomber2.gif" width="320" /></a></div>
<div>
<br /></div>
<div>
<br /></div>
<div>
<br /></div>
<div>
Для начала неплохо, но можно еще тюнить, подбирать коэффициенты и вводить новые параметры в функцию стоимости. Но налицо несколько ограничений:</div>
<div>
<a href="http://web.archive.org/web/20140606105751/https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjf_8whx66VQhNY2WxNRmqyLdwU_skmx8X3lqIugsDYZpQmZGOGSp0Q4959sOMUbAgcoTA_Y7TYxEgxyV813OdeBHReIKvESPRgESJJty2z49daGHXNlHevkRD7YOoTse4ivLlMig/s1600/output_zp7Eac.gif" imageanchor="1" style="clear: right; color: #02689b; float: right; margin-bottom: 1em; margin-left: 1em;"></a>- хорошо работает для статики. Хорошо, когда чоперы не пытаются тебя скушать, а другие соперники не подкладывают под тебя бомбы :). В случае, если это не так, приходится каждый раз расчитывать новый маршрут.</div>
<div>
- Алгоритм не смотрит дальше Goal state. В случае бесконечной игры вариант "убить чертика, а там хоть трава не расти" часто заканчивается тем, что бомбермена съедают как только он поставил бомбу под чопером</div>
<div>
- сложная функция стоимости. Как я уже указывал, в стоимость приходится закладывать множество параметров и искать баланс приоритетов между ними.</div>
<div>
<br /></div>
<h3 style="text-align: left;">
Ресурсы</h3>
<div>
1. Онлайн курс университета Беркли по ИИ <a href="https://www.edx.org/course/uc-berkeleyx/uc-berkeleyx-cs188-1x-artificial-579">Artificial Intelligence (код CS188.1x)</a></div>
<div>
2. Пример "Tour by Romania" в формате PowerPoint <a href="http://homes.ieu.edu.tr/~bhnich/SE420/A-Star-Search-Example.ppt">можно скачать здесь</a>. </div>
<div>
3. Тот же пример в формате PDF <a href="http://bit.ly/1hy3ezD">можно скачать здесь</a></div>
<div>
4. Мой гитхаб репозиторий с исходниками (еще будет меняться:)) <a href="https://github.com/szelenin/bomberman">https://github.com/szelenin/bomberman</a></div>
<div>
5. <a href="http://codenjoy.com/codenjoy-contest/board?gameName=bomberman">Онлайн игра bomberman</a> на нашем игровом портале <a href="http://codenjoy.com/portal/">Codenjoy</a></div>
</div>
</div>
Сергей Зеленинhttp://www.blogger.com/profile/01667820453041864348noreply@blogger.com0tag:blogger.com,1999:blog-24004232.post-85991010170757847852014-02-12T15:54:00.003-08:002014-02-13T01:01:59.475-08:00Прикольный фреймворк для тестов Spock.Недавно на проекте заюзали <a href="https://code.google.com/p/spock/" target="_blank">spock </a>для юнит тестов. Ему, оказывается, уже много лет (целых 4 года!), а я узнал о нем только сейчас. Так обидно стало за бесцельно прожитые годы рядом с морально устаревшим JUnit'ом :).<br />
<br />
<h3>
Кто такой Спок?</h3>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhxJ5FJn4sQueL-V6AM4mu2uJt3_o4poE7P9RyqO8rqjTL6IEsyESW67Ub0Er4a22pT73E-pINaYhJEGhPZcP901CJsg1zGKbz-fRdpwP44zehjVC82akSKVgQUFWZPwv5UFG2B_Q/s1600/spock_3.jpg" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhxJ5FJn4sQueL-V6AM4mu2uJt3_o4poE7P9RyqO8rqjTL6IEsyESW67Ub0Er4a22pT73E-pINaYhJEGhPZcP901CJsg1zGKbz-fRdpwP44zehjVC82akSKVgQUFWZPwv5UFG2B_Q/s1600/spock_3.jpg" height="200" width="193" /></a></div>
Спок - коммандер и научный сотрудник звездолета "Энтерпрайз", родился в 2230 году по земному летоисчислению. Наполовину землянин, наполовину вулканец. Увлечение компютерами привело его в звездный флот, где он и сделал свою карьеру научного сотрудника.<br />
Кому не хватило терпения посмотреть сериал, на википедии есть <a href="http://ru.wikipedia.org/wiki/%D0%A1%D0%BF%D0%BE%D0%BA" target="_blank">краткая биография</a> Спока.<br />
Не знаю, что сподвигло разработчиков назвать фреймворк для тестирования именем персонажа из Стартрека. Может двойственная натура героя (spock хорошо подходит как для тестирования Java так и Groovy кода), а может что еще.<br />
<br />
<a name='more'></a><br />
<br />
<h4>
</h4>
<h3>
Ближе к коду</h3>
<div>
Начну педалить код, а детали настройки и как подключать будут ниже. Я решил написать бота для бомбермена. Детали игры, инструкции, примеры кода для разных языков на сайте <a href="http://codenjoy.com/codenjoy-contest/" target="_blank">codenjoy</a>.</div>
<div>
<br /></div>
Вот так выглядит первый тест. Можно нормально назвать тест, а ключевые слова when и then добавляют читабельности тесту.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgsSO0iYaRZFcJ6eoLCTDJBr3N_laX1MFzWwarmnE0VrYnBrzA6PQbu4gjWK1Kf0JAerT8IHA3S7TAgytfuLUBjxYO-e-pBsqlgpCDgDkXyZAiNYvx1tdxhGAo6wmjU4r1mabtyKw/s1600/1st+test.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgsSO0iYaRZFcJ6eoLCTDJBr3N_laX1MFzWwarmnE0VrYnBrzA6PQbu4gjWK1Kf0JAerT8IHA3S7TAgytfuLUBjxYO-e-pBsqlgpCDgDkXyZAiNYvx1tdxhGAo6wmjU4r1mabtyKw/s1600/1st+test.PNG" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
Можно написать тест, используя data-driven подход. Имена колонок таблицы данных должны совпадать с именами переменных тестового метода:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgtbZbBshuGHHWVLp_kDSsa0JMX39sUSsfLmwIPAzWTF2BuvRmVw5EudyHpIzEuejOQveTc8fPBrlXJSDQsJtGjL14mqe_2oS8w5RT0ZOgW1odvW2G8YcHX_JEl4Y-5vjVhKhYXDg/s1600/1-1+test.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgtbZbBshuGHHWVLp_kDSsa0JMX39sUSsfLmwIPAzWTF2BuvRmVw5EudyHpIzEuejOQveTc8fPBrlXJSDQsJtGjL14mqe_2oS8w5RT0ZOgW1odvW2G8YcHX_JEl4Y-5vjVhKhYXDg/s1600/1-1+test.PNG" /></a></div>
<br />
Добавил строчку в таблицу данных и получил вот такую "колбасу":<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhlGY0nP24SYWCHwZ3QKG6IOjCKOtIV-iGhzEdER_0jtKAqUMydCpCEuS5hKbooQ_ba3MbdAk7LAgP7iuQIXphT9UdrlNtS3GxUSVcXNpE00jfHBw30S7zijxD4ZxEv5EdnqFrcYQ/s1600/2nd+test.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhlGY0nP24SYWCHwZ3QKG6IOjCKOtIV-iGhzEdER_0jtKAqUMydCpCEuS5hKbooQ_ba3MbdAk7LAgP7iuQIXphT9UdrlNtS3GxUSVcXNpE00jfHBw30S7zijxD4ZxEv5EdnqFrcYQ/s1600/2nd+test.PNG" /></a></div>
<br />
Колбасу сразу же почистил - вынес значения колонки board в константы:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhwbYiBUDUXFWit-njT7QxeLoPI1xQTYKQBTAA7MXeeojD7zrbGHslbpxLIrQb0eqSD32Ida2P4nO2hY_UeXnLujlUHgWgcI_aU4ZC2RUv2Dww1R6036kJVUWEQ50ybkNl03mzuQw/s1600/2-2+test.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhwbYiBUDUXFWit-njT7QxeLoPI1xQTYKQBTAA7MXeeojD7zrbGHslbpxLIrQb0eqSD32Ida2P4nO2hY_UeXnLujlUHgWgcI_aU4ZC2RUv2Dww1R6036kJVUWEQ50ybkNl03mzuQw/s1600/2-2+test.PNG" /></a></div>
<br />
До этого момента тесты проходили, но я нашел ошибку в тесте и добавил еще одну проверку в expect<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh6H7d98jwsvSSmI-Ra17EGbZO2VAuTr6hQRdZDcpS5JA1qNiW7q-p-5Sf0rCxBHIwAtsNubc40hJsad0rw3SbPlezgHOWCbqfLB7RLdrYZeYJSuWgKrnhsUIgbmXDnW7CFEIsscA/s1600/2-3+failed.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh6H7d98jwsvSSmI-Ra17EGbZO2VAuTr6hQRdZDcpS5JA1qNiW7q-p-5Sf0rCxBHIwAtsNubc40hJsad0rw3SbPlezgHOWCbqfLB7RLdrYZeYJSuWgKrnhsUIgbmXDnW7CFEIsscA/s1600/2-3+failed.PNG" /></a></div>
<br />
После чего тесты упали. Вот так отображаются фейлы тестов:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgVBHD4HkJrQilthFBihqwaEXsYXrVDHjZ6UCCNE4idPrUFE4wunQ-ZIvuW9mIu6daVLeqG1v8ppP51NiXZr3NSmLOzx6CTCyP3IZQAbk15wXLbf6cMn3FjxRfdChjPtNf-Ux6Ixw/s1600/2-3+fail1.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgVBHD4HkJrQilthFBihqwaEXsYXrVDHjZ6UCCNE4idPrUFE4wunQ-ZIvuW9mIu6daVLeqG1v8ppP51NiXZr3NSmLOzx6CTCyP3IZQAbk15wXLbf6cMn3FjxRfdChjPtNf-Ux6Ixw/s1600/2-3+fail1.PNG" /></a></div>
<br />
Поскольку тест упал на каждой строке данных, то вот 2й фейл:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEioSWLkHzLloluse07v791eYdrs2xaNEppZFJwz8d-M-zF65roKznC-pyWRNkYx5oFsQTkoHD1DHXgYzVGULErJX4oP75Q5c_T3s7Xq0TDjI9zRhEkDtqYuC27uDc1pirFylpeLXA/s1600/2-3+fail2.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEioSWLkHzLloluse07v791eYdrs2xaNEppZFJwz8d-M-zF65roKznC-pyWRNkYx5oFsQTkoHD1DHXgYzVGULErJX4oP75Q5c_T3s7Xq0TDjI9zRhEkDtqYuC27uDc1pirFylpeLXA/s1600/2-3+fail2.PNG" /></a></div>
<h4>
Делаю репорты выполения читабельнее</h4>
Понравилась возможность выводить значения данных в имени тестового метода. Для этого есть аннотация @Unroll. Для примера я сделал следующий изврат.<br />
Стал использовать мапу со значениями, а ключ принимаю как параметр теста:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhmM1mkYAcabPVwuDQPbFBccV69oHZAKUZ4yKk1gHvfbN4y7Lp2Pa51CAYrXzLktcpAICzFJzaaghQQgfWzbyHZkHSdnSfO4qh_LOfy8uDlnZ4W776_-Spff8zFiFzBv1x_iAcrqw/s1600/3-unroll.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhmM1mkYAcabPVwuDQPbFBccV69oHZAKUZ4yKk1gHvfbN4y7Lp2Pa51CAYrXzLktcpAICzFJzaaghQQgfWzbyHZkHSdnSfO4qh_LOfy8uDlnZ4W776_-Spff8zFiFzBv1x_iAcrqw/s1600/3-unroll.PNG" /></a></div>
<br />
Инициализацию мапы вынес в специальный метод setupSpec. setupSpec вызывается 1 раз перед всеми тестами. Нестатические поля надо помечать как @Shared, если их значения используются в тестах:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiyUlNEn-fdC-LWdrtoUI3SJsE3vFgXLp_yNVcYqmnf8s2qggRl4IlbqFn-_EurwXXHvwb9MzMrOyzMgCEf87cKSzhlplF3h9lV2KQf_1lP2Ty-RmXn_aC74X5R6lKyIl7UqTcoOA/s1600/3-setupspec.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiyUlNEn-fdC-LWdrtoUI3SJsE3vFgXLp_yNVcYqmnf8s2qggRl4IlbqFn-_EurwXXHvwb9MzMrOyzMgCEf87cKSzhlplF3h9lV2KQf_1lP2Ty-RmXn_aC74X5R6lKyIl7UqTcoOA/s1600/3-setupspec.PNG" /></a></div>
<br />
Ну и зафейленые методы выглядят теперь так: <br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhCKEC3XHTswedZFERwahTgxKoqgIea178ekwkrUTKmymd6bULf0jba2jL0INrTn5nLtrF7mqh_NRhfUBq5_6fXgbLCqLgpuIrWOih5b39K_RP0DQr7lCZh7FKzuU4dBUhOs5ZgZA/s1600/3-fail.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhCKEC3XHTswedZFERwahTgxKoqgIea178ekwkrUTKmymd6bULf0jba2jL0INrTn5nLtrF7mqh_NRhfUBq5_6fXgbLCqLgpuIrWOih5b39K_RP0DQr7lCZh7FKzuU4dBUhOs5ZgZA/s1600/3-fail.PNG" /></a></div>
<br />
<br />
<h3>
Как подключить</h3>
<div>
1. Нужно добавить плагин. Использую maven-compiler-plugin, с поддержкой груви компиляции
<script class="brush: xml" type="syntaxhighlighter"><![CDATA[ <plugin>
<groupid>org.apache.maven.plugins</groupId>
<artifactid>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<compilerid>groovy-eclipse-compiler</compilerId>
<encoding>UTF-8</encoding>
</configuration>
<dependencies>
<dependency>
<groupid>org.codehaus.groovy</groupId>
<artifactid>groovy-eclipse-compiler</artifactId>
<version>2.8.0-01</version>
<exclusions>
<exclusion>
<groupid>org.codehaus.groovy</groupId>
<artifactid>groovy-eclipse-batch</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupid>org.codehaus.groovy</groupId>
<artifactid>groovy-eclipse-batch</artifactId>
<version>2.1.8-01</version>
</dependency>
</dependencies>
</plugin>
]]></script>
</div>
<div>
2. Добавить зависимости на spock и на сам groovy:
<script class="brush: xml" type="syntaxhighlighter"><![CDATA[ <dependency>
<groupid>org.spockframework</groupId>
<artifactid>spock-core</artifactId>
<version>1.0-groovy-2.0-SNAPSHOT</version>
<scope>test</scope>
</dependency>
<dependency>
<groupid>org.codehaus.groovy</groupId>
<artifactid>groovy-all</artifactId>
<version>2.2.1</version>
</dependency>
]]></script>
</div>
<div>
3. Поскольку я использовал снапшотную версию spok, то добавить репозиторий:
<script class="brush: xml" type="syntaxhighlighter"><![CDATA[ <repositories>
<!-- Only required if a snapshot version of Spock is used -->
<repository>
<id>spock-snapshots</id>
<url>http://oss.sonatype.org/content/repositories/snapshots/</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
</repositories>
]]></script>
</div>
<div>
После настройки плагина можно миксовать Groovy и Java код:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjAAZwn7kvuC-PI1QJqOM8nh540E3ZuGcsOYVpZogRDVnfIia91BVUuXZOnoWfFIardylCWoFFIjj6t1oAV1ISW5sxb_iGraRJs1c06WSA6NThupvuSvO76Bc7UyMbPVppKj8WM7A/s1600/project+structure.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjAAZwn7kvuC-PI1QJqOM8nh540E3ZuGcsOYVpZogRDVnfIia91BVUuXZOnoWfFIardylCWoFFIjj6t1oAV1ISW5sxb_iGraRJs1c06WSA6NThupvuSvO76Bc7UyMbPVppKj8WM7A/s1600/project+structure.PNG" /></a></div>
<br /></div>
<span style="font-weight: normal;">А если поставить spock плагин к идее, то будет вообще сказка</span><br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgDlaGOdXUnQVOe3yu34k-8cGk8RoSRyN6Pp-CdrUgqcnOAIZP-hFLSFFXwQB11sbWPvsCQmVJqcIMVS-Od5GGsLbqoDRY395VGNWkGB4_4o4nT1iJlopUBHMV26N13S_iWkI5Ikg/s1600/plugin.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgDlaGOdXUnQVOe3yu34k-8cGk8RoSRyN6Pp-CdrUgqcnOAIZP-hFLSFFXwQB11sbWPvsCQmVJqcIMVS-Od5GGsLbqoDRY395VGNWkGB4_4o4nT1iJlopUBHMV26N13S_iWkI5Ikg/s1600/plugin.PNG" /></a></div>
<span style="font-weight: normal;"><br /></span>
<br />
<h4>
Ресурсы</h4>
<div>
1. Страничка фреймворка: <a href="https://code.google.com/p/spock/">https://code.google.com/p/spock/</a></div>
<div>
2. Наш сайт с игрушками : <a href="http://codenjoy.com/">codenjoy.com</a>. Онлайн батлы : <a href="http://codenjoy.com/codenjoy-contest/">http://codenjoy.com/codenjoy-contest/</a></div>
<div>
3. Репозиторий на гитхабе с кодом : <a href="https://github.com/szelenin/bomberman">https://github.com/szelenin/bomberman</a></div>
<div>
4. Пример использования от разработчиков: <a href="https://github.com/spockframework/spock-example">https://github.com/spockframework/spock-example</a></div>
Сергей Зеленинhttp://www.blogger.com/profile/01667820453041864348noreply@blogger.com2tag:blogger.com,1999:blog-24004232.post-23691372549956464542014-01-02T00:01:00.001-08:002014-01-02T00:09:00.546-08:00Парадокс Монти ХоллаПарадокс Монти Холла - это одна из задачек, где интуиция не работает. Ну, по крайней мере, не работает у людей, малознакомых с теорией вероятности :)<br />
<br />
<h4>
<b>Суть загадки </b>в следующем:</h4>
Есть 3 двери. За одной из них - автомобиль, за другими - ничего (в оригинале там козел, но про козлов и так в последнее время слишком много пишут ;)). Ты выбираешь дверь. Затем ведущий открывает одну из оставщихся дверей, за которой <strike>козел</strike> пусто. Ты можешь поменять свой выбор или остаться со своей дверью. <br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhr44aloMTVkaX6D8nap2MfBolPIwrMuEMga_aBLQBo2dSiSuhshyENvlsTVCuXz_HJjuI4n47q5u5xvdigxKRdiNKCP2jJvxnH4GvPRg23XRp34W8RPNzt9KpotEn_WEQ3BEeTBw/s1600/330px-Monty_open_door.svg.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="177" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhr44aloMTVkaX6D8nap2MfBolPIwrMuEMga_aBLQBo2dSiSuhshyENvlsTVCuXz_HJjuI4n47q5u5xvdigxKRdiNKCP2jJvxnH4GvPRg23XRp34W8RPNzt9KpotEn_WEQ3BEeTBw/s320/330px-Monty_open_door.svg.png" width="320" /></a></div>
<br />
<h4>
Вопрос</h4>
Стоит ли менять дверь и какая вероятность выиграша в каждом варианте?<br />
Здесь я предлагаю подумать и предложить свой вариант. Как обычно в конце - правильный ответ ;)<br />
<br />
<h4>
Решение</h4>
Решить эту задачку, написав программу предлагают на <a href="http://cs109.org/" target="_blank">гарвардском курсе по Data Science</a>. Также очень понятное логическое объяснение дано <a href="http://ru.wikipedia.org/wiki/%D0%9F%D0%B0%D1%80%D0%B0%D0%B4%D0%BE%D0%BA%D1%81_%D0%9C%D0%BE%D0%BD%D1%82%D0%B8_%D0%A5%D0%BE%D0%BB%D0%BB%D0%B0" target="_blank">в википедии</a>. Я же воспользуюсь тем, что гарвард не обязывает подписывать соглашение о неразглашении для своих архивных онлайн курсов и расскажу как я решал эту задачку.<br />
<b>Внимание. Если ты решился пройти курс cs109, то лучше сделай это сам, без моих подсказок :).</b><br />
<br />
Решать задачку предлагается на Python с расширениями для работы с массивами и прочими математическими штуками <b>numpy. </b>Numpy, а также масса других полезностей для дата анализа на питоне уже содержится в инсталлере <a href="http://continuum.io/downloads" target="_blank">Anaconda</a>. Установка анаконды тривиальная, детали опустим.<br />
<br />
<h4>
Блокнот</h4>
Блокнот - обалденная штука в ipython. После установки anaconda просто запускаем команду:<br />
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;">ipython notebook</span><br />
<br />
И в окне браузера нажимаем New Notebook:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgjf6YgRWlM4ZMORr7NMDiM7QMkGkzTFmNZ_wAHQ7QS603N8R-1Y-u762VCSd-f8LjZfXNfPTIGgn1Mzb5DWBj-uW8e7BKf99J_r0uxdrNrSg314kLIVKAWNGpRwmSHvIrEW1vRgw/s1600/1.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="233" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgjf6YgRWlM4ZMORr7NMDiM7QMkGkzTFmNZ_wAHQ7QS603N8R-1Y-u762VCSd-f8LjZfXNfPTIGgn1Mzb5DWBj-uW8e7BKf99J_r0uxdrNrSg314kLIVKAWNGpRwmSHvIrEW1vRgw/s640/1.PNG" width="640" /></a></div>
<br />
Notebook - это что-то вроде вики, в которую можно вставлять код на питоне. Причем результаты будут отображаться тут же на странице. Код вместе с разметкой автоматически записывается в файл (в данном случае Untitled0.ipynb)<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhMnu6eRbo8WGqupYnvPDSOE2QQM3gOuVVEQ9gLmtYNRjJZvPPX_OxUxjipql6g-DGRBzxIraCU5_p0JST0xTmBdhlL_sR2nKzNPBmgfWvllvDLOfBnaquJD4zQYBGHVKxcezLRww/s1600/2.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em; text-align: center;"><img border="0" height="162" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhMnu6eRbo8WGqupYnvPDSOE2QQM3gOuVVEQ9gLmtYNRjJZvPPX_OxUxjipql6g-DGRBzxIraCU5_p0JST0xTmBdhlL_sR2nKzNPBmgfWvllvDLOfBnaquJD4zQYBGHVKxcezLRww/s640/2.PNG" width="640" /></a><br />
<br />
Импортирую модуль Numpy и вывожу его версию:<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgTvtTKVgdNJvh6Msm3LAlMVgi6rBkdkbD0JTaHv1TyGGlHVuDydZPxksg-_EYHEYRs0rGj6X55x_wZagDI0LoNwHWL9VDGVrIecePN_oKEU2qmVyCxV6FOqSrclkHGI5JQt8F2jQ/s1600/3.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em; text-align: center;"><img border="0" height="71" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgTvtTKVgdNJvh6Msm3LAlMVgi6rBkdkbD0JTaHv1TyGGlHVuDydZPxksg-_EYHEYRs0rGj6X55x_wZagDI0LoNwHWL9VDGVrIecePN_oKEU2qmVyCxV6FOqSrclkHGI5JQt8F2jQ/s640/3.PNG" width="640" /></a><br />
<br />
Определяю функцию <span style="font-family: Courier New, Courier, monospace;">simulate_prizedoor</span>, которая будет генерировать массив интов. Каждый элемент массива - номер двери, за которой находится авто.<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjmaZuowkqrnjPdNVZ6CXZrSau1IqawYVj1m93IqWC8in1OBwsuJnFXThW3cL9KQzFByAF6XinEwAZYMMJBd0NR9MJuMMX9kEB1CC7uoMim55cDGdGz61PELEYHd69ydTTqALbJFw/s1600/4.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em; text-align: center;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjmaZuowkqrnjPdNVZ6CXZrSau1IqawYVj1m93IqWC8in1OBwsuJnFXThW3cL9KQzFByAF6XinEwAZYMMJBd0NR9MJuMMX9kEB1CC7uoMim55cDGdGz61PELEYHd69ydTTqALbJFw/s1600/4.PNG" /></a><br />
<br />
Очень похожая функция <span style="font-family: Courier New, Courier, monospace;">simulate_guess</span>, которая создает массив интов, где каждый элемент - случайный выбор из 3х дверей. Код абсолютно такой же как и в simulate_prizedoor<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgjQ0a52Gct9EEqPOR5vZPkUYZYUh25HV7nLkklfSR7HLerfhVMGunC-OnztyfgivJeEdg33z0YspFHDbBNKotnXEmSshejCo9maZ7V9CleyPqBIZTG6G8oQJalC1ne7OpdphoaoA/s1600/5.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em; text-align: center;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgjQ0a52Gct9EEqPOR5vZPkUYZYUh25HV7nLkklfSR7HLerfhVMGunC-OnztyfgivJeEdg33z0YspFHDbBNKotnXEmSshejCo9maZ7V9CleyPqBIZTG6G8oQJalC1ne7OpdphoaoA/s1600/5.PNG" /></a><br />
<br />
Дальше определяю функцию <span style="font-family: Courier New, Courier, monospace;">goat_door</span>, которая выбирает невыбранную пустую дверь. На входе массив с номером двери, за которой авто и массив с номером выбранной двери.<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi8oExDJ1PCTeK4OLshgeJHaVWRm0p7GThQ2eij8uok5o32fR3zJVsD09Gwtzxe7Ec3p2QGSzGF-_foKlqnJpbNOC3B9MuA1LIc130bAN9g89G-L5m3Ahx0p_fQ13JkVTedGIWVKg/s1600/6.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em; text-align: center;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi8oExDJ1PCTeK4OLshgeJHaVWRm0p7GThQ2eij8uok5o32fR3zJVsD09Gwtzxe7Ec3p2QGSzGF-_foKlqnJpbNOC3B9MuA1LIc130bAN9g89G-L5m3Ahx0p_fQ13JkVTedGIWVKg/s1600/6.PNG" /></a><br />
<br />
Функция <span style="font-family: Courier New, Courier, monospace;">switch_guesses </span>позволяет менять выбор двери<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiUDmX_0eCouT67qo2vuM8ljldIr9yfOW7LY_ZNn1W9m2TBA06j0KCKdShH4lz4zPx81LL2-GbpqBxqZeNYKeNk9awd4uShy_slREUmkYt39jemouKuRWkB8NVZOFFCLs5uRlsjgg/s1600/7.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em; text-align: center;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiUDmX_0eCouT67qo2vuM8ljldIr9yfOW7LY_ZNn1W9m2TBA06j0KCKdShH4lz4zPx81LL2-GbpqBxqZeNYKeNk9awd4uShy_slREUmkYt39jemouKuRWkB8NVZOFFCLs5uRlsjgg/s1600/7.PNG" /></a><br />
<br />
Дальше функция <span style="font-family: Courier New, Courier, monospace;">win_percentage</span> - подсчет процента угадываний<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg11EiAwnlTPNrcQ4R1bhHR-cRDpfugT4vWuI0n9R63vTOJhweJ1exU5-8UdLb5PtQ6ioHX5PfS637ZzpQCW60_S3gsoHWkqGWCIoAxBNGBJ1qNJLXVsiR1NGVO1hx7g1urvqis5g/s1600/8.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em; text-align: center;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg11EiAwnlTPNrcQ4R1bhHR-cRDpfugT4vWuI0n9R63vTOJhweJ1exU5-8UdLb5PtQ6ioHX5PfS637ZzpQCW60_S3gsoHWkqGWCIoAxBNGBJ1qNJLXVsiR1NGVO1hx7g1urvqis5g/s1600/8.PNG" /></a><br />
<br />
И, наконец, запускаю эксперимент 10тыс раз и выводим результат:<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgYOjZvmmJiDPAbeVueYosHSJ4aOHUInbBuVTnOvSBNteQ-b3_Enfe9y9thZfqsqBr5gAvUnq7LZyv7G3zuEWHRKwgzfWwZVcXgymiP5d2wlGKWVOamIQvYJG14l-E28w-iVWkp6Q/s1600/9.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em; text-align: center;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgYOjZvmmJiDPAbeVueYosHSJ4aOHUInbBuVTnOvSBNteQ-b3_Enfe9y9thZfqsqBr5gAvUnq7LZyv7G3zuEWHRKwgzfWwZVcXgymiP5d2wlGKWVOamIQvYJG14l-E28w-iVWkp6Q/s1600/9.PNG" /></a><br />
<br />
<br />
<h4>
Вывод</h4>
Полностью совпадает с теорией. Если менять решение, то вероятность выиграша 66.6%. Подробнее о теории можно почитать в <a href="http://ru.wikipedia.org/wiki/%D0%9F%D0%B0%D1%80%D0%B0%D0%B4%D0%BE%D0%BA%D1%81_%D0%9C%D0%BE%D0%BD%D1%82%D0%B8_%D0%A5%D0%BE%D0%BB%D0%BB%D0%B0" target="_blank">википедии</a><br />
<h4>
Ресурсы</h4>
1. <a href="http://cs109.org/" target="_blank">Гарвардский курс Data Science</a><br />
2. <a href="http://ru.wikipedia.org/wiki/%D0%9F%D0%B0%D1%80%D0%B0%D0%B4%D0%BE%D0%BA%D1%81_%D0%9C%D0%BE%D0%BD%D1%82%D0%B8_%D0%A5%D0%BE%D0%BB%D0%BB%D0%B0" target="_blank">Парадокс Монти Холла в википедии</a><br />
3. <a href="http://continuum.io/downloads" target="_blank">Анаконда</a><br />
4. Код здесь: <a href="https://github.com/szelenin/cs109">https://github.com/szelenin/cs109</a>Сергей Зеленинhttp://www.blogger.com/profile/01667820453041864348noreply@blogger.com0tag:blogger.com,1999:blog-24004232.post-460946777605637672013-09-28T10:42:00.000-07:002013-09-30T00:57:00.821-07:00Выжить при крушении. Улучшаем прогноз.В прошлый раз я использовал линейную регрессию для прогнозирования того, выжил ли пассажир при крушении или нет. Точность прогноза составила 0.77, что неплохо для начала. Самыми значимыми переменными (по значению P-Value) были Sex, Fare, Age. Их я и брал в для прогнозирования.<br />
<br />
<h3>
Смотрим чего не хватает</h3>
Что делать, если для некоторых значимых переменных отсуствуют значения? Например, для поля возраст в тренировочном наборе отсутствует значение в 177 случаях:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh8MseNEV-8gXWxUKqJKLgp67qpYiVvUv8xpt_0nmoIDGkRf40mT4D9ifbJAyfxDjr55NUPOE7Oybm4AlM7cMYRnGBDBMV_Wb7h7e6QM9I195X3g5Q2XuvRJMGZEvfxc2noLQoWHQ/s1600/1.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh8MseNEV-8gXWxUKqJKLgp67qpYiVvUv8xpt_0nmoIDGkRf40mT4D9ifbJAyfxDjr55NUPOE7Oybm4AlM7cMYRnGBDBMV_Wb7h7e6QM9I195X3g5Q2XuvRJMGZEvfxc2noLQoWHQ/s1600/1.PNG" /></a></div>
Один из участников соревнования предложил заполнить эти значения спрогнозированными данными. Детальнее можно ознакомиться на <a href="https://www.kaggle.com/c/titanic-gettingStarted/forums/t/5232/r-code-to-score-0-79426" target="_blank">форуме Kaggle</a>, я же опишу как я применил его идею.<br />
<br />
<h3>
Добавляем новую колонку</h3>
От каких переменных может зависеть возраст? Возможно, что от количества детей (Parch) или братьев-сестер (Sibsp), а может стоимости билета (Fare)? Может можно из данных вытащить дополнительную информацию, которая нам помогла бы спрогнозировать возраст? Например, имя пассажира уже содержит его титул: Mr, Mrs, Miss, Master.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhu7ugLOhBl2xPQZVvVjP_PV3RIgp1sc7HVBCg1jbUqwb8l30iYPCAh5qDHuWnHUrMkA7i5s5VH9HKccWA13ltMcp65_-sELFK_edVRMW_PurM-7eGBr3JQ1wascFIDE5piGLqvmg/s1600/2.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhu7ugLOhBl2xPQZVvVjP_PV3RIgp1sc7HVBCg1jbUqwb8l30iYPCAh5qDHuWnHUrMkA7i5s5VH9HKccWA13ltMcp65_-sELFK_edVRMW_PurM-7eGBr3JQ1wascFIDE5piGLqvmg/s1600/2.PNG" /></a></div>
<br />
Написать скрипт, который из полного имени вытащит титул из имени не составит особого труда. Я написал его на питоне, точнее на его расширенной версии <a href="https://store.continuum.io/cshop/anaconda/" target="_blank">Anaconda</a>, которая содержит массу нужных пакетов, в том числе модуль для чтения/записи csv файлов. Скрипт находится <a href="https://dl.dropboxusercontent.com/u/22607711/AddTitle.py" target="_blank">здесь</a>. После обработки в файлах с тренировочными и тестовыми данными появилась колонка Title.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhgAYN17gJBPk704ZOWPX8mgtG_YopL0YVfTKdhGjT1ugTbyMt5nWHnrwZTsc01cAJwFSzD89RzgJhhB-Y3PSpKcsqy3ylEOIFfWqrUfwpR5BF-CNKw0LaAYzIt2RC587eTAvk19w/s1600/3.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhgAYN17gJBPk704ZOWPX8mgtG_YopL0YVfTKdhGjT1ugTbyMt5nWHnrwZTsc01cAJwFSzD89RzgJhhB-Y3PSpKcsqy3ylEOIFfWqrUfwpR5BF-CNKw0LaAYzIt2RC587eTAvk19w/s1600/3.PNG" /></a></div>
Считываю полученный csv файл в R:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjsrNqWlcbT9DGYvTWS0xVNPGffXFpr4Gtzz0-nogsNtawb1YhUdzRvx_76JMhQt7mMdkVosTZX4QwFiD6Y_2Yy8dl3-OIdSpBa2vtdhniJBjBPcuCdl0Ii2CjEsP7hy3UtRFdpiA/s1600/4.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjsrNqWlcbT9DGYvTWS0xVNPGffXFpr4Gtzz0-nogsNtawb1YhUdzRvx_76JMhQt7mMdkVosTZX4QwFiD6Y_2Yy8dl3-OIdSpBa2vtdhniJBjBPcuCdl0Ii2CjEsP7hy3UtRFdpiA/s1600/4.PNG" /></a></div>
<br />
<h3>
Определяем переменные с помощью corrgram</h3>
Дальше определим, от каких переменных зависит возраст. Для этого воспользуюсь таким графиком, который называется коррелограмма (correlogram). Его я тоже нарыл на том же форуме, ссылка внизу. Реализована эта красота в пакете corrgram.<br />
Поскольку corrgram показывает зависимости между числовыми переменными, добавлю колонку TitleNum, в которой будет числовое значение от Title:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgCUFsePaoUpOY0g4cfLNtOLffoq6VjU1mx7oskjJFow9FdF0NLnlitguJhWIuJPhnp5X36GILG6ruKYHG68Jrz6JUplcRw725KcUTOV_nrnnbcIJs4OAGFVdOOcDyX20EsKzMiVg/s1600/5.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgCUFsePaoUpOY0g4cfLNtOLffoq6VjU1mx7oskjJFow9FdF0NLnlitguJhWIuJPhnp5X36GILG6ruKYHG68Jrz6JUplcRw725KcUTOV_nrnnbcIJs4OAGFVdOOcDyX20EsKzMiVg/s1600/5.PNG" /></a></div>
Строю график.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgvt1yQxz81G1rBa7oR6zGHeEGuZkF2vmnOCtwRhAG8I96UsVDMjSc5wqqI5uWfOODz76BfLBTB37dnsy4QYihc_wyaXJIEcymeJ7MJIgUa94oNaZm59JBZ9aS0FuGO3njDK6QOmA/s1600/corrgram.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgvt1yQxz81G1rBa7oR6zGHeEGuZkF2vmnOCtwRhAG8I96UsVDMjSc5wqqI5uWfOODz76BfLBTB37dnsy4QYihc_wyaXJIEcymeJ7MJIgUa94oNaZm59JBZ9aS0FuGO3njDK6QOmA/s1600/corrgram.PNG" /></a></div>
Вот что получилось:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh13H5RaXyoaljzDDepogDmzzYD1AXc3JPH4QeC-scujX-jcBPEBdyNGKVrde681SJ9TgCI7TPOXw9VXeZ1sU4pslbUQG4BoBlmcdtxhGfQLjrBi1RII2QqDPTLGhV0BzoPjEMumg/s1600/6.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="370" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh13H5RaXyoaljzDDepogDmzzYD1AXc3JPH4QeC-scujX-jcBPEBdyNGKVrde681SJ9TgCI7TPOXw9VXeZ1sU4pslbUQG4BoBlmcdtxhGfQLjrBi1RII2QqDPTLGhV0BzoPjEMumg/s400/6.PNG" width="400" /></a></div>
Чем насыщеннее цвет в нижней части, тем более тесная связь между переменными. Синий цвет - связь положительная, красный - отрицательная. Интересные графики в верхней части показывают эти изменения графически (чтобы отобразились графики в верхней части нужно задать параметер upper.panel=panel.ellipse ).<br />
Например, между Pclass и Age есть сильная отрицательная связь, т.к. соответствующий цвет квадратика в нижней части темно-красный. На графике так же видно, что с уменьшением Pclass возраст пассажира увеличивается (сейчас, как и 100 лет назад первым классом в основном путешествовали старперы, студики и прочий нищеброд ютились в эконом классах).<br />
<div>
<br /></div>
<h3>
Заполняем недостающие данные</h3>
<div>
Переменные, наиболее связанные с возрастом определили - это Pclass, SibSp и Title. Осталось заполнить отстутствующие значения и запихнуть в общую модель. Недостающие значения в тренировочных и тестовых данных я проставляю в процедуре, которую вызываю перед тем, как построить модель и делать прогноз. Вот код функции:</div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">```{r}</span></div>
<div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">predictMissing <- function(dataSet){</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> #Find missing Age</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> age.model = lm(Age~as.factor(Title) + SibSp + Pclass, data=dataSet)</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> fare.model = lm(Fare~Parch+Pclass, data=dataSet)</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> for (i in 1:nrow(dataSet)){</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> if (is.na(dataSet[i, "Age"])){</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> dataSet[i,"Age"] = predict(age.model, newdata=dataSet[i,])</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> }</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> }</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"> return(dataSet)</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">}</span></div>
</div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">```{r}</span><br />
<br />
<h3>
Финальный результат</h3>
После отправки новых прогнозов на Kaggle я увеличил счет на один процент(с 0.77512 до 0.78469). Немного, но главное - это полученный опыт! :)<br />
<br />
<h3>
Ресурсы</h3>
1. Пост от Wallace Campbell. https://www.kaggle.com/c/titanic-gettingStarted/forums/t/5232/r-code-to-score-0-79426<br />
2. Anaconda, python visualization and explaration tool. <a href="https://store.continuum.io/cshop/anaconda/">https://store.continuum.io/cshop/anaconda/</a><br />
3. Скрипт добавления колонки title. https://dl.dropboxusercontent.com/u/22607711/AddTitle.py<br />
4. Пост на Kaggle форуме про корелограммы. <a href="https://www.kaggle.com/c/titanic-gettingStarted/prospector#489">https://www.kaggle.com/c/titanic-gettingStarted/prospector#489</a>Сергей Зеленинhttp://www.blogger.com/profile/01667820453041864348noreply@blogger.com0tag:blogger.com,1999:blog-24004232.post-5332845382380972682013-09-02T07:10:00.002-07:002013-09-30T00:57:09.770-07:00Выжить при крушении. Анализ данных катастрофы "Титаник".Все знают чем закончилась эта печальная история с трансатлантическим рейсом, который совершал "Титаник" в апреле 1912 года. В результате крушения погибло 1495 человек, спаслось 712. Эта катастрофа стала предметом многих исследований, документальных и художественных фильмов. Создатели онлайн площадки для научного моделирования <a href="http://www.kaggle.com/" target="_blank">Kaggle</a> тоже решили использовать эту историю вместе с данными о всех пассажирах Титаника для тренировочного задания.<br />
<br />
<h4>
Коротко о задании</h4>
Есть данные о 891м пассажире (в формате csv), которые содержат класс, которым путешествовал, имя, пол, возраст, цену билета и т.д. Также дана информация о том выжил ли пассажир. Это т.н. тренировочный набор данных. Вместе с тренировочными данными прилагается тестовый набор, который содержит такую же информацию о еще 418 пассажирах кроме индикатора "выжил/нет". Необходимо предсказать кто из 418 выжил.<br />
Более подробно о деталях можно посмотреть здесь <a href="https://www.kaggle.com/c/titanic-gettingStarted">https://www.kaggle.com/c/titanic-gettingStarted</a><br />
<br />
<h4>
Инструменты</h4>
<div>
Для построения модели я буду использовать R и RStudio. Ссылки для скачивания внизу. Мне удобнее работать в RStudio, чем в консоли R т.к. там наряду с возможностью выполнять команды можно создать исполняемый документ (Rmd - R Markup Document или "чистый" R). RMD - это Markup document со скриптовыми вставками на языке R, в результате выполнения которого получается HTML.<br />
<br /></div>
<h4>
Загружаем данные</h4>
<div>
Загрузил тренировочный и тестовый наборы данных. Полный тренировочный набор разделил на данные для кросс-валидации (30% записей) и тренировочный (trainFaith). Это делается для того,чтобы затем можно было оценить эффективность модели. На тренировочных данных (trainFaith) буду "обучать" модель, а данные для кросс-валидации (cvFaith) использую для оценки.<br />
Делал примерно так:<br />
<br /></div>
<div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">```{r}</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">set.seed(333)</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">l=length(train[,1]);</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">cvSamples <- sample(1:l,size=(l/3),replace=F)</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">cvFaith <- train[cvSamples,]</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">trainFaith <- train[-cvSamples,]</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">```</span></div>
</div>
<div>
<br /></div>
<h4>
Определяем переменные</h4>
От чего зависит выжил пассажир или нет? Возможно, что сначала спасали женщин и детей, а может пассажиров первого класса спасали первыми? По одноименному фильму вполне можно сделать такие предположения. Тот, кто смотрел фильм, уже может считаться "экспертом" в предметной области. А что делать человеку, который ничего не слышал о Титанике? Можно использовать научный подход к определению первоначальных переменных для дальнейшего построения модели.<br />
Для "научного" подхода я буду использовать P-Values. Очень хорошее объяснение что такое P-Values можно посмотреть на <a href="https://www.khanacademy.org/" target="_blank">Khan Academy</a>, набор лекций <a href="https://www.khanacademy.org/math/probability/statistics-inferential/hypothesis-testing/v/hypothesis-testing-and-p-values" target="_blank">Hypothesis Testing and P-values</a>.<br />
Если коротко, то P-Value - это величина, которая показывает насколько принятая гипотеза не влияет на данные. Например, гипотеза: у женщин больше шансов быть спасенными при крушении. Если эта гипотеза неверна, то P-value для поля Sex по отношению к Survived будет близко к 1. К сожалению, не всегда можно сказать, что если P-value близко к 0, то гипотеза верна, т.к. на данные могут влиять другие факторы.<br />
Самый простой способ получить эти значения в R - это "впихнуть" (по-англ. fit) данные в линейную модель и вызвать на ней функцию summary. Самая простая модель <a href="http://www.machinelearning.ru/wiki/index.php?title=%D0%9B%D0%B8%D0%BD%D0%B5%D0%B9%D0%BD%D0%B0%D1%8F_%D1%80%D0%B5%D0%B3%D1%80%D0%B5%D1%81%D1%81%D0%B8%D1%8F_(%D0%BF%D1%80%D0%B8%D0%BC%D0%B5%D1%80)" target="_blank">линейной регрессии</a>, ее и возьмем:<br />
<br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">```{r}</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">glm1=glm(Survived ~ Sex + Fare + Age + SibSp + Parch, data=train, family="binomial")</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">summary(glm1)</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">```</span><br />
Здесь family="binomial" указана для построения <a href="http://www.machinelearning.ru/wiki/index.php?title=%D0%9B%D0%BE%D0%B3%D0%B8%D1%81%D1%82%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%B0%D1%8F_%D1%80%D0%B5%D0%B3%D1%80%D0%B5%D1%81%D1%81%D0%B8%D1%8F" target="_blank">логистической регрессии</a>. <br />
Вот что получилось. Смотрим сразу на раздел coefficients, колонка Pr:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh_KcqpR6OBE6Jj1m8T65sLVMiv9B8YP4u4U21nvCvrmcdTEZ5Iz_LXVA9hyphenhyphenLIEQcY7VCkqX6HDSIu0zaBaFUATQK6JqUnig8U8v8JJeqkXKRnclfnzbg2EFcP-n8ZcnVwQaEI5-A/s1600/coefficients.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh_KcqpR6OBE6Jj1m8T65sLVMiv9B8YP4u4U21nvCvrmcdTEZ5Iz_LXVA9hyphenhyphenLIEQcY7VCkqX6HDSIu0zaBaFUATQK6JqUnig8U8v8JJeqkXKRnclfnzbg2EFcP-n8ZcnVwQaEI5-A/s1600/coefficients.PNG" /></a></div>
Для построения модели возьму переменные Sex, Fare, Age и SibSp.<br />
<br />
<h4>
Обучение модели</h4>
Тут все просто. Впихиваю тренировочные данные в модель. В качестве набора - trainFaith<br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"><br /></span>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">```{r}</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">predictFunction = Survived ~ Sex + Fare + Age + SibSp</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">glm1 = glm(predictFunction, data=trainFaith, family="binomial")</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">```</span><br />
<div>
<br /></div>
<b>Прогнозирование</b><br />
Используем обученную модель для предсказаний кто выжил. Встроенная функция predict делает практически все что нам надо. Параметер type="response" конвертирует значения из логарифмической шкалы в обычную. В качестве данных используем cvFaith (помним, что это 30% случайно отобранных данных из полного тренировочн набора).<br />
Далее я создал новый набор, который состоит из 2х колонок: предсказанное значение и PassengerId.<br />
<br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">```{r}</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">rawpredict=predict(glm1, newdata=cvFaith, type="response")</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">cvPredicted=data.frame(Survived = rawpredict, PassengerId = cvFaith$PassengerId)</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">head(cvPredicted)</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">```</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"><br /></span>
<br />
<div>
Вероятность выживания в колонке Survived:</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi8RLLtHS8J49lg7JGKrLuVIayrzY_rItfuAFin3cQw-xunJ0dILRme110fJKlRup3kskGKfq-B24t9F98Ia67UZQTUY0WyTuOQ2iTL3lXPWpVctbuVyIrimEV3C4k1xt29jSYKtg/s1600/predicted.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi8RLLtHS8J49lg7JGKrLuVIayrzY_rItfuAFin3cQw-xunJ0dILRme110fJKlRup3kskGKfq-B24t9F98Ia67UZQTUY0WyTuOQ2iTL3lXPWpVctbuVyIrimEV3C4k1xt29jSYKtg/s1600/predicted.PNG" /></a></div>
<h4>
</h4>
<h4>
Оценка</h4>
<div>
Как известно, что выжить частично очень сложно, поэтому предсказанные значения, которые находятся в диапазоне от 0 до 1 нужно конвертнуть в 0 (не выжил) или 1 (выжил). Тут я решил выпендриться и использовать стороннюю библиотеку. Я хочу посмотреть изменится ли точность моих предугадываний, если в качестве порога я возьму число, отличное от 0.5.</div>
<div>
<br /></div>
<div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">```{r}</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">library(ROCR)</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">pred = prediction(cvPredicted$Survived, cvFaith$Survived)</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">perf <- performance(pred,"acc")</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">plot(perf)</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">```</span></div>
</div>
<div>
Получился такой график:</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgWK1EVuNII7uUl7LDtk-ZHn7t_YBAAo64A-YBp1tC0SziQs6OY_HW4V-XAoPbZlONLgqaSF9A8rTmgtbe4_GLtO81CgHRISCLRaTtsAnJj1XdtMR07i6mvYHefgWoE9CP9OzUbLA/s1600/cutoff.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="292" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgWK1EVuNII7uUl7LDtk-ZHn7t_YBAAo64A-YBp1tC0SziQs6OY_HW4V-XAoPbZlONLgqaSF9A8rTmgtbe4_GLtO81CgHRISCLRaTtsAnJj1XdtMR07i6mvYHefgWoE9CP9OzUbLA/s320/cutoff.PNG" width="320" /></a></div>
<div>
По оси Y точность прогнозирования (accuracy), по оси X - порог. Т.е. если в качестве порога я возьму 0.8, то точность модели будет около 0.65. Выбрать 0.5 в качестве порога вполне резонно, т.к. это обеспечит максимальную точность.</div>
<div>
<div>
<br /></div>
<div>
Теперь обучаю модель на полном наборе тренировочных данных и создаю финальный набор, который потом запишу в файл.</div>
<div>
<br /></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">```{r}</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">threshold = function (val) as.numeric(val > 0.5)</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">glmProd = glm(predictFunction, data=train, family="binomial")</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">rawpredict=predict(glmProd, newdata=test, type="response")</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">predicted=data.frame(Survived = threshold(rawpredict), PassengerId = test$PassengerId)</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">```</span><br />
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;"><br /></span></div>
</div>
<div>
Запись в файл производится одной командой:</div>
<div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">```{r}</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">write.table(predicted, "D:/Dropbox/kaggle/Titanic/output-R.csv", row.names=FALSE, quote=FALSE, sep=",", append=FALSE)</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace; font-size: x-small;">```</span></div>
</div>
<h4>
<span style="font-weight: normal;">После отправки на kaggle эта модель дала точность 0.77033. Это немногим больше, если прогнозировать только по полю Sex (это дало бы 0.7655). Но для начала пойдет. </span></h4>
<div>
<span style="font-weight: normal;"><br /></span></div>
<h4>
</h4>
<h4>
Ресурсы</h4>
1. <a href="http://cran.r-project.org/bin/">Язык для статистической обработки данных R</a><br />
2. <a href="http://www.rstudio.com/ide/download/desktop">RStudio - IDE для R</a><br />
3. P-Values <a href="https://www.khanacademy.org/math/probability/statistics-inferential/hypothesis-testing/v/hypothesis-testing-and-p-values">https://www.khanacademy.org/math/probability/statistics-inferential/hypothesis-testing/v/hypothesis-testing-and-p-values</a><br />
4. Здесь много алгоритмов моделирования : <a href="http://www.machinelearning.ru/">http://www.machinelearning.ru</a><br />
5. Пакет ROCR <a href="http://rocr.bioinf.mpi-sb.mpg.de/">http://rocr.bioinf.mpi-sb.mpg.de/</a><br />
6. <a href="https://dl.dropboxusercontent.com/u/22607711/Titanic.Rmd" target="_blank">Исходный текст</a>Сергей Зеленинhttp://www.blogger.com/profile/01667820453041864348noreply@blogger.com0tag:blogger.com,1999:blog-24004232.post-15358612612776792542013-05-07T04:03:00.003-07:002013-05-07T06:25:23.453-07:00Стоит ли проверять свои изменения перед комитом? Оцениваем сколько может стоить ошибка.Это продолжение предидущего поста, в котором я с помощью сетей Байеса попытался оценить влияние моей несознательности на качество приложения. С результатами исследования можно <a href="http://bit.ly/17t8VLk" target="_blank">ознакомиться тут</a>. Для тех, кто не читал, ниже приведена цель исследования и результат предидущей исследовательской итерации. Надеюсь этого вполне хватит, чтобы понять нижеследующий опус.<br />
<br />
<h3>
Цель и ожидаемый результат.</h3>
<div>
<br />
Хочу обосновать (или опровергнуть) необходимость запускать приложение перед комитом для проверки своих изменений. Моя интуиция говорит, что в большинстве случаев этого можно не делать, если есть хорошие юнит тесты.<br />
<br />
<h3>
Выводы предидущего исследования.</h3>
Согласно нашей модели если я, как разработчик, никогда не буду тестировать свои изменения перед тем, как отдать тестировщику, то в 58% случаев это сработает и качество системы не пострадает. Если же я всегда буду прогонять предкомитные тесты, то следует ожидать качественную систему в 78%.<br />
<div>
<br /></div>
<h3>
Приводим проценты к денежному эквиваленту.</h3>
<div>
Ок, основываясь на оптимистичных ожиданиях и субьективных оценках мы получили разницу в 20% между вероятностями "хорошей" и "плохой" сборки приложения в зависимости от того, поленюсь я или нет запустить приложение перед комитом. </div>
<div>
Много это или мало? Кто-то наверняка скажет, что 20% - это много и лучше бы разработчику потратить лишних 10 минут чтобы убедиться, что он отдает неполоманную систему. Однако лично мне эти проценты не позволяют делать окончательные выводы. </div>
<div>
<br /></div>
<div>
Я основываюсь на следующем предположении:<br />
<br />
<h4>
Предположение</h4>
</div>
<div>
Если в большинстве случаев мои изменения "проканают", то мне незачем тратить время на тестирование. Т.е. в том случае, если я буду тестировать, то я потрачу (к примеру) 10 минут + тестировщик тоже потратит 10 минут. Если тестировщик ничего не найдет, то свои 10 минут я потратил впустую. Если же тестировщик что-то найдет, то сэкономленного мной времени должно с лихвой хватить на исправление замечаний от тестера.</div>
<div>
<br /></div>
<h4>
Метод оценки</h4>
<div>
В предидущем исследовании мы получили вероятностые зависимости между моим действием проверять/не проверять систему (переменная <b>C</b>heck, дальше <b>C</b>) и качеством приложения, выраженном через то, переоткроется ли баг (переменная <b>R</b>eopen, дальше <b>R</b>) и найдутся ли новые ошибки (переменная <b>I</b>ntroduced new issues, дальше <b>I</b>).<br />
Как же, имея вышеперечисленные вероятностные характеристики оценить мои и тестировщика трудозатраты в вариантах, когда система мной проверена (<b>C</b>=1) и когда не проверена (<b>C</b>=0)?</div>
<div>
Я воспользуюсь методом, который применяется в алгоритме поиска оптимального решения в дереве возможных вероятных действий/состояний <a href="http://en.wikipedia.org/wiki/Expectiminimax_tree" target="_blank">expectimax search</a>. Много умных слов, но смысл попытаюсь донести на дурацком примере.<br />
<br /></div>
<div>
<h4>
Дурацкий пример</h4>
</div>
<div>
Предположим стоит очень жаркий день и ты проходишь мимо киоска с очень холодными напитками (или мороженым). У тебя есть выбор: пройти мимо или выпить чего-нибудь холодного. Если выпьешь холодного, то жару станет переносить чуть проще, но есть вероятность заболеть ангиной.</div>
<div>
<br /></div>
<div>
Выразим эту историю в виде структуры (дерево), которую можно загрузить в алгоритм поиска. Основными элементами дерева являются: состояние (state), действие (action), вероятность перехода в новое состояние (<b>P</b>) и выгода (<b>U</b>tility). Здесь многовато новых терминов в одном предложении, поэтому вот поясняющий рисунок.</div>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgzIbeY_l1BwcSpZe_WXJzKcHXIP_BXmwgaDvhGNtiwKXnx3PNU4HbyzFtLjeHGEzKFi0D98HdKIhDS97om4nmb9NAoQ7kzI6CHiEtB5dcfu-O55-5wZm0SQ0gT89NmvXxG7yhoiA/s1600/1-expectimax.PNG" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="227" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgzIbeY_l1BwcSpZe_WXJzKcHXIP_BXmwgaDvhGNtiwKXnx3PNU4HbyzFtLjeHGEzKFi0D98HdKIhDS97om4nmb9NAoQ7kzI6CHiEtB5dcfu-O55-5wZm0SQ0gT89NmvXxG7yhoiA/s400/1-expectimax.PNG" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;"><b>Рис 1</b>. Дерево вероятностных состояний</td></tr>
</tbody></table>
<div>
<br /></div>
<div>
В текущем состоянии (очень жарко) можно сделать 2 действия (actions): пройти мимо киоска или выпить ледяной напиток. </div>
<div>
<br />
<ul>
<li>Если пройти мимо, то это заберет 10 "условных единиц" здоровья. -10 - это выгода (<b>utility</b>), которую мы получим, перейдя в новое состояние. Вероятность перехода (P) в это новое состояние = 1.0.</li>
<li>Если выпить напиток, то тут немного интереснее и возможны следующие варианты:</li>
<ul>
<li>Можно заболеть с вероятностью 0.2, при этом здоровье пострадает на 100 условных единиц.</li>
<li> Может все будет хорошо и здоровье улучшится на 10 пунктов. Вероятность такого варианта 0.8.</li>
</ul>
</ul>
</div>
<div>
Теоретически дерево можно продолжить, т.к. в каждом новом состоянии могут быть доступны новые действия, каждое из которых может привести в очередное состояние.<br />
Например, если в качестве прохладительного напитка выбрать пиво, то в удовлетворенном состоянии может появиться действие "взять водки", "взять еще пива" и т.д. Каждое действие в свою очередь может приводить в новое состояние, причем <a href="http://ru.wikipedia.org/wiki/%D0%AD%D0%BC%D0%BF%D0%B8%D1%80%D0%B8%D0%B7%D0%BC" target="_blank">эмпирическим</a> путем установлено, что вероятность попадания в состояние с крестиком после действия "взять водки" значительно возрастает. Но для демонстрации метода и целей нашего исследования достаточно одного уровня.<br />
<br /></div>
<div>
Имея набор вероятных состояний с ассоциированной выгодой (utility) можно определить какое действие предпринять. Для этого нужно посчитать <a href="http://ru.wikipedia.org/wiki/%D0%9C%D0%B0%D1%82%D0%B5%D0%BC%D0%B0%D1%82%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%BE%D0%B5_%D0%BE%D0%B6%D0%B8%D0%B4%D0%B0%D0%BD%D0%B8%D0%B5" target="_blank">математическое ожидание</a> (expected value, дальше EV) для каждого действия и выбрать то, у которого значение EV выше. Посчитать EV довольно просто - нужно сложить все возможные выгоды, умноженные на вероятности их получения:</div>
$$EV_a=\sum_{s'}{P(s,a,s') U(s')}$$
<br />
<div>
Здесь \(EV_a\) - ожидаемая выгода действия <i>a</i>. \(P(s,a,s')\) - вероятность попадания в состояние <i>s'</i> если выбираем действие a. \(U(s')\) - выгода, которую мы получим, когда перейдем в состояние <i>s'</i>.<br />
<br />
В этом примере значения EV такие:<br />
<ul>
<li>Если выбираем "пройти мимо": \(EV=1.0 (-10) = -10\)</li>
<li>Если выбираем "выпить" \(EV=0.8*10 + 0.2 (-100) = -12\)</li>
</ul>
<br />
<div>
Если сравнить значения ожидаемой выгоды, то очевидно, что лучше не пить холодных напитков в жару (\(-10 > -12\)).</div>
<div>
<br /></div>
<h3>
Строим дерево</h3>
<h4>
Возможные состояния</h4>
<div>
Теперь определим, какие состояние возможны если делать проверку перед комитом и если ее не делать. Очевидно, что набор состояний в обоих случаях одинаковый:</div>
<div>
<br />
<ul>
<li>Изменение не принесло новых ошибок и мой фикс был успешно протестирован в новом билде (<b>I</b>=0 и <b>R</b>=0)</li>
<li>Баг отрепродюсился на новом билде (<b>R</b>=1), но новых багов не нашлось (<b>I</b>=0)</li>
<li>Мое изменение внесло новые баги (<b>I</b>=1), но изначальный баг успешно пофикшен (<b>R</b>=0)</li>
<li>Состояние "пж" (п="полная"): изменение не пофиксило изначальную проблему плюс оно породило новые баги (<b>I</b>=1 и <b>R</b>=1)</li>
</ul>
<div>
На рисунке изображены все возможные состояния системы, в которые она может попасть после моих потенциальных действий.</div>
</div>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgIum2swkt7tgvpWTJg2CcySbRkQyruA2Kh71FAX-dz26P-iRvQGvR_anZfk_q0bQT_Dfg_fPyEJCtlS9J1q2os0LYiUAERlMgddCPjoZ1rP_324OxO0NlM-BJc1OVYysa5-88iYA/s1600/2-states.PNG" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="220" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgIum2swkt7tgvpWTJg2CcySbRkQyruA2Kh71FAX-dz26P-iRvQGvR_anZfk_q0bQT_Dfg_fPyEJCtlS9J1q2os0LYiUAERlMgddCPjoZ1rP_324OxO0NlM-BJc1OVYysa5-88iYA/s640/2-states.PNG" width="640" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;"><b>Рис 2</b>. Возможные состояния системы после действия.</td></tr>
</tbody></table>
<div>
<br /></div>
<h4>
Вероятности переходов</h4>
<div>
В предидущем посте я предположил следующие вероятности для событий "Баг переоткрыт" (переменная <b>R </b>- Reopen) и "Внесеные новые баги" (переменная <b>I</b> - Introduced new issues). Для того, чтобы освежить значения вот эти таблицы из предидущего поста:</div>
<div>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgAhI-ArbSriJPNksgVjRjaNgkfZnD803jbCZ4rH3yrmj3VdiZmFLuz62GfaVqOUi1FTADO09niTcD9u9q40qO7Hw_G9yYuTRC5BzxBPFX7KuwiGqLhqm6VK2tNJ5PxVz0JjKM9QQ/s1600/2-C-Rprob.PNG" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgAhI-ArbSriJPNksgVjRjaNgkfZnD803jbCZ4rH3yrmj3VdiZmFLuz62GfaVqOUi1FTADO09niTcD9u9q40qO7Hw_G9yYuTRC5BzxBPFX7KuwiGqLhqm6VK2tNJ5PxVz0JjKM9QQ/s1600/2-C-Rprob.PNG" /></a></td></tr>
<tr><td class="tr-caption" style="font-size: 12.666666984558105px;"><b>Таблица 1</b>. <span style="font-size: x-small;">Вероятность возникновения </span><b style="font-size: small;">R</b><span style="font-size: x-small;"> при фиксированном </span><b style="font-size: small;">C</b></td></tr>
</tbody></table>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhgNtBovLLd6mXIuY8UHbIWVv6ZB2fQ5u2FUx5q9bYB2OjusKsGrjAJfdAY9OaRO1pxuz8Ss4qxL76fD-d1B4HtRM4sYEth-Q1qAGjHLzSzUYaFN2hppv7MbVjFLRW5xovLvImQ0Q/s1600/3-C-Iprob.PNG" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhgNtBovLLd6mXIuY8UHbIWVv6ZB2fQ5u2FUx5q9bYB2OjusKsGrjAJfdAY9OaRO1pxuz8Ss4qxL76fD-d1B4HtRM4sYEth-Q1qAGjHLzSzUYaFN2hppv7MbVjFLRW5xovLvImQ0Q/s1600/3-C-Iprob.PNG" /></a></td></tr>
<tr><td class="tr-caption" style="font-size: 12.666666984558105px;"><b style="font-size: medium;"><span style="font-size: x-small;">Таблица 2.</span></b><span style="font-size: small;"> </span><span style="font-size: x-small;">Вероятность возникновения <b>I</b> при фиксированном <b>C</b></span></td></tr>
</tbody></table>
<div class="separator" style="clear: both;">
В таблице представлены бинарные переменные. Если значение переменной 1, то в колонке Probability указана вероятность того, что событие произойдет. Если 0, то вероятность того, что события не будет.</div>
<div class="separator" style="clear: both;">
<br /></div>
<div>
<b>Например</b>, вот как следует читать первую строчку таблицы #1:<br />
Если проверить систему перед комитом (колонка <b>C</b>=<b>1</b>), то баг переоткроется с вероятностью 10% (<b>R</b>=1, <b>Probability</b>=0.1). </div>
<div>
</div>
</div>
<div>
<br /></div>
<div>
Теперь посмотрим на возможные состояния в нашей системы (см. рис 2). Как видишь, каждое состояние - это один вариант из набора из всех возможных значений <b>R</b> и <b>I</b>. Чтобы получить вероятность перехода, нужно посчитать совместную вероятность (joint probability) возникновения событий R и I при известном C. Для этого просто перемножим соответствующие значения <b>R</b> и <b>I</b>*.<br />
<br /></div>
<div>
<span style="font-size: x-small;">* Как только мы знаем, какое значение принимает C это делает события R и I независимыми и их совместная вероятность равна произведению вероятностей возникновения R и I.</span><br />
<span style="font-size: x-small;"><br /></span>
Так, например, для действия "проверять" (To Check, где C = 1) вероятность попасть в состояние, когда изменение успешно проверено (R=0) и новых багов не обнаружено (I=0) равна \(0.9 * 0.8 = 0.72\), для C=1, R=1 и I=0 - \(0.1 * 0.8 = 0.08\) и т.д. В табличном виде это выглядит следующим образом:<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiyo7GI2rh8thpxo-Cz6OPm_ar2y6mBWfuEKKCktI7qNsa8qznQUlTItWb25jLM05CD0RSwuxvofEBQvy9vUTzADM9NFjru4AuC7NYjupcZyhPVuyApsccAFeRirTCfl0fAVKihrA/s1600/3-table-3.PNG" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiyo7GI2rh8thpxo-Cz6OPm_ar2y6mBWfuEKKCktI7qNsa8qznQUlTItWb25jLM05CD0RSwuxvofEBQvy9vUTzADM9NFjru4AuC7NYjupcZyhPVuyApsccAFeRirTCfl0fAVKihrA/s1600/3-table-3.PNG" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;"><b>Таблица 3</b>. Совместные вероятност событий R и I при фиксированном C.</td></tr>
</tbody></table>
<h4>
Определяем выгоду</h4>
</div>
<div>
Ок, на данным момент у нас есть варианты возможных действий (проверять/не проверять), список состояний, в которые можем попасть (см. рис 2) и вероятности попадания в новое состояние при выборе того или иного действия (см. таблицу 3). Осталось совсем немного - определить выгоду (utility) каждого из состояний.</div>
<div>
Как можно оценить utility? Что "выгоднее" - чтобы тестировщик переоткрыл багу (<b>R</b>=1) или чтобы закрыл, но обнаружил новые (<b>I</b>=1)? Интуитивно кажется, что состояние (<b>R</b>=1,<b>I</b>=0) выгоднее чем (<b>R</b>=0,<b>I</b>=1) но насколько? Я предлагаю оценить трудозатраты тестировщика и разработчика для каждого состояния, а после расчета мат. ожидания (EV) выбрать то действие, которое его минимизирует.</div>
<div>
Оценка трудозатрат основана исключительно на моих субъективных ощущениях, которые я иногда включаю во время планирования очередного спринта. Оценю в абстрактных и никому до конца не понятных единицах - стори поинтах *. </div>
<div>
<br /></div>
<div>
Поигрю немного сам с собой в "планнинг покер" дабы получить искомые значения :). </div>
<div>
<b><br /></b></div>
<div>
<b>Стори №1</b>. Итак, самая минимальная задача, которую можно придумать - это проверка моего изменения. Ее возьмем за 1. В случае, если это будет делать тестировщик, то он прогонит основной набор тестов, относящийся к изменению за 1 единицу времени **. Я предполагаю, что набор этих кейзов небольшой и разработчик (если он не раздолбай) запустит такой же набор у себя перед комитом.</div>
<div>
<br /></div>
<div>
<b>Стори №2</b>. Бага переоткрыта. Тестировщик прогнал базовые тесты и обнаружил, что моего изменения в билде нет. Он переоткрывает багу и, скорее всего, проведет небольшое исследовательское тестирование дабы убедиться, что симптомы проблемы не поменялись. Предположу, что объем работы такой же как и в стори №1 (т.е. 1). </div>
<div>
Мне, как разработчику***, возможно понадобится чуть больше времени дабы заглушить муки совести, потом попробовать переубедить тестера, что ему показалось, затем провести исследование дабы обнаружить что тестер сделал все в правильном окружении, на правильных серверах и в правильную фазу лунного цикла. После этого я обычно быстро устраняю проблему. Рискну предположить что для разработчика это займет 2 единицы времени.</div>
<div>
<b><br /></b></div>
<div>
<b>Стори №3</b>. Тестировщик обнаружил новые проблемы. Скорее всего ему прийдется запустить более полный набор тестов, затем локализировать проблемы и зарепортить симптомы в виде новых багов. Предположу, что это сложнее стори №1 в 3 раза, следовательно займет 3 единицы времени.</div>
<div>
Я уже чувствую разочарование разработчика, который находится в подобном состоянии :) Согласись, это довольно печально - пофиксить 1 баг, а взамен получить пачку новых :). Скорее всего здесь кроется что-то очень сложное, поскольку мое изменение повлияло на те части системы, о которых я и не думал во время работы. Следовательно фикс может затянуться, т.к. во всех этих кусках прийдется колупаться. Ставлю 5 сторипоинтов (упс.. сори - единиц времени :)) этой задаче.</div>
<div>
<br /></div>
<div>
<span style="font-size: x-small;">* Далее будет несоответствие с "классическими" юзер стори, поскольку выдумывать роли и записывать каждую историю в виде "Как тестер я хочу чтобы не было багов" мне лень, да и на тему исследования это никак не влияет.</span></div>
<div>
<span style="font-size: x-small;">** Здесь может быть некоторое противоречие с теми сторипоинтами, которыми нас учат классики. В качестве сторипоинта я беру не сложность, как это принятно, а "идеальную" единицу времени, поскольку потом буду приводить эти единицы к денежному эквиваленту. Я исхожу из предположения, что любой тестировщик потратит 1 единицу времени на эту задачу.</span></div>
<div>
<span style="font-size: x-small;">*** И еще одно расхождение с классиками. Обычно историю оценивает команда (тестеры и девелоперы) и дает общую оценку. Но в целях данного трактата я разделю тестерские и девелоперские оценки. Дальше поймешь зачем я это сделал.</span></div>
<div>
<span style="font-size: x-small;"><br /></span></div>
<div>
Вот для наглядности варианты оценок в виде таблицы</div>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiENNf_I4sKijSHIVjOZnNQyBcKOYjPs8CoGP-Ou5DDsum2eGLjAUAAaPY5Z1feWcQNUXU_uqxjAy_i4miOzGQKUcUnvEW_XNdEcn-ehdXXzu4DhYRYRgAShM3txw_5P1e62LLD3g/s1600/4-table-4.PNG" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiENNf_I4sKijSHIVjOZnNQyBcKOYjPs8CoGP-Ou5DDsum2eGLjAUAAaPY5Z1feWcQNUXU_uqxjAy_i4miOzGQKUcUnvEW_XNdEcn-ehdXXzu4DhYRYRgAShM3txw_5P1e62LLD3g/s1600/4-table-4.PNG" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;"><b>Таблица 4</b>. Оценка трудозатрат в сторипоинтах.</td></tr>
</tbody></table>
<div>
Осталось посчитать выгоду каждого действия. Напомню, что выгода = математическое ожидание, которое считается как сумма вероятности события помноженное на его выгоду: \(\sum_{s'}{P(s,a,s') U(s')}\)</div>
<div>
Не буду загромождать статью текстом простейших вычислений, приведу сразу все значения в таблицах.</div>
<div>
<br /></div>
<div>
Оценка математического ожидания если программист параноидально добросовестен (C=1):</div>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiRkyMkxjuZV1EgtxiRoaxj73eMGXbQ24l1-CrBNiKMZlw5is67llet4KsG301vrT3LAdDrhznrySiAucYWDKx8N7PlHd9BlMt8NoV0SG56b8oPmVRmirDCrQUo4oL8xDXDTGHbog/s1600/5-table-5.PNG" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiRkyMkxjuZV1EgtxiRoaxj73eMGXbQ24l1-CrBNiKMZlw5is67llet4KsG301vrT3LAdDrhznrySiAucYWDKx8N7PlHd9BlMt8NoV0SG56b8oPmVRmirDCrQUo4oL8xDXDTGHbog/s1600/5-table-5.PNG" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;"><b>Таблица 5</b>. Оценка трудозатрат в случае 100% проверки перед комитом.</td></tr>
</tbody></table>
<div>
<br /></div>
<div>
Оценка мат. ожидания в случае абсолютного программиста-разгильдяя:</div>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjb8c8ZZA6PyqVTbDJiqoRMbq9CehTL2NQ-ljBLa9Y9aSmyOUnGQaIcoEFqQqTVVySco0qC6w2Pie8WFm_0UdceilHw3gkQEI3VEm1HjgqLHhR1Ke_cmBJecraQqq1Yx3DNecS_7Q/s1600/6-table-6.PNG" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjb8c8ZZA6PyqVTbDJiqoRMbq9CehTL2NQ-ljBLa9Y9aSmyOUnGQaIcoEFqQqTVVySco0qC6w2Pie8WFm_0UdceilHw3gkQEI3VEm1HjgqLHhR1Ke_cmBJecraQqq1Yx3DNecS_7Q/s1600/6-table-6.PNG" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;"><b>Таблица 6</b>. Оценка трудозатрат если не делать проверок перед комитом.</td></tr>
</tbody></table>
<div>
Как видно из таблиц, если проверять свои изменения перед комитом, то изменение будет стоить 3.9 условных единиц - "попугаев". Если же изменения не проверять, то 4.8.</div>
<div>
Следовательно можно было бы сделать вывод, что проверять изменения перед комитом обходится дешевле. Но раз уж мы говорим про стоимость, то давай переведем полученные мат ожидания (EV) в условные денежные единицы и посмотрим на окончательный результат.</div>
<div>
<br /></div>
<h4>
Переводим в доллары</h4>
<div>
Здесь мы попытаемся получить оценки решений проверять/не проверять в денежном эквиваленте. Денежный эквивалент мат ожидания называется expected monetary value (EMV). На русском это звучит как-то нелепо, поэтому я буду пользоваться английским названием.</div>
<div>
<br /></div>
<div>
Известно, что зарплаты программистов отличаются от зарплат тестировщиков. Следовательно цена 1 единицы времени программиста не равна цене 1 единицы времени тестера. Для расчета соотношения зарплат я взял на DOU <a href="http://dou.ua/lenta/articles/developers-salaries-may-june-2012/" target="_blank">средние зарплаты по киеву</a> QA Engineer и Software Engineer за май-июнь 2012 год. Там фигурируют такие цифры:</div>
<div>
- Средняя зп QA Engineer с опытом 1-10 лет = $1280</div>
<div>
- Средняя зп Software Engineer с опытом 1-10 лет = $2000</div>
<div>
<br /></div>
<div>
Исходя из стредних зарплат соотношение зп программиста к зп тестера = 2000/1280 = 1.5625. Пересчитаю таблицу оценки трудозатрат из сторипоинтов в доллары *. </div>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjEqJMpWW8XuhTr0vmTvislkXzEWqfV4-BYXpuuSBlm_GpA-kYfW1lD7awmGZbhVAKuJgX6Opta9d4-7h_0FdHklG1MUqfo7S42JL7byDGWVsvbh2THDeDJwGLLK4gGT3liQRMD5w/s1600/7-table-7.PNG" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjEqJMpWW8XuhTr0vmTvislkXzEWqfV4-BYXpuuSBlm_GpA-kYfW1lD7awmGZbhVAKuJgX6Opta9d4-7h_0FdHklG1MUqfo7S42JL7byDGWVsvbh2THDeDJwGLLK4gGT3liQRMD5w/s1600/7-table-7.PNG" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;"><b>Таблица 7</b>. Относительная оценка трудозатрат в $</td></tr>
</tbody></table>
<div>
<span style="font-size: x-small;">* Здесь я просто умножил оценки затрат для девелопера на полученное соотношение (1.5625).</span></div>
<div>
<span style="font-size: x-small;"><br /></span></div>
<div>
Повторюсь, что в этой таблице приведены относительные оценки трудозатрат программиста и тестировщика исходя из предположения, что средний программист получает в 1.56 раза больше чем средний тестировщик. Для того, чтобы выразить стоимости в деньгах нужно определиться чему равна единица времени (час, день, неделя и т.д.), затем найти стоимость тестеро-единицы (тестеро-часа/дня/недели и т.д.) и умножить на приведенные в таблице коеэффициенты.</div>
<div>
<br /></div>
<div>
Дальше остается расчитать EMV по известной формуле, где для подсчета Utility используем "девелоперские" и "тестерские" оценки из таблицы №7 *</div>
<div>
<br /></div>
<div>
Оценка EMV если программист параноидально добросовестен (C=1):</div>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjEqag44V-MqLhLxGhiFgVJGcA-OQPXxqEF74p4LBx9E3wlzar4jGtTWpjhCTGsqmzyzzdDovXluwdr3S5YGxlHI2bWlQ0P4lWTBWcF0kM-nxRkI5zybiiCV2nfQjeBvYwfVB51wg/s1600/8-table-8.PNG" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjEqag44V-MqLhLxGhiFgVJGcA-OQPXxqEF74p4LBx9E3wlzar4jGtTWpjhCTGsqmzyzzdDovXluwdr3S5YGxlHI2bWlQ0P4lWTBWcF0kM-nxRkI5zybiiCV2nfQjeBvYwfVB51wg/s1600/8-table-8.PNG" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;"><b>Таблица 8.</b> Относительное значение EMV если делать проверку.</td></tr>
</tbody></table>
Оценка EMVв случае абсолютного программиста-разгильдяя:<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg7D-tbrsTprQUY_jjQfPlMBefnw2gHoKqObSjHRN0T4m_twzgpyhS-_lPffksRmqwjQSo4AeF2fY2RsN4_8vx4OSFI56Te4tYKJ3T_jol6nApl2Gdjqr9Sad5fbKHU_sMkK7KHgw/s1600/9-table-9.PNG" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg7D-tbrsTprQUY_jjQfPlMBefnw2gHoKqObSjHRN0T4m_twzgpyhS-_lPffksRmqwjQSo4AeF2fY2RsN4_8vx4OSFI56Te4tYKJ3T_jol6nApl2Gdjqr9Sad5fbKHU_sMkK7KHgw/s1600/9-table-9.PNG" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;"><b>Таблица 9.</b> Относительное значение EMV если не проверять свои изменения.</td></tr>
</tbody></table>
<div>
<span style="font-size: x-small;">* Я понимаю, что на картинках не показано как именно получаются значения Utility, поэтому в конце поста я добавил ссылку на экселевский документ, чтобы можно было детальнее разобрать этот пример. </span></div>
<div>
<span style="font-size: x-small;"><br /></span></div>
<h3>
Выводы</h3>
<div>
Мы нашли два математических ожидания: оценили последствия безалаберности (если не проверять изменения) и абсолютной параноидальности (если всегда проверять свои изменения перед комитом). Результаты оказались не в пользу безалаберности. </div>
<div>
<ol>
<li>Во временных трудозатратах разница составляет ~ 20% (4.8 сторипоинта в случае безалаберности и 3.9 в случае паранойи)</li>
<li>В денежном эквиваленте разница между безалаберностью и паранойей составляет 10-12% (6.15 если не проверять свои изменения и 5.14 в случае проверки перед комитом). Видно, что на денежное ожидание (EMV) повлияло то, что программисты стоят дороже почти в 1.5 раза чем тестеры и экономия времени на проверке своих изменений дает такую разницу.</li>
</ol>
<div>
Мое изначальное предположение, что можно не проверять свои изменения перед комитом при наличии хорошего набора юнит тестов не подтвердилось. Впредь буду аккуратнее с использованием интуиции в подобных предположениях, но полностью выключать ее тоже не буду, ведь погрешность моей интуиции, выражанная в денежном эквиваленте составила всего 10% ;).</div>
</div>
<h4>
Ссылки на источники, используемые файлы</h4>
<div>
<br />
<ul>
<li><a href="http://bit.ly/10fNFEv" target="_blank">Таблица расчетов в Excel</a></li>
<li><a href="http://dou.ua/lenta/articles/developers-salaries-may-june-2012/" target="_blank">Обзор рынка зарплат в Украине за май-июнь 2012</a></li>
<li><a href="https://www.edx.org/courses/BerkeleyX/CS188.1x/2013_Spring/about" target="_blank">Курс Berkley по ИИ</a> </li>
</ul>
</div>
</div>
<br /></div>
Сергей Зеленинhttp://www.blogger.com/profile/01667820453041864348noreply@blogger.com2tag:blogger.com,1999:blog-24004232.post-28748273893446698142013-04-18T15:58:00.003-07:002013-04-19T07:09:02.193-07:00Стоит ли проверять свои изменения перед комитом? Задаем вопрос к сети Байеса.<br />
<h3>
Предисловие.</h3>
На этот пост меня вдохновила профессор Дафна Коллер (<a href="http://ai.stanford.edu/~koller/" target="_blank">Daphne Koller</a>), которая за первую неделю курса <a href="https://www.coursera.org/course/pgm" target="_blank">Probabilistic Graphical Models</a> расширила мое мировозрение простой, понятной и довольно эффективной моделью Байесовских сетей (<a href="http://en.wikipedia.org/wiki/Bayesian_network" target="_blank">Bayesian Network</a>).<br />
<div>
</div>
<br />
<div>
<div>
<br />
<h3>
Цель исследования.</h3>
<div>
Дело в том, что я давненько заметил за собой тенденцию отдавать свои изменения тестировщику без интеграционного тестирования. Слишком уж доверяю юнит тестам, и, к тому же, что греха таить, ленюсь лишний раз запустить приложение :). Обоснованность такого решения была основана исключительно на интуиции, которую я решил проверить с помощью новоприобритенных знаний.</div>
</div>
<div>
<br /></div>
<div>
<h3>
Ожидаемый результат.</h3>
</div>
<div>
Хочу обосновать (или опровергнуть) необходимость запускать приложение перед комитом для проверки своих изменений. Моя интуиция говорит, что в большинстве случаев этого можно не делать, если есть хорошие юнит тесты.</div>
<div>
<br /></div>
<div>
<h3>
Построение модели.</h3>
</div>
<div>
План такой (жутко абстрактный:)).<br />
Прежде всего надо определить что я хочу измерить, потом определить зависимости между измеряемыми элементами, потом на основании этих знаний построить модель.<br />
<br />
Итак, я могу влиять на решение проверять или не проверять приложение перед комитом.<br />
<h4>
Что зависит от моего решения делать проверку или нет? </h4>
</div>
<div>
<ol>
<li>Прежде всего, если я не проверю свои изменения, то логично предположить, что увеличится вероятность того, что тестировщик попросту "переоткроет багу" (Reopen *). То есть изменения, которые я отдал на тестирование не устранили проблему (если же речь идет о новой фиче, то это адекватно тому, что новую функциональность тестировщик не увидел).</li>
<li>Возможно, что тестировщик обнаружит, что начальная проблема решена, но появились новые проблемы (Introduced new issues). То есть вместе с фиксом я наплодил кучу новых проблем.</li>
<li>На что влияют эти 2 потенциальные проблемы? Рискну предположить, что они влияют на качество системы (Quality).</li>
</ol>
<div>
<span style="font-size: xx-small;">* Амсори, но дальше в описании я нечаянно подменил Reopen на Reproduce.</span></div>
<div>
<br /></div>
<div>
Исходя из вышеизложенных умозаключений в стиле КО определим переменные модели.</div>
</div>
</div>
<div>
<ul>
<li><b>Check</b> (<b>C</b>). Это мое решение делать проверку или нет</li>
<li><b>Reopen (R)</b>. Это вероятность того, что тестировщик "переоткроет" баг.</li>
<li><b>Introduced new issue (I).</b> Это вероятность того, что тестировщик найдет новые проблемы, связанные с моими изменениями.</li>
<li><b>Quality (Q). </b>Качество системы</li>
</ul>
<div>
<b>Вот и зависимости:</b></div>
<div>
Мое решение делать проверку (<b>C</b>) влияет на вероятность того, что тестировщик переоткроет баг (<b>R</b>) и на то, обнаружит ли он новые проблемы (<b>I</b>). Обе переменные (<b>R</b> и <b>I</b>) влияют на качество системы (<b>Q</b>).</div>
</div>
<div>
<br /></div>
<div>
Изобразим это в виде диаграммы зависимостей.</div>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi6JRvj7DMvz8kgw5Lj7uMB0wedjMaKtokFkfE9lmr3LDgBrkveD9ULTc5Uznh0z2iaWd1gACRNSKx5iwle3t_BgdUN30MuTpce2WQ8uwDj2lk_1a0S9EP6KHrg4usatrSzWLqQkw/s1600/1-BN.PNG" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi6JRvj7DMvz8kgw5Lj7uMB0wedjMaKtokFkfE9lmr3LDgBrkveD9ULTc5Uznh0z2iaWd1gACRNSKx5iwle3t_BgdUN30MuTpce2WQ8uwDj2lk_1a0S9EP6KHrg4usatrSzWLqQkw/s1600/1-BN.PNG" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;"><b>Рис 1</b>. Сеть Байеса.</td></tr>
</tbody></table>
<div class="separator" style="clear: both; text-align: left;">
Это и есть сеть Байеса. Я построил ее с помощью специальной программы <a href="http://reasoning.cs.ucla.edu/samiam/" target="_blank">SamIam</a>.</div>
<div>
<br /></div>
<div>
<h3>
Установка значений модели.</h3>
</div>
<div>
Что можно сделать с помощью полученной сети? Один из основных вариантов использования - это анализ того, как переменные влияют друг на друга. </div>
<div>
<br />
<b>Например</b>, я хочу посмотреть, как мое решение проверить мой фикс (<b>C</b>) влияет на качество (<b>Q</b>) через вероятность переоткрытия (<b>R)</b> и внесения новых багов (<b>I</b>).</div>
<div>
Для этого необходимо определить с какой вероятностью тестировщик переоткроет багу или найдет новые проблемы в зависимости от того, проверю я систему или нет.<br />
<h4>
КО подсказывает простые правила:</h4>
</div>
<div>
<ol>
<li>Если я перед комитом проверю приложение, то вероятность переоткрытия (<b>R</b>) очень маленькая. Вероятность внесения новых проблем (<b>I</b>) в этом случае тоже невысока.</li>
<li>Если я перед комитом поленюсь делать проверку, то вероятности появления событий <b>R</b> и <b>I</b> будут чуть выше. Но поскольку я доверяю юнит тестам они тоже будут небольшими.</li>
</ol>
<div>
<h4>
Изобразим вероятности событий в виде таблиц.</h4>
</div>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgAhI-ArbSriJPNksgVjRjaNgkfZnD803jbCZ4rH3yrmj3VdiZmFLuz62GfaVqOUi1FTADO09niTcD9u9q40qO7Hw_G9yYuTRC5BzxBPFX7KuwiGqLhqm6VK2tNJ5PxVz0JjKM9QQ/s1600/2-C-Rprob.PNG" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="236" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgAhI-ArbSriJPNksgVjRjaNgkfZnD803jbCZ4rH3yrmj3VdiZmFLuz62GfaVqOUi1FTADO09niTcD9u9q40qO7Hw_G9yYuTRC5BzxBPFX7KuwiGqLhqm6VK2tNJ5PxVz0JjKM9QQ/s320/2-C-Rprob.PNG" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;"><b>Таблица 1</b>. <span style="font-size: x-small;">Вероятность возникновения </span><b style="font-size: small;">R</b><span style="font-size: x-small;"> при фиксированном </span><b style="font-size: small;">C</b></td></tr>
</tbody></table>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhgNtBovLLd6mXIuY8UHbIWVv6ZB2fQ5u2FUx5q9bYB2OjusKsGrjAJfdAY9OaRO1pxuz8Ss4qxL76fD-d1B4HtRM4sYEth-Q1qAGjHLzSzUYaFN2hppv7MbVjFLRW5xovLvImQ0Q/s1600/3-C-Iprob.PNG" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="260" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhgNtBovLLd6mXIuY8UHbIWVv6ZB2fQ5u2FUx5q9bYB2OjusKsGrjAJfdAY9OaRO1pxuz8Ss4qxL76fD-d1B4HtRM4sYEth-Q1qAGjHLzSzUYaFN2hppv7MbVjFLRW5xovLvImQ0Q/s320/3-C-Iprob.PNG" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;"><b style="font-size: medium;"><span style="font-size: x-small;">Таблица 2.</span></b><span style="font-size: small;"> </span><span style="font-size: x-small;">Вероятность возникновения <b>I</b> при фиксированном <b>C</b></span></td></tr>
</tbody></table>
<div class="separator" style="clear: both; text-align: left;">
В таблице представлены бинарные переменные. Если значение переменной 1, то в колонке Probability указана вероятность того, что событие произойдет. Если 0, то вероятность того, что события не будет.</div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div>
<b>Например</b>, вот как следует читать первую строчку таблицы #1:<br />
Если проверить систему перед комитом (колонка <b>C</b>=<b>1</b>), то баг переоткроется с вероятностью 10% (<b>R</b>=1, <b>Probability</b>=0.1). </div>
<div>
Значения я брал из головы, исходя из своего опыта и субьективных предположений. Никаких исследований на этот счет не проводилось :). Как видишь, я довольно оптимистично отношусь к фиксам без дополнительных проверок, хотя до оптимизма Чака Норриса еще далековато.</div>
<div>
<br /></div>
<div>
Теперь внесем эти значения в модель (с помощью той же программы <a href="http://reasoning.cs.ucla.edu/samiam/" target="_blank">SamIam</a>)</div>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi5GlezWz8hvM9r9D_nWFRApBgQuLpORGgecqsdRz8_5VZ6RlqWmR1h1YQD3fJfUDT8DeUNnkXCz8dwpdBqri7x8E5wSM98eno3b6xLxBAqZjTMvM_EOXp7MQIAZnI1Ygf6_GYj5g/s1600/4-model.PNG" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="369" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi5GlezWz8hvM9r9D_nWFRApBgQuLpORGgecqsdRz8_5VZ6RlqWmR1h1YQD3fJfUDT8DeUNnkXCz8dwpdBqri7x8E5wSM98eno3b6xLxBAqZjTMvM_EOXp7MQIAZnI1Ygf6_GYj5g/s640/4-model.PNG" width="640" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;"><b>Рис 2</b>. Значения переменных.</td></tr>
</tbody></table>
<div>
На первый взгляд картинка кажется слегка сложной, но думаю после пояснений будет понятнее что на ней изображено:</div>
<div>
<ol>
<li>Значения переменной <b>C </b>(проверка приложения). В 30% случаях я проверяю систему, в 70% - отправляю тестировщикам без проверки. Значение <b>Yes</b>=0.3 соответствует 30%-й вероятности проверки перед комитом. Также <b>No</b>=0.7 соответствует 70%-й "безответственности" :).</li>
<li>Значения переменной <b>R</b>.</li>
<ol>
<li>Если <b>C</b>=<b>Yes</b> (если я проверяю приложение перед комитом), то в 10% бага репродюсится, в 90% - нет (значения в <b>Yes</b>-<b>Reproduced</b> и <b>Yes</b>-<b>NotReproduced</b> соответственно</li>
<li>Если <b>C</b>=<b>No</b> (если я ленюсь выполнить проверку), то в 20% бага репродюсится, в 80% - нет (значения в <b>No</b>-<b>Reproduced</b> и <b>No</b>-<b>NotReproduced</b> соответственно).</li>
</ol>
<li>Значения переменной <b>I</b></li>
<ol>
<li>Если <b>C</b>=<b>Yes</b>, то в 20% случаев тестировщик найдет новые проблемы в 80% - нет (значения в <b>Yes</b>-<b>Introduced</b> и <b>Yes</b>-<b>NotIntroduced</b> соответственно).</li>
<li>Если <b>C</b>=<b>No</b>, то в 40% случаев тестировщик найдет новые проблемы в 60% - нет (<b>No</b>-<b>Introduced</b> / <b>No</b>-<b>NotIntroduced</b> соответственно)</li>
</ol>
</ol>
<div>
Значения переменной Q (качество приложения) я вынес в отдельный рисунок, т.к. из-за того, что эта переменная зависит от 2-х других, в ней больше вариантов значений:</div>
</div>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjeDBpgU6dFMnHU741uayee9Aj4KYmOJzJWovoN9ULjzEudmH3OWBvLo7GlF8iWT0dfvX-i_xM9GD4KvK24_ANOYn7bO2jfUzI3a-sXRgHWA78cUEyGRok6WgQZcwACqjU9tuJCgQ/s1600/5-Qprops.PNG" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjeDBpgU6dFMnHU741uayee9Aj4KYmOJzJWovoN9ULjzEudmH3OWBvLo7GlF8iWT0dfvX-i_xM9GD4KvK24_ANOYn7bO2jfUzI3a-sXRgHWA78cUEyGRok6WgQZcwACqjU9tuJCgQ/s1600/5-Qprops.PNG" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;"><b>Рис 3</b>. Значения переменных-2. Значения Q.</td></tr>
</tbody></table>
<div>
<br /></div>
<div>
Пояснения значений качества (<b>Q</b>)</div>
<div>
<ol>
<li>Если багу переоткрыли (<b>R</b>=<b>Reproduced</b>), <b>И</b></li>
<ol>
<li>Если к тому же еще тестировщик нашел новые проблемы (<b>I</b>=<b>Introduced</b>), то в 1% случаев можно сказать, что качество хорошее, в 99% - нет</li>
<li>Если новых проблем не найдено (<b>I</b>=<b>NotIntroduced</b>), то качество хорошее в 40% случаев, плохое в 60%</li>
</ol>
<li>Если багу не переоткрыли (<b>R</b>=<b>NotReproduced</b>), <b>И</b></li>
<ol>
<li>Тестировщик нашел новые проблемы (<b>I</b>=<b>Introduced</b>), то с вероятностью 0.2 система хорошего качества, соответственно 0.8 - плохого</li>
<li>Если тестер новых проблем не обнаружил, то в 99% качество хорошее и 1% - плохое</li>
</ol>
</ol>
</div>
<div>
<h3>
Запросы к модели.</h3>
</div>
<div>
<b><span style="font-size: x-small;">Отступление. </span></b><br />
<span style="font-size: x-small;">Я заранее хочу предупредить, что здесь не будет дано обьяснение того, как именно происходит "магия" и по каким принципам переменные влияют друг на друга. Это тема отдельного поста, а ищущие могут найти объяснения в первых же лекциях курса <a href="https://www.coursera.org/course/pgm" target="_blank">Probabilistic Graphical Models</a>. </span><br />
<br />
Теперь самое интересное. Перейдем в Query mode и посмотрим на то, как повлияли зависимости между переменными на их значения<br />
<br />
<h4>
Событий не зафиксировано</h4>
</div>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjfD7aCRL0tEzdNsjOvTEniTpBB21M1XAPGnG-mEG9ZaVqD5d8NPN5GjMKLO5pQC6_FqHH0gHtViVLkszz3egUQTjuWE1AR9Jc0W5pUu-pM4lfDHoxaqYvQisc0A55jrM6TEug-pQ/s1600/6-Default.PNG" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="452" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjfD7aCRL0tEzdNsjOvTEniTpBB21M1XAPGnG-mEG9ZaVqD5d8NPN5GjMKLO5pQC6_FqHH0gHtViVLkszz3egUQTjuWE1AR9Jc0W5pUu-pM4lfDHoxaqYvQisc0A55jrM6TEug-pQ/s640/6-Default.PNG" width="640" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;"><b>Рис 4.</b> Никаких событий не наблюдается.</td></tr>
</tbody></table>
Мы видим вероятностые значения переменных если не наблюдается никаких событий. То есть это состояние, когда неизвестно, есть ли в системе переоткрытые или новые баги и я никому не говорю, проверяю я свой код перед комитом или нет.<br />
Чуть детальнее:<br />
<br />
<ul>
<li>Я продолжаю делать проверку в 30% случаев. При этом</li>
<ul>
<li>Вероятность того, что я своими изменениями внесу новые баги 34%. <b>I</b> приймет значение <b>Introduced </b>в 34 комитах из 100 *</li>
<li>Вероятность того, что тестировщик переоткроет багу после комита 17%. <b>R</b> станет равным <b>1</b> в 17 комитах *</li>
<li>При таких раскладах хорошее качество системы можно наблюдать в 64.42% случаев</li>
</ul>
</ul>
<br />
<span style="font-size: x-small;">* Ты верно обратил внимание на значения для переменных <b>I</b> и <b>R</b> (34-60 и 17-83). Незнание значения C делает эти узлы зависимыми. На интуитивном уровне я это понимаю так : если после комита появились новые баги, то, возможно это повлияет на вероятность переоткрытия старых через то, что, возможно кто-то просто забыл провести интеграционный тест.</span><br />
<br />
Дальше с помощью этой модели можно попытаться ответить на следующие вопросы<br />
<br />
<h4>
Как изменится качество системы, если я никогда не буду тестировать свои изменения перед тем, как отдать их тестировщику?</h4>
То есть я совсем разленюсь и перестану запускать приложение в интеграции. Для этого зафиксирую значение переменной <b>C</b> в <b>No:</b><br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj0-YgQedKLdcecw644XvLw4vXtZntyFan4noW5JeHG733gdjrK_noxz-OlSOSIWqmpz0RwfDleSCvQBoVTjXaR2xf12f8nj9PCNmEsb5tcADRQ19P__1JMQl4Sv3g8_02a0et2Eg/s1600/7-NotChecked.PNG" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="441" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj0-YgQedKLdcecw644XvLw4vXtZntyFan4noW5JeHG733gdjrK_noxz-OlSOSIWqmpz0RwfDleSCvQBoVTjXaR2xf12f8nj9PCNmEsb5tcADRQ19P__1JMQl4Sv3g8_02a0et2Eg/s640/7-NotChecked.PNG" width="640" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;"><b>Рис 5</b>. Совсем разленился - не тестирую в интеграции.</td></tr>
</tbody></table>
Как видишь, вероятность того, что мы будем наблюдать качественную систему упала с 64% до 58%. Вроде логично. *<br />
<br />
<span style="font-size: x-small;">* Видишь, здесь переменные <b>I</b> и <b>R </b>приняли те значения, которые мы вводили изначально. Это потому, что "зафиксировав" <b>C</b> мы разорвали связь между <b>I</b> и <b>R</b>. То есть, если я постоянно буду забивать на локальное тестирование, то будут действовать те вероятности, которые я выдумал и записал в таблицах 1 и 2, где <b>C</b>=0.</span><br />
<br />
<h4>
Как изменится качество системы, если я всегда буду тестировать перед комитом?</h4>
<div>
Перед тем, как отдать изменение тестировщику я всегда одолеваю свое нежелание прогнать самому несколько важных интеграционных тестов. Фиксирую значение <b>C</b> в <b>Yes</b>:</div>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg0FjNiMYOGWEiaidrSsO4M1_IQnBP3lpbCjCWgkps-leUZkUSAxmjYESUIkbmSDZxwOl1Z_mLC8emj1iRNgO_Gm9EsaRFL9d1dmZnteNiHTmiPIsDqyrkOaYXBjMr5VZsey3ZTLA/s1600/7.1-Checked.PNG" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="435" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg0FjNiMYOGWEiaidrSsO4M1_IQnBP3lpbCjCWgkps-leUZkUSAxmjYESUIkbmSDZxwOl1Z_mLC8emj1iRNgO_Gm9EsaRFL9d1dmZnteNiHTmiPIsDqyrkOaYXBjMr5VZsey3ZTLA/s640/7.1-Checked.PNG" width="640" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;"><b>Рис 6</b>. Всегда тестирую перед тем, как отдать тестировщику.</td></tr>
</tbody></table>
<div>
Предполагаемый уровень качества приложения повысился. В этом варианте из 78 комитов из 100 можно ожидать сносного качества.</div>
<div>
<br /></div>
<div>
Кроме фиксирования переменной, на которую я могу влиять непосредственно можно еще анализировать влияние остальных факторов. Следующий запрос к системе это демонстрирует.</div>
<h4>
А если тестировщих обнаружил новые баги после моего комита?</h4>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjNKxzmXPXAaQqn6WOgYQhfpVePtmvpaS7vdhE8MOeetaHg3PVAYPQPyCzz3-z1xMD3AT_C81SKnVUZF84DyyX5AF_KyC69yhelRtuobUvVEsVfJc_9vSKSDBFMXdfTr24JL4dXcA/s1600/8-Introduced.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="468" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjNKxzmXPXAaQqn6WOgYQhfpVePtmvpaS7vdhE8MOeetaHg3PVAYPQPyCzz3-z1xMD3AT_C81SKnVUZF84DyyX5AF_KyC69yhelRtuobUvVEsVfJc_9vSKSDBFMXdfTr24JL4dXcA/s640/8-Introduced.PNG" width="640" /></a></div>
<div>
<br />
<br />
Что в этом случае можно сказать о моей прилежности и добросовестности? А можно сказать, что с вероятностю 82.35% я не тестировал интеграционно свой код перед отправкой.<br />
А как изменилось качество? КО говорит, что качество не очень. Конкретнее можно надеятсья, что только в 16% подобных ситуаций качество будет приемлемо. *<br />
<br />
<span style="font-size: x-small;">* Еще одна примечательность. Вероятность того, что бага переоткроется (<b>R</b>) немного увеличилась. Это потому, что знание того, какое значение приняла <b>I</b> (появились новые баги) повлияло на <b>C </b>(логично же - раз есть новые баги, то я не добросовестно отнесся к интеграционному тестированию). А поскольку вероятность того, что я тестировал свои изменения уменшилась, то и увеличилась вероятность того, что бага переоткроется.</span><br />
<div>
<span style="font-size: x-small;"><br /></span></div>
<h3>
Выводы.</h3>
<div>
Мы построили сеть Байеса чтобы проанализировать зависимости между проведением "предкомитного" тестирования (<b>C</b>), вероятностями появления новых багов (<b>I</b>), переоткрытия старых (<b>R</b>) и как это влияет на качество системы (<b>Q</b>). </div>
<div>
Согласно нашей модели если я, как разработчик, никогда не буду тестировать свои изменения перед тем, как отдать тестировщику, то в 58% случаев это сработает и качество системы не пострадает. Если же я всегда буду прогонять предкомитные тесты, то следует ожидать качественную систему в 78%.<br />
Можно предположить, что даже имея довольно оптимистичные прогнозы насчет качества моих изменений все равно влияние предкомитного тестирования довольно существенное.<br />
<br />
Должен сказать, что это очень простая модель, которая не учитывает многие другие факторы, которые могут влиять на качество. К тому же исходные данные могут очень сильно отличаться в разных проектах и командах.<br />
<br />
<h4>
Дальнейшие исследования.</h4>
</div>
Все-таки мне не хватило аргументов, чтобы заставить себя чаще тестировать свои изменения интеграционными тестами. Хотя результат меня немного удивил. Я ожидал математическое подтверждение своей интуиции.<br />
Поэтому в следющем посте я постараюсь привести эти вероятностные характеристики к более ощутимым вещам. А именно к трудозатратам и, как следствие к стоимости своего решения игнорировать комплексное тестирование своих изменений перед их отправкой тестировщику.<br />
<br />
<br />
<div>
<div>
Disclaimer (что по-русски означает письменный отказ от ответственности)</div>
<div>
<ol>
<li><span style="font-size: x-small;">Прошу не относиться к этому исследованию, как к сколь-нибудь весомому аргументу, который может повлиять на твои методы разработки. Это всего лишь один из множества вариантов построения и оценки взаимодействия взаимозависимых элементов модели. А как известно, любая модель зависит от данных, которыми она оперирует, другими словами "garbage in - garbage out" :). К тому же и в модели тоже бывают ошибки ;)</span></li>
<li><span style="font-size: x-small;">Я старался избегать специальных терминов и попытался изложить идею максимально просто, но если ты нашел, что изложение слишком сложно для восприятия, то пожалуйста оставь коментарий или задай вопрос. </span></li>
</ol>
<h4>
Ссылки на источники, исходные файлы<br /><span style="font-weight: normal;">- Средство моделирования сетей Байеса </span><a href="http://reasoning.cs.ucla.edu/samiam/" style="font-weight: normal;" target="_blank">SamIam</a><br /><span style="font-weight: normal;">- Курс на Coursera <a href="https://www.coursera.org/course/pgm" target="_blank">Probabilistic Graphical Models</a><br />- Файл модели для SamIam можно скачать отсюда <a href="http://bit.ly/ZALP1J">http://bit.ly/ZALP1J</a></span></h4>
<span style="font-family: inherit;">PS. <span lang="RU" style="font-size: 11pt; line-height: 115%;">Я
давно заметил, что программисты склонны давать очень аргументированные отмазки
лишь бы не делать того, что им не хочется :). Пока что мои аргументы работают против меня, посмотрим, что получится во 2-й части исследования. </span></span><br />
<div>
</div>
</div>
</div>
</div>
<div>
</div>
</div>
Сергей Зеленинhttp://www.blogger.com/profile/01667820453041864348noreply@blogger.com1tag:blogger.com,1999:blog-24004232.post-10962900915117339312012-11-27T14:03:00.000-08:002012-11-28T00:35:50.642-08:00Baby steps in Clojure. Правдивый ли random в Java?Недавно <a href="http://apofig.blogspot.com/">Санек </a>скинул интересную статью про нерандомные рандомы в php <a href="http://boallen.com/random-numbers.html"><b>http://boallen.com/random-numbers.html</b></a>.<br />
Вот решил побаловаться и проверить рандомный ли рандом в java :)<br />
<br />
Сделаю тоже самое, что сделали ребята на пхп, только на Clojure, то есть сгенерирую (x * y) вариантов рандома (0 или 1), и, если выпадет 1, то нарисую точку на экране с координатой (x, y).<br />
<br />
Для начала определю функцию, которая будет считать рандомы.<br />
<br />
<pre class="codeblock brush: clojure">(defn rands [max-x max-y]
(for [x (range max-x) y (range max-y)
:let [rnd (cond (< (rand 1) 0.5) 0 :else 1)]
:when (= rnd 1)
]
[x y rnd]))
</pre>
<br />
<br />
Что здесь:<br />
- Входные параметры max-x и max-y<br />
- Перебераем все сочетания (0..max-x : 0..max-y) и отбераем только те значения, рандом которых вернет 1. Выражение <b style="font-family: 'Courier New', Courier, monospace;">(cond (< (rand 1) 0.5) 0 :else 1) </b><span style="font-family: inherit;">должно вернуть нам 1 или 0 с вероятностью 50%</span><br />
<span style="font-family: inherit;">- Возвращаем последовательность из векторов, состоящих из x, y, rnd. Где x, y - переменные, по которым итерируемся, rnd - рандомное значение (уже отфильтрованное ограничением </span><b><span style="font-family: Courier New, Courier, monospace;">:when</span></b><span style="font-family: inherit;">)</span><br />
<div>
<span style="font-family: inherit;"><br /></span></div>
<div>
Проверяю. Запускаю в REPL:</div>
<div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh0Dx26I5WtRRPYMbzMDicYFLoXzzxLPTS6zeiMMxTaEp5DIayAQt7jiJaYY1HSxaCMWxZOmSoWJqFVpkD_k9m688SndEXrj0E_QqJG1PEJX0sOudaaUyK5iuE2-RLtJeJ56mfq1Q/s1600/1.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="200" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh0Dx26I5WtRRPYMbzMDicYFLoXzzxLPTS6zeiMMxTaEp5DIayAQt7jiJaYY1HSxaCMWxZOmSoWJqFVpkD_k9m688SndEXrj0E_QqJG1PEJX0sOudaaUyK5iuE2-RLtJeJ56mfq1Q/s400/1.PNG" width="400" /></a></div>
</div>
<div>
<br /></div>
<div>
Вроде похоже на правду - возвращает значения x и y только для рандома, равному 1.</div>
<div>
<br /></div>
<div>
Теперь выведем на экран (по примеру, взятому из книжки Joy of Clojure). Запускаю в REPLe команды:</div>
<pre class="codeblock brush: clojure">(def frame (java.awt.Frame.))
(.setSize frame 512 512)
(.setVisible frame true)
</pre>
Что здесь:<br />
- Определяем переменную frame, которой присвоится значение нового экземпляра java.awt.Frame. На Java это выглядело бы как <span style="font-family: Courier New, Courier, monospace;">Frame frame = new java.awt.Frame();</span><br />
- Устанавливаем размер frame 512 x 51. Java: f<span style="font-family: Courier New, Courier, monospace;">rame.setSize(512, 512);</span><br />
- Делаем фрейм видимым. Java: <span style="font-family: Courier New, Courier, monospace;">frame.setVisible(true);</span><br />
<br />
Крутизна в том, что не надо перекомпиливать приложение! Результат выполнения команд виден сразу же.<br />
<br />
Вот как это выглядит из Идеевской консоли REPL. Я использую плагин La Closure для IDEA.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgyKoraPyEqFQnYWP7pcWBz67iqqpgZnyPNBrP26vZaROomzS14l2-mJ8VosCgGe0uBAtJYnfrWieOh1x_Xgev5ov_sNnYBmz0L65sS9tcdVPc-zi4W36ZUi4PeqKdENPKn0OPyXw/s1600/2.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="198" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgyKoraPyEqFQnYWP7pcWBz67iqqpgZnyPNBrP26vZaROomzS14l2-mJ8VosCgGe0uBAtJYnfrWieOh1x_Xgev5ov_sNnYBmz0L65sS9tcdVPc-zi4W36ZUi4PeqKdENPKn0OPyXw/s400/2.PNG" width="400" /></a></div>
<br />
<br />
Появилось пустое awt-шное окошко, в котором и будем рисовать:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgWli__maiEg5WIvlduV27kx5bA5ZwmKvw-Wvgzq2Df93yK9M5N6rLuj53VBYTDLmUgqZoLO9NyLX0gGvgdEayqlZgUykgOZwK3YdJsTnRIp3ou0EuURAm_Sj4pgjbmv7fA3dEWQg/s1600/3.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="318" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgWli__maiEg5WIvlduV27kx5bA5ZwmKvw-Wvgzq2Df93yK9M5N6rLuj53VBYTDLmUgqZoLO9NyLX0gGvgdEayqlZgUykgOZwK3YdJsTnRIp3ou0EuURAm_Sj4pgjbmv7fA3dEWQg/s320/3.png" width="320" /></a></div>
<br />
<br />
Теперь осталось совсем малость - нарисовать. Опять же в REPLe запускаю такие команды:<br />
<br />
<pre class="codeblock brush: clojure">(def gfx (.getGraphics frame))
(doseq [[x y rnd] (rands 512 512)]
(.fillRect gfx x y 1 1))</pre>
Что здесь:<br />
- Определили gfx, который равен graphics у frame. Java: <span style="font-family: Courier New, Courier, monospace;">Graphics gfx = frame.getGraphics();</span><br />
<span style="font-family: Arial, Helvetica, sans-serif;">-</span><span style="font-family: Courier New, Courier, monospace;"> </span><span style="font-family: inherit;">Проитерировались по списку, который нам вернет вызов функции (</span><span style="font-family: Courier New, Courier, monospace;">rands 512 512) </span><br />
<span style="font-family: inherit;">- Помним, что rands возвращает вектор из переменных [x, y и значение рандома rnd]. Значения x,y и рандома присвоятся в локальные переменные x,y,rnd соответственно. Такая форма в Clojure </span>называется <span style="font-family: inherit;">деструктуризация (destructuring).</span><br />
<span style="font-family: inherit;">- Для каждого элемента списка заполним единичный rectangle в координате x, y</span><br />
<span style="font-family: inherit;"><br /></span>
Вот что получилось на экране:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjil0K2Pq4INlSIdmHj9pt2HNLpoJ5XVtcSIxTu7vcUwd4fRqM1ZMoWFWzjmCf0efKT4D9vi8XWYnFZfIZko2ctdO_qOCKkEZ-F7aaamk6jTOpt0TbTwVwclM7yQI_n8z4diS8big/s1600/4.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="446" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjil0K2Pq4INlSIdmHj9pt2HNLpoJ5XVtcSIxTu7vcUwd4fRqM1ZMoWFWzjmCf0efKT4D9vi8XWYnFZfIZko2ctdO_qOCKkEZ-F7aaamk6jTOpt0TbTwVwclM7yQI_n8z4diS8big/s640/4.png" width="640" /></a></div>
<br />
<br />
Важно помнить, что изображение в awt-шном окошке "одноразовое". То есть оно исчезнет как только мы перекроем awt-шный фрейм другим окном или же свернем/развернем его.<br />
<br />
Так что псевдорандом в Java похож на рандом. А Clojure + REPL - прикольная вещь!<br />
<br />
Весь код целиком ниже. Тоже самое на Java наверняка потребовало бы чуть больше кода, но повторять это на Java уже не хочется :)<br />
<br />
<pre class="codeblock brush: clojure">(defn rands [max-x max-y]
(for [x (range max-x) y (range max-y)
:let [rnd (cond (< (rand 1) 0.5) 0 :else 1)]
:when (= rnd 1)
]
[x y rnd]))
(def frame (java.awt.Frame.))
(.setSize frame 512 512)
(.setVisible frame true)
(def gfx (.getGraphics frame))
(doseq [[x y rnd] (rands 512 512)]
(.fillRect gfx x y 1 1))
</pre>
Сергей Зеленинhttp://www.blogger.com/profile/01667820453041864348noreply@blogger.com0tag:blogger.com,1999:blog-24004232.post-31227542103840333082012-11-08T04:16:00.000-08:002014-04-08T00:18:06.498-07:00Играем в тетрис на C#. Запуск на Visual Web Developer Express 2010<h3>
1. Установка Visual Wеb Developer 2010 Express.</h3>
Если подходящая версия Visual Studio 2010 уже установлена, то этот этап можно пропустить :).<br />
<br />
Запускаем инсталлер, ставим. Я устанавливал <a href="http://www.microsoft.com/visualstudio/en-us/products/2010-editions/visual-web-developer-express" style="font-weight: bold;">отсюда</a>.<br />
<br />
Ниже картинки того, что происходило на экране во время установки.<br />
<br />
IIS и Web Deploy еще понятно, но зачем мне SQL Server ставить :)?<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjladA7dmA5iuUQZfk-l35W-ok-4jwEM1iktPBRiHu5iGCEhXeFNEjAFYYD9fqiV-MWvnkwa_jjtQricIPeo61EpgV2dlepMd0Zxe91Itg28Z5lhfsCjqMGHj-AEI9LP86Y0Cgn6A/s1600/webdeveloper_install.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjladA7dmA5iuUQZfk-l35W-ok-4jwEM1iktPBRiHu5iGCEhXeFNEjAFYYD9fqiV-MWvnkwa_jjtQricIPeo61EpgV2dlepMd0Zxe91Itg28Z5lhfsCjqMGHj-AEI9LP86Y0Cgn6A/s640/webdeveloper_install.PNG" height="438" width="640" /></a></div>
<br />
Ладно, жму Install. Просит пароль sa для SQL Server.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh-iYH95fFnvXBX3DuvJNj8bMKiii5f7sQoA8Nw9w0pz_WsppeAmJ4qBSh8DpYvIigozWrMizQb1WPWyGtnZFMY8J6DLumaPIosaf1-SV1eA9mRrQRSOEEW5QuAXryYd8JO1fAcgw/s1600/sapassword.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh-iYH95fFnvXBX3DuvJNj8bMKiii5f7sQoA8Nw9w0pz_WsppeAmJ4qBSh8DpYvIigozWrMizQb1WPWyGtnZFMY8J6DLumaPIosaf1-SV1eA9mRrQRSOEEW5QuAXryYd8JO1fAcgw/s640/sapassword.PNG" height="435" width="640" /></a></div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
Пустой пароль, как и 123 - не подходят :)</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi7FD4R0p9gCM0IG1ZvITKGMLCywADTCrNK7GkeVWbvNgeCoBjDsfDlEH6rZ6BL1ERAXZluBkZ1XVGFBZZ3WZgdGQRm_JKgG31CWFDxGEgpOPFPhDVvsCVjq3-FbDtMH00L1d6Hmg/s1600/pass.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi7FD4R0p9gCM0IG1ZvITKGMLCywADTCrNK7GkeVWbvNgeCoBjDsfDlEH6rZ6BL1ERAXZluBkZ1XVGFBZZ3WZgdGQRm_JKgG31CWFDxGEgpOPFPhDVvsCVjq3-FbDtMH00L1d6Hmg/s400/pass.PNG" height="137" width="400" /></a></div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
Качаем, ждем :)<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi8zAaMhsR_n8Y4JmgGzLliu3yf0CSRqKptKk_7Fejc6_FlGkcPeaSs4zoo1eqYIMYDgb6oQCMXG3FqI2VtlrJBDwiDOsJwqYkQHBtpGmuDUoPjwsiu9zMQxIwukbaSutvfl8JFEQ/s1600/download.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi8zAaMhsR_n8Y4JmgGzLliu3yf0CSRqKptKk_7Fejc6_FlGkcPeaSs4zoo1eqYIMYDgb6oQCMXG3FqI2VtlrJBDwiDOsJwqYkQHBtpGmuDUoPjwsiu9zMQxIwukbaSutvfl8JFEQ/s640/download.PNG" height="442" width="640" /></a></div>
<br />
<h3>
2. Устанавливаем NuGet package manager</h3>
Открываем окно Extension Manager из меню Tools->Extension Manager...<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiHJBmzI2Vud-m8ZOXGNrTgPc7Jp3ZY2z3FsAEhpTo8weA9WYyJJoeRTZd3PLfs5oiZG63jziOqxaPBNjZpnLHO3jqDU0hydEpfGovTFiPPSZb2RQfVUjgdRv-veR5C_Vqf8ivUaA/s1600/Install-Nuget-1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiHJBmzI2Vud-m8ZOXGNrTgPc7Jp3ZY2z3FsAEhpTo8weA9WYyJJoeRTZd3PLfs5oiZG63jziOqxaPBNjZpnLHO3jqDU0hydEpfGovTFiPPSZb2RQfVUjgdRv-veR5C_Vqf8ivUaA/s640/Install-Nuget-1.png" height="328" width="640" /></a></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgXNmZVqaBcKJwlWhexCyZyK3FGdK_XLz7yzn5t-k3PeUtSLryOSQYSGEJUBoYTO6QGPgG2WchDLOZ3AXx7taRyIzMLztG_EzJZRogorU865LKcRKSyiGhVuXrnkfddSmC0ZX9y3A/s1600/Install-Nuget-2.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgXNmZVqaBcKJwlWhexCyZyK3FGdK_XLz7yzn5t-k3PeUtSLryOSQYSGEJUBoYTO6QGPgG2WchDLOZ3AXx7taRyIzMLztG_EzJZRogorU865LKcRKSyiGhVuXrnkfddSmC0ZX9y3A/s640/Install-Nuget-2.PNG" height="336" width="640" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<div style="text-align: start;">
<br /></div>
<div style="text-align: start;">
Если NuGet Package manager не установлен, то ищем его в Online Gallery и устанавливаем</div>
<div style="text-align: start;">
<br /></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhkpx9mcVLEp3GM4w7mZODvjpd8qT8_jyO9HXF6VBPC3Nf6yP9q57pmMFn7dFaVDY4wRt5IrqElOR6C6bhxYr4SrYEI1NpTZ8yVe5tQltWrvJtnyERKJbFa0S_TWz_bBTW1oulLIw/s1600/Install-Nuget-3.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhkpx9mcVLEp3GM4w7mZODvjpd8qT8_jyO9HXF6VBPC3Nf6yP9q57pmMFn7dFaVDY4wRt5IrqElOR6C6bhxYr4SrYEI1NpTZ8yVe5tQltWrvJtnyERKJbFa0S_TWz_bBTW1oulLIw/s640/Install-Nuget-3.PNG" height="344" width="640" /></a></div>
<h3>
3. Стартуем</h3>
Скачиваем щаблон проекта <a href="http://codenjoy.com/files/tetris-servers.zip">http://codenjoy.com/files/tetris-servers.zip</a><br />
<br />
После распаковки открываем проект в Visual Studio. Проект находится в <unpack folder>\tetris-servers\tetris-servers\csharp\nancy<br />
<br />
Открываем Package manager console<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhiPxFv5dkJbHskzwVsPJSk3QzJ01x7riPK4IIs6fU9Byx_DTDGaQjmu63t0Z0BvY44n84i1g9mDxDfWUu518CFpV24sJ6CkVdXRrB5V1GyFiSjYqfyAVz396ZKs2ahsIYr7cyXpA/s1600/Install-PackageManager-1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhiPxFv5dkJbHskzwVsPJSk3QzJ01x7riPK4IIs6fU9Byx_DTDGaQjmu63t0Z0BvY44n84i1g9mDxDfWUu518CFpV24sJ6CkVdXRrB5V1GyFiSjYqfyAVz396ZKs2ahsIYr7cyXpA/s640/Install-PackageManager-1.png" height="194" width="640" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg2qV0vhAbaZnI4YpPAgQfmh4UeN2J1CjbwqWmWClJrKtphxmUmKc25ubBBcpCn6IYw7hb5odLLzopXWBKYNHrR1qJi50aYg-kfwCzNpfjpSAYXt1mUXO8pBkaBWUuZXRaOlDU2CA/s1600/Install-PackageManager-2.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg2qV0vhAbaZnI4YpPAgQfmh4UeN2J1CjbwqWmWClJrKtphxmUmKc25ubBBcpCn6IYw7hb5odLLzopXWBKYNHrR1qJi50aYg-kfwCzNpfjpSAYXt1mUXO8pBkaBWUuZXRaOlDU2CA/s640/Install-PackageManager-2.PNG" height="162" width="640" /></a></div>
и запускаем команду Install-Package Nancy.Hosting.Self<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg-RK2VkWBtG_0269pNHS6AACgw3-UHMIfnaWJ2mBesU35yQX72Cjrqg19Xa1XuBlB77Rb_kpr_8SqBp4efljwMHHsumqvNjqMbcQHwspMZMmB50me3r4uVaxSXG9o6n1Pi9jwe1w/s1600/Install-PackageManager-4.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg-RK2VkWBtG_0269pNHS6AACgw3-UHMIfnaWJ2mBesU35yQX72Cjrqg19Xa1XuBlB77Rb_kpr_8SqBp4efljwMHHsumqvNjqMbcQHwspMZMmB50me3r4uVaxSXG9o6n1Pi9jwe1w/s640/Install-PackageManager-4.PNG" height="120" width="640" /></a></div>
<br />
Если ошибок нет, то можно подключаться к игре.Сергей Зеленинhttp://www.blogger.com/profile/01667820453041864348noreply@blogger.com0tag:blogger.com,1999:blog-24004232.post-67174098039125930372012-10-28T09:34:00.000-07:002012-11-08T04:17:50.665-08:00Красивый file upload с jQueryКаждый веб разработчик хоть раз да писал что-нибудь, что испольует форму файлового аплоада. Стандартная форма файловой загрузки ужасна. Как ее ни украшай стилями, она все равно остается ужасной и слабо поддающейся кастомизации. В очередной раз посмотрел я на уродливую форму стандартной загрузки файлов и пошел ресерчить альтернативы.<br />
<br />
<h3>
jQuery File Upload </h3>
Проект лежит здесь: <a href="https://github.com/blueimp/jQuery-File-Upload">https://github.com/blueimp/jQuery-File-Upload</a><br />
Примерчики здесь: <a href="http://blueimp.github.com/jQuery-File-Upload/">http://blueimp.github.com/jQuery-File-Upload</a><br />
<br />
<b>Пример. </b>Ниже реализована загрузка файла с прогресом. Будет одна кнопка Upload, которая позволит выбрать файл и сразу загрузить его на сервер.<br />
<br />
<h4>
1. Скачиваем.</h4>
Скачиваю отсюда <a href="https://github.com/blueimp/jQuery-File-Upload/downloads">https://github.com/blueimp/jQuery-File-Upload/downloads</a>, распаковываю архивчик.<br />
<br />
<h4>
2. Копируем javascript и css файлы в свой проект.</h4>
Я буду пользоваться минимальной установкой, поэтому в свой проект копирую только некоторые файлы. Ресурсы моего проекта находятся в папке webapp/resources, в нее копирую следующие javascript файлики из архивчика:<br />
- js/vendor/jquery.ui.widget.js<br />
- js/jquery.iframe-transport.js<br />
- js/jquery.fileupload.js<br />
- css/jquery.fileupload-ui.css<br />
<br />
Подробнее о минимальной установке здесь: <a href="https://github.com/blueimp/jQuery-File-Upload/wiki/Basic-plugin">https://github.com/blueimp/jQuery-File-Upload/wiki/Basic-plugin</a><br />
<br />
<h4>
3. Добавляем Bootstrap и jQuery</h4>
3.1. Файлик с jQuery берем здесь <a href="http://jquery.com/download/">http://jquery.com/download/</a> и добавляем в проект.<br />
3.2. Bootstrap берем здесь <a href="http://twitter.github.com/bootstrap/index.html">http://twitter.github.com/bootstrap/index.html</a>. Добавляем файлы:<br />
- css/bootstrap.css<br />
- img/glyphicons-halflings.png<br />
- img/glyphicons-halflings-white.png<br />
<br />
<h4>
4. Создаем страничку</h4>
Красиво отобразить JSP страничку в блоге не получилось, поэтому даю ссылку на файл jsp.<br />
Полный текст JSP странички можно посмотреть <a href="https://dl.dropbox.com/u/22607711/FileUploadServlet.java">здесь</a>.<br />
<pre style="border: 0px; color: #333333; font-family: Consolas, 'Liberation Mono', Courier, monospace; font-size: 12px; line-height: 12.800000190734863px; padding: 0px;"></pre>
<h4>
5. Пишем сервлет загрузки</h4>
Ничего особенного в сервлете нет, главное, чтобы он вернул JSON, по которому пройдемся в колбеке done.<br />
Текст сервлета для этой странички можно глянуть <a href="https://github.com/tdd-elevator-training/tetris/blob/master/tetris-online/src/main/java/net/tetris/web/controller/FileUploadServlet.java">здесь</a>.Сергей Зеленинhttp://www.blogger.com/profile/01667820453041864348noreply@blogger.com1tag:blogger.com,1999:blog-24004232.post-3092898371990083212012-10-24T06:44:00.001-07:002012-10-24T06:56:06.531-07:00Unit тесты с embedded JettyЧастенько хочется проверить обращение к внешнему HTTP серверу. Раньше я пользовался встроенным в JDK http сервером (см пакет com.sun.net.httpserver). В этом случае тест работает быстрее, но вытаскивать параметры из http запроса приходилось писать самому.<br />
<br />
Я попробовал запустить embedded Jetty сервер в тесте, в котором создать сервлетик, который запоминает приходящие параметры реквеста. Вот что получилось.<br />
<br />
Сам тест
<script class="brush: java" type="syntaxhighlighter">
<![CDATA[public class MyTest {
private FakeHttpServer server;
@Before
public void setUp() throws Exception {
server = new FakeHttpServer(1111);
server.start();
}
@After
public void tearDown() throws Exception {
server.stop();
}
//If our logic failed to call server properly then test will hang on waitForRequest()
@Test(timeout = 1000)
public void shouldSendRequestControlCommands() throws IOException, InterruptedException {
server.setResponse("Hello");
String response = readServerResponse(new URL("http://localhost:1111/?param1=value1¶m2=value2"));
//You will definitely need this for asynchronous client
server.waitForRequest();
assertEquals("value1", server.getRequestParameter("param1"));
assertEquals("value2", server.getRequestParameter("param2"));
assertEquals("Hello", response);
}
private String readServerResponse(URL url) throws IOException {
URLConnection connection = url.openConnection();
connection.connect();
StringBuilder stringBuilder = new StringBuilder();
BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
String line;
while ((line = reader.readLine()) != null) {
stringBuilder.append(line);
}
return stringBuilder.toString();
}
}
]]>
</script>
<br />
Реализация фейкового сервера<br />
<script class="brush: java" type="syntaxhighlighter">
<![CDATA[
public class FakeHttpServer {
private String response;
private Server jettyServer = new Server();
private Map<String, String[]> parameters;
private int port;
private ReentrantLock lock = new ReentrantLock();
private Condition requestProcessed = lock.newCondition();
public FakeHttpServer(int port) throws IOException {
this.port = port;
}
public void start() throws Exception {
SelectChannelConnector connector = new SelectChannelConnector();
connector.setPort(port);
jettyServer.addConnector(connector);
ServletContextHandler root = new ServletContextHandler(jettyServer, "/");
root.addServlet(new ServletHolder(new HttpServlet(){
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
lock.lock();
try {
parameters = req.getParameterMap();
resp.getWriter().print(response);
requestProcessed.signal();
} finally {
lock.unlock();
}
}
}), "/*");
jettyServer.start();
}
public void setResponse(String response) {
this.response = response;
}
public void stop() throws Exception {
jettyServer.stop();
}
public String getRequestParameter(String name) {
return parameters.get(name)[0];
}
//For asynchronous client requests
public void waitForRequest() throws InterruptedException {
lock.lock();
try {
while (parameters == null) {
requestProcessed.await();
}
} finally {
lock.unlock();
}
}
}
]]>
</script>
<br />
Зависимости<br />
<script class="brush: xml" type="syntaxhighlighter">
<![CDATA[
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelversion>4.0.0</modelVersion>
<groupid>embedded-jetty</groupId>
<artifactid>embedded-jetty</artifactId>
<version>1.0-SNAPSHOT</version>
<name>embedded-jetty</name>
<packaging>war</packaging>
<properties>
<jetty.version>8.1.3.v20120416</jetty.version>
</properties>
<dependencies>
<dependency>
<groupid>org.eclipse.jetty</groupId>
<artifactid>jetty-servlet</artifactId>
<version>${jetty.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupid>junit</groupId>
<artifactid>junit</artifactId>
<version>4.10</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
]]>
</script>
<br />
Полностью проект можно скачать <a href="https://dl.dropbox.com/u/22607711/embedded-jetty-test.zip"><b>тут</b></a><br />
<br />
<b>Интересно</b><br />
Если будет использоваться асинхронная отсылка http запроса из теста, то синхронизировать сам тест с обработкой запроса можно с помощью метода FakeServer.waitForResponse.<br />
Вот интересная статья про семафоры <a href="http://www.baptiste-wicht.com/2010/09/java-concurrency-part-5-monitors-locks-and-conditions/">http://www.baptiste-wicht.com/2010/09/java-concurrency-part-5-monitors-locks-and-conditions/</a>Сергей Зеленинhttp://www.blogger.com/profile/01667820453041864348noreply@blogger.com0tag:blogger.com,1999:blog-24004232.post-33265410300786137022012-10-05T09:07:00.000-07:002012-10-05T09:12:08.201-07:00Динамическая конфигурация Java Security Policy в Jetty<h4>
Задача</h4>
В рамках моего веб приложения надо умудриться задеплоить еще один war, на котором будет работать какой-то сервлет. Поскольку этот war - это стороннее веб приложение, то надо запретить ему доступ к файловой системе, открытие сокетов и тем более вызывать System.exit.<br />
Подобная задача легко решается в OSGi контейнере (<a href="http://nierbeck.de/cgi-bin/weblog_basic/index.php?p=67">пример на OSGi тут</a>). Но у меня Jetty и необходимо очень <strike>тупое</strike> простое решение.<br />
<br />
Предположим в этом "левом" веб-приложении какой-нибудь умник решил проверить систему на прочность и написал такой код:<br />
<script class="brush: java" type="syntaxhighlighter">
<![CDATA[
public class TestServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
System.exit(-1); //protect here
}
}
]]>
</script>
<br />
Естественно, что при вызове сервлета System.exit сделает свое дело и погасит мой сервер.<br />
<br />
<h4>
Деплоим стороннее веб приложение</h4>
Прежде всего надо убедиться, что модули jetty-webapp и jetty-server подключены к проекту. У меня версия Jetty 8.1.3.v20120416<br />
<script class="brush: xml" type="syntaxhighlighter">
<![CDATA[
<dependency>
<groupid>org.eclipse.jetty</groupId>
<artifactid>jetty-server</artifactId>
<version>${jetty.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupid>org.eclipse.jetty</groupId>
<artifactid>jetty-webapp</artifactId>
<version>${jetty.version}</version>
<scope>compile</scope>
</dependency>
]]>
</script>
<br />
<br />
<br />
Дальше пишем код деплоймента веб архива в Jetty<br />
<script class="brush: java" type="syntaxhighlighter">
<![CDATA[
Server server = new Server();
SelectChannelConnector connector = new SelectChannelConnector();
connector.setPort(12345);
final WebAppContext webContext = new WebAppContext(new File("c:\\myApp.war").getAbsolutePath(),"");
Resource.setDefaultUseCaches(false);
webContext.setCopyWebDir(false);
webContext.setCopyWebInf(false);
webContext.setTempDirectory(new File(System.getProperty("user.home", "myApp")));
webContext.setExtractWAR(false);
server.addConnector(connector);
server.setHandler(webContext);
]]>
</script>
<br />
Если задеплоить этот код в любой web container, то как только мы дернем страничку из браузера, то все...<br />
<br />
<h4>
Настраиваем Java security manager</h4>
Самый тупой способ, который я нашел - это подставить свою реализацию java.security.policy:<br />
<script class="brush: java" type="syntaxhighlighter">
<![CDATA[
Policy.setPolicy(new Policy() {
@Override
public boolean implies(ProtectionDomain domain, Permission permission) {
boolean calledByWebapp = webContext.getClassLoader() == domain.getClassLoader();
return !(calledByWebapp &&
RuntimePermission.class.equals(permission.getClass()) &&
permission.getName().startsWith("exitVM"));
}
});
System.setSecurityManager(new SecurityManager());
]]>
</script>
Надо обратить внимание на, что Policy надо устанавливать ДО того, как переопределить SecurityManager, иначе не хватит прав для установки новой Policy :)Сергей Зеленинhttp://www.blogger.com/profile/01667820453041864348noreply@blogger.com0tag:blogger.com,1999:blog-24004232.post-36058371522325671912012-09-20T11:18:00.003-07:002012-09-20T11:19:20.605-07:00Git: Как пофиксить detached HEADЕсли git при комите ругается, что у вас detached HEAD, то пофиксить можно 4-мя командами:<br />
<br />
<span style="font-family: Courier New, Courier, monospace;">git branch temp</span><br />
<span style="font-family: Courier New, Courier, monospace;">git checkout temp</span><br />
<span style="font-family: Courier New, Courier, monospace;">git branch -f master temp</span><br />
<span style="font-family: Courier New, Courier, monospace;">git checkout master</span><br />
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: inherit;">И, опционально,</span><br />
<span style="font-family: Courier New, Courier, monospace;">git branch -d temp</span><br />
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
<span style="font-family: inherit;">Что при этом происходит</span><br />
<span style="font-family: inherit;">1. создаем временную ветку с текущего положения HEAD</span><br />
<span style="font-family: inherit;">2. переключаемся на временную ветку</span><br />
<span style="font-family: inherit;">3. сбросить master до позиции в temp</span><br />
<span style="font-family: inherit;">4. переключиться на мастер</span><br />
<span style="font-family: inherit;">5. удалить временную ветку</span>Сергей Зеленинhttp://www.blogger.com/profile/01667820453041864348noreply@blogger.com1tag:blogger.com,1999:blog-24004232.post-19508968743991644032012-09-18T14:12:00.002-07:002012-09-21T02:05:40.436-07:00Tetris coding dojo. Включаем мозг. Включение #0.<h3>
О чем это</h3>
Сейчас трудно найти программиста, который не знал бы, что такое ТДД или рефакторинг. Трудно найти такого, который еще и применял бы эти практики в жизни. Мне, например, повезло и я ощутил полезность всего спектра так называемых "инженерных практик" в реальной разработке. Многие ребята не могут похвастаться таким везением. Естественно, что не попробовав применить, например, ТДД на практике, сложно сделать какой-нибудь вывод о его полезности.<br />
Конечно же есть масса тренингов по инженерным практикам. Но проблема в том, что практические задания на подобных мероприятиях довольно простые. На них мы можем только отработать базовые навыки. К тому же приходится давать довольно много теории, рассматривать различныые варианты решения задачи и подводить слушателей к правильным теоретическим выводам. Не хватает практического подтверждения (или опровержения :)) правильности сделанных выводов.<br />
Решение этой проблемы пришло само собой:<br />
<br />
<h3>
Tetris Coding dojo</h3>
Вообще <a href="http://codingdojo.org/">Coding dojo</a> - отличное и веселое мероприятие. Мы (я и <a href="http://apofig.blogspot.com/">Саша Баглай</a>) подумали, что это подходящий формат для того, чтобы попрактиковаться в применении своих <strike>мозгов</strike> навыков в инженерных практиках. Мы одолжили идею coding dojo и сделали свой фреймворк.<br />
За основу взяли известную всем игру - тетрис. Будем учить компьютер играть в тетрис! Мало того, мы еще будем соревноваться в том, кто круче это сделает :)<br />
<br />
Думаю, что лучше один раз увидеть, чем написать 100500 слов описания.<br />
<iframe allowfullscreen="allowfullscreen" frameborder="0" height="315" src="http://www.youtube.com/embed/WrUC6w2hZTA" width="560"></iframe>
<br />
<h3>
Пробное включение</h3>
Недавно мы с <a href="http://apofig.blogspot.com/">Сашей</a> попробовали сами написать код управления фигурками. Это мероприятие выбило нас из жизни часа на 4 :). В результате пофиксили несколько багов во фреймворке. Я же научился расставлять "палочки" и "квадратики", сделал несколько небольших рефакторингов и окончательно убедился, что я тестозависим :).<br />
<br />
Код тетриса лежит на гитхабе, написан на java. Запускается одной командой (mvn jetty:run). Чуть позже напишу подробную инструкцию (если не поленюсь:)). Велкам с коментами, пожеланиями и участием.Сергей Зеленинhttp://www.blogger.com/profile/01667820453041864348noreply@blogger.com2tag:blogger.com,1999:blog-24004232.post-64581003106755684562012-09-01T14:04:00.002-07:002012-09-01T14:42:45.746-07:00Вспомнил детство : ZX Spectrum, деревянные игрушки...Говорят, что нереализованные детские мечты нужно осуществлять, ибо они как-то там мешают во взрослой жизни. Так вот, когда деревья были больше, трава зеленее, а самая навороченная компьютерная игра требовала 48Кб памяти и 0.8 Мгц частоты ЦПУ, у меня была идея фикс - победить злостных инопланетянских роботов-захватчиков в стратегической реал-тайм (!) игрушке "Nether Earth". Я бился в нее сутками, но эти гады все равно беспощадно уничтожали мою базу. Естественно, что это сказывалось на моем эмоциональном состоянии, успеваемости в школе и вообще надо было утереть нос моему товарищу, который эту игру якобы прошел.<br />
<br />
<h4>
Настало время реванша. </h4>
<div>
Первым делом нужен эмулятор ZX Spectrum. Их как оказалось довольно много, вот хотя бы с<a href="http://www.emu-land.net/computers/zx_spectrum/emuls/windows">писок эмуляторов под Видновс</a>.</div>
<div>
Но мне не нужны легкие пути, я взял эмулятор под Java - <a href="http://jspeccy.speccy.org/">JSpeccy</a> :). Не беда, что сайт разработчика на испанском, зато он регулярно выпускает обновления (последнее было в июне 2012 года!) в отличие от остальных. </div>
<div>
<br /></div>
<div>
Найти саму игру тоже оказалось несложно. На сайте <a href="http://bit16.ru/">Bit 16</a> я быстро нашел <a href="http://bit16.ru/index.php?gl=roms&cat=zx&act=top&topr=3">Nether Eath</a>.</div>
<div>
<br /></div>
<div>
Запустил как обычно из командной строки <br />
<span style="font-family: Courier New, Courier, monospace;">java -jar JSpeccy.jar</span> </div>
<div>
<br /></div>
<div>
Круто - появилась строка ввода! Правда в ZX Spectrum она замаскирована под название компании - разработчика ;)</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg9axdPKZbyCLrRaJahiV358UgMQYrsxcl_wmpzdObiMlB6Jn8suBcRBXLqKo6ANrjoSCSDWXBvgb9_PxB9a_7twXf8mq08QLtUL98FtJRRFHTuftXg62C6yB342lgAOqBR0X9UqQ/s1600/1.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="296" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg9axdPKZbyCLrRaJahiV358UgMQYrsxcl_wmpzdObiMlB6Jn8suBcRBXLqKo6ANrjoSCSDWXBvgb9_PxB9a_7twXf8mq08QLtUL98FtJRRFHTuftXg62C6yB342lgAOqBR0X9UqQ/s320/1.PNG" width="320" /></a></div>
<div>
<br /></div>
<div>
Дальше в меню File->Load... выбираю файл образа игры</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhlIqrkv8Nxkr3Fgy9Gmkf_JxpiJP7RAaGmW1w__03YFms8NTbx73rsW_s6yzH_GuYEwsLi_mZgrtCvsXtngGKRjqLba9SEb0KRGFg488QCPQMR8XpE3AkfET1-dJ4ni-3ch15q5A/s1600/2.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="294" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhlIqrkv8Nxkr3Fgy9Gmkf_JxpiJP7RAaGmW1w__03YFms8NTbx73rsW_s6yzH_GuYEwsLi_mZgrtCvsXtngGKRjqLba9SEb0KRGFg488QCPQMR8XpE3AkfET1-dJ4ni-3ch15q5A/s320/2.PNG" width="320" /></a></div>
<div>
<br /></div>
<div>
Ожидал, что игрушка сразу запустится, но этого, естественно, не произошло :). В Синклере игры считывались с магнитофона, для чего нужно ввести команду LOAD "". Магическое нажатие на клавишу <b>J </b>выдало команду LOAD, а <Ctrl> + P вывел двойную кавычку.</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjzkKw31xCHq7xMnftosrlOyX9SI5p47cU-scODEpRLv9-wu_QewS3mXXn1l-phtRcvONM5eHAr12hxh3coFKggmnx37DEJGpwPNmO6oPd9_fnbqgDfgBU4EX2Y4R4ydkSgrD3JIg/s1600/3.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="296" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjzkKw31xCHq7xMnftosrlOyX9SI5p47cU-scODEpRLv9-wu_QewS3mXXn1l-phtRcvONM5eHAr12hxh3coFKggmnx37DEJGpwPNmO6oPd9_fnbqgDfgBU4EX2Y4R4ydkSgrD3JIg/s320/3.PNG" width="320" /></a></div>
<div>
<br /></div>
<div>
Нажал на <ENTER> и пошла загрузка. Жаль, что звук с магнитофона не сэмулировали :)</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg3r14KKzaxuHqZl4BJVTXieGasEG24MfT7TmDJWZLzjAG-M0klIN_QeD-r-Ta2K8vx_LLP07W7kqimFE0RJfVI5iyMssu9Q7tqat2rSVKX0h2pifsWEuYwE50rIvQdYJUfPTlNnw/s1600/4.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="295" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg3r14KKzaxuHqZl4BJVTXieGasEG24MfT7TmDJWZLzjAG-M0klIN_QeD-r-Ta2K8vx_LLP07W7kqimFE0RJfVI5iyMssu9Q7tqat2rSVKX0h2pifsWEuYwE50rIvQdYJUfPTlNnw/s320/4.PNG" width="320" /></a></div>
<br />
Чудесная заставка, офигительный звук :). Без всяких звуковых карт и музыкальных сопроцессоров! Частота колебаний динамика программируется ручками :).<br />
<br />
<div style="text-align: center;">
<iframe width="420" height="315" src="http://www.youtube.com/embed/8R5-KU1LJL4" frameborder="0" allowfullscreen></iframe>
</div>
<br />
<div style="text-align: left;">
Запрограммировал клавиши, жму старт. Вот она - сила 3D графики! Никаких графических сопроцессоров и видеокарт. Почти "реал 3d" :).<br />
<br />
На экране<br />
1. Контрольный модуль, может летать и садиться на базу<br />
2. База<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiBCCvhY9kazvTRDsRSo1n8zdRNOf1s5Jdn7ot3sy5adeAHZ1JB9zsaeGTBcbMifk3PG5IU2HtaJBxNTL00-mxH4BCkbcXFlB29kQTye9BQNkKKgKOwcqFCTa_-ngc1MPtiy4TD8Q/s1600/5.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="241" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiBCCvhY9kazvTRDsRSo1n8zdRNOf1s5Jdn7ot3sy5adeAHZ1JB9zsaeGTBcbMifk3PG5IU2HtaJBxNTL00-mxH4BCkbcXFlB29kQTye9BQNkKKgKOwcqFCTa_-ngc1MPtiy4TD8Q/s320/5.PNG" width="320" /></a></div>
<br />
На базе можно создавать роботов из имеющихся деталей. Количество деталей ограничено и поставляется заводами.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiBFxGdRlgkqxntFtBhdsMqywZr0QyFEBikCWWmTgg_IMheVRIQb_bwWkmxTSfTEB48hpm4Xej5x2zk_U8oEnXnJp4NdXms3GSOWuS-MV3Cw3DetndSslBO-xY-yQ41Pv9VzJ6_pQ/s1600/6.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiBFxGdRlgkqxntFtBhdsMqywZr0QyFEBikCWWmTgg_IMheVRIQb_bwWkmxTSfTEB48hpm4Xej5x2zk_U8oEnXnJp4NdXms3GSOWuS-MV3Cw3DetndSslBO-xY-yQ41Pv9VzJ6_pQ/s320/6.PNG" width="320" /></a></div>
<br />
Создал первого супер робота<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg5IjB1sm8FTyAxZ-3tAhNaSTZPEpUEmg5Mgx90vuMcdAxWnaG_1zXIeFlH1pIVe43dfXKt8aWbLGiYehg3gXYai-q_gl-fKPFvdP7N_Oojz-QieONyOXQwSqmGMTuthWDuRQeGfg/s1600/7.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="233" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg5IjB1sm8FTyAxZ-3tAhNaSTZPEpUEmg5Mgx90vuMcdAxWnaG_1zXIeFlH1pIVe43dfXKt8aWbLGiYehg3gXYai-q_gl-fKPFvdP7N_Oojz-QieONyOXQwSqmGMTuthWDuRQeGfg/s320/7.PNG" width="320" /></a></div>
<br />
И выпустил его из завода
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgBdE2XeMRXUnYRMfqNJrnxcq7OOP_20UIFaFUDDh_F1su7oTaw-bNDhQdjiYpMDvur-PYoe2R4nhLDpkanVh9_ik8YpDEJWbz8DJsasxq7MuWLshKd8TXR61VB6r59yDKnwwsQnA/s1600/8.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="237" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgBdE2XeMRXUnYRMfqNJrnxcq7OOP_20UIFaFUDDh_F1su7oTaw-bNDhQdjiYpMDvur-PYoe2R4nhLDpkanVh9_ik8YpDEJWbz8DJsasxq7MuWLshKd8TXR61VB6r59yDKnwwsQnA/s320/8.PNG" width="320" /></a></div>
<br />
Роботом можно управлять непосредственно или запрограммировать его поведение.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiNegdDHF7xU6u4KA8yNwl9hLtOz4O4aIjTg3QzJCydDl6puSm9RjgJDZljfPIq8OISHsGdoHuZYVF8HCh8HkU_7_n64LaKFXHU_JfuXDzw_22nMHPqu9WwnNftM-rG7AJJvi1-Nw/s1600/9.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiNegdDHF7xU6u4KA8yNwl9hLtOz4O4aIjTg3QzJCydDl6puSm9RjgJDZljfPIq8OISHsGdoHuZYVF8HCh8HkU_7_n64LaKFXHU_JfuXDzw_22nMHPqu9WwnNftM-rG7AJJvi1-Nw/s320/9.PNG" width="320" /></a></div>
<br />
<br />
Варианты поведения<br />
- Пройти вперед / назад<br />
- Найти и уничтожить вражеского робота / фабрику / вражескую базу<br />
- Найти и захватить нейтральную
фабрику / вражескую фабрику / вражескую базу<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiZ_APxq-lPw4FKfz1jfBEyDBDXbAVJZ32a3PT4sZ0KmhMnJfJ3tdM6nTeFmylWLczMevVM89mkRI1U-4WaZmfONnKs_FOJeHbs9O6BboAb0dbKgryqs83ek9JG-A1nATGWo1co6A/s1600/10.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="235" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiZ_APxq-lPw4FKfz1jfBEyDBDXbAVJZ32a3PT4sZ0KmhMnJfJ3tdM6nTeFmylWLczMevVM89mkRI1U-4WaZmfONnKs_FOJeHbs9O6BboAb0dbKgryqs83ek9JG-A1nATGWo1co6A/s320/10.PNG" width="320" /></a></div>
<br />
Я задал команду захватить завод. После того, как робот постоит на заводе 10 сек, завод считается захваченным, о чем свидетельствует появившийсе вверху слева американский флаг :).<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjz5NYFL1onQ0ZS_e7B3QDs9IYiZPtA46MfMjntWxJtEtt4WQzfhUTCUq9NXPwu4fARVBU3WsLsKoL4S6pceh1oApxZu6jYt8R8P_IgyWyr-ASHjwRFq3Pi3udfbLLL2-P9ij8CQA/s1600/11.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="249" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjz5NYFL1onQ0ZS_e7B3QDs9IYiZPtA46MfMjntWxJtEtt4WQzfhUTCUq9NXPwu4fARVBU3WsLsKoL4S6pceh1oApxZu6jYt8R8P_IgyWyr-ASHjwRFq3Pi3udfbLLL2-P9ij8CQA/s320/11.PNG" width="320" /></a></div>
<br />
Что особенно меня убивало в этой игре - так это управление во время боя. Для того, чтобы повернуться нужно выбрать "Move robot", а дойдя до огневой позиции успеть остановить робота, выбрать оружие для стрельбы и стрельнуть. Жутко неудобно, особенно когда по тебе стреляют несколько вражеских машин.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiELNqq_26K2MTAaT5fj-uZI_f_Lqnm_4uy5PXmBUplR1EIJxDVXnrtXYS2oPkICqUCY7YfcXLuANC7iJRkU71I4jPfktO4il0sVUpx4_c1l7ezvS5xlykqxXUKlYzkxoxTsOgQkA/s1600/12.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="244" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiELNqq_26K2MTAaT5fj-uZI_f_Lqnm_4uy5PXmBUplR1EIJxDVXnrtXYS2oPkICqUCY7YfcXLuANC7iJRkU71I4jPfktO4il0sVUpx4_c1l7ezvS5xlykqxXUKlYzkxoxTsOgQkA/s320/12.PNG" width="320" /></a></div>
<br />
Вот такая нехитрая игрушка. Для середины 80х это была мега крутая игра. А сейчас даже не верится, что на 48кб можно разместить саму игру с "3д" графикой и звуком.<br />
<br />
Кстати, вчера играл 3 часа подряд, но мою базу опять захватили подлые инопланетские роботы :(</div>Сергей Зеленинhttp://www.blogger.com/profile/01667820453041864348noreply@blogger.com4tag:blogger.com,1999:blog-24004232.post-42774259886163610082012-08-20T07:42:00.000-07:002012-08-21T04:01:52.722-07:00Coding dojo. Установка сервера на PHP.Ниже инструкции для установки локального сервера coding-dojo <b><a href="http://szelenin.blogspot.com/2012/08/coding-dojo-php.html#apache">для Apache</a></b> (рекомендуемая) и <b><a href="http://szelenin.blogspot.com/2012/08/coding-dojo-php.html#sockets">для расширения sockets</a></b>.<br />
<br />
<h3>
<a href="http://szelenin.blogspot.com/2012/08/coding-dojo-php.html#apache" name="apache">Настройка локального сервера на Apache + php.</a></h3>
<i>Для того, чтобы воспользоваться этой конфигурацией необходим настроенный Apache + php. Как я настраивал можно почитать <a href="http://szelenin.blogspot.com/2012/07/denwer.html">здесь</a>. </i><br />
<i><br /></i>
1. <b>Скачиваем шаблон php скрипта</b> <b><a href="https://dl.dropbox.com/u/22607711/solver.php">отсюда</a> </b>и подкладываем его в любое место, доступное apache. В моем случае это <span style="font-family: Courier New, Courier, monospace;">Z:\home\calculator\www\solver.php </span><br />
<span style="font-family: Courier New, Courier, monospace;"><br /></span>
2. <b>Проверка локального сервера.</b><br />
Теоретически все готово. Проверить можно, запустив скрипт с примером запроса. Например: <a href="http://www.calculator/solver.php?q=what+is+the+sum+of+8+and+27">http://www.calculator/solver.php?q=what+is+the+sum+of+8+and+27</a><br />
<br />
Должны увидеть тело запроса в браузере. Примерно так:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi0_xHxHyPdzQBDOch3O8m_98xFsX8si6h03HkD0XBwyxZ6SKwmbOMeWkx6T1vqBIMhFsK-pfyQqYmAUHGbAi0Y6QJhv7ib3B1NPQWXb8CCl-JD2t7MszMcCy-quMF7rD-bvN6zoA/s1600/apache-test.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="56" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi0_xHxHyPdzQBDOch3O8m_98xFsX8si6h03HkD0XBwyxZ6SKwmbOMeWkx6T1vqBIMhFsK-pfyQqYmAUHGbAi0Y6QJhv7ib3B1NPQWXb8CCl-JD2t7MszMcCy-quMF7rD-bvN6zoA/s320/apache-test.PNG" width="320" /></a></div>
<br />
Рядом с solver.php должен создасться файлик<span style="font-family: Courier New, Courier, monospace;"> requests.txt</span>с содержимым запроса:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjNJM3ZQIitZRluR96crvZEiDLHsROq_pPOlmNvXlPTjmAZXwKTjhrObucmmVHcBLY7Ci6okElKDHjEmWKmR280MLUsRhMQ1D3cq4UCQDc4FZJ9esfNhgVb3uDRmUokkXces08-0A/s1600/request.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjNJM3ZQIitZRluR96crvZEiDLHsROq_pPOlmNvXlPTjmAZXwKTjhrObucmmVHcBLY7Ci6okElKDHjEmWKmR280MLUsRhMQ1D3cq4UCQDc4FZJ9esfNhgVb3uDRmUokkXces08-0A/s1600/request.png" /></a></div>
<br />
Дальше регистрируем нового игрока. В качестве URL надо ввести адрес, по <b>которому доджо сервер запустит скрипт solver.php</b>. В моем случае это
<a href="http://www.calculator/solver.php">http://www.calculator/solver.php</a>.<br />
Идем <a href="http://szelenin.blogspot.com/2012/08/coding-dojo-php.html#register"><b>сюда</b></a>.<br />
<br />
<h3>
<a href="http://www.blogger.com/blogger.g?blogID=24004232" name="sockets">Установка локального сервера, работающего на расширении sockets</a> </h3>
1. <b>Установка локального сервера.</b><br />
Скачиваем архивчик <a href="https://dl.dropbox.com/u/22607711/dojo.zip">отсюда</a>. Это содержимое репозитория со старт-поинтами взятый отсюда<br />
<a href="https://github.com/bodil/extreme_startup_servers">https://github.com/bodil/extreme_startup_servers</a>. <br />
Распаковываем (у меня в папку <span style="background-color: white; color: #666666; font-family: monospace; font-size: 13.142857551574707px; line-height: 10.285714149475098px;">C:\workspace\projects\dojo-startup\extreme_startup_servers</span>).<br />
<br />
В папке php/sockets находится 2 файлика : server.php и solver.php<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhItDQ9qMpGNC3A0am3hlLPdHFcu7nQ7QaVOJmVttnLc4xEmYef3cg3pjV6S-eb7jTibKSCiBcWeurCmfRilmQQjSdny8TqCpJtRIvcvtELjn_aaLO7_pPqdxjWMhBltzT3TKkjhw/s1600/sockets.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="91" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhItDQ9qMpGNC3A0am3hlLPdHFcu7nQ7QaVOJmVttnLc4xEmYef3cg3pjV6S-eb7jTibKSCiBcWeurCmfRilmQQjSdny8TqCpJtRIvcvtELjn_aaLO7_pPqdxjWMhBltzT3TKkjhw/s320/sockets.PNG" width="320" /></a></div>
<br />
<span style="color: red;"><b>Важно!</b></span><br />
Сервер требует расширения php_sockets. Для этого строка extension=php_sockets.dll в файле php.ini должна быть раскоментирована.<br />
<br />
2. <b>Запуск локального сервера.</b><br />
Запускаем server.php из командной строки:<br />
<span style="font-family: Courier New, Courier, monospace;">php server.php</span><br />
<br />
<i>Если при запуске не подключается расширение php_sockets, то см. ниже "<a href="http://szelenin.blogspot.com/2012/08/coding-dojo-php.html#whattodo">что делать...</a>"</i><br />
<br />
3. <b>Проверка локального сервера.</b><br />
Наш локальный сервер запущен. Не мешает проверить как он работает. Для этого в браузере вводим такой линк <span style="background-color: white; color: #666666; font-family: monospace; font-size: 13.142857551574707px; line-height: 10.285714149475098px;"><a href="http://localhost:1337/?q=what+is+the+sum+of+8+and+22" style="color: #02689b;">http://localhost:9000/?q=what+is+the+sum+of+8+and+22</a></span><span style="color: #666666; font-family: 'Trebuchet MS', Trebuchet, Verdana, sans-serif; font-size: x-small;">.</span><br />
<br />
По идее в консоли должно появится сообщение с запросом и ответом на него:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgUyNtVcfxTJ6p46yBEvme95bV0Ifn_b6ZaKNBLvkCd_xl9UXmUdscEFhazf6loFU9rTRoMZC45T6RrphsBoutrYgCObm6wS5SEidZaxt-JdhRmmiz3XMdrH9HENbassZecX9ts3g/s1600/request-answer.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="25" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgUyNtVcfxTJ6p46yBEvme95bV0Ifn_b6ZaKNBLvkCd_xl9UXmUdscEFhazf6loFU9rTRoMZC45T6RrphsBoutrYgCObm6wS5SEidZaxt-JdhRmmiz3XMdrH9HENbassZecX9ts3g/s400/request-answer.PNG" width="400" /></a></div>
<br />
Если посмотреть содержимое файла solver.php, то понятно почему ответ такой же как и запрос :)<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhv9VbsoAIKYALnu9WRNFHOi57rQpjowqLfvsckzkRSgaEw-C9QHPOXK7hCr7Vj7jhi5wMcajm4LuHwatFUJI3XUvEG2y0riP9vu65X_fq6lepTRBXmUJy-I7mWeobNxGipWeP7Xw/s1600/solver.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhv9VbsoAIKYALnu9WRNFHOi57rQpjowqLfvsckzkRSgaEw-C9QHPOXK7hCr7Vj7jhi5wMcajm4LuHwatFUJI3XUvEG2y0riP9vu65X_fq6lepTRBXmUJy-I7mWeobNxGipWeP7Xw/s1600/solver.PNG" /></a></div>
<br />
3.1. Немного модифицированный файл solver.php находится <a href="https://dl.dropbox.com/u/22607711/solver.php.txt" style="font-weight: bold;">здесь</a>. Он записывает все входящие запросы в файлик, что очень удобно во время соревнования.<br />
<br />
Дальше регистрируем наш сервер в системе. Описано <a href="http://szelenin.blogspot.com/2012/08/coding-dojo-php.html#register">здесь</a>.<br />
<br />
<h3>
<a href="http://www.blogger.com/blogger.g?blogID=24004232" name="register">Регистрация нового пользователя на доджо сервере.</a></h3>
<div class="separator" style="clear: both; text-align: left;">
Теперь можем регистрировать наш сервер. Переходим на страничку хоста (http://localhost:3000 или в случае другого компа – <b>IP адрес этой машины</b>), нажимаем I want to play и вводим имя (<b>латинскими буквами!</b>) и URL вашего сервера. </div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgix_SMwPsjv05-YTU83dl1hN7Dy8WB4hfCzktc_np5ckrPT6HBVx-vQcLFgJrS01IYBjDIzg8noUd6XtQdE8q2EWAQzraIPsiKoaO8kBcpjDAXXjCBZQ29sf_RShCrbkJvozdn0A/s1600/iwanttoplay.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="203" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgix_SMwPsjv05-YTU83dl1hN7Dy8WB4hfCzktc_np5ckrPT6HBVx-vQcLFgJrS01IYBjDIzg8noUd6XtQdE8q2EWAQzraIPsiKoaO8kBcpjDAXXjCBZQ29sf_RShCrbkJvozdn0A/s400/iwanttoplay.PNG" width="400" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEip0jpviIxMLc5_Vpa6ncXsxG6f26B3Sar0po9wBi2eNQrTAwRKhqQ1UXN93YFXyWtY2kGVfq7f2F091mA-pRHJANvV9zPP0XQLtYcc2Fj2zvIWKv6siokwXSFPdXzd-Zms45KHUg/s1600/addplayer.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="114" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEip0jpviIxMLc5_Vpa6ncXsxG6f26B3Sar0po9wBi2eNQrTAwRKhqQ1UXN93YFXyWtY2kGVfq7f2F091mA-pRHJANvV9zPP0XQLtYcc2Fj2zvIWKv6siokwXSFPdXzd-Zms45KHUg/s400/addplayer.PNG" width="400" /></a></div>
<br />
<br />
Если хост смог достучаться до сервера, то увидим примерно такую обнадеживающую надпись<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjS4JCb-0Jm9U96JgJazUaOV0cN-yqOJfK5JSXqSIFUugfDuIb66Ejp63pk8ap-YFcVCX-0VIsyXZzkLPBhZi9call6p1y-Dd774pg7PvavGIc8UhIJ5Cz1_K3wAuxLtebYgDG1TA/s1600/thanks.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="91" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjS4JCb-0Jm9U96JgJazUaOV0cN-yqOJfK5JSXqSIFUugfDuIb66Ejp63pk8ap-YFcVCX-0VIsyXZzkLPBhZi9call6p1y-Dd774pg7PvavGIc8UhIJ5Cz1_K3wAuxLtebYgDG1TA/s400/thanks.PNG" width="400" /></a></div>
<br />
Можем посмотреть результат работы нашей программы. Поскольку мы еще ничего не запрограммировали, то ожидаем примерно такую картинку:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh-O2XvKIeyVXvG-OKOYws55QlR9O1Tu2Le87TbutDW1mTRKV7JbJhaBFtk3NEMY3gvCT5cWRwmaGN8R42IZtW2CZWJDAhTL1YN6ahzQXLC2GK5AYczPqPYxM6oAoXDEIdSpbqgyw/s1600/hellosergey.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="286" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh-O2XvKIeyVXvG-OKOYws55QlR9O1Tu2Le87TbutDW1mTRKV7JbJhaBFtk3NEMY3gvCT5cWRwmaGN8R42IZtW2CZWJDAhTL1YN6ahzQXLC2GK5AYczPqPYxM6oAoXDEIdSpbqgyw/s400/hellosergey.PNG" width="400" /></a></div>
В консоли локального сервера будет примерно такое (в случае с сервером на sockets extension)<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhidF44F4bA8DZBZxaI6TzQ4R18xKY3-2Y1GRO7A1V5hW7Avw-PAmTZP1gFG8XVNq-1u7maHZbO3SfMmZW5gU2jDXhGuFi3HRd8yv-dK8WEmveiPWtvrelymekoIEfDjBAl7Tds0Q/s1600/request-answer2.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="134" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhidF44F4bA8DZBZxaI6TzQ4R18xKY3-2Y1GRO7A1V5hW7Avw-PAmTZP1gFG8XVNq-1u7maHZbO3SfMmZW5gU2jDXhGuFi3HRd8yv-dK8WEmveiPWtvrelymekoIEfDjBAl7Tds0Q/s640/request-answer2.PNG" width="640" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
В лог request.txt файле будет примерно такое:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhlIgC69MLw8dpWeu3Z7YnGk_loF6wfzityrJphM_LgqfXnfmaXVTCAWPq3cBvE0dxE2otvHh3byOvdUd_6m2T4KKVhDKlHwuP9SkmKMGXXYdHdru4rCi_SwNTkO_O6jtqbmlp-Ow/s1600/requests-sample.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="35" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhlIgC69MLw8dpWeu3Z7YnGk_loF6wfzityrJphM_LgqfXnfmaXVTCAWPq3cBvE0dxE2otvHh3byOvdUd_6m2T4KKVhDKlHwuP9SkmKMGXXYdHdru4rCi_SwNTkO_O6jtqbmlp-Ow/s320/requests-sample.PNG" width="320" /></a></div>
<br />
В принципе можно уже начинать программировать solver.php так, чтобы он давал адекватные ответы :)<br />
<br />
<b>Что делать, если...</b><br />
<br />
<b style="color: #666666; font-family: 'Trebuchet MS', Trebuchet, Verdana, sans-serif; font-size: small;"><a href="http://www.blogger.com/blogger.g?blogID=24004232" name="whattodo" style="color: #666666; font-family: 'Trebuchet MS', Trebuchet, Verdana, sans-serif; font-size: small;">Что делать, если не запускается php_sockets?</a></b><br />
Если при запуске server.php выдается подобная ошибка, то нужно скачать соовместимую версию библиотеки php_sockets.dll.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjhM9FS7vwoXVH30oOKnsWd_K50QFysqMJUkqtBqoo5k0I4PKnp5JFEoh8V_kr5uCSs5_hA9GypJib05BopJMwbwmBlckMyF_6vv42a5iGBX6oa1U6hZIRSsY3XW9PoydjDK68Jqg/s1600/warning-socket.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="198" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjhM9FS7vwoXVH30oOKnsWd_K50QFysqMJUkqtBqoo5k0I4PKnp5JFEoh8V_kr5uCSs5_hA9GypJib05BopJMwbwmBlckMyF_6vv42a5iGBX6oa1U6hZIRSsY3XW9PoydjDK68Jqg/s320/warning-socket.PNG" width="320" /></a></div>
<br />
Эта ошибка у меня полечилась скачиванием и установкой расширений денвера (<a href="http://www.denwer.ru/packages/php5.html">http://www.denwer.ru/packages/php5.html</a>).<br />
Второй вариант лечения - скачать zip с пакетом пхп <a href="http://windows.php.net/download/#php-5.3">отсюда </a>и скопировать php_sockets.dll в папку расширений php.Сергей Зеленинhttp://www.blogger.com/profile/01667820453041864348noreply@blogger.com2tag:blogger.com,1999:blog-24004232.post-58612227962587953882012-07-28T03:49:00.000-07:002012-07-28T03:49:35.521-07:00PHP для бабушек. Ставим Symfony.<br />
<b>Что такое Symfony? </b><br />
Клевый ответ на этот вопрос на сайте разработчиков: "Symfony - это PHP Web Development framework. Что тут неясно? А если неясно, то проще считай, что это религия!" (framework + philosophy+comunity)<br />
Не веришь? Можешь сам перевести отсюда <a href="http://symfony.com/what-is-symfony">http://symfony.com/what-is-symfony</a> <span style="background-color: white;">:)</span><br />
<br />
Попробую пройти "обряд крещения". Как обычно расписано по шагам для старичков с хреновой памятью типа меня :)<br />
Вот инструкция по установке и использованию. Я просто следовал ей.<br />
<iframe allowfullscreen="" frameborder="0" height="356" marginheight="0" marginwidth="0" scrolling="no" src="http://www.slideshare.net/slideshow/embed_code/8911543" style="border: 1px solid rgb(204, 204, 204); margin-bottom: 5px;" width="427"></iframe><br />
<h4 style="text-align: left;">
<i style="font-weight: normal; text-align: -webkit-auto;">Я проделывал это на двух компах, у которых путь установки проекта </i><i style="background-color: white; font-weight: normal; text-align: -webkit-auto;">отличается</i><i style="background-color: white; font-weight: normal; text-align: -webkit-auto;"> именем замапленного диска. Отсюда и разные пути в тексте и скриншотах.</i></h4>
<h4 style="text-align: left;">
1. Установка</h4>
<b>Модный способ - через композер</b>. Как поставить композер, описано <a href="http://szelenin.blogspot.com/2012/07/composer-maven-php.html" target="_blank">здесь</a>.<br />
В рутовой папке проекта (у меня это w:\home\calculator\www) запустить такое (я запускал из гитового баша как обычно):<br />
<br />
<div style="background-color: whitesmoke; font-family: Arial, Helvetica, sans-serif; font-size: 16px; margin: 0px; padding: 0px 0px 20px; text-align: left;">
<pre style="background-color: #232125; color: white; font-size: 14px; line-height: 1.3em; overflow: auto; padding: 0.7em;">php composer.phar create-project symfony/framework-standard-edition Symfony/</pre>
</div>
<br />
Нифига не заработало на домашнем компе, но заработало на рабочем. Поэтому следующий вариант такой:<br />
<br />
<div style="margin-bottom: 5px;">
<b style="background-color: white;">Скачиваю</b><span style="background-color: white;"> отсюда </span><a href="http://symfony.com/download" style="background-color: white;">http://symfony.com/download</a><span style="background-color: white;"> </span><span style="background-color: white;">Symfony Standart и распаковываю в рутовую папку. В руте у меня появится папка Symfony</span></div>
<span style="background-color: white;"><span style="font-family: 'Courier New', Courier, monospace;">w\</span></span><br />
<span style="background-color: white;"><span style="font-family: 'Courier New', Courier, monospace;">|-calculator</span></span><br />
<span style="background-color: white;"><span style="font-family: 'Courier New', Courier, monospace;"> |-www<br /> |-Symfony</span></span><br />
<span style="font-family: inherit;"><br /></span><br />
<b style="font-family: inherit;">Запускаю</b><span style="font-family: inherit;"> в папочке w:\calculator\www\Symfony (с баш строки как обычно)</span><br />
<span style="font-family: 'Courier New', Courier, monospace;">php bin/vendors install</span><br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhQhWM2aYJEkW4wz2kyHrA9tUCwfw6jGT65L6FztvoNHSGxVO7nC2CPq2c_LNlOTX3NFKqUOtKWF_lwoJnzVbTLI-CCcdNaOJZXxsnDF5UcOIDpEW1pQ_wSyO1Ab9tOvYTyNXG19Q/s1600/install.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhQhWM2aYJEkW4wz2kyHrA9tUCwfw6jGT65L6FztvoNHSGxVO7nC2CPq2c_LNlOTX3NFKqUOtKWF_lwoJnzVbTLI-CCcdNaOJZXxsnDF5UcOIDpEW1pQ_wSyO1Ab9tOvYTyNXG19Q/s1600/install.PNG" /></a></div>
<br />
далее идет непериводимая игра слов выдаваемых скриптом и гитом.<br />
<b>Появилась</b> папочка vendor. Сюда Symfony понапихивало массу полезностей.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEghVe4m0yJCvtOp686Zc6Aw52yRbB7qQRGkgIuD4ZVpHFdpzMroggqTMNouslBYCRIeG6G2tRjs0alpyEVLnNj5aKwF06WDK2fedpYYCmNbITBb54BgKSkcxnEzOpT9lki9P8Apsg/s1600/massa.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEghVe4m0yJCvtOp686Zc6Aw52yRbB7qQRGkgIuD4ZVpHFdpzMroggqTMNouslBYCRIeG6G2tRjs0alpyEVLnNj5aKwF06WDK2fedpYYCmNbITBb54BgKSkcxnEzOpT9lki9P8Apsg/s320/massa.PNG" width="160" /></a></div>
Все эти полезности описаны в файлике Symfony\deps. На 11м слайде презенташки можно увидеть как он выглядит.<br />
<br />
<span style="background-color: white; font-family: inherit;"><b>Проверяю</b>. Открываю в браузере * </span><span style="background-color: white; color: blue; font-family: Georgia, 'Times New Roman', serif;">http://www.calculator/Symfony/web/config.php</span><span style="background-color: white; font-family: inherit;">. Вижу страничку конфига.<br /><i>* www.calculator я добавил в файл Windows\System32\drivers\etc\hosts. Подробнее <a href="http://szelenin.blogspot.com/2012/07/denwer.html" target="_blank">здесь</a>.</i></span><br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhrtNLahnZZE39DO720TMzmUj4vsggFh70lc8thcMp-ucRYDqjLgY5a44ZHtQKCjvdVb4dWuZysb1Av0HWnjXHTfBKmB6x6fH0ONnuK4Nn6dFsM5FQXEwZHcHJjVQJrXd2sdnaJeA/s1600/symfony-config.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="424" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhrtNLahnZZE39DO720TMzmUj4vsggFh70lc8thcMp-ucRYDqjLgY5a44ZHtQKCjvdVb4dWuZysb1Av0HWnjXHTfBKmB6x6fH0ONnuK4Nn6dFsM5FQXEwZHcHJjVQJrXd2sdnaJeA/s640/symfony-config.PNG" width="640" /></a></div>
<span style="font-family: inherit;"><br />Тут он мне предлагает поделать всякие усовершенствования типа поставить акселератор, что-то поменять в php.ini. Естественно забиваю на эти сообщения - они мне жить пока не мешают.</span><br />
<span style="font-family: inherit;"><br /></span><br />
<h4>
<b>2. Создаю свое приложение.</b></h4>
<div>
<i>Тут я перепрыгнул на слайд 32. Сначала хочу создать приложение, а потом залить его в git.</i></div>
<div>
<b><br /></b></div>
<div>
<b>Создаю бандл</b></div>
<div>
в папке Symfony запускаю <span style="font-family: 'Courier New', Courier, monospace;">php app/console generate:bundle</span></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgOthE70p95pRyYehToHr1uk5ewSC3r48K3XPphpKRBwNMpgU9gk7mx601yB5RweOKbGIZq5kz6eVmosbIhc_vvo8xW4vvcH0OfeYeyaH2USCSSlPFtN4cvJ33jzifa-BzHcHpwug/s1600/generate-bundle.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="348" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgOthE70p95pRyYehToHr1uk5ewSC3r48K3XPphpKRBwNMpgU9gk7mx601yB5RweOKbGIZq5kz6eVmosbIhc_vvo8xW4vvcH0OfeYeyaH2USCSSlPFtN4cvJ33jzifa-BzHcHpwug/s640/generate-bundle.PNG" width="640" /></a></div>
<div>
<span style="font-family: 'Courier New', Courier, monospace;"><br /></span></div>
<div>
<span style="font-family: inherit;">Ввожу имя нейпспейса (Tdd/CalculatorBundle) и имя бандла (Calculator).</span></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjDFaH-sL9ktuyOocFjJkMPBIsDOMZSIzAYaEOpaYtLMGJkRQVGB4pxD9hmhX4-3Fsn_drFvC-Zm1nQaIDwgQ-JbOMr36RpbtBtWtxG-xlo5OudK4QQM1cNZh5hdZT4ajliIst_CA/s1600/bundle-namespace.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjDFaH-sL9ktuyOocFjJkMPBIsDOMZSIzAYaEOpaYtLMGJkRQVGB4pxD9hmhX4-3Fsn_drFvC-Zm1nQaIDwgQ-JbOMr36RpbtBtWtxG-xlo5OudK4QQM1cNZh5hdZT4ajliIst_CA/s1600/bundle-namespace.PNG" /></a></div>
<div>
<br /></div>
<div>
<span style="font-family: inherit;">Дальше куча вопросов, на которые просто жимаю <Enter></span></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh2yiuAtTN9dtXIgnFfKpoylCKfMCophVsOFAlb6YA6c8xRH3Xi3jo4NoIpJ5kzDCB_uN2ysMhl_ClflZExdf5U3KiXVdGdGserm0JNV034f-AEcZ_yJOIpKFE8ooZxPU7GbaDgNg/s1600/bundle-defaults.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="524" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh2yiuAtTN9dtXIgnFfKpoylCKfMCophVsOFAlb6YA6c8xRH3Xi3jo4NoIpJ5kzDCB_uN2ysMhl_ClflZExdf5U3KiXVdGdGserm0JNV034f-AEcZ_yJOIpKFE8ooZxPU7GbaDgNg/s640/bundle-defaults.PNG" width="640" /></a></div>
<div>
<br /></div>
<div>
<span style="font-family: inherit;"><b>Бандл - это</b> директория со всем, что нужно для фичи. Вот такое мне сгенерилось:</span></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEixOSikdFNxbo9sbsaQVxmZBvD-I-kXS9I9R7udvVQUbw41RbRVTQ8pizOensY538M_VS7w9BYTjOuJ91ENJMZCRll3W7L9F36FY5IRIkbO-Iaso3qA3DExTjKxtmqZS4s1iViaGA/s1600/bundle-tree.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEixOSikdFNxbo9sbsaQVxmZBvD-I-kXS9I9R7udvVQUbw41RbRVTQ8pizOensY538M_VS7w9BYTjOuJ91ENJMZCRll3W7L9F36FY5IRIkbO-Iaso3qA3DExTjKxtmqZS4s1iViaGA/s1600/bundle-tree.PNG" /></a></div>
<div>
<br /></div>
<div>
<span style="font-family: inherit;"><br /></span></div>
<h4>
3. Смотрим на тесты</h4>
Генератор бандлов также создал папку с тестами<br /><div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi0OW0ds7juan5cQiU85s4DQ2qwywFZx9Za-kSMZAu_zhnTS2mHilfnSQe5YLE-EkZbqOXVHd3GaHRDYulyM8wkW-_4EACRMGBlPDYD9XgjJldPd-NrDhzfjZvydIj6rqbaR_j9HQ/s1600/test-folder.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi0OW0ds7juan5cQiU85s4DQ2qwywFZx9Za-kSMZAu_zhnTS2mHilfnSQe5YLE-EkZbqOXVHd3GaHRDYulyM8wkW-_4EACRMGBlPDYD9XgjJldPd-NrDhzfjZvydIj6rqbaR_j9HQ/s1600/test-folder.PNG" /></a></div>
<div>
<br /></div>
<div>
В ней находится один тест на контроллер</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi0w-3lCTJ0_wKDY5cOeC8cL7TtXtHQC_Vk_zy0uJtK0zLEMmSP1Hy73JBqjWnKfSsD9cqiA5RWZiai51ydUGF0sR7k9aHxP5pYVUIPPzzB-7BllId_RygYXJu-WoI2AjrNEPOvuA/s1600/test-controller.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="230" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi0w-3lCTJ0_wKDY5cOeC8cL7TtXtHQC_Vk_zy0uJtK0zLEMmSP1Hy73JBqjWnKfSsD9cqiA5RWZiai51ydUGF0sR7k9aHxP5pYVUIPPzzB-7BllId_RygYXJu-WoI2AjrNEPOvuA/s640/test-controller.PNG" width="640" /></a></div>
<div>
<br /></div>
<div>
Запустить этот тест вместе со сьютом можно из корневой папки Symfony. Запускаю команду</div>
<div>
<span style="font-family: 'Courier New', Courier, monospace;">phpunit -c app/</span></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhzG9WisVrlFEBhnc23dbs43RMqlMq4oDCndt3ZHFSqIRSXhYPakJjBIHe6wQzXINP5GvPicpP7yYM_R-qRhxEcaqT7OlU9GgHMUWun381tvL2mSsD15gdCh7HKat6DDQ5YZEHFMw/s1600/run-suite.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhzG9WisVrlFEBhnc23dbs43RMqlMq4oDCndt3ZHFSqIRSXhYPakJjBIHe6wQzXINP5GvPicpP7yYM_R-qRhxEcaqT7OlU9GgHMUWun381tvL2mSsD15gdCh7HKat6DDQ5YZEHFMw/s1600/run-suite.PNG" /></a></div>
<div>
<i style="font-family: inherit;">параметр -c указывает конфигурационный файл для phpunit.</i></div>
<div>
<span style="background-color: white; font-family: inherit;"><b><i><br /></i></b></span></div>
<div>
<span style="background-color: white; font-family: inherit;"><b><i>Почему надо запускать тесты с опцией -c?</i></b></span></div>
<div>
<span style="background-color: white; font-family: inherit;">Тест на контроллер создает и запускает симфонийский кернел со всеми зависимостями. В файле app/phpunit.xml.dist есть ссылка на bootstrap, который делает предварительную инициализацию окружения. </span></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgv1HL-uYHfBHAQl5RxC_bXRjMwWQVMDudQYYisQIGx_J6ZgQ67K2aqR_kbwt5PuONjllqEWwUUYk8jzjDcVXAMtw4jaofcb45HWtZnlWz7_uJdVMqqRKP-jOdzDaTkIRpiYLdQCA/s1600/bootstrap.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="251" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgv1HL-uYHfBHAQl5RxC_bXRjMwWQVMDudQYYisQIGx_J6ZgQ67K2aqR_kbwt5PuONjllqEWwUUYk8jzjDcVXAMtw4jaofcb45HWtZnlWz7_uJdVMqqRKP-jOdzDaTkIRpiYLdQCA/s640/bootstrap.PNG" width="640" /></a></div>
<div>
<span style="background-color: white; font-family: inherit;"><br /></span></div>
<div>
<br /></div>
<div>
<span style="background-color: white; font-family: inherit;"><i><b>Настройки Netbeans для запуска тестов</b></i></span></div>
<div>
<span style="background-color: white; font-family: inherit;">Чтобы мои тесты запускались прямо в Netbeans я сделал следующее:</span></div>
<div>
- Указал где лежат тесты (окно Project properties, пункт Sources)</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEghToHOJsF3r1YRuSJHvFSQOQxqZYE0kuH1Q4foRCDR_FyaVEHSo2ei1-8URIr55F6RCeKoFtp7nJh1fYDpu3xd5_SVdDJtiHazwC46sNR_VWXaDOEWbUVh2NLALq2WLB47M4Q53g/s1600/netbeans-tests.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="462" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEghToHOJsF3r1YRuSJHvFSQOQxqZYE0kuH1Q4foRCDR_FyaVEHSo2ei1-8URIr55F6RCeKoFtp7nJh1fYDpu3xd5_SVdDJtiHazwC46sNR_VWXaDOEWbUVh2NLALq2WLB47M4Q53g/s640/netbeans-tests.PNG" width="640" /></a></div>
<div>
<br /></div>
<div>
- Выбрал phpunit.xml.dist в качестеве XML Configuration для тестов (окно Project properties, пункт PHPUnit)</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgsMj9KhMLGQ47rfUla_Ltiy5cEiAqdQ6zulOJzUIIqVUMOPKl6d62W-3sw8q-c0nuBPNJwfxlIRURBpSdFSZSU9KmqDPwFg1O7ph35c1fxd0TPCREVSJsneLFNGm-UUsd6jEEkkg/s1600/netbeans-tests.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="462" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgsMj9KhMLGQ47rfUla_Ltiy5cEiAqdQ6zulOJzUIIqVUMOPKl6d62W-3sw8q-c0nuBPNJwfxlIRURBpSdFSZSU9KmqDPwFg1O7ph35c1fxd0TPCREVSJsneLFNGm-UUsd6jEEkkg/s640/netbeans-tests.PNG" width="640" /></a></div>
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
<span style="text-align: -webkit-auto;">Теперь мои тесты запускаются также из ИДЕ:</span></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgEVjZYCYXCLGXM-tqERlRpe01JhEMLxj5Zf-rPa2VuLy9uHotSam8WybDHOgop4PSF-INQbq5dOYnSK2XblLTS3qDUBXuCkOCn6iLysxmK9tDwdCaL0tl9mfuQyMcPRn5vFk1B7g/s1600/run-netbeans.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgEVjZYCYXCLGXM-tqERlRpe01JhEMLxj5Zf-rPa2VuLy9uHotSam8WybDHOgop4PSF-INQbq5dOYnSK2XblLTS3qDUBXuCkOCn6iLysxmK9tDwdCaL0tl9mfuQyMcPRn5vFk1B7g/s1600/run-netbeans.PNG" /></a></div>
<div>
<br /></div>
<br class="Apple-interchange-newline" />Сергей Зеленинhttp://www.blogger.com/profile/01667820453041864348noreply@blogger.com3tag:blogger.com,1999:blog-24004232.post-52743252726436666612012-07-24T09:37:00.002-07:002012-07-24T12:35:29.446-07:00Composer - это Maven для PHP?Вот обнаружил, что на ПХП не так уж все и печально с менеджментом зависимостей. В PHP мире, оказывается существует замечательная штука - <a href="http://getcomposer.org/" target="_blank">Composer</a>.<br />
Вот, например, список пакетов, доступных для скачивания композером <a href="http://packagist.org/packages/">http://packagist.org/packages/</a>. И это только репозиторий композера по умолчанию! Кстати, то что джависты называют репозиторием мавена, в композере называется Packagist.<br />
Composer также позволяет скачивать зависимости из VCS (естественно, что git/github чаще всех встречается в качестве репозиториев PHPшных модулей).<br />
<br />
Короче, просмотрев "по диагонали" доку (<a href="http://getcomposer.org/doc/">http://getcomposer.org/doc/</a>) у меня начало развиваться чувство неполноценности мавена, как основного средства менеджмента зависимостей на моих Java проектах :)<span style="background-color: white;">. Вариантов хранения зависимостей - тьма! Можно хранить в VCS, можно в зип архиве на http server'e, также </span><span style="background-color: white;">поддерживаются </span><span style="background-color: white;">репозитории глобального менеджера зависимостей (<a href="http://szelenin.blogspot.com/2012/07/pear.html" target="_blank">PEAR</a>). А хочешь - ставь свой репозиторий и работай с ним. Код packagist'a доступен </span>для всеобщего доступа <a href="https://github.com/composer/packagist">https://github.com/composer/packagist</a><span style="background-color: white;"> </span><span style="background-color: white;">.</span><br />
<br />
"Хочу купить!" - так сказала бы моя жена, увидев красивую вещь, которая ей очень нужна. И я повторю ее фразу в отношении composer'a.<br />
<br />
<b>Итак, приступил к покупке Composer'a :)</b><br />
<span style="background-color: white;">Доку как обычно читать буду потом, сразу ставлю:</span><br />
<br />
1. В гитовом баш окне перехожу в Z:\usr\local\php5 (эта папка должна быть доступна по путям)<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj8tXNClcN9jQuS6jq2CydwTQ8cGhIv-5baQNhq0Fsmw5hjdPwnqYTjz52GSy7naom_CpzOJiAZqIa18HOHr9ExgeU-oS1sQUMcpc6qFyYDzbYhRRxoFlsdHp1_XPzNrnpm5bpsXQ/s1600/path.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj8tXNClcN9jQuS6jq2CydwTQ8cGhIv-5baQNhq0Fsmw5hjdPwnqYTjz52GSy7naom_CpzOJiAZqIa18HOHr9ExgeU-oS1sQUMcpc6qFyYDzbYhRRxoFlsdHp1_XPzNrnpm5bpsXQ/s1600/path.PNG" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<br />
2. <span style="background-color: white;">Запускаю такую следующую команду. Как поставить curl можно нагуглить если что.</span><br />
<span style="background-color: white; color: #222222; font-family: monospace, monospace; line-height: 20px; white-space: pre-wrap;">curl -s http://getcomposer.org/installer | php</span><br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhO5lxptxBhKAGtALLKYNL8vMjlnhqKyBL16KsvpAav95W20Gqbi18-o_Snvwj1OYHtSQ5NiFZrlPZDwBmYWQeNBz9e9NdTBLQzrvy3tJ3dBhMiLP2qnqEqTy_1TmnYhyOMRrHAmA/s1600/curl.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhO5lxptxBhKAGtALLKYNL8vMjlnhqKyBL16KsvpAav95W20Gqbi18-o_Snvwj1OYHtSQ5NiFZrlPZDwBmYWQeNBz9e9NdTBLQzrvy3tJ3dBhMiLP2qnqEqTy_1TmnYhyOMRrHAmA/s1600/curl.PNG" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
3. Для того, чтобы композер запускался из командной строки windows создаю батник composer.bat в z:\usr\local\bin с таким содержимым<br />
<span style="background-color: white;"><span style="background-color: white; line-height: 20px; white-space: pre-wrap;"><span style="color: #222222; font-family: monospace, monospace;">php z:\usr\local\php5\composer.phar %*</span></span></span><br />
<span style="background-color: white; color: #222222; font-family: monospace, monospace; font-size: 16px; line-height: 20px; white-space: pre-wrap;"><br /></span><br />
4. composer.phar уже есть исполняемый файл для баша. Если в баш окне попробовать выполнить <span style="background-color: white; color: #222222; font-family: monospace, monospace; font-size: 16px; line-height: 20px; white-space: pre-wrap;">composer.phar help</span><span style="background-color: white;">, то должно выдаться окно с хелпом</span><br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgerBRWzaNhoshJ2r1a1c-kNpXlLlo_ZiHTEcYhqA7irckNJLtF4JfAqNdRigqAPlBkgUzmQXus12SpvQdjWP0Xfj9-wytzehYI9TjRBr418JpPEGCkWoBLJdcuL0fYiXjyn6gcaQ/s1600/help.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgerBRWzaNhoshJ2r1a1c-kNpXlLlo_ZiHTEcYhqA7irckNJLtF4JfAqNdRigqAPlBkgUzmQXus12SpvQdjWP0Xfj9-wytzehYI9TjRBr418JpPEGCkWoBLJdcuL0fYiXjyn6gcaQ/s1600/help.PNG" /></a></div>
<span style="background-color: white;"><br /></span><br />
5. Такое же самое окошко с хелпом должно вывестись, если запускать из командной строки windows команду <span style="background-color: white; color: #222222; font-family: monospace, monospace; font-size: 16px; line-height: 20px; white-space: pre-wrap;"> composer help</span><br />
<br />
<b>Ну поставил, и че теперь?</b><br />
Теперь я хочу добавить фреймворк <a href="http://symfony.com/" target="_blank">Symfony2</a> в свой проект. Сам этот фреймворк лежит на
<a href="http://packagist.org/">http://packagist.org/</a>. Его зависимости разбросаны по всей сети.<br />
<br />
1. Создаю новый проект в папке w:\home. Использую свою любимую ИДЕ для этого.<br />
<br />
2. В корне проекта создаю файлик composer.json с вот таким содержимым:<br />
<script class="brush: javascript" type="syntaxhighlighter">
<![CDATA[
{
"require":{
"php":">=5.3.0",
"symfony/symfony":"=2.0.16"
}
}]]>
</script><br />
То есть я указал, что хочу версию php не ниже 5.3.0 и версию symfony 2.0.16.<br />
<br />
3. Инсталлирую зависимости в свой проект. В гитовом окне запускаю<br />
<span style="background-color: white; color: #222222; font-family: monospace, monospace; line-height: 20px; white-space: pre-wrap;">composer.phar install</span><br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiwam73hkOoGAXy93zhkNQLVEhXM_JzjcQy_kqD8aMXuK9fjlN3l_5LIj5V1h1bCd8CktV1_EeQWxxL3VSUxtqsqmk-hmtPbsXH7i8FlzRsGWuwXYPb5N0i43_ylqrp0XPdLxByAQ/s1600/install-calculator.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiwam73hkOoGAXy93zhkNQLVEhXM_JzjcQy_kqD8aMXuK9fjlN3l_5LIj5V1h1bCd8CktV1_EeQWxxL3VSUxtqsqmk-hmtPbsXH7i8FlzRsGWuwXYPb5N0i43_ylqrp0XPdLxByAQ/s1600/install-calculator.PNG" /></a></div>
В моем проекте появилась папка vendor. В нее композер аккуратненько сложил все необходимые зависимости и что самое интересное - сгенерировал скрипт autoload.php.<br />
<br />
4. Как использовать зависимости?<br />
В папочке с пхп скриптами (у меня это w:\home\calculator\www) создаю файлик index.php с таким содержимым:<br />
<script class="brush: php" type="syntaxhighlighter">
<![CDATA[
<?php
require_once __DIR__ . '/../vendor/autoload.php';
use Symfony\Component\HttpFoundation\Request;
$request = new Request();
print $request;
]]>
</script>
Request - это абстракция реквеста в Symfony. Что-то подобное классу HttpServletReqest из мира джавы.<br />
<br />
5. Открываю браузер на страничке <a href="http://www.calculator/index.php">http://www.calculator/index.php</a><br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiOi2KgBU7zSqRvv6m6gmxQ6egTIwTdepRqlVbf5j9eLRnjSHjt4YhtCrmZvkt6bSImJ0rNpoBNh4GwFKEQGtjTIVV_wFC4-RwynSqLMu1Rx49xXs3lobevCt2lO7PHo3El8pDhIQ/s1600/get.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiOi2KgBU7zSqRvv6m6gmxQ6egTIwTdepRqlVbf5j9eLRnjSHjt4YhtCrmZvkt6bSImJ0rNpoBNh4GwFKEQGtjTIVV_wFC4-RwynSqLMu1Rx49xXs3lobevCt2lO7PHo3El8pDhIQ/s1600/get.PNG" /></a></div>
<div class="separator" style="clear: both; text-align: left;">
Работает. Могу дальше колбасить код :)</div>
<br />
Composer позволяет не только устанавливать зависимые модули, но и создавать свои проекты и выкладывать их куда душа захочет (об этом подробнее позже). С помощью него можно так же добавить зависимость на любой проект, который изначально не использовал Composer.<br />
Вот еще классная статья на хабре <a href="http://habrahabr.ru/post/145946/">http://habrahabr.ru/post/145946/</a>Сергей Зеленинhttp://www.blogger.com/profile/01667820453041864348noreply@blogger.com2