[go: up one dir, main page]
More Web Proxy on the site http://driver.im/

いつも Google Cloud Japan Blog ならびに GCP Japan Blog をご覧いただきありがとうございます。本ブログは、2019 年 8 月 2 日(金)をもちまして、新プラットフォームに統合し移行いたしました。今後は、以下の新しい Google Cloud Japan 公式ブログより最新情報をお届けしてまいります。ぜひ、新しいブログをブックマークしてお楽しみください。

New! Google Cloud Japan 公式ブログ


※US 版 Google Cloud Blog の URL は http://cloud.google.com/blog です。ページ下部の言語設定ボタンから言語(英語コンテンツと日本語コンテンツ)の切り替えが可能です。もしくは、URL " https://cloud.google.com/blog/ja "の最後の "ja" の箇所を削除、もしくは追加することで言語の切り替えが可能です。



なお、本ブログは、今後数か月間でクローズいたします。2017 年以降のブログポストについては、新プラットフォームに統合し移行してまいります。新しいブログ上の、カテゴリや検索機能から該当ポストをお探しください。

また、最新情報は随時 Google Cloud Japan の Facebook および Twitter でもお知らせいたします。ぜひこちらも合わせてフォローいただければ幸いです。


Facebook:@GoogleCloudJapan
Twitter:@googlecloud_jp



今後とも Google Cloud Japan をよろしくお願いいたします。



約 10 兆円とも言われる自動車アフター マーケットの業務支援ソフトウェア メーカーとして、国内トップクラスのシェアを誇る株式会社ブロードリーフ。そんな同社が今取り組んでいるのが、「ブロードリーフのビジネスを変える」ためのプラットフォーム作り。昨今のビジネス環境変化を受けて、パッケージ ソフトを制作・販売する企業から、プラットフォーマーへの転針を図る同社は、Google Cloud Platform(GCP)に何を期待しているのでしょうか?


利用している Google Cloud Platform サービス
BigQueryGoogle Kubernetes Engine など

お話を伺った方
  • 開発本部 基盤開発部 部長 祖慶 良大氏(写真)
  • 開発本部 基盤開発部 松本 宏紀氏(インタビュー内のみ登場)

株式会社ブロードリーフ
整備工場など、自動車アフター マーケットを中心に、携帯電話販売代理店、旅行代理店など、幅広い業種・業界の現場業務を支援するソフトウエア・ITソリューション&各種サービスを提供(約 3 万社が利用)。近年は中国、フィリピンなど、アジア地域へも進出している。2005 年 12 月創業。開発拠点は東京・札幌・福岡の 3 か所。従業員数は 921 人(2018 年 12 月末時点)。

Google Cloud ならではの高品質なネットワークと開発環境としての GKE に魅力を感じた

「我々が GCP で実現したかったのは、ブロードリーフのビジネスを変えること。今回、その第一歩として、整備事業者向けの販売管理システム『Maintenance.c』を開発・提供開始しましたが、その背景には、昨今、お客さまを取り巻くビジネス環境が変容しつつあり、これまで提供していた機能的な価値に加え、EDI(Electronic Data Interchange/電子データ交換)などへの対応などが求められるようになったことが挙げられます。そこで、約 4 年前、ブロードリーフの提供する各種ソフトウェア・サービスをクラウドに移行した、新たなビジネス プラットフォームを構築することを決断しました。」(祖慶さん)

そのプラットフォームの名は『Broadleaf Cloud Platform』。計画は約 4 年前に立案され、プロトタイピング ベースでの検証を経て、約 2 年前から GCP 上での本格的な開発がスタートしました。

「開発に際し、主要なクラウド プラットフォームは一通り検証したのですが、GCP を選んだのにはいくつか理由があります。何より大きかったのがネットワークが充実していること。我々の提供するサービスは一般的なデータベースと比べて、かなりサイズの大きなデータをやり取りするため、GCP の回線の太さは魅力的でした。ちなみに、自動車アフター マーケット業界は取り扱うデータの種類がとにかく多いことも特徴の 1 つ。車種データだけでも何十万件レコード、その部品データに至ってはなんと約 4 億 6,000 万レコードにもなります。同じ型式の車でも地域やオプションの違いなどによって 1 台 1 台細かく異なる部品が使われていますから、そうしたデータを安定して、スピーディに検索できることは導入の最低条件でした。また、将来的にビジネスの規模に合わせてどこまでもスケールできること、月末月初や決算期、繁忙期(自動車業界の場合、2~3 月)のスパイクに耐えられるスケーラビリティも必要でした。加えて、BigQuery のように Google Cloud にしかないエッジの立ったサービスが多く揃っていたことも決定を後押ししています。」(祖慶さん)

「開発チームとしては、Google Kubernetes Engine(GKE)の存在が大きかったですね。PaaS は便利な反面、ベンダー依存が大きくなるため、それを避けるために Kubernetes を利用することを決めたのですが、GKE ならそれをフルマネージドで利用できます。当時、それができるのは GCP だけだったんですよ。具体的には新しいサービスのプロダクション環境を構築するのに、従来のオンプレ環境では機材調達からネットワーク設定までを含めて数週間かかっていたものが、約1時間でできるようになりました。開発時もバージョンアップ作業などを効率的に実施できるようになり、1日20回以上の頻繁なバージョンアップも可能になりました。」(松本さん)

なお、ブロードリーフは今回のビジネス プラットフォーム構築のタイミングで、モノリシックだったサービスを、GKE を駆使してマイクロサービス化。業界特有のノウハウが必要なものを国内3つの開発拠点で分散開発した上で、顧客管理や売上管理などは外部のベンダーに外注し、効率的にサービスを構築していったそうです。

「マイクロサービス化には『Broadleaf Cloud Platform』を将来的にサードパーティ ベンダーに開放したいという狙いもあります。」(祖慶さん)

「なお、GKE の利用に際しては、GKE を意識しない開発と運用をできるよう注意しています。実のところ、Kubernetes に詳しいエンジニアはまだまだ少ないですから、Kubernetes に詳しくないと開発できない、運用できないという状況は避けたかったんです。ですので、基本的には GitOps でソースをマージしたら、GitLab CI で Docker イメージをビルドして、開発環境であれば自動的にデプロイされるというようなかたちにしています。GKE ならマスターやノードの管理をしなくて良いのも助かっていますね。GKE のおかげで開発効率がかなり上がっていると感じています。」(松本さん)
東京・札幌・福岡に分散している開発拠点間のやりとりには Hangouts Meet を活用


蓄積されたビッグデータを BigQuery × AI で積極的に活用

サービスのクラウド プラットフォーム化によってもたらされる恩恵の 1 つが、より能動的なビッグデータ活用が可能になること。事実、ブロードリーフが GCP を採用した理由の 1 つには膨大なデータを瞬時に解析できる BigQuery の存在も大きかったそうです。

「具体的にはログの解析・集計のほか、検索動向の分析などにも利用しています。よく検索されているということは、そこが故障しやすい箇所ということ。さらに言えば、リコールの予兆とも言えます。『Maintenance.c』では AI 技術なども駆使して、これをシステム上でサジェストする仕組みを用意し、利用者(修理工場)が顧客(修理依頼者)に早めの問題解決を提案するなどできるようにしました。クラウド移行以前もこうした方法論はあったものの、リソースが不足しており実現できていませんでした。もちろん、大手自動車メーカーもすでにこうした取り組みを進めていると思いますが、ブロードリーフはそれをメーカーの壁を越えて横断的に行えるのが強みです。なお、AI 活用については、今後さらに推し進めていく予定。たとえば車の情報だけでなく、お客さまの特性も学習させ、入庫する前により適切なサジェストができるようにするなどといったことを考えています。また、Cloud Vision API でナンバープレートを自動認識させたり、Cloud Speech-to-Text API でお客さまとの電話内容などを自動的にテキスト化したりといったことも検討中です。」(祖慶さん)

まずは約 3 万社という既存顧客を対象に機能強化を進めている『Broadleaf Cloud Platform』。こうしたエンタープライズ領域に GCP を利用することについて、2 年前の時点では不安もあったと祖慶さん、松本さんは当時をふり返ります。

「当時はまだ、エンタープライズ領域で GCP の採用事例が少なかったため、可用性の点で若干の不安がありました。ただ、実際に 2 年ほど GCP を利用してみて、今は高い信頼性と安定感を備えたプラットフォームであることを実感しています。」(松本さん)

「現在はハイブリッド クラウドの時代ですから、それぞれの強みの部分を積極的に使っていくべき。GCP は特にマネージドなサービスが充実しているので、そうした点をより拡充していって欲しいですね。」(祖慶さん)

「今後は、GCP のマネージドなサービスを積極的に活用していって、運用の負荷を下げていくことも考えて行きたいですね。面倒なことはすべて Google Cloud に任せて(笑)、自分たちがより開発に専念していけるのが理想です。」(松本さん)

現在は予定通りに進行しているという『Broadleaf Cloud Platform』によるブロードリーフ事業のクラウド移行。今後はグローバル展開も推進していきたいとのことです。

「日本の自動車は世界中で走っています。中には古い車も多く、そのアフター マーケットは今後、ますます拡大していくことになるでしょう。国内と同様にサービスを提供し、データを収集していくためも、世界中に展開されている GCP のネットワークに期待しています。」(祖慶さん)

今回の取材では、札幌拠点に勤務している松本氏が Hangouts Meet でインタビューに参加している



株式会社ブロードリーフの導入事例 PDF はこちらをご覧ください。
その他の導入事例はこちらをご覧ください。


*この記事は Yujin Tang と David Ha による Cloud Blog の記事 "How to run evolution strategies on Google Kubernetes Engine" を元に翻訳・加筆したものです。詳しくは元記事をご覧ください。

強化学習(RL)は、ゲームチェスロボティクスですばらしい成果をあげており、それとともに機械学習コミュニティでの人気も高まっています。以前のブログ投稿では、Google の強力なコンピューティング インフラや、ハイパーパラメータのベイズ最適化などスマートなトレーニング サービスを活用して、AI Platform で RL アルゴリズムを実行する方法を紹介しました。このブログでは、進化戦略(ES)アルゴリズムについて説明し、Google Kubernetes Engine(GKE)で実行する方法を紹介します。

進化戦略は、進化の考え方に基づいた最適化テクニックです。最近、ES は RL に代わってさまざまな難題に対処するものと見なされるようになっています(12)。特に、ノイズの多いポリシー最適化の勾配推定をバイパスでき、かつ収束の早い分散コンピューティングに向いた性質を備えているという 2 点が、よく知られている ES のメリットとしてあげられます。ES は 1960 年代に開発され、容易にスケールする点がメリットとして知られていました。しかし、研究コミュニティのオープンソース プロジェクト(Salimans 他、2007 年)によって、ES を大量のマシンにスケーリングすることで RL アルゴリズムの最高性能に匹敵する結果を出せることが実証されたのは、つい最近のことです。そのため、最新の研究に進化型のアルゴリズムを取り入れる方法を探るディープ ラーニング分野のリサーチャがますます増えています(12345)。

進化型コンピューティング アルゴリズムをスケーリングする優れたインフラストラクチャを構築すれば、この領域はさらに進展するということは判明していましたが、大規模システム開発に明るい研究者は多くはいません。幸い、ここ数年間で Kubernetes などのテクノロジーが生み出され、専門のエンジニアでなくても簡単に分散環境によるソリューションを構築できるようになりました。そこで本稿では、Kubernetes を活用してスケーラブルな進化型アルゴリズムをデプロイする仕組みの実例を紹介します。コードと手順もこちらで公開していますので、ML 研究において GKE 上で ES を試す際の参考にお役立てください。

ちなみに、Google Cloud ではコンテナによる分散トレーニングが可能なプラットフォームとして AI Platform も提供しています。AI Platform では TensorFlow と同様の分散処理をサポートする ML フレームワークが利用可能です。しかしその目的は、おもにモデルを非同期でトレーニングすることであり、次のセクションで説明するような ES の分散コンピューティングの目的とは異なります。

進化戦略入門

ES は一種のブラックボックス最適化です。ベースとなるタスクや関数に勾配がない場合や、勾配の計算が非常に複雑な場合、または勾配推定にノイズが多く学習できない場合など、勾配ベースのアルゴリズムではうまく解決できない ML タスクに有効です。例として、下の図の左側に示す地形に立っていることを想像してみてください。あなたのタスクは、目隠しをして最も低い場所へ向かうことです。あなたは複数の魔法の玉を持っており、それを介してしか周囲の状況を知ることができません。
図 1. 球関数(左)と Rastrigin 関数(右)(出典: Wikipedia)

大ざっぱに言えば、勾配ベースのアルゴリズムでは、玉を落として転がしながら行く先を決めるというステップを経ていきます。玉の動く速さを見て、一番速く転がる方向(勾配が一番急な方向)に進みます。左側の地形であれば、このルールに従って何度か玉転がしを繰り返せばゴールにたどり着けます。しかし、同じやり方を右側の地形で試すとどうなるでしょうか。おそらく、山に囲まれた谷底にはまってしまい、目標は達成できないでしょう。

ES のアプローチはこれとはまったく異なります。最適化のステップごとに、複数回の試行を行います。そして、高い適応度(fitness)を示した設定に基づいて進み先を決めます(適応度とは、試行がどのくらい優れているかを測る指標です。上図の地形の例では地面の高さがそれに当たり、低いほど適応度が高いことになります。RL で言えば試行で得られる累積報酬に当たります)。この過程を通じて、適応度が低い試行は取り除かれ、適応度がもっとも高いものだけが生き残ります。これが進化の過程に似ているため、進化戦略という名前が付けられています。

先ほどのたとえに当てはめて ES の仕組みを説明するなら、各ステップで玉を落として転がす代わりにピストルに玉を込めて数発を発射し、いくつかの玉を周囲に散りばめる、ということになります。それぞれの玉が地面に打たれた時点で位置と高度がわかるので、ステップを繰り返すうちに高度が低いと推定される場所に徐々に移動していけます。この方法は、図の左右どちらの地形にもうまく働きます(高い山の向こうにも届く強力なピストルを使うことを想定します)。またこの方法なら、複数回の試行を同時に実行することで最適化を簡単に高速化できます。ますたとえるならピストルの代わりに散弾銃を使うようなものです。

ここまで、ES の基本的な考え方と仕組みを説明しました。さらに興味がある方は、筆者によるブログ記事により詳しい説明が記載されていますので、ぜひ読んでみてください。

Kubernetes 入門

Kubernetes は、コンテナ化したワークロードとサービスを宣言的な設定や自動化を活用して管理するためのプラットフォームです。Google が開発を始め、2014 年にオープンソース化されました。Kubernetes を詳細に解説するには何ページも必要なので、ここでは概説にとどめ、ES への応用を中心に説明します。

これまでの説明で、ES がいわゆるコントローラ ワーカー(controller-worker) アーキテクチャで実装できることが分かるかと思います。この場合、ある設定で試行するようコントローラがワーカーに命令し、ワーカーから戻ってきた結果に基づいて最適化を進める、という処理を繰り返し行います。この実装方法について、先ほどの地形の例を使って Kubernetes 上で ES を実行する方法の定義と説明を書いてみましょう。

今回は、銃や玉は使いません。その代わり、携帯電話で誰かに連絡して玉を発射してもらえます。ただしその前に、何を実行したいのかを具体的に伝えなければなりません(この例では、玉を発射してもらうことです)。そこで「サービス プロバイダへのメモ」に仕様を記述します。また記録のため「自分用のメモ」も準備します。サービス プロバイダにこの仕様を送れば、そこからは Kubernates の楽しい仕掛けが動き始めます。


サービス プロバイダへのメモ自分へのメモ
タスク名
Kubernetes による ES 実行の説明

設定
  • タスク実行用にクルマを 10 台用意。もしクルマが足りなければ、クルマを追加
  • 玉の射撃手を 100 人用意
  • 連絡係を 1 人用意

やるべきこと
サービス プロバイダへ、

私はこれから、私がどこにいるか、いくつの玉を撃ちたいか、どこに向けて撃ちたいかを指示します。そこにクルマを移動し、玉を撃ち、玉が当たった位置と高さを教えてください。
タスク名
Kubernetes による ES 実行の説明

やるべきこと
自分へ、

ゲームが終わるまで、以下を繰り返し実行する。

  1. サービス プロバイダを呼び出し、自分がどこにいるか、玉をいくつ撃つか、どの方向に撃つかを伝える
  2. 玉が当たった位置と高さをサービス プロバイダから受け取る
  3. ES アルゴリズムを使い、受け取った位置と高さに基づき次に移動すべき場所を計算する



この仕様を Kubernetes で実装する場合、サービス プロバイダへのメモにある「やるべきこと」の部分をワーカーのプログラムとして記述し、自分用のメモの部分をコントローラのプログラムとして記述します。これらをいくつかのランタイム ライブラリと合わせてパッケージ化し、コンテナ イメージを作成します。そして Kubernetes がサービス プロバイダとなり、ワークロードと呼ばれる仕様を受け取り。ワークロードはコンテナ イメージとリソースなどのいくつかのシステム設定で構成されます。

上記例にある 10 台のクルマは、クラスタ内の 10 個のノードに相当します。また 100 人の玉の射撃手は、実行したいコンテナ(Kubernetes の用語ではポッドと言います)の数を表します。Kubernetes は、これらのポッドを用意する責任を持ちます。また 100 人の射撃手を用意できても、個々の射撃手と直接やりとりして結果を集めたりするのは面倒な作業です。中には、病気で休んだ人(マシンの再起動により動作していないコンテナ)が、あなたの知らない別の人(新しく起動したコンテナ)に仕事を引き継いでいるかもしれません。こうした面倒に対処するため、Kubernetes はワークロードをサービスとして公開し、コントローラとワーカーの連絡窓口とします。サービスは関連するポッドを取りまとめているので、いつでもポッドに取り次いでくれるうえ、負荷の分散も可能です。

プラットフォームとして Kubernetes を使うと、高可用性(希望どおりの数のポッドを確実に実行可能)と大きな拡張性(実行時にポッドを追加・削除可能)が得られます。こうした性質を備えた Kubernetes は、ES に理想的なプラットフォームだと私たちは考えています。また、Kubernetes の可用性拡張性をノードレベルにまで拡張する GKE を導入することで、さらに優れたプラットフォームが実現可能です。

GKE による ES の実装

では、ES の実装方法の詳細と、それを GKE で実行する手順を見ていきましょう。コードと実装手順はこちらでアクセスできます。

実装

上述のとおり、実装にはコントローラ ワーカー アーキテクチャを採用します。各ワーカーは独立したサーバーで構成され、コントローラがクライアントとなります。プロセス間通信には gRPC を利用します。リモート プロシージャ コール(RPC)はデータを渡す手段としてはメッセージ パッシング インターフェース(MPI)などの他の選択肢ほど高効率ではありません。しかしデータのパッケージングが簡単で耐障害性も高いため、クラウド環境ではよく使われています。

次のコード スニペットに、メッセージの定義を示します。「ロールアウト」とは 1 回の試行を表しており、rollout_reward はロールアウトから返される適応度です。


  message RolloutRequest {
    // Index of rollout
    int32 rollout_index = 1;
    // Random seed from the master
    int32 env_seed = 2;
    // The weights of the neural network policy
    Repeated double policy_weights = 3;
    // Whether this request is evaluation
    bool evaluate = 4;
}

message RolloutResponse {
    // Index of rollout
    int32 rollout_index = 1;
    // The reward collected in the rollout
    double rollout_rewards = 2;
}

service Rollout {
  // The RPC service for policy evaluation
  rpc RolloutWithParameter(RolloutRequest) returns (RolloutResponse) {}
}

このサンプルで利用する ES アルゴリズムは、estool に基づく Parameter-exploring Policy Gradients(PEPG)と 、pycma に基づく Covariance Matrix Adaptation(CMA)です。これらは解決がとりわけ難しい連続制御 RL 環境である Google Brain の Minitaur Locomotion や OpenAI の BipedalWalkerHardcore-v2 でも試せます。また、コードを拡張して ES アルゴリズムを追加したり、設定を変更して独自の環境でアルゴリズムを試したりすることも簡単です。具体的には、algorithm.solver.Solver で定義されたインターフェースに従った実装であれば他のコードと合わせて実行できるはずです。

GKE で ES を実行する

GKE でのコードの実行には、Google Cloud Platform(GCP)で動作するクラスタが必要です。こちらの手順に従ってクラスタを作成します。この例では、次のコマンドと設定を使ってクラスタを作成しましたが、必要に応じて変更しても構いません。


  GCLOUD_PROJECT={your-project-id}

# Create a cluster.
gcloud container clusters create es-cluster \
--zone=us-central1-a \
--machine-type=n1-standard-64 \
--max-nodes=20 \
--min-nodes=16 \
--num-nodes=17 \
--enable-autoscaling \
--project ${GCLOUD_PROJECT}

クラスタができたら、次の 3 つのステップでサンプルの ES コードを GKE で実行できます。それぞれ簡単な bash コマンドです。
  1. コントローラとワーカーのコンテナ イメージをビルドする
  2. ワーカーをクラスタにデプロイする
  3. コントローラをクラスタにデプロイする

  # Step 1: Build a container image for the controller and the workers.
gcloud builds submit \
--tag gcr.io/${GCLOUD_PROJECT}/es-on-gke:1.0 . \
--timeout 3600 \
--project ${GCLOUD_PROJECT}

# Step 2: Replace the ${GCLOUD_PROJECT} in the YAML file and run the command.
kubectl apply -f deploy_workers.yaml

# Step 3: When all the workers are started and running,
#         replace the ${GCLOUD_PROJECT} in the YAML file and run the command.
kubectl apply -f deploy_master.yaml

図 2. デプロイが成功した GCP コンソールの例

以上です。これで、GKE 上の指定した環境で ES のトレーニングが実行されています。

トレーニング プロセスの確認は、次の 3 つの方法で行えます。
  1. Stackdriver—GCP コンソールで [GKE Workloads] ページをクリックすると、ポッドの詳細ステータス レポートが表示されます。es-master-pod の詳細に移動すると、[Container logs] から Stackdriver のログを確認できます。ここから、トレーニングやテストの報酬を確認します。
  2. HTTP サーバー—今回のコードではコントローラ内で簡単な HTTP サーバーが動作しており、トレーニングのログに簡単にアクセスできます。[GKE Services] ページにある es-master-service のエンドポイントからアクセスできます。
  3. kubectl—kubectl コマンドでもログやモデルを取得できます。例として以下のコマンドを示します。

  POD_NAME=$(kubectl get pod | grep es-master | awk '{print $1}')

# Download reward vs time plot.
kubectl cp $POD_NAME:/var/log/es/log/reward_vs_time.png $HOME/

# Download reward vs iteration plot.
kubectl cp $POD_NAME:/var/log/es/log/reward_vs_iteration.png $HOME/

# Download best model so far.
kubectl cp $POD_NAME:/var/log/es/log/best_model.npz $HOME/

# Download model at iteration 1000.
kubectl cp $POD_NAME:/var/log/es/log/model_1000.npz $HOME/

# Download all test scores.
kubectl cp $POD_NAME:/var/log/es/log/scores.csv $HOME/

ローカルで ES を実行する

トレーニングとテストの両方をローカルで実行してデバッグに利用できます。train_local.sh と test.py に適切なオプションを追加して使います。


  # Train locally.
# E.g. bash ./train_local.sh -c configs/BipedalWalkerHardcore.gin -n 10
bash ./train_local.sh -c {path-to-config-file} -n {number-of-workers}

# Test locally.
# E.g. python test.py --checkpoint=1000 --logdir=./log
python test.py --checkpoint={checkpoint} --logdir={path-to-log-directory}

実験結果

では、GKE で ES を実行するメリットを 2 つの例を通じて確認しましょう。1 つは OpenAI の BipedalWalkerHardcore 環境で CMA を使ってトレーニングした 2D ウォーカー、もう 1 つは Google Brain の MinitaurLocomotion 環境の四足歩行ロボットです。ここでは、テスト試行で 100 回連続して TargetReward よりも大きな平均報酬を実現できれば、エージェントがタスクを解決したと見なします。RL で試せばわかりますが、どちらのタスクも難易度はかなり高いです。

次の表は、実験環境の設定についてまとめたものです。比較のため、スタンドアロンの 64 コア Google Compute Engine インスタンスでも実験を行いました。この Compute Engine インスタンスのワーカーの数は、CPU 使用率が 90% を超えるように調整します。


表 1. 実験構成
環境アルゴリズムTargetRewardワーカー数
BipedalWalkerHardcoreCMA3001000 (GKE), 58-60 (GCE)
MinitaurLocomotionPEPG131000 (GKE), 58-60 (GCE)


今回の実装では、両方のタスクをクリアできました。結果を下に示します。

厳密にはタスクの内容に依存しますが、GKE で ES を実行すると速度を大幅に向上できる可能性があります。今回の例では、BipedalWalkerHardcore の学習は 5 倍、四足歩行ロボットの学習は 10 倍以上高速です。ML 研究においてこの高速化はより多くのアイデアを試すチャンスとなり、ML アルゴリズム開発のイテレーションの向上にもつながります。

まとめ

ES は、勾配ベースのアルゴリズムでよい結果が得られない ML タスクに対して強力なソリューションとなります。ES には並列処理との親和性が高い性質があるので、Kubernetes で ES を実行することで大幅な高速化を実現できます。これにより、ML リサーチャやエンジニアが新しいアイデアを試すイテレーションのスピードが上がります。

簡単にスケールできるという ES の力をもっとも活かせる応用は、難しい問題を解くためのシミュレーション環境をローコストに用意できるケースです。最近の研究(12)では、コントローラを実環境にデプロイする前に、まずシミュレーションで仮想ロボット コントローラをトレーニングする方法が効果的であることが実証されています。また、手作業のプログラミングではなく、収集した観測結果から学習で得られたディープラーニング モデルでもシミュレーション環境を実現できます(123)。こうした応用であれば、ES のスケーリングを活かした数 1000 ノード規模の並列シミュレーション環境での学習が可能でしょう。

進化の手法を用いると最適化する対象の選択肢がぐんと広がるため、従来の RL ポリシー最適化では対応できなかったさまざまな応用が可能です。たとえば、この最新の研究では RL 環境で ES を使い、ポリシーのトレーニングだけでなくより優れたロボットの設計まで学習しています。進化の手法を使うジェネラティブ デザインの領域では、ずっと多くのクリエイティブな応用が生まれるはずです。また、この最新の研究成果では、複数の RL タスクを実行できる最小のニューラル ネットワーク アーキテクチャが、進化型アルゴリズムを使うことでパラメータのトレーニングなしで得られることを実証しています。この結果は多くの ML リサーチャを驚かせ、進化の手法が切り拓くまったく新しい研究分野の存在を示しています。

ディープ ラーニングの革命は、大規模なディープ ニューラル ネットワークのトレーニングを実現する GPU が触媒となり生じました。同様に、ローコストな CPU で構成された大規模クラスタへ簡単にスケールできる進化の手法は、これからのコンピューティング革命をリードするカギとなるでしょう。

GKE や Kubernetes によるディープラーニングの詳細については以下をご覧ください。


Google Cloud Next ’19 in Tokyo の開催まで、3 週間を切りました。イベントへのお申し込みおよびセッション登録はお済みでしょうか? 本日は、7 月 31 日(水)の夜の時間帯に開催するセッションをピックアップしてご紹介します。18 時以降に開催するセッションでは、お弁当を提供させていただきますので、お仕事帰りにぜひお気軽にご参加ください。なお、ご来場の際には事前のイベント申し込み・希望セッションへの登録 をお願いいたします。


テーマ:データ分析 

7 月 31 日(水)| 5:20–6 PM
D1-5-S03 Google Cloud Platform でゲノム解析
GCP が提供する Genomics Pipelines API はゲノムの変異検出を高速かつ安価に実現することを可能にします。また BigQuery を活用することで、膨大な変異情報を標準的なSQLで解析することが可能になります。このセッションでは、GCP 上で、GATK や DeepVariant、ワークフローエンジン などのオープンソースを使用し、ゲノムデータをエンドツーエンドで解析する方法を学びます。

テーマ: DevOps、SRE

7 月 31 日(水)| 6:40–7:20 PM
D1-6-S07 開発者向け DevOps プラットフォーム 〜 メディアストリーム解析サービスでの GCP 活用 〜
NTTコミュニケーションズは、映像音声のリアルタイム解析に特化した開発者向け DevOps プラットフォーム『SkyWay Media Pipeline Factory』をリリースしました。 GKE や Stackdriver などの GCP サービスと、各種 3rd party API とをサーバレス / マイクロサービス アーキテクチャにより結合することにより、高いアジリティ、マルチテナント分離、分散コンポーネント管理を実現した GCP ベスト プラクティスを、デモを交えて紹介します。

テーマ:アプリケーション開発 

7 月 31 日(水)| 6:40–7:20 PM
D1-6-S01 失敗から学ぶ GCP 〜「ほしい本が必ず見つかる!」honto での書店の在庫検索システム構築〜
ハイブリッド型総合書店「honto」と、アプリ「honto with」では、自分のほしい本がどの店舗にあるか分かる在庫検索機能があります。現在 GCP 上で安定稼働しているこのシステムでは 1 億件を超える在庫データの参照、更新を可能にしています。このシステムを構築、運用するにあたっての数々の失敗と、どうやって乗り越えたかを共有します。

テーマ:ハイブリッド クラウド

7 月 31 日(水)| 6:40–7:20 PM
D1-6-S03 AdTech / MarTech エリアにおける GCP 活用 〜GKE を利用した DAC 、CRM ソリューションの移行〜
AdTech / MarTech エリアにおけるGCP活用例として、デジタル広告ビジネスを統合的に展開しているデジタル・アドバタイジング・コンソーシアム株式会社のCRMソリューション、DialogOne の移行事例を紹介します。 DialogOne を GCP に移行することを決めた理由、移行にあたっての学び、今後の展望について話します。

テーマ:ストレージ 

7 月 31 日(水)| 7:40–8:20 PM
D1-7-S07 ユースケースで見る FileStorage の最適化
本セッションでは、GCP における FileStorage の活用について、プロダクトの概要、機能、価格、そしてユースケースにおける最適なストレージの選択についてご紹介します。また、他の GCP のサービスとの連携にしたファイルのワークフローについてデモでご紹介します。

テーマ:ハイブリッド クラウド 

7 月 31 日(水)| 7:40–8:20 PM
D1-7-S04 ゼロから始める Google Kubernetes Engine 〜Kubernetes の基本からクラウドネイティブ・アプリの運用まで
Google Kubernetes Engine は小規模アプリから大規模システムまで、本番環境に必要な様々な機能を備えています。本講演では Kubernetes の基本となる機能、簡単なアプリの展開に加え、自動スケールやモニタリングなど Google Kubernetes Engine を最大限に活用した、クラウドネイティブ・アプリの運用方針までご紹介します。

テーマ:セキュリティ 

7 月 31 日(水)| 7:40–8:20 PM
D1-7-S01 実例から学ぶ、VPC Service Control Deep Dive
Google Cloud では企業の中の機密データの価値を最大化するため、その前提となる機密データを企業内で安全に扱うための方法として VPC Service Control という機能を提供しております。このセッションではその詳細を、実際に実装されたお客様の実例とともに解説します。


なお、7 月 31 日(水)・ 8 月 1 日(木)の 2 日間は、12:00 - 18:00 の時間帯で Google Cloud 認定資格取得者向けの特典をご用意しています。こちらにもぜひ足をお運びください。

Google Cloud Certified Lounge
Google Cloud 認定資格取得者専用のラウンジを東京プリンスホテル 11 階 Room H にご用意しております。ラウンジでリフレッシュしていただき、Google Cloud Next ’19 in Tokyo を 100% お楽しみください。

Google Cloud 認定資格取得者特典
Google Cloud 認定資格取得者の特典として、Google Cloud スペシャル バックパックをご用意しています。東京プリンスホテル 11 階 Room H、Google Cloud Certified Loung にお立ち寄りください。

ハンズオンラボ
実際に GCP Console を使って、Google Cloud のサービスを体験しませんか?初級から上級の演習を、自分のペースで行っていただけます。Google エキスパートが質問対応をいたします。東京プリンスホテル 3 階 Room F にお越しください。


————————————————————
お問い合わせ先
Google Cloud Next Tokyo 運営事務局
gc-nexttokyo-info@google.com

※この投稿は米国時間 2019 年 6 月 25 日に Google Cloud blog に投稿されたものの抄訳です。

Google Cloud のエンタープライズ向けクラウド データ ウェアハウスである BigQueryKaggle が統合されました。BigQuery をご利用のお客様は、超高速の SQL クエリを実行し、SQL で機械学習モデルをトレーニングし、Kernels でそのモデルを分析できるようになります。Kernels とは、無料で使える Kaggle のホステッド Jupyter ノートブック環境です。

BigQuery と Kaggle Kernels を一緒に使うことで、直感的に扱える開発環境を使用して BigQuery データにクエリを実行し、データの移動やダウンロードなしで機械学習を行えます。Kernels ノートブックまたはスクリプトに Google Cloud アカウントをリンクすると、BigQuery API Client ライブラリを使ってノートブックで直接クエリを組み立て、BigQuery で実行するという形で、ほぼあらゆるタイプのデータ分析が可能になります。たとえば、Matplotlib、scikit-learn、XGBoost など最新のデータ サイエンス ライブラリをインポートすれば、結果を可視化したり、最先端の機械学習モデルをトレーニングしたりすることができます。さらに、Kernels の無料使用枠は、GPU と最大 16 GB の RAM を使って 9 時間実行できるという贅沢な内容です。Kernels が提供する機能の詳細は Kaggle のドキュメントをご覧ください。

300 万人以上のユーザーを抱える Kaggle は、データ サイエンティストたちがデータの探索や分析、その成果のシェアのために集まる世界的なオンライン コミュニティです。Python か R の Kernels ノートブックを起動すればすぐにコーディングを開始でき、ほかの人々が書いた 20 万以上の公開 Kernels からヒントを得ることができます。

BigQuery のお客様にとって何よりも大きいのは、クエリや分析を 1 か所にまとめられる IDE(統合開発環境)、すなわち Kaggle Kernels を使えることでしょう。データ サイエンティストの仕事は細切れになりがちで、従来はクエリ エディタでクエリを実行し、そのデータを別の場所にエクスポートして分析を終えていましたが、Kernels はそれらをシームレスにつないで 1 つにまとめます。

しかも、Kaggle は Kernels を簡単に公開できるシェアのためのプラットフォームです。オープンソースの仕事を広めたり、世界最高レベルのデータ サイエンティストたちと議論したりすることができます。

Kaggle と BigQuery の始め方

BigQuery を初めてお使いになる方は、BigQuery サンドボックスでアカウントを有効にしてください。これにより、最大で 10 GB のストレージ、1 か月あたり 1 TB のクエリ処理、10 GB の BigQuery ML モデル作成クエリを無料で使用できます(料金体系の詳細は BigQuery のドキュメントをご覧ください)。

Kernels で BigQuery データセットを分析するには、まず Kaggle アカウントにサインアップします。サインインの後に、上のバーの “Kernels” をクリックし、さらに “New Kernel” をクリックすると、新しい IDE セッションがスタートします。Kernels にはスクリプトとノートブックの 2 つのタイプがあります。次に示すのはノートブック オプションの例です。
次に、Kernels のエディタ環境で、右側にあるサイドバーの “BigQuery” をクリックし、続いて “Link an account” をクリックして、Kaggle アカウントに BigQuery アカウントを紐づけます。アカウントがリンクされたら、BigQuery API Client ライブラリを使ってご自分の BigQuery データセットにアクセスできます。
Kaggle で公開されている Ames Housing データセットを使用して、これを試してみましょう。このデータセットは米国アイオワ州エイムズに建つ住宅の(ほとんど)すべての側面を記述する 79 種の説明変数から成り、その最終販売価格も含まれています。このデータから情報を得るためにクエリを作ってみましょう。このデータセットにはどのような家のタイプが含まれているのでしょうか。また、セントラル空調を設置している家、設置していない家はどれくらいあるのでしょうか。クエリは次のとおりです。

エイムズで最も一般的な家のタイプは平屋建てで、タイプに関係なく、ほとんどの家にセントラル空調が設置されていることがすぐにわかります。Kaggle には、このような形で探索できる公開データセットがほかにもたくさんあります。

SQL クエリを使った機械学習モデルの構築

データ分析のほか、BigQuery ML では SQL クエリを使って機械学習モデルを作成、評価できます。機械学習フレームワークやプログラミング言語に関する詳細な知識がなくても、データ サイエンティストであれば、わずかなクエリだけで回帰モデルの構築や評価を行えるのです。ここでは、エイムズにある不動産の最終販売価格を予想する線形モデルを作ってみましょう。このモデルでは、居住スペースの広さ、築年数、条件全般、品質全般を入力としてトレーニングを行います。モデルのコードは次のようになります。


  model1 = """
          CREATE OR REPLACE MODEL 
            `my-example-housing-dataset.ameshousing.linearmodel`
          OPTIONS(model_type='linear_reg', ls_init_learn_rate=.15, l1_reg=1, max_iterations=5) AS
          SELECT 
            IFNULL(SalePrice, 0) AS label,
            IFNULL(GrLivArea, 0) AS LivingAreaSize,
            YearBuilt, 
            OverallCond, 
            OverallQual
          FROM 
            `my-example-housing-dataset.ameshousing.train`
        """

このように、1 つのクエリだけで、Kernels の中に SQL ベースの機械学習モデルを作りました。Kernels を使用すれば、分析のためにもっと高度なクエリを作成したり、モデルを最適化して性能を高めたりすることができます。分析が完了したら、Kaggle コミュニティや、より広範なインターネット ユーザーに向けて Kernels ノートブックを公開することも可能です。

トレーニング統計情報の取得やモデルの評価に関するワークフローの続きの部分については、『Tutorial : How to use BigQuery in Kaggle Kernels』をご覧ください。このチュートリアルは Kernels ノートブックとして公開されています。モデルのトレーニングや評価を深く掘り下げる『Getting started with BigQuery ML』もあります。

BigQuery と Kaggle Kernels の統合の詳細は Kaggle のドキュメントをご覧ください。BigQuery を使って SQL 言語の基本を学べる Kaggle の SQL micro-course も内容を一新しており、サインアップすることをお勧めします。この統合機能をぜひお試しください。

- By Jessica Li, Product Manager, Kaggle and Jordan Tigani, Director, BigQuery, Google Cloud

Cloud OnAir は、Google Cloud の製品をわかりやすく解説し、最新の情報などをいち早く皆様にお伝えする Online 番組です。

6 月 20 日の放送では、G Suite の活用事例をテーマにお送りしました。実際にご活用いただいているお客様でどのように活用しているかを詳しく紹介するとともに、Google 社内でどのように使っているか、さらに、G Suite に関するよくある質問にお答えしました。

G Suite のお客様活用事例

コラボレーションとコミュニケーションのプラットフォームとして、G Suite は多数の企業にご利用いただいています。番組では、そうしたお客様の中から 3 社を取り上げ、G Suite 導入の背景や目的、導入後の効果、今後の方針についてご紹介しました。

  • 株式会社 TBS テレビ
全社を挙げて本格的な「働き方改革」に取り組んでいる株式会社 TBS テレビ。TBS グループ、約 6,500 人を対象とした、IT を活用した働き方改革プロジェクトを推進する一貫として、G Suite の導入を決めました。2017 年 7 月から検討を開始。2018 年 4 月より社内への先行導入がはじまり。順次ユーザー数を増やして、同年 7 月に全社展開に至りました。ドライブ、Gmail、カレンダー、ドキュメント、スプレッドシート、スライド、ハングアウトなどその利用機能は多岐に渡ります。
  • 横浜ゴム株式会社
横浜ゴム株式会社は、2014 年に G Suite を導入。主に Gmail を中心に利用し、その他の機能は、他のさまざまなシステムと併用する形で利用していました。その後、昨今の働き方改革を推進する流れの中で、より本格的に G Suite を活用していく決断をされました。メールだけでなく、共有ストレージなど、多くの機能を G Suite に置き換えており、Dialogflow を利用した社内チャットボットの運用もはじまっています。
  • メタウォーター株式会社
メタウォーター株式会社では、オンプレミスですべての IT インフラを構築、運用していましたが、2012 年 6 月に G Suite の全社利用を開始し、現在は約 3,400 アカウントがメールだけでなく、ドライブ、カレンダー、Hangouts Meet などさまざまなものを使用しています。さらに、全社で計 6 台の Jamboard も導入。Jamboard 上で Web 会議をしながら情報を共有するといった従来にない働き方を実践しています。たとえば、設計部門では、Google ドライブに設計図を保存しておき、Jamboard 上で設計図を開き打ち合わせをすることで、効率的かつ効果的な作業を行なっています。

G Suite のよくある質問と回答

続いて、番組では、G Suite に関する次の 8 つの質問に答えます。G Suite の導入を検討している方だけでなく、すでに導入済みで本格的な活用を計画されている方にとってもお役に立つ内容です。
  1. G Suite の新規ユーザーはどの程度いますか?
  2. G Suite の特徴は?
  3. 100% クラウドのメリットとは?
  4. G Suite を導入する場合は既存の Microsoft Office 形式のファイルをすべて Google ドキュメント形式などに変換する必要がありますか?
  5. Google Drive をファイル サーバーのように使えますか?
  6. 現在使っているシステムからの移行方法を教えてください。
  7. 既存システムと平行稼働が必要な場合に使えるサービスはありますか?
  8. G Suite を導入したいのですが、個人 Gmail のアクセスを制限するにはどのようにすればいいですか?


番組で説明した資料はこちらで公開しています。

Cloud OnAir では、各回 Google Cloud のエンジニアがトピックを設け、Google Cloud の最新情報を解説しています。過去の番組、説明資料、さらには視聴者からの質問と回答はこちらよりご覧いただけます。 最新の情報を得るためにもまずはご登録をお願いします。


日本 5 大証券会社の 1 社として、国内金融・証券業界に大きな貢献を果たしている大和証券。求められるセキュリティ基準の高さから、クラウド移行が難しいとされる金融・証券業界ですが、大和証券はそこに向けた最初の一歩を踏み出しました。それが、Google Cloud Speech-to-Text を活用した音声によるテキスト入力の実現。その背景と、今後も見据えた狙いについて、同社システム企画部の皆さんにお話をお伺いしました。

利用している Google Cloud Platform サービス

Google Cloud Speech-to-TextCloud Translation

写真左から

  • システム企画部 兼 大和証券グループ本社 システム企画部 主任 川口 哲平氏
  • システム企画部 ITソリューション課長 兼 大和証券グループ本社 システム企画部 ITソリューション課長、副部長 武部 啓造氏
  • システム企画部 兼 大和証券グループ本社 システム企画部 主任 鈴木 宏美氏

大和証券株式会社

1902 年に設立された「藤本 ビルブローカー」を源流とする老舗証券会社。大和証券のほか、大和証券投資信託委託や大和総研、大和企業投資などを傘下に擁する大和証券グループ本社の基幹企業。グループとして掲げる企業理念は「信頼の構築」「人材の重視」「社会への貢献」「健全な利益の確保」。現在の従業員数は 16,516 名(2018 年 9 月時点)。

Google Cloud Speech-to-Text なら金融・証券業界独自の用語にもスムーズに対応

「私が所属している先端 IT 戦略部(現在は組織改編によって「システム企画部」に)は、先端テクノロジーを駆使して、業務の効率化を図る取り組みを企画する部署。今回お話しする Google Cloud Speech-to-Text(Speech-to-Text) は、従業員のテキスト入力をサポートするために導入を決定しました。」(武部さん)

全国 160 か所の拠点で、約 9,000 名の従業員が働く大和証券では、取引情報、顧客情報の共有化のため、商談の記録を CRM(Customer Relationship Management)システムに細かく入力するという業務があります。しかし、その入力内容は個人差が大きく、特にキーボード タイピングを苦手とする従業員が入力に手間がかかりすぎることから、必要最低限の情報しか入力しなかったり、逆に慣れないキーボードで丁寧に入力しようとした結果、勤務時間が増加してしまう問題が発生していたといいます。

「実はその少し前に、お客様からの電話内容を録音して音声認識技術でテキスト化するという取り組みをおこなっていたのですが、これをその問題の解決に使えるのではないかと考えました。ただ、その機能が社内で実際にどれくらい使われるかはまったくの未知数。そこで今回は、使った分だけの従量課金でコストを抑えられるクラウド型の音声認識エンジンを利用することにしました。」(武部さん)

そうして、さまざまな音声認識ソリューションを評価していくなかで、最終的に  Speech-to-Text にいきついたのは、認識精度が高かったから。金融・証券業界では、「取引残高報告書」を「取残(とりざん)」と略すなど、一般に使われない独自の用語が多数存在していますが、Speech-to-Text はわずかな学習と独自辞書の追加で、さまざまな専門用語の認識をマスター。前後の文脈をもとに正しくテキスト化できたといいます。

「方向性の確定後、実際に開発をおこなったのは大和証券グループのシステム開発会社である大和総研なのですが、6 名のエンジニアで約 4 か月かけて、既存の CRM システムに Speech-to-Text を駆使したテキスト入力機能を組み込むことができました。2018 年 5 月の連休明けから各拠点に展開し、利用を開始しています。」(武部さん)
CRM システムに組み込んだ Speech-to-Text を活用したテキスト入力機能

「導入後の効果の検証はこれからなのですが、現場からは、これまでキーボード タイピングが苦手で入力が億劫だったという方から、より細かく商談内容を記録できるようになったという声が届いています。今後は、そうして情報量の高まった商談記録が実際のビジネスに活用されていくことに期待しています。」(鈴木さん)

「なお、コストに関しては上限を設定しているほか、マイクをオンにしたまま放置してしまって無駄な利用料金が発生してしまわないよう、無音状態が続くと自動的に切れる設定にしています。」(武部さん)


将来的にはクラウドでグループ各社、各拠点の情報共有を進めていきたい

独特な金融・証券用語を正確にテキスト化できる Speech-to-Text の導入によって、当初の課題をクリアした大和証券ですが、その成功から、新たな可能性が生じ、次なる取り組みが生まれていったと言います。

「思った以上に精度が高かったため、CRM システムへの入力だけでなく、広範な用途に使えるよう社内ポータルに音声認識システムを配置し、開放しました。ボタンを押して、マイクに向かって話すとその内容がテキストデータとして表示されます。それをメールやドキュメントなどにコピー&ペーストして利用してもらうイメージです。また、聴覚障害のあるスタッフが社内研修用のビデオを見る際に、自動的に字幕が付くような仕組みも作りました。これまでは別途、字幕付きビデオを作っていたのですが、Speech-to-Text を活用すれば、動画の音声をリアルタイムにテキスト化し、画面上に表示することができます。精度面でもまったく問題ありませんし、リアルタイムの情報共有の実現と同時に、結果的にはその手間と費用の大幅削減にもつながりました。」(武部さん)

なお、武部さん曰く、Speech-to-Text の導入には、これから拡大していくクラウド活用を見据えたテストケースという意味合いもあったそうです。情報セキュリティがとりわけ厳しい金融・証券業界では、外部にデータを置くことがタブーとされています。そこで、まずはデータを外部サーバーに保存することなく利用できる API でクラウド サービスを使い始めることにしたのです。

そして、現在は Speech-to-Text 以外の機能にも利用を拡大。具体的には Cloud Translation を社内の翻訳業務に導入しました。こちらも社内ポータルに「大和翻訳センター」という名称で配置し、誰でも使えるようにしています。

「大和証券では、機密保持の観点から外部の翻訳サービスの利用も禁止されているため、これまで英語で書かれた契約書やレポートの翻訳が大きな負担になっていました。しかし、Cloud Translation なら、Speech-to-Text 同様、社内システム上で問題なく利用できます。これまでも多くの社員にとって有用なレポートなどは、本部で翻訳し配布していたのですが、大和翻訳センター設置後は、コストや工数をかけずに、それぞれの社員が個別に必要な情報を素早く翻訳できるようになりました。」(川口さん)

現在はこうした API レベルでの活用に留まっている大和証券の GCP 活用ですが、今後は、段階的にその活用を深めていきたいと武部さんは言います。

「大和証券グループの大きな課題として、各社、各拠点が閉じたネットワークで業務をおこなっており、情報の共有が阻害されているというものがあります。やむを得ないことではあったのですが、今後はそうもいきません。セキュリティの問題をクリアにしつつ、クラウドへのデータ保存・共有などを実現していきたいですね。たとえば、現在は社内の地図データにクリッピングしている顧客情報を Google Maps Platform に移行できたらルート検索などの機能を組み合わせて使えるようになり、さらに業務効率が改善するのではないかと思っています。また、今後、AI 技術の導入によって、大和証券グループが進めている働き方改革がさらに推進されていくことにも期待しています。」(武部さん)

「大和翻訳センターの機能改善要望で最も多いのが、PDF や画像などに記載された英文(画像)の翻訳・テキスト化。今後、クラウド対応を進めていくなかで実現していきたいですね。」(鈴木さん)

「また、検索機能もさらに活用していきたいですね。現在は拠点ごとに情報が散乱しており、その鮮度もまちまちです。誰もが最新のデータを簡単かつ即座に入手できる環境の構築が急務だと考えています。また、アクセスログをきちんと解析して、利便性の向上にも役立てていきたいです。」(川口さん)


その他の導入事例はこちらをご覧ください。

Cloud OnAir は、Google Cloud の製品をわかりやすく解説し、最新の情報などをいち早く皆様にお伝えする Online 番組です。

6 月 13 日の放送では、Google Cloud Platform を実際にご活用いただいているアサヒプロマネジメント株式会社にご出演いただき、データ活用やデータ分析の取り組みについて伺いました。

データ活用の取り組み

アサヒグループにおいて、財務・人事・総務など、グループ全体の管理の請け負うアサヒプロマネジメント。グローバル化が進み、事業規模も急激に拡大する中で、IT を含むさまざまな業務に関してグループの最適な戦略・施策を立案、実行する会社です。このアサヒプロマネジメント株式会社では、事業拡大を支えるためにどのようなデータを扱っているのか、データを何のために使っているのか、データの活用によってどのようなことが改善されたのか、これらの点について、同社の清水博さんにお話いただきました。

清水さん曰く、社内に蓄積されたさまざまなデータ(たとえば POS 販売実績データ)だけでなく、メーカーや卸業者が持つ市場データや位置情報を組み合わせて分析し、より適切な戦略立案を進める上で、マーチャンダイジング(MD)業務の革新がポイントになるそうです。

アサヒプロマネジメントでは、MD の業務生産性向上を目指し、クラウドを利用した新システムを構築しました。その結果、各種データ・資料を一元管理、集計することで年間で MD 一人あたりの作業時間を 56% 削減することができたそうです。この結果を受けて、MD の業務内容を単純なデータ分析から、提案・検証および課題解決業務へとシフトさせることができたわけです。

GCP の活用

オンプレミスでの運用がまだまだ多いというアサヒプロマネジメントですが、クラウド化への方針転換は 2 年ほど前に遡るそうです。IT 部門として、グループ内の IT システムの品質を維持し、安定運用を継続することが第一と捉え、その実現の障害となることをいかに取り除くかを熟慮した結果、フルマネージドサービスの活用に至ったと、清水さんは語ります。

しかし、一方で、クラウドへの移行に十分な知見・経験が社内の既存の開発体制だけでは不十分であり、移行には時間がかかると判断。クラウドに精通した外部リソースの活用で、クラウドへの移行を迅速に推進することができたそうです。

フルマネージドな GCP を活用した新システムのもとで大きな効果を実証できた今、アサヒプロマネジメントでは、ますます変化し、多様化する消費者のニーズに対応するため、Systems of Engagement(SoE)と Systems of Records(SoR)を上手に連携したシステムの実現を目指していくとのことです。



番組で説明した資料はこちらで公開しています。


Cloud OnAir では、各回 Google Cloud のエンジニアがトピックを設け、Google Cloud の最新情報を解説しています。過去の番組、説明資料、さらには視聴者からの質問と回答はこちらよりご覧いただけます。 最新の情報を得るためにもまずはご登録をお願いします。


※この投稿は米国時間 2019 年 5 月 15 日に Google Cloud blog に投稿されたものの抄訳です。

多くの組織は、アナリティクスや販売数、業績評価指標などのデータで満たされた大規模なデータ ウェアハウスを運用しています。ただ、データはこうしたものに限られるわけではなく、たとえば星でいっぱいの夜空というような自然界も膨大なデータセットの供給源です。BigQuery GIS は、もともとは地球上の地理空間データを利用するユーザーのニーズに応えるために設計されたものですが、その球面座標系と組み込みの変換関数は、球面座標を使用するもう 1 つの分野、天文学にも非常に適しています。

BigQuery が天文データセットの分析に適した優れたプラットフォームである理由は次のとおりです。
  • BigQuery はオンライン分析(OLAP)向けに作られており、非トランザクショナルな膨大なデータセットの処理に最適化されています。毎年という周期(プロジェクトによって異なります)で発行される天文カタログの操作の大半は、これに当てはまります。
  • BigQuery は、BigQuery GIS を使った球面幾何学でのクエリをサポートします。天球上にオブジェクトを配置するには球面幾何学が必要です。
  • BigQuery GIS は専用のデータベース プラットフォームとほぼ同じスピードで天文データのクエリを実行できるほか、フルテーブル スキャンに使用すると、もっと高速になることがあります。

探求すべき天文データが足りなくなることはありません。たとえば、カタログ データは望遠鏡による観測結果を巨大なテーブルにまとめたものです。大規模なカタログ データセットのなかには数十億もの天体のさまざまな観測値を集めたものがあり、一部のデータセットには数時間とか数年といったスパンの観測値も含まれています。

人工衛星に搭載された望遠鏡である WISEGaia は、高解像度のイメージ データを地球に送ってきます。地上に新しく設置される巨大望遠鏡の LSST もまもなく稼働します。LSST は、観測データのカタログを 10 年間にわたって公開することが義務づけられています。この投稿記事の後半では、この種のカタログ データを BigQuery GIS で処理する方法を探ります。

天球座標系の基礎

BigQuery による天文カタログ データのクエリ例を示す前に、GIS のニーズをサポートするため BigQuery GIS に実装されている幅広い関数群について簡単に説明しましょう。

少しの間、地球を見下ろしてみる

地球は球体ですが、地球上の位置が GPS(全地球測位システム)で簡単に得られる緯度と経度の 2 次元情報で表現されることについて考えてみましょう。GPS は「緯度と経度」の座標であなたの位置を特定し、あなたが行きたい場所に案内してくれます。

移動距離を知りたいとしましょう。高校で習った幾何学を覚えている方なら、ピタゴラスの定理で移動距離を計算できると考えるかもしれません。近距離であればうまくいきそうな気がしますが、距離が長くなると話がややこしくなります。

移動距離を計算するには、出発点と目的地の緯度および経度をユークリッド平面上のデカルト座標に変換し、角度をメートルまたはマイルに変換する必要があります。ところが、ユークリッド距離は平面幾何学の問題ですが、あいにく地球の表面は平面ではなく球面なのでピタゴラスの定理は使えません。数学的な問題の大半は古代ギリシャやイスラムの数学者たちが 1000 年前に解決していますが、この変換が大変なことは今でも変わりありません。

幸いなことに、BigQuery GIS はこの種の計算を実行できる Google の S2 Geometry ライブラリを利用しており、上記の複雑な幾何学計算を単純な標準 SQL で実行できます。地球上の地点間の距離だけでなく、領域やポリゴンなどを使った複雑な計算にも対応可能です。BigQuery GIS は非常に強力で、非常に使いやすいものなのです。

再び天空に向かって

地球表面の幾何学を理解したところで、もう一度星空を見上げてみましょう。BigQuery GIS は、地球上の物体の移動とまったく同じ基本概念に沿って天体の移動を追跡します。つまり、夜空における星の位置を指定するときには、地球上の物体の位置を指定する場合と同じように、緯度経度のような座標を割り当てます。宇宙空間内での星の位置は、この座標によって正確に指し示すことができます。

とはいえ、宇宙空間は球体ではありません。宇宙は、恒星、銀河、ブラックホール、惑星、クエーサー、パルサー、星雲などが、文字どおり完全かつ 3 次元的に無限に広がっている空間です。それらは何光年も離れたところに散らばっており、地球上の自宅から最寄りの Google オフィスへの行き方を GPS で調べるのとはまったく異なります。

面白いのはここからです。上で挙げた天体は非常に遠いところにあるため、私たち人間は、近くにある天体と遠くにある天体を簡単に区別することができません。夜空を見上げたときの星のように、地球を中心とする大きな黒い球体の表面で光る点のようなものです。この投稿では天文学の歴史に深入りするつもりはありませんが、科学史に詳しい方であれば、古代ギリシャの科学者、そして彼らの知的後継者が天体を記述するときのモデルはまさにこのようなものだったことを思い出されるでしょう。興味のある方には Thomas S Kuhn の著書『The Structure of Scientific Revolutions』(科学革命の構造)をお勧めします。

天空に戻りましょう。夜空とすべての天体が、地球を中心とする大きな球と区別がつかないなら、天体に緯度経度を与えるという先ほどの提案も合理的に見えてきます。実際、天文学者はそうしています。彼らの座標は赤経(ra)および赤緯(dec)と呼ばれ、緯度や経度と同じように機能します。赤経は、時分秒を使った古い記法で表現されることがあります。

を見てみましょう。ベガ(映画『コンタクト』で有名な星)は、赤経 18h 36m 56s、赤緯 +38° 47′ 1″ にあります。幸いなことに、現代の天文学データは、地理学データと同様に度を単位とする小数表現で座標を格納しており、その最近の記法によれば、ベガの赤緯(+39°)は米国カンザスシティの緯度(北緯 39 度)と同じです。これは、カンザスシティでは毎日 1 回ずつベガが真上に見えるということです(夜であればの話ですが)。赤経が歴史的に 24 時間記法で表現されてきたのは、明らかにこの自転に由来します。


ご覧のように、天文学では空を見上げるのに対して、地理学では地面を見下ろすという違いはあるものの、天球座標系と地理座標系はよく似ています。

そこで、地球上の物体の位置を示すために緯度経度を使用するのと同じように、天球上の天体の位置を示すために ra と dec の球面座標を使用することは、有効な方法として確立されています(緩やかな形で)。ただし、次の点に注意することが重要です。

  1. 天球は、その主旨からして真球なので、地球が少し平べったいこと(楕円体になっていること)による GIS システムの補正機能は無効にする必要があります。好都合なことに、BigQuery GIS はデフォルトで真球を使用しています。
  2. 天球の極は、地球の地理学的な極と一致します。星の位置を示す座標(ra、dec)は固定されたままです。

天文学者が実行すべきクエリにはさまざまなものがありますが、LSST の例がこちらにいくつか示されています。この投稿では WISE データでの例を示します。

例とデータセット

WISE データセットには、天体のマルチエポック(または時系列)データをまとめたテーブルが含まれています。なかでも面白い例が、食変光星である「こと座ベータ星」の光度曲線です。BigQuery AllWise データセットに含まれるこの光度曲線データにアクセスするためのクエリは次のとおりです。


  SELECT
  w1mpro_ep,
  mjd,
  load_id,
  frame_id
FROM
  `bigquery-public-data.wise_all_sky_data_release.mep_wise`
WHERE
  source_id_mf='3425p651_ac51-032187'
ORDER BY
  mjd ASC

返されたデータを Data Studio でプロットすると、次のようになります。


ベンチマークとして使用できる数値を得るため、私たちは天文学者が興味を持つような現実的なクエリを試してみることにしました。そして、未加工のテーブルがロードされた状態での初期テスト実行後、次に示す 4 つの重要な最適化を施しました。

  • テーブルを分割しました。
  • 天体を三角測量するレベル 7 HTM 空間インデックスキーの整数値でデータをクラスタ化しました。
  • ジオメトリ型の POINT を使用して天体の位置を事前に計算しました。
  • ST_WITHIN の代わりに ST_CONTAINS を使用して、空間のリージョンを制限し、データセットのサイズを小さくしました。

最終的なクエリは次のとおりです。このクエリは、予想される天文データ クエリのタイプに最も近いクエリのなかで代表的なものです。


  CREATE TEMP FUNCTION
  ArcSecondDistance(p1 GEOGRAPHY, p2 GEOGRAPHY,
    d FLOAT64) AS (ST_DISTANCE(p1,p2) < d * 30.8874796235);
SELECT
  source_id_mf,
  point
FROM
  `bigquery-public-data.wise_all_sky_data_release.mep_wise`
WHERE
  ArcSecondDistance(point,
    ST_GEOGPOINT(201.5, -2.6),60)
  AND ST_CONTAINS(
   ST_GEOGFROMTEXT('Polygon((201.00 -3.10,201.00 -2.10,202.00 -2.10,202.00 -3.10, 201.00 -3.10))'),point)

上記 4 つの最適化の組み合わせにより、17 TB のテーブルのクエリに要する時間の中央値は 60 秒から 4 秒に下がりました。これによって、単一の天文データソースから関連情報を迅速に取得するよう最適化されたデータベース プラットフォームに匹敵する性能が、BigQuery からも得られるようになりました。さらに、フルテーブル スキャンに関しては、BigQuery は大きく優位に立つ可能性があります。

何よりも素晴らしいのは、BigQuery GIS と天文データセットがまだ開発初期の段階にあることです。私たちはもっと多くの天文カタログを BigQuery の一般公開データセットに追加していきます。WISE データセットは、いくつか計画しているうちの最初のものに過ぎません。

BigQuery GIS の初心者は、こちらのドキュメントを読むことで、地球上の地理データを分析する方法を学べます。BigQuery を使って自然現象を記録することに興味のある方は、BigQuery GIS を使用したハリケーン進路のプロットに関するこちらの優れたチュートリアルをご覧ください。ビジネス ユースで地上の GIS データのアナリティクスを実行する方法については、ニューヨーク市でのシティ バイクの移動データに関するチュートリアルが役に立ちます。地理空間(または天文)データを使用するにあたって何か発見がありましたら、ぜひ私たちにお知らせください。

- By Ross Thomson, Cloud Solutions Architect


※この投稿は米国時間 2019 年 5 月 29 日に Google Cloud blog に投稿されたものの抄訳です。

昨今のクラウドの普及によって、クラウド上にデプロイしたアプリケーションをエンドユーザーに広く提供することが一般的になってきました。それと同時に、不特定多数のエンドユーザーがクラウド上のリソースに直接アクセスすることを念頭に置くことによって、柔軟にクラウドの利点を活かしたシステムアーキテクチャを設計できるようになります。

本稿では、主にエンドユーザーに対してコンテンツを配信するサービスを提供している方に向けて、クラウド上のリソースを理解し、その強みを最大限活用することによって、従来の課題をスマートに解決し、サーバの開発や運用にかかるコストを削減する方法を、具体的なアーキテクチャの設計を通してご紹介します。全体を通して、エンドユーザーが署名付き URL を使って直接 Cloud Storage に画像ファイルをアップロードし、そのファイルを配信するシステムを Google Cloud Platform ( GCP )上に構築することを目指します。

本稿でご紹介するアーキテクチャのように、エンドユーザーからの画像ファイルのアップロードを受け付けるサーバを、クラウドを活用せず、大規模に開発・運用することは非常に困難な場合があります。考慮しなければならないものとして、例えばファイルのアップロードを担当するサーバのプロセスへのリクエストを一度キューに入れてフロー制御をしたり、リクエストの過負荷によるシステムダウンを防ぐ必要があります。さらに、関連するサーバの有限のリソース( RAM など)に対して、適切な制限を設定する必要もあるでしょう。

加えて、非常に大きなファイルをサーバにアップロードできるようにするためには、サーバの開発と運用にさらに大きなコストが発生する可能性があります。また、アップロードを担うサーバを何百万ものユーザーが使用する場合、そのスケーラビリティやユーザーエクスペリエンスを確保するために多くの作業が必要になります。

このような課題に対応するために、 Cloud Storage を画像ファイルのアップロードサーバとして活用するシステムアーキテクチャを提案します。次項から実際にその設計方法を見ていきましょう。なお、本稿で使用するソースコードはすべて GitHub 上で管理しています。

画像アップロード機能の構築をはじめましょう

このアーキテクチャに必要な GCP コンポーネントを掘り下げる前に、まずは要件を定義しましょう。
  • オペレーションコストを削減するために、マネージドサービスを可能な限り使用する。
  • 認証されたユーザのみがファイルをアップロードできるようにする。
  • アップロードされたコンテンツに対して適切にバリデーションを行う。

上記の要件を満たすために、次のアーキテクチャを提案します。

このアーキテクチャの主な特徴として、ファイルのアップロードからその配信に至るまで、全てのコンポーネントをサーバーレスに使用可能なアプリケーションランタイム、もしくはマネージドサービスによってのみ構成している点が挙げられます。これは一つ目の要件を満たすための設計指針に基づいています。次に、各ステップについて詳しく解説します。
  1. App Engine がユーザーからのリクエストを受け取ったら、 Cloud Storage の特定のバケット・特定のオブジェクトキーに対して PUT リクエストを実行可能とするための署名付き URL を生成し、ユーザーに返却する。このとき、 App Engine 上では任意のアプリケーションロジックに基づいて、認証済みのユーザーに対してのみ、当該署名付き URL を払い出すようにする。
  2. 署名付き URL を返却されたユーザーは、それを用いて Cloud Storage の特定のバケット・特定のオブジェクトキーに対してファイルをアップロードする。
  3. Cloud Storage へのファイルのアップロードを終えると、 Cloud Storage の finalize イベントをトリガーとして Cloud Functions を起動する。Cloud Functionsではアップロードされたファイルをバリデーションする。
  4. Cloud Functions上で、アップロードされたファイルが適切な形式、かつ適切なサイズであることを確認したら、 Cloud Vision API を用いて不適切なコンテンツをフィルタリングする。
  5. 手順の 3 と 4 の検証が完了したら、アップロード先のバケットから配信用のバケットにファイルをコピーする。
  6. コピーが完了した画像はインターネットに公開される。

次に、上記の手順を実現するための実装について解説します。

署名つき URL を App Engine Standard runtime 上で生成する

Cloud Storage には、個々のエンドユーザーが特定のリソースに対し、特定のアクションを実行できるようにするための署名付き URL と呼ばれる機能があります。署名付き URL を使用すると、ファイルを安全にアップロードするために、特定のエンドユーザーにのみ有効な一時認証情報を生成することができます。これについて、 Google Cloud 公式のクライアントライブラリを使用すると、簡単にこの機能を実装することができます。本稿ではこの機能を用いて、特定のエンドユーザーのために署名付き URL を生成させる API サーバを App Engine Standard runtime 上に作成します。

実装は公式のライブラリに頼るとしても、署名付き URL を生成する実際の流れについては理解しておくことが望ましいため、処理の流れを次に示します。

  1. 任意のバイト列を署名するためのサービスアカウントを用意する。
    1. 新たなサービスアカウントを作成するか、 App Engine のデフォルトサービスアカウントなどの既存のサービスアカウントに必要なパーミッションを付与する。
  2. 署名付き URL の生成に必要なシグネチャを生成する(本稿では Content_MD5Canonicalized_Extension_Headers を省略する)。
    1. ファイルのアップロードを受け入れる HTTP_Verb には、 PUT を選択する。
    2. Content-Type の値はアップロードされるファイルの MIME type によって異なる。この値はエンドユーザーから送信される API リクエストによって決定することとする。
    3. 有効期限をUnixタイムで X-Goog-Expires にセットする。 API リクエストを受け取った時点から未来の時間を設定する。本稿では15分と設定する。
    4. Canonicalized_Resource としてアップロード対象のバケットとオブジェクトキーを設定する。オブジェクトキーは UUID などを用いて、動的に生成することで、既存のオブジェクトキーとの重複を回避する。
  3. 手順 1 で用意したサービスアカウントの秘密鍵を用いて、手順 2 で生成した文字列を署名する。

各手順の詳細については、公式ドキュメントを参照してください。本稿では上記の手順の例として、 Go を用いて実装します。


  package main

import (
        "context"
        "encoding/base64"
        "fmt"
        "log"
        "net/http"
        "os"
        "time"

        "cloud.google.com/go/storage"
        "github.com/google/uuid"
        "golang.org/x/oauth2/google"
        iam "google.golang.org/api/iam/v1"
)

var (
        // iamService is a client for calling the signBlob API.
        iamService *iam.Service

        // serviceAccountName represents Service Account Name.
        // See more details: https://cloud.google.com/iam/docs/service-accounts
        serviceAccountName string

        // serviceAccountID follows the below format.
        // "projects/%s/serviceAccounts/%s"
        serviceAccountID string

        // uploadableBucket is the destination bucket.
        // All users will upload files directly to this bucket by using generated Signed URL.
        uploadableBucket string
)

func signHandler(w http.ResponseWriter, r *http.Request) {
        // Accepts only POST method.
        // Otherwise, this handler returns 405.
        if r.Method != "POST" {
                w.Header().Set("Allow", "POST")
                http.Error(w, "Only POST is supported", http.StatusMethodNotAllowed)
                return
        }

        ct := r.FormValue("content_type")
        if ct == "" {
                http.Error(w, "content_type must be set", http.StatusBadRequest)
                return
        }

        // Generates an object key for use in new Cloud Storage Object.
        // It's not duplicate with any object keys because of UUID.
        key := uuid.New().String()
        if ext := r.FormValue("ext"); ext != "" {
                key += fmt.Sprintf(".%s", ext)
        }

        // Generates a signed URL for use in the PUT request to GCS.
        // Generated URL should be expired after 15 mins.
        url, err := storage.SignedURL(uploadableBucket, key, &storage.SignedURLOptions{
                GoogleAccessID: serviceAccountName,
                Method:         "PUT",
                Expires:        time.Now().Add(15 * time.Minute),
                ContentType:    ct,
                // To avoid management for private key, use SignBytes instead of PrivateKey.
                // In this example, we are using the `iam.serviceAccounts.signBlob` API for signing bytes.
                // If you hope to avoid API call for signing bytes every time,
                // you can use self hosted private key and pass it in Privatekey.
                SignBytes: func(b []byte) ([]byte, error) {
                        resp, err := iamService.Projects.ServiceAccounts.SignBlob(
                                serviceAccountID,
                                &iam.SignBlobRequest{BytesToSign: base64.StdEncoding.EncodeToString(b)},
                        ).Context(r.Context()).Do()
                        if err != nil {
                                return nil, err
                        }
                        return base64.StdEncoding.DecodeString(resp.Signature)
                },
        })
        if err != nil {
                log.Printf("sign: failed to sign, err = %v\n", err)
                http.Error(w, "failed to sign by internal server error", http.StatusInternalServerError)
                return
        }
        w.WriteHeader(http.StatusOK)
        fmt.Fprintln(w, url)
}

func main() {
        cred, err := google.DefaultClient(context.Background(), iam.CloudPlatformScope)
        if err != nil {
                log.Fatal(err)
        }
        iamService, err = iam.New(cred)
        if err != nil {
                log.Fatal(err)
        }

        uploadableBucket = os.Getenv("UPLOADABLE_BUCKET")
        serviceAccountName = os.Getenv("SERVICE_ACCOUNT")
        serviceAccountID = fmt.Sprintf(
                "projects/%s/serviceAccounts/%s",
                os.Getenv("GOOGLE_CLOUD_PROJECT"),
                serviceAccountName,
        )

        http.HandleFunc("/sign", signHandler)
        log.Fatal(http.ListenAndServe(fmt.Sprintf(":%s", os.Getenv("PORT")), nil))
}

コード内で言及しているように、署名付き URL を生成するための方法は大きく分けて二つあります。

一つはサービスアカウントに紐付いた秘密鍵を使って署名を行う方法です。この方法は自分自身の手で秘密鍵を管理する必要があります。これは Google Compute EngineGoogle Kubernetes Engine などのフルマネージドでないランタイム上で API サーバを開発・提供するケースに適しています。

二つ目の方法は、 Cloud Identity and Access Management が提供する Service Account API の中の一つである serviceAccounts.signBlob を用いる方法です。これを用いることによって、アプリケーションランタイムで秘密鍵を管理せずに署名をすることができます。本稿では、秘密鍵の管理を避けるために、この方法を採用しています。

補足として、 Cloud Storage で署名付き URL を生成するためにサービスアカウント付与すべきパーミッションを次に列挙します。

  • storage.buckets.get
  • Storage.objects.create
  • Storage.objects.delete

二つ目の方法に示した serviceAccounts.signBlob API を使用する場合には、  roles/iam.serviceAccountTokenCreator ロールを付与します。このロールにまとめられたパーミッション群は公式ドキュメントで確認することができます。

署名付き URL を使ってファイルをアップロードする

ここまで説明した機能によって、ユーザーは App Engine 上で生成された署名付き URL を使って Cloud Storage にファイルを直接アップロードできるようになりました。 ここでは、署名付き URL を介して Put Object と呼ばれる API を呼び出すことで、実際にファイルをアップロードします。

次に示すのは、モバイルアプリケーションや Web ブラウザなどで実行される処理を想定した Go によるサンプルコードです。前出の App Engine 上で動かすことを想定したソースコードと対応するものになります。


  package main

import (
        "bytes"
        "fmt"
        "io/ioutil"
        "log"
        "net/http"
        "net/url"
        "strings"
)

const signerUrl = "<APPENGINE_URL>"

func getSignedURL(target string, values url.Values) (string, error) {
        resp, err := http.PostForm(target, values)
        if err != nil {
                return "", err
        }
        defer resp.Body.Close()
        b, err := ioutil.ReadAll(resp.Body)
        if err != nil {
                return "", err
        }
        return strings.TrimSpace(string(b)), nil
}

func main() {
        // Get signed url from the API server hosted on App Engine.
        u, err := getSignedURL(signerUrl, url.Values{"content_type": {"image/png"}})
        if err != nil {
                log.Fatal(err)
        }
        fmt.Printf("Signed URL here: %q\n", u)

        b, err := ioutil.ReadFile("/path/to/sample.png")
        if err != nil {
                log.Fatal(err)
        }
        // Generates *http.Request to request with PUT method to the Signed URL.
        req, err := http.NewRequest("PUT", u, bytes.NewReader(b))
        if err != nil {
                log.Fatal(err)
        }
        req.Header.Add("Content-Type", "image/png")
        client := new(http.Client)
        resp, err := client.Do(req)
        if err != nil {
                log.Fatal(err)
        }
        fmt.Println(resp)
}


Cloud Storage の Bucket Lock と Object Lifecycle を署名付き URL と組み合わせる

前出のアーキテクチャ図からもわかるように本稿では、ファイルのアップロード先を担うバケット (Uploadable Bucket)と、ファイルの配信を担うバケット (Distribution Bucket)の二つの Cloud Storage バケットを定義しています。しかしながら、これらのバケットをデフォルトのまま取り扱うと、大きく分けて二つの問題が発生します。

一つ目に、アップロードされた全てのオブジェクト(ファイル)は、それにかかる検証を終えた後に Distribution Bucket にコピーされますが、何もしなければそれらのファイルは Uploadable Bucket に存在し続けることになります。しかしそれらのオブジェクトが、それ以降の処理でいずれのコンポーネントからも参照されることはありません。したがって、コピーが完了したオブジェクトを Uploadable Bucket に残し続けておくことには意味がありません。言い換えれば不要な費用が発生し続けるということになります。

二つ目に、エンドユーザーは署名付き URL の期限内であれば、何度も特定のバケット・オブジェクトキーに対してファイルをアップロードできてしまうということです。言い換えれば、アップロードされたファイルをサーバ側のコンポーネントが参照する度に、その中身が書き換わっている可能性があるということになります。これにより、後述の Cloud Functions で動かすアプリケーションにおいて、リトライと共に冪等性を担保することが困難になります。用途によってはそのような仕様を持っていても構わないのですが、本稿ではよりシンプルな仕様を維持するために、これを問題と捉えて対策します。

さて、これらの問題を解消するために、Object Lifecycle ManagementRetention Policy という二つの Cloud Storage 固有の機能を活用します。

最初に、ライフサイクルを定義することによって、不要なオブジェクトが Uploadable Bucket に残り続けないようにします。これを実現するためには、バケットや署名付き URL の有効期限などに合わせて Cloud Storage 上に存在するオブジェクトのライフサイクルを定義します。本稿では次のように、オブジェクトが Uploadable Bucket にアップロードされてから1日が経過したら、それを不要と判断して削除するライフサイクルを定義します。


  {
  "rule": [
    {
      "action": {"type": "Delete"},
      "condition": {"age": 1}
    }
  ]
}

次に、ユーザーが有効期限内であったとしても何度もファイルをアップロードできないようにするため、保持ポリシーを定義します。

バケットロックと保持ポリシーを使用して、あるバケットにおけるオブジェクトの保持期間を設定し、その期間中はオブジェクトの上書きや削除ができないようにします。この保持期間を署名付き URL の有効期限に定める値と一致させることで、署名付き URL を介してアップロードされたオブジェクトが、エンドユーザーによって上書きされることを防ぐことができます。なお、前述のライフサイクルの定義によって、オブジェクトは1日が経過すると削除されるため、ライフサイクルとの競合が起こらないようにしてください。仮にライフサイクルとの競合が起きてしまうと、正常にオブジェクトを削除できないなどの問題が発生します。補足として、バージョン管理が有効となっているバケットでは、このバケットロックと保持ポリシーを有効化できない点に注意してください。

上記の設定を有効化したバケットを作成するためには、次に示すコマンドを実行します。


  REGION="REGION"
PROJECT_ID="PROJECT_ID"
UPLOADABLE_BUCKET="UPLOADABLE_BUCKET"
DISTRIBUTION_BUCKET="DISTRIBUTION_BUCKET"
LIFECYCLE_POLICY_FILE="/path/to/lifecycle.json"

# Creates the uploadable bucket
gsutil mb -p $PROJECT_ID -l $REGION --retention 900s gs://$UPLOADABLE_BUCKET
# Creates the bucket for distribution
gsutil mb -p $PROJECT_ID -l $REGION gs://$DISTRIBUTION_BUCKET
# Set lifecyle for the uploadable bucket
gsutil lifecycle set $LIFECYCLE_POLICY_FILE gs://$UPLOADABLE_BUCKET

ファイルを検証し、コピーする

これまで、署名付き URL を生成してファイルを直接 Cloud Storage にアップロードし、アップロードされたファイルを適切に管理する方法について解説してきました。しかし、アップロードされたファイルを実際に多くのエンドユーザーに配信する前に、アプリケーションの仕様に合わせてそのファイルの妥当性を検証する必要があります。ここでは、認証されたユーザーによってアップロードされたファイルの有効性・妥当性を検証する方法と共に、検証が済んだオブジェクトを Distribution Bucket にコピーする方法について解説します。

まず、本稿ではこれを行うために検証・コピーを行う処理を Cloud Functions 上に実装します。Cloud Functions を実行するためには、特定の Cloud Storage のイベントをトリガーとして、予めデプロイしておいた Cloud Functions を呼び出します。対象となる Cloud Storage のイベントには、オブジェクトの書き込み完了を意味する google.storage.object.finalize イベントを用います。

次に、どのようにバリデーションやそれに付随する処理を行うべきかを考える必要があります。これを順序立てて整理しましょう。

  1. アップロードされたオブジェクトと同一のオブジェクトキーを持つオブジェクトが Distribution Bucket に存在しないことを確認する。
    1. オブジェクトキーが存在する場合は、以降の処理を停止する。
    2. これは Cloud Functions が at least-once の実行を保証することを考慮し、同様の処理を不要に実行させないためのものである。
  2. Uploadable Bucket 内のオブジェクトのメタデータから、Content-Type と ファイルのサイズを取得する。
    1. サイズが規定サイズを超過するのものでないことを確認する。規定サイズはアプリケーションの仕様に基づいて決定する。超過している場合は以降の処理を停止する。
    2. 本稿ではアップロードされたファイルを Cloud Functions のメモリ上に一度展開するため、Cloud Functions のRAM上限値を考慮して規定サイズを決定するとよい。
  3. アップロードされたオブジェクトを実際に取得し、手順 2 で取得した Content-Type に基づいて、そのオブジェクトが適切なファイルフォーマットに準拠していることを確認する。
    1. 署名付き URL の生成時に指定する Content-Type はあくまでリクエストに指定すべき Content-Type を規定するものであり、アップロードされたオブジェクトがそこに指定されたフォーマットに準拠している保証はない。
    2. オブジェクトが指定されたフォーマットに準拠していないと見做された場合は、以降の処理を停止する。
  4. Cloud Vision の Safe Search Annotation を用いてアップロードされた画像ファイル(オブジェクト)が不適切な表現を含むものでないことを検証する。
    1. 暴力描写・アダルト表現・人種差別などの可能性があるものを検閲する。
    2. 本稿では likelihoodPOSSIBLE 以上のものを該当すると判定し、以降の処理を停止する。
  5. 全ての検証をパスしたら、アップロードされたオブジェクトを Distribution Bucket にコピーする。

上記の手順を実装するために、本稿では Cloud Functions Go 1.11 ランタイムを使用します。次にソースコードを示します。


  package function

import (
        "context"
        "errors"
        "fmt"
        "image"
        "image/gif"
        "image/jpeg"
        "image/png"
        "log"

        "cloud.google.com/go/storage"
        vision "cloud.google.com/go/vision/apiv1"
        "golang.org/x/xerrors"
        pb "google.golang.org/genproto/googleapis/cloud/vision/v1"
)

type GCSEvent struct {
        Bucket string `json:"bucket"`
        Name   string `json:"name"`
}

var retryableError = xerrors.New("upload: retryable error")

func validate(ctx context.Context, obj *storage.ObjectHandle) error {
        attrs, err := obj.Attrs(ctx)
        if err != nil {
                return xerrors.Errorf("upload: failed to get object attributes %q : %w",
                        obj.ObjectName(), retryableError)
        }
        // You can enlarge maximum size up to 20MB by modifying this line. 
        if attrs.Size >= 1024*100 {
                return fmt.Errorf("upload: image file is too large, got = %d", attrs.Size)
        }
        // Validates obj and returns true if it conforms supported image formats.
        if err := validateMIMEType(ctx, attrs, obj); err != nil {
                return err
        }
        // Validates obj by calling Vision API.
        return validateByVisionAPI(ctx, obj)
}

func validateMIMEType(ctx context.Context, attrs *storage.ObjectAttrs, obj *storage.ObjectHandle) error {
        r, err := obj.NewReader(ctx)
        if err != nil {
                return xerrors.Errorf("upload: failed to open new file %q : %w",
                        obj.ObjectName(), retryableError)
        }
        defer r.Close()
        if _, err := func(ct string) (image.Image, error) {
                switch ct {
                case "image/png":
                        return png.Decode(r)
                case "image/jpeg", "image/jpg":
                        return jpeg.Decode(r)
                case "image/gif":
                        return gif.Decode(r)
                default:
                        return nil, fmt.Errorf("upload: unsupported MIME type, got = %q", ct)
                }
        }(attrs.ContentType); err != nil {
                return err
        }
        return nil
}

// validateByVisionAPI uses Safe Search Detection provided by Cloud Vision API.
// See more details: https://cloud.google.com/vision/docs/detecting-safe-search
func validateByVisionAPI(ctx context.Context, obj *storage.ObjectHandle) error {
        client, err := vision.NewImageAnnotatorClient(ctx)
        if err != nil {
                return xerrors.Errorf(
                        "upload: failed to create a ImageAnnotator client, error = %v : %w",
                        err,
                        retryableError,
                )
        }
        ssa, err := client.DetectSafeSearch(
                ctx,
                vision.NewImageFromURI(fmt.Sprintf("gs://%s/%s", obj.BucketName(), obj.ObjectName())),
                nil,
        )
        if err != nil {
                return xerrors.Errorf(
                        "upload: failed to detect safe search, error = %v : %w",
                        err,
                        retryableError,
                )
        }
        // Returns an unretryable error if there is any possibility of inappropriate image.
        // Likelihood has been defined in the following:
        // https://github.com/google/go-genproto/blob/5fe7a883aa19554f42890211544aa549836af7b7/googleapis/cloud/vision/v1/image_annotator.pb.go#L37-L50
        if ssa.Adult >= pb.Likelihood_POSSIBLE ||
                ssa.Medical >= pb.Likelihood_POSSIBLE ||
                ssa.Violence >= pb.Likelihood_POSSIBLE ||
                ssa.Racy >= pb.Likelihood_POSSIBLE {
                return errors.New("upload: exceeds the prescribed likelihood")
        }
        return nil
}

// distributionBucket is the distribution bucket.
// It's used for distributing all of passed files.
// TODO: This value MUST be updated before deploying this function.
const distributionBucket = "DISTRIBUTION_BUCKET"

// UplaodImage validates the object and copy it into the distribution bucket.
func UploadImage(ctx context.Context, e GCSEvent) error {
        client, err := storage.NewClient(ctx)
        if err != nil {
                return fmt.Errorf("upload: failed to construct a client, error = %v", err)
        }
        defer client.Close()

        dst := client.Bucket(distributionBucket).Object(e.Name)
        _, err = dst.Attrs(ctx)
        // Avoid proceeding if the object has been copied to destination.
        if err == nil {
                log.Printf("upload: %s has already been copied to destination\n", e.Name)
                return nil
        }
        // Return retryable error as there is a possibility that object does not temporarily exist.
        if err != storage.ErrObjectNotExist {
                return err
        }
        src := client.Bucket(e.Bucket).Object(e.Name)
        if err := validate(ctx, src); err != nil {
                if xerrors.Is(err, retryableError) {
                        return err
                }
                log.Println(err)
                return nil
        }
        // Returns an error if the copy operation failed.
        // Will retry the same processing later.
        if _, err := dst.CopierFrom(src).Run(ctx); err != nil {
                return err
        }

        return nil
}

上記のソースコードにおける UploadImage という関数を Cloud Functions にデプロイするために、次のコマンドを実行します。


  gcloud functions deploy UploadImage --runtime go111 --trigger-resource [UPLOADABLE_BUCKET] --trigger-event google.storage.object.finalize --retry

--retry オプションを付けて実行することで、Cloud Functions や Cloud Storage の一時的なエラーに備えることができます。一方でリトライを有効化する際には、それを考慮してコーディングをする必要がある点に注意してください。また前出のソースコードについても、 Cloud Functions が at-least once の実行を保証することを考えれば、 Distribution Bucket へのコピーが一回以上行われる可能性を否定できないなど、冪等な実装になっているとは言えませんが、この場合は副作用はありません。

ファイルをアップロードする


ここまでに設計し、実装してきたシステムに対して、実際にファイルをアップロードしてみましょう。テストは非常に簡単です。前述の署名付き URL を App Engine にリクエストし、そのURLに対してファイルをアップロードするために示したソースコードを実行します。

Cloud Functions の実行が完了したら、Uploadable Bucket 内のオブジェクトが Distribution Bucket にコピーされていることを確認します。 Distribution Bucket へのアクセスを全体に対して公開していれば、コピーが完了した時点でそのオブジェクトはインターネットに公開されていることを意味します。なお、あなたが設計するシステムの要件によっては、Cloud Functions の処理は非同期に実行されるため、配信の開始を Push 通知などの方法でエンドユーザーに伝える必要があるかもしれません。もしくは、エンドユーザーからのリクエストを同期的に処理するシステムのデータベースの更新が必要となるかもしれません。マイクロサービス・アーキテクチャを採用しているならば、Cloud Functions から元のアプリケーションに HTTP リクエストを送信しても良いでしょう。

本稿で提案したシステムをカスタマイズして、あなたの目的に合わせたファイルアップロード機能を開発することができるはずです。 Google Cloud Platform には、ここで紹介したコンポーネントや機能以外にも様々な選択肢があります。例えば、ファイルをアップロードする際の条件をより詳細に規定したい場合や、POST Object を使いたい場合には、署名付きポリシードキュメントを署名付き URL の代わりに使用することができます。また、モバイルアプリや Web サービスの開発に Firebase を使用している場合には、ここで紹介した署名付き URL の代わりに Cloud Storage for Firebase を使用することができます。

本稿の内容があなたのアーキテクチャ設計の一助となれば幸いです。

Google Cloud のエキスパートと繋がり、本稿で提案したアーキテクチャについてより深く学びたい方は Google Cloud のコンサルティングサービスを確認してください。