Taste of Tech Topics

Taste of Tech Topics

Acroquest Technology株式会社のエンジニアが書く技術ブログ

Athenaでデータの格納形式ごとのクエリ実行性能を比較してみた

こんにちは、唄うエンジニア、miyajima です。

仕事の中でAmazon Athenaを利用する機会があったため、今回はそのAmazon Athenaを使った性能比較を試してみました。


Amazon Athena とは - Amazon Athena

Amazon Athena はS3に保存されているファイルのデータをSQL形式のクエリで直接検索することができるサービスです。
大量のデータを高速に分析、検索することができ、また一度テーブル定義すれば、あとはS3にファイルを置くだけで検索できるようになるのでデータの追加変更も容易です。またサーバレスですから費用は検索に使用した分だけとなり、運用コストも抑えられます。

Athenaサポートしているデータ形式は、以下のように多数用意されています。

  • 一般的なデータファイル形式: CSV, TSV, JSON
  • Hadoopの分散処理に適用した形式: Apache Avro, Apache Parquet, ORC
  • その他、 Apache WebServer、CloudTrail、Logstashのログ形式など

サポートされる SerDes とデータ形式 - Amazon Athena

またGZIPなどでの圧縮したデータも扱えます。

そのため、それぞれのデータ形式によって性能差がどのくらいあるのかは把握しておきたいところです。
また、AthenaはS3上の指定したパス上の全ファイルを1つのテーブルとして扱うため、ファイル数やファイルサイズがどのようにパフォーマンスに影響するのかも気になりますね。
ということで、いくつかの観点に沿って実行時間を調べてみました。

比較内容

今回は以下の観点にそって性能比較を行いました。

観点 内容
ファイルサイズ/ファイル数 データファイルの分割数を 1 / 10 / 100 / 1,000 / 10,000 にした5パターンのデータセットを用意し、それぞれの検索速度を比較
ファイル形式 同一の情報量のデータをCSVJSON、Parquet形式で用意し、検索速度を比較する。
圧縮/非圧縮 同一のデータをgzip圧縮した場合としない場合で比較。この時ファイルサイズによる影響も比較する。
検索方法 各ファイル形式に対して全検索、LIKE検索、列指定検索を比較する。

また、今回はデータ形式を主眼に比較を行っており、パーティションは指定していません。できれば別の機会に、パーティション指定の有無による性能比較も試したいと思います。

今回の調査にあたり使わせていただいたデータは、以下のページで公開されているWebサーバのログファイルです。

AIT Log Data Set V1.1 | Zenodo

この中から、Webサーバのアクセスログ(14万行)を複数のフォーマットに変換してS3に登録し、検索してみました。
mail.cup.com-access.log(抜粋)

192.168.10.190 - - [29/Feb/2020:00:00:02 +0000] "GET /login.php HTTP/1.1" 200 2532 "-" "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:73.0) Gecko/20100101 Firefox/73.0"
192.168.10.4 - - [29/Feb/2020:00:00:09 +0000] "POST /services/ajax.php/kronolith/listTopTags HTTP/1.1" 200 402 "http://mail.cup.com/kronolith/" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Ubuntu Chromium/77.0.3865.90 HeadlessChrome/77.0.3865.90 Safari/537.36"
192.168.10.190 - - [29/Feb/2020:00:00:12 +0000] "POST /login.php HTTP/1.1" 302 601 "http://mail.cup.com/login.php" "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:73.0) Gecko/20100101 Firefox/73.0"
192.168.10.190 - - [29/Feb/2020:00:00:13 +0000] "GET /services/portal/ HTTP/1.1" 200 7696 "http://mail.cup.com/login.php" "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:73.0) Gecko/20100101 Firefox/73.0"
192.168.10.190 - - [29/Feb/2020:00:00:14 +0000] "GET /themes/default/graphics/head-bg.png HTTP/1.1" 200 380 "http://mail.cup.com/themes/default/screen.css" "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:73.0) Gecko/20100101 Firefox/73.0"
192.168.10.190 - - [29/Feb/2020:00:00:14 +0000] "GET /themes/default/graphics/logo.png HTTP/1.1" 200 2607 "http://mail.cup.com/themes/default/screen.css" "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:73.0) Gecko/20100101 Firefox/73.0"
::1 - - [29/Feb/2020:00:00:18 +0000] "OPTIONS * HTTP/1.0" 200 110 "-" "Apache/2.4.25 (Debian) OpenSSL/1.0.2u (internal dummy connection)"
192.168.10.190 - - [29/Feb/2020:00:00:19 +0000] "GET /mnemo/ HTTP/1.1" 200 5681 "http://mail.cup.com/services/portal/" "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:73.0) Gecko/20100101 Firefox/73.0"
192.168.10.190 - - [29/Feb/2020:00:00:22 +0000] "GET /services/portal/ HTTP/1.1" 200 7053 "http://mail.cup.com/nag/list.php" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Ubuntu Chromium/77.0.3865.90 HeadlessChrome/77.0.3865.90 Safari/537.36"
192.168.10.190 - - [29/Feb/2020:00:00:26 +0000] "GET /mnemo/ HTTP/1.1" 200 5179 "http://mail.cup.com/services/portal/" "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Ubuntu Chromium/77.0.3865.90 HeadlessChrome/77.0.3865.90 Safari/537.36"
...

なお今回の性能比較の中で、全く同じ条件(データ形式、ファイルサイズ、ファイル数)でクエリ実行した場合でも、実行時間が平均値に対して20~40%程度のバラつきがありました。それもコールドスタートで発生するような「初回のみ遅い」という訳ではなく、連続で実行した時も途中の実行回だけ他よりも時間がかかる、というようなこともありました。
これは、AWS内部リソース割り当ての調整などで実行時間に差が発生しているのかもしれません。

これらのばらつきの影響を軽減するため、今回の測定では、5回測定した処理時間のうち最大値・最小値を除いた中央3値の平均を測定値とすることとしました(オリンピックでの芸術点の付け方と同じです)。

また、今回使用したデータは生の状態でも数百MB程度のものですが、GB、TB単位の大量のデータを扱う場合は、今回の誤差の影響は相対的に小さくなるのではないか、と思います。

比較結果

ファイルサイズ/ファイル数による比較

まず検証方法ですが、情報としては全く同じデータをJSONCSV、Parquetの形式で用意し、更にそのデータを1 / 10 / 100 / 1,000 / 10,000 のファイル数で分割したデータセットを作成します。
つまり、ここまでで以下の15パターンのデータセットが用意されました。

形式 ファイル数 1ファイルのデータ件数 1ファイルのサイズ(平均)
JSON
1
148,534
49,019.3 kB
JSON
10
14,854
4,901.9 kB
JSON
100
1,486
490.2 kB
JSON
1,000
149
49.0 kB
JSON
10,000
15
4.9 kB
CSV
1
148,534
37,124.2 kB
CSV
10
14,854
3,712.4 kB
CSV
100
1,486
371.2 kB
CSV
1,000
149
37.1 kB
CSV
10,000
15
3.7 kB
Parquet
1
148,534
-
Parquet
10
14,854
-
Parquet
100
1,486
-
Parquet
1,000
149
-
Parquet
10,000
15
-

※Parquetのデータはファイルを直接読み込んで得る形式ではなく、CSVデータをを元に変換しています。

そしてこれらのパターンそれぞれをAthenaの別々のテーブルに格納し、クエリを実行しました。

クエリ実行時間は、以下のようになりました。

ファイル分割数 1ファイルのデータ数 JSON CSV Parquet
1 148,534 2.17秒 2.19秒 1.90秒
10 14,854 2.15秒 2.10秒 1.83秒
100 1,486 2.11秒 2.34秒 1.89秒
1,000 149 2.03秒 2.65秒 2.26秒
10,000 15 3.09秒 3.04秒 2.49秒

ファイルサイズ・ファイル数での比較

今回は、そこまで影響は大きくはなかったですが、どの形式もサイズが小さいファイルが多くなると、処理時間が長くなる傾向が見えました。

圧縮/非圧縮の比較

今回はgzip圧縮した場合と非圧縮の場合、またファイル形式ははJSONCSVで試しました。
さらに、圧縮したデータの操作はファイルサイズや分割数にも影響する可能性があるため、ファイル分割数を上と同様にいくつかのパターンで実施しました。

ファイル分割数 1ファイル 100ファイル 10000ファイル
JSON:非圧縮 2.17秒 2.11秒 3.09秒
JSON:gzip 3.03秒 2.62秒 3.77秒
CSV:非圧縮 2.19秒 2.34秒 3.04秒
CSV:gzip 2.59秒 2.47秒 3.06秒

圧縮・非圧縮での比較

今回のデータではJSONCSVともに、検索にかかる時間はGZIP圧縮した方がわずかに長かったです。圧縮したデータを扱う方が効率は良くなるように感じますが、検証に用意出来たデータのサイズがそれほど大きくないため、あまりその効果が反映されなかったのかもしれません。

またデータ保存の観点では、データを圧縮した方がコスト効率がよいため、その点を踏まえて実際のデータで試した方がいでしょう。

ファイル形式と検索方法による比較

次はクエリによる比較です。 今回検証に使用したクエリは以下の3パターンです。

  • 全検索("SELECT * FROM <テーブル名>")
  • LIKE検索("SELECT * FROM <テーブル名> WHERE path LIKE '%.php%' ")
  • 列指定検索("SELECT host, path, status FROM <テーブル名>")

この3つのクエリで、JSON / CSV / Parquet の3種類の形式を、更にファイル分割数も替えて検索しました。

全検索

データ形式 1ファイル 100ファイル 10,000ファイル
JSON 2.17秒 2.11秒 3.09秒
CSV 2.19秒 2.34秒 3.04秒
Parquet 1.90秒 1.89秒 2.49秒

ファイル形式での比較(全検索)

LIKE検索

データ形式 1ファイル 100ファイル 10,000ファイル
JSON 1.82秒 1.53秒 2.69秒
CSV 1.94秒 1.64秒 2.71秒
Parquet 2.10秒 1.92秒 1.49秒

ファイル形式での比較(LIKE検索)

列指定検索

データ形式 1ファイル 100ファイル 10,000ファイル
JSON 1.80秒 1.54秒 2.60秒
CSV 1.46秒 1.35秒 2.59秒
Parquet 1.45秒 1.50秒 1.63秒

ファイル形式での比較(列指定検索)

CSVJSONは、どの検索方式でもファイル数が増えるにしたがって処理時間も増えているのが分かりますが、ParquetはLIKE検索、列検索の際、ファイル数が増えても処理時間が増えていません(というかLIKE検索は処理時間が減ってすらいます)。
Parquetは列指向データ形式といい、列単位でデータを取得、検索する際に最適化されたフォーマットです。この形式はデータの格納や検索を最適化するためのエンコーディングが複数用意されています。
エンコーディング方法の例:

エンコーディング方式 概要 効果の高いデータ列
Dictionary Encoding 出現する値を辞書に格納し、テーブルにはそのキーを格納する 出現する値の種類が限られているようなデータ
Run Length Encoding (RLE) 値と、その値を繰り返す回数のペアを格納する 同じ値が連続して出現するようなデータ
Delta Encoding 一定件数ごとに区切った中でのデータの前後の差分を抽出してその差分を保持する 一定のペースで変化する数値データ

参照: Encodings | Apache Parquet

そのため、これらの方式に適するデータを格納するようなスキーマを設計し、またそのカラムを検索、抽出の対象にできれば、よりParquetの特性を生かし、処理を高速化させる事が出来るのではないかと思います。

また今回はデータ量が数十MB程度しかありませんでしたので、それを強く実感できるほどではないのかもしれません。機会があれば、もっと多量のデータで検証してみたいと思います。

まとめ

今回の検証のまとめです。

  • 大量データを扱う場合は特に、処理時間、コストの両方の観点から、Parquet形式を積極的に使う。
  • ファイル数が細かく、多くなると、処理時間が長くなる傾向がある。
  • 今回のデータでは圧縮ファイルを使った場合の性能改善は見られなかった。更に大きなデータでの検証が必要。

それぞれで性能に差分は確認できましたが、より多くのデータ、複雑な条件になった時には、異なる傾向が見えるかもしれません。 機会があれば、別のデータセットで試してみたいと思います。

また、Athenaの性能改善を行う上で、AWS公式のパフォーマンスチューニングの情報は把握しておくとよいでしょう。

Amazon Athena のパフォーマンスチューニング Tips トップ 10 | Amazon Web Services ブログ

Acroquest Technologyでは、キャリア採用を行っています。
  • ディープラーニング等を使った自然言語/画像/音声/動画解析の研究開発
  • Elasticsearch等を使ったデータ収集/分析/可視化
  • マイクロサービス、DevOps、最新のOSSを利用する開発プロジェクト
  • 書籍・雑誌等の執筆や、社内外での技術の発信・共有によるエンジニアとしての成長
  少しでも上記に興味を持たれた方は、是非以下のページをご覧ください。 www.wantedly.com

エッジでLookout for Visionを実行すると爆速だった話

こんにちは、機械学習エンジニアの駿です。

先日庭の花壇にヒマワリの種をまいたのですが、早速芽が出てきました。
夏に黄色い花が咲くのが今から楽しみです。

今回はAWSの外観検査サービスである Amazon Lookout for Vision が、 AWS IoT Greengrass を使ってエッジでの推論が可能になったため、試してみました。

今までは Lookout for Visionクラウド側での推論(判定)しかできず、画像をネットワーク越しに送るため、オーバーヘッドが発生していました。
そのため、例えば工場の生産ラインで外観検査をしていたとすると、製品の撮影・検査をした後で、異常なモノを仕分けをしたりしますが、それに時間がかかってしまう、といった状況が発生することになってしまいます。

しかし、 Lookout for Vision のモデルを Greengrass でパッケージ化しエッジデバイスに配置して検査を行うことで、画像を送信するオーバーヘッドがなくなり、リアルタイムで処理ができるようになります。

Amazon Lookout for Vision

Lookout for Visionは画像が正常か異常を判定する、異常検知のサービスです。
教師画像は最低30枚と少ない枚数で始められますが、非常に高い精度で異常を判定することができます。

詳細については以下の記事があるので、そちらをご覧ください。

acro-engineer.hatenablog.com

AWS IoT Greengrass

Greengrass は Raspberry Piなどのエッジデバイス上にIoTアプリケーションを構築、デプロイ、管理するためのクラウドサービスです。
Lambda関数、Dockerコンテナなどをコンポーネントとしてパッケージ化し、エッジデバイスにデプロイ、実行することができます。

そんな Greengrass が2021年末に Lookout for Vision モデルのコンポーネントに対応し、
GPUを備えたエッジデバイスにデプロイすることで、クラウドに画像を送ることなく、オンプレミスかつリアルタイムの外観検査ができるようになりました。

構成

今回はエッジデバイスにJetsonNanoを使用し、 Greengrass サービスを通じて、 Lookout for Vision モデルコンポーネントと、 それを利用するための EdgeAgent コンポーネントをデプロイします。

今回の構成図(公式ドキュメントより)

手順

大まかな流れは下記のようになります。

  1. JetsonNano 上で Greengrass のサービスを起動する
  2. Lookout for Vision モデルを Greengrass 用にコンポーネント化する
  3. モデルコンポーネントAWSが提供している EdgeAgent を JetsonNano にデプロイする
  4. Python スクリプトからデプロイされたモデルを起動し、検査を実行する

早速、詳細の説明に入ります。

1. JetsonNano 上で Greengrass のサービスを起動する

公式ドキュメントでは動作環境としてJetson Xavierを推奨していますが、すぐに用意できなかったので、今回は JetsonNano を使ってみました。
ただし、性能を考慮した場合、実際の運用などでは Jetson Xavier などを利用するほうが安全だと思われます。

(1) JetsonNano に JetPack をインストール

エッジデバイス上で Lookout for Vision のモデルを使用するためには、GPUとCUDA、TensorRT などのライブラリが必要です。
JetPack を使うと、これらのライブラリをひとつひとつインストールする必要がなく、 既に整った環境を作ることができます。

JetPack4.5.1 のSDカードイメージをダウンロードし、公式ドキュメント に従ってセットアップを行います。

なお、Greengrass でデプロイする Lookout for Vision モデルが対応しているのが、バージョン4.4と4.5系のみのため、間違えて最新バージョンを入れないように注意が必要です。

(2) Greengrass サービスおよび Lookout for Vision モデルの起動に必要なライブラリのインストール

Greengrass サービスの起動に Java が必要になります。
今回はAWSが提供する Corretto をインストールしました。

また、Lookout for Vision のモデルの起動に Python3.8 もしくは 3.9 が必要です。
今回は 3.8 をインストールしました。

(3) クライアントアプリに必要なライブラリのインストール

公式ドキュメント に従って、 grpc をインストールし、サービス定義ファイルからクライアントインターフェイスを生成します。

また、画像読み込みのための Pillow もインストールします。

(4) Greengrass サービスのダウンロードおよび起動

Greengrass のコンソールから 「1つのCoreデバイスをセットアップ」を選択します。

「1つのCoreデバイスをセットアップ」

コアデバイス名など必要な情報を入力すると、インストーラのダウンロードコマンドおよび実行コマンドが生成されるので、エッジデバイス上でコマンドを実行します。

インストールが終わると、自動で Greengrass サービスの実行が始まります。

また、インストール中に Greengrass ユーザ ggc_user が作成されています。
そのままでは Greengrass でデプロイされたコンポーネントGPU にアクセスできないため、 ggc_user を video グループに追加します。

sudo usermod -a -G video ggc_user

Greengrass コンソールから追加したエッジデバイスが確認でき、ステータスが「正常」となっていたら Greengrass の準備は完了です。

JetsonNanoがCoreデバイスとして設定できた

(5) DLR のインストール

Lookout for Vision が libdlr.so を必要とするのですが、 pip でインストールできるDLRには .so ファイルが含まれていないようです。

ggc_user として whl からインストールすることで、 モデルコンポーネントが読み込めるようになります。
下記コマンドを ggc_user として実行してください。

curl -O https://neo-ai-dlr-release.s3-us-west-2.amazonaws.com/v1.10.0/jetpack4.5/dlr-1.10.0-py3-none-any.whl
python3.8 -m pip install dlr-1.10.0-py3-none-any.whl

2. Lookout for Vision モデルを Greengrass 用にコンポーネント化する

既に学習済みのモデルがあるものとします。

プロジェクトのページに遷移し左のメニューで「モデルのパッケージ」を選択します。
「モデルパッケージングジョブを作成」ボタンを押し、「モデルの選択」など必要な項目を埋めていきます。

「モデルパッケージングジョブを作成」

  • ターゲットハードウェア設定

    2022/05/07現在、プリセットの設定は Jetson Xavier 用しかないため、JetsonNano を使用する際は「ターゲットプラットフォーム」を選択して、設定を行います。

    項目
    オペレーティングシステム LINUX
    アーキテクチャ ARM64
    アクセラレーター NVIDIA
    コンパイラオプション {"gpu-code": "sm_53", "trt-ver": "7.1.3", "cuda-ver": "10.2"}

    コンパイラオプションは使用するデバイス、インストールしたライブラリのバージョンによって異なります。
    GPUコード、TensorRT バージョン、CUDA バージョンをそれぞれ指定します。
    上記値は JetsonNano+JetPack4.5.1の場合の値です。

    ターゲットハードウェアの設定

最後に「モデルパッケージングジョブを作成」ボタンを押して、パッケージングを開始します。
コンソール上で、「成功」ステータスになったら完了です。

Greengrass コンソールのコンポーネントページからも作成したモデルコンポーネントを確認することができます。

3. モデルコンポーネントAWSが提供している EdgeAgent を JetsonNano にデプロイする

Greengrass コンソールのデプロイページからデプロイを作成します。

今回はデプロイターゲットにコアデバイスを選択し、上で登録した JetsonNano にのみデプロイします。

コンポーネントの選択」画面で、パッケージした Lookout for Vision モデルコンポーネントを選択します。
このコンポーネントAWSが提供する aws.iot.lookoutvision.EdgeAgent に依存しているため、 そちらも自動でデプロイされます。

コンポーネントの選択」

そのほかの設定を変更する必要は必要ありません。
「デプロイ」を選択して、 JetsonNano にデプロイします。

デプロイのステータスが「完了」になったら成功です。

4. Pythonスクリプトからデプロイされたモデルを起動し、検査を実行する

エッジデバイス上で ggc_user としてログインします。

Pythonインタプリタを起動し、モデルの起動と検査を試してみます。

(1) モデルの起動

import grpc
from edge_agent_pb2_grpc import EdgeAgentStub
import edge_agent_pb2 as pb2
channel = grpc.insecure_channel("unix:///tmp/aws.iot.lookoutvision.EdgeAgent.sock")
stub = EdgeAgentStub(channel)
model_component_name = "lfv_component_aarm"

# まずはモデルが止まっていることを確認します。
model_description_response = stub.DescribeModel(pb2.DescribeModelRequest(model_component=model_component_name))
model_description_response.model_description.status == pb2.STOPPED
# -> True

# モデルを起動します
stub.StartModel(pb2.StartModelRequest(model_component=model_component_name))
# -> status: STARTING
# 起動するのを待ってから
model_description_response = stub.DescribeModel(ob2.DescribeModelRequest(model_component=model_component_name))
model_description_response.model_description.status == pb2.RUNNING
# -> True

(2) 検査実行

モデルが起動したら画像に対して検査を実行できます。

エッジデバイスに画像を用意して、Pillow で読み込んだものをモデルに送ります。

from PIL import Image

image = Image.open(image_path)
image = image.convert("RGB")
detect_anomalies_response = stub.DetectAnomalies(
    pb2.DetectAnomaliesRequest(
        model_component=model_component_name,
        bitmap=pb2.Bitmap(
            width=image.size[0],
            height=image.size[1],
            byte_data=bytes(image.tobytes())
        )
    )
)

is_anomalous = detect_anomalies_response.detect_anomaly_result.is_anomalous
confidence = detect_anomalies_response.detect_anomaly_result.confidence
print(f"Image is anomalous - {is_anomalous}")
print(f"confidence - {confidence:.2}")
# -> Image is anomalous - True
# -> confidence - 0.97

(3) モデルの停止

stub.StopModel(StopModelRequest(model_component=model_component_name))
model_description_response = stub.DescribeModel(ob2.DescribeModelRequest(model_component=model_component_name))
model_description_response.model_description.status == pb2.STOPPED
# -> True

channel.close()

結果

精度

前述の投稿で使用している Metal Nut 画像から正常を5枚、異常を5枚使って検査を実行したところ、すべて正しく判定することができました。

クラウド側で判定した場合と比較して、エッジデバイス上で判定しても同等の精度が出ることがわかりました。

実行時間

下記の関数を作り、画像1枚を推論するのにかかる時間を計測してみました。

def timeit():
    start_time = time.time()
    stub.DetectAnomalies(...)
    print(f"took {time.time() - start_time} seconds")

上記関数を10回実行した結果は下記のようになり、平均で0.25秒/枚で検査ができることになります。

# 所要時間(ms)
1 399.8
2 374.2
3 212.9
4 216.0
5 217.8
6 212.5
7 215.5
8 212.8
9 218.3
10 216.5
平均 249.6

awscli でクラウド側の Lookout for Vision モデルを使用した場合はネットワークの往復も含めて約3秒でした。

awscli の場合は画像変換の時間も含まれるため単純な比較はできませんが、エッジで実行することで10倍以上早くなっています。
Jetson Xavier などより計算力のあるデバイスを用いることで、さらにリアルタイム性のある外観検査ができるようになりますね。

オレゴンリージョンの Lookout for Vision を使用しているため、東京リージョンのものを用いるよりもさらに伝送時間がかかっていると思われます。)

料金

Greengrass を使ってエッジデバイスにデプロイした Lookout for Vision モデルを使用する場合、エッジ推論ユニットに基づいて月額料金がかかります。
1デバイス上で120検査/分までの検査は1エッジ推論ユニットとして扱われ、1エッジ推論ユニットは月100USD かかります。

個人で実施するにはちょっとお高めになっているので、工場など大規模で検査を行う必要があるユースケースを想定しているのがわかります。

まとめ

今回はGreengrassを使ってLookout for VisionのモデルをJetson Nanoにデプロイし、外観検査を行いました。
普段エッジデバイスを使うことがあまりないこともあり、最初の環境構築でバージョン不整合などでつまづいてしまいました。

実際に動かしてみて、エッジでの検査は画像をネットワーク越しに送信する必要がない分、10倍も早く実行できることがわかりました。
それでいてクラウドと同じモデルを使っているため、精度は同等です。

また、今回は Pythonインタプリタを使って検査を実行しましたが、クライアントアプリを作成して Greengrass コンポーネントとしてデプロイすることもできます。

今度は実際にカメラを繋いで、どれくらいのFPSが出せるのか、なども試してみたいです。

Acroquest Technologyでは、キャリア採用を行っています。
  • ディープラーニング等を使った自然言語/画像/音声/動画解析の研究開発
  • Elasticsearch等を使ったデータ収集/分析/可視化
  • マイクロサービス、DevOps、最新のOSSを利用する開発プロジェクト
  • 書籍・雑誌等の執筆や、社内外での技術の発信・共有によるエンジニアとしての成長
  少しでも上記に興味を持たれた方は、是非以下のページをご覧ください。 www.wantedly.com

モデル最適化ソフトウェアOpenVINOを用いた性能高速化とモデル比較の実験

皆さんこんにちは。
@tereka114です。

モデル最適化の選択肢の一つであるOpenVINOを試してみました。
モデル最適化とは、モデルの精度を殆ど落とさず、高速化する技術で、以下のような恩恵が得られることが知られています。

①特にGPU等を利用できない、RasberryPiのようなエッジデバイス上で機械学習モデルを動かすケースで性能を上げられる
②モデル最適化のエンジンが動く環境であれば、一度構築したモデルを複数の環境で実行させることができる

今回、その技術の一つであるOpenVINOとそれを用いた有名なモデルのベンチマークを紹介します。

OpenVINO

Intel社が開発したディープラーニングを高速に実行するためのソフトウェアです。
OpenVINOが学習済のモデルをハードウェアに合わせて最適化し、CPU、GPUなどのアクセラレータで高速で推論できるようにします。

www.intel.com

本記事では、インストールに関して詳細を扱いません。
インストール方法は次のリンク先を参考にしてください。

docs.openvino.ai

PyTorchからOpenVINOを動かす

PyTorchは実装のしやすさとPyTorch Image Models(timmライブラリ)を利用した学習モデルの多様性から私も含め、多くのデータサイエンティストが利用しています。
そのため、今回は、PyTorchで作られたモデルからOpenVINOを動かしてみます。

github.com

OpenVINOを利用する手順

PyTorchのモデルをOpenVINO形式に変換するには次のステップが必要になります。

1. PyTorchのモデルからONNX形式に変換する。
2. ONNXからOpenVINO形式に変換する。
3. OpenVINOモデルで推論する。

モデル変換・推論の流れ
PyTorchのモデルをONNXに変換する。

OpenVINOはPyTorchのモデルを直接変換できないため、まずはONNXに変換します。
PyTorchにONNX変換を行う関数が用意されているため、その関数を利用します。
ここで、モデルはevalを呼び出して推論モードにしておくことが必要です。
なぜならば、推論時の最適化を行う必要があるためDropoutやBatch Normalizationなど学習、推論で挙動が変わるものでは、期待する計算ができなくなります。

また、SwinTransformerには、ONNXに備わっていない演算があるため、その関数を外部からONNXに登録(roll関数)しています。

import torch
import torch.onnx as torch_onnx
import timm
import argparse
import torch
from torch.onnx.symbolic_helper import parse_args, _slice_helper
from sys import maxsize as maxsize


@parse_args('v', 'is', 'is')
def roll(g, input, shifts, dims):
    # Swin Transformerの計算に必要なOperatorを定義
    assert len(shifts) == len(dims)
    result = input
    for i in range(len(shifts)):
        shapes = []
        shape = _slice_helper(g, result, axes=[dims[i]], starts=[-shifts[i]], ends=[maxsize])
        shapes.append(shape)
        shape = _slice_helper(g, result, axes=[dims[i]], starts=[0], ends=[-shifts[i]])
        shapes.append(shape)
        result = g.op("Concat", *shapes, axis_i=dims[i])
    return result

parser = argparse.ArgumentParser()
parser.add_argument("--model")
parser.add_argument("--output")
parser.add_argument("--size", type=int)

args = parser.parse_args()

# モデルの読み込み
torch.onnx.symbolic_registry.register_op('roll', roll, '', version=9)
net = timm.create_model(args.model, pretrained=True)
net.eval()
# モデル出力のための設定
model_onnx_path = args.output # 出力するモデルのファイル名
input_names = ["input"] # データを入力する際の名称
output_names = ["output"] # 出力データを取り出す際の名称

# ダミーインプットの作成
input_shape = (3, args.size, args.size) # 入力データの形式
batch_size = 1 # 入力データのバッチサイズ
dummy_input = torch.randn(batch_size, *input_shape) # ダミーインプット生成

# 変換実行
if "swin" in args.model:
    # Swin Transformer用に、ONNXのOpsetを固定
    output = torch_onnx.export(
        net, dummy_input, model_onnx_path,export_params=True, 
        verbose=False, input_names=input_names, output_names=output_names, opset_version=11)
else:
    output = torch_onnx.export(
        net, dummy_input, model_onnx_path,export_params=True, 
        verbose=False, input_names=input_names, output_names=output_names)

この実装を次のコマンドで動かします。

python pytorch_to_onnx.py --model resnet50 --output resnet50.onnx --size 224
ONNXからOpenVINO形式に変換する。

ONNXからOpenVINOへの変換はOpenVINOのモデル最適化コマンドを実行するのみです。
前段のResNet50のモデルを利用して、変換する場合は以下のコマンドです。

python /opt/intel/openvino_2021/deployment_tools/model_optimizer/mo.py --input_model resnet50.onnx
OpenVINOモデルで推論する。

最後にOpenVINOを動作させます。
事前に以下のコマンドでOpenVINOのPythonモジュールをインストールします。

pip install openvino

以下、先程までコンパイルしたモデルの推論の実装です。
PyTorch、ONNX、OpenVINOの推論速度を比較する実装も含まれています

import numpy as np
import time as tm
import timm

import torch
import onnxruntime
from openvino.inference_engine import IECore

import argparse

parser = argparse.ArgumentParser()
parser.add_argument("--model")
parser.add_argument("--output")
parser.add_argument("--size", type=int)

args = parser.parse_args()
SIZE = int(args.size)

# Pytorchの準備
net = timm.create_model(args.model, pretrained=True)
net.eval()
# ONNXの準備
session = onnxruntime.InferenceSession(f"{args.output}.onnx")

# OpenVINOの準備
ie = IECore()
model_path = f'{args.output}.xml'
weight_path = f'{args.output}.bin'
net_openvino = ie.read_network(model=model_path, weights=weight_path)
exec_net = ie.load_network(network=net_openvino, device_name='CPU', num_requests=1)

# 時間計測用
time_onnx = 0
time_openvino = 0
time_pytorch = 0

# 予測結果比較用
out_onnx = []
out_pytorch = []
out_openvino = []

TIMES = 300
for i in range(TIMES):
    image = torch.rand(1, 3, SIZE, SIZE)
    with torch.no_grad():
        start_time = tm.time()
        out = net(image)
        out_pytorch.append(np.argmax(out[0]))
        time_pytorch += tm.time() - start_time

    start_time = tm.time()
    preds = session.run(["output"], {"input": image.cpu().numpy()})
    out_onnx.append(np.argmax(preds[0]))
    time_onnx += tm.time() - start_time

    start_time = tm.time()
    outputs = exec_net.infer(inputs={'input': image.cpu().numpy()})['output']
    out_openvino.append(np.argmax(outputs[0]))
    time_openvino += tm.time() - start_time

# 推論結果の整合性確認のため
print(np.sum(np.array(out_pytorch) == np.array(out_openvino)))
print(np.sum(np.array(out_pytorch) == np.array(out_onnx)))
print(np.sum(np.array(out_onnx) == np.array(out_openvino)))

# 計算結果
print('PyTorch: ', time_pytorch / TIMES)
print('ONNX: ', time_onnx / TIMES)
print('Open VINO: ', time_openvino / TIMES)

実行は次のコマンドです。

python infer.py --size 224 --model resnet50--output resnet50

性能実験

OpenVINOを利用すればどの程度高速化されるのか
画像認識の有名なモデルと先程の実装を用いて、性能を比較しました。

計測環境

現在一般的に利用されるモデルを中心に計測しました。
モデルの精度・性能の目安はPyTorchのモデル実装の宝庫であるtimmライブラリのリンクをご確認ください。

github.com

結果は次のとおりです。PyTorch(s),ONNX(s),OpenVINO(s)は1枚あたりの推論速度を示しています。
PyTorchと比較して、30-70%ほどの高速化を達成し、また、ONNXよりもほとんどの場合で高速化を達成できました。
また、本方式では、最終的な推論結果は変わりませんでした。

Model Image Size PyTorch(s) ONNX(s) OpenVINO(s) 高速化率(Pytorch) 高速化率(ONNX)
resnet50 224 0.271 0.137 0.112 58.67% 18.25%
resnet152 224 0.795 0.409 0.332 58.24% 18.83%
convnext_tiny 224 0.324 0.201 0.182 43.83% 9.45%
swin_tiny_patch4_window7_224 224 0.317 0.135 0.184 41.96% -36.30%
mobilenetv2_120d 224 0.065 0.031 0.028 56.92% 9.68%
mobilenetv3_large_100_miil 224 0.03 0.014 0.019 36.67% -35.71%
vit_tiny_patch16_224 224 0.088 0.053 0.057 35.23% -7.55%
vgg16 224 1.09 0.31 0.275 74.77% 11.29%
vgg19 224 1.309 0.388 0.339 74.10% 12.63%
tf_efficientnet_b0_ns 224 0.054 0.024 0.024 55.56% 0.00%
tf_efficientnet_b7_ns 224 0.448 0.216 0.18 59.82% 16.67%
モデル最適化

棒グラフはPyTorch(s)、ONNX(s)、OpenVINO(s)の値を表示しており、低い値であればよりよい性能であることを示しています。

最後に

CPUで処理を行った場合、OpenVINOの結果が最も早い場合が多かったです。
IoTデバイス上で動作させた場合に少し性能に満足できない場合にOpenVINOを適用すると良いかもしれません。
推論性能に困った場合の選択肢の一つに入れると良いと思います。

Acroquest Technologyでは、キャリア採用を行っています。


  • ディープラーニング等を使った自然言語/画像/音声/動画解析の研究開発
  • Elasticsearch等を使ったデータ収集/分析/可視化
  • マイクロサービス、DevOps、最新のOSSを利用する開発プロジェクト
  • 書籍・雑誌等の執筆や、社内外での技術の発信・共有によるエンジニアとしての成長

 
少しでも上記に興味を持たれた方は、是非以下のページをご覧ください。
Kaggle Grandmasterと話したいエンジニアWanted! - Acroquest Technology株式会社のデータサイエンティストの採用 - Wantedlywww.wantedly.com

Amazon PersonalizeのEC用レコメンダーを利用してみた

こんにちは、@shin0higuchiです😊

今回はAmazon Personalize を利用して、ECサイト向けのレコメンデーションを実現したいと思います。

皆さんも普段ECサイトを利用するかと思いますが、
おすすめされた商品を、つい買ってしまう方もいるのではないでしょうか。(私も、買うつもりが無かった物につい手を出してしまうことがあります...)

近頃のECサイトでは、ユーザーの年代や性別、商品の閲覧履歴などを踏まえて、ひとりひとりに最適化(パーソナライズ)された推薦(レコメンデーション)を導入することが多くあります。

ユーザーごとに的確な商品を推薦することは、売り上げの増加に直結しますが、適切なレコメンダーを構築するには相応の仕組みや知識が必要となり、ハードルが高いと言えます。通常は、学習のためのデータ加工やモデルの設計なども含め自分で実施したり、計算リソースの整備をしたりと、導入までの時間やコストが膨らみがちかと思います。
AWSAmazon Personalizeを利用することで、専門知識がなくとも簡単にレコメンダーを構築することが可能です。

2021年の末に、Amazon Personalizeの新機能としてEC向けに最適化されたレコメンダーがリリースされたのでそちらを試してみました。
aws.amazon.com




1. Amazon Personalize とは?

「Amazon Personalize」は、Amazon.com でも利用されているという、機械学習アルゴリズムが利用でき、手軽に高精度なレコメンデーションを実現できるサービスです。
前述のとおり、機械学習の専門知識がなくとも利用できるというのが大きな売りと言えるでしょう。
f:id:shin0higuchi:20220220235836p:plain:w600
f:id:shin0higuchi:20220220234114p:plain:w600

2. ECサイトレコメンダーの基本的な使い方

Amazon Personalize を利用する際の基本的な使い方の流れは次のようになります。

  1. データの準備
  2. S3へのデータ配置
  3. データセットの作成
  4. レコメンダーの作成
  5. 推論(レコメンド)の実行

データの準備

以下の3種類のデータが登録可能。
最低限 Interactions のデータがあればレコメンド可能ですが、より適切なレコメンドのためには3種類とも用意するのが良いと言えます。

No. 種別 概要 含まれるカラム例
1 Users ユーザのマスタ情報 ユーザID、任意のカテゴリ・数値データ(性別や年齢等)
2 Items 商品のマスタ情報 商品ID、任意のカテゴリ・数値(商品カテゴリや価格、登録日等)
3 Interactions ユーザの商品に対するアクションを記録したもの タイムスタンプや、アクションの種別

上記のデータは、CSV形式で用意します。
例えばInteractionsであれば、次のようなデータを用意することになります。

USER_ID,ITEM_ID,TIMESTAMP,EVENT_TYPE
1000,1,1645375967,view
1000,2,1645376900,view
1000,1,1645380267,purchase
2000,5,1645450267,view
...

上記のデータは、ユーザ1000はが商品1と2を閲覧したあとで商品1を購入しているのがわかりますね。
こういったデータを学習データとして用いてレコメンダーを構築していきます。

S3へのデータ配置

用意したCSVファイルはS3の任意のバケットに配置しておき、後述のPersonalize側の設定でパスを指定します。この時、Personalizeからアクセスできるようにバケットポリシーを設定します。

{
  "Version": "2012-10-17",
  "Id": "PersonalizeS3BucketAccessPolicy",
  "Statement": [ 
    { 
      "Sid": "PersonalizeS3BucketAccessPolicy",
      "Effect": "Allow", 
      "Principal": { 
        "Service": "personalize.amazonaws.com" 
      }, 
      "Action": [ "s3:GetObject", "s3:ListBucket" ], 
      "Resource": [ "arn:aws:s3:::bucket-name", "arn:aws:s3:::bucket-name/*" ] 
    }
  ] 
} 

データセットの作成

  • PersonalizeのメニューからDataset Groupを作成する。

f:id:shin0higuchi:20220304014233p:plain:w600

用意したCSVデータに合わせて、JSONスキーマを定義する。
f:id:shin0higuchi:20220313002736p:plain:w600

  • 必要に応じて、同様の手順でUser / Item のデータも登録する

f:id:shin0higuchi:20220313003219p:plain:w600

上記はS3から一括でデータを登録する方法ですが、以下のAPIからデータを登録することも可能です。
https://docs.aws.amazon.com/ja_jp/personalize/latest/dg/API_UBS_PutUsers.html
https://docs.aws.amazon.com/ja_jp/personalize/latest/dg/API_UBS_PutItems.html
https://docs.aws.amazon.com/ja_jp/personalize/latest/dg/API_UBS_PutEvents.html

レコメンダーの作成

これまでのAmazon Personalizeでは、ここから自前で「Solution」と「Campaign」を作成する必要がありました。(Solution≒「学習モデル」、Campaign≒「レコメンド実行APIのエンドポイント」と考えてもらえば良いかと思います。)

ですが、ECサイトドメインでは上記に相当する「レコメンダー」と呼ばれるものが用意されており、それを選択して有効化するのみで利用可能です。
f:id:shin0higuchi:20220313003605p:plain:w600

以下5種類のレコメンダーが用意されています。

  • Customers who viewed X also viewed(Xを見たお客様はこちらも見ています)
  • Frequently bought together(よく一緒に購入されている商品)
  • Best sellers(よく売れている商品)
  • Most viewed(よく閲覧されている商品)
  • Recommended for you(あなたへのオススメ)

いずれも実際に Amazon.com で利用されている内容のようで、実用的なものばかりですね。

推論(レコメンド)の実行

レコメンドの実行は REST API や、各種SDKが提供されています。たとえばPythonで呼び出す場合は以下のようになります。userIdが10のユーザに対して、5件のレコメンドを返す内容です。コメントアウトしていますが、フィルタを定義することで結果を絞って返すことも可能です。商品のカテゴリを絞ったり、特定の商品を除外したりする用途で利用できそうですね。

import boto3
client = boto3.client('personalize-runtime')
response = client.get_recommendations(
    userId='10',
    numResults=5,
#    filterArn='string',
#    filterValues={
#        'string': 'string'
#    },
    recommenderArn='作成されたレコメンダーのARN'
)

レスポンスは以下のように返ってきます。itemList というのが実際にレコメンドされた商品ですね。いとも簡単にレコメンダーが構築できました。

{
  "ResponseMetadata": {
    "RequestId": "22587508-868d-4683-b435-27fffe5db320",
    "HTTPStatusCode": 200,
    "HTTPHeaders": {
      "content-type": "application/json",
      "date": "Sat, 05 Mar 2022 10:33:53 GMT",
      "x-amzn-requestid": "22587508-868d-4683-b435-27fffe5db320",
      "content-length": "319",
      "connection": "keep-alive"
    },
    "RetryAttempts": 0
  },
  "itemList": [
    {
      "itemId": "210"
    },
    {
      "itemId": "582"
    },
    {
      "itemId": "535"
    },
    {
      "itemId": "93"
    },
    {
      "itemId": "30"
    }
  ],
  "recommendationId": "RID-d7fea433-9a85-45dd-868f-616e2f2d2870"
}

まとめ

Amazon Personalizeを利用することで、ECサイト向けのレコメンダーを構築する方法をご紹介しました。特にECサイトでの利用を想定して最適化されたモデルを、手軽にそのまま適用することができるのは大きな強みだと感じました。今回はデータ準備も含めて構築は1,2時間程度で済みました。全てゼロから実装したら検証も含め数週間~数ヶ月かかるのではないかと思います。

近頃のシステム開発の特徴として、時間をかけずに短期間で目に見える成果を求められるケースが多い印象があります。自前でゼロから構築すると時間やコストがかかるうえ、それに見合った効果が得られるかが見通しづらく、導入に踏み切るのは難しいと感じる方も多いのではないでしょうか。

ちなみに本記事では説明を割愛していますが、Amazon Personalizeは運用面でもかなり使いやすい設計になっています。レコメンドした後のユーザーの反応をフィードバックしてモデルを改善していくことも可能で、個人的にはそのメリットも非常に大きいと感じています。

精度の確認方法についてですが、従来の利用方法(レシピを自分で選択してSolutionと呼ばれるモデルを作る)では確認することができたのですが、ECサイト用のレコメンダーでは現状サポートされていないようです。現状ではレコメンド結果から自分で精度を算出する必要がありそうです。
#これまでも機能追加が頻繁におこなわれているので、そのうち実装されるのではないかと思っています。

また、料金体系は試される前に事前確認するようにしてください。(これまでの方式と異なりデータセット内のユニークユーザー数が料金に影響する点に注意です)
aws.amazon.com


Amazon.comでの実績をもとにECサイトに最適化されたレコメンダー。
是非導入を検討してみてはいかがでしょうか。


今回の記事は以上になります。最後までお読みいただきありがとうございました。


Acroquest Technologyでは、キャリア採用を行っています。

  • ディープラーニング等を使った自然言語/画像/音声/動画解析の研究開発
  • Elasticsearch等を使ったデータ収集/分析/可視化
  • マイクロサービス、DevOps、最新のOSSを利用する開発プロジェクト
  • 書籍・雑誌等の執筆や、社内外での技術の発信・共有によるエンジニアとしての成長

 
少しでも上記に興味を持たれた方は、是非以下のページをご覧ください。
Kaggle Grandmasterと話したいエンジニアWanted! - Acroquest Technology株式会社のデータサイエンティストの採用 - Wantedlywww.wantedly.com

AWS Lake Formationでデータレイク体験! #3 きめ細かな権限管理

機械学習アプリケーションエンジニアの@yktmです。

AWSで構築するデータレイクがテーマの本連載、今回は第3回目、最終回です。 前回は、AWS Lake Formationで構築したデータレイクとAmazon Athenaの連携を確認しました。今回は、Lake Formationの権限管理で、Athenaからのクエリにアクセスコントロールをかける部分を試していきます。

データレイクにはとにかくどんなデータも格納される、ということは誰がどのデータにアクセスできるか管理する必要がでてきます。 Lake Formationを使うと、データレイクにおけるアクセスコントロールを、要件に合わせ細かく設定することができます。

実際に権限管理を試してみると、確かに細かく権限管理できることが実感できました。他方、Lake Formationにおける権限管理の考え方が複雑なため、自分にとっては難しい部分もありました。 本記事では、実際に試してわかった、権限管理の具体的な設定方法、および権限管理の考え方をまとめてみたいと思います。

構成図

1. Lake Formationの権限管理

最初に、Lake Formationで、どのような権限管理ができるのか、簡単に見ていきます。

第1回の記事で書いたように、データレイクでは、事業・業務横断的にデータを管理するケースがあるかと思います。 データの中には、個人情報、業務上の秘匿情報など、自由にアクセスされたくないデータも混在していることも考えられます。 そのようなデータは、漏洩した際のリスクがあり、データレイク利用者すべてにアクセスを許可するのは非常にリスクが高くなります。

AWS Lake Formationは、データレイクに柔軟にきめ細かな「権限管理」を付与できることが特徴の一つです。

AWS Lake Formationは、

  • データベース、テーブルレベルのアクセス管理
  • 列、行、または行・列組み合わせによるセルレベルのアクセス管理

を提供しています。

例を通して、整理してみたいと思います。
「顧客マスタテーブル」と「販売実績テーブル」が存在し、セールスとアナリストがいる事業所を仮定します。

アナリストに、個々の顧客個人情報を見せないように、テーブルからデータを参照すること自体を禁止します。
また、販売時にサードパーティーの決済システムを利用していたとして、決済時に発行されるIDも必要ないでしょう。不要な列は、列レベルで参照を禁止します。
セールスには、担当地域の顧客情報は参照できても、それ以外の個人情報は参照させたくない。とすれば、担当販売地域のデータにのみアクセス可能にできます。

上記ケースのアクセスコントロール例を図示してみます。

アクセスコントロールの例

本記事では、テーブルレベルのアクセスコントロール、および列レベルのアクセスコントロールまでを扱っていきます。

2. 構築時のポイント

具体的な設定方法に移る前に、権限管理する際のポイントをまとめてみたいと思います。

ポイントは(1) IAM権限範囲(2) ペルソナ(3) 権限管理方式の3つあります。 それぞれ簡単に見ていきます。

(1) IAM権限範囲

Lake Formationは、データソースへのアクセスコントロールを行います。同様に、IAMもアクセスコントロールを実現するために利用されます。 したがって、それぞれ権限を細かく設定するか、粗く設定するか、組み合わせを考慮することが必要になります。

基本シナリオは、以下の2通りです。

方法 Lake Formation IAM
方法1(デフォルト) アクセスコントロールしない きめ細かにアクセスコントロールする
方法2(推奨) きめ細かにアクセスコントロールする 粗くアクセスコントロールする

このうち、AWSの推奨は「方法2」になります。 「方法1」は、IAM、S3バケットポリシーなど、複数のサービスにまたがり、複雑に権限管理する必要があります。その上、リソースの変更、追加時、全設定を見直す必要が出てきてしまいます。

ゆえに、「方法2」が推奨されています。しかし、Lake Formationのデフォルト設定は「方法1」になっています。
これは、AWS Glue データカタログの既存動作との互換性を保つためのようです。 したがって、Lake Formationの設定で、「方法2」に切り替える必要があります。 具体的な切り替え方法は後述します。

ここでの話は、以下のドキュメントにより詳細が書かれています。 docs.aws.amazon.com

(2) ペルソナ

どの属性のユーザに何のIAMポリシーを付与するか、その類型のことを、AWS Lake Formation ペルソナと呼びます。

例えば、データレイクの管理者や、データアナリスト、などです。 それぞれに、どの程度の権限を付与するか事前に設計することが重要になります。

以下、AWSが提案しているペルソナの例です。 docs.aws.amazon.com

(3) 権限管理方式

Lake Formationは2つの権限管理方式を提供しています。

名前付きリソースアクセスコントロールは、「ユーザAは、テーブルXにアクセス可能である」のように、ユーザごとに設定する権限管理方式です。
TBACは、「開発者タグを持つユーザは、テーブルXにアクセス可能である」のように、タグ付けによる権限管理方式です。

推奨されているのは、TBACです。理由は2つあります。

  • TBACでは管理する権限の数が、名前付きリソースメソッドよりも少なく済む
  • TBACでは、データソースに1つタグを付与し、各ユーザに対応するタグを付与すればよいです。
    つまり、「ユーザの数 + 管理対象データソース数」のみ管理するだけになります。
    他方、名前付きリソースアクセスコントロールでは、「ユーザの数 x 管理対象データソース数」となります。
  • TBACは、名前付きリソースメソッドよりもスケーラブル
  • リソースが増えた場合、名前付きリソースメソッドでは、全ユーザに対し新たに権限付与が必要になります。他方、TBACでは、リソースにタグを付与するだけで済みます。

    TBACに関するドキュメントは、以下になります。 docs.aws.amazon.com

    3. 権限管理してみる

    ここまで、権限管理に関する考え方を見てきました。ここからは、実際にLake Formationで権限管理していきたいと思います。

    まず、権限付与されていない状態でのクエリ実行を試します。権限管理されていない状態では、どのテーブルにもアクセスできない状態となります。

    次に、テーブルレベルのアクセスコントロールをかけていきます。この時、許可されたテーブルからデータを取得できるようになります。

    最後に、列レベルのアクセスコントロールを試していきたいと思います。許可された列のみ参照できるようにしていきます。

    権限付与されていない場合

    1回目の記事で作成した「データアナリスト」のIAMユーザでログインします。 Athenaコンソールを開き、salesテーブルからデータ取得するクエリを発行してみます。

    以下に添付するキャプチャは、クエリ発行の結果です。 ここから、①テーブルの候補が出てこないこと、②SELECT文の結果データを取得できていないことがわかります。

    権限付与されていないときのアクセスコントロール

    まずは、権限付与されていなければ、アクセスできないことがわかりました。

    権限管理用のタグ作成 (LF-Tag作成)

    「LF-Tag」とは、Lake Formationのタグベース権限管理で利用するタグのことです。 このタグをテーブルや列に付与することで、同じタグを持つユーザのみアクセスできる、といった具合にアクセスコントロールを実現することができます。

    タグベースでの権限管理方式については、権限管理方式で触れました。本記事では、LF-Tagを利用して、権限管理していきます。

    以下、roleというキーに対し、「leader」と「analyst」という2つの値をもつLF-Tagを作成していきます。

    「Add LF-Tag」を押下します。

    「key」と「value」に値を入れ、「Add LF-Tag」を押下します。

    LF-Tag作成後、一覧に作成したタグが表示されます。

    続いて、「データアナリスト」のIAMユーザに、「role=analyst」のタグを付与します。

    「Permissions」 > 「Data lake permissions」 > 「Grant」を選択し、権限付与画面に移動して以下のように設定します。

    テーブルレベルのアクセスコントロール

    続いて、許可されたテーブルにのみアクセスできることを確認していきます。 まず、「データレイク管理者」のIAMユーザで、Lake Formationコンソールを開きます。

    データベースへのアクセス権限付与

    少なくともデータベースに対してDESCRIBEを実行できなければ、テーブルの一覧も取得できません。したがって最初に、データベース自体へのアクセス権限を付与します。

    「Permissions」 > 「Data lake permissions」 > 「Grant」を選択し、権限付与画面に移動して以下のように設定します。

    この段階で、データアナリストはAthenaからデータベースを参照できるようになり、テーブルは参照できない状態になります。

    テーブルにLF-Tagを付与

    次に、roleがanalystのユーザに許可するテーブルを設定します。 「Data catalog」>「Table」から、アクセス許可するテーブルを選択し、「Actions」>「Edit LF-tags」からLF-tags編集画面に移動します。

    「role」タグの値を「analyst」に設定します。

    Athenaから権限設定結果を確認

    タグの設定が完了すると、データアナリストはAthenaからsalesテーブルを参照できるようになります。SELECT文を実行した結果が以下です。

    列レベルのアクセスコントロール

    ここまで、テーブルレベルでアクセスコントロールできることは確認できました。 次に、より細かなコントロールの例として、例レベルのアクセスコントロールをかけていきたいと思います。

    テーブルレベルのアクセスコントロールと同様に、LF-Tagを使って、列レベルのアクセスコントロールを設定していきます。

    テーブルの権限を「leader」に変更。

    「Tables」 > 「テーブル名」 > 「Edit schema」を選択します。

    アクセス許可する列に「analyst」のタグを付与

    analystが参照可能とする列を選択し、「Edit tags」を選択します。

    roleをanalystに変更し、「Save」します。

    Athenaから権限設定結果を確認

    列単位での設定は以上になります。データアナリストIAMユーザでログインし、Athenaコンソールを開くと、指定した列のみが参照できるようになっています。 SELECT文でも、許可された列のみが参照できるようになっています。

    5. 最後に

    AWS Lake Formationを試してみて、LF-Tagを使った設定はかなり簡単で、柔軟に設定できるなと感じました。

    ただ、気を付けておきたい点として、権限を付与、制限する要素それぞれの、「役割・作用」の理解が重要なのだと思いました。

    データレイクにおいて、過剰なアクセスコントロールは使い勝手を著しく損ない、「データスワンプ」になってしまうと言われています。 過剰な権限管理を避けながらも、スケーラブルかつ、安全にアクセスコントロールを設計するのは、エンジニアとしての力量が試されるな、と感じます。

    ここまで全三回に渡りLake Formationを使ったデータレイク構築を試してみた感想として、 Lake Formationは難しい部分はあれど、Lake Formationなしで同じことはしたくない、と感じました。

    ユーザごとの権限管理にせよ、テーブル・列レベルのアクセスコントロールにせよ、Lake Formationなしで構築することは技術的には可能と思います。 しかし、Lake Formationを使う方が簡単にかつ一元的に管理できるのは間違いなく、 Lake Formationの謳い文句である、「安全なデータレイクを数日で簡単にセットアップできる」も、その通りだった、と感じました。

    本連載では、Lake Formationの基本的な使い方と、権限管理に注目してきましたが、AWS Lake Formationには魅力的な機能がまだまだ備わっています。 例えば、行、セルレベルの権限管理、Governed Tableが提供するACID トランザクションのサポートなどです。

    今後、AWS Lake Formationの他の機能も試していければと思います。

    Acroquest Technologyでは、キャリア採用を行っています。
    • ディープラーニング等を使った自然言語/画像/音声/動画解析の研究開発
    • Elasticsearch等を使ったデータ収集/分析/可視化
    • マイクロサービス、DevOps、最新のOSSを利用する開発プロジェクト
    • 書籍・雑誌等の執筆や、社内外での技術の発信・共有によるエンジニアとしての成長
      少しでも上記に興味を持たれた方は、是非以下のページをご覧ください。 www.wantedly.com

    AWS Lake Formationでデータレイク体験! #2 Athenaで簡単データ連携

    機械学習アプリケーションエンジニアの@yktmです。

    AWSで構築するデータレイクがテーマの連載、今回は第2回目です。 前回は、AWS Lake Formationの概要と基本となるデータレイクの構築を確認しました。今回は、Amazon Athenaを利用し、前回構築したデータレイクから実際にクエリでデータを取得する部分を試していきます。

    AthenaはS3からSQLで簡単にデータを取得できることが特徴のサービスですが、実際使うと本当に手軽に扱うことができました。 その一方、気を付けておきたいポイントも分かったので、その点を踏まえ、まとめていきたいと思います。

    本記事で扱うのは、赤枠部分です。

    構成図

    1. Athenaとは

    まずはAthenaについて、その概要を見ていきます。

    Athenaは、SQLで、S3内のデータを集計・分析できるサービスです。

    Athenaの特徴は、主に以下4点です。

  • サーバレスで、インフラの管理、設定は不要
  • ソフトウェアの更新や、インフラのスケーリングは自動で行われるため、考える必要はないです。純粋にデータをどう取得、加工するかに集中できます。
  • ハイパフォーマンス
  • 高速にS3からデータを取得、操作できるようにパフォーマンスが最適化されています。クエリの並列実行も自動で行われるため、大規模なデータも、クエリ結果が短時間で取得できます。
  • AWSサービスとの統合、連携
  • Athenaは、AWS Glueと統合されており、Glueのクロール機能やETL機能と組み合わせることができます。また、フェデレーティッド SQLを利用すれば、DynamoDB、Redshift、CloudWatchなど複数のデータソースに対し分析することができます。
  • 機械学習の利用
  • Athena SQL クエリで SageMaker 機械学習モデルを呼び出すことができます。機械学習の推論により、異常検知や予測などの分析が可能になります。

    2. 利用方法

    ここからは、実際にAthenaを利用して、前回構築したデータレイクからデータを取得していきます。 前回の記事で作成した、「データレイク管理者」のIAMユーザで操作していきます。

    (1) クエリ結果保存用バケット作成

    Athenaからクエリを発行すると、その結果は自動的にS3に保存されます。 まずは、クエリ結果保存用のバケットをS3に作成しておきます。

    次にAthenaコンソールを開き、「設定」 > 「管理」から、「クエリ結果の設定」で、クエリ結果の保存先バケットを指定します。

    (2) SELECT文でデータ取得する。

    Athenaのクエリエディターを開くと、前回の記事で作成したテーブルの一覧が表示されていることが確認できます。

    なお、今回は前回の記事でGlueで作成したテーブルにクエリを発行しています。S3にcsvファイルを配置しただけでは、Athenaから参照できないのでご注意ください。

    続いて、SELECT文でsalesテーブルのデータを取得してみます。

    SELECT * FROM "sales" WHERE shop_id = 25 LIMIT 10; をクエリとして入力し、「実行」を押すとクエリ結果が表示されます。

    LIKE句なども、もちろん利用可能です。

    2014年のデータのみに絞り込む場合、SELECT * FROM "sales" WHERE date LIKE '%.2014' LIMIT 10;というクエリで取得できます。

    以下のドキュメントで、クエリに関して詳細に説明されています。

    docs.aws.amazon.com

    3. Athenaを利用する上での注意点

    Athenaは、簡単にS3上のデータを分析することができますが、他方、意図しない超過課金が発生するリスクもあります。データを大量に読み込ませないために、最低限気を付けておきたいポイントを簡単にまとめていきます。

    ワークグループによる制限

    Athenaには、「ワークグループ」という機能があります。

    このワークグループを利用することで、クエリ実行時のスキャンの最大量を設定できます。 スキャン最大量を超えるクエリが実行された場合、クエリはキャンセルされます。

    検証時など、意図しない大量データスキャンを避けるためにも、この設定をしておくことで安心できると思います。 ワークグループ作成時、もしくはワークグループの編集画面より、制限を設定できます。

    ワークグループは、スキャンの最大量制限のほかにも、

    • クエリを実行できるユーザーを管理
    • ワークグループ毎のメトリクス取得
    • ワークグループ毎のコスト管理

    にも利用できます。

    読み込みカラムの指定

    SELECT文でクエリを発行する際、読み込み対象のカラムを明示的に指定することが推奨されています。 特に、大量のカラムがあるテーブルや、文字列ベースのカラムが多い場合に有効なアプローチです。

    Parquet または ORC を利用する。

    AthenaはCSV、TSV、JSONなどのデータ形式のほか、Apache ParquetやLogstash、CloudTrailなどの形式をサポートしています。(サポートしているデータ形式)

    この中でも、Parquet・ORCを利用することが、よいとされています。 Parquetは、Hadoopで利用される列指向のデータ形式です。ORCは、Hiveで利用される列指向のデータ形式です。
    これらのデータ形式を利用するメリットは、2つあります。
    一つは、parquet形式にすることで、単純にデータ量を圧縮できることが挙げられます。 一例として、1TBのCSVファイルも、parquet形式にすることで、130 GBまで削減することができるとされています。

    databricks.com

    データ量を削減できることで、S3の利用コストを削減することができます。

    二つ目の理由として、列指向形式に変換することで、クエリ実行時のスキャンデータ量の削減が期待できる点が挙げられます。 列指向形式のデータにすることで、必要な列のみが選択的に読み込まれ、不要なデータのスキャンを避けることができるため、 コスト削減、パフォーマンス向上に寄与します。

    参考資料

    本記事では、よく使いそうなものに絞って、ベストプラクティス、チューニング方法を挙げてみました。 より詳しい最適化、チューニング方法は、以下のAWSが出している資料に載っています。

    aws.amazon.com

    www.slideshare.net

    4. コスト

    Athenaで発生する料金は「クエリ」に対して発生します。

    クエリ実行時のスキャンデータ量 1TBにつき、5USDが発生します。(リージョンによって、5USD以上の料金になります。)

    上記に加え、S3、Glue、Lambda、Sage Makerなど他サービスを利用する場合、それぞれの料金体系に応じた料金が発生します。 特にS3については、Athenaクエリ結果をS3に保存するため、転送量、リージョンには気を付けておきたい所かと思います。

    5. まとめ

    Athenaを利用するのは初めてでしたが、簡単にSQLでS3のデータを取得、分析できる点は便利だと感じました。

    データ分析をするとき、pythonでPandasを利用することが多いと思います。しかし、Pandasはパッケージとしてのサイズが大きく、サーバレス案件でAWS Lambda xPandasの組み合わせを採用すると、Lambdaのデプロイパッケージに関するサービスクォーターに達しないように意識する必要が出てきます。

    Lambda Layerに載せきれないといった時の代替手段として、Athenaを利用する手もあるんじゃないかなと思いました。 また、数百MB ~ 数GB程度の小~中規模程度のデータの検証、分析であれば、AWSコンソール上で手軽にクエリを実行できる点も魅力だと思います。

    他方、数十GB~数TB級の大規模データを分析する際は、綿密にチューニングしないと、コストとパフォーマンスの面で負担がかかってしまう点は注意が必要だと思いました。

    本記事では、Athena を使って、Lake Formationで構築したデータレイクからデータを取得するところまでを試してみました。 次の記事で、Lake Formationの権限管理で、Athenaからのクエリをアクセスコントロールしていきます。

    Acroquest Technologyでは、キャリア採用を行っています。
    • ディープラーニング等を使った自然言語/画像/音声/動画解析の研究開発
    • Elasticsearch等を使ったデータ収集/分析/可視化
    • マイクロサービス、DevOps、最新のOSSを利用する開発プロジェクト
    • 書籍・雑誌等の執筆や、社内外での技術の発信・共有によるエンジニアとしての成長
      少しでも上記に興味を持たれた方は、是非以下のページをご覧ください。 www.wantedly.com

    AWS Lake Formationでデータレイク体験! #1 何がうれしいのか?

    機械学習アプリケーションエンジニアの@yktmです。

    最近、実際の仕事でも、データレイクに関する話を耳にするようになってきました。私自身、あまり経験がない分野だったので、「安全なデータレイクを数日で簡単にセットアップできる」という、うたい文句にひかれて、今回、AWS Lake Formation + Athena による、簡易なデータレイク構築を試してみました。 実際に構築をしてみたところ、つまづいたポイントなどもいくつかあり、特にIAMの扱いや権限の考え方などは重要だと感じました。そこで、実際に構築してみた流れや、つまづいたポイントなどを中心に、記事にまとめていきたいと思います。

    ひとつの記事では、まとまらない量になってしまったので、3回にわたって連載していきます。

    各記事で取り扱うテーマは次の通りです。

    3回の記事を通して、以下の構成で構築していきます。 本記事では、赤枠部分の構築をしていきます。

    構成図

    初回の本記事では、データレイク、およびLake Formationの概要と、Lake Formationの基本的な構築を実施していきます。

    1. データレイクとは

    データレイクとは、様々な形式、かつ、大規模なデータを保存し、一元管理するための仕組みです。 同様に、大量のデータを保存・管理する仕組みとして、従来からデータウェアハウスがありましたが、以下のような違いがあります。

    特徴 データウェアハウス データレイク
    データタイプ 構造化データ すべての構造化・非構造化データ
    データ 分析用に加工したデータ 加工前データ
    分析用途 BI / データビジュアライゼーション 機械学習 / 予測分析
    関連AWSサービス Amazon RedShift S3, Lake Formation

    aws.amazon.com

    2. Lake Formationでできること

    Lake Formationの特徴は以下の3点です。

  • 簡易でスピーディな構築
  • データレイクを構築する際、ストレージの準備、データ取り込み方法、データ整形、セキュリティ、分析、モニタリング、監査など、横断的に検討することがあります。
    そのため、データレイクを一から構築するとなると、それぞれの技術要素に深い理解が求められ、数か月単位で時間・コストが必要とも言われています。Lake Formationでは、そうした障壁なく、短時間でデータレイクを構築することが可能です。
  • きめ細かな権限管理
  • データレイクを利用する際、データのサイロ化が起きないように、事業・業務横断的に、データを管理するケースがあります。
    その際、個人情報などは許可された役割の人にのみアクセス可能にする等の対応が必要になります。Lake Formationでは、データソースレベルだけでなく、行・列レベルできめ細かに権限設定できます。
  • データ取り込みの自動化
  • ブループリントと呼ばれるテンプレートにより、データベースやログのデータの取り込みが容易に設定できます。

    3. データレイク構築

    ここからは、実際にLake Formationでデータレイクを構築していきたいと思います。以下の流れで、データレイクを構築していきます。

    (1) データ準備

    利用データのダウンロード

    本記事では、AI・データサイエンスのコンペティションであるKaggleで公開されているデータを利用します。

    www.kaggle.com

    利用するファイルと、データの内容は、以下のようになっています。

    ファイル名 説明
    sales_train.csv 2013年1月から2015年10月までの日別販売トランザクションデータ
    items.csv 商品名のマスタデータ
    item_categories.csv 商品カテゴリのマスタデータ
    shops.csv 店舗情報のマスタデータ

    S3へのデータアップロード

    取得したCSVファイルを、S3にアップロードします。

    sales_train.csvをsales.csvにリネームし、以下のディレクトリ構成でS3にアップロードします。

    /
    ├ sales/
    |  └ sales.csv/
    ├ items/
    |  └ items.csv/
    ├ item_categories/
    |  └ item_categories.csv/
    └ shops/
        └ shops.csv/

    (2) IAMユーザの準備

    IAM管理者

    こちらは、Lake Formationにおける、ペルソナの考えに従い、AdministratorAccess権限を持つユーザを想定しています。 AdministratorAccess権限は、ルートユーザ同等の権限となるので、扱いには注意が必要です。

    docs.aws.amazon.com

    ワークフロー用ロール

    まず、Lake Formationがデータ自動収集するための、Workflow用にロールを作成していきます。 このWorkflowで収集されたデータは、データカタログという形で整理され、Athenaなどの分析ツールから参照するために使われます。

    この機構はAWS Glueに依拠するもので、実際Lake Formationの裏ではAWS Glueが動きます。

    こちらのロールは、公式ドキュメントに沿って、設定します。 ロールの名前は「LakeFormationWorkflowRole」とします。

    docs.aws.amazon.com

    ここで注意が必要な点として、S3バケットへのアクセス権限を付与する必要があるケースがあるということです。 なぜならば、AWSGlueServiceRoleには、特定のprefixを持つ、S3バケットへのアクセスのみ許可されているためです。

    S3バケットを読み取る権限がないと、Glueでテーブル作成時に以下のようなエラーが発生します。

    今回はインラインポリシーでバケットを読み取る権限を付与します。

    {
        "Version": "2012-10-17",
        "Statement": [
            {
                "Effect": "Allow",
                "Action": [
                    "s3:GetObject"
                ],
                "Resource": [
                    "arn:aws:s3:::{BUCKET_NAME}",
                    "arn:aws:s3:::{BUCKET_NAME}/*"
                ]
            }
        ]
    }

    データレイク管理者

    データレイク管理者は、AWSLakeFormationDataAdmin の権限が必須です。
    次の権限は、オプションとなります。

    • AWSGlueConsoleFullAccess
    • CloudWatchLogsReadOnlyAccess
    • AWSLakeFormationCrossAccountManager

    本記事では、オプションの権限も付与しておきます。

    次に、「datalake-admin-servicelinkrole-create」という名前で、インラインポリシーとしてサービスリンクロール作成権限と、ロール作成権限を付与します。

    {
        "Version": "2012-10-17",
        "Statement": [
            {
                "Effect": "Allow",
                "Action": [
                    "iam:CreateServiceLinkedRole",
                    "iam:PutRolePolicy"
                ],
                "Resource": "*"
            }
        ]
    }

    後述する、Data lake locationsの設定で、AWSServiceRoleForLakeFormationDataAccessというLake FormationのサービスリンクロールをIAMユーザに付与することになります。(ドキュメント)

    iam:CreateServiceLinkedRoleiam:PutRolePolicyが付与されていないと、以下のような権限エラーが発生します。

    最後に、データレイク管理者がWorkflowを利用するため、Pass Roleをインラインポリシーとして付与します。

    {
        "Version": "2012-10-17",
        "Statement": [
            {
                "Sid": "PassRolePermissions",
                "Effect": "Allow",
                "Action": [
                    "iam:PassRole"
                ],
                "Resource": [
                    "arn:aws:iam::<account_id>:role/LakeFormationWorkflowRole"
                ]
            }
        ]
    }

    最終的に付与した権限はこちらです。

    データレイク管理者のIAM

    ここでは、簡単に記載するため、FullAccessのポリシーを利用していますが、実際には要件に応じて適切な権限に絞ってください。

    データアナリスト

    データアナリスト向けには、Amazon Athena クエリを実行する権限(ここでは、AmazonAthenaFullAccess)を付与します。

    注意が必要な点として、Athenaクエリを実行する際、クエリの結果を保存するS3への書き込み権限が必要になります。 S3への書き込み権限がないままAthenaクエリを実行すると、以下のようなエラーが発生します。

    S3への書き込み権限不足によるエラー

    なので、インラインポリシーでAthena クエリ結果保存用S3への書き込み権限を付与します。
    次のドキュメントを参考に、権限を付与していきます。
    Athena クエリの実行時の「Access Denied (アクセスが拒否されました) 」エラーを解決する

    本記事では、「write-athena-query-result」という名前で、以下のインラインポリシーを付与します。

    {
        "Version": "2012-10-17",
        "Statement": [
            {
                "Effect": "Allow",
                "Action": [
                    "s3:GetBucketLocation",
                    "s3:GetObject",
                    "s3:ListBucket",
                    "s3:ListBucketMultipartUploads",
                    "s3:AbortMultipartUpload",
                    "s3:PutObject",
                    "s3:ListMultipartUploadParts"
                ],
                "Resource": [
                    "arn:aws:s3:::{BUCKET_NAME}",
                    "arn:aws:s3:::{BUCKET_NAME}/*"
                ]
            }
        ]
    }

    データアナリストに付与するIAMは、最終的にこうなりました。

    データアナリスト向けのIAM

    プロジェクトリーダー

    データアナリストよりは広い権限を持ち、データレイク管理者よりは狭い権限を持つユーザを想定して作成します。

    以下のようなポリシーを付与します。

    プロジェクトリーダー向けのIAM

    また、このユーザは、データレイク管理者としては設定せずにおきます。

    (3) Lake Formation構築

    データレイク管理者登録

    ここでの作業は、「IAM管理者 (AdministratorAccessを持つユーザ)」で行います。 前述したように、AdministratorAccess権限はルートユーザ同等の権限となるので、扱いには注意が必要です。

    先ほど作成した、データレイク管理IAMユーザをLake Formation上で、管理者として設定します。

    作成したデータレイク管理IAMユーザをLake Formation上で、データレイク管理者として割り当てます。 Lake Formationを初めて開くと、以下のようなダイアログが出てくるので、 「Add other AWS users or roles」を選択し、データレイク管理者とするIAMユーザを選択します。

    データレイク管理者を設定

    データレイク管理者設定は、「AdministratorAccess 」の権限を持つユーザによって実施可能です。 しかし、次の2つの権限を付与すれば、「AdministratorAccess 」の権限を持つユーザ以外でも、データレイク管理者設定可能です。

    • lakeformation:GetDataLakeSettings
    • lakeformation:PutDataLakeSettings

    ここでポイントになるのは、Lake Formationでは、「暗黙的なアクセス許可」が存在することです。 例えば、「AdministratorAccess 」は、「lakeformation:GetDataLakeSettings」と 「lakeformation:PutDataLakeSettings」の権限が暗黙的に付与されています。

    このほかにも、データレイク管理者・データベース作成者・テーブル作成者にも、それぞれ暗黙的なアクセス許可が付与されています。データレイク構築時、この点を押さえておくことは重要になりそうです。

    docs.aws.amazon.com

    尚、データレイク管理者IAMユーザでログインし、データレイク管理者に自身を追加しようとすると、権限エラーが発生します。最初、「AWSLakeFormationDataAdmin」を付与したIAMユーザで、自身をデータレイク管理者に設定しようとしたところ、User: arn:aws:iam::xxxx:user/admin is not authorized to perform: lakeformation:PutDataLakeSettings with an explicit denyのエラーが発生しました。

    また、注記として、「IAM管理ユーザ (AdministratorAccess AWS 管理ポリシーを持つユーザ) をデータレイク管理者に選択しないことを推奨します。」と公式ドキュメントに記載されています。

    データカタログ設定

    ここでの作業は、IAM管理者で実施します。

    前述のとおり、Lake Formationは、デフォルトでは、「IAMによるアクセスコントロール」のみが適用されるようになっています。 つまり、Lake Formation上での権限管理は、反映されない設定がデフォルトになっているということです。

    これは、Lake FormationはAWS Glueに依拠しており、既存のAWS Glueの挙動との互換性を取るためとされています。

    しかし、Lake Formationでは、「Lake Formation のアクセス許可」によりきめ細かなアクセスコントロールを取ることが推奨されています。 Lake Formationのアクセス許可を有効化するために、「Data catalog settings」から、IAMによるアクセスコントロールのみ適用される設定を変更します。

    データカタログに対する権限管理にLake Formation設定が反映されるように変更する。

    この設定に関して、Classmethod様が詳細に解説しています。 AWS Lake Formationにおけるデータカタログ設定の意味について | DevelopersIO

    データカタログ権限設定

    ここでの作業は、データレイク管理者で実施します。

    Athenaからクエリで参照するための、データカタログの設定をしていきます。 データカタログとは、平たく言えばデータレイク保存されているデータを、分析するために整理されたものです。

    前提として、Lake Formationは、AWS Glueがバックエンドとして動くため、クローラ、ETLなど、Glue の機能をもとにしています。 データカタログの作り方等は、AWS Glue寄りの話題となるので、本記記事での説明は割愛します。

    docs.aws.amazon.com

    S3パスの登録

    Lake Formationコンソールから、「Register and ingest」> 「Data lake locations」>「Register location」を選びます。 次に、データレイクに登録するS3 Pathを指定し、「Register location」を押下します。 ここでの操作が意味するのは、「Data lake locations」で設定されたS3バケットは、Lake Formationの権限管理配下に入ったということです。

    ここで、データレイク管理者にサービスリンクロール作成権限、ポリシー作成権限がないと、以下のようにエラーが発生します。

    権限不足による、S3パス登録の失敗

    データベース作成

    続いて、データベースを作成します。

    データベース・テーブル権限設定

    続いて、データベース・テーブルの権限設定に移ります。

    今回、Glueを利用してテーブルを作成するので、LakeFormationWorkflowRoleがテーブル作成できるように権限付与します。

    データロケーション登録

    「Permissions」>「Data locations」>「Grant」を選びます。 今回は、クロスアカウントではなく、同一アカウントの操作なので、「My account」 「IAM users and roles」で、ワークフロー用ロール(LakeFormationWorkflowRole)を指定します。

    ここでの操作が意味するのは、指定されたS3パスに対し、指定されたユーザ・ロールがデータベース作成権限を得たということです。

    一つ前で設定した、「Data lake locations」と「Data locations」の考え方がややこしいと感じました。
    「Data lake locations」と「Data locations」が、各IAMユーザにどのように作用するか、まとめてみます。

    バケットA、Bという二つのS3バケットが存在するとし、IAMユーザは本記事で作成している3つです。

  • データレイク管理者
  • AWSLakeFormationDataAdmin権限を持ち、かつデータレイク管理者に登録されている
  • プロジェクトリーダー(PL)
  • AWSLakeFormationDataAdmin権限を持っているが、データレイク管理者に登録されていない
  • データアナリスト(アナリスト)
  • Athenaへのアクセス権限のみ保持

    尚、AWSLakeFormationDataAdmin権限は、glue:CreateDatabaseポリシーを保持するため、データベース作成は可能です。 また、データレイク管理者は暗黙的にデータベース作成権限を持つので、すべての条件でデータベース作成可能です。

    Data lake locations Data locations バケットAデータベース作成 バケットBデータベース作成
    バケットA 指定なし データレイク管理者 データレイク管理者、PL
    バケットA バケットAへの権限をPLに付与 データレイク管理者、PL データレイク管理者、PL
    指定なし バケットAへの権限をPLに付与
    →データレイク管理下でないので、設定不可
    データレイク管理者、PL データレイク管理者、PL
    バケットB バケットBへの権限をアナリストに付与 データレイク管理者、PL データレイク管理者、※アナリスト

    上記から、「Data lake locations」で指定されたS3バケットに対して、Data locationsで権限付与されていなければ、AWSLakeFormationDataAdmin権限を持っていてもデータベース作成ができないことがわかります。 つまり、Lake Formationの権限管理配下にあるS3バケットに対して、Lake Formationによる権限付与がなければ操作できないということがわかります。 また、Lake Formationの権限管理配下にないS3バケットに対し、AWSLakeFormationDataAdminを持っていれば、データベース作成できることがわかります。

    ※の部分では、アナリストによるデータベース作成はできますが、 「Unknown error」が発生します。 アナリストのペルソナとして、データベース作成は役割の範囲外となると思います。 ここでは、敢えて権限付与したときの挙動を確かめましたが、通常の運用では役割の分割、設計を行うべきかと思います。

    データベース権限付与

    続いて、データベースに対して、LakeFormationWorkflowRoleがテーブル作成できるように権限付与します。

    「Data catalog」 > 「Tables」 > 「テーブル名」 > 「Action」 > 「Grant」を選択し、「Create Table」権限を付与します。

    データカタログ作成

    データカタログ(テーブル)は、Lake FormationのBlueprintやGlueの クローラを使い自動で作成する方法と、 コンソールから手動で作成する方法があります。

    本記事では、AWS Glueの クローラを利用して、テーブルを作成していきます。 本記事では、Glue自体は詳しく触れません。以下のキャプチャのように設定し、クローラを作成していきます。

    クローラを実行すると、テーブルが作成されます。

    今回は取り扱いませんが、クロスアカウントで利用する場合、S3のACL設定が必要になる場合もあります。

    Glueクローラーで、テーブル作成時にS3から403エラーが返ってきた場合、以下のドキュメントからトラブルシューティング方法を参照できます。

    aws.amazon.com

    4. まとめ

    以上で、Lake Formationを使った、ベースとなるデータレイクの作成は完了です。個人的には、S3やGlueとの連携部分で発生する、IAM権限設定が最初の難関でした。次に、「Data lake locations」と「Data locations」周りの考え方を理解するのに時間がかかりました。
    次回、Athenaを利用し、作成したデータレイクからデータ取得ができるかどうかを見ていきます。

    Acroquest Technologyでは、キャリア採用を行っています。
    • ディープラーニング等を使った自然言語/画像/音声/動画解析の研究開発
    • Elasticsearch等を使ったデータ収集/分析/可視化
    • マイクロサービス、DevOps、最新のOSSを利用する開発プロジェクト
    • 書籍・雑誌等の執筆や、社内外での技術の発信・共有によるエンジニアとしての成長
      少しでも上記に興味を持たれた方は、是非以下のページをご覧ください。 www.wantedly.com