![No Fraud](http://habrastorage.org/getpro/habr/post_images/da6/5ff/b1a/da65ffb1a44ad8e8752fe92a426b9f42.jpg)
В заключительной четвертой части статьи подробно обсудим наиболее сложную с технической точки зрения часть antifraud-сервиса – аналитическую систему распознания мошеннических платежей по банковским картам.
Выявление различного рода мошенничеств является типичным кейсом для задач обучения с учителем (supervised learning), поэтому аналитическая часть антифрод-сервиса, в соответствии с лучшими отраслевыми практиками, будет построена с использованием алгоритмов машинного обучения.
Для стоящей перед нами задачи воспользуемся Azure Machine Learning – облачным сервисом выполнения задач прогнозной аналитики (predictive analytics). Для понимания статьи будут необходимы базовые знания в области машинного обучения и знакомство с сервисом Azure Machine Learning.
Что уже было сделано? (для тех, кто не читал предыдущие 3 части, но интересуется)
В первой части статьи мы обсудили, почему вопрос мошеннических платежей (fraud) стоит так остро для всех участников рынка электронных платежей – от интернет-магазинов до банков – и в чем основные сложности, из-за которых стоимость разработки таких систем подчас является слишком высокой для многих участников ecommerce-рынка.
Во 2-ой части были описаны требования технического и нетехнического характера, которые предъявляются к таким системам, и то, как я собираюсь снизить стоимость разработки и владения antifraud-системы на порядок(и).
В 3-ей части была рассмотрена программная архитектура сервиса, его модульная структура и ключевые детали реализации.
В заключительной четвертой части у нас следующая цель…
Во 2-ой части были описаны требования технического и нетехнического характера, которые предъявляются к таким системам, и то, как я собираюсь снизить стоимость разработки и владения antifraud-системы на порядок(и).
В 3-ей части была рассмотрена программная архитектура сервиса, его модульная структура и ключевые детали реализации.
В заключительной четвертой части у нас следующая цель…
Цель
В этой части я опишу проект, на первом шаге которого мы обучим четыре модели, используя логистическую регрессию, персептрон, метод опорных векторов и дерево решений. Из обученных моделей выберем ту, которая дает большую точность на тестовой выборке и опубликуем ее в виде REST/JSON-сервиса. Далее для полученного сервиса напишем программного клиента и проведем нагрузочное тестирование на REST-сервис.
Создание модели
Создадим новый эксперимент в Azure ML Studio. В конечном виде он будет выглядеть так, как показано на иллюстрации ниже. Соотнесем каждый элемент эксперимента с этапом последовательности, который среднестатистический data scientist проделывает в процессе обучения модели.
![Azure ML experiment](http://habrastorage.org/getpro/habr/post_images/92b/ae8/080/92bae8080fd3b45f9c305204adf9ea92.png)
Рассмотрим каждый из этапов создания модели распознания мошеннических платежей, принимая во внимания технические детали, описанные в прошлой 3-ей части статьи.
Гипотеза
Основные концепции и предположения, полезные для создания модели, были обсуждены в первых 2-ух частях статьи. Повторяться не буду, лишь отмечу, что создание хорошей гипотезы – это итеративный процесс проб и ошибок, фундаментом для которого являются знания как в исследуемой предметной области, так и в области Data Science.
Получение данных
Набором данных для модели распознания мошеннических платежей будет являться лог транзакций, который состоит из 2-ух таблиц в NoSQL-хранилище (Azure Table): таблицы фактов о транзакциях TransactionsInfo и таблицы с предварительно рассчитанными статистическими метриками TransactionsStatistics.
На этапе получения данных загрузим эти 2 таблицы через элемент управления Reader.
Подготовка и исследование данных
Сделаем Inner Join загруженных таблиц по полю TransactionId. C помощь элемента управления Metadata Editor укажем типы данных (string, integer, timestamp), отметим столбец с ответами (label) и столбцы с предикторами (features), а также тип шкалы у этих данных: номинальные, абсолютные.
Не стоит недооценивать важность подготовки для создания адекватной модели: приведу простой пример с валютой платежа, которая храниться в виде ISO-кодов (целочисленное значение). ISO-коды – имеет номинальную (классификационную) шкалу. Но вряд ли стоит надеяться, что система автоматически определит, что в столбце «Currency» хранится не целочисленное значение с абсолютной шкалой (т.е. возможны такие операции как + или >). Потому что это слишком неочевидное правило, знаниями о котором система не обладает.
Набор данных может содержать пропущенные значения. В нашем случае, страну или IP-адрес плательщика не всегда есть возможность определить, такие поля могут содержать пустые значения. Проверив имеющийся набор данных, заменим пустые значения стран на «undefined» с помощью элемента управления Clean Missing Data. С помощью этого же элемента управления удалим строки, где в поле держатель карты, сумма платежа или валюта не содержатся значения, как строки, содержащие заведомо некорректные данные, то есть вносящие шум в модель.
На следующем этапе избавимся от неиспользуемых в модели полей: адрес (нас интересует только совпала ли страна плательщика со страной, откуда пришел запрос), хэш имени держателя карта (т.к. не имеет никакого влияния на результат платежа), RowId и PartitionId (служебные данные, попавшие к нам из Azure Table).
В заключении с помощью элемента управления Normalize Data проведем ZScore-нормализацию данных, содержащих большие числовые значения, такие как сумма платежа (столбец TransactionAmount).
Деление данных
Поделим получившийся набор данных на обучающую и тестовую выборку. Выберем оптимальное соотношение данных в обучающей выборки и в тестовой. Для наших целей с помощью элемента управления Split «отправим» 70% всех имеющихся данных в обучающую выборку, дополнительно включив произвольное смешивание данных (флаг Randomized split) при делении на поднаборы данных. Смешивание данных при делении позволит избежать «перекосов» в обучающей выборке, связанных с большими утечками номеров пластиковых карт (и, как следствие, аномальной активностью фрод-роботов в этой период).
Построение и оценка модели
Инициализируем несколько алгоритмов классификации и сравним какой из них дает лучший результат (точность) на тестовой выборке. Важно отметить, что совсем не факт, что на реальных данных будет достигнута та же производительность, что и тестовых данных. Поэтому очень важно понять, что в модели было не учтено, почему один из алгоритмов дает существенно худший или лучший результат, исправить ошибки и запустить алгоритм обучения заново. Этот процесс, заканчивается тогда, когда исследователь получает приемлемую по точности модель.
Azure ML позволяет нам подключать в одном эксперименте неограниченное количество алгоритмов машинного обучения. Это дает возможность на этапе исследования сравнить производительность нескольких алгоритмов с целью выявления того, который из них наилучшим образом подходит для нашей задачи. В нашем эксперименте мы используем несколько алгоритмов двуклассовой классификации: Two-Class Logistic Regression (логистическая регрессия), Two-Class Boosted Decision Tree (дерево решений, построенное методом градиентного роста), Two-Class Support Vector Machine (метод опорных векторов), Two-Class Neural Network (нейросеть).
Еще одна возможность получить лучшую производительность модели – настроить алгоритм машинного обучения, используя большое число параметров доступных для настройки алгоритма. Так для алгоритма Two-Class Boosted Decision Tree было указано количество деревьев, которое необходимо построить, а также минимальное/максимальное количество листьев на каждом дереве; для алгоритма Two-Class Neural Network количество скрытых узлов, итераций обучения и начальные веса.
На заключительном этапе просмотрим выходные данные элемента управления Evaluate Model (команда Visualize из контекстного меню элемента) для каждого из алгоритмов.
![antifraud evaluate model](http://habrastorage.org/getpro/habr/post_images/e46/434/cdf/e46434cdf004ef4e7aac884bab1567f2.png)
Элемент управления Evaluate Model содержит матрицу неточностей (confusion matrix), рассчитанные показатели точности работы алгоритма Accuracy, Precision, Recall, F1 Score, AUC, графики ROC и Precision/Recall. Если говорить упрощенно, то мы выберем алгоритм, чьи значения Accuracy, Precision, AUC ближе к 1, график ROC сильнее вогнут в сторону оси Y как для обучающей, так и для тестовой выборки.
Кроме того, непроходимо посмотреть на изменение AUC в зависимости от устанавливаемого значения Threshold. В случае с фродом – это важно, так как стоимость нераспознанных мошеннической платежей (False Positive) намного выше, чем стоимость платежей, ошибочно принятых за фрод (False Negative).
В таких случаях необходимо выбирать значение Threshold отличное от значения по умолчанию 0,5.
При выборе наиболее подходящего алгоритма для получения оптимальной модели распознания фрода, кроме уровня Threshold, учтем и тот факт, что логику принятия решения для некоторых алгоритмов (например, дерево решений) возможно воспроизвести, а для некоторых нет (персептрон). Наличие такой возможности может быть критичным, если важно знать, почему по определенному прецеденту система приняла конкретное решение.
Лучшую точность показал алгоритм двуклассовой нейронной сети – Two-Class Neural Network (показатели точности показаны на иллюстрации выше), за ним алгоритм на основе деревьев решений — Two-Class Boosted Decision Tree.
Публикация модели как веб-сервиса
После того, как была получена модель, работающая с требуемой точностью, опубликуем наш эксперимент как веб-сервис. Операция публикации проходит по нажатию кнопки «Publish Web Service» в Azure ML Studio. Процесс создания веб-сервиса из эксперимента тривиален и его описание я пропущу.
В результате Azure ML развернет масштабируемый отказоустойчивый (SLA 99,95%) web-сервис. После публикации сервиса станет доступна страница документации по API сервиса – API help, которая кроме общего описания сервиса, описания форматов ожидаемых входных и выходных сообщений, содержит еще и примеры вызова сервиса на C#, Python и R.
Принцип вызова сервиса программным клиентом можно изобразить так.
![Azure ML services.png](http://habrastorage.org/getpro/habr/post_images/39c/4b5/47a/39c4b547ad51baae518d23e99c749c54.png)
Подключение к Azure ML web-сервису
Возьмем пример на C# из API help и, немного изменив его, вызовем web-сервис Azure ML.
Листинг 1. Вызов web-сервиса Azure ML
private async Task<RequestStatistics> InvokePredictorService(TransactionInfo transactionInfo, TransactionStatistics transactionStatistics)
{
Contract.Requires<ArgumentNullException>(transactionInfo != null);
Contract.Requires<ArgumentNullException>(transactionStatistics != null);
var statistics = new RequestStatistics();
var watch = new Stopwatch();
using (var client = new HttpClient())
{
var scoreRequest = new
{
Inputs = new Dictionary<string, StringTable>() {
{
"transactionInfo",
new StringTable()
{
ColumnNames = new []
{
#region Column name list
},
Values = new [,]
{
{
#region Column value list
}
}
}
},
},
GlobalParameters = new Dictionary<string, string>()
};
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", ConfigurationManager.AppSettings["FraudPredictorML:ServiceApiKey"]);
client.BaseAddress = new Uri("https://ussouthcentral.services.azureml.net/workspaces/<workspace_id>/services/<service_id>/execute?api-version=2.0&details=true");
watch.Start();
HttpResponseMessage response = await client.PostAsJsonAsync("", scoreRequest);
if (response.IsSuccessStatusCode)
await response.Content.ReadAsStringAsync();
statistics.TimeToResponse = watch.Elapsed;
statistics.ResponseStatusCode = response.StatusCode;
watch.Stop();
}
return statistics;
}
Получим следующие запрос/ответ:
Листинг 2.1. Запрос к web-сервису Azure ML
POST https://ussouthcentral.services.azureml.net/workspaces/<workspace_id>/services/<service_id>/execute?api-version=2.0&details=true HTTP/1.1
Authorization: Bearer <api key>
Content-Type: application/json; charset=utf-8
Host: ussouthcentral.services.azureml.net
/* другие заголовки */
{
"Inputs": {
"transactionInfo": {
"ColumnNames": [
"PartitionKey",
"RowKey",
"Timestamp",
"CardId",
"CrmAccountId",
"MCC",
"MerchantId",
"TransactionAmount",
"TransactionCreatedTime",
"TransactionCurrency",
"TransactionId",
"TransactionResult",
"CardExpirationDate",
"CardholderName",
"CrmAccountFullName",
"TransactionRequestHost",
"PartitionKey (2)",
"RowKey (2)",
"Timestamp (2)",
"CardsCountFromThisCrmAccount1D",
"CardsCountFromThisCrmAccount1H",
"CardsCountFromThisCrmAccount1M",
"CardsCountFromThisCrmAccount1S",
"CardsCountFromThisHost1D",
"CrmAccountsCountFromThisCard1D",
"FailedPaymentsCountByThisCard1D",
"SecondsPassedFromPreviousPaymentByThisCard1D",
"PaymentsCountByThisCard1D",
"HostsCountFromThisCard1D",
"HasHumanEmail",
"HasHumanPhone",
"IsCardholderNameIsTheSameAsCrmAccountName",
"IsRequestCountryIsTheSameAsCrmAccountCountry",
"TransactionDayOfWeek",
"TransactionLocalTimeOfDay"
/* значения прочие предикторы */
],
"Values": [
[
"990",
"f31f64f367644b1cb173a48a34817fbc",
"2015-03-15T20:54:28.6508575Z",
"349567471",
"10145",
"32",
"990",
"136.69",
"2015-03-15T20:54:28.6508575Z",
"840",
"f31f64f367644b1cb173a48a34817fbc",
null,
"2015-04-15T23:44:28.6508575+03:00",
"640ab2bae07bedc4c163f679a746f7ab7fb5d1fa",
"640ab2bae07bedc4c163f679a746f7ab7fb5d1fa",
"20.30.30.40",
"990",
"f31f64f367644b1cb173a48a34817fbc",
"2015-03-15T20:54:28.6508575Z",
"2",
"1",
"0",
"0",
"0",
"0",
"1",
"2",
"0",
"0",
"true",
null,
"true",
"true",
"Monday",
"Morning"
/* значения прочих предикторов */
]
]
}
},
"GlobalParameters": { }
}
Листинг 2.2. Ответ web-сервиса Azure ML
HTTP/1.1 200 OK
Content-Length: 1619
Content-Type: application/json; charset=utf-8
Server: Microsoft-HTTPAPI/2.0
x-ms-request-id: f8cb48b8-6bb5-4813-a8e9-5baffaf49e15
Date: Sun, 15 Mar 2015 20:44:31 GMT
{
"Results": {
"transactionPrediction": {
"type": "table",
"value": {
"ColumnNames": [
"PartitionKey",
"RowKey",
"Timestamp",
"CardId",
"CrmAccountId",
"MCC",
"MerchantId",
"TransactionAmount",
"TransactionCreatedTime",
"TransactionCurrency",
"TransactionId",
/* значения прочие предикторы */
"Scored Labels",
"Scored Probabilities"
],
"Values": [
[
"990",
"f31f64f367644b1cb173a48a34817fbc",
"2015-03-15T20:54:28.6508575Z",
"349567471",
"10145",
"32",
"990",
"136.69",
"2015-03-15T20:54:28.6508575Z",
"840",
"f31f64f367644b1cb173a48a34817fbc",
/* значения прочих предикторов */
"Success",
"0.779961256980896"
]
]
}
}
}
}
Нагрузочное тестирование
Для целей нагрузочного тестирования воспользуемся IaaS-возможностями Azure – поднимем виртуальную машину (Instance A8: 8x CPU, 56Gb RAM, 40Gbit/s InfiniBand, Windows Server 2012 R2, $2.45/hr) в том же регионе (US Central South), в котором находиться наш Azure ML web-сервис. Запустим на VM задачу на ~20K запросов и посмотрим на результаты.
Листинг 3. Код клиента сервиса и задачи
/// <summary>
/// Entry point
/// </summary>
public void Main()
{
var client = new FraudPredictorMLClient();
RequestsStatistics invokeParallelStatistics = client.InvokeParallel(1024, 22);
LogResult(invokeParallelStatistics);
RequestsStatistics invokeAsyncStatistics = client.InvokeAsync(1024).Result;
LogResult(invokeAsyncStatistics);
}
private static void LogResult(RequestsStatistics statistics)
{
Contract.Requires<ArgumentNullException>(statistics != null);
Func<double, string> format = d => d.ToString("F3");
Log.Info("Results:");
Log.Info("Min: {0} ms", format(statistics.Min));
Log.Info("Average: {0} ms", format(statistics.Average));
Log.Info("Max: {0} ms", format(statistics.Max));
Log.Info("Count of failed requests: {0}", statistics.FailedRequestsCount);
}
/// <summary>
/// Client for FraudPredictorML web-service
/// </summary>
public class FraudPredictorMLClient
{
/// <summary>
/// Async invocation of method
/// </summary>
/// <param name="merchantId">Merchant id</param>
/// <exception cref="ArgumentOutOfRangeException"><paramref name="merchantId"/></exception>
public async Task<RequestsStatistics> InvokeAsync(int merchantId)
{
Contract.Requires<ArgumentOutOfRangeException>(merchantId > 0);
IEnumerable<TransactionInfo> tis = null; IEnumerable<TransactionStatistics> tss = null;
// upload input data
Parallel.Invoke(
() => tis = new TransactionsInfoRepository().Get(merchantId),
() => tss = new TransactionsStatisticsRepository().Get(merchantId)
);
var inputs = tis
.Join(tss, ti => ti.TransactionId, ts => ts.TransactionId, (ti, ts) => new { TransactionInfo = ti, TransactionStatistics = ts })
.ToList();
// send requests
var statistics = new List<RequestStatistics>(inputs.Count);
foreach (var input in inputs)
{
RequestStatistics stats = await InvokePredictorService(input.TransactionInfo, input.TransactionStatistics).ConfigureAwait(false);
statistics.Add(stats);
}
// return result
return new RequestsStatistics(statistics);
}
/// <summary>
/// Parallel invocation of method (for load testing purposes)
/// </summary>
/// <param name="merchantId">Merchant id</param>
/// <param name="degreeOfParallelism">Count of parallel requests</param>
/// <exception cref="ArgumentOutOfRangeException"><paramref name="merchantId"/></exception>
/// <exception cref="ArgumentOutOfRangeException"><paramref name="merchantId"/></exception>
public RequestsStatistics InvokeParallel(int merchantId, int degreeOfParallelism)
{
Contract.Requires<ArgumentOutOfRangeException>(merchantId > 0);
Contract.Requires<ArgumentOutOfRangeException>(degreeOfParallelism > 0);
IEnumerable<TransactionInfo> tis = null; IEnumerable<TransactionStatistics> tss = null;
// upload input data
Parallel.Invoke(
() => tis = new TransactionsInfoRepository().Get(merchantId),
() => tss = new TransactionsStatisticsRepository().Get(merchantId)
);
var inputs = tis
.Join(tss, ti => ti.TransactionId, ts => ts.TransactionId, (ti, ts) => new { TransactionInfo = ti, TransactionStatistics = ts })
.ToList();
// send requests
var statistics = new List<RequestStatistics>(inputs.Count);
for (int i = 0; i < inputs.Count; i = i + degreeOfParallelism)
{
var tasks = new List<Task<RequestStatistics>>();
for (int j = i; j < i + degreeOfParallelism; j++)
{
if (inputs.Count <= j) break;
var input = inputs[j];
tasks.Add(InvokePredictorService(input.TransactionInfo, input.TransactionStatistics));
}
Task.WaitAll(tasks.ToArray());
statistics.AddRange(tasks.Select(t => t.Result));
}
// return result
return new RequestsStatistics(statistics);
}
/* other members */
}
Вызов InvokeParallel():
Лучшее время ответа: 421.683 ms
Худшее время: 1355.516 ms
Среднее время: 652.935 ms
Количество успешных запросов: 20061
Количество отказов: 956
Вызов InvokeAsync():
Лучшее время ответа: 478.102 ms
Худшее время: 1344.348 ms
Среднее время: 605.911 ms
Количество успешных запросов: 21017
Количество отказов: 0
Ограничения (потенциальные)
Бутылочным горлышком разрабатываемой системы, на первый взгляд, будет являться Azure ML. Поэтому крайне важно понимать ограничения Azure ML в общем и web-сервисов Azure ML, в частности. Но по данному вопросу очень мало как официальной документации, так и результатов, полученных от community.
Так остается открытым вопрос с throttled policy конечных точек web-сервиса Azure ML: не ясно максимальное значение параллельных запросов web-сервису Azure ML (эмпирически проверена цифра 20 параллельных запросов на одну конечную точку), а также максимальный размер принимаемого сообщения (актуального для пакетного режима работы сервиса).
Менее актуально, но стоит вопрос с максимальным размером входных данных (Criteo Labs разместили датасет на 1 Тб данных), максимальным количеством предикторов и прецедентов, которые можно отдать на вход алгоритму машинного обучения в Azure ML.
Критически важно сократить время ответа веб-сервиса FraudPredictorML, а также время переобучения модели до минимальных значений, но пока нет никаких официальных рекомендаций по тому, как это возможно сделать (и возможно ли вообще).
Рекомендации клиентам
Антифрод-сервис никак не ограничивает клиентов как в предварительной проверке платежей, так и в последующей интерпретации результатов предсказания. Предварительные специфичные для бизнес-процесса проверки, а также окончательное принятие решения о принятии/отклонение платежа – это задачи, которые явно выходят из зоны ответственности антифрод-сервиса.
В независимости о роли клиента – интернет-магазин, платежная система или банк – для клиентов существуют следующие рекомендации:
- выполняйте предварительную проверку платежей, как используя технологии, принятые в отрасли (fingerprint и т.п.), так и использую собственные знания о клиенте (историю заказов и т.п.);
- интерпретируйте результат, применяя следующую практику: вероятность фрода ниже 0,35 – принимать оплату без 3D-Secure, вероятность от 0,35 до 0,85 – принимайте оплату с включенным 3DS, вероятность фрода – более отказывайте;
- выбирайте уровни, предложенные в предыдущем пункте, на основе собственной аналитики и регулярно пересматривайте их (минимизируйте упущенные выгоды и штрафы за фрод).
Рекомендации для комментирующих
В рамках этого цикла статей мы касались проблематики вопроса, юридической и технической стороны проблемы. Это техническая статья, она не преследует собой цели создать бизнес-план, сравнить с решениями конкурентов, вычислить дисконтированную стоимость проекта. Со всеми этими вопросами на РБК – не ко мне, не в это хаб, и, есть подозрение, даже не на этот сайт.
Заключение
В этом цикле, состоящем из 4-ех статей, мы провели эксперимент по проектированию и разработке высокомасштабируемого отказоустойчивого надежного antifraud-сервиса, работающего в near real-time режиме, с открытым для внешних программным клиентов REST/JSON API.
Применение алгоритмов машинного обучения (дерево решений, нейросети) позволили создать аналитическую систему, способную к самообучению как на накопленной истории, так и на новых платежах. Благодаря использованию PaaS-/IaaS-сервисов удалось сократить первоначальные финансовые затраты на инфраструктуру и ПО практически до нуля. Наличие у разработчика компетенций в предметной области, data science, архитектуре распределенных систем помогло драматически снизить количество участников команды разработки.
В результате менее чем за 60 человеко-часов и с минимальными начальными затратами на инфраструктуру (<$150, которые были покрыты из подписки MSDN) удалось создать ядро антифрод-системы.
Получившийся сервис, конечно, требует еще тщательной проверки (и последующего исправления) основных модулей, более тонкой настройки работы классификатора(ов), разработки серии вспомогательных подсистем, интереса и (что тут греха таить) инвестиций. Но и несмотря на указанные выше недоработки, сервис на порядок (и больше) эффективнее аналогичных разработок в отрасли как с точки зрения стоимости разработки, так и с точки зрения стоимости владения.
Другие части статьи
Если Вам осталось неясно в чем проблема (часть 1).
Если Вы не пропустили, почему проблему фрода
Если Вам интересно, как это выглядит с точки зрения программной архитектуры (часть 3).
Дмитрий Петухов,
архитектор, разработчик, энтузиаст, неутомимый исследователь и кофеман
kypexin
Дмитрий, вы используете модели из категории supervised learning, но из описания в 3 и 4 частях не очень понятно, откуда эти модели берут обучающие данные с уже имеющейся разметкой «фрод/не фрод»? Я правильно понимаю, что в таблице TransactionsInfo как раз собраны исторические данные о платежах, уже размеченные соответствующим образом? Если это так, то откуда именно эта разметка берётся?
codezombie Автор
Все верно: в таблице TransactionsInfo собрана история платежей + результаты по ним (получатель платежа в конце-концов всегда узнает, чем закончился проходящий через него платеж).
kypexin
Спасибо. У меня ещё много вопросов :)
codezombie Автор
Вопросов действительно много (легче еще одну статью написать), поэтому ответы будут короткие:
— описание того, как информация и рез-ты транзакции попадают в лог транзакций есть в 3-ей части;
— эксперт (я). Не обязательно, если у клиента не слишком специфический фрод;
— время — год, объем — как можно больше (big data!), дальше время/объем балансируем в зав-ти от точности модели и конечной стоимости ее владения;
— это не проблема для используемых алгоритмов машинного обучения;
— уровень точности предсказания и объем новых данных — основные триггеры для переобучения (т.е. время переобучения недетерминировано).
kypexin
Спасибо, понятно. Ещё пара вопросов: