BigQuery MLで異常検知を実行する
データアナリティクス事業本部・機械学習チームの貞松です。
今回は初のGoogle Cloud関連です。
本記事は「クラスメソッド Google Cloud Advent Calendar 2021」の12日目のエントリーです。
Google BigQuery上で機械学習を実行可能なBigQuery MLという機能があります。
この機能に関する概要と2020年12月時点の機能について、以下の記事にまとめられています。
2021年におけるBigQuery MLのアップデートトピックとして、異常検知機能の一般提供が挙げられます。 本記事では、BigQuery MLによる異条検知の概要について解説しつつ、適用可能なデータソース別の利用手順を示します。
BigQuery MLによる異常検知の概要
BigQuery MLによる異常検知は、2021年6月22日にプレビュー機能としてリリースされ、2021年12月6日に一般提供が開始した比較的新しい機能です。
BigQuery上で、対象となる時系列データあるいは非時系列データに対応したアルゴリズムによってモデルをトレーニングし、トレーニングされたモデル使用してML.DETECT_ANOMALIES
関数を実行することにより異常検知を実行します。
異常検知で利用可能なモデルタイプは以下の通りです。
- 時系列データに対応したモデルタイプ
- ARIMA_PLUS
- 非時系列データに対応したモデルタイプ
- KMeans
- Autoencoder
- PCA
BigQuery MLによる異常検知の利用手順
使用するデータセット
BigQuery MLによる異常検知の実行の為、BigQuery public datasetを使用します。
BigQuery public datasetは、BigQueryに保存され、Google Cloud Public Dataset Programを通じて一般に公開されているデータセットです。
実行環境
今回はBigQueryAPIのクライアントライブラリ(Python)を使用して、BigQuery MLを実行します。
実行環境のpythonバージョンおよびライブラリバージョンは以下の通りです。
- python 3.8.6
- google-cloud-bigquery 2.31.0
時系列データに対する適用
時系列データに対する異常検知については、対応するモデルタイプがARIMA_PLUSだけなので、何も考えずこちらを使用するケースについて解説します。
対象とするデータ
異常検知を実行する為の時系列データとして、BigQuery public datasetのIowa Liquor Retail Sales(2012年以降のアイオワ州の酒類販売データ)を使用します。
日毎の店舗別、商品別で販売データで、店舗や商品に関する情報を列として持っているデータになっています。これらを用いて時系列予測モデルを学習し、予測値から外れたデータポイントを異常として検出します。
モデルのトレーニング
BigQuery MLでモデルを学習するには、学習モデルの保存先データセットを指定する必要があります。
今回はtest_dataset
という名前のデータセットを予め作成しておきます。
from google.cloud import bigquery # Construct a BigQuery client object client = bigquery.Client(location="US") # Create dataset dataset = bigquery.Dataset("test_dataset") dataset.location = "US" dataset = client.create_dataset(dataset, timeout=30)
モデルを学習するクエリではcreate or replace model
に対して[データセット名].[モデル名]を指定します。
データセット名には前述で作成したtest_dataset
を指定し、モデル名には任意の名称を指定します。
また、使用するモデルタイプに合わせてoptionsと学習データのセットを指定します。
from google.cloud import bigquery client = bigquery.Client(location="US") l_query = """ create or replace model test_dataset.arima_plus_model options( model_type = 'arima_plus', time_series_timestamp_col = 'date', time_series_data_col = 'total_amount_sold', time_series_id_col = 'item_name', holiday_region = 'us' ) as select date, item_description AS item_name, sum(bottles_sold) AS total_amount_sold from `bigquery-public-data.iowa_liquor_sales.sales` group by date, item_name having date between date('2016-01-04') and date('2017-06-01') and item_name in ("Black Velvet", "Captain Morgan Spiced Rum", "Hawkeye Vodka", "Five O'Clock Vodka", "Fireball Cinnamon Whiskey") """ client.query(l_query)
異常検知の実行
学習済みモデルを使用して異常検知を実行するクエリでは、from句にml.detect_anomalies
を指定します。
ml.detect_anomalies
では、使用する学習済みモデルと評価指標に対する異常判定の閾値、異常検知の対象データセットを指定します。
時系列データに対する異常判定の閾値としてanomaly_prob_threshold
がサポートされています。
anomaly_prob_threshold
は、学習済みモデルによって算出されたデータポイントの異常確率が設定値を超えた場合に異常として判定されます。
from google.cloud import bigquery client = bigquery.Client(location="US") t_query = """ with new_data as ( select date, item_description as item_name, SUM(bottles_sold) as total_amount_sold from `bigquery-public-data.iowa_liquor_sales.sales` group by date, item_name having date between date('2017-06-02') and date('2017-10-01') and item_name in ('Black Velvet', 'Captain Morgan Spiced Rum', 'Hawkeye Vodka', "Five O'Clock Vodka", 'Fireball Cinnamon Whiskey') ) select * from ml.detect_anomalies( model `test_dataset.arima_plus_model`, struct(0.8 AS anomaly_prob_threshold), ( select * from new_data ) ) """ t_query_job = client.query(t_query) for row in t_query_job: print(row)
以下、結果出力の抜粋です。
anomaly_probability
が0.8
を超えているレコードでis_anomaly
がTrue
になっていることが確認できます。
Row(('Captain Morgan Spiced Rum', datetime.datetime(2017, 9, 22, 0, 0, tzinfo=datetime.timezone.utc), 815.0, False, 643.7963146260081, 2063.897000835903, 0.6690818449920262), {'item_name': 0, 'date': 1, 'total_amount_sold': 2, 'is_anomaly': 3, 'lower_bound': 4, 'upper_bound': 5, 'anomaly_probability': 6}) Row(('Captain Morgan Spiced Rum', datetime.datetime(2017, 8, 30, 0, 0, tzinfo=datetime.timezone.utc), 3127.0, True, 1736.2938591344162, 3086.0776314932864, 0.8260684144969315), {'item_name': 0, 'date': 1, 'total_amount_sold': 2, 'is_anomaly': 3, 'lower_bound': 4, 'upper_bound': 5, 'anomaly_probability': 6}) Row(('Captain Morgan Spiced Rum', datetime.datetime(2017, 9, 6, 0, 0, tzinfo=datetime.timezone.utc), 2582.0, False, 1732.8549697385156, 3104.421218622561, 0.24011500422537502), {'item_name': 0, 'date': 1, 'total_amount_sold': 2, 'is_anomaly': 3, 'lower_bound': 4, 'upper_bound': 5, 'anomaly_probability': 6}) Row(('Captain Morgan Spiced Rum', datetime.datetime(2017, 6, 7, 0, 0, tzinfo=datetime.timezone.utc), 2273.0, False, 1787.1898387041674, 2816.207126402066, 0.056588367397308104), {'item_name': 0, 'date': 1, 'total_amount_sold': 2, 'is_anomaly': 3, 'lower_bound': 4, 'upper_bound': 5, 'anomaly_probability': 6}) Row(('Captain Morgan Spiced Rum', datetime.datetime(2017, 8, 23, 0, 0, tzinfo=datetime.timezone.utc), 2332.0, False, 1739.9114155725497, 3067.5553773215565, 0.10977540326957624), {'item_name': 0, 'date': 1, 'total_amount_sold': 2, 'is_anomaly': 3, 'lower_bound': 4, 'upper_bound': 5, 'anomaly_probability': 6}) Row(('Captain Morgan Spiced Rum', datetime.datetime(2017, 9, 7, 0, 0, tzinfo=datetime.timezone.utc), 2158.0, True, 2283.0756681213584, 3657.7255232013163, 0.8705695913100937), {'item_name': 0, 'date': 1, 'total_amount_sold': 2, 'is_anomaly': 3, 'lower_bound': 4, 'upper_bound': 5, 'anomaly_probability': 6}) Row(('Captain Morgan Spiced Rum', datetime.datetime(2017, 9, 13, 0, 0, tzinfo=datetime.timezone.utc), 3032.0, False, 1729.586365881772, 3122.5945202126554, 0.7349100510921001), {'item_name': 0, 'date': 1, 'total_amount_sold': 2, 'is_anomaly': 3, 'lower_bound': 4, 'upper_bound': 5, 'anomaly_probability': 6}) Row(('Captain Morgan Spiced Rum', datetime.datetime(2017, 7, 5, 0, 0, tzinfo=datetime.timezone.utc), 2337.0, False, 2006.1944025516636, 3167.0951573949283, 0.41913698016019907), {'item_name': 0, 'date': 1, 'total_amount_sold': 2, 'is_anomaly': 3, 'lower_bound': 4, 'upper_bound': 5, 'anomaly_probability': 6}) Row(('Captain Morgan Spiced Rum', datetime.datetime(2017, 6, 29, 0, 0, tzinfo=datetime.timezone.utc), 2553.0, False, 2244.546340233833, 3383.3427108670867, 0.44361310291078593), {'item_name': 0, 'date': 1, 'total_amount_sold': 2, 'is_anomaly': 3, 'lower_bound': 4, 'upper_bound': 5, 'anomaly_probability': 6}) Row(('Captain Morgan Spiced Rum', datetime.datetime(2017, 8, 4, 0, 0, tzinfo=datetime.timezone.utc), 768.0, True, 770.438549669569, 2036.03791681777, 0.8017365997944836), {'item_name': 0, 'date': 1, 'total_amount_sold': 2, 'is_anomaly': 3, 'lower_bound': 4, 'upper_bound': 5, 'anomaly_probability': 6})
非時系列データに対する適用
BigQuery MLによる異常検知は、非時系列データに対しても適用することが可能です。この非時系列データは、厳密には独立同分布の確率変数を指します。 独立同分布(independent and identically distributed, IID)とは、それぞれの確率変数が他の確率変数と同じ確率分布を持ち、且つそれぞれ互いに独立している状態を指します。
今回は非時系列データに対応したモデルタイプの内、Autoencoderを使用するケースについて解説します。
対象とするデータ
異常検知を実行する為の非時系列データとして、BigQuery public datasetのml_datasets.ulb_fraud_detection
(不正な取引か否かを判別するラベルが付けられた匿名のクレジットカード取引データ)を使用します。
※ 何故かGoogle CloudのMarketplaceでは該当のページを見つけられなかったので、下記はKaggle上にある同一のデータセットのページです。
顧客情報の匿名化と次元削減の為にPCA(主成分分析)によって生成されたV1〜V28と取引額、不正な取引か否かを判別するラベルを列として持っており、これらのデータを用いて、カードによる取引が不正か否かを判別する(異常を検知する)モデルを学習します。
モデルのトレーニング
基本的な構文はARIMA_PLUSを使用する場合と同様です。
モデルの保存先データセットは、前述で作成したtest_dataset
をそのまま使用し、optionsにはautoencoder用のパラメータをそれぞれ設定します。
from google.cloud import bigquery client = bigquery.Client(location="US") l_query = """ create model `test_dataset.autoencoder_model` options( model_type='autoencoder', activation_fn='relu', batch_size=8, dropout=0.2, hidden_units=[32, 16, 4, 16, 32], learn_rate=0.001, l1_reg_activation=0.0001, max_iterations=10, optimizer='adam' ) as select * except(Time, Class) from `bigquery-public-data.ml_datasets.ulb_fraud_detection` """ client.query(l_query)
異常検知の実行
異常検知の実行についても、基本的な構文はARIMA_PLUSを使用する場合と同様です。
非時系列データに対する異常判定の閾値ではcontamination
を使用します。
contamination
には、異常検知対象のデータセットの内、どれくらいの割合を異常として検出するかを指定します。例えば0.02
を指定した場合、異常検知対象のデータセットの内2%
が異常として検知されるように閾値が計算されます。
from google.cloud import bigquery client = bigquery.Client(location="US") t_query = """ select * from ml.detect_anomalies( model `test_dataset.autoencoder_model`, struct(0.02 as contamination), table `bigquery-public-data.ml_datasets.ulb_fraud_detection` ) """ t_query_job = client.query(t_query) for row in t_query_job: print(row)
以下、結果の先頭10件を抜粋したものです。
今回contamination
を0.02
で設定していますが、内部的に計算された結果、mean_squared_error
に対して1.1
あたりが閾値として設定されているようです。
Row((True, 1.1081621646881104, 10820.0, -1.1146444169671101, 0.337142598090784, 2.193270429391, 2.14466690801262, 0.7193858585180101, -0.31510268811335695, -0.79518045400976, -0.689766596741344, 1.14860238190638, -0.160652410472398, -0.202300415262296, -1.9802927601814602, 2.3952694858601298, 0.7911363275406359, -1.88877821630977, 0.713839613472861, 0.0215782285745075, 0.0749188235525648, -1.6272529841306103, -0.547260793513201, 0.675917598532996, -0.00482247660281948, 0.11017192609236098, 0.32988482494969, -1.1945410771501002, -0.41361976515825205, 0.401690430905136, 0.207775541447575, 0.0, 0), {'is_anomaly': 0, 'mean_squared_error': 1, 'Time': 2, 'V1': 3, 'V2': 4, 'V3': 5, 'V4': 6, 'V5': 7, 'V6': 8, 'V7': 9, 'V8': 10, 'V9': 11, 'V10': 12, 'V11': 13, 'V12': 14, 'V13': 15, 'V14': 16, 'V15': 17, 'V16': 18, 'V17': 19, 'V18': 20, 'V19': 21, 'V20': 22, 'V21': 23, 'V22': 24, 'V23': 25, 'V24': 26, 'V25': 27, 'V26': 28, 'V27': 29, 'V28': 30, 'Amount': 31, 'Class': 32}) Row((False, 0.6027346253395081, 10910.0, 1.0848985925605301, 0.199358535128206, 1.6069162367218999, 2.88089313423186, -0.688178667404267, 0.62788617069338, -0.8444090535786991, 0.320399153823263, 1.6508587644861799, 0.129207331514003, -0.077701598220752, -3.1656461583703304, -0.0523785795289134, 1.38892202206499, -0.600845866869931, 0.379401149538819, 0.693252240036643, -0.166050337345302, -1.3921023110288102, -0.3485210093211, -0.229154915480322, -0.306368580072919, 0.107861357632864, -0.0322713053702367, 0.144010584102172, -0.0467716428639049, 0.0215045899497445, 0.0222373986974794, 0.0, 0), {'is_anomaly': 0, 'mean_squared_error': 1, 'Time': 2, 'V1': 3, 'V2': 4, 'V3': 5, 'V4': 6, 'V5': 7, 'V6': 8, 'V7': 9, 'V8': 10, 'V9': 11, 'V10': 12, 'V11': 13, 'V12': 14, 'V13': 15, 'V14': 16, 'V15': 17, 'V16': 18, 'V17': 19, 'V18': 20, 'V19': 21, 'V20': 22, 'V21': 23, 'V22': 24, 'V23': 25, 'V24': 26, 'V25': 27, 'V26': 28, 'V27': 29, 'V28': 30, 'Amount': 31, 'Class': 32}) Row((False, 0.612176775932312, 11153.0, -0.401896143858385, 0.9020529356378059, 2.96056044836648, 2.28779528789042, -0.0316799452866078, 0.283843379960376, 0.14021121506791298, -0.0470270806533182, 0.9587671354875641, -0.104688163781482, -0.0862278584893869, -2.7493782387110697, 1.5674245535199898, 0.714822484596932, -1.22044022119779, 0.4012229657775871, 0.235464763889832, 0.222484477836387, -0.87484711726326, -0.05380532702494901, -0.201845703016314, -0.073923672972158, -0.141768691698829, 0.30259291737365696, -0.218362791487778, -0.15155377797888098, -0.0975212981619207, -0.151215823537664, 0.0, 0), {'is_anomaly': 0, 'mean_squared_error': 1, 'Time': 2, 'V1': 3, 'V2': 4, 'V3': 5, 'V4': 6, 'V5': 7, 'V6': 8, 'V7': 9, 'V8': 10, 'V9': 11, 'V10': 12, 'V11': 13, 'V12': 14, 'V13': 15, 'V14': 16, 'V15': 17, 'V16': 18, 'V17': 19, 'V18': 20, 'V19': 21, 'V20': 22, 'V21': 23, 'V22': 24, 'V23': 25, 'V24': 26, 'V25': 27, 'V26': 28, 'V27': 29, 'V28': 30, 'Amount': 31, 'Class': 32}) Row((False, 0.7329543828964233, 11225.0, -0.319472963273313, 1.14391523562843, 1.92644332018603, 2.907521904278, 0.6689145232730649, 0.24144382864055203, 0.126259373932035, 0.0544757077217116, -0.49537051888871403, 0.513002901867128, 1.99799602476017, -1.77397512447346, 2.75505575288403, 1.6996382359412199, -0.265758944570786, 0.302556913290389, 0.24106708188227502, 0.6590742912092099, 1.05233343023198, 0.21668567054931498, -0.24571913218678698, -0.556407696377963, 0.142773382615279, -0.0712179899492115, -0.9086821657681979, -0.229380396875391, 0.15910389420718601, 0.17680188503088395, 0.0, 0), {'is_anomaly': 0, 'mean_squared_error': 1, 'Time': 2, 'V1': 3, 'V2': 4, 'V3': 5, 'V4': 6, 'V5': 7, 'V6': 8, 'V7': 9, 'V8': 10, 'V9': 11, 'V10': 12, 'V11': 13, 'V12': 14, 'V13': 15, 'V14': 16, 'V15': 17, 'V16': 18, 'V17': 19, 'V18': 20, 'V19': 21, 'V20': 22, 'V21': 23, 'V22': 24, 'V23': 25, 'V24': 26, 'V25': 27, 'V26': 28, 'V27': 29, 'V28': 30, 'Amount': 31, 'Class': 32}) Row((True, 1.1277347803115845, 11324.0, -0.771922206096642, 1.0372219382153498, 2.4572781482912203, 1.25714214232368, 0.7401267829159109, 1.5633895345380902, 0.0272594824682699, 0.462302741062738, 0.646801259938004, -0.477165499498217, -0.157360996360568, -2.8256407839877404, 1.2489840990969, 1.06548431389713, -2.14252842021836, 1.46306681923631, -0.7469548357293171, 1.28538048636127, -0.594104292936294, -0.16363577677528302, -0.25458569802743103, -0.487168227191215, -0.496858142511257, -1.49475927732162, 0.4624705096572789, -0.0751739294145728, 0.0243492662262647, 0.0419242577705409, 0.0, 0), {'is_anomaly': 0, 'mean_squared_error': 1, 'Time': 2, 'V1': 3, 'V2': 4, 'V3': 5, 'V4': 6, 'V5': 7, 'V6': 8, 'V7': 9, 'V8': 10, 'V9': 11, 'V10': 12, 'V11': 13, 'V12': 14, 'V13': 15, 'V14': 16, 'V15': 17, 'V16': 18, 'V17': 19, 'V18': 20, 'V19': 21, 'V20': 22, 'V21': 23, 'V22': 24, 'V23': 25, 'V24': 26, 'V25': 27, 'V26': 28, 'V27': 29, 'V28': 30, 'Amount': 31, 'Class': 32}) Row((False, 0.5209542512893677, 11326.0, -0.574411634229421, 1.07725070302389, 2.9467491476484797, 2.5969459941663, -0.0720326673856722, 0.900387032215987, -0.13465908386494999, 0.303339076445236, 0.6172735032689489, -0.24573465583758602, 0.135332783259484, -2.55747640449406, 1.77267439212587, 0.975639760543272, -0.6253926311188001, -0.0877882456892866, 0.8690496751470821, -0.129551087520276, -0.546276532344251, -0.0562631465706766, -0.119433507645914, 0.0722151018660031, -0.125845264578888, 0.0247878874825628, -0.27074751075549697, 0.0256015811167135, 0.131289539649172, 0.11632168389483301, 0.0, 0), {'is_anomaly': 0, 'mean_squared_error': 1, 'Time': 2, 'V1': 3, 'V2': 4, 'V3': 5, 'V4': 6, 'V5': 7, 'V6': 8, 'V7': 9, 'V8': 10, 'V9': 11, 'V10': 12, 'V11': 13, 'V12': 14, 'V13': 15, 'V14': 16, 'V15': 17, 'V16': 18, 'V17': 19, 'V18': 20, 'V19': 21, 'V20': 22, 'V21': 23, 'V22': 24, 'V23': 25, 'V24': 26, 'V25': 27, 'V26': 28, 'V27': 29, 'V28': 30, 'Amount': 31, 'Class': 32}) Row((False, 0.5277307033538818, 11494.0, -1.1697438529655901, 0.462877516201437, 1.5875791232791698, -1.2525598943701, 1.01281739344023, -0.9184125358364649, 0.817632446548305, -0.5228039864503761, 1.38824716825758, -0.022449424083296, 2.19601645201901, -1.8245379583769097, 2.01864097036614, 1.0829956899864, -1.0514387886508298, 0.45441738029237705, -0.46823157631313006, 0.146580100118293, -0.34289260829876, 0.280455162260437, -0.418851207093186, -0.546607316105327, -0.0421438220210644, -0.0212689058377677, -0.283979160054203, 0.556660650458781, -0.191722193195158, -0.0717727232169664, 0.0, 0), {'is_anomaly': 0, 'mean_squared_error': 1, 'Time': 2, 'V1': 3, 'V2': 4, 'V3': 5, 'V4': 6, 'V5': 7, 'V6': 8, 'V7': 9, 'V8': 10, 'V9': 11, 'V10': 12, 'V11': 13, 'V12': 14, 'V13': 15, 'V14': 16, 'V15': 17, 'V16': 18, 'V17': 19, 'V18': 20, 'V19': 21, 'V20': 22, 'V21': 23, 'V22': 24, 'V23': 25, 'V24': 26, 'V25': 27, 'V26': 28, 'V27': 29, 'V28': 30, 'Amount': 31, 'Class': 32}) Row((False, 1.0725932121276855, 12182.0, -0.187467992562576, 0.8930387137058009, 2.44329736549183, 1.87002955798549, 0.184394113729195, 0.0840150514674256, 0.31589223639451397, -0.157057154770261, 1.08080097798804, -0.17555624005224002, -0.0763064321758883, -2.06280635500951, 2.6138829871046, 0.48815556657775, -1.9687917850785, 0.621044993024558, -0.187531271638917, 0.018082857093576, -1.71585640806577, -0.15043558423607, -0.0835217836708874, 0.346032092548478, 0.0967871987671432, 0.34143898772198, -1.2165402123676001, -0.471877105647678, 0.0747044821133455, 0.025352797238609, 0.0, 0), {'is_anomaly': 0, 'mean_squared_error': 1, 'Time': 2, 'V1': 3, 'V2': 4, 'V3': 5, 'V4': 6, 'V5': 7, 'V6': 8, 'V7': 9, 'V8': 10, 'V9': 11, 'V10': 12, 'V11': 13, 'V12': 14, 'V13': 15, 'V14': 16, 'V15': 17, 'V16': 18, 'V17': 19, 'V18': 20, 'V19': 21, 'V20': 22, 'V21': 23, 'V22': 24, 'V23': 25, 'V24': 26, 'V25': 27, 'V26': 28, 'V27': 29, 'V28': 30, 'Amount': 31, 'Class': 32}) Row((False, 0.6245824694633484, 12326.0, 1.16782609551007, 0.32734008046965, 0.9344057634259159, 2.10306374298121, -0.19133428787549103, 0.213219903664585, -0.274590398524983, -0.0214110814167713, 1.1154524964628998, -0.0387815684171643, 0.365188990863, -1.90322197705051, 2.20882128350945, 1.07865659544523, -1.37963587788655, -0.20325746699234803, 0.9389907917348841, -1.000001760121, -0.8859026145705641, -0.168706873840079, -0.23444512156927697, -0.16720758960889998, 0.000715856306109016, 0.109779689598291, 0.3524366906077, 0.9984174303293321, -0.0674632077242019, -0.00319298795074523, 0.0, 0), {'is_anomaly': 0, 'mean_squared_error': 1, 'Time': 2, 'V1': 3, 'V2': 4, 'V3': 5, 'V4': 6, 'V5': 7, 'V6': 8, 'V7': 9, 'V8': 10, 'V9': 11, 'V10': 12, 'V11': 13, 'V12': 14, 'V13': 15, 'V14': 16, 'V15': 17, 'V16': 18, 'V17': 19, 'V18': 20, 'V19': 21, 'V20': 22, 'V21': 23, 'V22': 24, 'V23': 25, 'V24': 26, 'V25': 27, 'V26': 28, 'V27': 29, 'V28': 30, 'Amount': 31, 'Class': 32}) Row((True, 1.48124098777771, 12522.0, -3.58219216453489, -3.1571865185877996, 2.21561341508991, 1.55726353107094, 4.719179791255581, -1.9324695969600503, -1.87064066743881, 0.438862496592758, 0.500591634965006, -0.0280641248076933, 1.98378299478933, -2.44682851254968, 0.0295800328105169, 1.8935724913787202, -1.70843800295856, -0.11539452781989601, 0.63377795236539, -0.6101034968488149, -1.7461300585117798, 0.7755323249625421, 0.174725659996541, -0.313322727938542, 0.40252178086876295, -0.338439984746558, 0.4654250468451071, 0.876260033706203, -0.39400952604780304, -0.13257073541082098, 0.0, 0), {'is_anomaly': 0, 'mean_squared_error': 1, 'Time': 2, 'V1': 3, 'V2': 4, 'V3': 5, 'V4': 6, 'V5': 7, 'V6': 8, 'V7': 9, 'V8': 10, 'V9': 11, 'V10': 12, 'V11': 13, 'V12': 14, 'V13': 15, 'V14': 16, 'V15': 17, 'V16': 18, 'V17': 19, 'V18': 20, 'V19': 21, 'V20': 22, 'V21': 23, 'V22': 24, 'V23': 25, 'V24': 26, 'V25': 27, 'V26': 28, 'V27': 29, 'V28': 30, 'Amount': 31, 'Class': 32})
まとめ
BigQuery MLの学習モデルを使用して、データセットの異常検知を実行する方法について解説しました。
テーブルデータに対してSQLクエリによって操作することで、モデルの学習から異常検知まで実行することができるので、機械学習に関する一連の操作を比較的簡単に扱うことができるところがメリットですね。
結果のデータをテーブルデータとして書き戻したり、BIツールと接続することによって、その後の活用の幅が広がりそうです。