Реализация AI-детекции мошеннических транзакций в мобильном приложении
Мошенничество в финтех не выглядит как в кино. Это не один большой подозрительный перевод — это паттерн: несколько небольших транзакций в нестандартное для пользователя время, в нестандартной геолокации, с нестандартными получателями. Статические правила («заблокировать транзакцию > 50 000 рублей в ночное время») дают high false positive rate и фрустрируют честных пользователей. ML-модели работают с контекстом.
Почему это труднее скоринга
Дисбаланс классов. Мошеннических транзакций — 0.1–1% от общего числа. Модель, которая всегда отвечает «нормальная транзакция», имеет 99% accuracy и бесполезна. Нужны специальные техники: SMOTE oversampling, cost-sensitive learning, оптимизация threshold по F1/AUC-PR, а не accuracy.
Реальное время. Скоринг заёмщика — офлайн-задача, можно считать минуты. Детекция фрода — онлайн, решение нужно за 200–500мс до того, как транзакция подтвердится. Это накладывает ограничения на сложность модели.
Concept drift. Схемы мошенничества меняются быстрее экономических паттернов. Модель деградирует быстро — нужен более частый мониторинг и переобучение.
Feature engineering для fraud detection
def extract_transaction_features(
transaction: Transaction,
user_history: UserHistory,
real_time_context: RealTimeContext
) -> dict:
return {
# Отклонение суммы от исторической нормы пользователя
"amount_zscore": (transaction.amount - user_history.avg_amount) / user_history.std_amount,
# Время суток (0-23) — мошенничество пиково ночью
"hour_of_day": transaction.timestamp.hour,
"is_unusual_hour": transaction.timestamp.hour not in user_history.active_hours,
# Скорость: время с предыдущей транзакцией
"minutes_since_last_tx": (transaction.timestamp - user_history.last_tx_time).seconds / 60,
# Геолокация
"is_new_country": transaction.country not in user_history.known_countries,
"distance_from_last_tx_km": geo_distance(transaction.location, user_history.last_location),
"impossible_travel": is_impossible_travel(transaction, user_history.last_tx_location, user_history.last_tx_time),
# Получатель
"is_new_recipient": transaction.recipient_id not in user_history.known_recipients,
"recipient_fraud_score": real_time_context.recipient_risk_score, # Из внешнего источника
# Устройство и сессия
"is_new_device": transaction.device_id not in user_history.known_devices,
"session_age_minutes": real_time_context.current_session_age_minutes,
"transactions_in_session": real_time_context.session_tx_count,
}
Impossible travel — один из сильнейших признаков: транзакция в Москве в 14:00 и транзакция в Лондоне в 14:30 физически невозможна. Реализуется через Haversine distance между геолокациями и временным дельта.
Модель и inference
CatBoost и LightGBM — практичный выбор: быстрый inference (< 5мс), хорошая работа с категориальными признаками, встроенный SHAP.
import catboost as cb
model = cb.CatBoostClassifier(
iterations=500,
learning_rate=0.05,
depth=6,
loss_function="Logloss",
eval_metric="AUC",
class_weights={0: 1, 1: 50}, # Компенсация дисбаланса классов
random_seed=42
)
def predict_fraud_score(features: dict) -> dict:
feature_vector = prepare_features(features)
proba = model.predict_proba(feature_vector)[0][1]
# Многоуровневые пороги вместо бинарного решения
if proba > 0.85:
action = "block"
elif proba > 0.60:
action = "challenge" # Запросить подтверждение (биометрия, OTP)
else:
action = "allow"
return {
"fraud_probability": float(proba),
"action": action,
"risk_factors": get_shap_explanations(feature_vector)
}
Три уровня действий вместо бинарного «разрешить/заблокировать» снижают false positive rate: большинство подозрительных транзакций получают дополнительную аутентификацию, а не блокировку.
Интеграция в мобильное приложение
Fraud scoring — синхронный вызов в момент инициации транзакции пользователем:
// iOS — Swift
func initiateTransfer(_ transfer: TransferRequest) async throws -> TransferResult {
// 1. Получаем fraud score (цель < 300ms)
let fraudScore = try await fraudDetectionService.evaluate(
amount: transfer.amount,
recipientId: transfer.recipientId,
userLocation: locationManager.currentLocation
)
switch fraudScore.action {
case "block":
throw TransferError.blockedByFraudProtection(
reason: localizeRiskFactors(fraudScore.riskFactors)
)
case "challenge":
// Запрашиваем биометрию или OTP перед продолжением
try await authenticateAdditionally()
return try await processTransfer(transfer)
case "allow":
return try await processTransfer(transfer)
default:
return try await processTransfer(transfer)
}
}
Мониторинг в продакшене
Fraud detection без мониторинга — деградирующая система. Ключевые метрики:
| Метрика | Что измеряет | Целевой диапазон |
|---|---|---|
| False Positive Rate | Доля заблокированных честных транзакций | < 0.5% |
| Detection Rate | Доля пойманного мошенничества | > 85% |
| AUC-PR | Общее качество модели | > 0.85 |
| PSI признаков | Дрейф входных данных | < 0.2 |
False Positive Rate важнее Detection Rate для пользовательского опыта: заблокированная честная транзакция — прямые потери лояльности. Баланс настраивается через threshold.
Процесс работы
Сбор и разметка исторических транзакций (вместе с командой по рискам) → feature engineering → baseline (logistic regression) → gradient boosting с тюнингом threshold → A/B тест → онлайн-мониторинг PSI и FPR → ежемесячное переобучение.
Ориентиры по срокам
MVP с правилами + простой ML-моделью — 4–6 недель. Полная система с realtime inference, мониторингом и автоматическим переобучением — 2–3 месяца. При наличии готовой размеченной выборки — ускоряется на 3–4 недели.







