Обнаружение глубоких аномалий для крупномасштабных корпоративных данных
Обнаружение аномалий на основе глубокого обучения с использованием нейронных сетей автоэнкодера
Говоря обобщенно, обнаружение аномалий призвано помочь различать довольно редкие события и / или отклоняющиеся от нормы. Это очень важно для финансовой индустрии, так же как и в потребительском банкинге, аномалии могут быть критическими вещами - например, мошенничество с кредитными картами. В других случаях аномалия может быть чем-то, что компании ищут, чтобы извлечь из нее выгоду. Некоторые из других приложений включают вторжения в сети связи, фальшивые новости и дезинформацию, анализ здравоохранения, обнаружение промышленных повреждений, производство, безопасность и наблюдение и т. Д.
Пример использования, показанный в этой статье, взят из области SAP, в частности, Финансы. Бизнес-цель - найти аномальное поведение в финансовых транзакциях.
Типичная финансовая операция в Бухгалтерской информационной системе будет выглядеть так.
Большинство таких записей относятся к обычным транзакциям, но довольно много демонстрируют злонамеренное поведение, которое оказывается аномалиями. Наиболее широко используемый вариант использования в любой финансовой сфере - это обнаружение мошенничества, и методы обнаружения аномалий могут существенно помочь в обнаружении мошенничества в тех случаях, когда для этого требуется много ручных усилий.
В этой статье я расскажу о новейшем методе обнаружения аномалий с помощью Autoencoder Neural Network (AENN). Это метод обнаружения аномалий, основанный на глубоком обучении.
Ну про датасет
Набор данных, используемый для этого варианта использования, можно найти по предоставленной ссылке GitHub. Это синтетический набор финансовых данных, измененный, чтобы он выглядел более похожим на набор реальных данных, который обычно наблюдается в системах SAP-ERP, особенно в модуле управления финансами и затратами.
Набор данных содержит 7 категориальных и 2 числовых атрибута, доступных в таблице FICO BKPF (содержащей заголовки опубликованных записей журнала) и таблице BSEG (содержащей сегменты опубликованных записей журнала).
Другой атрибут «метка» также можно найти в данных, объясняющих истинный характер транзакции: обычная или аномалия (локальная или глобальная). Это необходимо для проверки модели и не будет использоваться в обучающей части.
Классификация аномалий:
Обычно в отрасли аномалии классифицируются по-разному в зависимости от сценария использования. При проведении подробного изучения реальных журнальных записей, обычно записываемых в крупномасштабных системах AIS или ERP, можно наблюдать две основные характеристики:
- атрибуты конкретных транзакций демонстрируют большое количество различных значений атрибутов, например информация о клиенте, размещенные вспомогательные книги, информация о суммах и
- транзакции демонстрируют сильную зависимость между определенными значениями атрибутов, например между информацией о клиенте и типом платежа, типом разноски и главной бухгалтерской книгой.
На основании этого наблюдения можно выделить два класса аномальных записей в журнале, а именно «глобальные» и «локальные» аномалии.
Глобальные аномалии бухгалтерского учета - это занесенные в журнал записи, для которых характерны необычные или редкие значения отдельных атрибутов. Такие аномалии обычно связаны с перекосом атрибутов, например редко используемые бухгалтерские книги или необычное время публикации. Традиционно тесты «красного флага», проводимые аудиторами во время ежегодного аудита, предназначены для выявления аномалий этого типа. Однако такие тесты часто приводят к большому количеству ложноположительных предупреждений из-за таких событий, как обратные проводки, резервы и корректировки на конец года, обычно связанные с низким риском мошенничества. Кроме того, при консультациях с аудиторами и судебными бухгалтерами «глобальные» аномалии часто относятся к «ошибке», а не «мошенничеству».
Локальные аномалии бухгалтерского учета - это занесенные в журнал записи, которые демонстрируют необычную или редкую комбинацию значений атрибутов, в то время как их значения атрибутов встречаются довольно часто, например необычные бухгалтерские записи, нерегулярные комбинации счетов главной книги, учетные записи пользователей, используемые несколькими бухгалтерскими отделами. Этот тип аномалии значительно труднее обнаружить, поскольку преступники намереваются скрыть свою деятельность, имитируя обычную модель активности. В результате такие аномалии обычно представляют собой высокий риск мошенничества, поскольку они соответствуют процессам и действиям, которые могут не выполняться в соответствии со стандартами организации.
Предварительные требования: ожидается, что аудитория будет знакома с основами работы нейронов и нейронных сетей в глубоком обучении. Здесь - отличный учебник, который даст вам точное представление о нейронных сетях.
Обнаружение аномалий с помощью нейронных сетей автоэнкодера - теория
Автоэнкодеры широко используются в компьютерном зрении и обработке речи. Но малоизвестный факт, что они также могут использоваться для обнаружения аномалий. В этом разделе мы познакомим вас с основными элементами нейронных сетей автоэнкодера.
Типичный автокодер состоит из двух функций нелинейного сопоставления, называемых нейронными сетями Кодировщик -f (x) и Декодер -g (x). Кодер обычно следует парадигме воронки с уменьшающимся набором нейронов, а декодер обычно является симметричным зеркалом кодировщика. Существует скрытый центральный слой, называемый скрытым слоем более низких размеров, который будет представлять собой сжатое богатое представление входных данных, достаточное для его восстановления с минимальной ошибкой реконструкции.
Идея использования этой алгоритмической парадигмы для обнаружения аномалий состоит из двух основных этапов: изучение нормального поведения системы (на основе прошлых данных) и обнаружение аномального поведения в реальном времени (путем обработки данных в реальном времени).
Из-за характера набора данных аномалий, который сильно склонен к регулярности, сеть учится восстанавливать регулярную транзакцию и не может сделать это для аномалии. Основываясь на таких высоких ошибках восстановления, мы можем определить, является ли транзакция регулярной или аномальной. Здесь функция потерь - это сама ошибка восстановления.
Loss function(reconstruction error) = arg min || x — g(f(x)) ||
В этом случае мы использовали двоичную кросс-энтропийную потерю, равную.
−(xlog(x’)+(1−x)log(1−x’))
x - входные данные, x ’- g (f (x)). Это измерение того, насколько похожи данные два распределения. Чем меньше потери, тем больше аналогичен ввод и его реконструкция.
Реализация
Примечание. Здесь все становится немного техническим, поэтому я советую всем нетехническим специалистам пропустить этот раздел. Вы можете пройти через это, но не пугайтесь :)
Импортируйте необходимые библиотеки и задайте некоторые параметры.
# importing utilities import os import sys from datetime import datetime # importing data science libraries import pandas as pd import random as rd import numpy as np # importing pytorch libraries import torch from torch import nn from torch import autograd from torch.utils.data import DataLoader # import visualization libraries import matplotlib.pyplot as plt from mpl_toolkits.mplot3d import Axes3D import seaborn as sns from IPython.display import Image, display sns.set_style('darkgrid') # ignore potential warnings import warnings warnings.filterwarnings("ignore")
Установите случайное начальное число и используйте графический процессор, если он доступен.
rseed = 1234 rd.seed(rseed) np.random.seed(rseed) torch.manual_seed(rseed) if (torch.backends.cudnn.version() != None and USE_CUDA == True): torch.cuda.manual_seed(rseed) USE_CUDA = True
Импортируйте данные во фрейм данных pandas.
ad_dataset = pd.read_csv('./data/fraud_dataset_v2.csv') ad_dataset.head()
Посмотрите на форму и подпись value_counts.
ad_dataset.shape Out[#]: (533009, 10) ad_dataset.label.value_counts() Out[#]: regular 532909 global 70 local 30 Name: label, dtype: int64
Как видите, это очень предвзятый набор данных, что верно для большинства реальных данных. Аномалии составляют 0,018% от всех данных. В таких случаях любой типичный алгоритм машинного обучения не работает. Но подход, показанный в статье, - это хитрый трюк, позволяющий использовать автоэнкодеры для поиска аномалий.
Давайте удалим этикетку для дальнейшей обработки, так как автоэнкодер - это неконтролируемый метод.
label = ad_dataset.pop('label')
Теперь давайте разделим категориальные и числовые атрибуты. Добавьте горячие кодировки к категориальным атрибутам, чтобы векторизовать их. Примените масштабирование журнала и минимальное-максимальное масштабирование к числовым переменным.
categorical_attr = ['KTOSL', 'PRCTR', 'BSCHL', 'HKONT', 'WAERS', 'BUKRS'] ad_dataset_categ_transformed = pd.get_dummies(ad_dataset[categorical_attr]) numeric_attr_names = ['DMBTR', 'WRBTR'] # add a small epsilon to eliminate zero values from data for log scaling numeric_attr = ad_dataset[numeric_attr] + 1e-7 numeric_attr = numeric_attr.apply(np.log) ad_dataset_numeric_attr = (numeric_attr - numeric_attr.min()) / (numeric_attr.max() - numeric_attr.min())
Свяжите числовые и категориальные атрибуты.
ad_subset_transformed = pd.concat([ad_dataset_categ_transformed, ad_dataset_numeric_attr], axis = 1) ad_subset_transformed.shape Out[#]: (533009, 618)
Теперь давайте реализуем сеть кодировщика (618–512–256–128–64–32–16–8–4–3).
# implementation of the encoder network class encoder(nn.Module): def __init__(self): super(encoder, self).__init__() # specify layer 1 - in 618, out 512 self.encoder_L1 = nn.Linear(in_features=ori_subset_transformed.shape[1], out_features=512, bias=True) # add linearity nn.init.xavier_uniform_(self.encoder_L1.weight) # init weights according to [9] self.encoder_R1 = nn.LeakyReLU(negative_slope=0.4, inplace=True) # add non-linearity according to [10] # specify layer 2 - in 512, out 256 self.encoder_L2 = nn.Linear(512, 256, bias=True) nn.init.xavier_uniform_(self.encoder_L2.weight) self.encoder_R2 = nn.LeakyReLU(negative_slope=0.4, inplace=True) # specify layer 3 - in 256, out 128 self.encoder_L3 = nn.Linear(256, 128, bias=True) nn.init.xavier_uniform_(self.encoder_L3.weight) self.encoder_R3 = nn.LeakyReLU(negative_slope=0.4, inplace=True) # specify layer 4 - in 128, out 64 self.encoder_L4 = nn.Linear(128, 64, bias=True) nn.init.xavier_uniform_(self.encoder_L4.weight) self.encoder_R4 = nn.LeakyReLU(negative_slope=0.4, inplace=True) # specify layer 5 - in 64, out 32 self.encoder_L5 = nn.Linear(64, 32, bias=True) nn.init.xavier_uniform_(self.encoder_L5.weight) self.encoder_R5 = nn.LeakyReLU(negative_slope=0.4, inplace=True) # specify layer 6 - in 32, out 16 self.encoder_L6 = nn.Linear(32, 16, bias=True) nn.init.xavier_uniform_(self.encoder_L6.weight) self.encoder_R6 = nn.LeakyReLU(negative_slope=0.4, inplace=True) # specify layer 7 - in 16, out 8 self.encoder_L7 = nn.Linear(16, 8, bias=True) nn.init.xavier_uniform_(self.encoder_L7.weight) self.encoder_R7 = nn.LeakyReLU(negative_slope=0.4, inplace=True) # specify layer 8 - in 8, out 4 self.encoder_L8 = nn.Linear(8, 4, bias=True) nn.init.xavier_uniform_(self.encoder_L8.weight) self.encoder_R8 = nn.LeakyReLU(negative_slope=0.4, inplace=True) # specify layer 9 - in 4, out 3 self.encoder_L9 = nn.Linear(4, 3, bias=True) nn.init.xavier_uniform_(self.encoder_L9.weight) self.encoder_R9 = nn.LeakyReLU(negative_slope=0.4, inplace=True) # init dropout layer with probability p self.dropout = nn.Dropout(p=0.0, inplace=True) def forward(self, x): # define forward pass through the network x = self.encoder_R1(self.dropout(self.encoder_L1(x))) x = self.encoder_R2(self.dropout(self.encoder_L2(x))) x = self.encoder_R3(self.dropout(self.encoder_L3(x))) x = self.encoder_R4(self.dropout(self.encoder_L4(x))) x = self.encoder_R5(self.dropout(self.encoder_L5(x))) x = self.encoder_R6(self.dropout(self.encoder_L6(x))) x = self.encoder_R7(self.dropout(self.encoder_L7(x))) x = self.encoder_R8(self.dropout(self.encoder_L8(x))) x = self.encoder_R9(self.encoder_L9(x)) return x
Создайте экземпляр кодировщика и включите
# init training network classes / architectures encoder_train = encoder() # push to cuda if cudnn is available if (torch.backends.cudnn.version() != None and USE_CUDA == True): encoder_train = encoder().cuda()
Теперь о сетевой реализации декодера, которая является симметричным зеркалом кодировщика. (3–4–8–16–32–64–128–256–512–618)
# implementation of the decoder network class decoder(nn.Module): def __init__(self): super(decoder, self).__init__() # specify layer 1 - in 3, out 4 self.decoder_L1 = nn.Linear(in_features=3, out_features=4, bias=True) # add linearity nn.init.xavier_uniform_(self.decoder_L1.weight) # init weights according to [9] self.decoder_R1 = nn.LeakyReLU(negative_slope=0.4, inplace=True) # add non-linearity according to [10] # specify layer 2 - in 4, out 8 self.decoder_L2 = nn.Linear(4, 8, bias=True) nn.init.xavier_uniform_(self.decoder_L2.weight) self.decoder_R2 = nn.LeakyReLU(negative_slope=0.4, inplace=True) # specify layer 3 - in 8, out 16 self.decoder_L3 = nn.Linear(8, 16, bias=True) nn.init.xavier_uniform_(self.decoder_L3.weight) self.decoder_R3 = nn.LeakyReLU(negative_slope=0.4, inplace=True) # specify layer 4 - in 16, out 32 self.decoder_L4 = nn.Linear(16, 32, bias=True) nn.init.xavier_uniform_(self.decoder_L4.weight) self.decoder_R4 = nn.LeakyReLU(negative_slope=0.4, inplace=True) # specify layer 5 - in 32, out 64 self.decoder_L5 = nn.Linear(32, 64, bias=True) nn.init.xavier_uniform_(self.decoder_L5.weight) self.decoder_R5 = nn.LeakyReLU(negative_slope=0.4, inplace=True) # specify layer 6 - in 64, out 128 self.decoder_L6 = nn.Linear(64, 128, bias=True) nn.init.xavier_uniform_(self.decoder_L6.weight) self.decoder_R6 = nn.LeakyReLU(negative_slope=0.4, inplace=True) # specify layer 7 - in 128, out 256 self.decoder_L7 = nn.Linear(128, 256, bias=True) nn.init.xavier_uniform_(self.decoder_L7.weight) self.decoder_R7 = nn.LeakyReLU(negative_slope=0.4, inplace=True) # specify layer 8 - in 256, out 512 self.decoder_L8 = nn.Linear(256, 512, bias=True) nn.init.xavier_uniform_(self.decoder_L8.weight) self.decoder_R8 = nn.LeakyReLU(negative_slope=0.4, inplace=True) # specify layer 9 - in 512, out 618 self.decoder_L9 = nn.Linear(in_features=512, out_features=ori_subset_transformed.shape[1], bias=True) nn.init.xavier_uniform_(self.decoder_L9.weight) self.decoder_R9 = nn.LeakyReLU(negative_slope=0.4, inplace=True) # init dropout layer with probability p self.dropout = nn.Dropout(p=0.0, inplace=True) def forward(self, x): # define forward pass through the network x = self.decoder_R1(self.dropout(self.decoder_L1(x))) x = self.decoder_R2(self.dropout(self.decoder_L2(x))) x = self.decoder_R3(self.dropout(self.decoder_L3(x))) x = self.decoder_R4(self.dropout(self.decoder_L4(x))) x = self.decoder_R5(self.dropout(self.decoder_L5(x))) x = self.decoder_R6(self.dropout(self.decoder_L6(x))) x = self.decoder_R7(self.dropout(self.decoder_L7(x))) x = self.decoder_R8(self.dropout(self.decoder_L8(x))) x = self.decoder_R9(self.decoder_L9(x)) return x
Создайте экземпляр декодера и поместите его на GPU.
# init training network classes / architectures decoder_train = decoder() # push to cuda if cudnn is available if (torch.backends.cudnn.version() != None) and (USE_CUDA == True): decoder_train = decoder().cuda()
Теперь настраиваем функцию потерь и некоторые гиперпараметры.
# define the optimization criterion / loss function loss_function = nn.BCEWithLogitsLoss(reduction='mean') # define learning rate and optimization strategy learning_rate = 1e-3 encoder_optimizer = torch.optim.Adam(encoder_train.parameters(), lr=learning_rate) decoder_optimizer = torch.optim.Adam(decoder_train.parameters(), lr=learning_rate) # specify training parameters num_epochs = 8 mini_batch_size = 128
Загрузите данные в тензор и на GPU.
# convert pre-processed data to pytorch tensor torch_dataset = torch.from_numpy(ad_subset_transformed.values).float() # convert to pytorch tensor - none cuda enabled dataloader = DataLoader(torch_dataset, batch_size=mini_batch_size, shuffle=True, num_workers=0) # note: we set num_workers to zero to retrieve deterministic results # determine if CUDA is available at compute node if (torch.backends.cudnn.version() != None) and (USE_CUDA == True): dataloader = DataLoader(torch_dataset.cuda(), batch_size=mini_batch_size, shuffle=True)
Теперь к нашему обучению. (Примечание: я советую не копировать и вставлять приведенный ниже код, поскольку форматирование может быть неправильным. Получите код по ссылке GitHub, указанной ниже.)
# init collection of mini-batch losses losses = [] # convert encoded transactional data to torch Variable data = autograd.Variable(torch_dataset) # train autoencoder model for epoch in range(num_epochs): # init mini batch counter mini_batch_count = 0 # determine if CUDA is available at compute node if(torch.backends.cudnn.version() != None) and (USE_CUDA == True): # set networks / models in GPU mode encoder_train.cuda() decoder_train.cuda() # set networks in training mode (apply dropout when needed) encoder_train.train() decoder_train.train() # start timer start_time = datetime.now() # iterate over all mini-batches for mini_batch_data in dataloader: # increase mini batch counter mini_batch_count += 1 # convert mini batch to torch variable mini_batch_torch = autograd.Variable(mini_batch_data) # =================== (1) forward pass ============================ # run forward pass z_representation = encoder_train(mini_batch_torch) # encode mini-batch data mini_batch_reconstruction = decoder_train(z_representation) # decode mini-batch data # =================== (2) compute reconstruction loss ====== # determine reconstruction loss reconstruction_loss = loss_function(mini_batch_reconstruction, mini_batch_torch) # =================== (3) backward pass ==================== # reset graph gradients decoder_optimizer.zero_grad() encoder_optimizer.zero_grad() # run backward pass reconstruction_loss.backward() # =================== (4) update model parameters ========= # update network parameters decoder_optimizer.step() encoder_optimizer.step() # =================== monitor training progress =================== # print training progress each 1'000 mini-batches if mini_batch_count % 1000 == 0: # print the training mode: either on GPU or CPU mode = 'GPU' if (torch.backends.cudnn.version() != None) and (USE_CUDA == True) else 'CPU' # print mini batch reconstuction results now = datetime.utcnow().strftime("%Y%m%d-%H:%M:%S") end_time = datetime.now() - start_time print('[LOG {}] training status, epoch: [{:04}/{:04}], batch: {:04}, loss: {}, mode: {}, time required: {}'.format(now, (epoch+1), num_epochs, mini_batch_count, np.round(reconstruction_loss.item(), 4), mode, end_time)) # reset timer start_time = datetime.now() # =================== evaluate model performance ================ # set networks in evaluation mode (don't apply dropout) encoder_train.cpu().eval() decoder_train.cpu().eval() # reconstruct encoded transactional data reconstruction = decoder_train(encoder_train(data)) # determine reconstruction loss - all transactions reconstruction_loss_all = loss_function(reconstruction, data) # collect reconstruction loss losses.extend([reconstruction_loss_all.item()]) # print reconstuction loss results now = datetime.utcnow().strftime("%Y%m%d-%H:%M:%S") print('[LOG {}] training status, epoch: [{:04}/{:04}], loss: {:.10f}'.format(now, (epoch+1), num_epochs, reconstruction_loss_all.item())) # =================== save model snapshot to disk ================ # save trained encoder model file to disk encoder_model_name = "ep_{}_encoder_model.pth".format((epoch+1)) torch.save(encoder_train.state_dict(), os.path.join("./models", encoder_model_name)) # save trained decoder model file to disk decoder_model_name = "ep_{}_decoder_model.pth".format((epoch+1)) torch.save(decoder_train.state_dict(), os.path.join("./models", decoder_model_name))
Нанесение убытков.
# plot the training progress plt.plot(range(0, len(losses)), losses) plt.xlabel('[training epoch]') plt.xlim([0, len(losses)]) plt.ylabel('[reconstruction-error]') #plt.ylim([0.0, 1.0]) plt.title('AENN training performance')
На этом наше обучение закончено. Теперь давайте посмотрим, как использовать наши модели для получения прогнозов.
Загрузите предварительно обученные модели.
# restore pretrained model checkpoint encoder_model_name = "ep_8_encoder_model.pth" decoder_model_name = "ep_8_decoder_model.pth" # init training network classes / architectures encoder_eval = encoder() decoder_eval = decoder() # load trained models encoder_eval.load_state_dict(torch.load(os.path.join("models", encoder_model_name))) decoder_eval.load_state_dict(torch.load(os.path.join("models", decoder_model_name)))
Выполните реконструкцию для всех данных.
# convert encoded transactional data to torch Variable data = autograd.Variable(torch_dataset) # set networks in evaluation mode (don't apply dropout) encoder_eval.eval() decoder_eval.eval() # reconstruct encoded transactional data reconstruction = decoder_eval(encoder_eval(data))
Получите потери реконструкции для всех данных.
# determine reconstruction loss - all transactions reconstruction_loss_all = loss_function(reconstruction, data) print(reconstruction_loss_all) reconstruction loss: 0.0034663924
Определите потери при реконструкции для отдельных транзакций.
# init binary cross entropy errors reconstruction_loss_transaction = np.zeros(reconstruction.size()[0]) # iterate over all detailed reconstructions for i in range(0, reconstruction.size()[0]): # determine reconstruction loss - individual transactions reconstruction_loss_transaction[i] = loss_function(reconstruction[i], data[i]).item()
Постройте точки данных в соответствии с потерями реконструкции, прикрепленными к этим меткам.
# prepare plot fig = plt.figure() ax = fig.add_subplot(111) # assign unique id to transactions plot_data = np.column_stack((np.arange(len(reconstruction_loss_transaction)), reconstruction_loss_transaction)) # obtain regular transactions as well as global and local anomalies regular_data = plot_data[label == 'regular'] global_outliers = plot_data[label == 'global'] local_outliers = plot_data[label == 'local'] # plot reconstruction error scatter plot ax.scatter(regular_data[:, 0], regular_data[:, 1], c='C0', alpha=0.4, marker="o", label='regular') # plot regular transactions ax.scatter(global_outliers[:, 0], global_outliers[:, 1], c='C1', marker="^", label='global') # plot global outliers ax.scatter(local_outliers[:, 0], local_outliers[:, 1], c='C2', marker="^", label='local') # plot local outliers # add plot legend of transaction classes ax.legend(loc='best')
График показывает, как выбранный подход элегантно обнаружил аномалии из набора данных с высокой степенью предвзятости. Посмотрим, сколько аномалий было выявлено.
ad_dataset['label'] = label ad_dataset[reconstruction_loss_transaction >= 0.1].label.value_counts() Out[#]: global 59 local 2 Name: label, dtype: int64 ad_dataset[(reconstruction_loss_transaction >= 0.018) & (reconstruction_loss_transaction < 0.05)].label.value_counts() Out[#]: local 23 Name: label, dtype: int64
Как видите, из 70 глобальных было обнаружено 59, что составляет 84%, а из 30 локальных обнаружено 23, что составляет 76,6%. Это намного лучшая производительность, чем у любых других старых методов, учитывая, что выбросы составляли всего 0,018% от всех данных.
Вот ссылка на Github для реализации кода вместе с набором данных.
Надеюсь, это дает четкое представление о подходе и о том, как его реализовать.
Заключение
Из этого можно сделать вывод, что применение алгоритмов глубокого обучения к классическим задачам машинного обучения со структурированными данными даст многообещающие результаты, если они хорошо спроектированы. Определение правильного алгоритма, подходящей функции потерь и идеального набора данных может помочь специалистам по обработке данных подключиться к глубокому обучению и использовать его возможности для повышения производительности устаревших подходов. Пример использования, упомянутый в этой статье, касается финансовых транзакций, но сама идея обнаружения глубоких аномалий может быть распространена на другие области, такие как производство и маркетинг.
использованная литература
Обнаружение аномалий в крупномасштабных учетных данных с использованием сетей глубокого автоэнкодера
Обзор методов обнаружения аномалий в финансовой сфере
Глубокое обучение для обнаружения аномалий: исследование
Обо мне
Я старший эксперт по ИИ в Wavelabs.ai. Мы в Wavelabs помогаем вам использовать искусственный интеллект (AI), чтобы революционизировать пользовательский опыт и сократить расходы. Мы уникальным образом улучшаем ваши продукты с помощью ИИ, чтобы полностью раскрыть ваш рыночный потенциал. Мы стараемся внедрять передовые исследования в ваши приложения.
Не стесняйтесь узнавать больше на Wavelabs.ai.
Ну вот и все в этом посте. Спасибо за прочтение :)
Оставайтесь любопытными!
Вы можете связаться со мной в LinkedIn.