TECH PLAY

NTTドコモビジネス

NTTドコモビジネス の技術ブログ

602

JANOG51参加報告 イノベーションセンターの田島です。サービスプロバイダーネットワーク網の技術検証から検証用 AS の設計・構築・運用まで担当しています。 2023/01/25 ~ 27 の日程で富士吉田市にて開催されました JANOG51 に登壇し、参加された方々と議論しました。 この記事ではまず田島が登壇したセッション内容について振り返り、加えて NTT Com の他のメンバが登壇したセッションについてもいくつか紹介します。 JANOG51はアーカイブ動画が 2023/02/28 まで配信中です。 この記事以外でも NTT Com からパネリスト参加や LT での発表もありますので、 プログラム一覧 から資料や録画を是非ご覧ください。 もし本番ネットワークをまるごと仮想環境に"コピー"できたらうれしいですか? TIS 株式会社、ビッグローブ株式会社、NTT Com の3社協同プロジェクトで取り組んでいる、ネットワークトポロジーをモデルへ抽象化して処理する取り組みについての発表でした。 以前、本ブログの記事 ネットワークをモデルとして抽象化しオペレーションを高度化するチャレンジ でも紹介しましたが、さらに内容が深まっています。 発表内容については JANOG51 のプログラムページから資料が閲覧できますので詳しくはそちらをご参照ください。 実際にユースケースを動かしてみたターミナルの動画もリンクがあります。 簡単に振り返ると、このセッションでは我々が考案したネットワークの各レイヤーをグラフとして捉えて抽象化する、トポロジーモデルを介した検証環境の立ち上げについて発表し議論しました。 既存の本番環境の設定からモデルデータへ抽象化することで、使用するアプライアンスやネットワーク OS が本番環境と検証環境で違っていても相互変換可能にしました。 この仕組みを用いると十数台のキャリアルーターからなる本番環境であっても、操作可能な検証環境を数分で起動できました。 議論も活発に行われ、モデルによる抽象化の功罪や、検証環境に求める要件、そしてこのシステムを普及させるための方法など、参加者の方々と共に問題についての理解が深まりました。 このプロジェクトでの途中成果物は GitHub で既に公開しています。 現在はまだドキュメントの整備などが追いついていませんが、今後更新し、サンプルの検証シナリオをみなさまの PC 上で体験できるようにする予定です。ご期待ください。 ホワイトボックス伝送の導入に向けたキャリアの取り組み このセッションではホワイトボックス伝送装置の NOS、他ベンダー相互接続性、設計・制御ソフトウェアについてキャリアとしての観点での評価結果と課題を、イノベーションセンターの木村と張が発表しました。また、本取り組みに関連するコミュニティである TIP( Telecom Infra Project )、IGF( IOWN Global Forum )での我々の活動も紹介しました。 議論ではホワイトボックス伝送、ディスアグリゲーションモデルの訴求点に関して参加者の方々と意見交換を行い、課題について理解が深まりました。 今後は新たなホワイトボックス伝送装置や周辺ソフトウェアを評価し、伝送ネットワークのオープン化に向けての技術開発を継続していきます。 CUEとKubernetesカスタムオペレータを用いた新しいネットワークコントローラをつくってみた このセッションでは、我々が開発したネットワークコンフィグの宣言的管理を可能にするシステムについて、イノベーションセンターの奥井が発表しました。 このツールを用いることで、高レベルなインテントベースのコンフィグ作成、静的解析や構成テスト、GitOps が可能となり、アプリケーションインフラのマニフェストと同じようにネットワークコンフィグを管理・デリバリできます。 クラウドネイティブなアプリケーション開発で培われた、Infrastructure as Code と GitOps のプラクティスやオープンソース(Kubernetes、CUE、FluxCD など)を駆使して実装されています。 議論では、Kubernetes カスタムオペレータの使い方の是非について活発な意見交換が行われ、装置状態を観測しつつ管理ライフサイクルを隠蔽するための案として Ansible Operator を用いるのはどうか、といったコメントもいただき、さらなる取組に向けて大変有意義なものになりました。 現時点では gNMI/OpenConfig を用いたデバイス限定、かつコンフィグ管理に限定した利用となりますが、GitHub 上で公開していますので、ご興味があればご覧いただけると嬉しいです。今後は、対象デバイスの拡張や実運用を見据えた機能拡充を進めていく予定です。 おわりに 上記以外にも多数の興味深いセッションがありますので、是非公開期限の 2/28 までに JANOG51 プログラムページからアーカイブ動画をご覧ください。
アバター
はじめに こんにちは。イノベーションセンター、テクノロジー部門、データ分析コンサルティングPJの更科です。 この記事では、2022年12月08日にβ版フリートライアルキャンペーンが始まった Node-AI で時系列データの因果分析・重要度可視化・要因分析などをしてみようと思います。 Node-AIは時系列データの分析をするNTT Communicationsの内製開発サービスで、製造業を中心に様々な領域で活用されています。Node-AIについて詳しくは「 ノーコードAIモデル開発ツール Node-AI 」や「 ノーコードAI開発ツールNode-AIの紹介 」をご覧ください。 読んでほしい人 ノーコード(ローコード)AI開発ツールに興味がある人 時系列データの可視化に興味がある人 因果分析、特徴量重要度、要因分析に興味がある人 伝えたいこと Node-AIを使ってデータの様々な情報を可視化できる! Node-AIを使って因果分析・重要度可視化・要因分析ができる! 重要度可視化やニューラルネットワーク(Multilayer perceptron、以下「MLP」)の要因分析ではNTT Comの独自技術が使われている! 用いるデータ 今回は こちら の、シエーヴル(ベルギー)にある住宅における消費電力、気温、湿度等のデータを使っていきます。 データには以下の情報が含まれています。 date : 日時/時刻(10分刻み) Appliances : 家電の消費電力(Wh) (AIモデルで予測する対象) lights : 照明設備が消費する電力(Wh) T1 : キッチンの気温(°C) RH_1 : キッチンの湿度(%) T2 : リビングの気温(°C) RH_2 : リビングの湿度(%) T3 : 洗濯室の気温(°C) RH_3 : 洗濯室の湿度(%) T4 : オフィスルームの気温(°C) RH_4 : オフィスルームの湿度(%) T5 : 浴室の気温(°C) RH_5 : 浴室の湿度(%) T6 : 住宅の外(北側)の気温(°C) RH_6 : 住宅の外(北側)の湿度(%) T7 : アイロン室の気温(°C) RH_7 : アイロン室の湿度(%) T8 : 子供部屋の気温(°C) RH_8 : 子供部屋の湿度(%) T9 : 両親の部屋の気温(°C) RH_9 : 両親の部屋の湿度(%) T_out : 観測所(シエーヴル)での気温(°C) Press_mm_Hg : 観測所(シエーヴル)での気圧(mm Hg) RH_out : 観測所(シエーヴル)での湿度(%) Windspeed : 観測所(シエーヴル)での風速(m/s) Visibility : 観測所(シエーヴル)での視程(km) Tdewpoint : 観測所(シエーヴル)での露点温度(°C) これらを使って未来の消費電力(Appliances)を予測するという問題を考えます。 消費電力が増える要因を特定する事で、例えば 各世帯の蓄電池への電力供給を最適化する 外気温等の予測可能なデータが影響している場合は、夜間の電気代が安い時間帯にバッテリー充電をしておく 消費電力増大の予兆が見えたら、自動で空調を弱めてブレーカーが落ちないようにする などの施策ができます。 統計量とグラフ まずはデータをアップロードして、統計量やグラフを確認します。 データのアップロードの仕方やモデル作成手順については「 ノーコードAIツールNode-AIを使って簡単に需要予測をしてみた 」をご覧ください。 (今回のデータは日付が「日-月-年」のフォーマットに従っており、Node-AIでは適切に読み込めないため、「年-月-日」のフォーマットに変形してからアップロードしています。) Node-AIは自動で統計量などを計算し、以下のように表示してくれます。 テーブル(元データ) 統計量など グラフ ここで、どのようなデータであるのか概要を掴んだり、欠損値等の確認をしてどんな前処理をするか決めていきます。 この例では、グラフを見ると多くの時間帯で消費電力が40~100程度である一方で急激に消費電力の増える時間帯がある事などがわかります。 仮に家電の使用状況等から「もっとなだらかなグラフになるはず」という事実がわかっているなら、データの測定方法に問題があるのかもしれません。 今回用いているデータに欠損値はありませんでしたが、欠損値がある場合は「欠損値補間」カードを使って直前や直後の値で埋めたり、前後の値を使った線形補間もできます。 他に「文字列置換」「外れ値補間」、「移動平均」、「時間差分」、「正規化」、「時間窓切り出し」、「時間窓結合」、「リサンプリング(アップサンプリング、ダウンサンプリング)」といった前処理用のカードが用意されています。 因果分析 データ分析を現実の問題に適用する場合、データ間の因果関係は重要な要素となります。 たとえば、A, B, C3つの値があり、AがB, Cそれぞれに影響しているもののBとCの間には因果関係がないという場合を考えます。 このとき、BとCには相関関係があるかもしれませんが、Cの値を人為的に操作してもBの値は変わらないという事になります。 Node-AIにはVAR-LiNGAM(詳しくは こちら をご覧ください。)という手法を用いてこのような因果関係を推論する機能があります。 右側にはデータ間の因果関係が表示され、左側には因果の強さが表示されます。 t=0やt=-1は時刻を表しており、今回は10分刻みのデータを用いているため、tが1増えると時刻が10分進む事になります。 この例では、消費電力(Appliances)に10分前の子供部屋の湿度(RH_8)と両親の部屋の湿度(RH_9)が負の影響を与えていると推論されています。(中央付近の5つのノードが繋がっている箇所に注目しています。) 部屋の湿度が下がった際に、加湿器を使って消費電力が増えているのかもしれません。 このように因果分析をする事でデータ間の因果関係がイメージ通りであるか確認でき、時には新しい気づきを得られる事もあります。 重要度可視化 Node-AIには特徴量重要度を可視化する「重要度可視化」カードも用意されています。 重要度可視化ではHilbert-Schmidt Independence Criterion(HSIC)を発展させたNTT Comの独自技術を使って重要度を算出しています。(詳しくは こちらの論文 をご覧ください。) 線型な関係のみを使って算出される相関係数とは異なり、HSICでは非線型な関係も検出できるという特徴があります。 相関係数と重要度(HSIC)の違いについてもう少し具体的に説明します。 以下は各グラフの相関係数を表した図です。 (出典: Wikipedia ) たとえば3段目(赤枠内)のように、2つの(数値型)データを縦軸横軸にとってプロットしたグラフがランダムではなさそうな形になる場合、2つのデータにはある種の関係があると思われますが相関係数の絶対値は大きくなりません。 しかし、これらのグラフに対してHSICの値は大きくなります。 以下は今回のデータについてNode-AIで重要度を可視化した結果です。 Node-AIでは目的変数(予測したい値)に対して重要度が高い説明変数(予測に用いる値)から順番に表示され、「類似」をクリックする事で、その説明変数に対して重要度の高い別の説明変数が表示されます。 ここではT6(外気温)に対してT_out(観測所での気温)、T2(リビングの気温)、Tdewpoint(観測所での露点温度)の順番で重要度が高いと表示されています。 T6(外気温)とT_out(観測所での気温)は、地形の問題もありますが大きく変わらないと思うので重要度が高くなるのは自然かなと思います。 T2(リビングの気温)がT6(外気温)に対して重要度が高くなるのは、リビングに玄関があって頻繁に外気が入ってくる、といった事情があるのかもしれません。 この例では単純に相関の高いものが出てきていますが、データに依っては他の関係によって重要度が高くなる場合もあります。 データ量が多く処理に時間がかかる場合などに、重要度が低いデータを説明変数から外す事で性能を保ったまま処理を高速化させる事が期待できます。 学習済みモデルの性能評価及び要因分析 最後に学習済みモデルの性能評価と、性能に影響している要因の可視化について説明します。 Node-AIには大きく分けて「ニューラルネットワーク(MLP)」、「線形モデル」、「Random Forest」の3つのモデルが搭載されています。 今回は住宅の10分間における消費電力を直近2時間の各種データ(消費電力、湿度、気温、etc.)と線形モデルを使って予測してみます。 こちらも、データの前処理やモデルの学習については「 ノーコードAIツールNode-AIを使って簡単に需要予測をしてみた 」をご覧ください。 学習済みモデルと評価用のデータを使って性能評価した結果が以下になります。 モデルの予測値と実測値が重なって表示されており、左側にはモデルの性能を表す各種指標が表示されます。(青が予測値、水色が実測値を表しています。) MAE(Mean Absolute Error/平均絶対値誤差)が27.3なので、今回作成したモデルの予測値は実測値から平均的に27.3ずれているという事になります。 モデルの性能を表す各種指標が自動で計算されるため、モデルのパラメータを変えて学習し直した時など、性能をすぐに確認できて、とても便利です。 要因分析では、この結果にどのデータが強く影響しているか一目でわかるように表示してくれます。 因果分析と少し似ていますが、作成したモデルがどの値を見ているか(或いは見ていないか)がわかるため、精度向上のためのフィードバックが得られます。 ここで、赤はプラスの影響、青はマイナスの影響を表し、色の濃さで影響の大きさがわかります。 たとえば、目的変数の直前の値だけに大きく依存して他の値をほとんど使っていない場合は、表の右上が赤くなり、他は真っ白になるため一目でわかります。(グラフを右にスライドさせるだけのモデルなどです。) このような場合はRidge回帰のように一部の値に依存しにくいモデルを使う事で、精度向上の可能性があります。 (少なくとも異なる学習済みモデルができるはずです。) ある程度精度の高い学習済みモデルができた後は、要因分析とドメイン知識を組み合わせて様々な考察ができます。 例えば、洗濯室の湿度(RH_3)に着目してみると1~2時間前の値はプラスに影響しているものの、1時間以内の値はマイナスに影響しています。 ここから「この家では、洗濯室の近くでシャワーを浴びて湿度が上がった1~2時間後に洗濯機を動かして消費電力が増えるのかもしれない。その間は換気をして湿度が下がっているのかもしれない。」といった考察ができます。(全然違うかもしれません。) このように、現実の問題を考える場合は、データからはわからないドメイン知識とデータ分析からわかる定量的な事実を組み合わせる事が大切かなと思います。 モデルでニューラルネットワーク(MLP)を選択した場合はXAI(説明可能なAI)の技術を使って要因分析を行なっています。(詳しくは こちらの論文 をご覧ください。) このように、時間ごとに要因分析が行われ動画で表示されます。目的変数の値が大きく変わったときに、どのデータが影響しているかなど、コマ送りで確認できます。 まとめ Node-AIではデータが持つ様々な情報を簡単に可視化できる! Node-AIで因果分析・重要度可視化・要因分析ができる! 重要度可視化やニューラルネットワーク(MLP)の要因分析ではNTT Comの独自技術が使われている! 終わりに いかがだったでしょうか。この記事を読んで、少しでもNode-AIに興味を持ってくれた方がいれば幸いです。2023年1月現在、Node-AIはフリートライアルキャンペーンを行っているので、興味を持ってくれた方は是非ご自身でNode-AIを使ってみてください!(リンクは こちら )
アバター
はじめに こんにちは。 Smart Data Platform (SDPF) クラウド/サーバー SDNチームの田島( @UdonYuya )です。 普段はSDPFクラウド/サーバーのSDN(Software Defined Network)基盤の開発をしています。 この記事では、2022年12月9日に開催されたTech-Nightと、2022年12月28日に行われたTech-Midnightいうイベントをご紹介します。 Tech-Nightとは Tech-NightはNTT Com内の有志で開催している発表会です。 業務や趣味で技術的に挑戦したことやサービスの裏側について部署の垣根を越えて共有することや、アウトプットの場、対外発表の練習などを目的として開催しています。 定期開催しており、最近は3ヶ月に1度の頻度で開催しています。 第1回目の開催は こちら で紹介されている通り、 2018年12月の開催で、今回の開催で4年間続くイベントとなりました。 Tech-Nightの発表内容 今回のTech-Nightでは次の6つのテーマの発表がありました。 とう道見学&チーム定例の議事録を工夫した話 JenkinsからGitHub Actionsに移行してCIの実行時間と安定性を改善した話 プロジェクトX ~データセンターYから撤退せよ~ 【新入社員が】Firecracker論文【読んでみた】 OCNにRPKI入れた話 GitHubの社内レポジトリ探検してみた それぞれの発表の簡単な紹介をします。 とう道見学&チーム定例の議事録を工夫した話 1つ目は SDPFクラウド/サーバー の仮想サーバーチームの宮岸さん( @daiking1756 )からの発表でした。 この発表では、「とう道見学紹介」と「チーム定例の議事録を工夫した話」の2つのトピックが話されました。 とう道見学 とう道は通信ケーブルを通す地下トンネルのことで、NTTでは一部地域でこのとう道の維持管理を行っています。 参考: このとう道の見学会がNTT Comでは定期的に行われており、担当者さんが丁寧に各設備の説明などを行ってくれるそうで、 ケーブルが束ねられ鉄の管の中に通されている様子も実際に見て確かめることができるそうです。 このような通信の超低レイヤをこうして体験できるのもNTTならではのことではないかと思います。 地下の大きなトンネルなので、なんとなく寒そうなイメージを私は持ったのですが、意外とそんなこともないらしく10月の朝でも中は暖かったそうです。 実際に行ってみないとわからない意外な発見ですね。 次回の開催時は私も応募して参加したいなと思っています。 チーム定例の議事録を工夫した話 次にチーム定例の議事録を工夫した話で、宮岸さんが定例会議に持った課題感とその改善策について話していただきました。 定例会議で特定のスコープの話にフォーカスしすぎて他のタスクの議論が疎かになってしまうなど、 定例会議の課題は多くの人が多かれ少なかれ抱えていてると思います。 このテーマはNTT Comの開発者ブログにも書かれているのでぜひチェックしてみてください。 JenkinsからGitHub Actionsに移行してCIの実行時間と安定性を改善した話 2つ目は飯國( @guni1192 )さんからの発表でした。 飯國さんはSDPFクラウド/サーバーにおけるネットワークコントローラ ESI (Elastic Service Infrastructure)の開発をされています。 このテーマもNTT Comの開発者ブログにも書かれているのでぜひチェックしてみてください。 飯國さんは1年目社員の方だったのですが、新しくチームに入ったばかりにも関わらず、 現行CI基盤の課題を正しく汲み取って要件定義し、問題を推測ではなく計測によって発見・改善されています。 CI基盤の改善は社内の他チームにとっても重要な課題であり、知見の共有としてもかなり意味のある発表でした。 プロジェクトX ~データセンターYから撤退せよ~ 3つ目はSDPFクラウド/サーバー開発環境の保守運用チームの方からの発表でした。 この発表では、SDPFクラウド/サーバーの開発環境のサーバーやネットワーク機器を含むデータセンターの移転についてお話をしていただきました。 SDPFクラウド/サーバー開発組織にはデータセンターの配線やラック管理など物理作業の専門チームがいて、 開発チームはそれより上のレイヤに専念できるような体制となっております。 今回のデータセンター移転に際しても、非常に困難な課題であるにも関わらず私達開発チームへの依頼はほとんどなく、普段の開発業務に集中できました。 その内部では大規模なサーバーやネットワーク機器の再設計や再配線などの物理的な工程を始め、 タスクの切り分け、チーム・部署・会社を越えたマネジメントなど、開発とは別次元のエンジニアリングが行われていることを知ることができました。 【新入社員が】Firecracker論文【読んでみた】 4つ目は一人目の宮岸さんと同じSDPFクラウド/サーバーの仮想サーバーチームの松下さん( @bean_public )からの発表でした。 チームで実施しているジャーナルクラブという勉強会で共有した内容をTech-Nightでも発表していただきました。 Firecrackerとは、AWS Lambdaというサーバーレスのコンピューティングサービスで使われている仮想化基盤です。 発表では論文を読んでわかったFirecrackerの目的やアーキテクチャを、 SDPFクラウド/サーバーの仮想化基盤の開発者視点で説明していただきました。 個人的にはジャーナルクラブという取り組みが素晴らしいなと感じていて、 仕事以外で何かを学び、さらにそれをチームに共有できる場所と時間を設けていることを真似したいと思いました。 とりあえずFirecracker論文は読もうと思います。 あと、SDPFクラウド/サーバーにも実装されることを期待します(笑) OCNにRPKI入れた話 5つ目はインターネットプロバイダーであるOCNのバックボーン開発チームの中森さん( @to_nakamori )からの発表でした。 RPKI(Resource Public Key Infrastructure)とは、 IPアドレスやAS番号などのアドレス資源の割り振り・割り当てを証明するための公開鍵基盤のことで、 経路ハイジャックの防止などインターネットの経路制御をセキュアに保つための仕組みです。 (参考: https://www.nic.ad.jp/ja/rpki/ ) RPKIではROA(Route Origin Authorizations)と呼ばれるIPとAS番号のペアをリソース証明書から生成し、 それを実際に受信した経路情報と比較して正しい経路なのかを判別します。 今回の発表では、ROAキャッシュサーバーをOCNに導入した経緯や導入途中で見つかったバグ、行った試験などを話していただきました。 OCNへの導入は(OCN顧客向けは除き)完全に完了されたそうですが、RPKIの性質上OCNだけが導入してもあまり効果がでないため、 今後は他のISP事業者などにRPKIの導入、ROAの登録を促していくことが必要になってくるそうです。 GitHubの社内レポジトリ探検してみた 最後はイノベーションセンターでセキュリティ製品の研究開発をしているMetemcyberチームの西野さん( @nitky )からの発表でした。 セキュリティ強化ツール Threatconnectome の検証で得た知見から、NTT Com社内リポジトリの特徴を紹介していただきました。 NTT ComではGitHub Enterprise Cloudを利用しているのですが、 エンタープライズ配下にOrganizationだけでも13個、リポジトリはinternal/publicだけでも640というかなりの数となっていてまさに探検といった感じでした。 発表の中では各Orgの特徴やよく使われている言語、見どころなどが紹介され、 例えば私が所属しているSDPFクラウド/サーバーのOrgはリポジトリ数が302(社内公開になっているもの)もあり、 1週間に約150ものリポジトリを更新する巨大Orgであることが紹介されました。 またエンタープライズ全体での拡張子別の集計の紹介もあり、プログラミング言語ではPython、Golang、JS、PHPの次にCUE言語のファイルが多いこともわかりました。 CUE言語が全体の5位に来るのはかなり特徴的なんじゃないかと思います。 ほかにもpemファイルが1133あり、一見セキュリティ上まずいんじゃないかとなるんですが、これは性能検査などのテスト用に使われているファイルで大丈夫とのことでした。 発表の最後ではGitHub運用やコードが参考になるおすすめOrgの紹介もあり、 私は特にCI/CDの手法なんかをこのおすすめOrgのリポジトリから真似しようかなと聞きながら考えておりました。 Tech-Midnightとは Tech-Midnightは2020年から行われている年末に開催しているLT大会です。 Techという名前を冠していますが、こちらではトークテーマに制限なく何でも5分間話していい会となっています。 Tech-Midnightは年末に開催されることもあって、某国民的歌合戦になぞらえて紅白に分かれたLT合戦という形式で行われています。 本家の紅白の組分けは男女で分けられますが、こちらの組分けは完全ランダムとなっています。 2020年と2021年はどちらも紅組の勝利となっており、今年は白組の初勝利となるか、紅組の三連覇となるか、という視点でも盛り上がりました。 このLT大会が終わるといよいよその年が終わったなという実感が湧きます。 Tech-Midnightの発表内容 またTech-Midnightでは次の24個(多い!)テーマの発表が紅白に分かれて戦いました。 ちなみに私もTech-Midnightでは発表者として参加して、紅組の「Build an Orchestrator in Goの紹介」という発表をしました。 そして気になる結果は、なんと初の白組の勝利でした! 今年感動したアルゴリズム1・2や、危ないアプリHackの類型(スマホアプリのハッキング・グリッチ事例紹介)などを中心に 聞き手の驚きを誘う発表が多く納得の勝利となりました。 Tech-Midnightは比較的内輪向けのイベントのため発表の詳細は割愛しますが、 Techに限らずバラエティ豊かなテーマと5分の発表のテンポ感が相まり、Tech-Nightに負けず劣らず盛り上がりました。 おわりに 本記事ではNTT ComのエンジニアコミュニティのイベントであるTech-NightとTech-Midnightを紹介しました。 これまで紹介したように、ネットワークからとう道、フォロワーの増やし方や留学のお話など本当にいろんなテーマの話を聞くことができ、非常に好評なイベントとなっています。 さらにNTT Comではお昼の勉強会の TechLunch や、 一般向けの NTT Com Open TechLunch も開催しています。 このようなイベントの開催は、知識・情報の共有だけでなく、さらにアウトプットを通した発表者個人の知識・技術の向上やエンジニア組織としての一体感の醸成にも繋がっています。 今後もこの取り組みを続けるとともに、輪を広げ、より活力のある組織へ成長させていきたいと考えていますので、共感できる方はぜひ! NTT Comではメンバーを募集しています 新卒採用 NTTドコモのサイトに移動します(NTT ComはNTTドコモグループの一員として新卒採用をしています) 経験者採用 障がい者採用
アバター
はじめに 初めまして!イノベーションセンターテクノロジー部門 データ分析コンサルタントPJの松岡和志です。普段はお客様の経営課題に対して、データ分析を通して解決策を提示する仕事をしています。 この記事では内製開発サービスである ノーコードAI開発ツール Node-AI を用いた需要予測について紹介していきます。 読んでほしい人 AIについてこれから勉強しようと思っている人 AIを使ったデータ分析をやりたい人 AIを活用した需要予測を業務に導入したいと思っている人 効率的にデータ分析したい人 ノーコードツールに興味がある人 伝えたいこと Node-AIで簡単に需要予測をするやり方 Node-AIはAIを作る工程がわかりやすく分かれていること Node-AIとは Node-AIは、ブラウザ上からノーコードでAIモデルを作成できるサービスです。NTT Comが独自に開発し、 2021年10月11日にリリース しました。現在は製造業のお客様を中心に、異常検知やプラント運転支援などの様々な領域で活用されています。 以前 こちらのブログ記事 でも紹介しているので、お時間がある方はぜひこちらもお読み下さい! また最近 このような賞 も受賞させていただきました。 何を予測するのか 今回取り扱うデータは、2011年~2012年のワシントンD.Cで使われた自転車シェアサイクル 1 です。 こちら からダウンロードできます(要ユーザー登録)。 データのカラムとしては以下のようなものがあります。 dteday : 日付 season : 季節 (1:春, 2:夏, 3:秋, 4:冬) yr : 年 (0:2011、1:2012) mnth : 月(1〜12) hr : 時間(0〜23) holiday : 休日の判定 weekday : 曜日 workingday : 平日は1、土日祝日は0 weathersit : 1 : 晴れ~少しの雲 2 : くもり 3 : 小雨 4 : 大雨 temp : 気温 atemp: 体感温度 hum:湿度 windspeed:風速 casual:このシェアサイクルの非会員の自転車利用数 registered:このシェアサイクルの会員の自転車利用数 cnt: 自転車利用の総台数 (casualとregisteredの合計) 今回は自転車利用の総台数(cnt)を予測するために、会員の利用数(registered)と非会員の利用数(casual)を分けて予測するモデルを作成していきたいと思います。※流れは同じなのでcasualのみ対象として記載します。 自転車利用の総台数を予測することによって 利用希望者が全員満足に利用できる台数の必要最小限数での設置 利用数の少ない時間帯に合わせた計画的なメンテナンス実施 等ビジネスに有効な施策を講じることができます。 予測モデル作成の流れ 予測モデル作成は データの統計やグラフを見ることで全体の概要を把握 データの前処理(AIモデルを当てはめるためにデータを加工したり抜けているデータを補完したり、モデルを作るデータとそのモデルを評価するデータに分けたりすること) モデル作成、学習 評価 (評価で納得できなければ)前処理に戻ってパラメーター変更 といった流れになります(より詳細な内容やNTT Com独自のノウハウについては こちら )。 Node-AIでは予測モデル作成に必要な工程がカードで分かれているため、どうやってAIモデルを作るのかを理解したい人にもおすすめです。 予測したいデータを設定する まずはNode-AIに予測したいデータをアップロードします。上記のデータをダウンロードしてから、こちらからデータをNode-AIにアップロードします。 その後できたデータカードを、キャンバス(Node-AI上にてデータを配置したりデータを処理するカードを設置するエリア)に出してからクリックして開いてみると、データのテーブルや各カラムごとの統計、時系列での推移が可視化されているグラフが見られます。 各カラムの統計 統計について、予測したいcasualの『75%』と『最大値』を見てみます。 『75%』は48.000で『最大値』は367.000となっていて、これはcasualの全データ中75%は48以下であり、最大値は367ということを示しています。 また『平均』が35.676であることから、1時間での非会員の利用数は大体30台〜50台であるが、何かしらの出来事があった際に10倍近くの自転車が使われたということがイメージできます。 この10倍近く使われる台数と時間を予測できれば、利用者と会社双方に理想的な自転車の設置ができそうだと思いますね。 予測したいデータ(casual)の時系列推移 時系列推移の期間選択を見ると、後半にある山が高く、かつ山になる頻度が増えているように見えます。 これはざっくり2012年の方が2011年と比較して利用台数、利用頻度が増えている事を示します。 情報が無いのであくまで想像の域を出ませんが、このサービスの認知度上昇や利用可能場所の増加によってより多くの利用者が増えたのではないかといったことも考えられます。 ここで今回予測する非登録者の利用数(casual)を目的変数、そして予測に関係していそうなパラメーターとして holiday : 休日かどうかの判定 weekday : 曜日 workingday : 土日祝日でない場合は1、そうでない場合は0。 weathersit : 1 : 晴れ~少しの雲 2 : くもり 3 : 小雨 4 : 大雨 temp : 気温 atemp: 体感温度 hum:湿度 windspeed:風速 を説明変数に選択して保存を押します。 これでデータの設定は完了しました。 データの前処理~モデル作成~学習~評価 ここからがさらに簡単です。 本来データ分析をする際には、様々な工程をRやPythonといったプログラミング言語を用いて実装していく必要があります。Node-AIはこの工程をカードを繋げるだけで実装できます。(様々な工程について説明した内容は こちら ) そしてさらにNode-AIには『データの前処理~モデル作成~学習~評価』といった一連の流れをできるようカードが設計されている レシピ があります。 レシピとは、処理したい様に繋げたカード群等を保存呼び出しできる機能です。 クイックスタートレシピをダウンロードしてインポートすることで、データの前処理からAIモデルの評価までつながっている状態のフローが出てきます。 基本的に上のカードから順にクリックして実行を押すだけで、AIモデルができて評価まで見ることができます! このレシピの中で少し設定が必要なデータ分割のカードと時間窓切り出しについてお話しします。 データ分割 これは元々のデータカードから、どこまでをAIモデルを作るためのデータ(学習データ)とそのAIモデルがどの程度あっているかを確認するためのデータ(テストデータ)に分けるものです。 今回はシンプルに、2011年のデータは学習データに、2012年はテストデータにするように設定します。 時間窓切り出し これはモデル作成の際に時系列データを学習可能な形式に変換するカードです。 今回の様に多変量での時系列データを予測するモデルを作成する際には、予測先の1つの時点を予測をするために、一定の時間幅で切り取ったデータを用いる事が多く、このカードはその設定をするものです。 詳しい説明はこちら 。 今回は1週間後の需要予測をするために、過去1週間のデータを説明変数とするよう設定します。 このデータは1時間刻みのデータなので、1週間は24(時間)×7(日)=168をN(予測先)とM(窓幅)に設定します。 他のL(丸め幅)とS(ストライド幅)は、推奨されている1に設定します。 評価 MLPカードや学習カード、正規化カードについてもクリックしてすぐに実行を押します。以下のような予測モデルができました。 作成したモデルは既知のデータを元に作られたものなので、未知のデータをどの程度予測してくれるのかを評価する必要があります。 しかし、手元にない未知のデータに対して予測精度の評価はしようがありません。 そこで学習データとテストデータに分けて、学習データで作成したAIモデルの予測値とテストデータの値を比較することでAIモデルの予測精度を評価します。 今回は2011年の情報を学習データとして予測モデルを作成し、2012年の情報をテストデータとすることで予測モデルの評価をします。 今回はRMSE、R 2 の2つを評価指標として考えます。 RMSEとは二乗平均平方根誤差のことで、予測値と実測値の誤差を2乗して平均したものの平方根を取ったものです。 RMSEは0に近いほど予測精度が高いということを示しています。 R 2 とは決定係数のことで、モデルと実測値を比較してのモデルの当てはまりの良さを示しています。1に近いほどモデルが実測値に当てはまっていることになります。(ただし、これがどのくらいの値が良いかの目安は諸説あります) R 2 =0.57ということでそこそこ当てはまっているんだなということが分かります。 今回取り扱わなかった評価指標や今回取り扱った2つの詳細については こちら をご覧ください。 パラメーターの再設定 このようにレシピを使えばすぐにモデルを作成できますし、より精度を上げたいなと思ったらAIについて調べて勉強すればよりNode-AIを使いこなせるようになります! たとえば多重共線性と呼ばれる、説明変数間で相関係数が高いものを用いて分析するという問題を回避するために、説明変数を減らしてみるなどが考えられます。 実例として先程の分析の説明変数を見てみると holidayとworkingday tempとatemp はそれぞれ似たようなものを説明していると思いませんか? ということで多重共線性を回避するために、holidayとatempを説明変数から除外して分析した結果がこちらになります。 評価指標はこのように変化しました。 RMSE 39.0→36.5 R 2   0.57→0.62 RMSEは0に、R 2 は1に近づくことで予測精度とモデルの当てはまりの良さが向上しました。 これ以外にも様々な手法があるので、ぜひとも自分の手で動かしてみてください! ちなみに、Node-AIを起動してから最初のモデル評価を確認するまで1時間程度でできました。この工程を直感的なカード配置と簡単な数値入力でできるのは個人的には非常に楽だなと思います。 もう1つのやさしい機能として NTT Com独自技術 でもあるAIの可視化機能の重要度可視化や要因分析、因果分析については別の記事で話させていただきたいと思います。 まとめ Node-AIを使うことによって簡単に需要予測のAIが作れる カード毎に分かれているのでAI作成の工程が理解できる レシピを使うことによってデータをつなげるだけでAIモデル作成評価ができる 現在Node-AIは メールアドレスを登録するだけで30日間無料で使えます!  こちらの記事を読んでデータ分析に興味を持った方はぜひ登録して使ってみてください。分析対象となるデータから、データをつなげるだけですぐAIモデル開発までできるレシピもあります。まずは触ってみてご自身の職場で使えそうだなと思ったら、ぜひとも こちらのフォーム からお問い合わせをお願いします。 Fanaee-T, Hadi, and Gama, Joao, "Event labeling combining ensemble detectors and background knowledge", Progress in Artificial Intelligence (2013): pp. 1-15, Springer Berlin Heidelberg, doi:10.1007/s13748-013-0040-3. ↩
アバター
この記事はSDPFクラウド/サーバー 仮想サーバーチームの宮岸( @daiking1756 )とCOTOHA Call Center開発チームの立木の共同執筆です。 二人共エンジニアではあるのですが、普段は全く違うチームで開発をしています。 この記事では、普通に働いていたら交わることのない私たちが、一緒にハッカソンに参加したことで得た学びと知見を共有します。 はじめに 私たちが今回参加したのは Twilioハッカソン2022 です。 その名の通り、Twilioを使った作品を作るハッカソンです。 今回のテーマは - Twilioを使って、ちょっと仕事を楽にしよう - でした。 当日はエンジニアを中心に30名ほどが参加しておりました。 宮岸は学生時代からハッカソンが好きで、 このTwilioハッカソンも以前から参加してみたかったものの1つでした。 今回2人でTwilioハッカソンに参加したきっかけは後述します。 まずは「ハッカソン」や「Twilio」について、用語の説明と2人の経験を載せておきます。 ハッカソンについて そもそもハッカソンって何?という方向けに、説明を載せておきます。 ハッカソンは、ハック(hack)とマラソン(marathon)を組み合わせた造語とされ、 プログラマーや設計者などのソフトウェア開発の関係者が、 短期間に集中的に開発作業を行うイベントを指します。 https://www.nic.ad.jp/ja/basics/terms/hackathon.html エンジニア界隈ではある程度知名度があると思いますが、語源は意外と知られていないかもしれないですね。 そして宮岸と立木のハッカソン歴は下記のとおりです。 宮岸: 学生時代からハッカソンに参加するのが好きだった 立木: 学生時代に1度だけハッカソンに参加して以来(人生では2回目) Twilioについて Twilioは電話やSMS・ビデオ・チャット・SNSなど世の中にある様々なコミュニケーションチャネルをWeb・モバイルアプリケーションとつなぐ「クラウドコミュニケーションAPI」です。 https://cloudapi.kddi-web.com/availability 一言でいうと、APIから電話を掛けたりSMSを送ったりできるサービスです。 また、それ以外にもビデオチャットやグループ通話など、コミュニケーションに関する様々な機能が提供されています。 気になる宮岸と立木のTwilio歴は下記のとおりです。 宮岸: できることは何となく理解していたけど、使ったことは無かった 立木: 業務で数ヶ月使っているけど、本格的に学んだことはなかった Twilioハッカソンに参加したきっかけ 1年目研修の中で グループリフレクション(ぐるり) という、3、4人組での振り返り会があります。 ぐるりに立木は参加者として、宮岸は3年目社員ファシリテーター役として参加していました。 立木が「業務でTwilioを触っている」と話していたため、 宮岸が「Twilioハッカソンってのがあるけど、よかったら一緒に出てみない?」と声を掛け、 今回2人でTwilioハッカソンに参加するきっかけとなりました。 また、各々の参加のモチベーションとしては下記のとおりです。 宮岸: ハッカソンに参加するのは好きで、前からTwilioハッカソンにも出てみたかった 宮岸自身がハッカソンに参加して良いカルチャーショックをたくさん受けたので、立木さんにも体験してみて欲しかった 立木: 業務でTwilio使っていて、知識を深めたい状態だった ハッカソンに出て外部のエンジニアと交流してみたかった ハッカソン当日の流れ 当日の細かい流れは割愛しますがざっくりと書いておくと下記の通りです。 サポート技術インプット(Twilio / kintone / obniz / LINE) アイデア発散とチームアップ ハッカソンタイム 成果発表 サポート技術インプットの時間で各サービスが実現可能なことの説明を聞いた後は、さっそく開発する作品のアイデアを練る「アイデア発散」の時間です。 1つのスプレッドシートを参加者全員で一斉にワイワイ編集するスタイルです。 30人でワイワイやるのはお祭り感があって楽しかったです。 具体的な流れは下記の通りです。 ステップ1: 最近感じた課題を一言で書く(10分間で150個埋まった) 例: 「チャットに気づかない時がある」 ステップ2: 他の人が書いた課題に対して一言でコメントする(共感/発散/解決策) 例: 「一定時間経過したら電話で通知」(10分間でステップ1の150個の課題に対してコメントが付いた) ステップ3: ステップ1とステップ2を見て何か思いつくアイデアを各自が記入する(10分間で約200個のアイデアが生まれた) 例: 「リアクションが一定時間無いユーザにチャットの内容を自動音声で連絡するサービス」 結果、30分で約200個のアイデアが生まれました。 その中から宮岸のアイデアが上位に選出され、そのアイデアに共感してくれたメンバーも立木と他に2名集まりました。 こうして計4名の「チームtwelve」が誕生しました。 ※ ぐるりのチーム番号が12だったことと、Twilioとスペルが似ていることがチーム名の由来です。 裏話ですが、今回は2人で申し込みしたため、「事前にこういうもの開発しようねー」というアイデアを持って参加しました。 しかし、アイデア発散をしている間に楽しくなってしまい、結局事前に練っていたアイデアとは全然違う作品を開発しました。 続いて、実際に開発に取り組む「ハッカソンタイム」です。私たちのチームではまず、メンバーで作業分担を決めました。 今回は、メンバーの経験や希望を踏まえて、 リーダー: 1人(宮岸) 開発: 2人(立木) 発表資料作成: 1人 という形にしました。 開発と同時に発表資料作成をすることで、4人の認識を合わせながら開発を進めることができる、はずでした。。。 詳細は後述します。 開発の流れについては、最初に各機能を実装するパーツ(関数)を作り、その後一気につなぎ合わせる方法を取りました。 機能の結合を始めるタイミングが遅れたため、全てのパーツ(関数)をつなぎ合わせることはできませんでしたが、最低限の機能+αが動く作品を作ることはできました。 こちらも詳しくは後述します。 作った作品について(やさしい呼び出しくん)の概要 私たちは「やさしい呼び出しくん」という作品を開発しました。 作品の概要は下記の通りです。 障害対応などの急ぎの要件で複数人をメンションした際に、反応が無い人には自動で電話を掛けてくれるアプリケーション。 これで人の呼び出しではなく、急ぎの用件に集中できます。 今回のハッカソンで開発された作品はProtopediaというサービスに登録することになっています。 詳細はそちらに情報をまとめておりますので、リンク情報を載せておきます。 protopedia.net 作品の構成と処理の流れ 制限時間の関係で最後までは完成しませんでしたが、今回開発した作品のシステム構成図は以下の通りです。 AWSのLambda上でServerless Frameworkを使い、Expressを動かしています。 今回参加したハッカソンではスポンサーにTwilio・kintoneが参加しているため、電話の発信とSMSの送信にはTwilioを、データベースはkintoneを使用しました。 また、Slackからのメンションとリアクションの情報を取得するためにSlack APIを、遅延処理を行うためにAWSのSQSを使用しています。 システムの処理の流れは以下のようになっています。 Slackで特定のワードが先頭に付いたメンションを含むメッセージを投稿する(例:「!やさしい呼び出しくん @A-san @B-san 〇〇のシステムがダウンしているようです。急ぎで対応お願いします。」) SlackのEvents APIを使用し、特定のチャンネルのメッセージ投稿を検知し、Lambdaにリクエストを投げる メンション付きの投稿である場合は、メンション元・メンション先の情報や日時などの投稿の情報をkintoneに保存する 投稿にリアクションがあった場合はSlackのEvents APIを使用し、Lambdaにリクエストを投げ、kintoneのステータス情報を更新する SQSでn分後、リアクションをしていない方全員に対してTwilioから発信する処理を実行 電話に応答できなかった方には、TwilioからSMSを送信する 今回はSQSを使った遅延処理まではできず、投稿後すぐにTwilioから発信する仕様になりました。 最終的には期限までに完成させることができ、ローカル環境ではありますが発表会でデモを行うことができました。 審査の結果・・・ 審査の結果、やさしい呼び出しくんが 最優秀賞 を頂くことができました! リアルタイムデモが成功したこと、テーマに沿った作品だったことと、Twilioの様々な機能を活用していたことが「Twilioハッカソン」としては評価されたようです。 またチームとしては、諦める機能と諦めない機能を合意して、途中から発表を意識した開発できたことが良かったと思います。 参加して気づいたこと、思ったこと、学び 時間の制約のある中での開発の難しさ 今回ハッカソンに参加してみて、時間の制約のある中での開発は難しいと感じました。 具体的には、メンバー間で実装箇所の認識に齟齬があり、2人が同じ部分を実装してしまうということが起こりました。 この問題を解決するために、リーダーと開発メンバー2人で話し合い、同じ機能を2人で分担して実装していたところを、それぞれが別の機能を実装するように変更しました。 ハッカソンという時間の短い中で早く作業を進めようとするあまり、このような役割分担やお互いのコミュニケーションが疎かになりがちだと感じました。 役割分担の明確化やお互いの進捗の共有などの意思疎通の重要性を改めて感じました。 また、開発手法についても、パーツ(関数)を後で一気に繋ぎ合わせるのではなく、できた機能から結合させていく方が良かったのではないかと思いました。 そうすれば、機能自体は完成したにも関わらず、作品には組み込めないということが起こらないためです。 実際に、今回パーツが完成していたのにも関わらず、作品に組み込めなかった機能があり、もったいなかったです。 作品のゴールを共有することの大切さ 今回は作品の目指すべき方向性の議論は重要視せずに、開発に着手しました。 ハッカソンの限られた開発時間では、早く手を動かしてどんどん開発を進めたくなってしまうものです。 1人で開発しているのであればそれでもいいのですが、今回はほぼ初対面の4名という即席チームだったので、結果的にこれが裏目に出ました。 必要のない機能を開発しそうになったり、想定していないユースケースに対応しようとしてしまっていたのです。 まだアイデアも生煮えの状態で開発に着手して、作りながら作品の価値を模索していくような状況では、MVP(Minimum Viable Product、顧客のニーズを満たす最小限のプロダクトのこと)を最速で作ることを意識するべきでした。 今になって振り返ると「最初にラフな資料を作って認識を合わせ、MVPができるまではなるべくモブプロ・ペアプロを多めでやっていく方が良かったなー」と思います。 ※ 頭の中で 移動手段のMVPのメタファー が何度もよぎりました。 この経験は今後ハッカソンに参加するときは勿論、業務での開発やその他のチーム開発など、あらゆるところで活きるものとなりそうです。 知識として理解はしていましたが、百聞は一見にしかず、百見は一体験にしかずという具合に、ハッカソンというある意味本番ではない場で一度体験できたのは非常に有り難いです。 メンバー間でコミュニケーションを取りながら本当に価値があることに時間を使うことが重要 と強く感じました。 個別の感想 宮岸の感想 自分がアイデアの発起人ということもあり、ハッカソン中は開発者というよりもユーザ目線で作品のことを考えることを意識していました。 今まで参加したハッカソンで、もしかすると自分が書いたコードは少ないかもしれませんが、 「どういう課題を解決する作品にしたいか」 「ユーザ目線だとどういう機能があると便利か」 「実装コストを考えると何を諦めるべきか」 といったことを2日間で考えることができ、非常に貴重な体験となりました。 たまたま研修で出会った立木さんと一緒にハッカソンに参加して、チームメンバーで足りないところを補いながら高め合い、最優秀賞を受賞できたことは素直に嬉しかったです。 ハッカソンやプロトタイピングが好きな仲間が社内外にもっと増えれば、盛り上がって嬉しいですし、この記事を読んで興味を持つ方がいれば幸いです。 立木の感想 私は社会人として参加するハッカソンでは初めて(人生では2回目)の参加となりましたが、非常に学びのあるハッカソンだったと感じています。 今回他社のハッカソン常連者の方と同じチームで参加し、実装面や使用する技術という点でも新たな学びを得ることができました。 他社のエンジニアの方と交流する機会はあまりなかったため、刺激になりモチベーションを高めることができました。 また、ハッカソンという場を通じて、Twilioやkintone、Slackなど様々なサービスのAPIに触れることができ、勉強になりました。 期間中はドキュメントを見ながら試行錯誤して実装し、結果としてサービスに詳しくなり技術力を高めることができたと感じています。 加えて、限られた時間内にチームで議論し、協力しながら1つのものを完成させるハッカソンは楽しいと改めて思いました。 また機会があればハッカソンに参加したいと考えています。 おわりに 今回はTwilioハッカソン2022に参加して得た学び・知見について紹介しました。 初めて会う方もいる中でのチームマネジメントの難しさ、時間の制約のある中での開発など困難もありましたが、ハッカソンで作品を完成させるまでに数多くのことを学べました。 結果的に、最優秀賞も受賞でき、良い経験になりました。 今後もこの経験を活かし、さらにエンジニアとしてパワーアップしていきたいと考えています! 最後にお知らせですが、NTT Comは一緒に働く仲間を募集中です。詳しくは下にある採用情報を確認していただけると嬉しいです!
アバター
この記事は NTTコミュニケーションズ Advent Calendar 2022 18日目です…が、少々遅れてお届けします。 はじめに PS本 5G&IoTサービス部 増田です。Advent Calendar参加も(たぶん)3年目となりました。 「テキスト指示をもとに、AIがお好みの画像を生成する(Text-to-Image)」「今ある画像へ、テキスト指示で編集を加える(Image-to-Image)」 -- そんな画像生成AIが注目を集めています。2022年の上半期、Open AIによるDALL-E 2の公開、Midjourneyの登場と盛り上がりを見せました。2022年8月23日のStable Diffusion一般公開からは、使ってみた報告、従来研究との融合、商用プロダクト応用と、研究者、アーティスト、プログラマなど様々な人の参加により、界隈はさらに活気づいています。 画像生成AIの技術解説は、たくさんの素晴らしい記事に譲ります。技術の全体像説明 1 や、図解による日本語 2 や英語 3 の解説があります。今年のアドベントカレンダーでも、テーマとして複数取り上げられました。そのものズバリ 画像生成AI Advent Calendar 2022 に様々な考察を読むことができます。また、2022年8月〜11月の目まぐるしい変化は、やまかず氏のnote 『 日刊 画像生成AI 』に詳しくまとまっています。 ジェネラティブAI全般にも、上記の画像生成のコアとして使われる「拡散生成モデル」ブームが到来しました。テキストからの画像生成とその発展に加え、サウンド生成、動画生成、モーション生成、3Dモデル生成と、ここ数ヶ月で様々な論文が発表されました。 次に興味が湧くのは、こうした技術が今後どのように社会に受容され、影響を及ぼすかという点です。本記事では、画像生成を中心としたジェネラティブAIを取り巻くビジネス、技術応用、法律その他を以下のステップで概観します。 ジェネラティブAIのビジネス機会と投資 差異化要素を生み出す人材、プロダクトやその素材 考慮すべき制約(法解釈と規制、コミュニティの倫理観) これから数年スパンで、ジェネラティブAIどう浸透し、社会を変化させるかという流れを読み解く、または予測する一助となれば幸いです。 1. ジェネラティブAIのビジネス機会と投資 ジェネラティブAI、中でも画像生成AIの技術的ブレークスルーは、ビジネス機会や投資目線ではどのように捉えられているのでしょうか。 IT分野を中心とした調査会社 Gartnerは、2022年の戦略的テクノロジートレンドの先頭に「ジェネラティブAI」を取り上げました。2025年には全データの10% (現在は1%未満) をジェネラティブAIが生み出すと予測しています。次に「来る」技術を予測するGartnerハイプサイクルでは、ジェネラティブAIが盛り上がりのほぼ頂点にプロット 4 されました。 イギリスの経済誌 Financial Timesは、ジェネラティブAIへの投資が2020年比で425%増加し、21億米ドル (約2800億円) に達する 5 と報じました。Web3の減速とAIへの資金還流が伸びの原因とされています。事実、大型調達の発表が相次いでいます。2022年10月には、Stable Diffusionモデルの発表元 Stability.aiが1.1億米ドル (約150億円) の資金調達 6 をし、成長戦略を発表しました。12月には、同じくStable Diffusionに関わったRunway MLが、5000万米ドル (約68億円) の調達を発表 7 しました。以前よりジェネラティブAIで名の通っていた会社だけではありません。Stable Diffusionモデル公開後に生まれた会社もあります。例えば Sharif Shameem氏 によるLexica.artは、Stable Diffusionの公開直後に生成コンテンツ検索エンジンとして開始 8 、すぐに500万米ドル (約6.8億円) を調達 9 しました。モデルのオープン化がもたらした効果といえるでしょう。 さて、これらの調達資金は何に投じられ、その会社とその顧客にどんなリターンを生むと考えられるでしょうか。 2. 差異化要素を生み出す人材、プロダクトやその素材 一般に、どこにリソースを投下することが最も効率よく事業を成長させるか、「てこ」となる場所は事業種別により異なります。そのため、調達資金の重点投下先も異なります。例えば、企業向け業務系SaaSの場合は営業やカスタマーサクセスの人員、個人向けゲームやコンテンツであればその制作費や広告宣伝費に多くを投じるでしょう。AIスタートアップの場合、それらに加えて独自のデータセット、計算資源を投下した学習、抱える研究者の技術によりもたらされる独自のモデル、それらを利用シーンと結合させるアプリケーション開発、プロフェッショナルサービスによる課題ごとにカスタマイズをした対応などが競争力と成長の源泉となります。 ジェネラティブAI、特に画像生成AIの付加価値や差異化要素は、どう生み出されるのでしょうか。ここでは 「人材やステークホルダ」 が 「プロダクトを生み出す素材や道具」 に作用し、 「法規制と倫理観」 の制約下で 「プロダクト」 を生み出すとモデル化します。各要素を詳しく見ていきましょう。 2.1. 人材やステークホルダ 主要な人材やステークホルダには、 「クリエイター」「アプリケーション開発者」「研究者」 が挙げられるでしょう。また、領域間の越境人材も見られます。 クリエイター 「絵を描く」「CGを制作する」といった活動をするクリエイターは、アマチュアかプロかを問わず、既に自身の制作プロセスに画像生成AIを取り入れ始めています。日本での一部事例を挙げます。 深津貴之氏 は、Stable Diffusion前夜の記事 10 で耳目を集め、SFマガジン 23年2月号表紙の制作 11 などいちはやく制作現場に取り入れています。 852話氏 は、Midjourney、Stable Diffusionを活用した美麗な画像を日々発表し、画像生成AI初の紙刷り画集を出版しました。 横原大和氏 や 齋藤彰氏 は、元々3D CGに強みを持ちながら、画像生成AIを制作プロセスに取り入れた習作例を日々発信されています。 アプリケーション開発者 機械学習モデル全般に言えることですが、モデルがあるだけでは「試す」ことはできても「継続して使う」ことは困難です。利用シーンに合わせたツールやアプリケーションに埋め込まれて初めて真価を発揮します。アプリケーションを開発し、継続的に改善できるエンジニアが必要です。 また、アプリケーションには二種類あります。既にあるアプリケーションやWebサービスに機能要素を足すケースと、スタンドアロンで新規アプリケーションを開発するケースです。Lexica.artの Sharif Shameem氏 や、LINEボット お絵描きばりぐっどくん の生みの親である 西野颯真氏 は後者の例と言えるでしょう。後段の「プロダクト」の項目でも取り上げます。 研究者 ジェネラティブAI領域にもともと関心があった研究者が、今回のブームを受け一歩早く動き出しています。一例に、Google Brainの著名な研究者であった David Ha (hardmaru) 氏 は、Stability.aiにHead of Strategyとして移籍しました。Stable Diffusion以前に静かな盛り上がりを見せたDisco Diffusion界隈のオープンソース活動をしていた研究者は、複数がMidjourneyへ立上げメンバ等として参画しました。 2.2. プロダクト 上に挙げるような人材やステークホルダにより生み出されるプロダクトには、どんなものがあるでしょうか。 アーリーステージ、テック企業向け投資を主に手がけるベンチャーキャピタル Andreessen Horowitz (a16z) は、画像生成に限らず、ジェネラティブAIの「今」を捉える記事を発表しています。2022年11月には、「Art Isn’t Dead, It’s Just Machine-Generated (アートは死なない。ただ機械が生成する) 12 」「The Generative AI Revolution in Games (ゲーム業界における生成AI革命) 13 」の2本が掲載されました。 「Art Isn’t Dead, It’s Just Machine-Generated」では、コード自動生成 (Copilot等) と画像生成を比較して取り上げています。前者に求められる厳密さに対して画像生成は既に十分実用に耐える出力ができる点と、画像生成AIのハイプは (関連リポジトリのGitHubスター数ベースでは) ブロックチェーンブーム等を遥かに凌駕する点に触れ、画像生成などのジェネラティブAIが一気に普及すると論じています。「2022年末のインターネットアーカイブが、人間が多くを生成した最後のリポジトリとして大切にされる日が来るかもね」という締めが印象的です。 「The Generative AI Revolution in Games」では、コスト・品質・スピードのうち2つしか取れないトレードオフ問題の終焉、産業の中では複雑性・リアルタイム性が高いゲーム業界が最も大きな影響を受けるだろう、と論じています。 ここでは、活用が期待される画像生成AIプロダクトについて、「四階建て」の構造でモデル化して議論を進めます。プロトタイプフェーズと、製品フェーズでも構造が多少異なると言えますが、本稿では詳細を省きます。 下階から上階に積み上げる形で、 「基礎となる画像生成モデル」「追加学習等によるカスタマイズ」「アプリケーション」「活用プロセス」 と、各階の様子を見てみましょう。 一階:基礎となる画像生成モデル 一階は「基礎となる画像生成モデル」です。画像生成には、Imagen、DALL-E2、Midjourney、Stable Diffusionなど著名なモデルがあります。 一部企業を除き、「一からの学習」に必要な計算資源へ安定してアクセスすることは困難です。基礎となる画像生成モデルの一つであるStable Diffusionの公開は、一階部分をGoogle、OpenAIなどの従来からマシンパワーとキャッシュに余裕のある企業に独占された状態とさせず、二階より上で起こるイノベーションに張ってみたものと言えます。様々な議論を呼びつつも、爆発的に「追加学習」「アプリケーション」「活用プロセス」例が出て、イノベーションが加速されたことは確かです。 二階:追加学習等によるカスタマイズ 二階は「追加学習等によるカスタマイズ」です。カスタマイズのためには、まず基礎となるモデルを手元に持つ必要があります。加えて、ここには研究知見やそれを実装する能力、学習に使うデータセットと計算資源が必要となります。具体例としては、拡散生成モデル部分の再学習に加えて、Textual Inversion、DreamBooth、Hypernetworks、LoRAなど、学習の箇所や方法に一定の制約を加える方法があります。「ガチャに頼らず、想定した表現を一発で生成するにはどうしたら良いか」は、この秋冬の大きな研究テーマとなっています。 また、 追加学習されたモデルは、「公開されないケース」「公開されるケース」 があります。さらに「公開されないケース」には、料金を取り利用できるケースと、活用プロセスに隠蔽され使われる場合があります。 公開されないケース: AIピカソが、先日12月20日に発表した「AIいらすとや」 14 は、この二階部分の技術と、フリーのイラストサイト「いらすとや」との提携によりデータセットを使い実現した、「無限にいらすとや風クリップアートを生成できる」機能です。 公開されるケース: Hugging Faceの Stable Diffusion Dreambooth Concepts Library には、DreamBoothにより追加学習した様々なモデルが170以上公開されています。 三階:アプリケーション 三階は「アプリケーション」です。一階や二階部分を活用しながら、コーディングを前提として、アプリケーション部分の実装を差異化領域とできます。先ほどアプリケーションには 「既存アプリやWebサービスに機能を足すケース」 と 「新規アプリを開発するケース」 があると述べました。 「既存アプリやWebサービスに機能を足すケース」 の既存アプリケーションには、Photoshop、Figmaなどが挙げられます。これらは、既にクリエイターの制作プロセスに深く浸透しています。以下のようなサードパーティプラグインが発表されていますが、今後半年から一年で公式機能としての実装が進むことでしょう。 Adobe Photoshopは高機能な画像編集ソフトウェアです。 Nicolay Mausz氏 による flying dog for Stable Diffusion は、Adobe Exchange上で89米ドルにて販売され、Stability.ai側のサーバへ描画リクエストを送ることによるクラウドでの動作、両方をサポートしています。 Figmaは共同編集をしながらインタフェースデザインを行えるツールです。 Antonio Cao氏 により、 Ando - Your design copilot という名前で、Stable Diffusionのプラグインが公開されました。生成枚数が42枚/月に制限されたFree版、月額18米ドルのPro版の料金プランがあります。 営利企業による製品だけではありません。 GIMPはオープンソースの画像編集ソフトウェアですが、 BlueTurtleAI氏 により、GIMPプラグインがMITライセンスで公開されています。 「新規アプリを開発するケース」 はどうでしょうか。 従来Webアプリケーション開発を行なっていた企業やエンジニアが、画像生成AIを素材として選び、他に先駆けサービスとしてリリースする例があります。 既存アプリケーションが同様の機能を実装してきても差異化要素を持ち続けられるかは、「一発芸」で終わらず、継続した利用シーンの探索と新機能のリリースが大切となるでしょう。 日本ではお絵描きばりぐっどくんのようなLINEボット、AIピカソのようなスマートフォンアプリがリリースされています。 なお、一階や二階部分のリリースと共に簡易アプリケーションとして公開される場合に、できるだけ三階部分の実装を軽くするため、DiscordやHugging Face上のGradioアプリケーションなどをフロントエンドとして活用するケースも見られます。 四階:活用プロセス 四階は「活用プロセス」です。コーディングを前提とせず、クリエイターやアーティストにより、生成におけるコツや他制作ツールとの組み合わせ発見など 「制作プロセスの効率化や独自色の付加」 や、 「生成コンテンツのキュレーション」 などが行われます。 ジェネラティブAIを人間が活用する際、「サンドイッチワークフロー」が普及する 15 と言われます。 サンドイッチワークフロー: 人間がAIに指示を与える(例:画像生成AIへのプロンプト提示) AIが生成オプションを提示する(例:複数種類の画像生成) それを人間が選択し、仕上げる(例:画像に対するレタッチ、コラージュ、別ツールへの取込み・編集などの仕上げ) 発明された活用プロセスは、クリエイターやその集団の「秘伝のタレ」として非公開とするケースもあれば、一般に公開するケースもあります。公開され、かつ利用頻度が高いものは、三階のアプリケーションレベルで実装され、コモディティ化します。 2.3. プロダクトを生み出す素材や道具 プロダクトを生み出す、またそれを改善し続けるためには、人材に加えて 「研究知見」「計算資源」「データセット」 などの素材や道具が必要となります。 研究知見 論文の形で様々な研究知見の公開が続きます。機械学習系の国際カンファレンスが開催されるごとに、ジェネラティブAI系の知見が更新されています。この秋は画像生成AIのカスタマイズ、及び画像の次としての映像、音楽、3Dモデルなどの生成についての論文が多数公開され、耳目を集めました。 この分野の論文は、Arxivにプレプリントとして公開され、すぐに個別のDiscordでそれらが論じられたり、Twitterでの拡散が進みます。同時に、またはその後、GitHubやHuggingFaceへの著者による公式実装やコミュニティによる再実装公開や、同様に著者またはコミュニティによるGradio等のプロトタイプアプリ、デモが公開され、それを試した人々によりアプリケーションなどへの取り込みが進むというのがよく見られる流れです。ただし、論文化される前に特許が出願されているケースがあり、ソースコードが公開されている場合もライセンスに気をつけて活用する必要があります。 計算資源 2018年のOpenAIの記事に、「How AI Training Scales (AIの学習はどのようにスケールするか) 16 」があります。その後数年で、大量のデータで学習させ、様々なタスクにカスタマイズを行える基盤モデルの活用が増え、そのサイズは増加の一途を辿っています 17 。大量のデータセット収集、及びその権利関係の整理とともに、モデルの学習に必要となる膨大なマシンパワーを持つことが、差異化に必要となりました。 学習用の計算資源と一口に言っても、前述の通り 「一からの学習」「追加学習(重め)」「追加学習(軽め)」 と程度が異なります。(ここは、2.2. プロダクトの「一階」「二階」に対応) 一からの学習例: 画像生成などのジェネラティブAIを一から学習するには、膨大な計算資源が必要です。Stable Diffusionの当初リリースモデルの学習において、60万米ドル (約8200万円) を投じました 18 。NVIDIA A100を256枚、15万GPU時間を投じたとされています。 追加学習(重め)例: 継続する企業運営のための資金調達ではなく、単発の学習に必要な資金をクラウドファンディングで集める例もあります。Stable DiffusionのフォークであるUnstable Diffusionは、Kickstarterプロジェクトを立ち上げ、25千米ドル (約350万円) のゴールを設定しました(ただし、その後Kickstarterによりプロジェクトは停止) 19 。クローズ後6週間をかけてデータセット準備、学習を進め、モデルを配布する予定とされていました。これは、公開されたStable Diffusionモデルをベースとした追加学習であり、1億円弱よりは大幅にコストが抑えられます。 追加学習(軽め)例: 特定のスタイル、キャラクター、人物に特化した制御可能な画像生成AIを学習により作る場合、より小さなスケールで学習を行い、カスタムの学習モデルを提供できます。手法が確立されていれば、学習に1時間とかからず、計算資源のコストは一件数千円以下の世界です。 データセット Stable Diffusionの学習において、 LAION というオープンなデータセットが重要な役割を果たしました。画像生成AIにおいては、以下のデータセットが活用されています。 インターネット等に公開されたデータを、スクレイピング等で収集したもの 企業所有など非公開でストックされているもの 前者の公開されたデータであっても、公開経緯が著作権などに配慮をしないものであると、それらを使って良いのかという法的、倫理的問題をはらみます。 言語や画像のデータはインターネットに莫大な量があり、日々生み出されていますが、学習で使い果たすことが来るかもしれません。 現状、言語は2040年に枯渇、高品質に限定すると2024年に枯渇、画像は2038年に枯渇するのではと、過去トレンドと計算機の制約をもとに推定した例 20 があります。 ジェネラティブAIにより生成されたコンテンツが指数関数的に増え始めると、やがて学習対象に無視できない量での混入も始まることでしょう。 3. 考慮すべき制約 ジェネラティブAI、特に画像生成AIにおける付加価値や差異化要素は、 「プロダクトを生み出す素材や道具」 に 「人材やステークホルダ」 が作用し、 「法規制と倫理観」 の制約下で 「プロダクト」 が生み出されているとモデル化して話を進めてきました。制約として意識すべき 「各国の法解釈と規制」 や 「対象コミュニティの倫理観」 について見ていきましょう。 3.1. 各国の法解釈と規制 STORIA 柿沼太一弁護士が、AI界隈の事情について以前より情報発信 21 をされています。2022年11月には画像生成AIの日本国内法についてのセミナーが開催され、録画と資料が無料公開 22 されました。 また、画像生成AIの著作権については各国各様の対応が行われています。Skyland Ventures 中村公哉弁護士 23 によると、英国はAIの生成物に著作権を認める方向であるのに対し、米国や日本は積極的な人間の関与があるかどうかにより認められるかどうかが変わる 24 と解釈されているようです。中国は、国内で流通させるコンテンツを生成するAIに対して新たな規制が発表されました。ジェネラティブAIの提供者は、ユーザの身元を確認し、コンテンツ管理をすること、そして人間の顔やリアルなシーンの生成時はAIによって生成されたことを明示すること、としています 25 。 3.2. 対象コミュニティの倫理観 ジェネラティブAIは、まず画像生成を中心にクリエイターの間で大きな議論を巻き起こしています。主なものは、 「AI制作物」「学習データセット」 の扱いです。前者は、制作コストの低い画像生成AI作品が増え、人手による作品の存在感・重要度低下と、中長期での産業自体の衰退に繋がるのではという視点、後者は学習に使われたデータセットの作者の権利はどこまで守られるべきかという点です。各プレーヤは、自身の関わりの深いステークホルダからの反響を注意深く観察しながら、それぞれの立ち位置を模索しているようです。法的に正しいかだけでなく、心情として受け入れられるかも大きな課題です。 画像素材などのコンテンツを扱うサービスやユーザ投稿型のイラストや写真ギャラリーでは、画像生成AIを使った制作物への対応方針が分かれています。画像素材提供サービス Adobe Stockは、AI作品投稿を条件付きで認めるとしました。ユーザ投稿型のイラストサイトであるpixivも同様の方針です。画像素材提供サービス Shutterstockは、OpenAIのDALL-E2を自社サイトに統合、学習に使われた画像の作者への分配の仕組みを作る 26 としました。一方で、画像素材提供サービス Getty Imagesは法的リスクを理由に投稿を受け付けないとしています。クリエイターが所属するプラットフォーム上で画像生成AIを展開することには、法規制だけでなく、様々な倫理観をファシリテートする難しさがあります。 今後、この倫理観と技術発展を見ながら、法規制についても更新されていくものと思われます。 まとめ 画像を中心としたジェネラティブAIを取り巻くビジネス、技術応用、法律その他を概観することを試みました。 画像生成AIの盛り上がり ジェネラティブAIのビジネス機会と投資 差異化要素を生み出す人材、プロダクトやその素材 考慮すべき制約(法解釈と規制、コミュニティの倫理観) さらに、画像生成AIやそれを使ったプロダクトがどう生み出されるかを、 「プロダクトを生み出す素材や道具」 に 「人材やステークホルダ」 が作用し、 「法規制と倫理観」 の制約下で、 「プロダクト」 とモデル化し、関わる要素をさらに概観しました。 人材やステークホルダ クリエイター アプリケーション開発者 研究者 プロダクト 基礎となる画像生成モデル 追加学習等によるカスタマイズ アプリケーション 活用プロセス プロダクトを生み出す素材や道具 公開された知見 計算資源 データセット 考慮すべき制約 各国の法解釈と規制 対象コミュニティの倫理観 これからの数ヶ月・数年スパンで、画像生成AIやジェネラティブAI全般が上に挙げた要素の動的な相互依存関係の中でどう社会に浸透していくか、目撃できることが楽しみですね。また、本記事がそれらの流れを読み解く、または予測するための一助となれば幸いです。 Stable Diffusion を基礎から理解したい人向け論文攻略ガイド (ステート・オブ・AIガイド) ↩ 世界に衝撃を与えた画像生成AI「Stable Diffusion」を徹底解説! - Qiita ↩ "The Illustrated Stable Diffusion" by Jay Alammar ↩ What’s New in Artificial Intelligence from the 2022 Gartner Hype Cycle™ | Gartner ↩ Investors seek to profit from groundbreaking ‘generative AI’ start-ups | Financial Times ↩ Stability AI, the startup behind Stable Diffusion, raises $101M | TechCrunch ↩ Runway Raises $50 Million At $500 Million Valuation As Generative AI Craze Continues | Forbes ↩ https://twitter.com/sharifshameem/status/1562455690714775552 ↩ https://twitter.com/danielgross/status/1575149080124313600 ↩ 世界変革の前夜は思ったより静か|深津 貴之 (fladdict)|note ↩ 史上初、SFマガジンの表紙がAIイラストに 特集は「AIとの距離感」:NEWS Weekly Top10 - ITmedia NEWS ↩ Art Isn't Dead, It's Just Machine-Generated | Andreessen Horowitz ↩ The Generative AI Revolution in Games | Andreessen Horowitz ↩ あの「いらすとや」が画像生成AIに! モバイルアプリ「AIピカソ」が画風を習得 - 窓の杜 ↩ Generative AI: autocomplete for everything | Noahpinion ↩ How AI Training Scales | OpenAI ↩ https://twitter.com/emollick/status/1584743837637160960 ↩ https://twitter.com/EMostaque/status/1563870674111832066 ↩ Unstable Diffusion: Unrestricted AI Art Powered by the Crowd (Suspended) by Unstable Diffusion — Kickstarter ↩ https://twitter.com/bioshok3/status/1598144094068359168 ↩ AI開発を円滑に進めるための契約・法務・知財 | slideshare ↩ https://twitter.com/tka0120/status/1598238927403372544 ↩ https://twitter.com/kimiya_nakamura ↩ 画像生成AIをめぐる倫理的問題の最新事情。各国で分かれる対応とは | モリカトロンAIラボ ↩ 中国が画像生成AIの画像に「AI生成マークの表示」を義務化&AIユーザーも実名登録制へ - GIGAZINE ↩ Shutterstock will start selling AI-generated stock imagery with help from OpenAI - The Verge ↩
アバター
この記事は、 NTT Communications Advent Calendar 2022 24日目の記事です。 はじめに イノベーションセンターの木村と申します。初めてのアドベントカレンダー&Engineers’blog投稿です。普段の業務は、機械学習をもちいた時系列データ分析の研究開発やお客様データ分析案件支援を主として行っています。プライベートでは自転車にお熱でZwiftでバーチャルライドをしたり、最近ではテクニック向上のためバニーホップの練習に励んでいます(なかなか上達しません…)。 今日はクリスマスイブということで、 時系列データ分析コンテンツ「ごちきか」 をプレゼント(?)します!年末休みのお供にぜひご照覧ください。 サマリー 時系列データ分析コンテンツ「ごちきか」を公開しました (余談として)基盤やデプロイ方法を紹介します What is 「ごちきか」? 私たちのチームでは、社会・産業DXのためのSmart World の一貫として、 時系列データ分析手法の研究開発、お客様のデータ分析支援や社内データ分析人材育成 を行っています。 【最近の研究開発成果】 NTT Comの開発した要因分析手法がAISTATS2022にて採択 www.ntt.com 2022年度人工知能学会全国大会 (JSAI2022) で発表してきた話 〜因果探索編〜 engineers.ntt.com また、これらの研究開発成果は、同じくイノベーションセンターで開発している ノーコードAI開発ツールNode-AI に搭載されていきますので、こちらもぜひご覧ください。詳しくは、以下の公式サイトとEngineers’Blogをご参照ください。 sdpf.ntt.com engineers.ntt.com この ごちきか(gochikika)プロジェクト は、これら研究開発成果や社内向けデータ分析人材育成コンテンツをまとめたナレッジベースです。主に製造業の時系列データを対象として、前処理からモデリングまで基本的な分析手法をPythonのソースコード付きで解説しています。 Why ごちきか 世の中にはありがたいことにたくさんのデータ分析に関するドキュメントがあります(いつも参考にさせていただいています)。しかし、私たちの興味のある時系列データ分析(特にプラント向けのようなドメイン知識に紐づくもの)については多くはなく、例えば新入社員の教育の際に困ることがありました。 さらに、学会発表資料やコードが個人管理のPCやGitHubに分散しており、チームの研究開発成果や検証結果が属人化していまっていました。社内向けライブラリを作ることも試しましたが、ユーザー不在で維持管理コストが高く普及しませんでした。そこで、ライブラリ化の前段として、ドキュメント化第一で考え、一元管理することとしました。研究者のアウトプットとしてもドキュメント化は重要であると考えました。 本記事の投稿は、私たちの取り組み内容や研究開発、興味がある分野について知ってもらうことが目的の1つです。 How to ごちきか それぞれの記事は、なるべくそのページ内でコードが完結するようにしています。したがって、一部数式が多く難しく見えるかもしれませんが、基本的なPythonの使い方がわかればコピー&ペーストで試すことができます。 いくつかコンテンツが含まれていますが、現在は2つの項目に大別されています。 分析 主に製造業の時系列データを対象として、基本的な分析手法をPythonコード付きで解説しています。 内部で開発した酢酸ビニルプラントシミュレーターから生成したデータを使用 可視化、前処理、モデリング、学習の実行まで一連の分析が可能。例えば以下のようなコンテンツがあります。 可視化: matplotlibを使った 時系列データの可視化 , スペクトル解析 前処理: 次元削減 , 次元圧縮 モデリング: PLSモデル , ガウス過程回帰モデル 学習: クロスバリデーション , ハイパーパラメータ最適化(ベイズ最適化) 特集記事 比較的新しめであったり難易度の高い手法や、私たちの取り組みを知ってもらうための学会発表資料が掲載されます。また一部未分類なコンテンツが格納されています。 例えば、変数間の依存関係をはかる Hilbert-Schmidt独立性基準 や、Trace LASSO/Graphical LASSOといった 発展的なスパースモデリング手法  があります(個人的な推しコンテンツです)。 基盤について 私たちの主な業務は研究開発やデータ分析なので、その片手間でサーバーやセキュリティ対策を完全には運用しきることは難しいです。そこで、以下を目的としてごちきか基盤を作成しました。 【モチベーション】 ドキュメント執筆に注力したい 実験ノート(=Jupyter Notebook)をそのまま使いまわしたい ⇒ MyST-NB インフラにとても詳しいわけではない。でもやるからには便利なシステムにしたい サーバーレス(セキュリティ対策、運用コストの低減) 記事投稿から自動で配信 ⇒ GitHub Actions, AWS MyST-NB をベースとした静的サイトをGitHub Actionsでビルドし、AWSで配信することとしました。概略図は以下の通りです。 MyST (MyST-NB) MyST は、Jupyterのエコシステム上で計算科学のドキュメント化を行う Executable Book Project の中核をなすOSSで、Markdownを拡張し柔軟な記述を可能にしたものです。Pythonにはドキュメンテーションビルダー Sphinx がありますが、 recommonmark というファイルフォーマットを使用しており、Markdownになれたユーザーからすると少々記述に癖がありました。なおrecommonmarkの開発は終了しており、MySTへの移行がすすめられています(MySTはrecommonmarkの文法も使用可能)。 このSphinx+MySTベースで、Jupyter Notebookをhtmlやpdfのドキュメントに変換するものが MyST-NB です。なお以後の説明ではMySTとMyST-NBをまとめて、MySTと記載します。 前述の通り、MySTは標準のMarkdownをJupyter Notebookで扱うだけではなく、リッチな機能を有しています。数式のレンダリングはもちろんのこと、注意書きを示すAdmonition、長すぎるコードをトグル形式で隠す機能、Plotlyやipywidgetsを使ったインタラクティブな描画が可能です。さらに、コンパイル時にJupyter Notebookを実行するテスト機能もあります(機械学習では計算時間がかかるためあまり向きませんが)。 実際にごちきかのコンテンツ、 データセット集, Electricity Transformer Temperature では、admonition、セルの非表示、Plotlyを使ったプロットをサンプルとして示しています。 GitHub Actions ソースコードの管理は会社契約のGitHub Enterpriseで行っています。契約して整備してくださった方々に感謝。 記事のクオリティを担保し、執筆者以外にも最低一人は知識を展開する(バス係数を上げる)ため、 GitHub Flow に従った編集フローを採用しています。 執筆記事に対するレビューが完了すると、公開用のブランチにマージされ、これをトリガーとしてGitHub Actionsが起動します。ActionsはMySTをもちいてJupyter Notebookをhtmlにビルドし、AWS S3にアップロードします。これにより、手動ビルドの必要なく自動で更新作業が済みます。また、逐次ビルドするため通常のGitHub Pagesのようにビルド後のソースを管理する必要もありません(最近だと ベータ版 としてPagesでもActionsが使えるようになったようです)。 AWS AWSでは、ビルドされたコンテンツをS3に格納し、Route53とCloudFrontで配信します。サーバーレスな構成になっているので、運用コストを下げることができました(社内向けだったこともあり、月額利用料金換算でも1ドル以下)。 基本的な設定は以下の公式ドキュメントに従っています。 docs.aws.amazon.com まとめ 時系列データ分析コンテンツ「ごちきか」を公開しました。ごちきかは、時系列データ分析手法や研究開発成果をPythonコード+その解説記事としてまとめ、広く公開するものです。みなさまのデータ分析の一助となれたら幸いです。 私たちの取り組みに興味がございましたら、時系列データ解析/予測/異常検知/最適化を対象としたPoC、各種究機関との共同研究案件、Node-AIのご契約を募集中ですので、ご興味があればこちらまでご連絡ください!(メール:ai-deep-ic[at]ntt.com) また、 中途 /新卒採用も実施しておりますので是非エントリーください。 今後は、まだまだ載せきれていない学会発表資料や、デザイン的につたないところがあるので改善していきたいです。面白い研究成果が出ましたらEngineers’Blogに寄稿&宣伝させてください🙇 明日はいよいよ最終日!トリの記事もお楽しみに!!
アバター
この記事は、 NTT Communications Advent Calendar 2022 23 日目の記事です。 はじめに こんにちは、デジタル改革推進部の組橋です。普段は社内データの整備や分析をしています。 この記事では、社内ツールなどの自作ツールを管理する方法や関連するサービスを紹介します。 パッケージリポジトリとは パッケージ ソフトウェアにおけるパッケージとはいくつかの機能を1つにまとめて、管理しやすくしたものを指します。 例えば、Pythonのパッケージは pip コマンドでインストールできます。 pip install $PACKAGE_NAME リポジトリ リポジトリは、保管場所という意味があります。 パッケージリポジトリはパッケージの保管場所です。単にリポジトリというと最近はGitリポジトリを指すことが多いですが、これはソースコードの保管場所ですね。 例えば、Pythonの公式パッケージリポジトリは Python Package Index (PyPI) であり、 pip でインストールする際のデフォルトの参照先に指定されています。 自分たち専用のパッケージリポジトリ Pythonに限らず他の言語にも公式のパッケージリポジトリがあり、公開されているパッケージを利用して開発することは一般的です。 公開されているパッケージに自分たちのほしいものがないとき、自作することはよくあることかと思います。 しかし、プロジェクトごとにツールを作って utils や tools みたいなディレクトリに置いていくと、似たようなツールが散乱してしまいます。 そこで、自分たち専用のパッケージリポジトリを用意すると、ツールの配布や管理が便利になります。 リポジトリがあることで再利用が促進され、開発効率が上がるかもしれません。 Pythonのプライベートパッケージリポジトリ Pythonで独自のパッケージリポジトリを用意するには、 pypiserver を用いる方法が簡単です。 pypiserver自身もPython製なので pip でインストールできます。 pip install pypiserver 本番運用はリポジトリ用のVMなどで実行するかと思いますが、ここではローカルで実行する方法を紹介します。 はじめに、 htpasswd コマンドで htpasswd.txt ファイルを作成します。デフォルトではMD5で生成されます。 $ htpasswd -c htpasswd.txt $USERNAME New password: Re-type new password: Adding password for user USERNAME pypi-server run コマンドを実行することでサーバが起動します。引数に指定している ./packages はパッケージを格納するディレクトリです。 pypi-server run -p 8080 -P htpasswd.txt ./packages パッケージをアップロードする方法は公式のPyPIと同様です。 例えば、 setuptools を用いてパッケージをアップロードするには、 ~/.pypirc にリポジトリのサーバ情報を書きます。 [distutils] index-servers = local [local] repository: http://localhost:8080 username: $USERNAME password: $PASSWORD --repository または -r オプションを使用すると、 ~/.pypirc に記載した設定を使用してアップロードできます。 python setup.py sdist upload -r local 標準ライブラリである setuptools を用いた方法を紹介しましたが、Pythonでパッケージを作成する場合、 Poetry もおすすめです。 今回のテーマから逸れるので説明は割愛しますが、興味がある方はぜひ触ってみてください! インストールするには -index-url または --extra-index-url のオプションでリポジトリのURLを指定します。 オプションではなく、環境変数に設定する方法や pip.conf に記載する方法もありますが、ここでは割愛します。 pip install --extra-index-url http://localhost:8080 $PACKAGE_NAME 専用リポジトリを導入するメリットの1つとして、上記のように公式のパッケージと同じ形式(例えば pip install )でインストールできることがあります。 プライベートパッケージリポジトリサービス 前節では独自にサーバを立ててリポジトリを運用する紹介しましたが、 Gemfury や AWS CodeArtifact などパッケージリポジトリのサービスが存在します。 Gemfury Gemfury は古くからあるサービスで、パッケージリポジトリに特化しています。また、対応している言語も豊富です。 主にユーザ数で課金される Personal プランと、アップロードできるプライベートパッケージ数で課金される Team プランの2種類があります 1 。 Gemfuryのリポジトリにパッケージをアップロードする方法はいくつかあります。 以下の方法を用いることで言語を問わず、同じようにアップロードできます。 curl コマンド Gemfury CLI GemfuryのWebページ(GUI) ここでは Gemfury CLI を利用する方法を紹介します。 Homebrew を使用してインストールできます。 Homebrew以外のインストール方法については、 こちらのドキュメント を参照してください。 brew tap gemfury/tap brew install fury-cli login コマンドでログインします。 $ fury login Please enter your Gemfury credentials. Email: : youremail@domain.com Password: : ******** You are logged in as " USERNAME " push コマンドでパッケージをアップロードできます。 fury push $PACKAGE_FILE Pythonパッケージの場合は、前述した ~/.pypirc を利用することもできます。 [fury] repository: https://pypi.fury.io/$USERNAME/ username: $TOKEN password: アップロードしたPythonパッケージをインストールするには、自作リポジトリと同様にリポジトリのURLを指定します。 pip install --extra-index-url https://pypi.fury.io/ $ACCOUNT / $PACKAGE_NAME プライベートなパッケージの場合はトークンを使用します。 pip install --extra-index-url https:// $TOKEN :@pypi.fury.io/ $ACCOUNT / $PACKAGE_NAME AWS CodeArtifact CodeArtifact は、AWSのサービス群の1つです。 基本的な使用方法はGemfuryと大きく変わらないため詳細は割愛しますが、furyのように専用のCLIやGUIからのアップロードはできません。 指定されたリポジトリのURLとトークンを用いてアップロードします。 トークンは、AWS CLIで発行できます 2 。 また、トークンの有効期限は最大12時間といった制限があります。 対応言語の比較 紹介したサービス以外にも類似サービスはいくつかあります。 ここでは、各サービスで対応しているプログラム言語の対応表を紹介したいと思います。 なお、対応表にはプログラム言語に限定して記載しています 3 。 Gemfury AWS CodeArtifact GCP Artifact Registry Azure Artifacts GitHub Packages PyPI (Python) ○ ○ ○ ○ - RubyGems (Ruby) ○ - - - ○ Composer (PHP) ○ - - - - Go Modules (Go) ○ - - - - Maven (Java) ○ ○ ○ ○ ○ npm (JavaScript) ○ ○ ○ ○ ○ NuGet (.NET) ○ ○ - ○ ○ 使用感が大きく異なることはないと思われるので、対応している言語や普段使用している環境などに応じて選択すると良いでしょう。 おわりに 社内ツールを配布や管理を便利にするプライベートパッケージリポジトリの導入や利用について紹介しました。 チーム内や部門内などで共通のパッケージリポジトリを用意しておくと便利なので皆さんもお試しください。 https://fury.co/pricing ↩ https://docs.aws.amazon.com/ja_jp/codeartifact/latest/ug/tokens-authentication.html ↩ サービスによってはaptなどLinuxパッケージを管理できるものもあります。 ↩
アバター
この記事は、 NTT Communications Advent Calendar 2022 22日目の記事です。 はじめに こんにちは、イノベーションセンターの鈴ヶ嶺( @suzu_3_14159265 )です。普段は、クラウド・ハイブリッドクラウド・エッジデバイスなどを利用したAI/MLシステムに関する業務に従事しています。 本日は、Rustでベクトル化された乱数生成器を実装する方法を紹介します。乱数生成器には Permuted congruential generator(PCG) という高速でシンプルな実装を取り扱います。ベクトル化には1つの命令で複数のデータを適用するSingle Instruction, Multiple Data(SIMD)を活用します。 また、以下のように毎年Rustネタのアドベントカレンダーを書いているのでぜひ見ていただけると嬉しいです! NTTコミュニケーションズ Advent Calendar 2021 Rustで実装するmalloc NTTコミュニケーションズ Advent Calendar 2020 Rustで実装するNetflow Collector PCGとは Permuted congruential generator(PCG) 1 は、シンプルな実装でメモリ消費も低く高速な乱数生成アルゴリズムです。次式のような線形合同法の出力に対してXorshift 2 のような排他的論理和とビットシフト操作 ( x ^= x >> N) を加えることで、線形合同法で見られる偶数と奇数が交互にでるような下位ビットの低いランダム性を改善しています。 また、PCGは TestU01 3 と呼ばれる乱数生成器をテストするツールのBig Crushと呼ばれる最も大きいテストを突破しています。 PCGの活用事例をみると、例えば Numpy の乱数生成器はデフォルトでPCGが採用されています。 The default BitGenerator used by Generator is PCG64. https://numpy.org/doc/stable/reference/random/generator.html ちなみに、他の乱数生成器との比較は次のようにPCGの公式ページに表としてまとめられており分かりやすいので気になる方は参照してください。 https://www.pcg-random.org/ Rustでは、乱数生成ライブラリの rand_pcg クレートから利用が可能です。次に利用方法のサンプルコードを示します。 use rand :: prelude :: * ; use rand_pcg :: Pcg32; fn main () { let mut rng = Pcg32 :: from_entropy (); let ru32: u32 = rng. gen (); println! ( "{}" , ru32); } PCGの実装方法は、Rustのrand 0.8.5のソースコードの一部を抜粋してコメントアウトを用いて次にインラインで説明します。 https://github.com/rust-random/rand/blob/0.8.5/rand_pcg/src/pcg64.rs const MULTIPLIER: u64 = 6364136223846793005 ; // 乗数は固定値 pub struct Lcg64Xsh32 { state: u64 , // 出力の2倍の状態をもつ increment: u64 , // 任意の奇数 } pub type Pcg32 = Lcg64Xsh32; // LCG(線形合同法)64bitにXSH(xorshift操作)をするという意味 impl Lcg64Xsh32 { // 線形合同法の1ステップ fn step ( &mut self ) { self .state = self .state . wrapping_mul (MULTIPLIER) // 積 . wrapping_add ( self .increment); // 和 } } impl RngCore for Lcg64Xsh32 { fn next_u32 ( &mut self ) -> u32 { let state = self .state; self . step (); // 線形合同法で現在の状態から次の状態を計算 // xorshift操作のbit数パラメータ const ROTATE: u32 = 59 ; // 64 - 5 const XSHIFT: u32 = 18 ; // (5 + 32) / 2 const SPARE: u32 = 27 ; // 64 - 32 - 5 let rot = (state >> ROTATE) as u32 ; let xsh = (((state >> XSHIFT) ^ state) >> SPARE) as u32 ; // xorshift操作 xsh. rotate_right (rot) // ビット回転により出力は状態のビット数の半分(32bit) } } ベクトル化とは ベクトル化はfor文などで繰り返し1つずつ計算している処理を高速に処理する手法です。今回はその中のSingle Instruction, Multiple Data(SIMD)という技術を活用します。SIMDは1つの命令で複数のデータを処理する方法です。例えば256bitのSIMD命令がある場合は64bit x 4個=256bitのように64bitのデータを同時に4個処理可能です。 Rustでは次のように core::arch::x86_64 ( https://doc.rust-lang.org/core/arch/x86_64/index.html ) 以下の _mm128_** , _mm256_** などのunsafeな関数を利用することでSIMDを利用することが可能になります。 例えば64bitごとに計4個の値を同時に加算するコード例を以下に示します。 今回はIntel AVX2環境で実装します。 #[cfg(any(target_arch = "x86_64" ))] use core :: arch :: x86_64 :: * ; fn main () { unsafe { sample (); } } #[cfg(any(target_arch = "x86" , target_arch = "x86_64" ))] #[target_feature(enable = "avx2" )] unsafe fn sample () { let a = _mm256_set_epi64x ( 1 , 2 , 3 , 4 ); let b = _mm256_set_epi64x ( 5 , 6 , 7 , 8 ); let c = _mm256_add_epi64 (a, b); println! ( " a : {:?}" , a); println! ( " b : {:?}" , b); println! ( "a+b: {:?}" , c); } 以下は、出力結果です。 a : __m256i(4, 3, 2, 1) b : __m256i(8, 7, 6, 5) a+b: __m256i(12, 10, 8, 6) このように2つの配列 [1, 2, 3, 4] , [5, 6, 7, 8] 各要素の加算された結果 [6, 8, 10, 12] が返ってきたことが分かると思います。このように1つの命令で複数のデータを処理します。これをPCGの実装の中の線形合同法のステップ、出力のxorshift操作とビット回転に適用していきます。 その他にも応用的なSIMDの活用方法を知りたい場合は simdjson などの高速JSON Parserの作者で著名なDaniel Lemire先生の githubのrepo を覗いてみるのをお勧めします。次に説明する実装も、C言語で実装された simdpcg や SIMDxorshift を参考にして作成しました。 実装 ここでは、SIMDを用いてベクトル化したPCG実装を示します。基本方針としてユーザが直接利用する関数に関してはsafeな関数として実装していきます。 64bitごとの積を計算する _mm256_mullo_epi64 はavx2にはないため別途実装していることなどに注意してください。 また、 portable_simd を利用するためnightlyを利用します。 #![feature(portable_simd)] #[cfg(target_arch = "x86_64" )] use std :: arch :: x86_64 :: * ; use std :: simd :: {u32x4}; #[cfg(any(target_arch = "x86" , target_arch = "x86_64" ))] pub struct Avx2Pcg { state: __m256i, inc: __m256i, mul_l: __m256i, mul_h: __m256i, } const MULTIPLIER: i64 = 6364136223846793005 ; // 乗数は固定 #[cfg(any(target_arch = "x86" , target_arch = "x86_64" ))] #[target_feature(enable = "avx2" )] #[inline] // 64bitごとの積 unsafe fn _mm256_mullo_epi64 (x: __m256i, ml: __m256i, mh: __m256i) -> __m256i { let xl = _mm256_and_si256 (x, _mm256_set1_epi64x ( 0x00000000ffffffff )); let xh = _mm256_srli_epi64 (x, 32 ); let hl = _mm256_slli_epi64 ( _mm256_mul_epu32 (xh, ml), 32 ); let lh = _mm256_slli_epi64 ( _mm256_mul_epu32 (xl, mh), 32 ); let ll = _mm256_mul_epu32 (xl, ml); let ret = _mm256_add_epi64 (ll, _mm256_add_epi64 (hl, lh)); return ret; } #[cfg(any(target_arch = "x86" , target_arch = "x86_64" ))] #[target_feature(enable = "avx2" )] #[inline] unsafe fn _mm256_rorv_epi32 (x: __m256i, r: __m256i) -> __m256i { let ret = _mm256_or_si256 ( _mm256_sllv_epi32 (x, _mm256_sub_epi32 ( _mm256_set1_epi32 ( 32 ), r)), _mm256_srlv_epi32 (x, r), ); return ret; } impl Avx2Pcg { #[inline] fn next ( &mut self ) -> __m128i { unsafe { self . next_ () } } #[cfg(any(target_arch = "x86" , target_arch = "x86_64" ))] #[target_feature(enable = "avx2" )] #[inline] unsafe fn next_ ( &mut self ) -> __m128i { let old_state = self .state; // 積和計算 self .state = _mm256_add_epi64 ( _mm256_mullo_epi64 ( self .state, self .mul_l, self .mul_h), self .inc, ); // xorshift let xorshifted = _mm256_srli_epi64 ( _mm256_xor_si256 ( _mm256_srli_epi64 (old_state, 18 ), old_state), 27 , ); let rot = _mm256_srli_epi64 (old_state, 59 ); // ビット回転 let ret = _mm256_castsi256_si128 ( _mm256_permutevar8x32_epi32 ( _mm256_rorv_epi32 (xorshifted, rot), _mm256_set_epi32 ( 7 , 7 , 7 , 7 , 6 , 4 , 2 , 0 ), )); return ret; } #[inline] pub fn next_u32x4 ( &mut self ) -> [ u32 ; 4 ] { let m128: u32x4 = self . next (). into (); return * m128. as_array (); } #[inline] pub fn from_state_inc (state: [ i64 ; 4 ], inc: [ i64 ; 4 ]) -> Avx2Pcg { unsafe { Avx2Pcg { state: _mm256_set_epi64x (state[ 0 ], state[ 1 ], state[ 2 ], state[ 3 ]), inc: _mm256_set_epi64x (inc[ 0 ] | 1 , inc[ 1 ] | 1 , inc[ 2 ] | 1 , inc[ 3 ] | 1 ), // 奇数のため "| 1" を追加 mul_l: _mm256_set1_epi64x (MULTIPLIER & 0x00000000ffffffff ), // 乗数は固定 mul_h: _mm256_set1_epi64x (MULTIPLIER >> 32 ), // 乗数は固定 } } } #[inline] pub fn from_entropy () -> Avx2Pcg { let mut state_buf: [ u8 ; 32 ] = [ 0u8 ; 32 ]; let mut inc_buf: [ u8 ; 32 ] = [ 0u8 ; 32 ]; let _ = getrandom :: getrandom ( &mut state_buf); let _ = getrandom :: getrandom ( &mut inc_buf); let state_buf64: [ i64 ; 4 ] = unsafe { std :: mem :: transmute (state_buf) }; let inc_buf64: [ i64 ; 4 ] = unsafe { std :: mem :: transmute (inc_buf) }; Avx2Pcg :: from_state_inc (state_buf64, inc_buf64) } } 実験 実際に、32bit x 10000000個の合計40Mbの乱数生成時間でRustのPCG実装のベースラインと今回のベクトル化されたPCG実装の比較します。 実験環境はGoogle Cloudのインスタンスサイズn2-standard-2, Ubuntu20.04LTSです。 Rust versionはrustc 1.68.0-nightly (d0dc9efff 2022-12-18)を利用します。 また、ループアンローリングでループ内に複数の命令数(1, 2, 4)を展開したケースもわけて計測します。 ちなみに、計測ツールには Criterion.rs を利用して試行回数100回で計測しました。 use criterion :: {criterion_group, criterion_main, Criterion}; use rand :: prelude :: * ; use rand_pcg :: Pcg32; use simd_pcg :: Avx2Pcg; const N: usize = 10000000 ; pub fn avx_one (c: &mut Criterion) { c. bench_function ( "avx one" , | b | { b. iter ( || { let mut arr = vec! [ 0u32 ; N]; let mut rng1 = Avx2Pcg :: from_entropy (); for i in ( 0 ..N). step_by ( 4 ) { let r1_arr = rng1. next_u32x4 (); arr[i..(i + 4 )]. copy_from_slice ( & r1_arr); } }) }); } pub fn avx_two (c: &mut Criterion) { c. bench_function ( "avx two" , | b | { b. iter ( || { let mut arr = vec! [ 0u32 ; N]; let mut rng1 = Avx2Pcg :: from_entropy (); let mut rng2 = Avx2Pcg :: from_entropy (); for i in ( 0 ..N). step_by ( 8 ) { let r1_arr = rng1. next_u32x4 (); let r2_arr = rng2. next_u32x4 (); arr[i..(i + 4 )]. copy_from_slice ( & r1_arr); arr[(i + 4 )..(i + 8 )]. copy_from_slice ( & r2_arr); } }) }); } pub fn avx_four (c: &mut Criterion) { c. bench_function ( "avx four" , | b | { b. iter ( || { let mut arr = vec! [ 0u32 ; N]; let mut rng1 = Avx2Pcg :: from_entropy (); let mut rng2 = Avx2Pcg :: from_entropy (); let mut rng3 = Avx2Pcg :: from_entropy (); let mut rng4 = Avx2Pcg :: from_entropy (); for i in ( 0 ..N). step_by ( 16 ) { let r1_arr = rng1. next_u32x4 (); let r2_arr = rng2. next_u32x4 (); let r3_arr = rng3. next_u32x4 (); let r4_arr = rng4. next_u32x4 (); arr[i..(i + 4 )]. copy_from_slice ( & r1_arr); arr[(i + 4 )..(i + 8 )]. copy_from_slice ( & r2_arr); arr[(i + 8 )..(i + 12 )]. copy_from_slice ( & r3_arr); arr[(i + 12 )..(i + 16 )]. copy_from_slice ( & r4_arr); } }) }); } pub fn baseline_one (c: &mut Criterion) { c. bench_function ( "baseline one" , | b | { b. iter ( || { let mut arr = vec! [ 0u32 ; N]; let mut rng1 = Pcg32 :: from_entropy (); for i in ( 0 ..N). step_by ( 1 ) { arr[i] = rng1. gen (); } }) }); } pub fn baseline_two (c: &mut Criterion) { c. bench_function ( "baseline two" , | b | { b. iter ( || { let mut arr = vec! [ 0u32 ; N]; let mut rng1 = Pcg32 :: from_entropy (); let mut rng2 = Pcg32 :: from_entropy (); for i in ( 0 ..N). step_by ( 2 ) { arr[i] = rng1. gen (); arr[i + 1 ] = rng2. gen (); } }) }); } pub fn baseline_four (c: &mut Criterion) { c. bench_function ( "baseline four" , | b | { b. iter ( || { let mut arr = vec! [ 0u32 ; N]; let mut rng1 = Pcg32 :: from_entropy (); let mut rng2 = Pcg32 :: from_entropy (); let mut rng3 = Pcg32 :: from_entropy (); let mut rng4 = Pcg32 :: from_entropy (); for i in ( 0 ..N). step_by ( 4 ) { arr[i] = rng1. gen (); arr[i + 1 ] = rng2. gen (); arr[i + 2 ] = rng3. gen (); arr[i + 3 ] = rng4. gen (); } }) }); } ループアンローリング 命令数 baseline avx2(今回の実装) 1 27.601 ms 24.447 ms 2 23.357 ms 19.803 ms 4 22.839 ms 19.526 ms 上の表が試行回数100回の計測時間の平均結果、図が計測時間の分布を示しています。 この結果からベクトル化された今回の実装のavx2は特にループアンローリングの命令数が多い場合baselineよりも有意に高速であることが分かります。 また、こちらの実験コードは以下に置いておきます。 https://github.com/suzusuzu/simd-pcg 次のコマンドで実行可能です。 git clone git@github.com:suzusuzu/simd-pcg.git cd simd-pcg rustup run nightly cargo bench まとめ 本記事では、PCGを対象にSIMDによるベクトル化をRustで実装する方法を紹介しました。また、比較実験の結果からもSIMD化による高速化が有効であることを示しました。 それでは、明日の記事もお楽しみに! 参考 Intel® Intrinsics Guide PCG, A Family of Better Random Number Generators xoshiro / xoroshiro generators and the PRNG shootout simdpcg SIMDxorshift O’Neill, Melissa E. "PCG: A family of simple fast space-efficient statistically good algorithms for random number generation." ACM Transactions on Mathematical Software (2014). ↩ Marsaglia, George. "Xorshift rngs." Journal of Statistical Software 8 (2003): 1-6. ↩ L'ecuyer, Pierre, and Richard Simard. "TestU01: AC library for empirical testing of random number generators." ACM Transactions on Mathematical Software (TOMS) 33.4 (2007): 1-40. ↩
アバター
はじめに こんにちは、クラウド&ネットワークサービス部で SDPF のベアメタルサーバー・ハイパーバイザーの開発をしている山中です。 先日 GitHub Actions self-hosted runners のオートスケーリング構成の紹介(クラウドサービス開発を支える CI の裏側) の記事で、自作の runner controller と Docker を用いた、オンプレミスでの CI 環境構成についてご紹介しました。 今回の記事では、構築した CI 環境上で動かしている workflow の紹介をしながら、workflow 作成についての Tips をいくつかご紹介したいと思います。 engineers.ntt.com 記事を書いたモチベーション 実際の業務で GitHub Actions を使用するにあたって、ありがちな悩みを解決するための workflow の作成事例や工夫などの記事が検索であまり見つからなかったためです。 どのような workflow を作成するかは開発現場によって様々であり、今回紹介する Tips も汎用的ではないものもありますが、他のチームはこんな風にこの機能を使ってるんだ〜みたいに読んでもらえればなと思います。 なお、この記事は GitHub Actions を普段使っている方向けに書いていることや、後ろのトピックほど私たちのチーム独自の内容になっていることをご了承ください トピック run_name、Job Summary で workflow の実行パラメータを見やすく composite action、reusable workflow による処理の再利用の例 複数リポジトリ間での機密情報の扱い方 独自キャッシュによる効率的なファイル共有 run_name、Job Summary で workflow の実行パラメータを見やすく 私たちのチームではリポジトリごとに単体テストの workflow を作成しています。 単体テストは workflow_dispatch で実行することになっており、PR に紐づくブランチを指定して手動で workflow を実行することになっています。 私たちのリポジトリでは、単体テストは環境の初期化やテスト項目数の多さなどにより時間がかかるものが多く、1並列でしかテストを流せないという制約もあります。 Pull Request(以下、PR)の更新などによって自動でテストを流してしまうと、開発者が望むタイミングでテストを流しづらくなってしまうため、workflow_dispatch を使っています。 workflow_dispatch の歯がゆい点 そんな workflow_dispatch ですが、GUI 上での見え方にやや難があり、以下のように workflow 実行時に指定したブランチがひと目で分かりません。 右上の Filter からブランチを絞り込んで表示することも可能ですが、わざわざ絞り込むのが手間です。 on: push などで workflow を実行した場合はブランチを表示してくれるのですが、workflow_dispatch の場合は workflow の name や実行者ぐらいしか表示してくれません。 run-name の活用 こんなときに役立つのが、 run-name です。 workflow ファイル内で run-name を定義すると、実行された run の名前をカスタマイズでき、GUI 上での見え方に変化を与えることができます。 例えば以下のように run-name を定義することで「どの run がどのブランチから実行されたのか」がひと目で分かるようになります。 run-name: ${{ github.workflow }} (${{ github.ref_name }}) また、workflow 実行時に指定した inputs も run-name で参照でき、以下のように run-name を定義することで「どの run がどのパラメータで実行されたのか」もひと目で分かるようにもできます。 run-name: ${{ github.workflow }} / ${{ inputs.environment }} (${{ inputs.scenario }}) Job Summaries の活用 ここで表示した inputs ですが、数が多くなってくると run-name に全て表示しきれなくなってきます。 そんなときは Job Summaries を使うのがおすすめです。 GitHub Actions では workflow 実行時に渡した inputs を見やすい場所に表示してくれないため、 どのような inputs で workflow を実行したのか辿りづらい のですが、 以下のような step を定義しておくと、inputs を job summary として run の画面に表示できるようになります。 steps: - name: post test summary uses: actions/github-script@v6 with: script: | parametersTable = [ [{ data: 'key', header: true}, { data: 'value', header: true }], ['branch', '${{ github.ref_name }}'] ] retryCommand = `gh workflow run --ref ${{ github.ref_name }} '${{ github.workflow }}'` for ([key, value] of Object.entries(context.payload.inputs)) { parametersTable.push([key, value]) retryCommand += ` -f ${key}='${value}'` } await core.summary .addRaw("### Test Parameters\n") .addTable(parametersTable) .addRaw("\n### Retry Command\n") .addCodeBlock(retryCommand) .write() workflow の再実行コマンドの表示 上の図の下部に Retry Command というコードブロックが表示されていますが、これも工夫点の1つです。 workflow の再実行をしたい場合、同じパラメータで再実行するのであれば re-run の機能を使えばよいですが、少しだけパラメータを変更して再実行したい場合、inputs が多いとパラメータの再入力が面倒です。 そこで、同じパラメータで workflow を実行するためのコマンドを run の画面に表示し、 パラメータを少しだけ変更して workflow を再実行するという操作を簡単に行える ようにしています。 composite action、reusable workflow による処理の再利用の例 GitHub Actions における処理の再利用といえば composite action や reusable workflow ですよね。 自分で独自に custom action を作成する場合、 選択肢としては Docker container や JavaScript を用いて作成する方法がありますが、個人的に一番使いやすいと思っているのが composite action です。 Docker container や JavaScript だと workflow ファイルの記法から外れて所定の形式に従って Dockerfile を書いたり JavaScript を書いたりする必要がありますが、composite action の場合は workflow ファイルと ほぼ同じ記法 で一連の処理を記述でき、学習コストやメンテナンスコストを抑えられます。 composite action で定義した step は呼び出し元の job で定義された step と同じように実行されるため、 処理をまとめて複数の workflow で使い回せるようにしたり、workflow ファイルの巨大化を防ぐ のに向いています。 composite actions の活用例 私たちのチームでは先程述べた単体テストを行うリポジトリが数多くあり、それぞれに workflow ファイルを作成しています。 数が多いと同じ処理は共通化したい欲が強くなるため、共通処理リポジトリを作成し、そこに様々な composite action を作成しています。 特に共通化の恩恵を受けているのが、以下のように workflow の最初と最後に定義している pre-processing と post-processing という job です。 jobs: pre-processing: runs-on: xxx steps: - uses: <org>/<repo>/.github/actions/unit-test/pre-processing@master lint: ... unit-test: ... post-processing: if: always() needs: unit-test runs-on: xxx steps: - uses: <org>/<repo>/.github/actions/unit-test/pre-processing@master pre-processing と post-processing では以下のような処理を行っています。 post-processing job は if: always() により workflow が途中で失敗しても必ず実行されるようにしています。 pre-processing 先程でも述べた workflow 実行時に渡した inputs の表示 workflow 実行時に指定したブランチに紐づく PR のステータス更新( checks を pending に変更) workflow_dispatch だと PR の checks が自動更新されないため、手動で更新する必要がある post-processing workflow 実行時に指定したブランチに紐づく PR のステータス更新(checks を success や error などに変更) テスト結果の slack 通知 最近リリースされた Slack や Microsoft Teams で workflow についてのイベントを subscribe する機能 も非常に便利ですが、複数リポジトリのテスト結果を1つの Slack channel に投稿する場合、どのリポジトリからの通知なのかが若干分かりづらいため、私のチームでは workflow 側に定義した Slack 通知でもうしばらく運用しようかなと思っています。 reusable workflows の活用例 reusable workflow も私たちのチームにとって非常にありがたい機能です。 私たちのチームではベアメタルサーバー 1 とハイパーバイザー 2 の2つのプロダクトを開発しているのですが、 ハイパーバイザーはベアメタルサーバーの機能をがっつり利用しているため、ハイパーバイザーの結合テストではベアメタルサーバーの環境構築も必要となります。 このような事情のため、以下のように reusable workflow を呼び出すことで、ベアメタルサーバーとハイパーバイザーの結合テストの処理の共通化をうまく行っています。 マイクロサービスアーキテクチャにおける結合テストで、結合先のサービスをモック化できず自前で構築しないといけない場合には、このような reusable workflow の使い方が向いているのではないかと思います。 name: ベアメタルサーバーの結合テスト jobs: initialize-baremetal-environment: uses: <org>/<repo>/.github/workflows/initialize_baremetal_environment.yml@master with: ... create-baremetal-instance: ... name: ハイパーバイザーの結合テスト jobs: initialize-baremetal-environment: uses: <org>/<repo>/.github/workflows/initialize_baremetal_environment.yml@master with: ... initialize-hypervisor-environment: uses: <org>/<repo>/.github/workflows/initialize_hypervisor_environment.yml@master with: ... create-hypervisor-instance: ... 複数リポジトリ間での機密情報の扱い方 workflow 内で扱うパスワード等の機密情報の格納先の第一候補は secrets ですよね。 ですが、 扱っているリポジトリが多く、各リポジトリで同じ機密情報を使いたい場合、各リポジトリに同じ secrets を設定していくのは手間 です。 Organization Administrator の権限があれば、Organization に secrets を設定して各リポジトリから参照することもできますが、Organization Administrator ではない開発者からするとこの方法は取りづらいです。 secrets はコードとして管理できるものでもないので構成管理もしづらいです。 機密情報の参照用の自作 action そこで私たちのチームで作成しているのが「機密情報の参照用の自作 action」です。 共通処理リポジトリに set-credentials という composite action を作成しており、この action を以下のように呼び出すことで、機密情報を環境変数に設定して利用可能にしています。 steps: - uses: <org>/<repo>/.github/actions/common/set-credentials@master with: decryption_key: ${{ secrets.DECRYPTION_KEY }} credential_names: test_key test_middleware_pasword - runs: bundle exec rsepc ← 環境変数 test_key と test_middleware_password が export された状態で実行される set-credentials action の中身は以下です。 name: set credentials description: set credentials inputs: decryption_key: required: true credential_names: required: true description: ex) 'hoge_password fuga_password' runs: using: composite steps: - name: set credentials to GITHUB_ENV working-directory: ${{ github.action_path }} run: | for credential_name in ${{ inputs.credential_names }}; do credential_value=$(openssl xxxx -d -in secret.yml.enc -pass pass:${{ inputs.decryption_key }} xxxx | yq -r .${credential_name}) if [ $credential_value == null ]; then continue fi echo "::add-mask::${credential_value}" echo "${credential_name}=${credential_value}" >> $GITHUB_ENV done shell: bash action のディレクトリに配置してある secret.yml.enc というファイルを inputs で与えた decryption_key で復号しつつ credentials_names で指定した値だけを抽出し、GITHUB_ENV に格納して job 内で利用可能にしています。 このとき add-mask の workflow command によってログに機密情報の文字列が直接表示されないようにしているのがポイントです。 この方法だと 各リポジトリに設定する secrets は1つで済む ことになり、それ以外の機密情報はコードとして構成管理できるので取り回しもしやすくなります。 (機密情報を環境変数に格納すると、悪意のある外部 action から情報を読み取られるリスクがあるため、その点は注意しながら使っています) 独自キャッシュによる効率的なファイル共有 長い workflow を作っていくと、どの部分から処理を再実行できるようにするかを意識して job を分割していくことになります。 job を分割していくと job 内で利用するファイルを job 間で共有したくなってきますが、こんなときに便利なのが GitHub Actions の機能として提供されている cache です。 この機能を使うことで、ブランチやタグの制約はありますが job 間や run 間、workflow 間でキャッシュしたファイル(ディレクトリ)を使い回すことができます。 公式の cache の注意点 ですが、この cache 機能では 機密情報のキャッシングを行うのは推奨されません 。 私たちの workflow では、複数の job 間で Ansible のコードを使いまわしてテスト環境の構築などを行いたいと思い、Ansible のコードをディレクトリ丸ごとキャッシングしようとしていたのですが、この Ansible にはパスワードなどの機密情報が入っているため GitHub 公式の cache action でキャッシングするのは好ましくありません。 独自の cache action そこで作成したのが以下のような独自 cache action です。 name: cache description: save/restore local directory to/from remote storage or remove outdated directory on remote storage inputs: key: required: false default: ${{ github.run_id }} path: required: false default: . action: required: true type: choice options: - save - restore - clean runs: using: composite steps: - name: save local directory to remote storage by rsync if: ${{ inputs.action == 'save' }} run: | <rsync コマンドにより指定したパスの内容をストレージサーバに保存> shell: bash - name: restore local directory from remote storage by rsync if: ${{ inputs.action == 'restore' }} run: | <rsync コマンドによりストレージサーバから指定したパスの内容を取得> shell: bash - name: delete content last accessed at before 1 week if: ${{ inputs.action == 'clean' }} run: | <ストレージサーバ上の古いファイルを削除> shell: bash inputs として key path action を与えて action を呼び出すことで、runner が動作する環境上の path の内容を、検証環境内のストレージサーバ上に key という名前で save したり、逆にストレージサーバ上の内容を runner 環境上へ restore できるように作っています。 検証環境内の転送速度はかなり高いため、多少サイズが大きいディレクトリをキャッシングしても読み書きにさほど時間はかかりません。 デフォルトでは key は github.run_id としており、実行した run 内でのみキャッシュが共有可能となるため、job 間でのデータ共有に特化した作りとなっています。 もちろん公式の cache でも紹介されているように hashFiles と組み合わせることでライブラリ部分だけをキャッシングするといった使い方もしています。 独自 cache のスコープ この仕組みは、検証環境内の self-hosted runner でのみ動作させる前提であるため、意図的にキャッシュのスコープを制限しないようにしています。 つまり、異なるブランチ同士でも、はたまた リポジトリの垣根を超えても、 key を適切に指定すればキャッシュを利用できるようにしています 。 こうすることで、公式の cache を超えたリポジトリ横断での効率的な CI を実現することが可能となっています。 キャッシュの削除も定期実行 workflow で行っているため、ストレージサーバの容量がいつの間にか膨れ上がっていた、ということも防げるようになっています。 余談 先述した reusable workflow を使いつつ、job 間の依存関係を解きほぐしながら並列実行できる部分を積極的に並列実行するようにした結果、以下のような長い workflow ができあがりました。(workflow 自体の時間を短くできて re-run できる箇所も多くて良いのですが見づらい、、) さいごに いかがでしたでしょうか? GitHub Actions には様々な機能があり、それらを組み合わせてオリジナルの workflow を作っていく楽しさがあります。 みなさんも時間があるときに GitHub Actions の公式ドキュメントを全部読み漁って、自分の workflow に適した機能をぜひ見つけてください。 次回の記事では CD にフォーカスした内容についてご紹介をしたいと思います。 CD は CI よりも課題が多く現在も検討を続けている最中のため、具体的な方針が固まったら記事を執筆する予定です。 最後になりますが、SDPF クラウドは国内最大級のクラウドサービスです。 開発メンバーは、数千台以上の物理サーバーの操作の自動化をはじめとした、技術的難易度の高い課題に取り組みつつ、日々より良いサービスにしようと邁進しております。 今回紹介した workflow を活用した CI 設計など、大規模サービスだからこそのやりがいのある課題もたくさん転がっています。 もし私たちのチームに興味を持たれた方は こちら からの応募をお願いいたします。 専有型の物理サーバーをオンデマンドに利用可能とするサービス。 https://sdpf.ntt.com/services/baremetal-server/ ↩ ベアメタルサーバー上に vSphere ESXi や Hyper-V など代表的なハイパーバイザーを予めセットアップした状態で利用可能とするサービス。 https://sdpf.ntt.com/services/vsphere/ https://sdpf.ntt.com/services/hyper-v/ ↩
アバター
はじめに こんにちは、イノベーションセンターの竹中です。 本記事では、SR-TE を一元的に管理するためのソフトウェア実装である Pola PCE の概要や利用方法について紹介します。 Pola PCE は私と 三島 で実装し、現在 NTT Com より OSS 公開中です。 [Multi-AS Segment Routing 検証連載 #11] PCE 実装の検証 記事でも一部機能を紹介しましたが、本記事では Example を用いた試用環境の準備、ベンダールーターを用いての環境準備、また各機能について紹介します。 また、Go で開発したツールを OSS として公開する中で得た知見を NTT Communications Advent Calendar 2022 8日目 で紹介しているので、こちらも是非ご覧ください。 目次 Pola PCE 概要 Pola PCE とはどのような特徴を持つ PCE であるか、またその活用方法について記載 Docker 環境を用いた Explicit Path 機能の検証 手元の Docker 環境のみで Pola PCE を検証する手順を紹介 Multi-vendor で構成されるネットワークを用いた Dynamic Path 機能の検証 Multi-vendor 機器を用いて構築された SR-MPLS 環境において Pola PCE を利用する手順を紹介 SR-TE や PCE に興味を持ち、簡潔な構成で Pola PCE の機能を試してみたい方は、まずは Docker 環境を用いた検証をお勧めします。 なお PCE とは何か、については下記記事にて紹介しているためこちらも併せて参考にしてください。 engineers.ntt.com 開発へのモチベーション Pola PCE の紹介の前段として、開発に至ったモチベーションについて話します。 開発のモチベーションは大きく分けて 3 つあります。 1 つ目は、最新 RFC や Internet-Draft で議論中の PCE に関する機能を先行して実装し、検証したいという要求です。 自作の PCE を準備することで、RFC や Internet-Draft の更新に合わせてリアルタイムに実装状況を更新し、自作 PCE - 各社ベンダー製 PCC の相互接続検証を行いたいです。 2 つ目は、Multi-vendor 対応な PCE を利用したいという要求です。 Multi-AS Segment Routing 検証連載 で公開していますが、私たちは Multi-vendor 機器環境で Segment Routing の検証を行なっています。そのため、PCE についても Multi-vendor 対応な製品を利用したいです。 PCE の技術仕様に関しては RFC や Internet-Draft で未提案な部分もあり、PCC 機能にはベンダーごとに独自実装の部分も存在します。そのため、現状 Multi-vendor 環境において Color や Preference の情報などを含む PCEP の相互接続はまだできません。自作 PCE によってそのような問題の解決したいです。 3 つ目は、外部連携用の API を提供する軽量なツールが欲しいという要求です。 必要に応じて機能の分割や拡張などが行えるようにマイクロサービスアーキテクチャを目指し、機能ごとに分かれたコンポーネントを API で連携する仕組みを利用したいです。例えば SR Policy 投入機能は独立して動作させたり、トポロジーを描写するツールと連携させて利用したいです。 以上のモチベーションから Pola PCE を実装しました。 Pola PCE 概要 できること Pola PCE では現在(v1.1.2) SR-MPLS で構成されるネットワークにおいて 以下の機能が利用可能です。 PCC へ SR Policy の追加・更新 PCC で利用中の SR Policy の確認 GoBGP との連携による TED の取得 TED を保持することで経路計算が可能になり、Dynamic Path が利用可能 Multi-vendor 環境での利用 IOS XR、Junos、FRRouting を PCC として利用可能 マイクロサービスアーキテクチャに基づいた実装 Pola PCE はマイクロサービスアーキテクチャに基づいた実装となっており、それぞれのプロセス間の連携を gRPC が担っています。 そのため、各利用者の用途に応じて必要な機能のみを組み合わせて利用できます。 最小構成であれば Pola PCE 単体で SR Policy の管理や Explicit Path の追加や更新ができる簡素な SR Policy 管理ツールとして利用できます。 また Pola PCE のデーモンが gRPC インターフェースを提供しているため、デーモンを中核として、例えば以下のような高機能なネットワークコントローラを構築することも可能です。 構成 Pola PCE の構成は以下の通りです。 ツール単体は polad / pola CLI tool からなります。 polad は PCE デーモンであり、PCEP Session の管理や必要に応じたトポロジー情報の取得・管理、また、保持している情報を出力するための gRPC インターフェースを提供します。 pola CLI tool は polad の gRPC クライアントとなっており、入力したコマンドに応じて適切な gRPC リクエストを送信し polad の保持する情報を取得・更新します。 トポロジー情報の取得の必要があれば GoBGP と連携させたり、gRPC インターフェースを提供しているためユーザーが polad の gRPC クライアント機能をもつツールを用意することで Pola PCE を制御することも可能です。 実装予定の新機能 私たちはチームの取り組みとして SR-MPLS だけでなく SRv6 環境での検証も行っております。 SRv6 環境での利用も想定しているため、今後の直近のアップデート予定として PCEP の SRv6 対応 を開発中です。プロトコル拡張自体がまだ Internet-Draft で議論中のため、各ベンダーの SRv6 PCC 機能の実装状況や独自実装の内容を把握しつつ、Multi-vendor で動作する SRv6 PCE へと拡張する予定です。 Docker 環境を用いた Explicit Path 機能の検証 公開中の example を用いて動作検証をします。検証環境の構築には OSS の Docker ベースなネットワークエミュレータである tinet を利用しているため、Docker が利用できる x86_64 CPU 機器が1台あれば手元で試せます。 検証環境 公開中の spec.yaml を用いて、tinet により以下のトポロジーを作成します。 SR-MPLS ドメインは FRRouting version:latest (2022年12月5日現在では v8.4.1) を用いて構築されます。 手順 Docker、tinet のインストールについては手順を省略します。 pola リポジトリの取得 GitHub から pola リポジトリを clone します。 user@server:~$ git clone https://github.com/nttcom/pola トポロジーの構築・機器への config 投入 ディレクトリを sr-mpls_l3vpn へ変更したのちに tinet コマンドを 1 つ実行すると全環境構築が完了します。 user@server:~$ cd pola/examples/sr-mpls_l3vpn/ user@server:~/pola/examples/sr-mpls_l3vpn$ tinet upconf | sudo sh -x docker ps でトポロジー図に記載してある、各機器名称に従ったコンテナが作成・起動していることが確認できます。 user@server:~/pola/examples/sr-mpls_l3vpn$ docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES fe439ecd0a78 host_ubuntu:latest "bash" 25 minutes ago Up 25 minutes host02 0b17cd9386aa host_ubuntu:latest "bash" 25 minutes ago Up 25 minutes host01 f92da0d39aea frr:latest "/sbin/tini -- /usr/…" 25 minutes ago Up 25 minutes p02 d5093e6fa5c1 frr:latest "/sbin/tini -- /usr/…" 25 minutes ago Up 25 minutes p01 8341a6836618 frr:latest "/sbin/tini -- /usr/…" 25 minutes ago Up 25 minutes pe02 a24a70528e1a frr:latest "/sbin/tini -- /usr/…" 26 minutes ago Up 25 minutes pe01 25c3666b6f17 pola:latest "bash" 26 minutes ago Up 26 minutes pola なお、Pola PCE や FRRouting の初期設定は example の spec.yaml に記載しているため、気になる方はご確認ください。 PCEP Session の確認 pola session コマンドによって PCEP Session を確認します。 docker exec で pola コンテナに入り、コマンドを実行します。 user@server:~/pola/examples/sr-mpls_l3vpn$ docker exec -it pola /bin/bash root@pola:/# pola session sessionAddr(0): 10.0.255.1 SR Policy の発行 SR Policy のパラメータ指定する yaml ファイルを作成し、 pola sr-policy add コマンドで SR Policy を PCC に発行します。 本検証では、pe01 から pe02 への VPN 経路(color 1)に対して、pe01 -> p01 -> p02 -> pe02 を通るようにするための SR Policy を発行します。 作成する yaml ファイル # policy1.yaml srPolicy : name : "policy1" pcepSessionAddr : "10.0.255.1" srcAddr : "10.255.0.1" dstAddr : "10.255.0.3" color : 1 segmentList : - sid : 16002 nai : "10.255.0.2" - sid : 16004 nai : "10.255.0.4" - sid : 16003 nai : "10.255.0.3" コマンド実行 root@pola:/# pola sr-policy add -f policy1.yaml --no-link-state success! 作成した SR Policy は pola sr-policy list コマンドで確認ができます。 ※ 現在 FRRouting で利用されている SR Policy の Color、Preference 項目は pola sr-policy list から確認できない状態です。FRRouting の PCEP RFC 対応が進むと確認できるようになります。 root@pola:/# pola sr-policy list LSP(0): PcepSessionAddr: 10.0.255.1 PolicyName: policy1 SrcAddr: 10.0.255.1 DstAddr: 10.255.0.3 Color: 0 Preference: 0 DstAddr: 10.255.0.3 SegmentList: 16002 -> 16004 -> 16003 経路と SR Policy の紐付け FRRouting の route-map を用いて、SR Policy と BGP で広告される経路とを紐付けます。 pe01 コンテナに入り、route-map を適用する config を投入します。 user@server:~/pola/examples/sr-mpls_l3vpn$ docker exec -it pe01 /bin/bash bash-5.1# vtysh -c 'conf t' -c 'router bgp 65000' -c 'address-family ipv4 vpn' -c 'neighbor 10.255.0.3 route-map color1 in' 疎通確認 疎通確認を行います。 まずは host01 から host02 へ ping が通ることを確認します。 user@server:~$ docker exec -it host01 ping -c 5 192.168.1.2 PING 192.168.1.2 (192.168.1.2) 56(84) bytes of data. 64 bytes from 192.168.1.2: icmp_seq=1 ttl=62 time=0.198 ms 64 bytes from 192.168.1.2: icmp_seq=2 ttl=62 time=0.126 ms 64 bytes from 192.168.1.2: icmp_seq=3 ttl=62 time=0.122 ms 64 bytes from 192.168.1.2: icmp_seq=4 ttl=62 time=0.110 ms 64 bytes from 192.168.1.2: icmp_seq=5 ttl=62 time=0.112 ms --- 192.168.1.2 ping statistics --- 5 packets transmitted, 5 received, 0% packet loss, time 4098ms rtt min/avg/max/mdev = 0.110/0.133/0.198/0.032 ms また、host01 から host02 へ ping を打っている状態で pe01 の net0 interface を通るパケットをキャプチャします。 net0 interface から ping のパケットが出ていることと、SR Policy で指定した MPLS ラベルが積まれていることを確認できます。 host01 user@server:~$ docker exec -it host01 ping 192.168.1.2 pe01 user@server:~/pola/examples/sr-mpls_l3vpn$ docker exec -it pe01 tcpdump -i net0 tcpdump: verbose output suppressed, use -v[v]... for full protocol decode listening on net0, link-type EN10MB (Ethernet), snapshot length 262144 bytes 11:24:48.358826 MPLS (label 16004, exp 0, ttl 63) (label 16003, exp 0, ttl 63) (label 80, exp 0, [S], ttl 63) IP 192.168.0.2 > 192.168.1.2: ICMP echo request, id 1067, seq 12, length 64 11:24:49.382800 MPLS (label 16004, exp 0, ttl 63) (label 16003, exp 0, ttl 63) (label 80, exp 0, [S], ttl 63) IP 192.168.0.2 > 192.168.1.2: ICMP echo request, id 1067, seq 13, length 64 11:24:49.957688 IP 10.0.0.2 > 224.0.0.5: OSPFv2, Hello, length 48 11:24:50.406778 MPLS (label 16004, exp 0, ttl 63) (label 16003, exp 0, ttl 63) (label 80, exp 0, [S], ttl 63) IP 192.168.0.2 > 192.168.1.2: ICMP echo request, id 1067, seq 14, length 64 11:24:51.430769 MPLS (label 16004, exp 0, ttl 63) (label 16003, exp 0, ttl 63) (label 80, exp 0, [S], ttl 63) IP 192.168.0.2 > 192.168.1.2: ICMP echo request, id 1067, seq 15, length 64 11:24:52.454779 MPLS (label 16004, exp 0, ttl 63) (label 16003, exp 0, ttl 63) (label 80, exp 0, [S], ttl 63) IP 192.168.0.2 > 192.168.1.2: ICMP echo request, id 1067, seq 16, length 64 Multi-vendor で構成されるネットワークを用いた Dynamic Path 機能の検証 GitHub から Pola PCE のバイナリファイルをダウンロードし、SR-MPLS を用いた Multi-vendor L3VPN 環境で検証します。 検証環境 以下のトポロジーを用いて検証します。 各ルーターの製品とバージョンは以下の通りです。 rt01: ASR9901(IOS XR 7.6.1) rt02: MX204(Junos 22.1R1.10) rt03: ASR9901(IOS XR 7.5.1) rt04: MX204(Junos 21.4R1.12) rt05: Cisco 8201(IOS XR 7.5.1) rt06: PTX10001-36MR(Junos 21.2R1.15-EVO) rt07: ASR9902(IOS XR 7.6.1) rt08: MX204(Junos 21.4R1.12) 手順 本検証では Dynamic Path 機能を検証します。 Dynamic Path 検証にあたって、Pola PCE が経路計算に利用するトポロジー情報を取得する手段として GoBGP を利用します。SR-MPLS ドメインの任意のルーターと GoBGP で BGP-LS Session を確立し、かつ Pola PCE と連携することで Pola PCE が Traffic Engineering Database (TED) としてトポロジー情報を保持します。 そのため、本章では GoBGP を Pola PCE で扱うための方法についても説明します。 Pola PCE / GoBGP のバイナリダウンロード Pola PCE と GoBGP のバイナリファイルをダウンロードします。 Pola PCE ダウンロード user@pce:~$ wget https://github.com/nttcom/pola/releases/download/v1.1.2/pola_1.1.2_linux_amd64.tar.gz user@pce:~$ tar -zxvf pola_1.1.2_linux_amd64.tar.gz user@pce:~$ sudo install -t <path が通っているディレクトリ> polad user@pce:~$ sudo install -t <path が通っているディレクトリ> pola GoBGP ダウンロード GoBGP は daemon (gobgpd) と CLI ツール (gobgp) の 2 つの実行ファイルから構成されますが、今回利用する実行ファイルは gobgpd のみになります。 user@pce:~$ wget https://github.com/osrg/gobgp/releases/download/v3.9.0/gobgp_3.9.0_linux_amd64.tar.gz user@pce:~$ tar -zxvf gobgp_3.9.0_linux_amd64.tar.gz user@pce:~$ sudo install -t <path が通っているディレクトリ> gobgpd gobgpd の起動 gobgpd は toml (または yaml、json) 形式のファイルに config を記載し、実行します。 トポロジー情報を取得するため、gobgpd を起動して RR である rt03 / rt04 と BGP-LS の Session を確立します。 作成する toml ファイル # gobgpd_cfg.toml [global.config] as = 65001 router-id = "10.99.0.254" [[neighbors]] [neighbors.config] neighbor-address = "10.255.1.3" peer-as = 65001 [[neighbors.afi-safis]] [neighbors.afi-safis.config] afi-safi-name = "ls" [[neighbors]] [neighbors.config] neighbor-address = "10.255.1.4" peer-as = 65001 [[neighbors.afi-safis]] [neighbors.afi-safis.config] afi-safi-name = "ls" gobgpd 起動 user@pce:~$ sudo gobgpd -f gobgpd_cfg.toml -l debug {"level":"info","msg":"gobgpd started","time":"2022-12-08T10:53:05Z"} {"Topic":"Config","level":"info","msg":"Finished reading the config file","time":"2022-12-08T10:53:05Z"} {"Key":"10.255.1.3","Topic":"config","level":"info","msg":"Add Peer","time":"2022-12-08T10:53:05Z"} {"Key":"10.255.1.3","Topic":"Peer","level":"info","msg":"Add a peer configuration","time":"2022-12-08T10:53:05Z"} {"Key":"10.255.1.4","Topic":"config","level":"info","msg":"Add Peer","time":"2022-12-08T10:53:05Z"} {"Key":"10.255.1.4","Topic":"Peer","level":"info","msg":"Add a peer configuration","time":"2022-12-08T10:53:05Z"} {"Duration":0,"Key":"10.255.1.3","Topic":"Peer","level":"debug","msg":"IdleHoldTimer expired","time":"2022-12-08T10:53:05Z"} {"Duration":0,"Key":"10.255.1.4","Topic":"Peer","level":"debug","msg":"IdleHoldTimer expired","time":"2022-12-08T10:53:05Z"} {"Key":"10.255.1.3","Topic":"Peer","level":"debug","msg":"state changed","new":"BGP_FSM_ACTIVE","old":"BGP_FSM_IDLE","reason":{"Type":7,"BGPNotification":null,"Data":null},"time":"2022-12-08T10:53:05Z"} {"Key":"10.255.1.4","Topic":"Peer","level":"debug","msg":"state changed","new":"BGP_FSM_ACTIVE","old":"BGP_FSM_IDLE","reason":{"Type":7,"BGPNotification":null,"Data":null},"time":"2022-12-08T10:53:05Z"} 現時点では対向の rt03 / rt04 に BGP-LS Session を確立するための config が投入されていないため、BGP Session はまだ確立しません。 BGP-LS Session の確立 rt03 / rt04 に BGP-LS Session を確立するための config を投入します。 IOS XR / Junos 共に、BGP-LS Session を確立するための config を記載します。 IOS XR router isis 1 distribute link-state instance-id 32 ! ! router bgp 65001 address-family link-state link-state ! neighbor-group pola remote-as 65001 timers 10 30 update-source Loopback0 address-family link-state link-state ! ! neighbor 10.99.0.254 use neighbor-group pola BGP-LS Session の UP を確認します。 RP/0/RSP0/CPU0:rt03#show bgp link-state link-state summary Thu Dec 8 20:25:17.394 JST BGP router identifier 10.255.1.3, local AS number 65001 BGP generic scan interval 60 secs Non-stop routing is enabled BGP table state: Active Table ID: 0x0 RD version: 221 BGP main routing table version 221 BGP NSR Initial initsync version 65 (Reached) BGP NSR/ISSU Sync-Group versions 0/0 BGP scan interval 60 secs BGP is operating in STANDALONE mode. Process RcvTblVer bRIB/RIB LabelVer ImportVer SendTblVer StandbyVer Speaker 221 221 221 221 221 0 Neighbor Spk AS MsgRcvd MsgSent TblVer InQ OutQ Up/Down St/PfxRcd 10.99.0.254 0 65001 7 51 221 0 0 00:00:59 0 Junos set policy-options policy-statement TE term 1 from family traffic-engineering set policy-options policy-statement TE term 1 then accept set protocols bgp group pola type internal set protocols bgp group pola local-address 10.255.1.4 set protocols bgp group pola family traffic-engineering unicast set protocols bgp group pola export TE set protocols bgp group pola neighbor 10.99.0.254 set protocols mpls traffic-engineering database import policy TE BGP-LS Session の UP を確認します。 user@rt04> show bgp group pola Group Type: Internal AS: 65001 Local AS: 65001 Name: pola Index: 2 Flags: <Export Eval> Export: [ TE ] Options: <GracefulShutdownRcv> Holdtime: 90 Preference: 0 Graceful Shutdown Receiver local-preference: 0 Total peers: 1 Established: 1 10.99.0.254+42121 lsdist.0: 0/0/0/0 polad の起動 polad は yaml 形式のファイルに config を記載し、実行します。 トポロジー情報の利用を有効化して GoBGP からトポロジー情報を取得するための config を記載します。 PCEP に関しては、PCC と疎通性のあるアドレス、PCEP のデフォルトポートである 4189 を設定しています。 また、GoBGP は gRPC のデフォルトポートである 50051 で gRPC リクエストを待ち受けています。そのため以下の config では GoBGP の gRPC クライアントの接続先ポートを 50051 とし、Pola PCE が提供する gRPC サーバは 50052 で待ち受けるように設定しています。 作成する yaml ファイル # polad_cfg.yaml global : pcep : address : "10.99.0.254" port : 4189 grpc-server : address : "127.0.0.1" port : 50052 log : path : "./pola_log/" name : "polad.log" ted : enable : true source : "gobgp" gobgp : grpc-client : address : "127.0.0.1" port : 50051 polad 起動 user@pce:~$ mkdir pola_log user@pce:~$ polad -f polad_cfg.yaml 2022-12-11T08:06:23.233Z info gRPC listen {"listenInfo": "127.0.0.1:50052", "server": "grpc"} 2022-12-11T08:06:23.233Z info PCEP listen {"listenInfo": "10.99.0.254:4189"} 2022-12-11T08:06:23.249Z info Request TED update {"source": "GoBGP", "session": "127.0.0.1:50051"} 2022-12-11T08:06:23.249Z info Update TED Update TED のログが出力されていれば、gobgpd との接続が完了し TED の更新が完了しています。 PCEP Session の確立 PCC とする rt01 / rt02 へ PCEP Session を確立するための config を投入します。 IOS XR / Junos 共に、PCEP Session を確立するための config を記載します。 IOS XR segment-routing traffic-eng pcc source-address ipv4 10.255.1.1 pce address ipv4 10.99.0.254 ! ! ! ! PCEP Session が確立していることを rt01 で確認します。 RP/0/RSP0/CPU0:rt01#show segment-routing traffic-eng pcc ipv4 peer Sun Dec 11 17:41:06.157 JST PCC's peer database: -------------------- Peer address: 10.99.0.254, Precedence: 255, (best PCE) State up Capabilities: Stateful, Update, Segment-Routing, Instantiation Junos set protocols mpls lsp-external-controller pccd set protocols source-packet-routing lsp-external-controller pccd set protocols pcep pce pola local-address 10.255.1.2 set protocols pcep pce pola destination-ipv4-address 10.99.0.254 set protocols pcep pce pola pce-type active set protocols pcep pce pola pce-type stateful set protocols pcep pce pola lsp-provisioning set protocols pcep pce pola spring-capability PCEP Session が 確立していることを rt02 で確認します。 user@rt02> show path-computation-client status Session Type Provisioning Status Uptime pola Stateful Active On Up 102 LSP Summary Total number of LSPs : 0 Static LSPs : 0 Externally controlled LSPs : 0 Externally provisioned LSPs : 0/16000 (current/limit) Orphaned LSPs : 0 pola (main) Delegated : 0 Externally provisioned : 0 Pola PCE 各種情報の確認 PCC と PCEP Sessoin が確立できたため、Pola PCE の持つ各種情報を確認します。 Pola PCE の操作は pola CLI コマンドを用いて行います。 PCEP Session の確認 PCEP Session を確認します。 user@pce:~$ pola session -p 50052 sessionAddr(0): 10.255.1.1 sessionAddr(1): 10.255.1.2 TED の確認 gobgpd から取得したトポロジー情報を確認します。 user@pce:~$ pola ted -p 50052 Node: 1 0000.0aff.0107 Hostname: rt07 ISIS Area ID: 49.0000 SRGB: 16000 - 24000 Prefixes: 10.255.1.7/32 index: 7 Links: Local: 10.1.4.2 Remote: 10.1.4.1 RemoteNode: 0000.0aff.0106 Metrics: IGP: 10 TE: 10 Adj-SID: 24008 Local: 10.1.12.2 Remote: 10.1.12.1 RemoteNode: 0000.0aff.0105 Metrics: IGP: 10 TE: 10 Adj-SID: 24002 <snip> SR Policy の発行 SR Policy を表す yaml ファイルを作成し、pola sr-policy add コマンドで SR Policy を PCC に発行します。 本検証では、color 100 が付与されている rt01 から rt02 への VPN 経路・ rt02 から rt01 への VPN 経路それぞれに対して、TE メトリックに従った経路を通るための SR Policy を発行します。TE メトリックはトポロジー図に記載した設定をしているため、パケットはトポロジー図中青線の通りの経路を通ります。 作成する yaml ファイル # policy_dynamic_rt01.yaml asn : 65001 srPolicy : pcepSessionAddr : "10.255.1.1" name : "dynamic_rt01" srcRouterId : "0000.0aff.0101" dstRouterId : "0000.0aff.0102" color : 100 type : "dynamic" metric : "te" # policy_dynamic_rt02.yaml asn : 65001 srPolicy : pcepSessionAddr : "10.255.1.2" name : "dynamic_rt02" srcRouterId : "0000.0aff.0102" dstRouterId : "0000.0aff.0101" color : 100 type : "dynamic" metric : "te" コマンド実行 user@pce:~$ pola sr-policy add -f policy_dynamic_rt01.yaml -p 50052 success! user@pce:~$ pola sr-policy add -f policy_dynamic_rt02.yaml -p 50052 success! 作成した SR Policy は pola sr-policy list コマンドで確認ができます。 user@pce:~$ pola sr-policy list -p 50052 LSP(0): PcepSessionAddr: 10.255.1.1 PolicyName: dynamic_rt01 SrcAddr: 10.255.1.1 DstAddr: 10.255.1.2 Color: 100 Preference: 100 DstAddr: 10.255.1.2 SegmentList: 16005 -> 16008 -> 16006 -> 16002 LSP(1): PcepSessionAddr: 10.255.1.2 PolicyName: dynamic_rt02 SrcAddr: 10.255.1.2 DstAddr: 10.255.1.1 Color: 100 Preference: 100 DstAddr: 10.255.1.1 SegmentList: 16006 -> 16008 -> 16005 -> 16001 PCC で受けとった SR Policy の確認 各 PCC で受け取った経路が有効化されていることを確認します。 IOS XR RP/0/RSP0/CPU0:rt01#show segment-routing traffic-eng policy Mon Dec 12 07:41:10.566 JST SR-TE policy database --------------------- Color: 100, End-point: 10.255.1.2 Name: srte_c_100_ep_10.255.1.2 Status: Admin: up Operational: up for 00:03:14 (since Dec 12 07:37:56.127) Candidate-paths: Preference: 100 (PCEP) (active) Name: dynamic_rt01 Requested BSID: dynamic PCC info: Symbolic name: dynamic_rt01 PLSP-ID: 3 Protection Type: unprotected-preferred Maximum SID Depth: 10 Dynamic (pce 10.99.0.254) (valid) Metric Type: TE, Path Accumulated Metric: 0 16005 [Prefix-SID, 10.255.1.5] 16008 [Prefix-SID, 10.255.1.8] 16006 [Prefix-SID, 10.255.1.6] 16002 [Prefix-SID, 10.255.1.2] Attributes: Binding SID: 24012 Forward Class: Not Configured Steering labeled-services disabled: no Steering BGP disabled: no IPv6 caps enable: yes Invalidation drop enabled: no Max Install Standby Candidate Paths: 0 Junos user@rt02> show spring-traffic-engineering lsp detail Name: dynamic_rt02 Tunnel-source: Path computation element protocol(PCEP) Tunnel Forward Type: SRMPLS To: 10.255.1.1-100<c> State: Up Path Status: NA Outgoing interface: NA Auto-translate status: Disabled Auto-translate result: N/A BFD status: N/A BFD name: N/A Segment ID : 128 ERO Valid: true SR-ERO hop count: 4 Hop 1 (Strict): NAI: IPv4 Node ID, Node address: 10.255.1.6 SID type: 20-bit label, Value: 16006 Hop 2 (Strict): NAI: IPv4 Node ID, Node address: 10.255.1.8 SID type: 20-bit label, Value: 16008 Hop 3 (Strict): NAI: IPv4 Node ID, Node address: 10.255.1.5 SID type: 20-bit label, Value: 16005 Hop 4 (Strict): NAI: IPv4 Node ID, Node address: 10.255.1.1 SID type: 20-bit label, Value: 16001 Total displayed LSPs: 1 (Up: 1, Down: 0) 本検証で発行した SR Policy は、BGP Color Extended Community の 100 が付加されている VPN 経路に対して適用されます。Color の付加方法は [Multi-AS Segment Routing 検証連載 #4] Color-Based Steering in Single-AS で紹介しているため、こちらの記事を参考にしてください。 疎通確認 疎通確認を行います。 rt01 の Customer ネットワークと rt02 の Customer ネットワークで相互に traceroute を行い、経路を確認します。 事情により rt06 を通る経路の traceroute は当該ホップの結果で * * * と表示されています。 rt01 -> rt02 RP/0/RSP0/CPU0:rt01#traceroute 192.168.1.254 vrf Customer Mon Dec 12 08:30:46.261 JST Type escape sequence to abort. Tracing the route to 192.168.1.254 1 10.1.1.2 [MPLS: Labels 16008/16006/16002/130 Exp 0] 26 msec 4 msec 4 msec 2 10.1.5.2 [MPLS: Labels 16006/16002/130 Exp 0] 1 msec 1 msec 2 msec 3 * * * 4 192.168.1.254 1 msec 1 msec 1 msec rt02 -> rt01 user@rt02> traceroute 192.168.0.254 routing-instance Customer no-resolve traceroute to 192.168.0.254 (192.168.0.254), 30 hops max, 52 byte packets 1 * * * 2 10.1.11.2 0.906 ms 0.837 ms 1.177 ms MPLS Label=16005 CoS=0 TTL=1 S=0 MPLS Label=16001 CoS=0 TTL=2 S=0 MPLS Label=24013 CoS=0 TTL=2 S=1 3 10.1.5.1 2.108 ms 2.132 ms 3.897 ms MPLS Label=16001 CoS=0 TTL=1 S=0 MPLS Label=24013 CoS=0 TTL=3 S=1 4 10.1.1.1 3.844 ms * 3.963 ms トポロジー図に記載の通りの経路となっていることが確認できます。 まとめ 本記事では Pola PCE の概要と手元の Docker 環境・ Multi-vendor ネットワーク環境での利用方法について紹介しました。 気軽に試験環境を作成できるため是非お手元で試してみてください! 質問や気になった点、機能追加も大歓迎です。 ブログコメントや GitHub Issues/PRs を是非お願いします! GitHub - nttcom/pola: PCEP Library and Stateful PCE Implementation with Go
アバター
この記事は、 NTTコミュニケーションズ Advent Calendar 2022 20日目の記事です。 こんにちは。コミュニケーション&アプリケーションサービス部の石井です。 普段の業務では文章要約技術を用いたAPIサービス 1 の開発・運用に取り組んでおります。 この記事ではグラフニューラルネットワーク(GNN)、特に Heterogeneous Graph(異種グラフ) を扱ったGNNについて紹介していこうと思います。 本記事で扱う内容 この記事で取り扱う内容は以下です。 グラフニューラルネットワーク(GNN)とは Heterogeneous Graph(異種グラフ) 機械学習におけるグラフベースの問題設定 Pytorch-geometricによるモデル構築 GNNの概要と Heterogeneous Graph について簡単に説明をした後に、実際にモデルを作成していく流れで展開していきます。 本記事ではアルゴリズムの詳細な解説などは省略しますので、より深く興味がある方はリンクをつけておきますので論文や解説記事を参照してみてください。 グラフニューラルネットワークとは グラフニューラルネットワーク(GNN)とはグラフで表現されたデータを深層学習で扱うためのニューラルネットワーク手法の総称です。グラフデータから表現抽出をして目的のタスクを解くというEnd2Endアプローチによる機械学習アルゴリズムとなります。メジャーな手法としては GCN 2 や GraphSAGE 3 などがあります。 GNN の仕組みについて GCN の手法を元に簡単に記載すると、グラフの頂点(ノード)の特徴量に対して隣接するノードの特徴量に重みを掛けたものを加えていく演算をすることで、対象ノードにグラフ構造の情報を加味した表現を獲得させるといった動きをします。より詳しい説明については distill 4 というサイトに「 Understanding Convolutions on Graphs 」というGNNの解説記事があるのでそちらを参考にしてみてください。 Heterogeneous Graph Heterogeneous Graphを理解する前提としてまずはグラフの定義から話していきます。 グラフ理論において、グラフとは頂点を示すノードとその間の関係であるエッジから表現されるデータ構造になります。つまり、ノード間をエッジで繋ぐことによってノードによるネットワーク構造を表現したものが、一般的によく目にするグラフと呼ばれるものになります。これを数式的に定義すると以下のようになります。 : ノードの集合、 : エッジの集合 そして、このグラフにおいて1種類のノードと、同じ意味合いのエッジによって関係を示したものを Homogeneous Graph と呼びます。一方で、複数の多様なノードとエッジを含んでいる関係のグラフを Heterogeneous Graph と言います。例えば、ソーシャルネットワークのような人と人の間を交友関係でリンクしたグラフは Homogeneous Graph であり、店舗利用関係のような人と店舗の間を購買実績でリンクしたグラフは Heterogeneous Graph となります。想起しやすいように図で示すと以下のようになります。 ちなみに GNN ベースのアルゴリズムの多くは入力が単一のノードとエッジを持つ、Homogeneous Graph を対象とした手法となっています(最近では HAT 5 のような Heterogeneous Graph を対象としたアルゴリズムも増えてきています)。その上で、なぜ Heterogeneous Graph を扱う必要があるのかという問いについてですが、現実世界において観測対象をグラフ表現で構造化しようとした場合に、複数のノードまたはエッジによる関係を定義する頻度が高いからです。ある人と人の関係を構造化する場合よりも、ある人と物やサービスの関係を構造化する場合の方がパターンが多いのは容易に想像できます。 と、ここまで Heterogeneous Graph の話をしてきましたが、そもそも GNN 自体が深層学習分野の中でも近年注目されている技術であり、データ分析競技協会である KDD CUP 2021 では「 OGB-LS 」というカテゴリで3つのグラフTaskが取り扱われるなどその注目度の高さが伺えます。また、今年開催された国際会議である KDD 2022 のResearch Trackの中では全254の論文の中で約80以上もの論文がグラフに関連した内容を取り扱うなどトレンド領域の1つとなっていることが分かりますね。 グラフにおける問題設定 次にグラフにおける問題設定について少し触れます。 通常の機械学習の問題設定では分類や回帰といったタスクを一般的に考えますが、グラフを扱う場合にはグラフに適用した問題設定を検討する必要があります。この問題設定には大きく3つの種類があります。 1つはノードを対象としたタスク(Node Centric)で、グラフ中におけるノード単位の分類や回帰といったタスクを扱います。2つめはグラフを対象としたタスク(Graph Centric)で、グラフ単位として分類や回帰といったタスクを扱います。グラフ単位のタスクは活用イメージがしづらいかと思いますが、化合物の分類などグラフが複数存在するパターンを想像してもらえると分かりやすいかと思います。最後はエッジを対象としたタスク(Edge Centric)で、各々ノード間のエッジに対して予測をして、「エッジが形成されるのか」や「エッジのクラスは何か」といったタスクを扱います。このようにグラフを扱う場合には自身の目的に応じたタスク設計が必要になるため、問題設定をしっかり検討した上で必要なデータ収集や実装を行っていきます。 また、もう少しタスクの補足をしておくと、上記の問題設定に加えて「trunsductive」と「inductive」と言う学習と推論時の状況について考慮しておくことも重要になります。 「trunsductive」とは学習と推論で同じグラフを扱う場合のことを指し、「inductive」は学習データにない新しいグラフを扱う場合のことを示します( semi-inductive 6 といった考え方も存在します)。なぜこのような問題設定の違いを意識する必要があるかというと、これは GNN のモデル構築にて選択するアルゴリズムが異なってくるためになります 7 。そのため、「trunsductive」と「inductive」どちらかによって選択可能なアルゴリズムに制約が出てくることに注意してください。 ただ、一般的には新しい未知のノードやエッジに対して予測を行いたいといった場合の活用シーンの方が多いと考えられるため、「inductive」な問題設定をベースとして考えておけばまずは良いかと思います。 実際に試してみた ここからは実際に Heterogeneous Graph を扱った GNN のモデルを構築してみようと思います。 今回は Kaggle で公開されている「 Recipes and Reviews 」のオープンデータを利用します。このデータは Food.com 8 と言う海外のレシピ共有サイトより料理レシピとそのレシピに対してのユーザレビューの情報をデータ収集したものになります。料理レシピのデータにはレシピにおけるメタ情報と定量的な栄養素といった特徴量を含んでおり、ユーザレビューのデータはあるユーザが該当する料理レシピを5段階で評価した内容が含まれています。データサイズについても500,000以上のレシピ数と1,400,000のレビュー数があるため比較的ボリュームの大きいデータとなっています。 利用するフレームワークですが、 Pytroch-geometric を用いて実装を行っていきます 9 。Pytorch-geometricでは、バージョン2.0.0より Heterogeneous Graph をサポートしています。そのため、Heterogeneous Graph を対象とするモデルを作成する場合にはバージョンに注意してご利用ください。 では、問題設定としては以下を考えてみようと思います。 ユーザとレシピの関係による二部グラブ構造の Heterogeneous Graph を定義して、ユーザがレシピに興味・関心を示すかを予測するタスク(リンク予測)を解こうと思います。データ内容はユーザによるレシピの評価を意味するため、評価したという事実を興味・関心があるという区分として扱うのは厳密には正しくはないとは思いますが、今回は便宜上このような問題設定とします。 データ準備 グラフデータ整形 データの読み込みからテーブルデータをグラフデータに変換するための処理を記述します。元となるデータは data/ のフォルダに配置しているためご自身の環境に合わせて適切に設定してください。 各ノードについてのID割り当てと各ノードIDによる COO形式 10 での集合リストによってエッジを表現してグラフデータを定義します。 import os import numpy as np import pandas as pd from tqdm import tqdm from sklearn.preprocessing import StandardScaler from sklearn.metrics import roc_curve, roc_auc_score import matplotlib.pyplot as plt import torch import torch.nn.functional as F from torch import Tensor from torch.nn import Module import torch_geometric import torch_geometric.transforms as T from torch_geometric.nn import SAGEConv, to_hetero from torch_geometric.data import HeteroData from torch_geometric.loader import LinkNeighborLoader # データの読み込み(pandas) df_recipes = pd.read_csv( '../data/food/recipes.csv' ) df_reviews = pd.read_csv( '../data/food/reviews.csv' ) # データ準備 df_reviews = df_reviews[df_reviews.RecipeId.isin(df_recipes[ "RecipeId" ].unique())] # 不要データ除外 df_recipes[ 'RecipeServings' ] = df_recipes[ 'RecipeServings' ].fillna(df_recipes[ 'RecipeServings' ].median()) # 欠損値補完 # ユーザノードとレシピノードのIDマップ作成 unique_user_id = df_reviews[ "AuthorId" ].unique() unique_user_id = pd.DataFrame( data={ "user_id" : unique_user_id, "mappedID" : pd.RangeIndex( len (unique_user_id)), } ) unique_recipe_id = df_reviews[ "RecipeId" ].unique() unique_recipe_id = pd.DataFrame( data={ "recipe_id" : unique_recipe_id, "mappedID" : pd.RangeIndex( len (unique_recipe_id)), } ) review_user_id = pd.merge( df_reviews[ "AuthorId" ], unique_user_id, left_on= "AuthorId" , right_on= "user_id" , how= "left" , ) review_recipe_id = pd.merge( df_reviews[ "RecipeId" ], unique_recipe_id, left_on= "RecipeId" , right_on= "recipe_id" , how= "left" , ) # ユーザIDとレシピIDのエッジ情報をTensorへ変換 tensor_review_user_id = torch.from_numpy(review_user_id[ "mappedID" ].values) tensor_review_recipe_id = torch.from_numpy(review_recipe_id[ "mappedID" ].values) tensor_edge_index_user_to_recipe = torch.stack( [tensor_review_user_id, tensor_review_recipe_id], dim= 0 , ) 前処理 レシピノードが持つ特徴量の前処理として標準化を行います。 今回利用する特徴量は既存のデータセット中に含まれる該当レシピの糖分や油分といった料理における構成要素のみをパラメータとして利用します。本来であればこのフェーズで特徴量エンジニアリングなどを行いますが今回は本題から外れてしまうのでスキップします。 # レシピノードの特徴量定義 recipe_feature_cols = [ "Calories" , "FatContent" , "SaturatedFatContent" , "CholesterolContent" , "SodiumContent" , "CarbohydrateContent" , "FiberContent" , "SugarContent" , "ProteinContent" , "RecipeServings" , ] df_recipes_feature = pd.merge(df_recipes, unique_recipe_id, left_on= 'RecipeId' , right_on= 'recipe_id' , how= 'left' ) df_recipes_feature = df_recipes_feature.sort_values( 'mappedID' ).set_index( 'mappedID' ) df_recipes_feature = df_recipes_feature[df_recipes_feature.index.notnull()] df_recipes_feature = df_recipes_feature[recipe_feature_cols] # 標準化 scaler = StandardScaler() scaler.fit(df_recipes_feature) scaler.transform(df_recipes_feature) df_recipes_feature = pd.DataFrame(scaler.transform(df_recipes_feature), columns=df_recipes_feature.columns) # レシピノードの特徴量をTensorへ変換 tensor_recipes_feature = torch.from_numpy(df_recipes_feature.values).to(torch.float) データローダー ここまで定義してきたデータを用いて Pytorch で扱えるデータセットとしてデータローダーを作成します。 データは RandomLinkSplit を用いてエッジに対してのデータ分割を行い、その後に LinkNeighborLoader でエッジベースのミニバッチを作成するデータローダーを定義します。この LinkNeighborLoader では全てのエッジの中からランダムサンプリングを適用し、そのエッジの隣接ノードから更にサンプリングを行うことで、全てのノードを使ったサブグラフによるミニバッチデータ作成を実施しています。 # HeteroDataオブジェクトの作成 data = HeteroData() data[ 'user' ].node_id = torch.arange( len (unique_user_id)) data[ 'recipe' ].node_id = torch.arange( len (unique_recipe_id)) data[ 'recipe' ].x = tensor_recipes_feature data[ 'user' , 'review' , 'recipe' ].edge_index = tensor_edge_index_user_to_recipe data = T.ToUndirected()(data) # 学習・評価用のデータ分割 transform = T.RandomLinkSplit( num_val= 0.1 , num_test= 0.1 , disjoint_train_ratio= 0.3 , neg_sampling_ratio= 2 , add_negative_train_samples= False , edge_types=( "user" , "review" , "recipe" ), rev_edge_types=( "recipe" , "rev_review" , "user" ), ) train_data, val_data, test_data=transform(data) # 学習用データローダー定義 edge_label_index = train_data[ "user" , "review" , "recipe" ].edge_label_index edge_label = train_data[ "user" , "review" , "recipe" ].edge_label train_loader = LinkNeighborLoader( data=train_data, num_neighbors=[ 20 , 10 ], neg_sampling_ratio= 2 , edge_label_index=(( "user" , "review" , "recipe" ), edge_label_index), edge_label=edge_label, batch_size= 256 , shuffle= True , ) # 検証用データローダー定義 edge_label_index = val_data[ "user" , "review" , "recipe" ].edge_label_index edge_label = val_data[ "user" , "review" , "recipe" ].edge_label val_loader = LinkNeighborLoader( data=val_data, num_neighbors=[ 20 , 10 ], edge_label_index=(( "user" , "review" , "recipe" ), edge_label_index), edge_label=edge_label, batch_size= 3 * 256 , shuffle= False , ) モデル学習 モデル定義 モデル全体像の簡単なアーキテクチャを説明すると、初めにユーザノードとレシピノードを分散表現に変換した後で、2層の GNN レイヤーにて分散表現から重要特徴量を抽出していき、最後に異なるノード間のエッジ存在確率を出力するようなモデルとなっています。また、GNN レイヤーにおけるアルゴリズムには GraphSAGE を用いており、これにより inductive な問題設定に対応したモデルとなるように配慮しています。 class GNN (Module): def __init__ (self, hidden_channels: int ): super ().__init__() self.conv1 = SAGEConv(hidden_channels, hidden_channels) self.conv2 = SAGEConv(hidden_channels, hidden_channels) def forward (self, x: Tensor, edge_index: Tensor) -> Tensor: x = self.conv1(x, edge_index).relu() x = self.conv2(x, edge_index) return x class Classifier (Module): def forward ( self, x_user: Tensor, x_recipe: Tensor, edge_label_index: Tensor ) -> Tensor: edge_feat_user = x_user[edge_label_index[ 0 ]] edge_feat_recipe = x_recipe[edge_label_index[ 1 ]] return (edge_feat_user * edge_feat_recipe).sum(dim=- 1 ) class Model (Module): def __init__ (self, hidden_channels: int ): super ().__init__() self.recipe_lin = torch.nn.Linear( 10 , hidden_channels) self.user_emb = torch.nn.Embedding(data[ "user" ].num_nodes, hidden_channels) self.recipe_emb = torch.nn.Embedding(data[ "recipe" ].num_nodes, hidden_channels) self.gnn = GNN(hidden_channels) self.gnn = to_hetero(self.gnn, metadata=data.metadata()) self.classifier = Classifier() def forward (self, data: HeteroData) -> Tensor: x_dict = { "user" : self.user_emb(data[ "user" ].node_id), "recipe" : self.recipe_lin(data[ "recipe" ].x) + self.recipe_emb(data[ "recipe" ].node_id), } x_dict = self.gnn(x_dict, data.edge_index_dict) pred = self.classifier( x_dict[ "user" ], x_dict[ "recipe" ], data[ "user" , "review" , "recipe" ].edge_label_index, ) return pred 学習と評価 学習用と検証用のデータローダーを用いてモデル学習とそのモデルの評価を行なっていきます。 評価はリンク予測によるエッジがあるかないかの2値分類となるため ROC-AUC 11 で精度を確認してみようと思います。 def train (model, loader, device, optimizer, epoch): model.train() for epoch in range ( 1 , epoch): total_loss = total_samples = 0 for batch_data in tqdm(loader): optimizer.zero_grad() batch_data = batch_data.to(device) pred = model(batch_data) loss = F.binary_cross_entropy_with_logits( pred, batch_data[ "user" , "review" , "recipe" ].edge_label ) loss.backward() optimizer.step() total_loss += float (loss) * pred.numel() total_samples += pred.numel() print (f "Epoch: {epoch:04d}, Loss: {total_loss / total_samples:.4f}" ) def validation (model, loader, device, optimizer): y_preds = [] y_trues = [] model.eval() for batch_data in tqdm(loader): with torch.no_grad(): batch_data = batch_data.to(device) pred = model(batch_data) y_preds.append(pred) y_trues.append(batch_data[ "user" , "review" , "recipe" ].edge_label) y_pred = torch.cat(y_preds, dim= 0 ).cpu().numpy() y_true = torch.cat(y_trues, dim= 0 ).cpu().numpy() auc = roc_auc_score(y_true, y_pred) return auc, y_pred, y_true # パラメータセット model = Model(hidden_channels= 64 ) device = torch.device( "cuda" if torch.cuda.is_available() else "cpu" ) optimizer = torch.optim.Adam(model.parameters(), lr= 0.001 ) model = model.to(device) # 学習・評価 train(model, train_loader, device, optimizer, 6 ) auc, y_pred, y_true = validation(model, val_loader, device, optimizer) # 精度確認(ROC-AUC曲線) fpr, tpr, thresholds = roc_curve(y_true, y_pred) plt.plot(fpr, tpr, label=f "AUC: {auc:.3f}" ) plt.xlabel( 'FPR: False positive rate' ) plt.ylabel( 'TPR: True positive rate' ) plt.legend(loc= 'lower right' ) plt.grid() 作成したモデルを用いて、検証用データより ROC-AUC を確認しました。 結果としてROC-AUCが 0.974 という数値でしたので、比較的高精度の予測が行えるモデルとなりました。 また、定量的な精度指標に加えて、予測モデルを使ってあるユーザノード(特定ユーザ)に対してランダム抽出したレシピに興味を持つかといったリンク予測がどれくらい当てられているのかを可視化してみました。 図の見方は真ん中のオレンジ色の丸が対象のユーザノードを示しており、その周りにある緑色のノードが興味を示す正例レシピノードで、グレー色が興味を示さない負例レシピノードとなります。上段の左図が正解データによるユーザとレシピの関係で、右図が予測結果によるユーザとレシピの関係です。下段の図はこれらの正解データの図と予測結果の図より正解と予測が一致するノードを緑色、不一致のノードを赤色で表現した図です。これを見ると興味を示すべきレシピに対して、興味がないと判別している予測がいくつかありますが、概ね予測がうまくできていることが見て取れますね。 終わりに 今回は Heterogeneous Graph の紹介から GNN でのモデリング方法について紹介しました。グラフデータはちょっと癖があるため扱いづらい部分もありますが、Pytorch-geometric などのフレームワークを用いることである程度簡単に実装できるようになっています。GNN が扱えるようになると問題解決の幅も広がっていくかと思いますので、興味がある方は是非試してみてください。 アドベントカレンダーも終盤ですが最後まで楽しんでいってください! https://www.ntt.com/about-us/press-releases/news/article/2020/0423.html ↩ M.Schlichtkrull, T.N.Kipf, P.Bloem, R.V.D.Berg, I.Titov, and M.Welling, " Modeling relational data with graph convolutional networks ", in European Semantic Web Conference, 2018. ↩ W.Hamilton, Z.Ying, and J.Leskovec, " Inductive representation learning on large graphs ", in NeurIPS, 2017. ↩ https://distill.pub/ ↩ Wang, Xiao, et al. " Heterogeneous graph attention network. " The world wide web conference. 2019. ↩ Ali, Mehdi, et al. " Improving Inductive Link Prediction Using Hyper-relational Facts. " International Semantic Web Conference. Springer, Cham, 2021. ↩ SONG, J. AND YU, K., 2021. " Framework for Indoor Elements Classification via Inductive Learning on Floor Plan Graphs. " ISPRS International Journal of Geo-Information, Volume 10. ↩ https://www.food.com/ ↩ Pytorch-geometric以外にも DGL や Pytorch-BigGraph などのライブラリがあります。 ↩ 疎行列を表現する格納方式の1つで、列・行・データの3つの1次元配列により疎行列を表現するデータ形式です。 ↩ ROC-AUC は二値分類のタスクに対する評価指標の1つ。範囲として 0.0 〜 1.0 の値をとり、1.0 に近づくほど予測精度が高いことを示す。 ↩
アバター
みなさんこんにちは デジタル改革推進部の浅野です。 NTT Comでは社内の分析課題をコンペにしてみんなで解くイベントがあるのですが、今回は正解データが十分用意できなくてもコンペを開催することに成功したので、その妙技をご紹介したいと思います。 (過去の開催は こちら にも投稿しております) 分析コンペとは 分析コンペとは予め用意された課題に対して参加者が予測を行いその精度を競う催しです。コンペサイトでは Kaggle や Atmacup などが有名です。 コンペでは参加者に学習用のデータが配られます。学習用データの中には正解データが含まれており、それを頼りに参加者は予測を作ります。さらに参加者に秘密の評価用データも裏にあり、最終的な順位はそちらで決めます。 つまりコンペを開くには、正解データを十分に用意しておく必要があります。 今回のコンペのテーマ 8月に新しいコンペのテーマ案として「社内にかかってくる迷惑電話の検知」が社内で挙がりました。迷惑電話の検知なので、予測対象となるのは迷惑電話番号となります。 背景としては、社内には月に数十万件の電話がかかってきており、ほとんどは業務の電話ですが、中には不動産勧誘などの業務外の電話が含まれています。迷惑電話は一度に大量にかかってくるといった特徴はわかっているものの、具体的な受信間隔や通話時間、そもそもどれくらい迷惑電話が含まれているのかなどは不明でした。また、社員からの通報も受け付けていましたが、正解データとしては使うには量が不足していました。 正解データが十分にない時点でコンペ化は困難に思えたのですが、色々とアイデアを出し合い、「Slack連携型ソシャゲ風 コンペシステム」を編み出しました。 Slack連携型ソシャゲ風 コンペシステム 今回 採用した仕組みは以下です。 参加者に素材を配布する 「通話履歴データ」はただのログです。正解ラベルはついていません。 「前日までの採点結果」はその名の通り、前日までに参加者から提出された怪しい電話番号の確認結果になります。 参加者が素材から迷惑電話番号を掘り出す 怪しい電話番号を見つけたらSlack Botへ提出する 提出には1個 魔法石を消費します。また、採点・公開済みの電話番号は提出できません。 毎朝メンテ時間を設け、運営が迷惑電話番号か確認する 採点結果を公開する 全ての採点結果を全員に公開します。 運営が知らない迷惑電話番号だった場合はボーナスポイントをつけます。(事前にコンペ運営側でもちょっと掘っていた) このサイクルを期間中、回し続けます。あらかじめ正解データを用意するのではなく、提出に応じて採点してフィードバックするのが肝です。(Active learningに似ている?) 正解データの不足を運営のマンパワーで解決する脳筋的解決法とも言えます。 このシステムのユニークな点として、 初日、正解データは全く与えられない 最初はドメイン知識を頼りに手探りで提出することになります。 状況が日々変化する 一度いいモデルを作ったら終わりではなく、正解データが毎日増えるので仮説検証を繰り返すことになります。実際のデータサインエンスの業務もそんな感じなのでよい練習?になります。 調整弁としての魔法石 運営による確認作業がキャパを超えないように、配布する魔法石によって提出数を制限します。 謎の戦略性がある いいロジックを見つけても、採点結果が翌日に全体に公開されてしまうので真似されてしまうかもれません。一方で温存しておくと先に掘られるかもしれません。 わかりやすい迷惑電話番号は早期に掘り尽くされてしまいますが、後発組はリスクなく先発組の残した採点結果を利用できます。 元々は正解データの不足を補うための仕組みでしたが、状況が刻々と変わるゲーム性が高い新感覚コンペに仕上がりました。 なお、参加者に配るデータ内の電話番号は全てハッシュ化しています。 これはプライバシー上の配慮のほか、参加者が検索を行なって迷惑電話番号か確認する行為を防ぐ意味もあります。 開催の様子 コンペには様々な部署から社員61名が参加しました。 参加登録や現在のランキング表示はSlackのbot上で行いました。 提出の推移は以下の通りです。 毎日100件前後の投稿があり、最終日には300件の投稿がありました。3週間の期間で総投稿数は1300件を超えました。 そして、全体では 252件の新たな迷惑電話番号 を発見できました。 閉会式では入賞者にヒーローインタビューを行ってロジックを共有してもらいました。 仮説を立ててロジックを組む方、機械学習を駆使して解く方、ドメイン知識をフル活用して驚異の正答率を出す方など、非常に内容の濃い面白い内容でした。コンペの詳しい内容についてはまたどこかでご紹介できればと思います。 最後はちゃんとサービス終了のお知らせを出してコンペは終了しました。 終わりに 社内で分析コンペを開くと社内の集合知が使えるのはもちろん、テーマの背景業務に対する社内理解が広がる、育成に繋がるなど色々とよいことがあります。コンペを開きたいけど正解データが手元になくて困ってる方、本記事が参考となれば幸いです。
アバター
はじめに こんにちは。イノベーションセンターテクノロジー部門の齋藤と申します。普段はコンピュータビジョンの技術開発やAI/MLシステムの検証に取り組んでいます。今回は、モバイル向けの推論フレームワークのncnnに触れてみたので、その結果について書いて行きます。 ncnnとは ncnn 1 とは、モバイル向けの推論フレームワークでAndroidとiOSにどちらも対応しています。Pytorchの場合モデルは、pthの形式で1つのファイルで構成されています。ncnnの場合モデルは、param(モデル構造)とbin(重み)ファイルに分割されています。 自身のncnnを使用するモチベーションは、ncnnのデータフォーマットにあります。モバイルで使用するフレームワークにTensorFlow Lite 2 がありますが、他のフレームワークからモデルを変換するためにNCHW形式からNHWC形式に変換する必要があります。ONNX 3 からTensorFlow 4 に変換する際、 Conv layerが余計に追加される問題 があるなど、うまく変換されないケースもあります。 https://github.com/onnx/onnx-tensorflow/issues/754#issuecomment-801775203 https://github.com/onnx/onnx-tensorflow/issues/782#issuecomment-1317208239 現在では、先程あげた例は上記リンクのように問題が解消されていると思います。しかし、モデルの変換をストレートにできた方が、変換されないケースに時間を使うことがないのではないかと考えています。 ncnnを使用することが推論速度やメモリの使用量の性能面で良さそうか考えた時に、MXNet 5 、ncnn、ONNX、および OpenVINO 6 の各フレームワークの性能を比較している ブログ がありました。このブログでは、Intel CPUを使用した場合ncnnよりもonnxを使用した方が推論時間やメモリ性能上、上回っていることを述べています。特にメモリの使用量に関して、ncnnがONNXと比較して5倍ほど消費してしまうことを 問題視 していましたが、以下で解消されるようです。 i dont know is it to late to answer this issue,i met zhe same situation as you. it is beacuse that ncnn have some acceleration algorithm witch consum more momery,if you want to reduce the usage you can set opt.use_winograd_convolution as false. In my project,r101 just uses about 450mb 推論速度やメモリの使用量をみるとONNXの方が良さそうに思えます。ただ、このベンチマークが各フレームワークの条件を揃えるためにfp32を使用していると仮定をすれば、fp16にすることにより推論速度の高速化は可能かと思われます。これらの理由により、今回はncnnを使いたいと思います。次は、ONNX -> ncnnによるモデルの変換をしていきます。 補足 N – バッチ サイズ C – 特徴マップの数 (チャネル数) H – 高さ W – 幅 onnx2ncnn 今回は ncnnのリポジトリ 内にあるONNXからncnnに変換するコードを実行しました。 onnx-simplifierの web版 もありそこでも変換できるみたいです。 cd tools/onnx onnx2ncnn input.onnx output.param output.bin ONNXのモデルの最適化をしてみたのですが、中をNetron 7 で確認をしたところ構造があまり変わっていないように見えたので今回は省きます。 Androidの環境構築 今回使用するモバイル開発に必要なツール群は主に以下となります。 Android NDK 8 ncnn Android NDK(Native Development Kit) Android NDKとは、AndroidでCやC++のコードを使用可能にするツールです。今回はC++をアプリで使っているため、利用しています。この URLからNDKをダウンロード 可能です。 unzip android-ndk-r25b-linux.zip cd android-ndk-r25b export NDK_PATH=${PWD} ncnn 実行方法には自分の手元でbuildする方法とbuild済みのものをダウンロードする方法がする方法があります。 https://github.com/Tencent/ncnn/releases/tag/20220729 。今回は自身の手元でbuildする方法を試したため以下のような手順を取りました。 git clone -b 20221128 https://github.com/Tencent/ncnn.git cd ncnn git submodule update --init export NCNN_DIR=${PWD} export ANDROID_ABI=arm64-v8a # ANDROID_PLATFORMは、Android13の場合33、Android12の場合31 export ANDROID_PLATFORM=33 # Vulkan: https://developer.android.com/ndk/guides/graphics export VULKAN_SDK=$(pwd)/1.2.189.0/x86_64 export LD_LIBRARY_PATH=${NCNN_DIR}/build/install/lib/:$LD_LIBRARY_PATH mkdir -p build_${ANDROID_ABI} cd build_${ANDROID_ABI} # ANDROID_PLATFORM # cmakeのversionは3.23で実行 cmake -DCMAKE_TOOLCHAIN_FILE=${NDK_PATH}/build/cmake/android.toolchain.cmake -DANDROID_ABI="${ANDROID_ABI}" -DANDROID_PLATFORM=android-${ANDROID_PLATFORM} -DNCNN_VULKAN=ON -DNCNN_DISABLE_EXCEPTION=OFF -DNCNN_DISABLE_RTTI=OFF .. make -j$(nproc) install ここでビルドしたものもしくは、ダウンロードしたものをCMakeLists.txtにパスを記述しビルドをさせます。 今回はncnnの サンプルアプリ が出ていたので、それをベースに作成しています。 動作結果 実行環境は以下となります。 Pixel6 Pro プロセッサは、Google Tensor(CPU: ARM_Coretex-X1 , Architecture: armv8.2 a) 今回はcoco test 2017 9 画像の20枚使用し、推論速度の比較をしました。結果は以下の表のようになりました。モデルは、yolov5s 10 とfp16化したものを利用しております。 モデル  1枚あたりの平均推論時間 (ms)   yolov5s 82.085 yolov5s fp16 81.488 20枚の画像を使用した結果ではfp16化によってほぼ速度は変わらないような結果となっております。単純に速度が2倍になると考えておりましたが、今回動かした環境ではその恩恵が得られていないように思えるので、調査をしたいです。 終わりに 今回はncnnの実行環境の構築とモデルの動作をさせました。実行環境によって推論時間が異なりますが、 mmdeploynのベンチマーク のSnapDragon888の推論時間をみると近い値になっていることから、今回動かしたncnnの推論速度は違和感がないかなと思います。ONNXとncnnのモデルを比較していないため、ONNXと比較したncnnの良い部分を実感しにくいものとなってしまったため、今後調査してみたいです。 それでは、明日の投稿もお楽しみに。 https://github.com/Tencent/ncnn ↩ https://www.tensorflow.org/lite ↩ https://onnx.ai/ ↩ https://www.tensorflow.org ↩ https://mxnet.apache.org/ ↩ https://www.intel.co.jp/content/www/jp/ja/internet-of-things/openvino-toolkit.html ↩   https://netron.app/ ↩ https://developer.android.com/ndk?hl=ja ↩ https://cocodataset.org/#home ↩ https://github.com/ultralytics/yolov5 ↩
アバター
この記事は、 NTT Communications Advent Calendar 2022 16日目の記事です。 はじめに こんにちは、イノベーションセンター テクノロジー部門の池田です。 普段は SkyWay というプラットフォームを開発しています。 この記事では、GitHub ActionsからGoogle Cloud Platform(以下GCP)のCloud FunctionsにPipenvを利用したPythonアプリケーションをデプロイした際の話をGitHubのEnvironmentsなどに触れつつ紹介したいと思います。 モチベーション SkyWayで使うPythonのアプリケーションをクラウド上にデプロイしたかったのですが、毎度手動でデプロイするのはもちろん面倒です。 また、自動化した場合でもproduction,stagingなどの環境ごとに条件分岐を書いたり、意図しない自動デプロイが発生したりする可能性もあります。 これらの問題をEnvironmentsを使うことで解消できそうだったため導入しました。 行ったこと Environmentsの利用 Environments はGithub Actionsの機能で、それぞれの環境に対して別々の設定をできます。 例えば、production環境向けでは GCS_BUCKET_PRODUCTION という環境変数の値のバケットに、staging環境向けでは GCS_BUCKET_STAGING という環境変数の値のバケットにファイルをアップロードするということを行っていた場合、 それら接尾辞をつけた2つの環境変数を登録して条件分岐して利用するなどが必要でした。 しかし、Environmentsを利用することで環境毎に GCS_BUCKET へ別の値を入れて利用することで、不要な情報を減らして管理や開発ができます。 また、デプロイ前に承認を必須とすることが可能で、上記の不意のデプロイを防いだりデプロイ権限を持つ人の承認を得るというプロセスを構築したりできます。 デプロイ元のブランチも制限できますが正直なところyaml内でブランチを列挙するのと比較してどういった嬉しさがあるかは理解できていません。 一点、フリープランではプライベートリポジトリでのEnvironmentsが利用不能なのでご注意ください。 Environmentsの作成 Environmentsの設定にはリポジトリ毎のSettingsの左のメニューからアクセスできます。 Required reviewers を設定することで、実行する前にレビュアーの誰かに承認されることを必須とできます。誰が承認したかは各ワークフローの詳細ページから確認可能です。 環境毎のSecretは左のメニューのSecretsからではなくこちらのページの下のところで設定します。 環境間のSecretは名前が重複しても大丈夫ですし、むしろ同名の方が使う時の利便性が高いです。 GCPのサービスアカウントの生成&IAMの設定 GitHub ActionsからCloud Functionsに関数をデプロイするには、デプロイ用と関数実行用の2種類のサービスアカウントを用意する必要があります。 デプロイ及び実行に必要なすべての権限を持たせたサービスアカウントを共用することも仕組み上は可能ですが、デプロイ時・関数実行時共に余分な権限を持った状態で動作させることになるのでおすすめしません。 Cloud Functionsへデプロイする用のサービスアカウントでは Googleの提供するActionsのドキュメント にあるような以下の権限が必要になります。 Cloud Functions 管理者 サービス アカウント ユーザー Cloud Functionsを実行する用のサービスアカウントについては実行される関数の機能に基づいて設定する必要があります。 例えば以下で指定する様にSecret Manager内の値を使うのであればそれらへのアクセス権限を付与する必要があります。 同様にBigQueryやCloud Storageなどのサービスにアクセスするならそれらへの権限が必要です。 Secret Managerへの値の追加 特に機密性の高い情報を扱う場合は、GCPのSecret Managerを利用することがあるかと思います。 これを用いるとGitHub上に情報を置かず、Cloud Functionsの環境変数からも見えない値を利用できる様になります。 Secret Managerでのシークレットの作成については説明を省略しますが、作成後に概要タブにあるリソースID 1 は後で利用するのでメモしておくと良いです。 ワークフローを書く ここまででデプロイに必要な設定の準備をおこなったので、GitHub Actionsで実行するワークフローを設定していきます。 タイムアウトの設定 この用途に限りませんがタイムアウトの設定は重要です。 デフォルトでは 6時間 に設定されているため、知らないうちにタスクが実行されたままだと時間の枠を消費してしまいますし、Self-hosted runnerの場合は他のジョブがブロックされてしまいます。 今回は通常時だと6分ほどでデプロイが終わるので余裕を持たせて10分で設定しました。 これは timeout-minutes: 10 のように設定するだけです。 Environmentの設定 導入部分で環境による条件分岐が不要になると書きましたが、1回だけ条件文を書く必要があります。それがどの環境を使うのかの宣言部分です。しかし、environmentではifを使うことができません。 そのため、三項演算子で条件分岐する方法を参考 2 , 3 にしました。具体的には以下の様な形です。 このようにすると、mainブランチではproduction環境として、それ以外ではstaging環境として動きます。 environment : name : ${{ (github.ref == 'refs/heads/main' && 'production' ) || 'staging' }} requirements.txtの生成 Cloud Functionsへのデプロイ時にはCloud Buildが利用されており、その中で buildpacks を利用しているようです。 必要な依存情報の収集には requirements.txt を利用しているようで、このファイルが無い状態でデプロイしようとすると、デプロイには成功しますが実行時にモジュールが無いためエラーが発生します。 Pipenvで requirements.txt を生成するには pipenv requirements > requirements.txt を実行すれば生成できます。 requirements.txt は他のファイルと同様にリポジトリ内に置いてバージョン管理しても良いですが、 Pipfile や Pipfile.lock との二重管理になったり更新漏れの可能性があったりするためCIの中で一時的に作成するのが良いと思います。 requirements.txt とは関連しませんが、2022/12/12時点においてCloud FunctionsではPython 3.11には対応していないため、 Pipfile で python_version = "3.11" の様な設定をしているとデプロイ後の環境と差分が生まれるので注意が必要です。 GitHub Actionsの認証 google-github-actions/auth を用いてgcloudコマンドをGitHub Actionsから実行できるようにします。 色々と認証情報を渡す方法はありますが、JSON形式のサービスアカウントの鍵を用いる場合、 credentials_json にJSONを渡す必要があります。 この値はyaml内に直接埋め込むわけにはいかないので環境毎のSecretに登録しておきます。 デプロイ GitHub ActionsからCloud Functionsにデプロイするには大きく2つの方法があります。 1つは google-github-actions/setup-gcloud を用いてgcloudコマンドを利用できる様にした後、gcloudを次のステップで叩くという方法です。 もう1つは google-github-actions/deploy-cloud-functions を用いてgcloudコマンドを使わずにデプロイする方法です。 前者はコマンドを構築する必要があり、後者は対応していない引数 4 があるなどどちらも一長一短です。 以下は設定可能な項目で重要だと思った部分です。 キーは google-github-actions/deploy-cloud-functions のものを利用しています。 service_account_email 上で設定したCloud Functionsを実行する用のサービスアカウントの情報を渡すためのオプションです。 gcloudコマンドでは --service-account となっており紛らわしいですが、ここで渡すのはメールアドレスの文字列でありJSONの鍵ではないことに注意が必要です。 env_vars Cloud Functionsに環境変数として渡される値を KEY1=VALUE1,KEY2=VALUE2 のように渡します。 google-github-actions/deploy-cloud-functions では --set-env-vars 相当の動作しかできず、このオプションの有無に関わらず既存の環境変数は消えます。 gcloudコマンドでは --update-env-vars を使うことで既存の環境変数を残したまま一部に対して更新や追加が可能です。 secret_environment_variables 'API_KEY=projects/xxxx/secrets/yyyy/5' のようにSecret Managerのリソースへの参照を渡します。 gcloudコマンドでいう --set-secrets 相当です。gcloudコマンドとは異なりバージョンを指定しなかった場合にエラーにはならずにlatestとして扱ってくれます。 gcloudコマンドでは --update-secrets が環境変数と同様に存在します。 おわりに 本記事ではGitHub ActionsからCloud FunctionsにPipenvを利用したPythonアプリケーションをデプロイする方法について紹介しました。 Python以外のアプリケーションだったりCloud Functions以外のデプロイ先だったりに対しても活かせる部分があったのではないかと思います。 この記事の内容がより良い自動化につながると嬉しいです。 今回デプロイしたリポジトリは社内のGitHub Enterprise以下のOrganizationを利用しました。 社内のGitHub Enterpriseの詳細については こちら をご覧ください。 projects/xxxx/secrets/yyyy の様な形式の文字列です。 ↩ https://qiita.com/technote-space/items/cbeed6ddd0488499afaa ↩ https://dev.classmethod.jp/articles/github-actions-get-only-necessary-secrets-according-to-branch-name-in-ternary-operator-like-description/ ↩ たとえば第二世代の関数をデプロイできません。 ↩
アバター
この記事は、 NTT Communications Advent Calendar 2022 15日目の記事です。 2022/12/16 追記 想像以上に反響がありましたので、追記します。 「エンジニアのわがまま」発言について そのような発言が出たのは、エンジニア側とシステム担当が互いに本音をぶつけ合ったからこそでした。 限られた時間枠の中でエンジニア側から畳みかけるように数多くの問題意識や要望をシステム担当側に突きつけるような形となり、双方ヒートアップした結果としてそのような発言につながっていました。 また、システム担当からすると下記の事実もヒートアップにつながる一因だったと思います。 新しい事務用 PC のリリースをやり遂げた直後で、利用する社員から「以前より便利になった」との声も出ていたタイミングだった 事務用 PC と開発・検証用 PC の 2 台持ちが必要なのはエンジニアが多く、その対象が全社員ではないこと しかし、このイベントと今回紹介した取り組みを契機にシステム担当とエンジニアの間の風通しは格段と良くなり、エンジニア側としてもシステム担当が抱える思いや背景事情、価値観への理解が進みました。 システム担当側もエンジニアのペインに対する解像度が上がるとともに、エンジニアを頼ってくれるようになり、今では新しい技術の導入に際してエンジニアに相談が来ることも少なくありません。 本音をぶつけ合い、双方の価値観をすり合わせながら知恵を出し合った結果が今回の新しい開発・検証用 PC につながっています。 はじめに みなさんこんにちは、イノベーションセンターの @Mahito です。 普段は社内のエンジニアが働きやすくなることを目標に、コーポレートエンジニアやエンジニア向けイベントの企画・運営をしています。 今回はこの夏社内でリリースされたエンジニア向けの開発・検証用 PC について、私や社内のエンジニアたちがどう関わったかのかを少しご紹介したいと思います。 NTT Com では元々、会社支給の事務用 PC でメールを始めとする社内システムへのアクセスなどの業務を行い、開発・検証する場合は各部や開発チームなどが個別に管理する PC を利用していました。そのため、社内のエンジニアは事務用 PC と 開発・検証用 PC を 1 日の中で何度も往復する手間がありました。また、セキュリティ的にも各部やチームごとに対応がバラバラで、問題があった際に会社として統一的な対応が難しい状況にありました。 今回リリースした開発・検証用 PC では、社内のエンジニア有志がシステム担当やセキュリティ担当と話し合いをしながら、会社で求められる様々な要件を技術的に解決し、会社として統一的なセキュリティ対策を施しました。これにより、開発・検証用 PC からメールなど社内の主要なリソースへアクセスできるようになり、エンジニアが何度も PC を往復することなく一日の業務をほぼ済ますことができるようになりました。 きっかけ エンジニアが何度も PC を往復する状況を改善するきっかけは情報システムを担当する部署とエンジニアの話し合いでした。私が主催する NTT グループのエンジニア向けイベントにおいて、NTT グループ各社のシステム担当と、エンジニアがディスカッションする企画を実施しました。 NTT Com のエンジニア側からは、先に述べたように事務用 PC と開発・検証用 PC との往復が手間なため、会社として開発・検証用 PC からメールなどを見られるようにしてもらいたいという要望が上がりました。その場では開発・検証用 PC として MacBook を利用しているユーザが多く、会社として Mac を業務で使えるように認めて欲しいと言う声もありました。しかし、当時のシステム担当からは「それはエンジニアのわがまま」と言われ、その日に結論が出ることはありませんでした。 (議論するエンジニアとシステム担当) 議論には当時の経営層が数名参加していたこともあり、改めて私を含む社内のエンジニア、システム担当、セキュリティ担当とで話す場が設けられました。そこでは改めて開発・検証用 PC からメールなどにアクセスできるようにすることはわがままではなく、業務効率につながる改善だという話をしました。セキュリティ担当からも現状の開発・検証用 PC のセキュリティ的な課題を解決するきっかけにつながるとの援護もあり、「事務用 PC と同等のセキュリティが実現できるのであれば社内のリソースへのアクセスを認める」という条件が出されました。 わがままを技術で実現 セキュリティの要件の中で一番大きかったものは個人情報の漏洩対策です。 従来の開発・検証用 PC では社内のリソースにはアクセスできないため、個人情報などへのアクセスはありませんでした。しかし、メールなどの社内リソースにアクセスができるようになった場合、個人情報にアクセスする可能性があるため、個人情報の漏洩対策が必要となりました。 具体的には、 個人情報保護法施行規則 7条(個人の権利利益を害するおそれが大きいもの)の第1号においてカッコ書きの中に「高度な暗号化その他の個人の権利利益を保護するために必要な措置を講じたものを除く」という記述があり、この対応が必要となりました。 対応には 「個人情報の保護に関する法律についてのガイドライン」及び 「個人データの漏えい等の事案が発生した場合等の対応について」 に関する Q&A(抜粋) で、以下の1と2または3のいずれかの要件を満たすことが必要とされています。 暗号化した情報と復号鍵を分離するとともに復号鍵自体の漏えいを防止する適切な措置を講じていること 遠隔操作により暗号化された情報若しくは復号鍵を削除する機能を備えていること 第三者が復号鍵を行使できないように設計されていること 事務用 PC は Windows であったため、Windows で要件を満たす方法はすでにわかっていましたが、Mac ではまだ社内のノウハウがない状態でした。そこで我々は社内のエンジニア有志で情報を集め、検証環境を用意し、モブプログラミングならぬモブ設定会などを通じてノウハウを貯めていきました。 システム担当との話し合いから2ヶ月ほど経つ頃には、上記の要件を Apple T2 セキュリティチップ , FileVault 2 , Microsoft Intune などを利用することで技術的に満たせると確信を得ました。それに加え、情報システム担当・セキュリティ担当から出された追加の要件にも対応し、その翌月には新しい開発・検証用 PC のトライアルを社内の一部で開始しました。 セキュリティと柔軟な開発・検証業務の両立 新しい開発・検証用 PC では上記のように会社として求められるセキュリティの要件を満たしていきました。一方で、事務用 PC と完全に同等のセキュリティ要件で PC の設定やソフトウェアをガチガチに固めてしまうと、利用者の使い勝手のみならず開発・検証用途に求められる機能を損なう恐れがありました。 そのため、利用者が自由にソフトウェアや OS の設定をできるようにしつつ、必要なセキュリティを確保するために、下図のように管理範囲を明確にしました。 (新開発・検証用 PC と従来 PC との管理範囲比較) 上図で示すように、ハードウェアセキュリティ、ウイルス対策と検知、そして一部の OS の設定ポリシーをシステム担当側で管理し、それ以外を利用者に管理してもらいます。システム担当側では以下のような制限や設定を施していますが、利用者がこれらを意識することはほぼありません。 社内リソースへのアクセスは登録済みの PC に限定(端末制限) PC のポリシーチェック(パスワードポリシー、OS バージョン、その他設定など) EDR/AV の自動配布と全アクティビティの監視・異常検知 etc... これにより、PC へのソフトウェアのインストールなどの自由は残しつつ、何かあった際に会社側で検知・追跡・確認ができるようになっています。 社内の反応 トライアル当初から従来だと事務用 PC からしかアクセスできなかった、メールや社内ポータル、Office などの会社リソースを開発・検証用 PC から使える様になった上、従来の開発・検証用 PC とほぼ変わらない使い勝手ということもあり利用者からは好評を得ています。特に、私を含む Mac で開発・検証作業をしていた人からは「ほぼ一日の作業を Mac で完結できて便利」との声が上がっています。 この夏の全社リリースでは従来より初期の利用申請や設定の手間が少し増えたこともあり多少の混乱はありましたが、現在は特に大きな問題もなく新しい開発・検証用 PC の利用者は社内で着実に増えていっています。 まとめ 元々は「エンジニアのわがまま」と言われたところから始まった話ですが、この夏無事に新しい開発・検証用 PC を社内でリリースするに至りました(トライアルの開始から色々紆余曲折を経てだいぶ時間がかかりましたけど)。 まだすべての社内システムにアクセスできないことや、Linux デスクトップユーザのサポートが出来ていないなどの課題もあります。しかし、現在は最初の頃と異なり、情報システム担当やセキュリティ担当と話し合いをしながら課題解決に取り組みやすい状態にあります。この状態を活かして、今後も引き続き情報システム担当やセキュリティ担当、社内のエンジニアたちと協力をしながらエンジニアが働きやすくなる環境を目指して、残る課題の解決に当たっていきます。 以上で、 NTT Communications Advent Calendar 2022 15日目の記事は終わりです。 それでは、明日もお楽しみに!
アバター
この記事は、 NTT Communications Advent Calendar 2022 14日目の記事です。 はじめに 皆様こんにちは。イノベーションセンター所属の @sublimer です。 普段はWebRTCプラットフォーム 「SkyWay」 の開発・運用の業務に取り組んでおり、現在は新しいSkyWayの正式リリースに向けて、インフラ・バックエンド・フロントエンドのコードをガリガリ書く楽しい日々を送っています。 一方のプライベートでは、自宅Kubernetesクラスターを盆栽のごとく愛情を持って育てています。 今回は、新しいSkyWay正式リリースに向けて、TURNサーバー(P2P通信においてNAT越えのために使用されるサーバー)の負荷試験を行った際に得られた知見をご紹介します。 ⚠️ 注意 この記事では、負荷試験の実施方法について書いています。 負荷試験は、管理下にないシステムやサービスに対して行った場合は、DoS攻撃とみなされ罪に問われる可能性があります。 また、利用しているクラウドサービスが負荷試験を認めていない場合もあります。 もし負荷試験を実施する際は、十分に注意して実施するようにしてください。 今回の負荷試験についても、問題が生じないよう十分検討してから実施しています。 TURNサーバーの負荷試験の必要性 冒頭でも述べた通り、TURNサーバーはP2P通信においてNAT越えのために使用されます。 クライアントと通信先の間に位置してデータを中継することで、NAT越えを実現するのです。 例えばWebRTCを使ってP2Pで通信する際に、間にNATが入っていて直接通信できない場合でも、TURNサーバーがデータを中継することでNATを越えて通信できます。 このように、TURNサーバーは通信を中継する役割を担っているため、大量のトラフィックをさばく高い性能が求められます。 そのため、サービスリリース前に負荷試験を行い、どのくらいの負荷までならサービスに影響が出ないのか、想定通りにスケールするのかを確認しておくことが重要です。 試験方法の検討 プロトコルとしてHTTPを用いるケースであれば、 k6 、 Locust といったツールや、 k6 Cloud 、 Azure Load Testing といったサービスを用いることで、比較的容易に負荷試験を始めることができます。 しかし、TURNのプロトコルはHTTPほど一般的なものではないため、負荷試験の方法を検討するところからはじめました。 今回検討した方法は、以下の2つです。 turnutils_uclientを用いる ヘッドレスブラウザを用いる 2つを比較検討した結果、今回はturnutils_uclientを用いる方法を採用しました。以下で詳細を説明します。 まずturnutils_uclientについてです。 turnutils_uclient は、OSSのTURNサーバー、 「coturn」 に付属している、TURNのテスト用のCLIクライアントです。 turnutils_uclientを使う利点としては、様々なコマンドラインオプションがあるため、負荷試験の際のトラフィックを柔軟に制御できるという点が挙げられます。 またCLIツールであるため、動作のオーバーヘッドが比較的小さく、出力された試験結果の集計も容易にできます。 一方で、目的のトラフィックを発生させるためのオプションの指定がやや複雑であるという課題があります。 次にヘッドレスブラウザについてです。ヘッドレスブラウザは、GUIが無いブラウザのことで、 Puppeteer や Playwright が有名です。 予めWebRTCで通信するWebアプリケーションを作り、それをヘッドレスブラウザ上で動かすことで、テスト用のクライアントとして使うことができます。 ヘッドレスブラウザを使う利点としては、ブラウザ経由でWebRTCの通信ができるため、エンドユーザーの使い方に、より近い状況を再現して負荷試験ができるという点が挙げられます。 一方で、クライアント毎にブラウザを起動することになるため、負荷試験のように大量のクライアントを生成する際は、比較的大きなオーバーヘッドが生じるという課題があります。 両者を比較した結果、今回はturnutils_uclientを用いた方法を採用しました。 ヘッドレスブラウザを用いた方法の場合、ヘッドレスブラウザを制御するアプリケーションと試験用のWebアプリケーションの両方を作る必要があり、実装のためのコストがやや大きくなると想定されたためです。 turnutils_uclientの「オプションの指定がやや複雑である」という課題は、後述する負荷試験用のスクリプトを作成することで解決しました。 ここで、「WebRTCの通信を実際に行うヘッドレスブラウザの方が、より現実に即した試験が行えるのでは?」と思われた方もいるかもしれません。 もちろん、ヘッドレスブラウザを使った方が、よりエンドユーザーの使い方に近い状況で試験が行なえます。 ただ、今回はトラフィックが増加した際の負荷についての試験が目的ですので、指定しただけのトラフィックを発生させられれば問題ありません。 また、TURNサーバーは、中継しているデータがWebRTCの通信によるもなのかどうかに関わらず、単なるバイナリのデータとして中継しています。 以上を踏まえ、turnutils_uclientによって生成したトラフィックで試験を実施しても、問題は無いと判断しました。 試験観点の検討 今回は、以下の5つの観点について負荷試験を実施することとしました。 負荷を線形に増加させた際の挙動の確認 TURNのプロトコルごとの比較 新規接続のスパイクの影響の調査 VMスペックごとの比較 スケールアウト時の挙動の確認 それぞれの試験観点について、なぜ試験が必要なのかを簡単に説明します。 1. 負荷を線形に増加させた際の挙動の確認 TURNサーバーがさばけるトラフィックの限界値を調べるために実施します。 限界値が分かれば、その値に合わせて監視システムのアラート条件を決めたり、スケールアウトさせる基準を決めることができます。 2. TURNのプロトコルごとの比較 クライアント・サーバー間の通信で用いられるプロトコルごとの、特性を調べるために実施します。 TURNは、以下の3通りのプロトコルでクライアント・サーバー間の通信ができます。 TURN-UDP TURN-TCP TURN-TLS どのプロトコルが使われるかは動的に決まるケースがほとんどであるため、予め特性を把握しておく必要があります。 ちなみに、TURNと中継先のクライアントの間の通信は、WebRTCにおいてはUDPのみが用いられます。 3. 新規接続のスパイクの影響の調査 サービスを提供する際に、事前にエンドユーザーの使い方を予測するのは困難であるため、どの程度の新規接続のスパイクであればさばけるのかを調べるために実施します。 スパイクはある程度のトラフィックをさばいている状態で突然発生するため、定常状態として一定量のトラフィックを流した状態で試験を実施します。 4. VMスペックごとの比較 VMのスペックによってさばけるトラフィックは異なるため、どのスペックのVMを使えば最も費用対効果が高くなるのかを調べるために実施します。 5. スケールアウト時の挙動の確認 負荷が高くなった際にスケールアウトを実施して、きちんとスケールするかどうかを検証するために実施します。 TURNサーバーは1台1台が独立して動くため、基本的には台数を増やしてあげるだけでスケールします。 また、上記の5つの試験観点に加えてWebRTCのループバックの通信をTURN経由で行い、目視、及びwebrtc-internalsの値を確認してWebRTCの通信品質が大きく低下していないかを確認することとしました。 ループバックの通信では、ストップウォッチの画面を画面共有することで、簡易的にRTTの計測を実施しました。 正確なRTTの値を確認する際は、webrtc-internalsに表示される remote-inbound-rtp の roundTripTime 1 の値を確認しました。 負荷試験用のスクリプトの実装 turnutils_uclientはTURNのテスト用クライアントとして汎用的に使えるようになっているため、負荷試験用に用いる際にはオプションの指定について工夫が必要になります。 ここで、turnutils_uclientのオプションについて、いくつか抜粋して簡単に説明します。 -l : TURNのメッセージのサイズをbyte単位で指定します。 -n : TURNのメッセージの送信回数を指定します。 -z : TURNのメッセージの送信間隔をミリ秒単位で指定します。 -t : TURNサーバーとの間の通信をTCPで行います。 -S : TURNサーバーとの間の通信を暗号化します。 -t と組み合わせることで、TURN-TLSの通信になります。 このように、turnutils_uclientにはトラフィックの帯域幅や通信する時間を指定するオプションはありません。 ですので、「 bpsの通信を 秒間行う」という条件を指定したい場合は、メッセージの送信回数や送信間隔を計算してからオプションとして渡してあげる必要があります。 送信回数を 、送信間隔を msとすると、以下の計算式で と から と を求めることができます。 今回は、TURNのメッセージのサイズは1024 byteで固定としています。 したがって、例えば1 Mbps(1,000,000 bps)の通信を60秒間流す場合は、 、 msとなります。 今回負荷試験を実施するにあたって、上記の方法で値を計算しturnutils_uclientのオプションとして指定するためのスクリプトを、TypeScriptで実装しました。 試験用のスクリプトは、指定されたパラメーターを元にturnutils_uclientのオプションを計算し、 Node.jsの child_process 2 モジュールを使って、turnutils_uclientを子プロセスとして大量に起動する仕組みになっています。 今回負荷試験に用いた試験用のスクリプトには、オプションの値の計算に加えて、以下のような機能も実装しました。 試験の各パラメーターをコマンドライン引数として指定できる機能 TURNの認証情報を、APIから取得する機能 試験結果をJSONファイルとして出力する機能 複数の試験結果のJSONファイルを集計し、CSVファイルとして出力する機能 今回は、条件を変えて繰り返し試験をする必要があったため、試験の条件をコマンドライン引数として設定できるようにしました。 また、複数の試験結果を集計してCSVファイルとして出力することで、容易にグラフを作成できるようにしました。 負荷試験の実施 負荷試験を実施する前に、以下の2点の確認を実施しました。 負荷試験用のスクリプトが想定通りのトラフィックを生成できているか 負荷をかける側のVMが飽和状態になっていないか スクリプト内で行ってるパラメーターの計算処理が本当に正しいのかを、実際に試験用のトラフィックを生成して、そのトラフィックを計測して確認しました。 計測にはdstatコマンドとvnstatコマンドを使用し、負荷をかける側とかけられる側の両方のVMでトラフィックを計測しました。 その結果、想定通りのトラフィックを生成できていることが確認できました。 また、負荷をかける側がボトルネックとなり試験結果に影響することが無いよう、負荷をかける側のVMのメトリクスをチェックしました。 その結果を踏まえ、負荷をかける側のVMのスペックを設定しました。 実際の負荷試験では、予め検討した試験用パラメーターを指定してスクリプトを実行し、その結果をJSONファイルとして出力しました。 試験後は、出力されたJSONファイルを元に集計処理を行い、複数の試験結果をCSVファイルに出力した後、Google SpreadsheetにCSVファイルをインポートしてグラフ化を行いました。 最後に、試験結果やグラフをまとめ、負荷試験のレポートを作成しました。 この流れで、前述した5つの観点について試験を実施しました。 負荷試験の結果 今回は、「負荷を線形に増加させた際の挙動の確認」の観点についての試験結果を、抜粋してご紹介します。 トラフィック量を増やして繰り返し試験を実施した際のCPU Idleと送信トラフィックの値は、それぞれ以下のグラフのようになりました。 (上段:CPU Idle 下段:送信トラフィック) トラフィックが増えるに従ってCPU使用率も上昇していることが見て取れます。 また、CPU使用率がほぼ100%で張り付くと、ネットワークトラフィックも頭打ちとなり、性能が飽和状態になっていることが分かります。 このときの通信のRTTやパケロス率、jitterといった品質を示す指標は、いずれも非常に悪化していました。 加えて、TURN経由で行っているWebRTCのループバックの通信の映像にもカクつきが生じており、性能が上限に達していたことが分かりました。 この結果より、TURNサーバーはトラフィックの増加とともに負荷も高くなるものの、CPU使用率が張り付くまではトラフィックをさばける事が分かりました。 他の4つの観点についても試験し、TURNサーバーに性能面で大きな問題はないことが分かりました。 ところで、今回試験を実施する中で、turnutils_uclientには「標準出力にログを二重に出力してしまう」という意図しない挙動があることに気づきました。 折角の機会だと思い、この挙動を修正するプルリクエストを出してみました。 github.com OSSにプルリクエストを出した経験はあまりないのですが、すぐにマージしていただけて、貴重な経験ができたかなと思います。 おわりに TURNの負荷試験はインターネット上に公開されているノウハウがHTTPほどは無いため、手探りで進めた部分もありましたが、無事に試験を終えることができました。 今回の試験を通して、以下のような知見を得ることができました。 turnutils_uclientの各オプションの役割 負荷試験で確認すべき観点 TypeScriptでのツール開発の流れ 昨年、自分でTURNサーバーを実装してみた ことに加え、今回turnutils_uclientを用いて負荷試験を行い、TURNについての理解がより深まったかなと思います。 負荷試験を実施した後、今回の負荷試験で得られたデータを元に、TURNサーバーの台数やVMのスペックなどの検討を実施しました。 お客様に安定して利用していただけるよう、十分なリソースを確保しましたので、正式にリリースされた際はぜひ使っていただければと思います。 「新しいSkyWayを今すぐに使ってみたい!!」という方向けに、 Beta版としてもリリース していますので、気になる方はこちらもどうぞ!! また、SkyWayの開発チームでは、現在インターンシップの参加者を募集しています。 以下のリンク先の、「ビデオ通話プラットフォーム「SkyWay」、低遅延ライブ配信プラットフォーム「Smart vLive」、テレプレゼンスロボット(遠隔操作ロボット)のいずれかの技術開発」に詳細な内容が記載されています。 information.nttdocomo-fresh.jp 締切は、なんと今日!! 12月14日 13:00ですので、少しでも興味がある方はお早めに応募してみてください!! それでは、明日もお楽しみに!! 参考サイト turnutils_uclient · coturn/coturn Wiki · GitHub https://www.w3.org/TR/webrtc-stats/#dom-rtcremoteinboundrtpstreamstats-roundtriptime ↩ https://nodejs.org/api/child_process.html ↩
アバター
この記事は、 NTT Communications Advent Calendar 2022 13日目の記事です。 はじめに こんにちは。 SDPF クラウド/サーバー 仮想サーバーチームの宮岸( @daiking1756 )です。 普段はOpenStackを使ってIaaSを裏側からお世話する仕事をしています。 この記事では 先日開催された第6回 NTT Agile MeetupのLightning Talkで話した「チーム定例の議事録を工夫した話」を元に、 当日は時間の関係で話せなかったチーム定例を改善するために行った3つのことを紹介していきます。 3行まとめ 最初に3行まとめです。 チームの定例のBefore Afterを書いておきます。 Before After 全ての議題を話し切るまで時間を定例を後ろに延長していた 後ろに予定を入れることで時間厳守 定例開始後に議題を決めることもあった 開始前に「議題の整理」を行うことで、短時間で濃い議論になった 各議題の完成の定義が曖昧であり、次回議事録を見た際に継続議論が必要なのかの判断が必要だった 各議題のステータスを管理することで話すべき議題の判断が容易になった なぜ定例にフォーカスしたのか パレートの法則(別名: 80:20の法則) という有名な法則があります。 文字通り、全体の数値の大部分は、全体を構成するうちの一部の要素が生み出しているという法則です。 プログラミングの文脈では「プログラムの処理にかかる時間の8割は、コード全体の2割の部分が占める」という話でよく用いられます。 つまり、コード全体の2割の部分は何度も繰り返し実行されることで(ループ処理や頻繁に呼ばれる関数など)、全体の実行時間の大部分を占めているということになります。 1 ここから、全体のパフォーマンスを改善させる1つの方針として、 繰り返し 実行される部分に着目することが大事ということが分かりますね。 そして「定例 = 繰り返し 行われる会議」なのです。 つまり、定例を改善することが、チームのパフォーマンス改善に大きく影響するのではないか?と思い、フォーカスすることにしました。 そもそもどんな定例をやっているのか 現在私のチームで行っている定例は大きく下記の3つです。 スクラムイベント(デイリースクラム、スプリントプランニング/レビュー/レトロスペクティブ) 2 企画チーム / 運用チーム / ベンダー との定例 サブチーム(チーム内で担当領域ごとに結成されているチーム)定例 このうち、影響範囲が少なく小さく始められる & 私の中で問題点が見えていた「サブチーム定例」を手始めに改善の対象としました。 定例がズルズル延長してしまう問題 サブチーム定例では、困っているタスクについて状況を確認したり、同期コミュニケーションの力でみんなの悩みを解決することを目的で行っています。 まず問題だと感じていたのは、 定例がズルズル後ろに延長していた ことです。 サブチームでの話題は参加者も少なく、議題のコンテキストも深く共有していることが多いため、深く長い議論になりがちです。 それ自体は全く問題ないのですが、時にはスコープ外の議論へ発展することもあり、設定した時間内で全ての議題を話し切ることができていませんでした。 他の定例の前に開催時間を変更 そこで、サブチーム定例をデイリースクラムの直前に行うようにしました。 これにより、デイリースクラムの時間になったら強制的にサブチーム定例を終了することになり、結果としてサブチーム定例がズルズル延長することは無くなりました。 ※ スクラムイベントは比較的時間通りに開始・終了できていたため、全体が遅延することもありません。 続けていると徐々にですが、参加者全員が時間制限を意識した定例を行うことが少しずつできるようになってきて、意識するだけでかなり変わるもんだなぁ(小並感)と思いました。 とはいえ、意識だけでは解決できない問題もありまして・・・。 話すべき内容が時間内に話しきれない 当たり前ですが、時間が短くなったことで、話すべき内容を時間内に話し切れないことが増えました。 また、その中には、優先度を上げて対応するべき内容が含まれていることもあり、このままではチームのアジリティが落ちてしまうと感じていました。 そこで、短時間の定例で 効果的に優先度の高い議題をカバー するために、 「議題の整理」 をしっかりすることにしました。 どんな風に「議題を整理」したのか 今までも事前に議題を議事録ページ 3 に書くことはありましたが、優先度やその議題のステータスが不明確なために、どの議題から話すべきかを決めるのに時間がかかっていました。 (それ故に、とりあえず上から話していき、時間が足りなくなっていました) そこで、各議題について下記の項目を整理した状態で、定例開始までに議事録ページに記載しておくようにしました。 項目は、よくあるMTGのベストプラクティスや、チームの状況を踏まえて、無理のない範囲で始められそうなものを えいや で選びました。 詳細はNTT Comが公開している リモートワークハンドブック をご参考下さい。 これらを議事録ページに反映させると下記の画像のようなイメージになります。 特に優先度とステータスの項目は、視覚的に分かりやすく色や絵文字をで表現しています。 チームの共通認識を作る 上記の「議題の整理」の効果を高めるために、 定例を運用する中で下記のルールをチームの共通認識として持つことにしました。 議題を記載する際は優先度順に記載する 優先度:高 以上の議題のステータスが定例中に✅へ遷移せず残ってしまった場合は、別途チーム内で話す時間を作り最速で✅へ遷移させる 読んでみると1つ1つは当たり前のことが書かれているのですが、一度書き起こして、ぶれない共通認識としておくことで、スムーズで効果的な定例が行われるになりました。 細かい話 繰り返し発生する定例の議事録は大きく2つの取り方があります。 新規と追記です。 自明ですが、一度書いておきます。 新規: 毎回新しい議事録ページを作成する 追記: 1つのページにどんどん追記していく サブチーム定例で多いのは新規と追記のハイブリッド型です。 基本的には追記していき、四半期に1回ほどのペースで新規ページにリニューアルしています。 また、サブチームでも四半期に一度は、立ち止まって仕事っぷりを振り返る時間を取っており、 振り返りの場で次の四半期にやることや目標を立てます。 この目標を定例ページの議事録の先頭に書いておくことで、チームや個人が目標を意識できるような効果も狙っています。 このあたりはまだまだ実験中の取り組みですが、いろいろ試しながら改善を続けています。 やってみての感想 手始めに自分が参加しているサブチーム定例について、いろいろ試行錯誤しながら改善活動を行っていきました。 すると隣のサブチームから「いい定例やってんね」「その議事録良さそうね」と声が掛かり、気づかないうちに他の定例にも布教されていることがありました。 また、たまに隣の定例を覗いてみると、そのチーム独自の文化やアレンジがされていることもあり、発見や学びが多くあります。 ドキュメント化されていないTipsや文化などの中にも重要なものは多いですが、その当事者たちの内側では当たり前になっていてその重要さや特殊さに気づかないことがありますよね。 内側で気づければよいのですが、これらを見出すのはその外側にいる人たちの役割なのかもしれませんね。 「越境はいいぞ」的な話は聞いたことがありましたが、今回の経験を通して越境の重要性を肌で感じることができました。 今後も積極的に越境していきます。 そして、ここまで読んでいて感じた方もいると思いますが、この記事中には定量的な話がほぼ出てきません。 定性的な話がメインでした。 それでもチームメンバーからのフィードバックにより、この取り組みに一定の効果があったことは確信しています。 しかし、定量的なデータも合わせて用意できていれば、更にこの取り組みの効果を多角的に分析できていたことでしょう。 また、そこから新たな改善ネタが見つかることもあるでしょう。 「問題の定量化と改善の計測」 大事。 特にこのようなブログ記事などで取り組みを紹介する際には、問題の定量化と改善の計測ができたら更に良かったと思っています。 計算機ではなく人間が絡む領域だと全てを定量化するのは難しくなりますが、今後はこの点も意識した改善活動を行っていきます。 おわりに いかがでしたでしょうか。 今回紹介した取り組みは私のチームではたまたまうまく機能していますが、決してベストプラクティスのようなものではありません。 もう少し厳格なルールや仕組みを作ったほうがいいチームもあれば、その逆もあるでしょう。 それを見極めるためには、チームをよく観察し、小さく試して小さな成功を積み上げていくことが大事だと思います。(私もまだまだ全然できていないですが) この記事の内容が少しでも皆さんのより良い定例ライフに繋がれば幸いです。 さて、NTT Comでは現在、現場受け入れ型インターンシップを募集しております。 なんと申し込み期限は12月14日(水)13:00迄まで! 残り時間僅かです。 様々なポジションで募集しておりますが、ぜひ仮想サーバーチームのインターンシップもチェックしてみて下さい。 engineers.ntt.com information.nttdocomo-fresh.jp それでは、明日の投稿もお楽しみに。 単にI/O待ちなどで処理に時間がかかる場合も勿論ありますが、ここでは割愛しています。 ↩ 定例という感じでは無いですが、繰り返し行うという意味で挙げています。 ↩ SaaS型のドキュメント作成ツールを使って作成することを想定しています。 ↩
アバター
この記事は、 NTT Communications Advent Calendar 2022 12日目の記事です。 はじめに はじめまして、クラウド&ネットワークサービス部でデータマネジメントに関するサービス企画・販売企画を担当している古志将樹です。 今回はNTT Comで提供しているデータマネジメント基盤である「インフォマティカソリューション」について簡単に触ってみたので、その操作方法などを紹介したいと思います。 データ活用が必要とされる背景、課題 インフォマティカソリューションの紹介をする前に、データ利活用を進めるにあたってよくぶつかる壁を説明します。 近年、DX/データ分析の動きが盛り上がっていますが、そのために使用するデータは1つに集まっていることはまれで、クラウド、オンプレなど各所に散在してることがよくあります。そのため、以下のような課題をDX/データ分析を実施する前に解決する必要があります。 アプリケーション変革期においてデータ移行・連携・同期コストが増大すること 散在するデータ資産の品質・信頼性・安全性の担保ができないこと 業務・顧客サービス・デジタル戦略へのデータ資産活用が困難であること これらを解決するのが今回ご紹介するデータマネジメント基盤であるインフォマティカソリューションになります。 NTT Comではこちらのサービスをお客様のDXを推進するプラットフォームである Smart Data Platform(SDPF) 上でご提供しております。 マッピングチュートリアル では、ここからはインフォマティカソリューションの1つのデータ統合の機能であるIntelligent Data Management Cloud(IDMC)について、簡単なチュートリアルをやってみたいと思います。 Intelligent Data Management Cloud(IDMC)について こちらは、データ流通の基本となる部分になります。 AシステムからBシステムにデータを連携する といった機能を持っており、オンプレ、クラウドの各所に点在しているシステムのデータを統合する時に必須となります。 主に ポッド と セキュアエージェント というシステムの2つから構成されており、コントローラーである ポッド は、データ処理に関わる命令を セキュアエージェント に送り セキュアエージェント でデータ処理を行っています。 そのため、実データはセキュアエージェントに送られるわけですが、お客様によっては、インターネット上に実データを通したくないといったご要望をお持ちのこともあります。その場合においては、こちらNTT Comの FIC や UNO を使い、閉域網を使用して安全に他システムや様々なクラウドと連携できます。 特徴としては以下になります。 開発時は 変換部品 をドラッグアンドドロップで繋げていき、実装していく流れで以下のようなものがあります。これらを使用してデータ連携できます。 データの結合する ジョイナ データのグループ化、例えば売り上げの集計などを行う際に利用する アグリゲータ データのフィルタリングを行う フィルタ JSONファイルなど半構造化データを構造化形式に変換できる 構造パーサー ソース、ターゲットシステムへの接続は コネクタ を利用することでGUI上が可能。SAP、ERP、oracle、各種クラウドサービスなどの基本的なシステムや、REST、SOAP、ODataに対応しているシステムであれば接続可能となっております。 マッピングの流れ こちらが今回実施するマッピングの流れとなります。 以下に添付している画像に記載の通りですが、FTPサーバーとセキュアエージェント上にある 売り上げ一覧 と 商品マスタ を商品コードをキーに結合し、いくつかのデータ加工して最終的にあらかじめ用意しているOracleのテーブル上に格納するというマッピングとなります。 このマッピングでは、 コネクタを使用して簡単にデータソース、ターゲットと接続可能 、 繋げたシステムのデータ加工方法 、 半構造データでもインテリジェント構造モデルを使用してデータ加工可能 といった点を知っていただければと思います。 今回、各種データをFTPサーバー、セキュアエージェントに置いていますが、これがSAPやERP上の場合も利用するコネクタが異なるだけでデータの加工における設定は基本的に同じなので、ぜひ見てみてください。また2022年10月24日現在、インフォマティカ社から 1か月の無償トライアル も出されているのでお時間ある方は触ってみてください。 具体的な流れとしては以下になります。 インテリジェント構造モデルを使ったJsonファイル(商品マスタ)のパターン抽出 今回使用する商品マスタのデータはJson形式のため読み込める形にします。 コネクタの設定 データソースとターゲットの設定をします。 「売り上げ一覧」と「商品マスタの結合」 売り上げ一覧には product_code 、 性別 、 年齢 、 ストアコード などがあり、商品マスタには product_code 、 商品名 、 食品名 、 食品のカテゴリ 、 値段 などがあります。ですので product_code をキーに商品マスタと売り上げ一覧のデータを結合します。 商品名変更 新しく product_Name というカラムを作り一部の商品名を“江戸前幕の内弁当”に変更します。 収益計算 新しく Revenue というカラムを作り、税込み価格と販売数のカラムから収益を計算してrevenueに格納します。 オラクルへの格納 最後にあらかじめ用意していたOracleのテーブルに作成したデータを格納します。 以下は今回使用するデータになります。 sales.csv:売り上げデータです。 store_code sales_date sales_time product_code amount payment_code gender age 0 2006/7/18 0:00:02 A00001 1 1 M 20 27001 2006/7/19 0:00:05 A00002 2 1 F 30 27002 2006/7/20 0:00:08 A00003 1 1 F 20 27003 2006/7/21 0:00:11 A00004 1 2 F 50 27004 2006/7/22 0:00:14 A00005 1 3 M 60 27005 2006/7/23 0:00:17 A00006 3 4 F 20 27006 2006/7/24 0:00:20 A00007 1 1 M 20 27007 2006/7/25 0:00:23 A00008 1 2 M 30 19001 2006/7/26 0:00:26 A00009 2 1 M 20 19002 2006/7/27 0:00:29 A00010 5 1 F 60 19003 2006/7/28 0:00:32 A00011 3 2 F 20 19004 2006/7/29 0:00:35 A00012 1 1 F 20 19005 2006/7/30 0:00:38 A00013 1 1 M 40 19006 2006/7/31 0:00:41 A00014 4 3 F 20 19007 2006/8/1 0:00:44 A00015 1 1 M 20 0 2006/8/2 0:00:47 A00016 1 1 M 30 27001 2006/8/3 0:00:50 A00017 2 5 M 30 27002 2006/8/4 0:00:53 A00018 1 1 F 20 27003 2006/8/5 0:00:56 A00019 1 1 F 20 27004 2006/8/6 0:00:59 A00020 2 1 F 60 27005 2006/8/7 0:01:02 A00021 1 1 M 20 27006 2006/8/8 0:01:05 B00001 1 2 F 30 27007 2006/8/9 0:01:08 B00002 1 3 M 20 19001 2006/8/10 0:01:11 B00003 3 4 M 50 19002 2006/8/11 0:01:14 B00004 1 1 M 60 19003 2006/8/12 0:01:17 B00005 1 2 F 20 19004 2006/8/13 0:01:20 B00006 2 1 F 20 19005 2006/8/14 0:01:23 B00007 5 1 F 30 19006 2006/8/15 0:01:26 C00001 3 2 M 20 product.json:商品マスタデータ(一部)です。 [ { "M_PRODUCT_CODE": "A00001", "M_PRODUCT_NAME": "幕の内弁当", "M_PRODUCT_CATEGORY": "お弁当", "M_COST": "382", "M_UNIT_PRICE": "550", "M_UNI_PRICE_TAX": "578" }, { "M_PRODUCT_CODE": "A00002", "M_PRODUCT_NAME": "天丼", "M_PRODUCT_CATEGORY": "お弁当", "M_COST": "411", "M_UNIT_PRICE": "500", "M_UNI_PRICE_TAX": "525" }, { "M_PRODUCT_CODE": "A00003", "M_PRODUCT_NAME": "カツ丼", "M_PRODUCT_CATEGORY": "お弁当", "M_COST": "365", "M_UNIT_PRICE": "500", "M_UNI_PRICE_TAX": "525" } JSON_File.txt:product.jsonファイルのパスを記載したファイル、インテリジェント構造モデルによるproduct.jsonファイルの読み取りに使用します。 PATH /home/infa/product.json インテリジェント構造モデルによるJsonファイル(商品マスタ)のパターン抽出 今回使用する商品マスタのデータはJson形式のためデータ加工する前にパターン抽出して構造化します。インテリジェント構造モデルについての詳細は こちら をご覧ください。 では、まず適当なブラウザから Informatica Cloud に接続して、ユーザー名とパスワードを入力してログインします。 データ統合 をクリック > 新規 コンポーネント > インテリジェント構造モデル 以下の項目を入力 名前:インテリジェント構造モデルの名前です。適当な名前を入力ください プロジェクト > 参照 からこれから作成するモデルの保存場所を指定します スキーマ/サンプルファイル:今回使用する商品マスタのデータをローカルフォルダから選択 *ここでいうローカルフォルダはセキュアエージェントではなく、このブラウザにアクセスしているローカルのPCになります。 構造の検出 をクリック後、 保存 をクリックする 以上でモデルを作成できました。 同じ画面で検出したファイルの構造がツリー上で可視化されるようになります。 コネクタ設定 次にコネクタの設定をします。 IDMCではさまざまなコネクタを用意しておりまして、Salesforce、SAP、Oracle、Amazon S3など様々なサービスから指定のコネクタを使いデータを利用できます。 今回は、SA上とFTPサーバー上にあるデータを引っ張ってきて、oracleの既存のテーブルに格納しますので、 FTPコネクタ とセキュアエージェントにあるデータを利用できる フラットファイルコネクタ と オラクルコネクタ を利用します。 コネクタによって設定するプロパティは違いますが、以下ではOracleコネクタ設定時に入力が必須な項目について記載します。詳細は こちら をご覧ください。 管理者 > 接続  > 新しい接続 をクリック タイプ:コネクタのタイプを選択します。今回は Oracle を選択 ランタイム環境:このコネクタを使用する環境を選択します。 ユーザー名:Oracleで設定しているユーザー名です。 パスワード:Oracleで設定しているパスワードです。 ホスト:OracleのIPアドレスです ポート:Oracleのポート番号です。基本的には1521番でよいかと思います。 サービス名:Oraleのサービス名です コードページ:文字コードを選択します。 「売り上げ一覧」と「商品マスタ」の結合 コネクタの設定できましたら次はデータベースの設定です。 ここからの操作では変換部品を選択、繋げていき、プロパティで詳細設定するというのを繰り返していく形になります。 データ統合 > 新規 > マッピング > マッピング > 作成 でマッピングを作成 まずは売り上げデータの設定をします。 変換部品から ソース を選択 プロパティで ソース > 接続 からあらかじめ作成しておいたFTPコネクタを選択、 オブジェクト にて売り上げデータ(sales.csv)を選択 補足にはなりますが、プロパティの ソース > フィールド では読み込んだファイルのカラム名が表示され、プロパティの横にあるプレビューではデータの中身を確認できます。 商品マスタの設定をします。こちらは、先ほど作成したインテリジェント構造モデルを利用して、半構造化データであるJsonファイルを読み取れる形で出力するために、 構造パーサー という変換部品とJsonファイルへのパスが記載されたテキストファイル(JSON_File.txt)を使います。 変換部品から ソース を選択して、プロパティで ソース > 接続 からあらかじめ作成しておいたフラットファイルコネクタを選択。 オブジェクト にて商品マスタへのパスが記載されたファイル(JSON_File.txt)を選択。 変換部品から 構造パーサー を選択。プロパティにて 構造パーサー > インテリジェント構造モデル から先ほど作成したインテリジェント構造モデルを選択。 ソースと構造パーサーを繋げて、 構造パーサー のプロパティの フィールドマッピング にてPATH(JSON_File.txtに記載されているJSONファイルのパス)をFilePath(product.jsonのカラム)にドラッグアンドドロップ。 これでデータの読み込みは完了です。次にこちらを ジョイナー というパーツを使って売り上げデータと商品マスタを結合します。 ジョイナー という変換部品を選択 売り上げデータと商品マスタを ジョイナー に繋げる。商品マスタをmasterに、売り上げデータをdetailに入れます。 ジョイナー のプロパティにて設定をします 受信フィールドにてそれぞれmasterとdetailに設定したファイルのどのカラムを抽出するかを設定します。今回は商品マスタのファイルからは Product_Code Product_Name Price_TAX を、売り上げデータに関しては Age amaount product_code store_code を持ってきます。 結合条件でそれぞれのファイルの Product_Code をキーに結合するように設定します。この際、結合タイプというのがあるのですがこれはmaster側のみ、もしくはdetailのみにあるprooduct_codeを含めるかどうかを設定します。今回ノーマルというものにしていてmaster側のみ、またはdetailのみにあるprooduct_codeは除外するという感じです 商品名変更 この後はデータ加工部分になります。 1つ目で一部商品名を変更して新しく作成したProduct_Nameに格納、2つ目で価格と販売量から収益を計算します。 では、まず1つ目ですがこちらは式という変換部品を利用します。 式 という変換部品を選択する。 ジョイナー と 式 を繋げる。 プロパティの受信フィールドにて利用するカラムを指定します。 フィールド選択条件 を 名前付きフィールド に変更、 設定 にて age amount M_PRODUCT_NAME M_UNI_PRICE_TAX product_code store_code を選択する。 プロパティの式にて以下の式を作成します。これは新しく product_name を作成して、そのカラムに product_code が A00001 で store _code の先頭が 19 のものに指定の名称を入れます。そして、それ以外のものは既存のカラムである Product_Name を格納するというものです。 IIF(product_code='A00001' AND SUBSTR(store_code, 1, 2)='19', '江戸前幕の内弁当',M_PRODUCT_NAME ) 収益計算 次に収益の計算をします。 アグリゲータ という合計や平均などを行う際に利用する変換部品を使用します。こちらはグループごとの集計も可能なので今回は収益を計算してかつproduct_Nameとage、商品名と年齢が同じデータをグループ化してみます。 アグリゲータ という変換部品を選択 プロパティの受信フィールドにて フィールド選択条件 を 名前付きフィールド に変更、 設定 にて age 、 amount 、 M_UNI_PRICE_TAX 、 product_Name を選択 プロパティの集計にて +ボタン 選択して新しいフィールドにて,以下のように設定して OK をクリック フィールドマップ:出力フィールド 名前:Revenue(適当な名前を入れてください。) タイプ:decimal 精度:10 スケール:0 作成したRevenueのフィールドにて、「式」の 設定 をクリックして以下の記載後、「検証」ボタンで式が有効か確認。こちらは商品の税込み価格と売り上げ数から1つの商品の売上合計金額を算出する式になります。 SUM(To_Decimal(M_UNI_PRICE_TAX) * To_Decimal(amount) ) product_Name と age が同じレコードをグループ化します。プロパティ グループ化 の「+」を選択、 フィールド名 から以下の2つをそれぞれ選択 Product_Name age Oracleへの格納 ここまでがデータ加工の部分で最後にこれらをあらかじめ作成しておいたオラクルのテーブルに格納していきます。 ソース を選択 プロパティの 受信フィールド にてどのカラムを使うかを設定 プロパティの ターゲット > 接続 からあらかじめ作成しておいたoracleコネクタを選択。 オブジェクト > 選択 にてあらかじめOracle側で作成しておいたテーブル名を選択 フィールドマッピング にてどのカラムに今回作成したカラムを格納するかを設定。Oracleのテーブル側に今回作成したカラム( Product_Name 、 AGE 、 REVENUE )と同じ名称のカラムを作成しているので 同じ名称ごとに対応付けします。 以上でマッピングは完了です。以下がマッピングの完成形となります。 では、最後に実行してみましょう。画面右上の 保存 と 実行 をクリックすると今回作成したマッピングが実行されます。 どうでしたか?問題なく動きましたでしょうか?エラーが出た場合はエラーログも見られるので確認してみてください! 最後に 今回はインフォマティカの簡単なマッピングを作成しました。ご紹介したのはあくまで一例で、 Cloud Integration Hub というハブの機能や Cloud Application Integration というAPI開発の機能などIDMCにはいくつもの機能があります。 今後、DXやデータ活用が進む中そのためにデータ統合は必須の機能になっていくかと思いますので、この記事が参考になっていれば幸いです。 ご覧いただきありがとうございました! それでは、明日の記事もお楽しみに! 参考リンク Smart Data Platform(SDPF)とは NTT Com Knowledge Center(データ統合インフォマティカ ソリューション) インフォマティカ 1か月の無償トライアル
アバター
この記事は、 NTT Communications Advent Calendar 2022 10日目の記事です。 こんにちは! SDPF クラウド/サーバー ESI チーム入社1年目の飯國 ( @guni1192 ) です。 普段は SDPF クラウド/サーバーにおけるネットワークコントローラ ESI (Elastic Service Infrastructure) を開発しています。 今回は ESI チームにおける CI 改善の取り組みについて紹介します。 CI/CD をセルフホストしている方向けに、CI の Workflow や実行基盤の改善の一例として参考になればと思います。 今までの ESI チームの CI 基盤の課題 ESI チームでは Jenkins を運用していました。 ESIの開発当初(6、7年前)から大きく構成は変わっておらず、チーム内から以下のような問題点があげられました。 課題1: CIのJob内容が Jenkinsfile でコード管理されておらずブランチ単位でのJobの設定変更が不可能 変更する際は管理者となっている社員に依頼しなければならない 課題2: Jenkins Agent に Jobの実行に必要な依存関係(言語, ライブラリ, ミドルウェア)が溜まる サーバのストレージ容量が逼迫する (ストレージ容量を空けるためのオペレーションコスト) ソフトウェアの依存関係を正しくテストできない。 CI の実行環境は毎回作って壊してクリーンな状態にしたい 課題3: Flaky Test が多く、CI の Job が2つ以上溜まっているとほぼFailする 課題4: CI がパスするまでの時間が長い (3-4時間) この様な状態だと、1営業日で PR をマージすることはほぼ不可能な状態です。 ESIチームのCI基盤の要件 これまでの Jenkins の課題を踏まえ、CI基盤及びパイプラインを再設計することにしました。 以下の要件達成を目指します。 要件1: 開発者自身 が必要なワークフローを定義し、バージョン管理できる → 課題1 要件2: コンテナやVMなどの使い捨ての環境でCIを実行したい → 課題2 要件3: 環境要因で Fail しないキャパシティ → 課題3 要件4: CI が長くとも1時間程度で終了する → 課題4 GitHub Actions への移行 GitHub Actions で前述した要件を達成可能か検証しました。 GitHub Actions は GitHub が提供する CI/CD プラットフォームです。 開発者自身がWorkflowをYAML形式で定義し、リポジトリにPRを送ることでCI/CDパイプラインを作成できます(要件1達成)。 GitHub Actions に移行するにあたって、CI の Job を実行するサーバ(Runner)は Self-Hosted Runnerを用います。 Self-Hosted Runner は名前の通り、自分でベアメタルサーバ、VM、コンテナを用意し、その上で CI の Job を実行する Runner です。 Self-Hosted Runner についての社内事例を2つほど紹介します。 engineers.ntt.com engineers.ntt.com actions-runner-controller + Google Kubernetes Engine Self-Hosted Runner のオーケストレーションには actions-runner-controller を用います。 actions-runner-controller は Runner を Kubernetes の Custom Resource として扱うことができる Custom Controller です。 actions-runner-controller は Runner を Pod として提供し、使用されたら破棄する Ephemeral Runnerを標準で採用しています(要件2達成)。 また、Runnerの オートスケーリングや Prometheus Metrics の出力などが可能です。 今回は Kubernetesクラスタの管理に Google Kubernetes Engine(GKE) を使用しました。 とりあえず GitHub Actions へ移行してみた GKEに手っ取り早く環境構築して、Jenkinsのときと同じ様なフローをGitHub ActionsのWorkflowに作って検証してみました。 この時点で要件3、要件4は未達成で、WorkflowとGKEの構成の両方に手を入れる必要がありました。 Flaky Test の解消 Jenkinsを使っていたときから「何もしてないのにCIがFailする」という事象が頻発し、人がCIを利用していない時間帯を見計らってジョブを流すということをしていました。 CI 基盤が開発する上で大きなブロッカーになってしまっていました。 まずは原因を調査するために Grafana, Prometheus, Node Exporter, kube-state-metrics を導入し、Runner のパフォーマンスを計測しました。 原因は、Node の Disk I/O が過負荷になることで、Integration Test内でDBへの書き込みがタイムアウトになることでした。 対策としては大きく2つあげられます。 クラスタ全体のDisk I/Oのスループットを上げる タイムアウト値の緩和 今回は、できるだけテストコードを修正して仕様を変えたくなかったため、タイムアウト値の緩和は見送りました。 クラスタ全体のDisk I/O性能をあげるためにクラスタの構成や Kubernetes の設定を修正して、Job を処理できるようにしました。 Disk I/O の負荷分散 まず、Nodeの台数(=local storageの台数)を増やし、負荷を分散させます。 Node数を増やしただけでは、Kubernetes が Pod (Runner) を Disk I/O の多い Node にスケジュールして Disk I/O が偏る可能性があります。 そのため、 Pod Topology Spread Constraints などの仕組みを使って Pod を Node に分散配置します。 しかし、従来の Workflow では1つの Job を実行する Pod の Disk I/O が非常に高いため、1つの Job を複数の Pod に分散させるように Workflow を改修する必要があります(後述)。 ストレージ単体の性能を上げる (SSD化) Node のローカルストレージを HDD から SSD に変更しました。 クラスタ全体のDisk I/Oのスループットを上げた結果 数ヶ月運用し、今まで Disk I/O 要因で起こっていたと思われる Flaky Test が報告されなくなくなりました(要件3達成)。 それはそれとして、定量的に Flaky Test を検知する仕組みが必要だと考えたので今後の課題にしたいと思います。 CIの実行時間の短縮 次にCIの実行時間の短縮を図ります。 これまで ESI の Integration Test は Docker Compose で API, DB, 外部サービスの mock を含む複数のコンテナを構築し、APIのテストを行っていました。 Jenkins のときは Jenkins Agent はベアメタルサーバで容易にスケールアウトできる環境ではなかったため、 1つの Jenkins Agent 内で 複数の Docker Compose 環境を作ってテストしていました。 この構成だと、並列数(==テスト環境数)が大きく制限されてしまいます。 なぜなら、1つ Runner (Pod) 内で複数の環境を作ってしまうと、1つのNodeのlocal storageに負荷が集中するためです。 Disk I/O の負荷分散について前述しましたが、従来の構成ではほぼ機能しない仕組みとなっているのはこのためです。 Disk I/O の負荷分散及び、並列数をスケーラブルにするため、複数台の Runner によってテスト項目ごとにテストを分散実行するように Workflow を再設計しました。 ESIチームでは大きく分けて14種類のテスト項目がありました。 つまり、14並列でテストを実行すれば、CIの待機時間は 14種類のうち、一番長い実行時間のテスト項目となります。力技ですね。 今回はそれぞれテスト項目のチューニングを行った結果、実行時間が最長のテスト項目は1時間だと言うことがわかりました。 これにより、並列数次第ですが最短1時間程度でCIをパスできます。 Workflowの改修結果 Runnerを10並列で動かした結果、実行時間の大幅な短縮に成功しました(要件4達成)。 まとめ 今回は SDPF クラウド/サーバー ESI チームにおける CI の改善に関する取り組みについて紹介させていただきました。 CIはソフトウェアが常に動くことを保証するために欠かせないものです。 Workflow や基盤の両方から改善することは開発者体験の向上にも繋がります。 GitHub Actions on GKEに移行することで、開発者主体でワークフローを設計し、CIの実行時間の短縮、Flaky Testの解消ができました。 今後は、Self-Hosted Runner の信頼性の計測 (Prometheus Exporter の実装) や、Disk I/O のレイテンシを考慮した Kubernetes Scheduler の実装なども考えていきたいと思っています。 また、GitHub のロードマップに Self-Hosted Runner の Kubernetes への対応が入っているので注目です。 github.com ここからは宣伝です。 SDPF クラウド/サーバーは国内最大級の IaaS です。ESI チームはネットワーク機器のオーケストレーションを行うためのソフトウェアを開発しています。 ESI チームはインターンシップの募集も行っているため、クラウドサービスのソフトウェア開発に興味がある学生さんはぜひご応募ください。 ESI チームのポストは、 エンタープライズ向け大規模クラウド/ネットワークサービスを支えるコントローラ開発 内の クラウドネットワーク/仮想アプライアンス です。 締切(12/14)が迫っているため、ご応募はお早めに! information.nttdocomo-fresh.jp
アバター