Полигон алгоритмов/Пошаговая реализация собственного алгоритма

Материал из MachineLearning.

(Различия между версиями)
Перейти к: навигация, поиск
(5. Напишите логику тестирования алгоритма на задании)
(6. Напишите код самого алгоритма)
Строка 318: Строка 318:
=6. Напишите код самого алгоритма=
=6. Напишите код самого алгоритма=
 +
Создайте класс, отвечающий за работу алгоритма. В нашем примере он называется ''Algorithm''. Далее будем описывать данный класс.
 +
 +
a. Если у алгоритма есть '''параметры''', то опишите их.
 +
 +
Для одних параметров значения определяются в задании (те параметры, которые являются внешними и описываются на сайте системы при регистрации алгоритма), для других значения определяются на стадии обучения алгоритма.
 +
 +
Задайте все параметры внутренними полями класса:
 +
 +
:<code>private int _seed;</code>
 +
b. Добавьте возможность '''обучения алгоритма''', т.е. функцию, которая по обучающей выборке и параметрам из задания будет определять внутренние настройки (параметры) алгоритма. Соответственно, атрибутами обучения будут данные задачи, индексы объектов обучения, значения параметров, определенные в задании. В нашем примере стадию обучения алгоритм будет проходить в конструкторе класса:
 +
<code>
 +
:public Algorithm(ProblemData data, int[] indexes, string[] paramNames, string[] paramValues, bool[] paramUsages)
 +
:{
 +
::/* Разбор переданных параметров */
 +
 +
 +
::// имитация обучения
 +
::_seed = 0;
 +
::foreach (int i in indexes)
 +
::_seed += i;
 +
:}
 +
</code>
 +
c. Добавьте возможность '''рассчитать классификацию для определенной выборки''', т.е. по данным задачи и индексам объектов в выборке вернуть ''TestResult''.
 +
 +
Определите данную функцию:
 +
<code>
 +
:public TestResult GetTestResult(ProblemData data, int[] indexes)
 +
</code>
 +
В теле функции:
 +
 +
:1. Определите число объектов на классификацию по длине массива индексов объектов:
 +
::<code>int objectsCount = indexes.Length;</code>
 +
:2. Определите число признаков задачи по описанию признаков PropertiesDescription в данных задачи
 +
::<code>int propertyCount = data.PropertiesDescription.Length - 1;</code>
 +
:3. Определите число классов задачи (описание классов, как целевого признака, записано в последней строке ''PropertiesDescription'' данных задачи)
 +
::<code>int classCount = data.PropertiesDescription[propertyCount].Values.Length;</code>
 +
:4. Создайте структуру для возвращения результатов теста. В ней обязательно должна быть определена матрицу оценок ''ProbabilityMatrix''. В данную матрицу на этапе работы алгоритма следует проставить оценки отнесения ''i''-го объекта классификации (первый индекс) к ''j''-му классу (второй индекс). Создание вектора ответов – необязательно, но если он присутствует, то должен быть согласован с матрицей оценок (т.е. ответ алгоритма на объекте должен соответствовать максимальной оценке в матрице оценок на данном объекте). Веса объектов и признаков должны присутствовать только для тестов на обучении, поэтому их следует создавать и сохранять на этапе обучения алгоритма.
 +
<code>
 +
::var testResult = new TestResult
 +
::{
 +
:::Answers = new int[objectsCount],
 +
:::ObjectsWeights = null,
 +
:::ProbabilityMatrix = new double[objectsCount][],
 +
:::PropertiesWeights = null
 +
::};
 +
</code>
 +
:5. Напишите логику работы алгоритма (заполнение матрицы оценок и вектора ответов, если нужно)
 +
<code>
 +
::try
 +
::{
 +
:::/*Работа алгоритма*/
 +
::}
 +
::catch (Exception exp)
 +
::{
 +
:::testResult.Error = true;
 +
:::testResult.ErrorException = exp.Message;
 +
::}
 +
:</code>
 +
:Возвращаем результат:
 +
::<code>return testResult;</code>;
 +
 +
 +
:Пример полного кода функции расчета результатов классификации на выборке:
 +
<code>
 +
::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. Протестируйте взаимодействие алгоритма с системой, работу алгоритма=
=7. Протестируйте взаимодействие алгоритма с системой, работу алгоритма=

Версия 22:22, 2 апреля 2010

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

Далее описывается написание программы алгоритма на C# в Microsoft Visual Studio 2008. Соответствующую реализованную программу можно скачать по адресу http://poligon.machinelearning.ru/files/ExampleAlg.rar

Скачать данную инструкцию в формате pdf можно здесь

Краткий список действий
  1. Создайте новый проект
  2. Добавьте в проект Web Reference на обрабатывающий сервис системы
  3. Добавьте Web Reference на тестовый сервис системы
  4. Напишите логику работы с сервисом
  5. Напишите логику тестирования алгоритма на задании
  6. Напишите код самого алгоритма
  7. Протестируйте взаимодействие алгоритма с системой, работу алгоритма
  8. Зарегистрируйте алгоритм на сайте
  9. Проверьте работоспособность алгоритма на сайте: создание отчета, запуск программы алгоритма, просмотр отчета












1. Создайте новый проект

Создайте новый проект типа ConsoleApplication. Дайте ему название. В нашем примере используется название TestServiceAlgorithm.

2. Добавьте в проект Web Reference на обрабатывающий сервис системы

a. Правой кнопкой нажмите на название проекта в Solution Explorer, выберете пункт Add Service Reference

b. В появившемся окне в левом нижнем углу нажмите на кнопку “Advanced…”

Изображение:AddWebReferenceStep1.png

c. В появившемся окне в левом нижнем углу нажмите на кнопку “Add Web Reference…”

Изображение:AddWebReferenceStep2.png

d. Введите URL-адрес веб-сервиса: http://poligon.machinelearning.ru/ProcessingService.asmx. Нажмите на кнопку “Go”

Изображение:AddWebReferenceStep3.png

e. Назовите новый Web Reference подходящим образом

Изображение:AddWebReferenceStep4.png

f.Нажмите на кнопку "Add Reference"

Изображение:AddWebReferenceStep5.png

В нашем проекте появляется новый 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. Рассмотрим схему получения и обработки одного задания алгоритмом со вставками кода:

  1. запрос задания для алгоритма
    ProcessingTask procTask = procService.GetTask(algSynonim, algPassword);
  2. если задание получено и не было ошибки, то запрос данных задачи
    ProblemData data = procService.GetProblem(algSynonim, algPassword, procTask.ProblemSynonim);
  3. тестирование алгоритма на полученном задании (подробнее в пункте Логика тестирования алгоритма на задании)
    В результате тестирования возвращаются наборы результатов теста на обучении и на контроле:
    //Определение числа тестов
    int learnTaskCount = procTask.LearnIndexes.Length;
    //создание наборов результатов тестов
    var learnResults = new List<TestResult>(learnTaskCount);
    var testResults = new List<TestResult>(learnTaskCount);
  4. регистрация результатов
    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)

Личные инструменты