Русский
Русский
English
Статистика
Реклама

Из песочницы Создание системы антифрода в такси с нуля

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


image


Введение


Кто раз умеет обмануть, тот много раз еще обманет.
Лопе де Вега

Фрод в нашем случае это ситуация, когда водитель обманывает компанию. Мошенничество с целью получения денег.


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


Постановка задачи и паттерны поведения


Первоначальная цель создать MVP, который за минимум времени разработки даст максимальный результат.

После консультаций с директорами городов, кураторами и менеджерами были выявлены самые частые схемы мошенничества:


  • Воровство комиссии. Водитель отменяет заказ (или просит по-братски отменить поездку пассажира), потом его выполняет, забирает у клиента наличку. В компанию не приходит ни копейки;
  • Фейковые заказы с целью получения доплат. Доплаты это сумма, которую агрегатор из своего кармана добавляет к выручке водителя за поездку. Тогда ему становится выгоднее брать непопулярные заказы (короткие и дешёвые, например). Водитель делает такой заказ сам себе или другу с левой сим-карты, потом проезжает 200 метров и получает незаслуженную денежку.
  • Более мелкие паттерны, которые мы объединили в один Подозрительные водители:
    • Водители, у которых отключена комиссия (скажем, менеджер города мог поставить 0% комиссии своему другу);
    • Водители, которые покупают безлимитный тариф и по одному аккаунту работают вдвоём-втроём.

image


Функции в нашей команде распределены так:


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

Сложности


  • Важность снижения как False Positive, так и False Negative ошибок. Человеческим языком:
    • Не хочется обвинять честного водителя, так как он нас покинет (и будет прав, чёрт возьми!);
    • Не хочется оставлять нарушителя безнаказанным.
  • Необходимость ручной проверки и человеческий фактор. На этом пункте мы ещё остановимся подробнее;
  • Водители. Да, сами водители это сложность для аналитика. Любая задача, связанная с ними, намного тяжелее аналогичной задачи, связанной с пассажирами. Занимались мы как-то предсказанием оттока и тех, и других Но это уже совсем другая история.

Процесс


image


Основную задачу выполняет SQL-запрос в DWH, который возвращает подозрительные поездки и водителей. Те должны обладать набором заранее рассчитанных признаков. Вот, например, как выглядит фильтрация по паттерну Доплаты:


WHERE susp = 1 -- Флаг на подозрительность  AND finished_orders >= 3 -- Три и более УСПЕШНЕ поездки с одним водителем  AND cancelled >= 3 -- Три и более водителя, с которыми у номера телефона были только ОТМЕН  AND dist_fin_drivers <= 2 -- Успешные поездки максимум с ДВУМЯ водителями  AND ok <= 2 -- Не больше 2-х УСПЕШНХ поездок с другими водителями

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


Еще пример. В паттерне Воровство комиссии ключевую роль играют координаты водителя. Отменил заказ, но отметился на всём его протяжении? Ну-ну. Считаем расстояния, добавляем ещё несколько важных фильтров и выгружаем такие поездки.


image


Далее в работу вступает скрипт на python. Он оборачивает выгрузку в pandas, сохраняет ее в postgres, преобразует в нужный вид и выгружает на проверку в листы Google (о них мы еще поговорим подробнее). Часть скрипта, выгружающая поездки, запускается автоматически дважды в день с помощью Apache Airflow.


Рассмотрим работу с API на примере.
Считываем креды и коннектимся:


credentials = ServiceAccountCredentials.from_json_keyfile_dict(    config.crd,    ['https://www.googleapis.com/auth/spreadsheets',     'https://www.googleapis.com/auth/drive'])httpAuth = credentials.authorize(httplib2.Http())service = googleapiclient.discovery.build('sheets', 'v4', http=httpAuth)sheet = service.spreadsheets()

Добавляем данные на лист:


base_range = f'{city_name}!A{ss_row + 1}:Z{ss_row + reserved_rows}'sheet.values().append(spreadsheetId=spreadsheetid,                                 range=base_range,                                 body={"values": df_pos.values.tolist()},                                 valueInputOption='RAW').execute()

Забираем резолюции:


range_from_ss = f'{city_name}!A{ss_row}:S{ss_row + reserved_rows}'data_from_ss = service.spreadsheets().values().get(            spreadsheetId=spreadsheetid,            range=range_from_ss).execute().get('values', [])data_from_ss = pd.DataFrame(data_from_ss)data_from_ss_cols = ['id', 'Резолюция', 'Комментарий']data_from_ss = data_from_ss.loc[1:, data_from_ss_cols]

Заносим их в PG:


vls_ss = ','.join([f"""({', '.join([f(d[c]) for c in data_from_ss_cols])}                    )""" for d in data_from_ss.to_dict('rows')])sql_update = f"""    WITH updated as (        UPDATE fraud_billing        SET resolution = tb.resolution,            comment=tb.comment,            dt = NOW()        FROM (VALUES {vls_ss}) AS tb(fraud_billing_id, resolution, comment)        WHERE fraud_billing.fraud_billing_id = CAST(tb.fraud_billing_id AS INTEGER)            AND ((fraud_billing.resolution IS NULL AND tb.resolution IS NOT NULL)                OR (fraud_billing.comment IS NULL AND tb.comment IS NOT NULL)                OR (fraud_billing.comment IS NOT NULL AND tb.comment IS NOT NULL                   AND fraud_billing.comment <> tb.comment)                OR (fraud_billing.resolution IS NOT NULL AND tb.resolution IS NOT NULL                    AND fraud_billing.resolution <> tb.resolution)               )        RETURNING {alias_cols_text_with_id}        )    INSERT INTO fraud_billing_history ({cols_text_with_id})    SELECT {cols_text_with_id}    FROM updated;"""crs_postgres.execute(sql_update)con_postgres.commit()

В самой postgres для каждого паттерна реализовано две таблицы:


  • хранение записей о поездках и водителях;
  • история обновлений.

Логи скрипта:
image


Менеджеры на местах проверяют поездки на наличие ошибок первого рода (казнить нельзя, помиловать).


Пример того, как выглядит работа менеджера с листом:


image


Иногда по всем признакам сразу видно водитель фродил.


Иногда приходится копать глубже: смотреть трекинг, вызывать водителя в офис, созваниваться с пассажиром.


И так для каждого паттерна.


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


image


На картинке справа наша FP-ошибка будет равна нулю, но мы не поймаем многих мошенников.


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


После подтверждения факта фрода в игру вступает карательная машина правосудия. В зависимости от степени тяжести, рецедивов и общей ситуации в городе, водителя:


  • предупреждают;
  • штрафуют;
  • урезают в правах;
  • блокируют временно или навсегда.

На данном этапе мы лишь наблюдаем и логируем информацию.


Отмечу, что результат работы зависит от населённого пункта. Города различаются по населению, площади, уровню конкуренции, условиям для водителей, менеджменту. Для примера сравним кол-во подозрений и фрода за последние недели:


image


Как видите, подобрать универсальные правила и для Новосибирска, и для Магнитогорска нетривиальная задача.


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


  • Менеджеры городов хорошо в них разбираются, так как эксельку знают все;
  • У Гугла превосходный API удобный и лёгкий;
  • Они бесплатны;
  • Они обладают всем необходимым функционалом, в том числе контролем версий.

Самая большая проблема


Ручная проверка является важной частью системы антифрода. А там, где люди там ищи проблемы:


  • Самоуправство. Кто-то ведёт на листе расчёты, кто-то удаляет столбцы, кто-то придумывает свои виды резолюций. Мы ввели защиту на листы, но алгоритмы практически при каждом обновлении всё равно ругались на аномалии в данных. Пришлось написать дополнительные скрипты для проверки спрэдшитов перед обновлениями;
  • Необязательность. Проблема встречается не так часто, как предыдущая, но несёт намного больше вреда:
    • кто-то вообще не проверяет поездки, хотя их уже накопилось более тысячи;
    • кто-то не оставляет комментарии;
    • кто-то проставляет резолюции по своим внутренним ощущениям, без проверки;
    • кто-то не наказывает пойманных водителей.

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


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

Развитие и первые результаты


Как мы совершенствуем алгоритмы, то есть снижаем ошибки?


  • Невыявленные случаи фрода. Здесь нам помогают кураторы и менеджеры городов. Бывает, что они самостоятельно ловят фродовые поездки и водителей, которые пропустили наши скрипты. Мы выясняем причину и дорабатываем код;
  • Ложные обвинения. Таких ошибок гораздо больше, плюс их мы можем посчитать. Здесь помогают комментарии менеджеров к поездкам, которые они признали честными.

В начале нашего пути ошибка по самому масштабному паттерну (воровство комиссии) в среднем составляла около 35%. Сейчас меньше 25%. В то же время, по другому паттерну доплатам удалось не только свести ошибку к нулю, но и в десятки раз уменьшить количество таких случаев. Выдвинем гипотезу: водители поняли, что теперь за подобное наказывают, и решили, что риск не стоит свеч. И придумали другие схемы.


image


За первые месяцы работы удалось достичь следующих результатов:


  • 15 тысяч поездок признаны фродом;
  • 6800 водителей понесли наказание;
  • более 500 тысяч рублей вернулись в компанию только по воровству комиссий.

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


Заключение


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


В конце концов, мы лишь в начале пути.

Источник: habr.com
К списку статей
Опубликовано: 28.07.2020 10:08:24
0

Сейчас читают

Комментариев (0)
Имя
Электронная почта

Google api

Python

Анализ и проектирование систем

Antifraud

Analytics

Taxi

Sql

Spreadsheets

Категории

Последние комментарии

© 2006-2020, personeltest.ru