Полигон алгоритмов/Пошаговая реализация собственного алгоритма
Материал из MachineLearning.
 (→6. Напишите код самого алгоритма)  | 
				 (→6. Напишите код самого алгоритма)  | 
			||
| Строка 377: | Строка 377: | ||
:</code>  | :</code>  | ||
:Возвращаем результат:  | :Возвращаем результат:  | ||
| - | ::<code>return testResult;</code>  | + | ::<code>return testResult;</code>  | 
| Строка 414: | Строка 414: | ||
:::return testResult;  | :::return testResult;  | ||
::}  | ::}  | ||
| + | |||
| + | </code>  | ||
=7. Протестируйте взаимодействие алгоритма с системой, работу алгоритма=  | =7. Протестируйте взаимодействие алгоритма с системой, работу алгоритма=  | ||
Версия 22:23, 2 апреля 2010
В данной инструкции описывается реализация программы, отвечающей за проведение тестирования Вашего алгоритма системой Полигон. Взаимодействие с системой происходит при помощи веб-сервиса, к которому обращается программа. Алгоритм должен периодически запрашивать новые задания на тестирование, каждое из которых содержит несколько тестов (т.е. несколько обучающих и контрольных подвыборок данных задачи). При получении задания алгоритм должен рассчитать его и сохранить полученные результаты через веб-сервис. Подробнее о взаимодействии системы Полигон с пользовательскими алгоритмами и описание функций веб-сервиса смотрите здесь.
Далее описывается написание программы алгоритма на C# в Microsoft Visual Studio 2008. Соответствующую реализованную программу можно скачать по адресу http://poligon.machinelearning.ru/files/ExampleAlg.rar
Скачать данную инструкцию в формате pdf можно здесь
- Создайте новый проект
 - Добавьте в проект Web Reference на обрабатывающий сервис системы
 - Добавьте Web Reference на тестовый сервис системы
 - Напишите логику работы с сервисом
 - Напишите логику тестирования алгоритма на задании
 - Напишите код самого алгоритма
 - Протестируйте взаимодействие алгоритма с системой, работу алгоритма
 - Зарегистрируйте алгоритм на сайте
 - Проверьте работоспособность алгоритма на сайте: создание отчета, запуск программы алгоритма, просмотр отчета
 
1. Создайте новый проект
Создайте новый проект типа ConsoleApplication. Дайте ему название. В нашем примере используется название TestServiceAlgorithm.
2. Добавьте в проект Web Reference на обрабатывающий сервис системы
a. Правой кнопкой нажмите на название проекта в Solution Explorer, выберете пункт Add Service Reference
b. В появившемся окне в левом нижнем углу нажмите на кнопку “Advanced…”
c. В появившемся окне в левом нижнем углу нажмите на кнопку “Add Web Reference…”
d. Введите URL-адрес веб-сервиса: http://poligon.machinelearning.ru/ProcessingService.asmx. Нажмите на кнопку “Go”
e. Назовите новый Web Reference подходящим образом
f.Нажмите на кнопку "Add Reference"
В нашем проекте появляется новый namespace <название проекта>.<объявленное название web reference> (в нашем случае TestServiceAlgorithm.ProcessService), содержащий все типы и функции веб-сервиса. Включите данный namespace во все используемые файлы кода (Program.cs):
using TestServiceAlgorithm.ProcessService;
Для обращения к функциям сервиса создайте экземпляр класса ProcessingService, описание которого находится в подключенном namespace:
var procService = new ProcessingService();
3. Добавьте Web Reference на тестовый сервис системы
Аналогичным образом добавьте в проект тестовый веб-сервис, который расположен по адресу http://poligon.machinelearning.ru/TestService.asmx. В данном примере этот web reference называется TestService. Тогда для использования функций тестового сервиса вместо обрабатывающего нужно подключать namespace, соответствующий тестовому сервису:
using TestServiceAlgorithm.TestService;
И для обращения к функциям тестового сервиса нужно создать экземпляр класса TestService, находящийся в данном namespace:
var procService = new TestService();
При настройке взаимодействия и отладке работы алгоритма следует обращаться именно к тестовому сервису.
Далее считаем, что переменная procService соответствует нужному нам сервису.
4. Напишите логику работы с сервисом
Данный код следует писать в функции Main файла Program.cs.
a. Поскольку при вызове любой функции сервиса требуются параметры авторизации, введите переменные, отвечающие за эти данные.
- const string algSynonim = "algSynonim";
 - const string algPassword = "algPassword";
 
b. Рассмотрим схему получения и обработки одного задания алгоритмом со вставками кода:
- 	запрос задания для алгоритма
ProcessingTask procTask = procService.GetTask(algSynonim, algPassword);
 - 	если задание получено и не было ошибки, то запрос данных задачи
ProblemData data = procService.GetProblem(algSynonim, algPassword, procTask.ProblemSynonim);
 -  тестирование алгоритма на полученном задании (подробнее в пункте Логика тестирования алгоритма на задании)
- В результате тестирования возвращаются наборы результатов теста на обучении и на контроле:
 //Определение числа тестов- int learnTaskCount = procTask.LearnIndexes.Length;
 - //создание наборов результатов тестов
 - var learnResults = new List<TestResult>(learnTaskCount);
 - var testResults = new List<TestResult>(learnTaskCount);
 
 - 	регистрация результатов
- ProcessingState state = procService.RegisterResult
 - (algSynonim, algPassword, procTask.PocketId, learnResults.ToArray(), testResults.ToArray());
 
 
Полный код простой логики запроса и обработки одного задания:
- //Запрос задания для алгоритма
 
- ProcessingTask procTask = procService.GetTask(algSynonim, algPassword);
 
- if (procTask != null)
 
- {
 
- //Проверка на ошибку при получении задания
 
- if (procTask.ProcessingState.Status == StatusType.Error)
 
- throw new Exception("Задание возвращено с ошибкой: " + procTask.ProcessingState.Message);
 
- //Если задание есть и не было ошибки, надо получить данные по задаче
 
- ProblemData data = procService.GetProblem(algSynonim, algPassword,procTask.ProblemSynonim);
 
- //Проверка на ошибку при получении данных задачи
 
- if (data.ProcessingState.Status == StatusType.Error)
 
- throw new Exception("Данные задачи возвращены с ошибкой: " + procTask.ProcessingState.Message);
 
- //Определение числа тестов
 
- int learnTaskCount = procTask.LearnIndexes.Length;
 
- //создание наборов результатов тестов
 
- var learnResults = new List<TestResult>(learnTaskCount);
 
- var testResults = new List<TestResult>(learnTaskCount);
 
- /*
 
- Код обработки задания (заполнение списков learnResults и testResults)
 
- */
 
- //Регистрация результатов
 
- ProcessingState state = procService.RegisterResult(algSynonim, algPassword, procTask.PocketId, learnResults.ToArray(), testResults.ToArray());
 
- //Проверка на ошибки при сохранении результатов:
 
- if (state.Status == StatusType.Warning)
 
- throw new Exception("При сохранении результата" + procTask.PocketId + " было выдано предупреждение: " + state.Message);
 
- if (state.Status == StatusType.Error)
 
- throw new Exception("Произошла ошибка при сохранении результата" + procTask.PocketId + ": " + state.Message);
 
- }
 
c.	После каждого вызова функций веб-сервиса стоит проверять возвращенный статус (структура ProcessingState) на произошедшие ошибки или выданные сервером предупреждения.
Например:
- //вызов функции получения задачи с сервиса
 - ProblemData data = procService.GetProblem(algSynonim, algPassword, procTask.ProblemSynonim);
 
- //Проверка на ошибку при получении данных задачи
 - if (data.ProcessingState.Status == StatusType.Error)
- throw new Exception("Данные задачи возвращены с ошибкой: " + procTask.ProcessingState.Message);
 
 
При вызове функций обрабатывающего сервиса ошибками могут являться неверный синоним или пароль алгоритма (ошибки авторизации). При регистрации результатов ошибку может вызвать неверный формат регистрируемых данных (результатов теста) (подробнее в пункте Тестирование взаимодействия алгоритма с системой).
Нужно определить действия программы в случае возникновения ошибки – сгенерировать исключение (throw new Exception), записать ошибку в файл или выдать сообщение пользователю.
d.	Поскольку для расчета статистик по одной задаче алгоритму требуется рассчитать несколько заданий, можно добавить проверку новых заданий в цикле. При этом следует ставить таймер между запросами заданий, чтобы обращения к серверу не были слишком частыми (возможен бан).
- for (int counter = 0; counter < 1000; counter++)
 - {
- /*
 - Запрос и последующая обработка одного задания (см.выше)
 - */
 - Thread.Sleep((procTask != null) ? 500 : 60000);
 
 - }
 
В данном примере программа ждет полсекунды, если задание приходило, и минуту, если задания не было. Число 1000 соответствует суммарному числу запросов заданий программой. Алгоритм может запрашивать задания в вечном цикле или конечное число раз на усмотрение автора.
e. Для оптимизации трафика можно данные задачи сохранять в алгоритме и запрашивать новые, только если в задании указана другая задача.
Для этого нужно:
Перед объявлением цикла ввести переменную, в которой будут храниться данные последней полученной задачи, и переменную с синонимом последней полученной задачи:
- ProblemData lastData = null;
 
- string lastProblemSyn = "";
 
Вместо прежнего кода запроса данных задачи вставить новый:
- if (lastProblemSyn != procTask.ProblemSynonim)
 - {
- lastData = procService.GetProblem(algSynonim, algPassword, procTask.ProblemSynonim);
 - /* проверка ошибок */
 - lastProblemSyn = procTask.ProblemSynonim;
 
 - }
 - ProblemData data = lastData;
 
5. Напишите логику тестирования алгоритма на задании
Задание содержит несколько тестов алгоритма. Каждый тест задается выборкой объектов обучения и выборкой объектов классификации. Соответственно, тестирование алгоритма на задании заключается в последовательном прохождении тестов задания. В каждом тесте алгоритм должен сначала обучиться на обучающей выборке, а потом вернуть результаты классификации на контрольной и обучающей выборках.
Количество тестов определено длиной массива LearnIndexes в задании, она должна совпадать с длиной массива TestIndexes. Определите количество тестов:
- int learnTaskCount = procTask.LearnIndexes.Length;
 
Создайте списки для сохранения результатов тестов:
- var learnResults = new List<TestResult>(learnTaskCount);
 - var testResults = new List<TestResult>(learnTaskCount);
 
Результатом теста является объект типа TestResult. Данный класс объявлен в namespace веб-сервиса.
Следующая часть кода пишется в область «Код обработки задания» перед сохранением результатов тестирования (см. пример полного кода в пункте Логика работы с сервисом).
В цикле for (var i = 0; i < learnTaskCount; i++)  напишите поэтапное прохождение тестов:
1. Обучение алгоритма на обучающей подвыборке с заданными параметрами (в данном случае создание объекта алгоритма, у которого потом будет вызываться функция расчета классификации).
var alg = new Algorithm(data, procTask.LearnIndexes[i], procTask.AlgParamNames, procTask.AlgParamValues, procTask.AlgParamUsages);
В данном случае обучение алгоритма происходит в конструкторе класса. Он требует данные задачи, индексы объектов обучения, информацию о параметрах алгоритма. Именно у созданного (обученного) экземпляра класса Algorithm будет вызываться функция расчета результатов классификации. Разбор параметров алгоритма, переданных в задании, можно обрабатывать при создании алгоритма, как в данном случае, либо сразу после получения задания и конструктору передавать отдельную структуру с параметрами.
Подробнее код класса Algorithm будет рассмотрен ниже (пункт Написание кода самого алгоритма).
2.	Расчет результатов классификации обученного алгоритма (получение TestResult) на обучающей и контрольной выборках.
Вызовите функцию расчетов результатов классификации для обученного алгоритма. Параметрами являются данные задачи и индексы объектов для классификации.
- TestResult learnTestRes = alg.GetTestResult(data, procTask.LearnIndexes[i]);
 - TestResult testTestRes = alg.GetTestResult(data, procTask.TestIndexes[i]);
 
Для отслеживания корректности записанных результатов, нужно сохранять порядковый индекс теста:
- learnTestRes.Index = i;
 - testTestRes.Index = i;
 
Сохраните результаты тестов в созданных массивах результатов:
- learnResults.Add(learnTestRes);
 - testResults.Add(testTestRes);
 
Полный пример кода обработки (проведения тестирования) одного задания:
- //Определение числа тестов
 - int learnTaskCount = procTask.LearnIndexes.Length;
 
- //создание наборов результатов тестов
 - var learnResults = new List<TestResult>(learnTaskCount);
 - var testResults = new List<TestResult>(learnTaskCount);
 - for (var i = 0; i < learnTaskCount; i++)
 - {
- // обучение алгоритма
 - var alg = new Algorithm(data, procTask.LearnIndexes[i], procTask.AlgParamNames, procTask.AlgParamValues, procTask.AlgParamUsages);
 
 
- //Получаем результат классификации на объектах обучения
 - var learnTestRes = alg.GetTestResult(data, procTask.LearnIndexes[i]);
 - learnTestRes.Index = i;
 - learnResults.Add(learnTestRes);
 
- //Получаем результат классификации на объектах контроля
 - var testTestRes = alg.GetTestResult(data, procTask.TestIndexes[i]);
 - testTestRes.Index = i;
 - testResults.Add(testTestRes);
 
- }
 
6. Напишите код самого алгоритма
Создайте класс, отвечающий за работу алгоритма. В нашем примере он называется Algorithm. Далее будем описывать данный класс.
a. Если у алгоритма есть параметры, то опишите их.
Для одних параметров значения определяются в задании (те параметры, которые являются внешними и описываются на сайте системы при регистрации алгоритма), для других значения определяются на стадии обучения алгоритма.
Задайте все параметры внутренними полями класса:
private int _seed;
b.	Добавьте возможность обучения алгоритма, т.е. функцию, которая по обучающей выборке и параметрам из задания будет определять внутренние настройки (параметры) алгоритма. Соответственно, атрибутами обучения будут данные задачи, индексы объектов обучения, значения параметров, определенные в задании. В нашем примере стадию обучения алгоритм будет проходить в конструкторе класса:
- public Algorithm(ProblemData data, int[] indexes, string[] paramNames, string[] paramValues, bool[] paramUsages)
 - {
- /* Разбор переданных параметров */
 
 
- // имитация обучения
 - _seed = 0;
 - foreach (int i in indexes)
 - _seed += i;
 
- }
 
c. Добавьте возможность рассчитать классификацию для определенной выборки, т.е. по данным задачи и индексам объектов в выборке вернуть TestResult.
Определите данную функцию:
- public TestResult GetTestResult(ProblemData data, int[] indexes)
 
В теле функции:
- 1.	Определите число объектов на классификацию по длине массива индексов объектов:
int objectsCount = indexes.Length;
 - 2.	Определите число признаков задачи по описанию признаков PropertiesDescription в данных задачи
int propertyCount = data.PropertiesDescription.Length - 1;
 - 3.	Определите число классов задачи (описание классов, как целевого признака, записано в последней строке PropertiesDescription данных задачи)
int classCount = data.PropertiesDescription[propertyCount].Values.Length;
 - 4. Создайте структуру для возвращения результатов теста. В ней обязательно должна быть определена матрицу оценок ProbabilityMatrix. В данную матрицу на этапе работы алгоритма следует проставить оценки отнесения i-го объекта классификации (первый индекс) к j-му классу (второй индекс). Создание вектора ответов – необязательно, но если он присутствует, то должен быть согласован с матрицей оценок (т.е. ответ алгоритма на объекте должен соответствовать максимальной оценке в матрице оценок на данном объекте). Веса объектов и признаков должны присутствовать только для тестов на обучении, поэтому их следует создавать и сохранять на этапе обучения алгоритма.
 
- var testResult = new TestResult
 - {
- Answers = new int[objectsCount],
 - ObjectsWeights = null,
 - ProbabilityMatrix = new double[objectsCount][],
 - PropertiesWeights = null
 
 - };
 
- 5. Напишите логику работы алгоритма (заполнение матрицы оценок и вектора ответов, если нужно)
 
- try
 - {
- /*Работа алгоритма*/
 
 - }
 - catch (Exception exp)
 - {
- testResult.Error = true;
 - testResult.ErrorException = exp.Message;
 
 - }
 
- Возвращаем результат:
return testResult;
 
- Пример полного кода функции расчета результатов классификации на выборке:
 
- public TestResult GetTestResult(ProblemData data, int[] indexes)
 - {
- //определяем число объектов на классификацию
 - int objectsCount = indexes.Length;
 
 
- //определяем число признаков
 - int propertyCount = data.PropertiesDescription.Length - 1;
 
- //определяем число классов
 - int classCount = data.PropertiesDescription[propertyCount].Values.Length;
 
- var testResult = new TestResult
 - {
- Answers = new int[objectsCount],
 - ObjectsWeights = null,
 - ProbabilityMatrix = new double[objectsCount][],
 - PropertiesWeights = null
 
 - };
 - try
 - {
- /*Работа алгоритма*/
 
 - }
 - catch (Exception exp)
 - {
- testResult.Error = true;
 - testResult.ErrorException = exp.Message;
 
 - }
 - return testResult;
 
- }
 
7. Протестируйте взаимодействие алгоритма с системой, работу алгоритма
8. Зарегистрируйте алгоритм на сайте
9. Проверьте работоспособность алгоритма на сайте: создание отчета, запуск программы алгоритма, просмотр отчета
|   |  Статья в настоящий момент дорабатывается. Sintsova 00:16, 3 апреля 2010 (MSD)  | 






