TDD и проблема байесовского фильтра спама - PullRequest
4 голосов
/ 20 апреля 2009

Хорошо известно, что байесовские классификаторы являются эффективным способом фильтрации спама. Они могут быть довольно краткими (наш только несколько сотен LoC), но весь основной код должен быть написан заранее, прежде чем вы получите какие-либо результаты.

Однако подход TDD требует, чтобы можно было написать только минимальный объем кода для прохождения теста, поэтому с учетом следующей сигнатуры метода:

bool IsSpam(string text)

И следующая строка текста, которая явно является спамом:

"Cheap generic viagra"

Минимальное количество кода, которое я мог бы написать:

bool IsSpam(string text)
{
    return text == "Cheap generic viagra"
}

Теперь, возможно, я добавлю еще одно тестовое сообщение, например,

"Online viagra pharmacy"

Я мог бы изменить код на:

bool IsSpam(string text)
{
    return text.Contains("viagra");
}

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

Так как же TDD должен работать с ситуацией такого типа, когда эволюция кода из самого простого кода для прохождения теста не является правильным подходом? (В частности, если заранее известно, что лучшие реализации не могут быть тривиально разработаны).

Ответы [ 9 ]

4 голосов
/ 20 апреля 2009

Начните с написания тестов для нижестоящих частей алгоритма спам-фильтра.

Прежде всего вам необходимо иметь в виду примерный план того, каким должен быть алгоритм. Затем вы изолируете основную часть алгоритма и пишете тесты для него. В случае спам-фильтра, который, возможно, будет вычислять некоторую простую вероятность с использованием теоремы Байеса (я не знаю о байесовских классификаторах, поэтому я могу ошибаться). Вы строите его снизу вверх, шаг за шагом, пока, наконец, у вас не будут реализованы все части алгоритма, и собрать их вместе просто.

Требуется много практики, чтобы знать, какие тесты писать в каком порядке, чтобы вы могли выполнять TDD достаточно маленькими шагами. Если вам нужно написать более 10 строк кода, чтобы пройти один новый тест, вы, вероятно, делаете что-то не так. Начните с чего-то меньшего или смейтесь над некоторыми из зависимостей. На меньшей стороне безопаснее ошибиться, поэтому шаги слишком малы, а ваш прогресс медленный, чем пытаться делать слишком большие шаги и терпеть неудачи.

Пример "Дешевой универсальной виагры", который у вас есть, лучше подходит для приемочного теста . Возможно, он даже будет работать очень медленно, потому что сначала вам нужно инициализировать спам-фильтр с примерами данных, чтобы он не был полезен в качестве теста TDD. Тесты TDD должны быть FIRST (F = Fast, как во многих сотнях или тысячах тестов в секунду).

2 голосов
/ 20 апреля 2009

Вот мое мнение: разработка через тестирование означает написание тестов перед написанием кода. Это не означает, что каждая единица кода, для которой вы пишете тест, должна быть тривиальной.

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

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

Было бы интересно узнать, согласны ли вы и другие.

0 голосов
/ 02 марта 2010

Я не думаю, что проверка того, является ли конкретная строка спамом, на самом деле является модульным тестом, это скорее тест клиента. Есть важное отличие, потому что это не красный / жадный тип вещей. В действительности у вас должно быть пара сотен тестовых документов. Первоначально некоторые из них будут классифицироваться как спам, и по мере улучшения продукта классификации будут более точно соответствовать тому, что вы хотите. Таким образом, вы должны создать специальное приложение для загрузки нескольких тестовых документов, их классификации, а затем оценить общую оценку. Когда вы закончите с этим клиентским тестом, оценка будет очень плохой, так как вы не реализовали алгоритм. Но теперь у вас есть средства для измерения прогресса в будущем, и это довольно ценно, учитывая количество обучения / изменений / экспериментов, которое вы можете ожидать в будущем.

Когда вы реализуете свой алгоритм (и даже тестирование клиента на собственном опыте), вы все равно можете проводить TDD с реальными модульными тестами. Первый тест для компонента байесовского фильтра не будет измерять, оценивает ли конкретная строка как спам, но будет ли строка проходить через компонент байесовского фильтра соответствующим образом. Ваши следующие тесты будут сосредоточены на том, как реализован байесовский фильтр (правильное структурирование узлов, применение обучающих данных и т. Д.).

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

0 голосов
/ 02 марта 2010

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

Цикл: Красный -> Зеленый -> Рефакторинг

Вы не просто подпрыгиваете между красным и зеленым. Как только вы пройдете тестирование (зеленый), вы произведете рефакторинг рабочего кода и тестов. Затем вы пишете следующий провальный тест (красный).

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

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

0 голосов
/ 23 апреля 2009

Проблема здесь не в тестовой разработке, а в ваших тестах. Если вы начинаете разработку кода для одного теста, то все, что делает ваш тест, это задает функцию проверки строки.

Основная идея TDD - подумать о ваших тестах, прежде чем писать код. Вы не можете исчерпывающе протестировать спам-фильтр, но вы можете найти разумное приближение по десяткам или сотням тысяч тестовых документов. При наличии такого количества тестов наивный байесовский алгоритм является более простым решением, чем оператор переключения строк на сто тысяч.

В действительности, вы не сможете пройти 100% ваших юнит-тестов, поэтому вам просто нужно пройти как можно больше. Вы также должны убедиться, что ваши тесты достаточно реалистичны. Если вы думаете об этом таким образом, у разработки, ориентированной на тестирование, и машинного обучения много общего.

0 голосов
/ 20 апреля 2009

Мне кажется, что с байесовским фильтром спама вы должны использовать существующие методы. В частности, вы будете использовать теорему Байеса и, возможно, какую-то другую теорию вероятностей.

В таком случае, кажется, что лучший подход - это выбрать свой алгоритм, основанный на этих методах, который следует либо опробовать и протестировать, либо, возможно, экспериментальный. Затем ваши модульные тесты должны быть разработаны для проверки правильности реализации алгоритмом, который вы выбрали, в ispam, а также для базового теста с результатом от 0 до 1.

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

Это не значит, что производительность функции isspam не важна. Но это не обязательно должно быть частью модульного тестирования. Данные могут быть получены из результатов альфа-тестирования, новых теоретических результатов или ваших собственных экспериментов. В этом случае может потребоваться новый алгоритм и новые модульные тесты.

См. Также этот вопрос о тестировании генераторов случайных чисел.

0 голосов
/ 20 апреля 2009

(1) Нельзя сказать, что строка «является спамом» или «не является спамом» так же, как если бы вы говорили, является ли число простым числом. Это не черный или белый.

(2) Неправильно и, конечно, не цель TDD, писать функции обработки строк, используя только те самые примеры, которые использовались для тестов. Примеры должны представлять собой вид значений. TDD не защищает от глупых реализаций, поэтому вы не должны притворяться, что у вас нет никакой подсказки, поэтому вы не должны писать return text == "Cheap generic viagra".

0 голосов
/ 20 апреля 2009

У вас есть свои юнит-тесты, верно?

Это означает, что теперь вы можете реорганизовать код или даже переписать его и использовать модульные тесты, чтобы увидеть, не сломали ли вы что-либо.

Сначала заставьте это работать, затем сделайте это чистым - Пора для второго шага:)

0 голосов
/ 20 апреля 2009

Для меня то, что вы называете минимальное количество кода для прохождения теста - это целая функция IsSpam(). Это согласуется с его размером (вы говорите, только несколько сотен кубических метров).

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

В любом случае, рефакторинг заключается не просто в добавлении нового материала поверх старого. Для меня это более деструктивный подход, когда вы выбрасываете старый код для простой функции и заменяете ее новым кодом для усовершенствованной и более сложной функции.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...