コネヒト開発者ブログ

コネヒト開発者ブログ

ECS×Fargate ターゲット追跡ServiceAutoScallingを使ったスパイク対策と費用削減

こんにちは。インフラエンジニアの永井(shnagai)です。

今回は、ターゲット追跡ServiceAutoScallingを使い、ECS×fargateで運用しているサービスのスパイク対策と費用削減に取り組んだのでその内容をまとめています。

内容はざっくり下記4項目について書いています。

  • 抱えていた課題
  • キャパシティプランニングに対する考え方
  • ECS ターゲット追跡ServiceAutoScallingとは何か?
  • どんな結果になったか?

抱えていた課題

コネヒトでのWebのアーキテクチャはほとんどがECS×Fargateの基盤で動かしています。 ECSのバックエンドをEC2からFargateに移行したタイミングで、大きく下記2点のメリットは享受していました。

  • EC2を意識しないことでの運用コスト削減
  • オートスケールの容易さ

ですが、サービス運用にあたりまだ下記のような課題がありました。

  • ①FargateのコストがEC2リザーブドインスタンスに比べて費用が高い
  • ②オートスケールは容易になったが、平均50sくらい起動にバッファが必要なので、瞬間的なスパイクに対する瞬発力が弱い

今回この2つの課題に対して、ターゲット追跡ServiceAutoScallingを本格導入することで解決することに成功したのでどのようなアプローチをしていったかについて紹介します。

キャパシティプランニングに対する考え方の変化

アプリケーションの実行環境としてクラウド利用やコンテナ化が進んできたことで、スケールアウトやスケールアップが容易になったことを背景として、昔のようなピークトラフィックに耐えうるキャパシティを事前に用意しておくようなアーキテクチャは、私達のような小・中規模のウェブサービスでは選択しなくてもよい時代になりました。

もちろんサービスの可用性を高く保つのはインフラエンジニアとしての本分なのでそこは保ちつつ、クラウドは従量課金なので、ユーザが多いときには多くのリソースを構え少なくなったらリソースを最小化するというより動的なキャパシティコントロールをすることがそのまま費用削減につながります。同じ品質のサービスを提供するのであれば費用は少ない方がいいに決まっており、費用削減もサービス運用に大きな意味を持つと思っています。

コネヒトの最近の話を少し振り返ると、下記のような流れをたどっています。

アーキテクチャ ポイント 費用
ECS×EC2時代 EC2バックエンド時はオートスケールが複雑だったので採用せずにピークトラフィックに*数倍に耐えうるEC2を用意 ReservedInstanceを利用し費用は圧縮
ECS×Fargate時代 EC2(RI)と比べると費用が高いので、ピークトラフィックに耐えうるタスク数までFargateの必要数をチューニングして減らす+想定外のスパイクはオートスケール利用(ちょっと遅いという課題感あり) SavingsPlansを利用し30%弱の費用を圧縮するが、タスク数減らしてEC2時代と同じくらいの費用感
ECS×Fargate(ターゲット追跡ServiceAutoScalling)時代 負荷に応じてタスク数を動的にコントロール。最小数を小さくして夜間とピーク時で3倍程のタスク数の差が出る。平均的なタスク数の削減に成功 従量課金を生かして2のパターンと比べて25%程の費用削減に成功

ECS ターゲット追跡ServiceAutoScallingとは何か?

ここからは、今回活用したターゲット追跡ServiceAutoScallingについて紹介します。

簡単に説明すると、CPU使用率/メモリ使用率/リクエスト数の3つから追跡するメトリクスと値を選択することで、ECS側でタスク必要数 (起動するコンテナ数)を動的にコントロールしてくれる機能です。

実はこれまでもスパイク時のオートスケール用には使っていたのですが、今回スケールインもこの機能に任せる設定を入れて、負荷が低い状態の時に費用削減するようなアプローチを取りました。

具体的にどのような動きをするか

現在は、ECSサービスの平均CPU使用率 を使っているのですがこの挙動が少し理解出来なかったので検証時に調べてみました。

端的に言うと、指定した値に収束するようにタスク数をコントロールするような挙動になります。 例えば、CPU 40%という値を設定したとすると具体的に下記のような挙動になります。(これが中々わかりにくかった)

  • 平常時に10タスク起動で、CPU40%
  • リクエスト数が増え負荷が上がり、CPU55%に
  • タスク数が10→13にスケールアウトしCPU40%に収束
  • 深夜になり、リクエスト数が減りCPU20%に
  • タスク数が13→5にスケールインしCPU40%に収束

裏側では、CloudWatchAlarmがセットされて下記のような設定がされていました。(このアラームの設定は変更負荷)

  • 【スケールアウト判定】 3分間連続しきい値違反
  • 【スケールイン判定】15分間連続しきい値-3%に収束

設定のポイント

ここから設定のポイントを書いていきます。

スケールの数を設定

サービス内のタスクのスケールイン/アウトの幅をまず設定します。

項目 内容
Minimum number of tasks (タスクの最小数) スケールインの最小値なのでこの数以下にはタスク数は減らない
タスクの必要数 サービスで設定しているタスクの必要数が入ります
Maximum number of tasks (タスクの最大数) スケールアウトの最大値なのでこの数以上にはタスク数は増えない
ターゲット追跡するメトリクスを指定

スケーリングポリシーには、「ターゲット追跡」と「ステップスケーリング(任意のCWAを指定)」があり、今回は「ターゲット追跡」を使う場合の設定について書きます。

ターゲット追跡の対象と出来るのは下記3つです。

ECSサービスの平均CPU使用率 ECSサービスの平均メモリ使用率 ECSサービスに紐づくALBのリクエスト数

また、スケーリングの設定として

項目 内容 ポイント
ターゲット値 追跡したメトリクスでのしきい値 サービスの性質により検証しながら適切な閾値を入れる
スケールアウトクールダウン期間 スケールアウトした後の待機時間(この間は連続でスケールアウトは発動しない) コネヒトでは60sにしています
スケールインクールダウン期間 スケールインした後の待機時間(この間は連続でスケールインは発動しない) コネヒトでは900sにしています(ゆっくり縮退してほしいので)
スケールインの無効化 On/Offでスケールインを行うかどうか 今回の目的だとOff

どんな結果になったか?

最後にこのターゲット追跡ServiceAutoScallingを導入してどのような結果が出たかを紹介します。

①FargateのコストがEC2リザーブドインスタンスに比べて費用が高いという課題に対しては、1日平均のタスク起動数が25%程削減出来ました!!

従量課金なので、Fargateの利用料金が25%削減しています。

f:id:nagais:20200610114745p:plain

②オートスケールは容易になったが、平均50sくらい起動にバッファが必要なので、瞬間的なスパイクに対する瞬発力が弱いという課題に対しても解決策を見つけることが出来ました。

どうゆうことかというと、ターゲット追跡のしきい値を緩めたことでリソースに余裕のある状態でスケールアウトが走るので、例えばpush通知で急激にリクエストスパイクする際もレイテンシ悪化なくスケールアウト出来るようになりました。

これまでは、CPU60%超えで発動というような強めの設定をしていたので、スケールアウトする際にはすでにリソースがかつかつで既存のタスクが死んでいくというような状況だったので、スケールアウトで収束するまでに時間が少しかかっていたということに改めて気づきました。

実は、スケールアウト時に間に合わずレスポンスタイム悪化すると使えないなと思っていたのですが、レイテンシに全く影響のでない抑えめのしきい値の最適値を見つけ、現在はこのような波形で動的にタスク数が制御されています。(設定後一度もアラートは鳴ってません)

ちなみに、導入時は、ピーク時間帯が毎日不安で、最適なタスク数と追跡するしきい値1週間は張り付いてリソース状況を観察していました。

f:id:nagais:20200610114809p:plain

最後に宣伝です! コネヒトでは一緒に成長中のサービスを支えるために働く仲間を探しています。 少しでも興味もたれた方は、是非気軽にオンラインでカジュアルにお話出来るとうれしいです。

人の生活になくてはならないサービスをつくる、インフラエンジニア募集! - コネヒト株式会社のインフラエンジニアの求人 - Wantedly www.wantedly.com

カテゴリ類推の機械学習モデルをプロダクションに導入した話

こんにちは。インフラエンジニアの永井(shanagi)です。

今回は、昨年から取り組んでいる機械学習の分野で、自分の作ったモデルをサービスに本番リリースする機会があったので、PoCで終わりがちな機械学習をプロダクト導入にこぎつけるためにどのようなプロセスを歩んだのかとそこで得た自分なりの知見を中心にご紹介できればと思います。

機械学習のプロダクト導入に向けては歩むステップが非常に多く、個々の詳細な内容について細かく書くと非常にボリューミーな内容になってしまうので、詳細は割愛し機会があればまたどこかでご紹介出来ればと思います。

内容は、ざっくり下記2つに絞りました。

  • どんな機能をリリースしたのか?
  • 導入までの全体アプローチ

ちなみに、なぜインフラ畑の自分が機械学習をやっているのかについては、昨年末に下記ブログにまとめたのでもし興味がある方がいれば読んでみてもらえればと思います。

kobitosan.hatenablog.com

どんな機能をリリースしたのか?

まずは今回どのような機能をリリースしたのかを紹介します。 ママリはママ達の悩みや疑問を解決するQAプラットフォームなので、毎日ユーザであるママさん達から多くの質問投稿があります。

ママリ内のカテゴリとしては、「妊娠・出産」「育児・グッズ」「お金・保険」などがあり、質問文の内容に合わせて投稿時にユーザに選択してもらっています。 今回は、この質問投稿時のカテゴリ選択において、質問本文の内容から適切なカテゴリを類推してユーザに推薦する「カテゴリ類推」エンジンをリリースしました。

↓質問の本文からカテゴリを類推している様子です!カテゴリを考え中の時にカテゴリ類推のAPIが呼ばれています。 f:id:nagais:20200512144003p:plain

詳しくは、この後の章で触れるのですがこのリリースによって、下記の指標にプラスの影響を出すことが出来ました。

- 回答率の向上
- 質問投稿時のCVRの向上

機械学習の導入は、導入時はマイナス影響なしの状態で入れてそこからモデルアップデートを通して数値を上げていくものという期待値調整をするので、初期モデルである程度プラスの成果が出たのは運がよかった面もあるな思っています。

導入までの全体アプローチ

前提として昨年からコネヒトでは、機械学習を活用したプロダクトの改善を組織として推し進めていくという取り組みを行っています。組織から機械学習導入を期待されているというのは、説明コストが大幅に下がること(PoCまでのハードルが下がる)を意味しており今回のカテゴリ類推導入に向けても大きな後ろ盾になりました。

全体のステップ

今回カテゴリ類推を本番リリースするにあたり大まかに下記のステップをたどりました。

①仮説作りのためのデータ分析
↓
②仮説を裏付けるためのデータ分析
↓
③モデル作成
↓
④オフライン検証と品質チェック
↓
⑤デモ作成(AWSとAndroidアプリ)
↓
⑥A/Bテスト(PoC)と効果測定
↓
⑦本リリース

それぞれどんなことをしたのかとそこで得た知見ベースで振り返っていこうと思います。

進める上で気をつけた点として、ある程度の精度はオフライン検証で出来るが、実際のユーザの行動は出してみないとわからない部分が大きいのでできるだけ早くA/Bテストまで持っていくことを意識しました。

①仮説作りのためのデータ分析

機械学習導入を期待されているといっても闇雲に機械学習を入れればいいというわけではもちろんありません。 サービスが抱える課題を解くためのツールとして機械学習が適切であればそれを使います。

その課題を可視化して理解し、仮説(機械学習で解決すべき課題)に落とし込むのにはデータ分析が必要です。 解くべき課題に関しては、主にサービスのKPIやそれを細分化したものの達成を設定することが多いと思います。今回のカテゴリ類推においても、当時のサービスKPIの一つになっていた回答率を分析することからはじめました。

データ分析を進める上での工夫として、属人化しがちなデータ分析の知見を気軽に共有するために、GitHubのissueでデータ分析のログを残すようにしています。 仮説を裏付ける際や簡単なモデルの試作品を作る際に使うjupyter notebookのソースも同じリポジトリで管理するようにしており、後からでも過去のプロジェクトを気軽に参照出来るようにしています。

f:id:nagais:20200512143537p:plain

可視化したデータは下記のようなダッシュボードにまとめておくことで、以後のA/Bテスト時の結果計測にも使えるような形にしておきました。

f:id:nagais:20200511205800p:plain

②仮説を裏付けるためのデータ分析

①のデータ分析の結果、質問に対して適切なカテゴリが選択されると回答がつく確率が上がりそうなことがわかりました。 このカテゴリ選択を適切にすることで回答率を上げられそうという仮説を裏付けるためのデータ分析に入ります。 同時に、どのようにして投稿のカテゴリを適切に設定するかを検討しました。その中で、今回採用する機械学習を使った質問投稿時の「カテゴリ類推」を導入すれば、ユーザが入力する投稿内容を元に適切なカテゴリを設定出来てUXも上がりそうという仮説が最終的に出来上がりました。

この仮説を裏付けるために更にカテゴリと回答に絞った分析を行い下記のような事実が浮かび上がってきました。(一部抜粋)

これらは質問文の内容から適切なカテゴリが入ることで回答を増やしてくれそうという仮説の裏付けになりました。

  • 特定のカテゴリに偏りがある(質問内容とは関係なくカテゴリを選択している質問が多くある)
  • ユーザは好きだったり興味のあるカテゴリを回遊しながら回答してくれていることが多そう

③モデル作成

続いて、ローカルのjupyter notebookで「カテゴリ類推」のモデル作成を行いました。 ざっくりメモですが、どんなことをやったか羅列しておきます。

  • 学習データの作成
    • 過去の膨大な質問データを使えるので加工等は不要
    • 1ヶ月分のデータを取ってpandasで分布みたりしてデータ件数を揃える
  • 質問文を機械学習で使える形に前処理する
    • 小文字化とテキストから不要な文字を削除や置換
    • neologdnを使った正規化とUnicode正規化
    • MeCabを使ったトークナイズ(どの品詞を使うと一番精度高いか色々と試したり)
  • 学習データとテストデータの分割
    • scikit-learnのtrain_test_splitを使う
    • 最初層化抽出してなくて、層化抽出することで大きく精度が伸びたのはなつかしい
  • 特徴抽出
    • 文字列をベクトルに変換する手法
    • Bag of WordsとTF-IDFを試す
    • 最終的に、TF-IDF(min_df,max_dfでストップワード兼ねる)を使った
  • アルゴリズム選定
    • scikit-learnを使っていたので、SVM,ロジスティック回帰,ランダムフォレストを試す
    • GridSearchしながら同じ条件でどのアルゴリズム一番精度が出るかを繰り返した
    • 恥ずかしながら当時はニューラルネットワークはまだ手をつけてなかったので試してない(今後検討)

手元である程度の精度が出るモデルが出来たのでオフライン検証と品質チェックに進みます。

④オフライン検証と品質チェック

オフライン検証として、学習データに含まれないN日分のデータからの正答率を出して指標としました。

オフライン検証の結果、ある程度精度の出る(ユーザに違和感なく提供出来そうな)モデルが出来ました。 ただ、機械的に正答率が高くても、ユーザにとって不快な推薦をしてしまうとサービス価値の毀損につながります。ママリはコミュニティサービスなので、せっかく質問しようとしたユーザにカテゴリの推薦で不快な思いをさせてしまっては機会損失につながります。

ここの部分の不安を解消するために、CSチームと連携して、オフライン検証の結果間違ったデータに関して品質チェックを行ってもらいました。 結果、ユーザに致命的に不快感を与えるような予測はないという裏付けをもらってから次のステップに進みました。

⑤デモ作成(AWSとAndroidアプリ)

ディレクターや社内で説明するのに動いているものを見せるのが一番説得力が増すと考えて、Androidエンジニアに協力してもらいデモ用のAPIを叩くデモアプリを作ってもらいました。 今回このデモの果たしてくれた役割は大きく、やはり動くものをサクッと作るのは大事だなと思いました。

⑥A/Bテスト(PoC)と効果測定

デモやデータ分析と品質チェックの結果をディレクターに説明して、A/BテストへのGoサインを無事こぎつけました。

A/Bテストを行う上では、当たり前ですがトラッキングするためのダッシュボードを事前に作っておくことが重要でした。 今回は①で作ったダッシュボード+mixpanelでアプリver毎のイベントをトラッキングすることで効果測定を行いました。 結果指標として掲げていた項目で明らかな優位が見られました!

- 回答率の向上
- 質問投稿時のCVRの向上

裏話でもないですが、ユーザに直接届くものを作ってリリースする機会はこれまであまりなかったので、結果を毎時ドキドキ見ていたのを思い出します。 興奮してこんなツイートもしてました。

⑦本リリース

効果測定の結果、明らかな優位が見られたので簡単なレポートをまとめ関係者に共有し本リリースが決まりました。 ここでのレポーティングは事前にデータ分析する時に効果測定時のレポートを意識しながら可視化したのが役立ちサクッと作りました。

その後、A/Bテスト時は実現までのスピードを重視し手動作成したモデルを使っていたので、前処理とモデル作成のバッチ処理を作成し自動化しました。バッチはAWS上でECS+Fargateベースで動かしています。

最後にどんなアーキテクチャで動いているのかを載せておきます。

f:id:nagais:20200512101636p:plain

今回は技術的な内容には踏み込まずにデータ分析から機械学習のプロダクト導入に至るまでの道筋についてご紹介しました。 コネヒトでは、これからも今回の事例のようにテクノロジーの力でプロダクトを伸ばしていってくれる仲間を絶賛募集しています。ご興味ある方は是非一度お話だけでもさせてもらえるとうれしいです。

www.wantedly.com

ママリでAWSを使った動画配信をはじめました。

こんにちは! フロントエンドエンジニアのもりやです。

ママリではよりユーザーにとってわかりやすく情報を伝えるべく、4/22 からママリ内の人気記事などを動画にし配信する取り組みをはじめました。

news.connehito.com

今回、動画配信にあたって AWS 上で動画変換と配信を行ったので、使用したサービスやシステム構成などを解説しようと思います。

システムの全体像

AWSサービスを使って、以下のような構成で作りました。

20200511084620.png

CloudCraft

※この図は動画の配信のみに絞っています。動画の情報をアプリに配信するAPIなどは別にあります。

大まかに説明するとこんな感じです。

  • CloudFront + S3 を使ってHTTP (HLS) で配信
  • MediaConvert を使ってMP4形式の動画をHLS 形式へ変換
  • API Gateway + Lambda を使って動画変換のAPIを提供
    • DynamoDB を使って動画の情報を保存

以下、詳しく説明します。

動画のフォーマット

配信にあたり、まず動画をどのように配信するかを決めました。 今回配信する動画は、数十秒程度がメインなのでそのまま動画ファイルを配信する方法も検討しました。 ただ、1分を超える動画もありますし、全部ダウンロードしないと再生できないというのは体験的に良くないのでストリーミングでの配信に決めました。

動画のストリーミング配信は全く知見がなかったので色々調べたところ、以下の2つが主流のようです。

  • HLS (HTTP Live Streaming)
  • MPEG-DASH(ISO/IEC 23009)

あまり詳細な仕様までは調べきれていないですが、いろいろ調べた結果、HLSの方がシンプル、MPEG-DASHはDRMなどの複雑な要件にも対応できるがその分仕様も複雑、という印象でした。

今回の配信要件はシンプルで、iOS/Android でもネイティブのエンジニアの方から再生できることが可能であることを確認したのでHLSを採用することにしました。 ちなみに、HLSはAppleが提唱している規格なので、iOSは当然対応していますし、HLS形式の動画はSafariで直接開くことも可能です。(他のブラウザは対応していません)

CloudFront + S3

HLSは配信にあたって静的なHTTPサーバーにファイルを配置するだけで良いので、よくある CloudFront + S3 で配信しています。

MediaConvert

S3に置いてある動画ファイルを変換して、変換した動画ファイルをS3に出力してくれるサービスです。 今回の動画配信で最も重要なサービスでした。

ジョブの作成

MediaConvert は「ジョブ」という単位で変換を実行します。 ジョブは、以下のような入力と出力を設定して、変換の詳細を設定します。

変換の全体像

入力が一つに対して、出力が複数あることが分かるかと思います。 例えば、1つの動画からHLSとMPEG-DASHの2つを同時に変換して出力することができます。

ちなみに入力も複数作れるようなのですが、今回複数入力を使う用途はなく私にも用途が分からないので、説明は割愛します。 (動画の結合とか、複数の動画を横に並べて結合したりできるんでしょうか?)

入力の設定

20200511084801.png

入力は S3 に配置した動画ファイルのパスを指定します。 他にもたくさん設定項目がありますが、今回はここの設定以外は特に使っていません。 動画は奥が深い・・・。

出力の設定(動画)

20200511084926.png

出力先の S3 プレフィックスを入力します。 (※プレフィックスであり、このままの名前で出力されるわけではありません。)

また、下の方の「出力」欄を見ると、2つの出力があるのが分かるかと思います。 例えばHLSであれば、複数のビットレートの動画を1つの配信に含め、クライアントがネットワークの状況に応じて切り替えることができます。 そういった場合に、1つの出力グループに対して、複数の出力を設定することができます。 (ちなみに、今回のママリの配信では諸事情により1つの出力にしています)

20200511084953.png

各出力に対して、動画の詳細を設定できます。 こちらも大量の設定項目がありますが、主に使ったのは以下の2つです。

  • ビデオコーデックと解像度
  • ビットレート

他は特にいじらなくても、デフォルトのままで特に問題ありませんでした。

出力の設定(キャプチャ画像)

今回、動画に配信する際にサムネイル用の画像も一緒に作りたいということで、動画内から1秒おきにキャプチャ画像を作り、その中から適切な画像を選んで配信する、という方法を取りました。

例えば、30秒の動画なら、30枚のJPEG画像が出力され、その中からサムネイルとして良さそうなものを選ぶ、という感じです。

20200511085029.png

こんな感じの出力設定をします。 画質にあまり拘らなければ、フレームレートを調整すれば大丈夫です。 1/1 で1秒に1枚、という設定になります。

変換の実行

設定が完了したら、変換を実行します。 以下のようにジョブリストに表示され「COMPLETE」と表示されれば完了です。 ちなみに設定にエラーがある場合は「ERROR」と表示されます。

20200511085044.png

変換が完了すると S3 にはこのように出力されます。

20200511085108.png

※ジョブ作成時に設定したプレフィックスなどによって出力先は変わります。

出力先を CloudFront で配信している S3 バケットにしておくと、変換完了後にすぐインターネットからアクセスできるので便利でした。

API Gateway + Lambda

AWS SDK でも実行できるのですが、curl やスクリプト、管理画面などから使いやすいように、シンプルなAPIを提供するようにしました。

  1. アップロードの情報を提供するAPI
    • S3 の Pre-Signed URL を使って、アップロードに必要な一時URLを生成して返却するAPIです。
    • アップロード自体は、アクセス元の責務にしてます。
  2. 変換をリクエストするAPI
    • MediaConvert にジョブを作るAPIです。
    • ジョブIDなどは DynamoDB で管理しています。
  3. 動画情報を取得するAPI
    • MediaConvert に問い合わせて、ジョブの情報を取得して返却します。
    • 変換が完了している場合は、インターネットからアクセスできるURLなども返却します。

なお、API Gateway は API Key でアクセスを制限しています。

変換作業

今回は、1本辺り数十秒程度がメインの動画を200本以上、再生時間だと2時間以上の動画ファイルをアップロード&変換しました。 curl などで手動アップロードも可能ですが、時間がかかる、ヒューマンエラーが発生しやすい、設定ミスなどで再変換したい場合でも再実行しやすくしたい、といった目的のため Node.js でスクリプトを書きました。

詳細は省きますが Node.js を使いアップロードを最大10並列で実行するスクリプトを書いて実行しました。 大体変換完了まで16分ほどで終わったので、かなり早くできました。 MediaConvert は複数実行すると並列に変換してくれるので、単に2時間の動画を変換するよりも早く終わります。便利ですね!

料金

CloudFront での配信料金

これは動画に限らないですが CloudFront からインターネットへの転送は 1GB あたり 0.114 USD かかります。 $1 = 107円で、100GB の配信をしたとすると 0.114USD * 100GB * 107円 = 約1,220円 かかります。

https://aws.amazon.com/jp/cloudfront/pricing/

MediaConvert での変換料金

今回の肝である MediaConvert での変換にはどのぐらいの料金がかかるのか紹介します。 最近のスマホは解像度が高くなっているので、MediaConvert の算定方法だと 4K に該当する画質で変換しました。

https://aws.amazon.com/jp/mediaconvert/pricing/

フレームレートは <30fps でやったので、 1分間の変換料金は 0.034USD になります。 $1 = 107円で、60分の動画を変換した場合、 0.034USD * 60分 * 107円 = 約218円 になります。 めっちゃ安いですね!

VODやってる会社さんとかはわかりませんが、今回のように小規模な動画配信を行う場合、 動画変換の開発や設定、サーバー維持費など考慮すればめちゃくちゃコスパいいサービスだな、と感じました。

その他

S3 API Gateway Lambda DynamoDB などの料金などがありますが、CloudFront での配信料金に比べると微々たるもの、もしくは無料枠で収まる程度でしかなかったので、省略します。

おわりに

今回初めての動画配信でしたが、HLS で配信し、AWS のサービスを使うことでかなり容易に構築や配信準備を行うことができました。 動画配信は敷居が高いイメージでしたが、今回のようなシンプルな配信と AWS のサービスを組み合わせる場合は割と楽にできます。

未知の領域に踏み込んでみるのは楽しいですね。 コネヒトでは、新しいことにどんどんチャレンジしたいエンジニアを募集中です!

www.wantedly.com www.wantedly.com www.wantedly.com

BigQueryのスケジューリングクエリで日付別シャード化テーブルを作成する

こんにちは!テクノロジー推進グループでエンジニアをやってるaboです。

この記事では、Google BigQueryのスケジューリングクエリを使って日付別シャード化テーブルを作成する方法を紹介します。

日付別シャード化テーブルはBigQueryにおけるテーブル分割の方法の1つです。弊社では費用と計算コスト減を目的にテーブル分割を行なっており、その中でも日付別シャード化テーブルは以下のようなメリットがあり、採用しています。

  • where句でテーブル名の絞り込みが出来るのでスキャン量が調整出来て便利
  • テーブルが日付ごとに分かれるので、バッチ失敗時のリランやテーブル再生成が気軽に行える

cloud.google.com

では実際に日付別シャード化テーブルを作成する手順に移ります。

手順

1. クエリを用意して「スケジュールされたクエリを新規作成」を選択

このクエリの結果がそのままテーブルの中身になります。下記の画像の例では、抽出対象となるテーブルが日付別シャード化テーブルになっており、前日分のデータを抽出しようとしています。

f:id:aboy_perry:20200422193705p:plain
クエリを書いて、スケジュールを新規作成

2. クエリ結果の書き込み先のテーブル名をシャード化したい日付にあわせて入力

ここが本題ですが、テーブル名の指定には、文字列のほか、run_daterun_time という2つのパラメータが使えます。特にrun_timeパラメータを使うと前日の日付でシャード化することができたりします。抽出対象とするデータが前日分だからシャード化する日付も前日のものにしたい、というケースで活躍します。

以下が入力例です。

例1)テーブルの日付をバッチ実行日にしたい

table名_{run_date}

例2)テーブルの日付をバッチ実行日の前日にしたい

table名_{run_time-24h|"%Y%m%d"}

f:id:aboy_perry:20200422203355p:plain
スケジュール設定画面

ハマりポイント

run_daterun_timeUTCになるので注意が必要です!つまりスケジュールをJST 09:00以前にした場合、run_daterun_timeはJSTの実行日の前日を示します。例2ではそこからさらに-24hしているため最終的にJSTの実行日の前々日でシャード化されることになります ԅ( ˘ω˘ԅ)

他の入力例や詳細は下記をご覧ください。

cloud.google.com

3. 残りの入力欄に適当な値を設定して、「スケジュール」を実行

f:id:aboy_perry:20200422225501p:plain
スケジュールを実行

これでスケジューリングしたクエリが実行されたら、日付別シャード化テーブルができてるはずです!👏 下の画像のように、日付別シャード化テーブルはBigQueryのGUI上で日付ごとに選択できたり、見やすくなっています。

f:id:aboy_perry:20200422231227p:plain
日付別シャード化テーブル

また、「スケジュールされたクエリ」画面では、スケジュールの詳細を見ることができ、次に実行される予定の時間を確認したり、クエリやスケジュールの変更を行うことができます。

f:id:aboy_perry:20200422224520p:plain
スケジュールされたクエリの一覧

日付別シャード化テーブルからデータを抽出する

できた日付別シャード化テーブルは、WHERE句で _table_suffix を用いて日付をまたいで検索することができます。

以下はhoge_events_{日付}という日付別シャード化テーブルから、過去3日分のデータを抽出する例です。

SELECT *
FROM
    `dataset_name.hoge_events_*` as events
WHERE
    _table_suffix BETWEEN FORMAT_DATE("%Y%m%d", DATE_SUB(CURRENT_DATE("Asia/Tokyo"), INTERVAL 3 DAY)) AND FORMAT_DATE("%Y%m%d", DATE_SUB(CURRENT_DATE("Asia/Tokyo"), INTERVAL 1 DAY))

また、今月分のデータを指定する場合はLIKEを使って以下のようにもできます。

SELECT *
FROM
    `dataset_name.hoge_events_*` as events
WHERE
    _table_suffix LIKE FORMAT_DATE("%Y%m%%", CURRENT_DATE("Asia/Tokyo"))

おわりに

UTCである点はハマりポイントかなと思いますが、簡単に日付別シャード化テーブルを作ることができます。テーブル分割を取り入れ、コストを削減していきましょう〜。


コネヒトでは、データを活用してプロダクトを成長させたい!機械学習やりたい!といった方を募集中です!

www.wantedly.com

www.wantedly.com

www.wantedly.com

レコメンドエンジン導入までの取り組みとアーキテクチャについて

こんにちは!MLエンジニアのたかぱい(@takapy)です。

今回は、ママリのアプリ内にレコメンドエンジンを導入したので、導入までの取り組みやアーキテクチャについてご紹介できればと思います。


目次


ママリ内での課題

ママリはサービスとして6年目を迎え、サービスの成長とともにアプリ内の記事数も増えており、それに伴いユーザーが本来欲しい情報にたどり着くことも難しくなってきました。

加えて「子育て層のユーザー」という切り口1つとっても、0才児のママと1才児のママでは悩みや欲しい情報がまったく異なります。

このような背景から、これまで人的に行っていたルールベースでの記事配信に対し課題を感じており、機械学習で解決できないか、ということで取り組み始めました。

アーキテクチャ概要

全体のアーキテクチャは以下のようになっています。

アプリのログはBigQueryに蓄積されているため、そこからデータを取得・構造化した後、推薦記事を計算してDynamoDBに保存しています。

APIはFlaskで構築し、APIのログはAthena経由でRedashから参照できるようにしています。
このRedashはBigQueryのデータも参照できるので、ここでAPIのログとBigQueryの行動ログデータを突き合わせて、各種指標(CTRなど)をモニタリングしています。

f:id:taxa_program:20200330195638p:plain
アーキテクチャ

EDAとアルゴリズムについて

まずはデータの理解を深めるために、簡単なEDAを実施しました。
ママリの記事にはカテゴリ*1がついているのですが、あるユーザー属性別にカテゴリ毎の閲覧数を見てみると、大きな偏りがあることが分かりました。
(例えば、ユーザー属性Aの人は、「子育て・家族カテゴリ」の記事をよく見ているが、ユーザー属性Bの人は「住まいカテゴリ」の記事をよく見ている、など)

f:id:taxa_program:20200330184036p:plain
特定カテゴリにおけるユーザー属性別の記事閲覧数例

この結果を元に、ユーザーを数十種類のクラスタにハードクラスタリングし、「クラスタ単位」と「クラスタ×ユーザー単位」の2種類に分け、推薦記事を計算しました。

「クラスタ単位」とは、以下のようにクラスタ粒度でRatingテーブルを作成して推薦記事を計算するイメージです。こうすることで、新規ユーザーに対してもある程度嗜好性を考慮した記事を推薦してくれることを期待しています。

f:id:taxa_program:20200330184949p:plain
クラスタ単位の推薦記事計算例

「クラスタ×ユーザー単位」とは、ユーザーごとにそれぞれのクラスタ内で推薦記事を計算するイメージです。

f:id:taxa_program:20200330185134p:plain
クラスタ×ユーザー単位の推薦記事計算例

また、今回は「0→1」での導入だったため、レコメンドの精度よりは実装までのスピードを意識して取り組みました。
そのため、推薦アルゴリズムには業界内での成功事例があり、かつ比較的実装コストも低い協調フィルタリングと、Matrix Factorization(MF)を採用しようと決め、この2つのアルゴリズムでオフライン検証に進みました。

オフライン検証の失敗と学び

全ユーザーの直近3週間の行動ログ(アプリの閲覧履歴、ユーザーのアクションを含む)をBigQueryから取得し、構造化しました。
そこからデータを時系列に、古い2週間と直近1週間に分割し、前者を学習データ、後者をテストデータとしてオフライン検証を行いました。

オフライン検証では、推薦したアイテムをクリックするか否かを評価するため、Recallという指標を用いました。Recallとはユーザーが実際に嗜好したアイテムのうち、推薦したアイテムでカバーできたものの割合です。

結果、このオフライン検証では良い数値がでませんでした。

そもそもオフラインでの評価は、我々がおすすめしようとするまいと、ユーザーが嗜好(クリック)したものを予測しているので、それを正確に予測できることにどれだけ意味があるのか、という議論ポイントはあると思っています。
そんな中でオフライン検証ではあまり良い予測ができないケースもある(良い予測ができても、それがそのままオンラインでの精度にならない)ということを学べたのは、今回の収穫の1つであったと思います。

とはいえ、PoCばかり進めていても仕方ないよね、ということから、未知の部分は多いもののプロダクションでどういう動きをするか見てみよう、ということでA/Bテストを実施しました。

A/Bテストについて

冒頭で説明したアーキテクチャは最終的なもので、A/Bテスト時の構成はかなりシンプルにしました。

今回、オフライン検証で良い数値が出た訳でもないため、現状の表示条件(ルールベース)よりCTRが下がる恐れがありました。
そんな中で全体のアーキテクチャを先に設計・構築してしまうと、実装までのスピードが遅くなったり、仮に指標が下がってしまった場合の時間/金銭的コストが大きくなってしまいます。

そのため、下図のような最小限の構成としました。

f:id:taxa_program:20200330191934p:plain
A/Bテスト時のアーキテクチャ

結果的に、A/BテストではCTRが7ポイント向上し、検定を行いそのCTRに有意差が確認できたことから、プロダクションへ本格導入することに決定しました。

レコメンドアルゴリズムについて

今回試した2つのアルゴリズムについて簡単にご紹介します。
(詳細な実装方法などはWeb上に優良な記事がありますので、ここでは概要の説明に留めます)

強調フィルタリング(アイテムベース)

協調フィルタリングは大別して「ユーザーベース」と「アイテムベース」の手法があります。今回実装したのはアイテムベースの協調フィルタリングです。

まず、上記Rating行列から全てのアイテム間の類似度を計算して、類似度行列を生成します。この時の類似度計算方法には「ユークリッド距離」や「コサイン類似度」を使用できます。今回はコサイン類似度を使用しました。

f:id:taxa_program:20200330192217p:plain
類似度行列作成例

次に、推薦アイテムを計算したいユーザーのベクトルと、この類似度行列の積をとります。
下記例では、user Dに対して推薦したいアイテムを計算しています。計算結果からすでに評価しているitem 2を除外すると、item 4の値が一番高いため、user Dにはitem 4を推薦すればよい、ということになります。

f:id:taxa_program:20200330192410p:plain
userDに対する推薦記事計算例

Matrix Factorization

Matrix Factorizationはその名前の通り、Rating行列をuserの特徴量行列(P)とitemの特徴量行列(Q)に分解します。
例えば、m人のユーザーとn個のアイテムを考えたときに、m > k > 0であるk次元に次元削減して変換することを目的とします。
これは、評価値を表すRating行列(R)を、ユーザー要素を表すk × mの行列(P)と、アイテム要素を表すk × nの行列(Q)に近似することです。


R \approx PQ^T

図にすると以下のようなイメージです。

f:id:taxa_program:20200330193600p:plain
行列分解例

そして、分解された2つの行列の積をとると、新しいuser×itemのRating行列が生成されます。このRating行列は密な行列となるので、値の高いアイテムをそのまま推薦すればよいことになります。
例えば、user Aに対してはすでに評価しているitem 1とitem 2を除外すると、item 3を推薦すればよい、ということになります。

f:id:taxa_program:20200330193324p:plain
最終的な行列

最後に

今回は0からレコメンドエンジンを構築するまでの取り組みや、そのアーキテクチャについてお話しました。

私自身、レコメンドエンジンの構築はMovieLensのデータセットなどを使ってやってみた経験しかなかったので、実サービスに対して取り組むことで、実際にユーザーの反応を見ることができたり、安定して配信するための基盤作りに携われたり、とても面白かったです。

今回のレコメンドエンジンはまだまだ課題もあり、これからユーザーの反応を見ながらアップデートしていく予定なので、その時はまた取り組みをご紹介できればと思います。

*1:「子育て・家族」「病院」「住まい」など

Sign in with AppleでのiOSアプリとサーバーとの連携

こんにちは!エンジニアの柳村です。

Twitterなどの3rd partyのログイン機能を提供しているアプリは6/30までに対応が必要です。(2ヶ月延期されましたね!)

アプリ単体でSign in with Appleをできるようにするのはとても簡単です。しかし大抵のアプリの場合はそれだけでは完結せず、サーバー側でSign in したユーザーと紐付ける必要があります。

サーバー側はFirebase AuthenticationやAuth0といったIDaaSにまかせるという手もありますが、今回は自前で実装することを前提にその実現方法を見ていきたいと思います。

全体の流れ

クライアント側とサーバー側のざっとした流れはこのようになります。

f:id:yanamura:20200327094652p:plain
sign in with apple flow

クライアントからサーバー側にid_tokenを渡すやり方とauthorization_codeを渡すやり方の2つの方法がありますが、ここではauthorization_codeを使ったやり方のみ紹介します。また、nonceの生成やクライアントサーバ間の受け渡し方法についても割愛します。

iOSアプリ側

Apple Developer Programでやること

アプリのIdentifierのCapabilitiesにSign in with Appleを設定します。 その際に、configureボタンを押してPrimary App IDを設定します。

また、Identifierを更新するとProvisioning ProfileがInvalidになってしまうので更新する必要があります。

XcodeのProject設定でやること

TARGETSのSigning&Capabilitiesで+Capabilityを押してSign in with Appleを追加します。

実装

iOSアプリ側でやることは、Sign in with Appleをするボタンを用意し、ボタンが押されたらASAuthorizationAppleIDProviderを生成し、performRequest()を呼ぶとログインに成功するとcallbackが呼ばれ、authorization_codeが取得できるので、これをサーバーに渡すだけです。(ドキュメント: Implementing User Authentication with Sign in with Apple)

ボタンの用意

import AuthenticationServices
...

let appleButton = ASAuthorizationAppleIDButton() // デザインを変えたい場合はUIButtonなどに変えれば良い
appleButton.rx.controlEvent(.touchUpInside)
    .subscribe(onNext: { [unowned self] in
        let appleIDProvider = ASAuthorizationAppleIDProvider()
        let request = appleIDProvider.createRequest()
        request.requestedScopes = [.email]
        request.nonce = `something` // nonceについては割愛します

        let authorizationController = ASAuthorizationController(authorizationRequests: [request])
        authorizationController.delegate = self
        authorizationController.presentationContextProvider = self
        authorizationController.performRequests()
    })
    .disposed(by: disposeBag)

delegateの実装

extension XXViewController: ASAuthorizationControllerDelegate {
    @available(iOS 13.0, *)
    func authorizationController(controller: ASAuthorizationController, didCompleteWithAuthorization authorization: ASAuthorization) {
        switch authorization.credential {
        case let appleIDCredential as ASAuthorizationAppleIDCredential:
            // ここでauthorizationCodeやidTokenが取得できる。
            print("#apple \(String(data: appleIDCredential.identityToken!, encoding: .utf8))")
            print("#apple \(String(data: appleIDCredential.authorizationCode!, encoding: .utf8))")

            // サーバーにauthorizationCode送る処理
        default:
            break
        }
    }
}

extension XXViewController: ASAuthorizationControllerPresentationContextProviding {
    @available(iOS 13.0, *)
    func presentationAnchor(for controller: ASAuthorizationController) -> ASPresentationAnchor {
        return view.window!
    }
}

サーバー側

Apple Developer Programでやること

Keysでkeyを追加します。 Sign in with AppleをENABLEにし、configureボタンを押してPrimary App IDを設定します。 作成が完了すると秘密鍵(.p8)をダウンロードします。この秘密鍵と作成したKeyのKey IDは、次でclient secretを作成するのに使います。

サーバー側の実装概要

サーバー側でやることは大きく2ステップで、まずAppleの/auth/token APIを叩いてid token(JWT)を取得します。 次にid tokenをAppleの/auth/keys APIを叩いて取得した公開鍵でverifyしてuser identifierを取得します。

id tokenの取得

1. client secret(JWT)の作成

以下のheaderとpayloadを使ってJWTを生成します。

header

{
  alg: ES256,
  kid: // Apple Developer ProgramのKeysで作成したKeyのKeyID
}

payload

{
  iss: // TEAM ID,
  iat: 今の時間,
  exp: 今の時間+適当な値(max 15777000),
  aud: https://appleid.apple.com,
  sub: // Bundle ID
}

署名はApple Developer ProgramのKeysで作成したときにダウンロードした秘密鍵を使います。

2. /auth/tokenにPOSTする

以下のデータをhttps://appleid.apple.com/auth/tokenにPOSTします。API仕様

{
    client_id: // Bundle ID
    client_secret: // 1. でつくったclient secretをいれる,
    code: , // iOSアプリから受け取ったauthorization_codeをセットする
    grant_type: 'authorization_code',
    redirect_uri: ""// 空文字
}

成功するとid_tokenが含まれたJSONが取得できます。

id tokenのverify

上で取得したid token(JWT)をverifyする必要があります。

1. 公開鍵の取得

https://appleid.apple.com/auth/keysをGETするとJWKSが取得できます。(API仕様

これには複数のJWKが含まれています。この中から、JWKのkidとid tokenのheaderのkidと一致するものを使って公開鍵を生成します。

2. id tokenのverify

ドキュメントVerify the Identity Token に以下のように記載があります。

- Verify the JWS E256 signature using the server’s public key

- Verify the nonce for the authentication

- Verify that the iss field contains https://appleid.apple.com

- Verify that the aud field is the developer’s client_id

- Verify that the time is earlier than the exp value of the token


これに従って、1. で取得した公開鍵を使ってid tokenをverifyし、id tokenのpayloadのnonce, iss, aud, timeを検証します。

id tokenのpayloadに含まれる内容は以下になります。(ドキュメント)

{
  iss: 'https://appleid.apple.com'
  sub: // The unique identifier for the user.
  aud: // Bundle ID
  exp: // expire time
  iat: // The time the token was issued.
  nonce: // nonce
  nonce_supported: true or false, 
  email: // The user's email address.
  email_verified: true or false,
}

subがuser identifierになるので、これを使ってユーザーを識別します。

まとめ

このように、iOSアプリ側は割と簡単に実装できますが、サーバー側は結構やることがあります。Googleみたいにサーバ側用のライブラリが用意されていればよいのですが、残念ながらAppleは用意してくれていません。。Githubにいくつかライブラリがありましたが、実装が間違っている(特にid tokenのverify周り)ものがときどき見受けられたので選定には注意が必要かなと思いました。Sign in with Appleを導入し、かつ自前でサーバー側も実装する場合は余裕を持って取り組んだほうがよさそうかなと思いましたので、まだ3ヶ月くらいありますが早めに対応したほうがよさそうでした。

最後に宣伝です!

コネヒトではエンジニアを募集しておりますので少しでも興味のある人はお話だけでも聞きにきてください! www.wantedly.com

Zoomウェビナーを使ったオンライン勉強会の裏側

こんにちは。2017年11月にAndroidエンジニアとしてjoinした関根です。03/11(水)にリモートワークを考えよう、というテーマでオンラインの勉強会を開催しました。パネリストとオーディエンスのみなさま、誠にありがとうございました!今回はその勉強会の配信の裏側を書かせて頂きます。

なお、当日の様子とLTスライドは下記の記事内に掲載しています。 www.wantedly.com

開催する上で考えたこと

今回のイベントは普段のオフラインの勉強会とは違い下記のような制約がありました。

  • パネリストがオンラインかつ複数拠点で発表できること
  • オーディエンスがなるべく手間をかけずに参加できること
  • オンラインでもLTに対して質疑応答が可能なこと
  • オンラインでも参加者全員がコラボレーションの体感が得られること

これらの制約をクリアし、どのようなツールでどのようなコミュニケーション設計にすれば、普段の勉強会と変わらない体験を提供できるかのかを考えるところから始めました。

どのツールを利用するか

結論から書くと今回はタイトルにある通りZoomウェビナーを利用しました。 選択した理由としては下記の点です。

  • 画面共有機能でスライドを共有できるのでパネリストが集まる必要がなかったこと
  • 質疑応答機能、チャット機能、手を上げる機能など双方向のコミュニケーションを取る機能が充実していたこと
  • 視聴者はアプリのインストールが不要で参加できること

コネヒトでZoomを試験導入中で使い慣れていたことや、ビデオ通話が高品質で安定していたことが理由としてありますが、先述した制約の全てを解消することができたのでZoomを選んで正解だったと考えています。注意点ですが、Zoomウェビナーはプランにより最大参加人数が決まっているので、最適なプランを選びましょう。*1

配信トラブルがあった場合

オンラインイベントがはじめてだったので、イベント時に配信トラブルが起きることも考えられました。Zoomウェビナーにはレコーディング機能があり、後日動画を公開することが可能で安心感がありました。 録画したものはYoutubeに公開していますので、是非ご覧ください! www.youtube.com

コミュニケーション設計はどうするか

オフライン勉強会を開催が決定して一番悩んだのが、コミュニケーション設計をどうするかでした。 普段の勉強会とは違い全参加者がオンラインでコミュニケーションをとる必要があるので、普段の勉強会とは違った難しさがありました。

参加者の役割と可能な操作

まず簡単にZoomウェビナーの各参加者の役割を解説します。 この役割のメンバーがそれぞれにどのようにコミュニケーションをとってもらいリモートワークへの理解を深めるかを考えなければいけませんでした。

役割 ビデオ通話 チャット Q&A投稿 手を挙げる機能
ホスト オペレーター できる できる 回答のみ できない
パネリスト 登壇者 できる できる 回答のみ できない
オーディエンス 観客 パネリストに昇進することで可能 できる 質問と質問への投票 できる

これを前提のどのようなコミュニケーションを取ることが最適か相談し方針を決めていきました。概ね以下のような議論を行いました。

コミュニケーションの課題と対策

オフラインでのリアクションをオンラインでどのように再現するか?

オフラインの勉強会では自然と生まれる拍手や歓声がオンラインでは難しいので以下のような状況が想定されました。

  • パネリストにオーディエンスの反応が伝わりにくい
  • 全参加者が勉強会に参加してる感が薄まる

これはチャット機能を利用し、👏👏👏👏👏👏👏や888888888のようなコメントを書き込んでもらうことで登壇者に参加者のリアクションが伝わるような工夫をしました

質疑応答はどのように実施するか?

オフラインの勉強会で行われているのと同じように、質疑応答の時間を取らないとリモートワークの知識を深める場としての意義が少し低下してしまう様に思いました。これについてはZoomウェビナーの機能を踏まえ以下の2つの案があがりました。

  1. 質疑応答機能を利用して、テキストで質問する。
  2. 手を挙げる機能を押したオーディエンスのビデオ通話を許可し直接質問してもらう

これはZoomが初めての参加者が多くいることが予測されたので、オーディエンスがよりシンプルな操作で質問できる2.の案を採用しました。

ホストとパネリストのコミュニケーションはどうするか?

オフラインのイベントでは、イベント中のコミュニケーションは、カンペを出す、耳打ちをするなどで取ることが可能ですが、今回はパネリストの方がそれぞれ別の場所にいるため、他の方法を取る必要がありました。これは以下のような案があがりました。

  1. Slackなどチャットツールで連絡を取り合う
  2. Zoom内でホストとパネリストのみへの発言機能を利用する

結論としてZoomウェビナーのチャット機能では誤操作の可能性があったため、Slackでコミュニケーションを取る方法を採用しました。

これ以外にも相談したことは数多くあるのですが、紹介しきれないのでここまでとし、次は当日の流れを紹介します。

当日の流れ

当日は以下のタイムラインで進行をして行きました。

17:30 - 18:30(リハーサル)
18:50 - 19:00(前説)
19:00 - 19:40(LT&質疑応答)
19:40 - 19:45(結び)

それぞれの様子を簡単に紹介して行きます。

リハーサル

Zoomウェビナー機能の実践セッションを利用しホストとパネリストで、リハーサルを行いました。 パネリストの皆様とイベント開始前に簡単な打ち合わせをしながら下記の確認をしました。

  • 画面共有の操作手順の確認
  • 映像と音声の見え方聞こえ方チェック
  • 質疑応答の流れの共有

他にも質疑応答時の流れをリハーサルしたり、オーディエンスからの見え方を確認するなど、ギリギリまでバタバタとしながら本番に臨みました。

前説

司会からオーディエンスに向けて以下のような説明を行いました。

  • 登壇者の画面がみれているかの確認
  • リアクションの取り方の説明
    • チャットの画面を開いて、リアクションの練習をしてもらう
  • 質疑応答の説明
    • 手をあげる機能を利用してもらう
    • ホスト側でビデオ通話を許可するので直接質問してもらう

開始時に会場側でハウリングが発生したり*2、司会の映像が遅れるなどのトラブルもありましたが、参加者のみなさんに、前説からチャットで盛り上げていただいたので、あたたかい気持ちでスムーズにイベントを開始することができました。

LT&質疑応答

概ねスムーズに進行できたと思いますが、はじめてのオンラインイベントらしく、下記のようなちょっとしたトラブルもありました。

  1. 手を挙げる機能を利用した質疑応答で、立候補者がでなかった
  2. オーディエンスのチャット設定がパネリストだけに見えるようになっていた

これらのトラブルにも以下のように、参加者の皆さまのご協力により、それぞれ対応をすることができました。

  1. パネリストの方にチャットから質問を選んでもらうやり方に切り替える
  2. 司会からチャットの設定を変更するようにオーディエンスに促してもらう

パネリストとオーディエンスの皆さま、スムーズなイベント進行にご協力いただきありがとうございました!!

反省点

今回はじめてオンライン勉強会を開催したため、様々な点で反省はあるのですが、特に質疑応答の体験をもう少しよくできたと感じています。次回開催時はZoomの質疑応答機能を利用してみたいと思っています。 最後となりますが、今回のイベントの経験オンライン勉強会用のチェック項目を掲載し、本記事の締めとさせて頂きます。*3

オンライン勉強会用チェック項目
事前準備

- 周りの音が入り込まない静かな環境を用意できるか
  - ヘッドフォンがあると安心
- 配信で利用するPCは安定して映像音声を届けられるか
  - 可能ならば予備のPCを用意しておく
- オンラインでのリアクションを決める
- 質疑応答の方法/流れを決める
 - オーディエンスへの説明の方法も一緒に決める
- 録画機能を利用するかを決める
  - 事後配信をするかも一緒に
- 参加者に配信URLを共有するタイミングを決める
- リハーサル時間を当日に用意する
   - 特に質疑応答の手順は複雑なので入念に

本編中

 - ハウリングに気をつけよう
 - オーディエンスのコメント設定は全員向けになっているか
   - デフォルトではパネリストにだけ見える設定
 - 周りの雑音に気をつけよう
 - 録画機能状況に気をつけよう
 - 配信画面への映り込みに気をつけよう

ここまでお読み頂きありがとうございました!

*1:ZoomミーディングのアドオンなのでZoomミーディングのプロプラン以上の契約も必要です。

*2:犯人は私です

*3:Zoomウェビナーの文脈が強めです