TECH PLAY

NTTドコモビジネス

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

602

みなさんこんにちは、イノベーションセンターの @Mahito です。 普段は社内のエンジニアが働きやすくなることを目標に、 コーポレートエンジニアのような活動やエンジニア向けイベントの企画・運営をしています。 今回は、本 NTT docomo Business Engineers' Blog のレビューに、 GitHub Copilot code review を利用し始めたことと、その学びについてお話します。 なお、GitHub Copilot code review を導入以降は投稿がないので、 これが GitHub Copilot code review の初レビュー記事となっております。 企業ブログにおけるレビューの重要性と難しさ 本ブログにおけるこれまでの取り組みと GitHub Copilot code review 導入の背景 仕組みによるレビュー スタッフ内でのレビュースキルの共有 GitHub Copilot code review の導入 GitHub Copilot code review の設定 GitHub Copilot code review の実行 textlint では指摘されなかった typo の指摘 サービス名の確認 GitHub Copilot code review を動かしての学び 1. 指摘の非決定性 2. 執筆者・レビュアーの負担軽減 3. レビュー観点の明示化によるレビュアーの観点共有 textlint と GitHub Copilot code review の使い分け まとめ 企業ブログにおけるレビューの重要性と難しさ まず、GitHub Copilot code review を導入した話の前に企業ブログのレビューについて少し説明します。 本ブログのように、現在さまざまな企業でブログが展開されています。 しかし、企業ブログでは記事を発信する際に以下のようにさまざまな観点からのレビューが必要となります。 内容は正確か 誤字脱字はないか 企業のブランドイメージや広報ルールに合致しているか 誤解を招く表現がないか 引用は正しくされているか etc... 誤字脱字には簡単なタイポから自社や他社の商標やサービス名の誤り(本来大文字にするところが小文字になっている等)なども含まれます。 また、投稿者が意図した内容と記事を読んだ人の間で解釈が異なることから問題になるケースもあります。 こうした問題を避けるためにも記事を公開する前のレビューが重要となります。 しかし、上記のような観点は機械的なレビューではカバーできないことも多く、 人の手によるレビューが必要となります。 一方、人によるレビューでは問題を見逃してしまうことや、 スキル・経験・意見の違いによって、レビューの結果のばらつきによる問題が生じることもあります。 例えば A さんには問題のない表現に見えたので指摘をしなかったが、B さんが見ると問題がある表現だった、ということがあります。 そのため、レビューワーのスキルや考え方の共有をしながら、 企業として必ず守らなければいけないルールのラインをレビュー全体で揃えていくといった難しさがあります。 本ブログにおけるこれまでの取り組みと GitHub Copilot code review 導入の背景 仕組みによるレビュー 本ブログでは記事の管理やレビューに GitHub を利用しています。 投稿者は記事を執筆した後 PR を出し、レビュアーが PR に記事の修正や改善の提案を繰り返しながら、記事をブラッシュアップしていきます。 (なお、GitHubを活用した記事公開プロセスについては、別記事 「開発者ブログをリニューアルしたついでにレビューと記事公開プロセスをいい感じにしたお話」 で紹介していますので、ご興味がありましたらご覧ください) 。 以前より、レビューには textlint を用いた文章確認の仕組みを用意しています。 textlint は、文章の誤字脱字や表記ゆれ、文法確認などを行うためのツールです。 textlint を用いることで、設定したルールに基づき、 文法的な誤りや表記ゆれを自動的に検出し、レビュアーが指摘する必要のある問題を減らすことができます。 例えば、1文が長すぎるといったところや、広報ルールに沿ってない表現(「様々」は「さまざま」にするなど)を自動的に検出し、指摘できます。 PR の作成や修正がされたタイミングで GitHub Actions を用いた CI も用意しており、 投稿者の手元や PR 上で常に文章確認をしています。 昨年には、textlint では対応できない誤字脱字などの誤りが一定数あることから、 LLM を用いることでより抜けのない校正ができるのではと考え、CI に組み込んだこともあります ( LLM校正CIを自社のブログに導入してみた で詳しく紹介しています)。 しかしながら、こちらは LLM のモデル更新が頻繁にあることや、プロンプト調整の難しさ、 さらに実際には指摘されるコメントの半数ぐらいが的外れな修正提案だったこともありお蔵入りとなりました。 スタッフ内でのレビュースキルの共有 上記のような機械的な仕組みを用いたレビューは、ある程度の効果はあるものの、 最終的には本ブログのスタッフによるレビューに頼ることとなっています。 一方、人によるレビューでは問題を見逃してしまうことや、 スキル・経験・意見の違いによって、レビューの結果のばらつきによる問題が生じることもあります。 例えば A さんには問題のない表現に見えたのでコメントをしなかったが、B さんが見ていれば気付ける問題だった、ということがあります。 本ブログスタッフでは、レビュースキルを共有するためにいくつかの取り組みを行っています。 例えば、新しくスタッフになってくれた人向けのドキュメント整備や、ペアレビューを実施することでレビューのスキルを共有しています。 また、レビューをしている際に不安な場合は、Slack で他のスタッフに相談することもあります。 他にも、今回のようにスタッフ自身がブログ記事を書いて、他のスタッフからのレビューを受けることで、 レビュースキルの共有がされることもあります。 GitHub Copilot code review の導入 先述の通り、一時的に導入した LLM 校正 CI はお蔵入りをしましたが、 そのアイデア自体は面白かったことや、スタッフのレビューを下支えするためにも再度 LLM を使ったレビューをできないかと考えていました。 2025年4月4日に GitHub Copilot code review がリリースされたため、活用の検討を始めました。 GitHub Copilot code review は、 GitHub Copilot がコードレビューをした上でフィードバックをくれる機能です。 また、GitHub Copilot code review は、 .github/copilot-instructions.md に Copilot に対する コンテキストを追加設定 することで、レビュー観点を明示的に指示できます。 上記で述べたように、新規スタッフにレビュースキルを共有するための取り組みの中で、 レビュー観点などについてはすでにドキュメント化されているので、 これを追加コンテキストとして GitHub Copilot code review に設定しました。 GitHub Copilot code review の設定 実際の設定にはスタッフ向けのレビューマニュアルからレビュー観点を抽出し、 抽出したものを Claude Code に与えて .github/copilot-instructions.md を作成してもらいました。 現在は Claude Code が出力した内容で日本語としておかしな箇所などを直したものを利用おり、 以下がその内容の一部です。 ### 2. 商標およびブランド名の検証 - **目的**: 商標名およびブランド名の適切な名称使用を確実にする - **ガイドライン**: - 企業および製品の正しい名称が使われているかを検証する - prh.ymlで既にカバーされている一般的な間違い(GitHub vs Github等)も確認する - 潜在的な商標侵害を特定する - 誤用される商標名やブランド名の修正を提案する - テック企業とその製品に特に注意を払う GitHub Copilot code review の実行 GitHub Copilot code review を実行するには、 GitHub 上でプルリクエストを作成し、レビュアーに対して GitHub Copilot code review を有効にする必要があります。 レビュアーに割り当てると、あとは自動的に GitHub Copilot code review が実行され、 PR のコメントとしてレビューコメントが追加されます。 冒頭でも述べたように、本記事が GitHub Copilot code review の初レビュー記事となります。 実際に本記事のドラフトに対して GitHub Copilot code review を動かして、 GitHub Copilot code review から指摘された内容をいくつかお見せいたします。 textlint では指摘されなかった typo の指摘 確かにここは「は」を入れたほうが自然な文となりますので修正しました。 この他にも文法的な指摘や、不要なスペースの指摘など我々の textlint の設定では拾われなかった細かい typo をいくつか指摘されております。 サービス名の確認 こちら指摘されている "Claude Code" については、ツール名としては正しいです。 Claude Code は Claude の API を利用しておりサービス名としては "Claude" が正しいのかも知れませんが、 今回はツールとして Claude Code を利用したことを伝えたいので、"Claude Code" のままにしています。 今回は過検知という結果になっていますが、 商標やサービス名についても .github/copilot-instructions.md に書いた内容で確認しているのがわかりました。 また、製品名やブランド名以外にツール名についても確認するよう追加指示の必要があるとわかりました。 GitHub Copilot code review を動かしての学び 実際に GitHub Copilot code review 動かしてみて、いくつかの学びがありましたので共有しておきます。 1. 指摘の非決定性 まず、GitHub Copilot code review を動かすたびに指摘が変わるケースに遭遇しました。 こちらは内容が変わるというよりも、前回のレビューでは指摘されず修正もされなかった箇所が次回のレビューでは指摘されることがありました。 それなら最初から指摘してほしかったという気持ちになりつつも、 私自身 1 度目のレビューで見逃していた箇所に再レビュー時で気づき、申し訳無さもありながらコメントすることもあります。 そういう意味で、GitHub Copilot code review を含む生成 AI もちょっと人間っぽい不確実さがあるなと感じました。 一応対策としては、GitHub Copilot code review が一度レビューを終えた後で、 再度 GitHub Copilot code review を実行することで、指摘箇所が変わることもあるので何度か動かしてみるのもありかなと思いました。 ただ、指摘箇所が変わるということもあり、 .github/copilot-instructions.md にも書いた指示内容が正確に反映されているのか分かりづらく、 このあたりは使いながら時間をかけて調整する必要があると感じています。 2. 執筆者・レビュアーの負担軽減 こちらは普段から CI に Linter や Formatter を導入されている方はわかると思うのですが、 GitHub Copilot code review を使い機械的な指摘を受けることで、 執筆者・レビュアー双方の心理的な負担を減らすことができます。 私がコードや記事を書いている際にも簡単なミスや typo の指摘を人にされると、 「こんな簡単なところの指摘させて申し訳ない」と思うことが多々あります。 レビュアーの立場でも「こんな些細なところの指摘をして直させるのは心苦しい(でも必要だし)」と思いながら指摘することがあります。 GitHub Copilot code review を使うことで、 Linter や Formatter のように記事内容をレビュアーが確認する前に GitHub Copilot code review が問題点を指摘してくれるため、 執筆者・レビュアー間でのやりとりが減り、双方の心理的な負担を減らすことができると感じました。 3. レビュー観点の明示化によるレビュアーの観点共有 こちらは当初想定していなかったメリットではありますが、 .github/copilot-instructions.md にレビュー観点を明示化することで、レビュアーの観点を共有しやすくなると感じました。 これまでもドキュメントに記載することで共有を図っていたのですが、 今回 GitHub Copilot code review 用に .github/copilot-instructions.md を作成することで、 レビューの要点をより明示でき、的確にレビュー観点をレビュアー内で共有できるようになったと考えています。 textlint と GitHub Copilot code review の使い分け GitHub Copilot code review がなんとなく使えることがわかりましたが、 では textlint は不要かというとそういうわけではありません。 GitHub Copilot code review は生成 AI を用いたレビューであり、 先述したようにその結果が必ずしも毎回一致するとは限りません(非決定性)。 例えば、textlint のルールとして紹介した「様々」という単語に対して、 GitHub Copilot code review は「さまざま」と修正を提案することもあれば、しない場合もあります。 このように結果が必ず一意になるように確認する場面(決定性)については、 textlint を用いた確認が有効です。 逆に、文脈などから判断が必要な場合においては、GitHub Copilot code review のような、生成 AI を用いたレビューが有効だと考えられます。 まとめ 本記事では、企業ブログにおけるレビューの難しさと、それを手助けする可能性として GitHub Copilot code review を活用する方法について紹介しました。 もちろん生成 AI の出力のため完璧でないことを前提にした運用となりますが、 これまでスタッフが確認してきたけど漏らしがちなところを GitHub Copilot code review にサポートしてもらえるのではないかと思っています。 なお、今回の記事は GitHub Copilot code review によるレビューを反映したうえでレビューアーに見てもらっていますが、 レビュアーからは記事をさらに良くするための指摘をいくつもされており、やはり人のレビューは大事だなと改めて感じています。 今回は企業ブログのレビューでの活用を紹介しましたが、 個人ブログでも活用できるかと思いますので、よろしければぜひ試してみてください。 この記事がどこかの企業や個人のブログの運営のお役に立てれば幸いです。
アバター
イノベーションセンターの加藤です。この記事ではWhisperによる音声認識の前処理と後処理にLLMとOCRを組み込むことで、映像の文字起こし精度の向上を図った際の検証結果を紹介します。 Whisperとは OCRの結果を盛り込み専門用語を認識させる 大規模言語モデルで全体の文章を調整する 各アプローチの融合 結果の考察 まとめ Whisperとは Whisper 1 はOpenAIによって提供されているオープンソースの音声認識モデルです。 色々なサイズのモデルが提供されており、最も大きいモデルであるlarge-v3は日本語を含む多言語に対応し高い認識精度を誇ります。 しかしもちろん完璧ではなく、Whisper(large-v3)で日本語の音声を書き起こしてみるとそれなりに誤認識が見られます。また、専門用語や人名など、あらかじめ知っていないと正しく書けない単語についてもうまく書き起こせないという音声認識共通の問題もあります。 そこで本稿では、Whisperに日本語やドメイン知識に関する事前知識をうまく盛り込み音声認識の精度を向上させる2つの手法を実験してみました。 できればオープンなデータで実験したかったのですが、スライドを映しながら喋る形式で、専門用語を含み、正解データとして読み上げ原稿がある動画を見つけられなかったため、社内勉強会の録画を使って2つの手法を試してみました(そのため評価指標しか出せません🙇)。約8分の映像に対して単語単位の誤り率(Word Error Rate, WER)を測定し、それぞれの手法を比較します。 OCRの結果を盛り込み専門用語を認識させる Whisperのプロンプトには直前に認識した文章やキーワードなどを埋め込める領域が存在し、ここに専門用語や人名を埋め込むと書き起こしの語彙をコントロールできます。 あくまで言語モデルの出力を誘導する程度の機能であり、専門用語の読みを明示的に指定できるわけではありませんが、以下の例のように常識的な読み方であればWhisperが正しく書き起こしてくれます。 さらに、文字起こしの対象が映像であるとき、映像に映っている文章が文字起こしの際のヒントになることがあります。特に講演などでスライドを画面に映しているときは発話内の専門用語が画面に表示されていることが多いのではないかと思います。 そこで発話を検出したタイミングの映像フレームをEasyOCR 2 で文字認識し、拾えた単語をWhisperプロンプトに埋め込む処理を追加しました。 その結果、Whisperに標準で実装されているビームサーチと比較して、WERが0.059から0.056に少し改善しました。 大規模言語モデルで全体の文章を調整する Whisperは一般的な大規模言語モデル(LLM)と同様に、入力と現時点の出力 から次の単語(トークン) としてあり得るものを確率 の形で予測します。 普通は最も確率の高いトークン を選択するか、確率に沿ってランダムに選択するかのどちらかですが、確率の高い候補を複数確保して文章全体でもっともらしいトークン列 を探索するビームサーチと呼ばれる手法も存在します。 このビームサーチで得られる複数の候補に対して、Whisperよりも大きなLLMを使ってより信頼度の高いもっともらしさを算出すれば、音声認識の結果を修正できそうです。 そこで、検出された各発話区間に対して上位5件の認識結果をWhisperのビームサーチから取得し、Sarashina2-7B 3 を用いて文章全体のもっともらしさ(Perplexity)が最適になるような認識結果の組み合わせを探索しました。 その結果、WERは0.059から0.048に改善しました。 各アプローチの融合 それぞれのアプローチは一長一短です。LLMによるアプローチではそもそもWhisperが専門用語を聞き取れなかったら修正は困難ですし、特に会話中に現れる人名は単なる日本語の一般知識だけではカバーできません。一方でOCRによるアプローチでは、Whisperが音声をチャンク分けして独立に認識する仕組みを採用しているために、単語単位では正しくても文章としてはおかしいことがよくあります。 そのため、両方を組み合わせることでこれらの弱点を補うことができそうです。 しかしながら、それぞれの手法を単独で使用した時は認識精度が向上したにもかかわらず、併用するとWERが0.059から0.077に悪化するという結果になってしまいました。 結果の考察 以上の結果を表にまとめました。 naive: ビームサーチのみ ocr: OCRによって取得した単語をWhisperのプロンプトに注入してビームサーチ llm: ビームサーチした結果の候補からLLMが選択 both: ocrとllmの併用 naive ocr llm both WER 0.059 0.056 0.048 0.077 併用による精度劣化の原因を考察するために、認識結果が悪化した典型的なサンプルを紹介します。 このようなスライドを映しながら話している場面で、認識結果は次のようになりました。 原稿(Ground Truth) 次に文法制約付き生成の既存手法のLimitationとして、 naive 次に文法制約付き生成の既存手法のリミテーションとして ocr 次に文法制約付き生成の既存手法のリミテーションとして、 llm 次に、文法制約付き生成の既存手法のリミテーションとして、 both 5.、6.、7. 併用(both)した時に出力がおかしくなっています。そこでこの時点で画面をOCRした時の結果と、OCRによって得たキーワードをプロンプトに注入したか否かでビームサーチの候補がどう変化したかをみてみます。 OCRによって得られたキーワード 背景、文法制約付き生成手法、2.、3、提案手法、4.、5.、まとめ キーワード注入なしでのビームサーチ結果(5候補) 候補 スコア(自信度) 次に文法制約付き生成の既存手法のリミテーションとして -0.062 次に文法制約付き生成の既存手法のリミテーションとして、 -0.069 次に、文法制約付き生成の既存手法のリミテーションとして、 -0.081 次に文法制約付き生成の既存手法の リミテーションとして -0.114 次に、文法制約付き生成の既存手法のリミテーションとして -0.136 キーワード注入ありでのビームサーチ結果(5候補) 候補 スコア(自信度) 次に文法制約付き生成の既存手法のリミテーションとして -0.245 5.、6.、7。 -0.914 5.、6.、7. -0.985 5.、6。 -1.068 5.、 -1.395 OCRがいくつかの文章を見落としているのは一旦無視して、箇条書き用の数字がキーワードとして抽出されていることがわかります。そしてビームサーチの結果がこのようなキーワードに引っ張られてしまい、自信度は低いもののまるで箇条書きの続きを出力するような「5.、6.、7」という候補を出力してしまっています。 このようにWhisperのプロンプト機能はコントロールが難しく、音声とは全く関係のない文章がプロンプトに引きずられる形で出力されてしまうことがあります。さらに後処理のLLMは音声認識の自信度を考慮していないので、このような変な文章を選択してしまうリスクが発生します。 この問題はLLMの文章選択の際にWhisperの自信度を考慮しながら文章全体のもっともらしさを最適化することで回避できますが、Whisperの出力する自信度とLLMの出力するもっともらしさのバランスは慎重に設定する必要がありそうです。 まとめ 本稿ではオープンソースの音声認識モデルであるWhisperの性能を向上させるために、OCRで抽出した専門用語のプロンプト注入と、LLMによる自然な出力文章の探索という2つのアプローチを紹介しました。いずれの方法も性能向上に寄与しましたが、プロンプト注入のアプローチは認識候補の品質を下げる傾向にあり、LLMによるアプローチと併用するためには別の工夫が必要そうでした。 OpenAI, Whisper ( https://github.com/openai/whisper ) ↩ Jaided AI, EasyOCR ( https://github.com/JaidedAI/EasyOCR ) ↩ SB Intuitions, Sarashina2-7B ( https://huggingface.co/sbintuitions/sarashina2-7b ) ↩
アバター
こんにちは、イノベーションセンターのメディアAI プロジェクト(以下、PJ)の小林、加藤、岡本です。普段はコンピュータビジョンの技術開発やAI/機械学習(ML)システムの検証に取り組んでいます。 我々メディアAI PJでは5月27日から30日にかけてグランキューブ大阪で開催されたJSAI2025(2025年度 人工知能学会全国大会)に参加しました。本記事ではJSAI2025で発表された「画像・3D AI応用関連」、「VLM(Vision Language Model)」、「ハルシネーション対策」に関する興味深かった研究をいくつか紹介したいと思います。 目次 目次 JSAI2025とは 画像・3D AI応用関連 深度情報を用いた画像識別における解釈性向上に関する一考察1 変電所設備を対象とした3次元物体検出4 重み付き残差接続による境界品質を考慮したゼロショットセグメンテーション手法の提案7 VLMに関する論文 Crosslingual Visual Promptにもとづくテキスト付き画像からの日常物体検索10 判断根拠を説明する視覚言語モデルの自己改善手法12 項目反応理論を用いた視覚言語モデルのマルチモーダルな推論能力および問題特性の評価17 ハルシネーション対策に関する論文 [アプローチA] 反論・再考プロンプトによるHallucination検出手法の提案19 [アプローチA] 反復サンプリングを活用したLLM推論時の外部情報検索機能の最適化22 [アプローチA] 長文コンテキスト質問応答における大規模言語モデルによる誤引用文の訂正手法の提案24 [アプローチB] LLM内部演算値を用いたLLM回答の信頼度定量化とOOD検知方式26 最後に JSAI2025とは JSAI(人工知能学会全国大会)は日本最大規模のAIに関する学術イベントであり、39回目の開催となりました。今年は史上最多の4939名(現地4032名、遠隔907名)の参加があったとのことで、昨今のAI技術への注目度の高さを感じられました。JSAIでは昨今注目を浴びている大規模言語モデルをはじめ、画像・音声処理、AIの応用・社会実装など数多くのセッションが開催され賑わいを見せていました。 本ブログではJSAIに参加したメンバで気になった発表を紹介したいと思います。 ※以下で使用する全ての画像は原論文で掲載されている画像を引用しております。 画像・3D AI応用関連 深度情報を用いた画像識別における解釈性向上に関する一考察 1 この研究では画像識別においてRGBと深度情報を融合し、異なるモディリティ間を統合的に扱うマルチモーダルモデルを一貫した解釈で特徴を捉えられるようにする手法を提案しています。 Vision Transformer(ViT) 2 などのモデルをベースにRGB、Depthを融合する従来の融合手法(Early-Fusion、Late-Fusionなど)では大きく以下の2つの課題があります。 RGB情報と深度情報それぞれが独立したエンコーダで学習されており、モダリティ間の相互作用が十分に考慮されていない 異なるモダリティ間で一貫した解釈を得ることが難しい 例えば、自動運転において歩行者を識別する際、深度エンコーダが歩行者に注目している一方で、RGBエンコーダがほとんど注目しないといったAttentionの不整合が生じることで、モデルの解釈性と信頼性が損なわれてしまいます。 これらの課題に対し、この研究では、RGBエンコーダと深度エンコーダ間でAttention Weightを相互に共有する新たなモデルを提案しています。この提案モデルに含まれるFusion機構(Share-Fusion)は、一方のエンコーダから出力されたAttention Mapをもう一方のエンコーダへ重み付けして反映させることで、双方のモダリティ間で相互作用を促し、注目箇所の矛盾を軽減することを目指します。これにより例えばRGBエンコーダが信号機に注目できなかった場合でも、深度エンコーダが信号機に注目していれば、RGBエンコーダも信号機に注目するように誘導し、より正確な予測とAttention Mapの一貫性向上を期待しています。 実験ではWashington大学提供のRGB-Dオブジェクトデータセット 3 を用いた画像分類タスクにおいて、Late-FusionモデルにAttention共有機構を加えた場合とそうでない場合で検証しています。 結果、画像分類精度において両エンコーダ間で相互にAttention情報をフィードし合うパターンが(0.7541)、従来のLate-Fusionモデル(0.7366)を上回る結果を示しました。提案のAttention共有機構がAttention Mapの一貫性向上に大きく寄与することが定量的に確認されました。 変電所設備を対象とした3次元物体検出 4 この研究では設備保守の自動化に向けて、変電所設備のLiDAR点群に対し、空間的配置を加味した3次元物体検出を検証しています。 研究では名古屋市内の変電所構内77kVエリアを据え置き型LiDAR(Leica BLK360)で測定し、この点群データに対し、「碍子(insulator)」「 可動スイッチ(connector)」「ラインスイッチの設備全体(whole)のオブジェクト」についてバウンディングボックス(BBOX)によるアノテーションを用いたものを実験データとし、2つの3次元物体検出手法を比較検討していました。 点群パターンマッチングを用いた3次元物体検出:対象とするデータラインスイッチは形状が普遍なため、wholeに含まれる点群と入力点群間でパターンマッチングを行い、検出を試しています。実験結果から、この手法は対象となる点群の大きさ、密度、欠損(オクルージョン)に検出性能が影響されると判明しています。特に碍子のような小さな個別のオブジェクトの検出は困難でした。 Transformerにもとづく3次元物体検出:Transformerを3次元物体検出に応用したアルゴリズムの1つである3DETR(3D Detection Transformer) 5 の適用を検討しています。3DETRは、PointNet++ 6 で点群から特徴量を抽出し、Transformerエンコーダ点ごとの埋め込み表現へと変換後、Transformerデコーダがクエリーに対するBBOXを出力するモデルです。広域の設備全体の点群を入力が困難であったため領域分割をし、データ拡張を実施していました。 実験の結果、事前学習モデルを利用したファインチューニングでは、検出性能が向上し、対象物体の種類や点群密度が異なる場合でも有効に機能することが明らかになりました。特に硝子やコネクタのような細かい設備を正確に検出できるだけでなく、点群の一部が欠けている場合でも、ある程度の位置推定が可能であることを示しています。 重み付き残差接続による境界品質を考慮したゼロショットセグメンテーション手法の提案 7 事前に学習していない物体を検出するゼロショットセグメンテーションにおいて、既存のRobust Segment Anything Model(RobustSAM) 8 の課題であるノイズのある劣化画像に対して頑健であると報告されています。一方で、RobustSAMはクリアな画像に対して物体の境界領域を正確に抽出できないことが指摘されています。この点に対して、本研究ではクリア画像と劣化画像の双方で精度を両立させる手法を提案しています。 RobustSAMはSAMを基盤として、SAMに劣化情報を除去する機構(Anti-Degradation Token Generation Module: AOTGおよびAnti-Degradation Mask Feature Generation Module: AMFG)を追加することで、雨や雪といったノイズが含まれる劣化画像に対しても頑健性を高め、高い精度でセグメンテーションを行うことが可能です。 しかし、RobustSAMはクリア画像に対してセグメンテーション領域が過剰に広がり、物体の境界を正確に推論できないという問題が確認されています。この研究ではその事象を定量的に評価するための指標である「Overflow Score (OS)」を定義していました。OSは、正解領域の輪郭付近に位置する領域の中で、予測領域が含まれている割合を示し、OSの値が大きいほど境界の外側を過剰に予測していることを意味します。評価実験では、RobustSAMはSAMに比べてOSが大きく、クリア画像・劣化画像ともに境界をうまく捉えきれていないことを定量的に示していました。 この課題に対し、研究は、劣化情報を除去する前の特徴量に境界情報が含まれている可能性に着目し、RobustSAMのAMFGに「重み付きの残差接続」のような機構を追加する新しいモデルを提案しています。 これにより、入力画像に応じて劣化情報を除去した特徴量を重視するか、除去前の特徴量を重視するかを柔軟に選択することが可能になります。 評価実験ではMSAR10kデータセット 9 と人工的に生成した劣化画像に対してIoUとOSを指標として評価しています。プロンプトが点の場合、クリア画像に対し提案手法のIoU:89.77%、OS:25.32%がRobustSAMのIoU:89.57%、OS:29.42%となり、有効性を示していました。劣化画像に対してもIoUは同等の精度(提案手法:89.08%, RobustSAM:89.27%)を保ちつつ、OSは2%優れる結果となっていました。 VLMに関する論文 Crosslingual Visual Promptにもとづくテキスト付き画像からの日常物体検索 10 ロボットが屋内外で撮影した画像の中から、ユーザーの自然言語クエリーに合致する日常物体を高精度に検索する手法を提案しています。日常物体を含む画像検索では、商品ラベルや標識などに含まれるscene text(画像中の文字情報)を考慮した検索が必要です。 例えば、「“Lipton” の前にある白い液体の入った容器」というユーザーからの自然言語クリエーがある場合、scene textを考慮することは不可欠です。しかし、従来のCLIP 11 やBEiT-3といったマルチモーダル検索は、視覚特徴と言語特徴の単純なマッチングに留まるため、容器ラベルなどのscene textを含む画像では文字情報を正しく活用できず、検索精度が低下しやすいという課題がありました。 そこで本手法では、画像内の文字領域をOCRで検出し、位置情報とともに特徴量化し、最終的に画像特徴量と統合するScene Text Visual Encoder(STVE)を導入しました。また、クエリーを「全文の意味」と「名詞句ごとの意味」に分けてエンコードし、両者を統合する Multi-Query Encoder(MQE)を設計しました。 これらによって、文字情報と視覚情報を統合した特徴量と、操作指示の意図を多粒度に捉えた特徴量をコサイン類似度で計算することで、scene textを含む日常物体検索において従来のモデルを大きく上回ることを定量・定性評価の両面から示しました。下の画像は定性評価での例であり、scene textを考慮した画像検索が従来のモデルに比べて可能であることを示しました。 判断根拠を説明する視覚言語モデルの自己改善手法 12 本研究では、Vision Language Model(VLM)が人間の主観に基づいて画像が美しいかを評価する画像の審美性評価タスクにおいて予測スコアとその判断根拠を同時に自然言語で生成し、自律的に性能を向上させる手法を提案しています。従来手法では、「スコア予測に偏ると説明文生成力が低下すること」「高品質な説明データを集めるには膨大な人手コストがかかること」「予測結果と生成される説明文の整合性を維持しづらいこと」の3つの問題がありました。そこで本手法では既存の画像に対してスコアが付与されたデータセットと指示学習済みVLMの能力を組み合わせ、自己改善によりVLMのスコア予測能力と判断根拠の説明能力を高め、また整合性の向上に努めています。 提案手法では、データセットの画像と正解スコアを条件としてVLMに説明文付きの応答を生成させ、「好ましい応答」と誤スコアを用いた「好ましくない応答」を生成し、両者を対比させるDirect Preference Optimization(DPO) 13 用の学習データを構築します。さらに、誤スコア応答中のスコア部分を正解値に置き換えることで、予測スコアと説明文の整合性を高めるためのDPOデータも自動的に作成し、説明の一貫性向上を図ります。これら二種のDPOデータセットで各々LoRAを適用したVLMモデルを別々に訓練した後、TIES-Mergingにより2つのモデルを重みレベルで統合する工程を繰り返すことで、スコア予測精度と説明整合性の双方を段階的に改善しました。 実験には画像の審美性評価の代表的ベンチマークであるAVA 14 とAADB 15 を使用し、各評価者の平均スコアを0から9まで10段階に離散化した上で、LLaVA-NeXT-7B 16 を中心とする0.5B~7Bパラメータの複数モデルにLoRAを適用して訓練しました。AVAデータに対してLLaVA-NeXT-7Bをベースとしたモデルでは、Zero-shotでのSpearman順位相関係数(SRCC)は0.446、GPT-4oを用いた説明整合性スコア(Cons)は3.36でしたが、4回目の反復後にはSRCCが0.739、Consが3.57へと大幅に向上し、説明文がスコアとより高い一貫性をもって生成されるようになりました。また、小型モデルでも同様の改善が確認され、提案手法の汎用性が示されました。 項目反応理論を用いた視覚言語モデルのマルチモーダルな推論能力および問題特性の評価 17 視覚言語モデル(VLM)の評価は、ベンチマークを通して画像とテキストを同時に扱うクロスモダリティーの能力を評価する必要があります。しかし、現状のベンチマークの問題には、画像やテキストのみをVLMに提供することで回答可能なショートカット問題が存在し、正しくVLMの性能を評価することが課題となっています。 そこで本研究では、MMMUデータセット 18 を「画像のみ」「テキストのみ」「両者併用」の3パターンでVLMに解かせ、正誤データをIRT(項目反応理論)モデルに入力し、各VLMの「画像処理能力」「テキスト処理能力」「両者統合能力」と各問題の難易度パラメータを同時に最尤推定しました。難易度パラメータはさらに、「選択肢由来の基本難易度」、「画像投入による難易度の低下量」、「テキスト投入による難易度の低下量」、「両者併用時による難易度の低下量」といった4種に分解し、各問題ごとに推定しています。 実験結果から「両者併用時による難易度の低下量」を用いることによって、テキストのみを用いて解けてしまうような問題の抽出と、画像とテキストどちらも正答を導く上で必要な問題の抽出を可能としました。 提案手法により抽出された画像を用いずにテキストのみを用いて比較的回答が導きやすい例。問題文のみから正答を導くことができる。 提案手法により抽出された画像とテキストの双方が必要な問題例。 ハルシネーション対策に関する論文 本章では大規模言語モデルのハルシネーション対策についてまとめました。モデルへの介入度合いによって次の2つのアプローチに分類しています。 アプローチA: 入出力の工夫のみ アプローチB: モデルデータにアクセスが必要 [アプローチA] 反論・再考プロンプトによるHallucination検出手法の提案 19 LLMに質問を繰り返した時の回答の揺らぎから信頼度を測る手法です。 質問を繰り返すアプローチとして有名な既存手法にSelfCheckGPT 20 と呼ばれるものがあり、これは高いサンプリング温度で回答を複数生成させて出力の一貫性を回答の確信度とみなすものですが、本手法ではより出力トークン数を抑えてコストの削減を狙っています。 本手法ではLLMのチャットセッションを3つ(Bot1, Bot2, Bot3)用意します。まずBot1に質問を投げ回答してもらい、Bot2にその回答に対する反論を考えてもらいます。そしてその反論をBot1に投げて再度回答してもらい、Bot3を用いて最初の回答と反論後の回答を比較して意見の一貫性を測ります。 もし意見が変化していなければBot1の回答には十分な信頼性を持つとみなし、反論に流され意見を変えていればハルシネーションを起こしているとみなします。 実験ではクイズを題材にした質問応答ベンチマークJAQKET 21 を用いて、知識問題に対してLLMがハルシネーションを起こしているかどうかを予測させました。その結果、出力トークン量を揃えた条件においてSelfCheckGPTよりも精度(Precision)は低く、再現率(Recall)は高くなることが分かりました。これはつまりハルシネーションを誤検知した数が多いということであり、ユーザーの反論に対してすぐに意見を翻してしまうというLLMの特性が如実に現れたといえます。 しかし再現率の高さから、回答の正確性が強く求められる分野ではこの手法が役に立つだろうと筆者は述べています。 [アプローチA] 反復サンプリングを活用したLLM推論時の外部情報検索機能の最適化 22 こちらはRAGを用いて回答の正確性を担保するアプローチです。RAGは具体的なソースに基づいて回答できるという利点がありますが、質問と関係のないソースを取得したり、ソースの読解に失敗して回答を間違えたりするという課題があります。 そこで本手法ではソース取得部分においては複数回質問することでソース検索用クエリーの一貫性を保ち、読解部分ではあらかじめテンプレートの回答例を作っておくことで回答の方向性を安定させるという複合的な方法で回答の信頼性を高めました。 実験ではWikipediaをもとにして作られたJEMHopQA 23 データセットを用いて、Wikipediaの検索を可能にした状態でLLMに回答させました。その結果、2つの工夫がそれぞれ回答の精度向上に寄与していることが示せました。 [アプローチA] 長文コンテキスト質問応答における大規模言語モデルによる誤引用文の訂正手法の提案 24 こちらもRAGアプローチで、LLMがソースとして利用した部分を正確に引用するための手法を提案したものです。 LLMに引用を任せてしまうと、時々勝手に文章を変えてしまうことがあります。そこで本手法ではLLMの出力した引用文が元ソースと一致しなかった時に、高い類似度の文章があればそれを抽出し、類似する文章が見つからなければLLMに引用文を自己修正させることを繰り返しています。 実験ではHotpotQA 25 と呼ばれる英語の質問応答データセットを用いて、回答の正確性と引用の質をそれぞれGPT-4oに判定させました。その結果、回答の正確性は既存手法より少し劣るものの、高々5回の自己修正で既存手法よりも高い質の引用文を提示できています。 [アプローチB] LLM内部演算値を用いたLLM回答の信頼度定量化とOOD検知方式 26 LLMが学習していないドメインに関する質問に対しては、LLM内部で計算される特徴ベクトルが正しく答えられた時の特徴ベクトルの分布から大きく外れるだろうという仮定に基づいた分布外(Out-Of-Distribution, OOD)検知手法に関する論文です。 質問文を入力している時または応答文を出力している時のLLMの各アテンション層のベクトル(アテンションベクトル)を抽出し、LLMが正しく答えられた質問応答に対するアテンションベクトルとのコサイン類似度を測ることで、その質問に対する応答の信頼性を見積もります。 実験ではあるドメイン(業務におけるQ&A)のデータに対してファインチューニングされたLLMを用いて、質問文がそのドメインに含まれているかどうかをアテンションベクトルを用いて判定させました。その結果、真陽性率・真陰性率ともに95%を超える高い正解率で判定できることを示しました。 本手法はハルシネーションそのものを検出できる訳ではありませんが、LLMをファインチューニングする時には想定していなかった質問内容を実用時に検出するという使い方ができそうです。 最後に 本ブログでは、私たちが興味を持ったJSAI2025の発表についてご紹介しました。NTTドコモビジネスでは、今回ご紹介した分野に限らず、画像や映像、さらには音声言語も含めたさまざまなメディアAI技術の論文調査や研究開発に今後も積極的に取り組んでいきます。 更家崚介, 清水良太郎, 後藤 正幸: "深度情報を用いた画像識別における解釈性向上に関する一考察", https://doi.org/10.11517/pjsai.JSAI2025.0_4N2GS705 ↩ Alexey Dosovitskiy, Lucas Beyer, Alexander Kolesnikov, Dirk Weissenborn, Xiaohua Zhai, Thomas Unterthiner, Mostafa Dehghani, Matthias Minderer, Georg Heigold, Sylvain Gelly, Jakob Uszkoreit, Neil Houlsby : "An Image is Worth 16x16 Words: Transformers for Image Recognition at Scale", ICLR 2021 ↩ Peter Henry, Michael Krainin, Evan Herbst, Xiaofeng Ren, Dieter Fox : "RGB-D Mapping: Using Depth Cameras for Dense 3D Modeling of Indoor Environments", IROS 2011 ↩ 瀬川修 : "変電所設備を対象とした3次元物体検出", https://doi.org/10.11517/pjsai.JSAI2025.0_3N5GS704 ↩ Ishan Misra, Rohit Girdhar, Armand Joulin : "An end-to-end transformer model for 3d object detection", ICCV 2021 ↩ Charles R. Qi, Li Yi, Hao Su, Leonidas J. Guibas : "Pointnet++: Deep hierarchical feature learning on point sets in a metric space", NeurIPS 2017 ↩ 永見陽輝, 櫻井洸介, 山極綾子, 後藤 正幸 : "重み付き残差接続による境界品質を考慮したゼロショットセグメンテーション手法の提案", https://doi.org/10.11517/pjsai.JSAI2025.0_3N1GS701 ↩ Wei-Ting Chen, Yu-Jiet Vong, Sy-Yen Kuo, Sizhuo Ma, Jian Wang : "RobustSAM: Segment Anything Robustly on Degraded Images", CVPR 2024 ↩ Ming-Ming Cheng, Niloy J. Mitra, Xiaolei Huang, Philip H. S. Torr, and Shi-Min Hu : "Global Contrast Based Salient Region Detection", CVPR 2011 ↩ 戸倉健登, 是方諒介, 小松拓実, 今井悠人, 杉浦 孔明 : "Crosslingual Visual Promptにもとづくテキスト付き画像からの日常物体検索", https://doi.org/10.11517/pjsai.JSAI2025.0_1Win452 ↩ Alec Radford, Jong Wook Kim, Chris Hallacy, Aditya Ramesh, Gabriel Goh, Sandhini Agarwal, Girish Sastry, Amanda Askell, Pamela Mishkin, Jack Clark, Gretchen Krueger, Ilya Sutskever : "Learning Transferable Visual Models From Natural Language Supervision", PMLR 2021 ↩ 丹治直人, 山崎 俊彦 : "判断根拠を説明する視覚言語モデルの自己改善手法", https://doi.org/10.11517/pjsai.JSAI2025.0_4A3GS1003 ↩ Rafael Rafailov, Archit Sharma, Eric Mitchell, Stefano Ermon, Christopher D. Manning, Chelsea Finn : "Direct Preference Optimization: Your Language Model is Secretly a Reward Model", NeurIPS 2023 ↩ Murray, Naila and Marchesotti, Luca and Perronnin, Florent : "AVA: A large-scale database for aesthetic visual analysis", CVPR 2012 ↩ Shu Kong, Xiaohui Shen, Zhe Lin, Radomir Mech, Charless Fowlkes : "Photo Aesthetics Ranking Network with Attributes and Content Adaptation", ECCV 2016 ↩ Haotian Liu, Chunyuan Li, Yuheng Li, Bo Li, Yuanhan Zhang, Sheng Shen, Yong Jae Lee : "LLaVA-NeXT: Improved reasoning, OCR, and world knowledge", https://llava-vl.github.io/blog/2024-01-30-llava-next/ 2024 ↩ 上林駿希, 増井建斗, 新恭兵, 包含, 鹿島久嗣, 大谷まゆ, 竹内孝 : "項目反応理論を用いた視覚言語モデルのマルチモーダルな推論能力および問題特性の評価", https://doi.org/10.11517/pjsai.JSAI2025.0_3N6GS701 ↩ Xiang Yue, Yuansheng Ni, Kai Zhang, Tianyu Zheng, Ruoqi Liu, Ge Zhang, Samuel Stevens, Dongfu Jiang, Weiming Ren, Yuxuan Sun, Cong Wei, Botao Yu, Ruibin Yuan, Renliang Sun, Ming Yin, Boyuan Zheng, Zhenzhu Yang, Yibo Liu, Wenhao Huang, Huan Sun, Yu Su, Wenhu Chen: "MMMU: A Massive Multi-discipline Multimodal Understanding and Reasoning Benchmark for Expert AGI", CVPR 2024 ↩ 山里飛鳥, 小山航平: "反論・再考プロンプトによるHallucination検出手法の提案", https://doi.org/10.11517/pjsai.JSAI2025.0_1Win429 ↩ Potsawee Manakul, Adian Liusie, Mark Gales: "SelfCheckGPT: Zero-Resource Black-Box Hallucination Detection for Generative Large Language Models", EMNLP 2023 ↩ JAQKET: クイズを題材にした日本語QAデータセット https://www.nlp.ecei.tohoku.ac.jp/projects/jaqket/ ↩ 藤田真伎, 駒田拓也, 吉村健, 藤本拓, 白水優太朗, 川口貴子: "反復サンプリングを活用したLLM推論時の外部情報検索機能の最適化", https://doi.org/10.11517/pjsai.JSAI2025.0_1Win443 ↩ 石井愛, 井之上直也, 鈴木久美, 関根聡: "JEMHopQA: 日本語マルチホップQA データセットの改良", 言語処理学会 2024 ↩ 萱場啓太, 山岡裕司: "長文コンテキスト質問応答における大規模言語モデルによる誤引用文の訂正手法の提案", https://doi.org/10.11517/pjsai.JSAI2025.0_2H4GS1102 ↩ https://hotpotqa.github.io ↩ 中川慎二, 小松亮太, 惠木正史: "LLM内部演算値を用いたLLM回答の信頼度定量化とOOD検知方式", https://doi.org/10.11517/pjsai.JSAI2025.0_1Win446 ↩
アバター
はじめに 背景 アーキテクチャ紹介 知見・ノウハウ まとめ はじめに こんにちは、イノベーションセンターの村田です。 NTTドコモビジネス株式会社は、日本最大級のネットワーク展示会である 「Interop Tokyo 2025(会場:幕張メッセ、会期:2025年6月11日〜13日)」 において構築される ShowNet に対し、全国の放送局からの映像を伝送するネットワークの一部をコントリビューションしました。 本記事では、映像伝送を実現したアーキテクチャ、およびその過程で得られた知見・ノウハウをお伝えします。 背景 Interop の準備から本番までは、次の3期間に分かれて進行します。 全国の放送局からの映像を伝送するネットワークについても、それぞれの期間に応じた検証・運用が行われました。 Pre-HotStage 期間(2025年5月12日〜29日) ShowNet を構築する前段階として、放送局の端末と渋谷の映像機器を接続し、映像伝送の可否確認やパフォーマンステストを実施。 HotStage 期間(2025年5月30日〜6月6日) ShowNet をゼロから構築・検証する期間であり、放送局の端末と幕張の映像機器を接続して、同様の映像伝送検証とパフォーマンステストを実施。 会期(2025年6月11日〜13日) 来場者へ ShowNet の披露が行われる中、放送局から幕張会場までの映像伝送を運用。 私たちがコントリビューションしたのは、上図のネットワークの赤い箇所となります。 全国の放送局から ShowNet までの映像伝送を安全に実施するため、ネットワークにはセキュア(閉域)かつ高信頼であることが求められました。 映像伝送を実現するために、以下のサービスを活用しました。 アクセスプレミアム フレキシブルモバイルアクセス(FMA) (以下、FMA) 放送局のモバイルネットワークに対し、交換機と仮想ゲートウェイを提供 Flexible InterConnect (以下、FIC) AWS や ShowNet 、FMA の仮想ゲートウェイに対し、閉域ネットワークを提供 Amazon Web Services (以下、AWS) Pre-HotStage 期間に、放送局端末と渋谷の映像機器をつなぐ模倣環境として活用 IOWN Open APN FIC から ShowNet までを、フォトニクスネットワーキングのオープンアーキテクチャで接続 アーキテクチャ紹介 私たちがコントリビューションしたアーキテクチャはこちらです。 FIC-Router 各放送局とバックアップ回線を AWS へ接続する仮想ルーター FIC-Port ShowNet からの物理回線を終端して FIC-Router へ接続するリソース AWS Direct Connect ゲートウェイ (以下、DXGW) FIC-Router と AWS を接続 AWS Site-to-Site VPN AWS と Pre-HotStage 拠点を接続 AWS Transit Gateway (以下、TRGW) 上記の AWS リソースを集約接続するルーター 放送局 3 局とバックアップ回線の計 4 本の接続に対し、FIC-Router ごとに個別の DXGW と異なる AS 番号を割り当て、経路を分離しました。 FIC-Router と各 DXGW、DXGW と TRGW、TRGW と Site-to-Site VPN はそれぞれ BGP により相互接続され、会期中はトラブル無く映像伝送を実現できました。 映像伝送するメインのネットワークは上図の通りですが、HotStage 期間中も Pre-HotStage 期間で作成した AWS への接続を維持し、接続検証やパフォーマンス評価として効果的に活用できました。 知見・ノウハウ 今回のコントリビューションを通じて得られた知見・ノウハウを2つ紹介します。 1つめは、FIC のモニタリング機能を活用したトラブルシュートの効率化です。 FIC は各 BGP コネクションの接続状況や受信経路をグラフィカルに表示します。 例えば、BGP コネクションが成立すると緑、不成立なら赤を示します。 HotStage 期間中、FIC-Router と ShowNet 間で 4 つの BGP コネクションを順に確立し ping 確認したところ、4 つめだけ疎通できない事象が発生しました。 ShowNet 側のルーターから FIC-Router の詳細状況を確認できず、原因特定が難しい状況でした。 しかし、FIC のモニタリング画面で 1 つの BGP コネクションが赤く表示されており、疎通不可原因がコネクション不成立によるものと特定できました。 これにより迅速な復旧が可能となり、現場対応の効率化につながりました。 2つめは、柔軟な検証・トラブル対応を可能にするネットワーク設計です。 Pre-HotStage 期間中の検証や接続確認の過程で、パフォーマンス測定やトラブルシューティング用の仮想マシンが必要になりました。 TRGW をハブとする構成にしていたため、必要な仮想マシンをすぐに各ネットワークと接続でき、突発的なニーズにも柔軟に対応できました。 実際に用意した仮想マシンは、検証用途だけでなく本番中の状況確認にも活用され、安定運用を支える重要な要素となりました。 まとめ 本記事では、全国の放送局からの映像を ShowNet へ伝送するネットワークのアーキテクチャと、そこで得た知見・ノウハウを紹介しました。 今後もネットワークとクラウドの連携検証をさらに進め、得られた知見をもとに、柔軟で高信頼なネットワーク構成の展開に取り組んでまいります。
アバター
NTTコミュニケーションズ開発者ブログチームの小林です。 私たちNTTコミュニケーションズ株式会社は、明日2025年7月1日より社名をNTTドコモビジネス株式会社と改め、NTTグループの法人向け総合ICT事業の中核として新たな出発を迎えることとなりました。 www.ntt.com これに伴い、1999年の設立より親しまれてきたロゴマーク(シャイニングアーク)と、NTT Comの名称については、本日で利用を終了します。 NTT Comの設立は、ちょうど26年前の1999年7月1日でした。長距離・国際通信事業を担う存在としてスタートを切り、いまその歴史に区切りを迎えることとなります。音声通信と固定回線が中心だった事業環境は、その26年の間に大容量のデータ通信と固定・モバイルの複合環境に変化してきました。クラウドやセキュリティなど扱う領域も広がり、私たちの開発者ブログでもそうした記事をたびたびお届けしてきました。 とはいえ、私たちの仕事は明日からも変わらず続いていきます。もちろんこのブログも続きます!(ドメイン、記事URLもそのままです) ただちょっと見た目が変わります。せっかくなので、タイトルロゴだけ先行公開します。 ちなみにここまでのNTT Com Engineers' BlogのStatsです。 記事数: 543件 うち、2021年のリニューアル後に書かれた記事: 324件 累計はてブ数: 14604件 読者のみなさまのおかげで続けられています。ライターにとっても大きな励みになっています。ありがとうございます。 今回、同じくドコモグループのNTTコムウェア株式会社もNTTドコモソリューションズ株式会社に、NTTグループにおいても日本電信電話株式会社がNTT株式会社に、といったように複数社の商号が変わります。併せてCIも刷新します。新たなステージに進むNTTグループ、ドコモグループの今後にぜひご期待ください!
アバター
こんにちは、イノベーションセンターの安井です。 この記事では、Open XR Opticsという新しい光伝送技術が実装されたトランシーバーの実機検証結果をご紹介します。 本記事で以下を扱います。 1つの光信号を複数に分け、柔軟な帯域保証を可能にする新技術「Open XR Optics」 実機を用いた検証により、その基本性能、障害からの復旧挙動、そして実用上の注意点 5G基地局やデータセンター間接続など、将来のネットワークを支える技術の展望(可能性と課題) P2MP技術 Open XR Opticsとは 検証 検証構成 基本動作の確認 パワー 周波数 自動調整の完了に要する時間 障害復旧の挙動 CH番号を衝突させた場合の動作 ROADM を途中に入れた場合の干渉 今後の展望 1. 省電力・小型化 2. マネジメント・セキュリティ機能 3. 復旧時間短縮 P2MP技術 光ファイバ通信においてPoint-to-Multipoint(P2MP)とは、1つの局から複数のユーザと同時に通信するトポロジーを指します。 現在の主流方式はパッシブ光ネットワーク(PON) 1 です。 PONでは光スプリッターを用いて1本の光ファイバ回線を複数ユーザに分岐共有し、単一の局(OLT)から多数のエンドポイント(ONU) 2 へ通信します。 FTTHやモバイルフロントホール 3 で広く採用されています。 P2MP方式のメリットは、一本の光ファイバや装置で多数のユーザをまとめて収容できるコスト効率にあります。 通信事業者は機器やファイバ敷設の費用を大幅に削減できます。 一方でデメリットもあり、1波を時間で分割するTDM・TDMA方式 4 のため、ユーザ毎の帯域は同時アクセス数に左右され帯域保証が難しいです。 また従来型のGPONやXG-PON等 5 では下りに対し上り速度が遅く、映像配信やビデオ会議など上りを多く使う用途でボトルネックの原因となります。 さらに、TDMA制御による通信遅延およびその揺らぎ(ジッタ)が発生し、リアルタイム性が求められる用途では制約があります。 Open XR Opticsとは 近年、帯域保証型の光伝送方式の1つとしてデジタルコヒーレント方式が普及しています。 光信号の振幅や位相といった波の性質をデジタル信号処理で高度に制御し、従来よりも長距離かつ大容量の通信を可能にした技術です。 主に長距離のバックボーン回線や都市・DC間の大容量通信で広く利用されていますが、Point-to-Point(P2P)接続に限られており、P2MP構成を実現できません。 Open XR Opticsは、1つの光信号をデジタル信号処理によって複数のチャネル(サブキャリア)に分割することで、P2MP構成でも帯域保証型の光伝送を実現します。 各サブキャリアは独立に変調・復調・帯域制御が可能であり、異なる宛先ごとに必要な帯域を柔軟に割り当てて送信できます。 例えば400GbpsのHub側(集約側)トランシーバーからは、25Gbpsのサブキャリアを最大16本生成可能です。 Leaf側(各末端側)には必要な帯域分だけのサブキャリアが割り当てられ、P2MP接続が実現します。 PONとOpen XR Opticsの違いを下表にまとめました。 項目 パッシブ光ネットワーク(PON) Open XR Optics 通信方式 TDM/TDMA(時間分割) DWDM(波長分割) 帯域 共有(ベストエフォート) 専有(帯域保証) 遅延 TDMA制御による遅延・ゆらぎあり 極めて小さい 主な用途 FTTH(一般家庭向け) 5G基地局、企業拠点間接続 コスト 低コスト 比較的高コスト ここからは、Open XR Opticsの特徴をもう少し具体的に説明します。 柔軟な帯域制御と帯域確保: 400Gコヒーレント信号を25G単位のサブキャリアに分割し、必要に応じて1ユーザに複数サブキャリアを割り当てられます。 これにより、各Leaf側ノードへの帯域配分を需要に合わせ調整可能です。また1サブキャリアは専有帯域のため、他ユーザの影響でスループットが低下することはありません。 PONのようにフレーム単位でのTDMA制御が不要なため遅延も極めて小さいです。 双方向(Bidirectional: BiDi)伝送対応: 通常のコヒーレントトランシーバーのような2芯伝送だけではなく、1芯の光ファイバで1芯双方向通信(BiDi)を行うことが可能です。 PON等のアクセス系ネットワークではファイバのコストを抑える目的で、往復に別々のファイバを用意せず1芯で下りと上りを異なる波長に分けて同時伝送しています。 同様にXR OpticsでもHub-Leaf間は一本の光ファイバで接続可能で、異なるサブキャリア(波長)を上下に割り当てることでBiDiを実現します。 既存光インフラとの親和性: XR OpticsはPONと同様に光スプリッターを用いたP2MPですので、既存のアクセスネットワーク用ファイバをそのまま活かすことができます。 また、XR OpticsはCバンド帯 6 (波長1530 - 1565 nm)のコヒーレントDWDM技術 7 ですが、この波長帯は主流方式のGPON等では利用されていないため、既存のPONを使用するファイバにオーバーレイが可能です。 これにより、新規にファイバを引き直すことなく現行PON網に高帯域サービスを追加導入することも検討できます。 ただし、コヒーレントDSP(デジタル信号処理プロセッサー)を搭載したトランシーバは現状ではPON用ONUなどより高価で消費電力も大きく、搭載可能な装置も限られます。 つまり大量の宅内ONUを安価に配布するFTTH用途には不向きであり、むしろ高帯域を要求する5G基地局や企業の拠点間接続、メトロアクセス回線の集約などに適しています。 Open XR Forumのwhite paperでは、主に次のような利用シーンが想定されています。 今後さらに決定的なユースケースが現れることにも期待しています。 Open RAN 8 /モバイルxHaul 9 向け: 基地局〜集約装置間をP2P、あるいは複数局をP2MPで束ねる 企業拠点間の専用線サービス PONオーバーレイ型専用線サービス: 既設PONファイバにCバンドを重畳し、中小拠点をP2MPで収容 メトロネットワーク集約: P2MPを柔軟に組み合わせて回線数を削減 次章では、実際にXR Optics機器を用いた検証環境を構築し、そのユースケース適性や動作検証結果を紹介します。 検証 実際に Open XR Optics のQSFP-DDトランシーバーをHub 1個/Leaf 2個(最大 100G x2)使用し、検証を実施しました。 住友電気工業株式会社から Open XR Optics対応スイッチ(FTU9100)や、Infinera社(現在Nokia社の傘下にあります)製のQSFP-DDトランシーバー等を借用しました。 下記写真でサイズ比較すると、同じQSFP-DDの400G OpenZR+ トランシーバーよりもヒートシンク部分が厚いです。 検証構成 Hub 側: QSFP-DD Open XR Optics(400G)トランシーバー 1個 Leaf 側: QSFP-DD Open XR Optics(100G)トランシーバー 2個 スプリッタ: 1:2(50:50) イーサネットテスター: 100Gポート x 4 で最大 200G の双方向試験が可能 基本動作の確認 Open XR Opticsのパワーや周波数設定はほぼ自動です。 下記のような仕様になっています。 パワー Hub:出力パワー指定が可能 Leaf:出力パワーは自動制御 ユーザが明示的に変更する手段はなく、リンクアップ時に自動調整されます 周波数 Hub:中心周波数を指定可能 Leaf:Hubの周波数を中心として自動割り当て スイッチ側でLeaf毎にCH番号を設定すること、HubとのリンクはLeaf毎に別々となります CH番号はHub側の電気レーンとサブキャリアの対応づけを示し、Leaf毎に割り当てるという意味になります 自動調整の完了に要する時間 スイッチ側にHubとLeafとしてのポートを設定した後、リンクアップするまでにパワーと周波数の自動調整にどの程度時間がかかるかを確認しました。 2分岐でリンクアップまで約2分30秒必要でした。 Leaf側トランシーバーは2個のまま、Hub側の設定のみ4分岐にしてみると約4分かかり、分岐数が増えるほど再調整に時間がかかる傾向のようです。 一般的なコヒーレントトランシーバーよりやや遅い印象です。 Hub側のパワーや周波数指定をリンクアップ後に変更する場合、再自動調整のため約2分30秒待つ必要があります。 障害復旧の挙動 Leaf側で物理的な障害が発生した際の動作を確認しました。 ケーブルを抜去し戻した場合、リンクの復旧には30秒要しました。 トランシーバー自体を抜去し戻した場合、自動調整のため約2分30秒かかりました。 いずれも当該LeafのみリンクダウンするためHubや他Leafの通信に影響は及ぼしませんが、やはり再調整を伴う場合は一般的なコヒーレントトランシーバーより復旧が遅くなります。 CH番号を衝突させた場合の動作 leaf側で設定するCH番号はLeaf毎に別々に設定する必要がありますが、故意に設定を重複させた場合の動作を確認しました。 CH番号を故意に重複させると、先に確立しているLeaf(Leaf1とする)はリンクアップしたままですが、後から設定したLeaf(Leaf2とする)はリンクアップしませんでした。 この状態を維持したままLeaf1をリンクダウンさせた場合、Leaf2が元々Leaf1で使用していたCH番号でリンクアップしました。 このことから、CH番号を重複させることで障害や誤設定を契機にリンクの乗っ取りが可能です。 ROADM を途中に入れた場合の干渉 実際のNW応用を想定し、OpenXR OpticsをROADM経由で接続した場合、自動調整機能がROADMと干渉しないかを確認しました。 ROADM区間は実際にデータセンター間の通信で利用している区間(約6km/4.4dB)を使用しました。 Hub側の送信光パワーは手動指定で常時一定のため、問題なくLeafまで到達できます。 Leaf側の自動調整は特に問題なく、ROADMのパワー自動調整機能との干渉は発生せず無事リンクアップしました。 今回は問題ありませんでしたが、ROADMの機種によってはROADMの受光可能範囲が狭めに制限されているため、OpenXR Opticsの自動調整されたパワーが合わずROADMのリンクに悪影響を及ぼす恐れがあります。 今後の展望 検証を通して、OpenXR OpticsがコヒーレントDWDM技術を用いてP2MPを実現したことを確認できました。 Open XR Opticsを実用レベルの運用に適用するは以下のような課題が考えられます。 1. 省電力・小型化 Leaf側でも消費電力や発熱が大きく、QSFP-DDのフォームファクターであるため搭載できる機器は限られます。 より小型・低価格になればアクセス系に適用しやすくなります。 2. マネジメント・セキュリティ機能 ユーザ毎の運用監視のためにサブキャリア単位での状況把握機能と、ユーザ毎のセキュリティを担保する機能が必要です。 今回の検証では未実装でしたが、サブキャリア毎の管理機能を含むマネジメント機能はOpen XR Optics Forumで検討中です。 3. 復旧時間短縮 分岐数が増えた際の再調整時間を数秒レベルに抑えられるかは、適用できるサービスレベルを左右します。 とはいえ概ね動作に問題はなく、多拠点を効率的に収容できる新たな光伝送方式として今後期待できます。 本記事が、Open XR Optics による P2MP 伝送や光伝送技術への理解の参考になれば幸いです。 今後も検証や標準化動向を追いかけ、最新情報を共有していきます。 光スプリッターという電源不要の受動素子で光ファイバを分岐し、一本の光ファイバを複数ユーザ間で共有するネットワーク方式。主に一般家庭向けのFTTHサービスで使用される。 ↩ OLT(Optical Line Terminal)はPONで通信事業者の収容局に設置される装置。複数のONUと通信し、ユーザ側へのデータ送受信を管理する。ONU(Optical Network Unit)はユーザ宅や拠点側に設置される装置。OLTと通信し、光信号を電気信号に変換してネットワーク接続を提供する。 ↩ 携帯電話基地局内の無線装置であるRU(Remote Unit)と基地局制御装置であるDU(Distributed Unit)を接続する区間。DUは複数のRUと接続し、通信を集約する役割を持つ。 ↩ 時間軸で複数ユーザーの通信を順番に切り替え、一本の回線を共有する通信方式。 ↩ GPONは下り最大2.5Gbps、上り最大1.25Gbpsの速度を提供するPONの一種。XG-PONはGPONの後継規格で、下り最大10Gbps、上り最大2.5Gbpsまで速度向上した規格。 ↩ 光通信で一般的に使用される波長帯域(1530〜1565 nm)。伝送損失が最も低い波長であり、長距離・大容量通信で広く用いられる。 ↩ DWDM(Dense Wavelength Division Multiplexing)異なる波長の光信号を一本の光ファイバ上で同時に高密度多重化し、伝送容量を大幅に向上させる技術。 ↩ 携帯電話基地局の無線アクセスネットワークをオープンな規格で構築する取り組み。さまざまなベンダー製品が混在可能で、ベンダーロックインを防ぐ目的がある。 ↩ モバイルネットワークにおけるフロントホール、ミッドホール、バックホールを総称した用語。フロントホールはRUとDU間、ミッドホールはDUとCU(Central Unit)間、バックホールはCUとコアネットワーク間の通信を表す。 ↩
アバター
こんにちは、NTT Com イノベーションセンターのNetwork Analytics for Security(NA4Sec)プロジェクトです。 この記事では、2025年5月20日-23日に開催されたセキュリティカンファレンスBotconf 2025について紹介します。 Botconfとは Team NA4Secとは 講演内容 カンファレンスの様子 おわりに Botconfとは Botconf は"The Botnet & Malware Ecosystems Fighting Conference"の略称で、その名の通り、ボットネットやマルウェアエコシステムに特化した国際的なセキュリティカンファレンスです。 2013年より毎年フランスで開催されており、昨年はNice(ニース)、12回目となる今年はAngers(アンジェ) *1 で開催されました。 本カンファレンスでは、法執行機関・学術機関・CSIRT・脅威分析チームなどを中心に世界中のさまざまな組織から約400名が集結し、最新の分析結果について共有します。 特徴的なのは、その情報共有の深度です。 一部の講演は YouTube で公開される一方、TLP(Traffic Light Protocol) *2 への配慮が徹底されており、オープンな場では共有できない、クローズドなコミュニティだからこそ話せる貴重な情報交換が活発に行われます。 Team NA4Secとは 「NTTはインターネットを安心・安全にする社会的責務がある」を理念として、インターネットにおける攻撃インフラの解明・撲滅を目指すプロジェクトです。 NTT Comグループにおける脅威インテリジェンスチームとしての側面も持ち合わせており、有事において脅威インテリジェンスを提供し、意思決定を支援することもあります。 イノベーションセンターを中心として、NTTセキュリティ・ジャパンやエヌ・エフ・ラボラトリーズからもメンバーが参画し、日夜攻撃インフラ *3 を追跡しています。 今回、Team NA4Secから皆川、神田、鮫嶋の3名がBotconf 2025で講演してきました。 講演内容 NA4Secからは"Operation So-seki: You Are a Threat Actor…"というタイトルで、親ロシア派ハクティビスト *4 グループを長期追跡した分析結果について講演しました。 講演では、ハクティビストを追跡するための技術手法や、それによって明らかになった長期的な活動傾向について共有しました。 さらに、こうした政治的背景を持つ攻撃者と対峙する中で得られた学びとして、「脅威アクターの性質に合わせたインテリジェンス共有」の重要性を提言しました。 非常にセンシティブなテーマでしたが、講演後にはヨーロッパの法執行機関や軍の関係者の方が直接私たちの元へ訪れ、深い関心を示してくれました。 また、「インテリジェンス共有」のパートについては、フランスやインドのリサーチャーからも「我々も全く同じ問題意識を持っている」と声をかけられ、国を越えて意気投合できたのは大きな収穫でした。 現在、日本国内で能動的サイバー防御が大きな展開を見せる中でも、海外の有力な会社・組織と個別の繋がりを築くことができたのは大きな意義があると感じています。 登壇者の鮫嶋(左: @islairand )、神田(中央: @ashy0x41 )、皆川(右: @strinsert1Na ) 本講演で扱った情報の多くは会場限定となりますが、公式サイトに講演資料が掲載されていますので、ご興味のある方はぜひご覧ください。 https://www.botconf.eu/botconf-presentation-or-article/operation-so-seki-you-are-a-threat-actor/ カンファレンスの様子 カンファレンス会場は"Le Parc des Expositions d'Angers"という施設で、アンジェの中心部からバスで20分ほど離れた場所に位置しています。 会場内は広く、多数用意された座席も人気の発表時にはほぼ満席になるほどの盛況ぶりでした。 発表の合間にある休憩時間には、コーヒーを片手にあちこちで参加者同士の活発な交流が見られました。 メインカンファレンス2日目の夜には、立食形式のGala receptionが開催され、世界中の参加者と交流を深めることができました。 おわりに Botconfは、信頼できるコミュニティ内で最新の調査結果をTLPに基づき深く共有できる、世界でも数少ない貴重な場所だと改めて感じました。 次回(2026年)はフランスの北東部、Reims(ランス)で開催されるとのことです。 この記事を読んで少しでも興味を持たれた方は、ぜひ参加を検討してみてはいかがでしょうか。 *1 : フランス西部の都市で、パリから高速鉄道で約1時間30分。住みたい街ランキング上位としても知られる *2 : FIRSTが標準化した機密情報共有のプロトコル 。情報の機微性に応じて「RED(共有禁止)」「AMBER(組織内共有可)」などのレベルで区別し、機密情報の不用意な拡散を防ぐ仕組み *3 : 攻撃者の管理するマルウェアや C&Cサーバ など *4 : 政治的・社会的な意思表示や目的実現のために、ハッキングという手段を用いる活動家。"Hack"と"Activist"を組み合わせた造語
アバター
こんにちは、イノベーションセンターの福田です。 NTT コミュニケーションズ株式会社は、日本最大級のネットワーク展示会である 「Interop Tokyo 2025(会場:幕張メッセ、会期:2025年6月11日〜13日)」 において構築される ShowNet に対し、生成 AI と Model Context Protocol (MCP) による 5G コアオペレーション自動化のコントリビューションを行いました。 本記事ではその構築にあたって具体的にどのようなことを開発したのか、その実装を通して得た知見やノウハウを紹介します。 背景 どんなことをしたのか Model Context Protocol (MCP) とは? サーバー・クライアント・ホスト アーキテクチャ 動かしてみてわかったこと MCP を利用することによるメリット MCP を利用する上での注意点 MCP のツールの網羅性 MCP のツール名について まとめ 背景 5G コアで設定するパラメータは標準の識別子や設定項目を網羅的に対応しようとすると、多数のパラメータ管理が必要になります。 さらにいくつかのパラメータを中心とした依存関係が形成されており、その依存関係にしたがって設定値を更新したり削除したりしなければなりません。 今回、この 5G コアのパラメータを生成 AI で全部あるいは一部を変更することを立案し、生成 AI によるオペレーション自動化にチャレンジするということになりました。 どんなことをしたのか こうした背景から、生成 AI が既存データから必要なデータの構造や値を把握して、オペレータの指示する通りに 5G コアのパラメータを処理する仕組みを作ることになりました。 まず始めに生成 AI の動作環境とモデルについて決めました。 今回、 5G コアの環境が Amazon Web Services 上で構築されるため、そちらに合わせて Anthropic PBC の Claude や Amazon.com, Inc. の Amazon Nova といったさまざまなモデルが利用可能である Amazon Bedrock を用いました。 生成 AI のモデルは Anthropic PBC が提供する Claude 3.7 Sonnet を用いることにしました。 当初、以下のサービスや機能を組合せて実現しようとしていました。 Amazon Bedrock にて機能提供されている Amazon Bedrock Agents による AI エージェント Amazon OpenSearch Service による Retrieval-Augmented Generation を組み合わせた構成を検討しました。 これらのマネージドサービスを用いることで開発工数を抑えながら、事実(既存のパラメータ)に基づいた生成ができると考えたためです。 なかでも Amazon Bedrock Agents には AWS Lambda の関数を設置し、その関数を事前に定義したパラメータで呼び出すことができるという機能があります。 始めはこの機能を用いることで必要なパラメータをやり取りすることを検討していました。 ところが、先述したとおり、 5G コアのパラメータ数が非常に多く用途毎に設定がわかれていました。 そのため、命令の度に柔軟にパラメータ数を幅広く増減させる必要があるのに対し、 Amazon Bedrock のパラメータ定義はデフォルトで5つしか設定できず(※ある程度上限緩和は可能)柔軟さに欠けることが判明しました。 そのため Amazon Bedrock Agents の代替手段の調査にのりだしました。 その中で昨今ホットな話題である MCP を用いることで柔軟にツールの利用やコンテキストの提供を実装可能だということがわかってきたため、 MCP での実装に切り替えました。 その結果 MCP を用いて 5G コアのパラメータを生成 AI が生成・設定可能な構成が実現しました。 さらにオペレータ側が自然言語による指示を実施すると生成 AI の方でその指示に合わせて各種パラメータ操作のオペレーションを実施するサービスを実装することに成功しました。 以下ではその実装を通して得られたノウハウや実際の構築例を通して MCP の使い所について共有します。 Model Context Protocol (MCP) とは? まず、本題へ入る前に何度か出ている Model Context Protocol (MCP) について知らない方向けに解説をします。 もしすでにご存知であれば本章は読み飛ばしていただいて構いません。 Model Context Protocol (MCP) は Anthropic PBC が提唱する、 AI モデルが各種サービスやさまざまなドキュメントへのアクセス、写真や画像といったものを統一して扱えるようにするためのオープンなプロトコルです。 Anthropic PBC は公式ドキュメントの中で AI 用の USB-C ポートという表現を用いていますが、まさしくその表現の通り AI モデルがさまざまなものへアクセスするのに使用する統一的なインターフェースを提供します。 Think of MCP like a USB-C port for AI applications. Just as USB-C provides a standardized way to connect your devices to various peripherals and accessories, MCP provides a standardized way to connect AI models to different data sources and tools. ref: Model Context Protocol - Introduction 最近では AIOps という AI によるシステムの運用を自動化するパラダイムが提唱されていますが、日々さまざまな機能の開発や改修が行われているサービスやシステムを AI が扱うにはそれらのサービスのさまざまな操作方法や知識を AI も取り込まなければなりません。 そこで AI モデルを開発する各社では生成 AI にツールを操作させたりコンテキストを提供する方法を提唱しています。 たとえば ChatGPT を提供する OpenAI では Function Calling という AI がツールを呼び出すためのインターフェースを定義しています。 MCP もそのうちの1つですが、 Function Calling が構造上、生成 AI の実行環境に合わせてその処理フローを実装する必要があるのに対し、以下に説明するクライアント・サーバーの構造を取ることで個別の処理フローを実装する必要がなくなっています。 サーバー・クライアント・ホスト MCP は以下の3つのコンポーネントからなります。 サーバー AI が利用したいサービスやシステムに MCP で通信させるためのインターフェースを提供する クライアント サーバーと接続するためのインターフェース ホスト IDE などの MCP を利用した処理を行うプログラム ref: https://modelcontextprotocol.io/introduction これらはアプリケーションやコンテナーで1つにまとめられる場合もあれば別々に運用されることもあります。 これらを適切に実装・提供して組み合わせることで AI がさまざまなシステムやサービスを操作できるようになります。 アーキテクチャ ここまで MCP について解説しました。 本項ではこの MCP を実際に 5G コアオペレーション自動化のミッションを解決するにあたってどのようにシステムに組み込んでいったのかをアーキテクチャの観点から解説します。 今回我々が自動化するにあたって作ったシステムは次の通りです。 5G コアのパラメータは Amazon Relational Data Service へ保管されており、その DB 自体は 5G コアが操作するものであったため直接書き込み操作はせず、変わりとして GitLab 上にパラメータが設置してあり、そのパラメータを操作すると 5G コアに設定が同期するようになっていました。 そこで、 DB については直接書き込みをしないようにリードレプリカを作成し、ここから最新の 5G コアパラメータを取得できるようにした上で生成した 5G コアパラメータを GitLab へ書き戻す、という構成をとることになりました。 この構成を元に、我々は以下のコンポーネントでオペレーション自動化システムを作成することにしました。 DB のリードレプリカから 5G コアパラメータを取得する REST API サーバー オペレータが生成 AI とやりとりし、生成 AI が指示された内容をもとに REST API サーバーや GitLab を操作する MCP ホスト AI モデルを提供する Bedrock このうち、 REST API サーバーは Amazon Elastic Kubernetes Service (以下、 EKS) を構築し、その上でサービスとして公開しました。 サービスは同一ネットワーク上からアクセスできるように Elastic Load Balancing における Network Load Balancer を使ってネットワーク内に公開しました。 また、 MCP ホストはパブリックな場所に Amazon EC2 の インスタンス を設置し、その上に Docker Compose 化して動作させました。 これによってユーザーがインターネットを通してインスタンスから MCP へアクセスできるようにしています。 今回、 REST API サーバーと MCP ホストは元々ローカル環境で開発・検証しやすいようにコンテナー化して Docker Compose にて検証基盤を立ち上げて検証できるようにしていました。 本来であればこの Docker Compose 環境と同じように EKS や Amazon Elastic Container Service で REST API サーバーと MCP ホストを配置すると思います。 ですが今回は短い期間で上記環境を動作させねばならなかったことや頻繁に変更を伴う検証をしながら実装したため、実装・スケジュール優先で確実に実現できる形で実施しています。 動かしてみてわかったこと 今回上記環境を構築してみていくつかわかったことがあるため紹介します。 MCP を利用することによるメリット 今回、 MCP を実装・利用してみると、実際に Anthropic が述べているような MCP は USB-C のようなものであるということが実感できました。 MCP 自体はまさしくさまざまなシステムを AI が操作しやすくするための標準化手法であり、その点こそ利用することのメリットになっていることを実感しました。 システムがどんなツールを提供しているのかや、どんなコンテキスト情報を与えられるのか、システムを上手く使うためにはどのようなプロンプトを提供する必要があるのかといったことを統一されたインターフェースで提供します。 これによって AI は各システムの実装差異であったりインターフェースの違いといったものを意識せずに統一した方法でアクセスできます。 ちょうどプログラミング言語におけるインターフェースと同じように中身を知らなくてもその操作方法さえわかってしまえばいいわけです。 MCP そのものは新しい技術ではなく既存技術の組み合わせで実現されているため、技術的な部分で押さえておかなければいけないところは少ないです。 ですが、標準化によって生成 AI が事前に知識がなくてもシステムを操作できることが示されたというところは押さえておく必要があります。 MCP を利用する上での注意点 MCP のツールの網羅性 実装途中で気付いたのですが、生成 AI が MCPでシステムを上手く操作できるかは、 MCP サーバが提供するツールの網羅性に左右されます。 たとえば今回使用した GitLab MCP Server でその網羅性が問題になりました。 今回、生成 AI が GitLab のリポジトリーからファイルを探索する際に ファイルの中身を取得する API を使っていました。 この API はディレクトリー名を指定すると 404 Not Found を返すような仕様になっていました。 このエラーを生成 AI はエラーがあったという事実だけを見てしまい、ファイル探索ができないと判断して所望のファイルを取得するところまで実施せず、探索を打ち切ってしまうようになっていました。 その影響でファイルが検索できずに新しい探索タスクを再度実施するという事象が発生するようになっていました。 そのため、パラメータ変更操作のようなファイルを特定する探索タスクがあるとその探索途中にエラーを認識しては再度探索をためすという操作を繰り返してしまい、全体として操作にかかる時間が長くなってしまうという事象が発生しました。 そこで我々の方で GitLab MCP Server へファイルをリストするツールを実装してみました。 すると生成 AI 側で探索タスクの処理内容を以下のように変更してくれました。 リストするツールを用いてファイルの位置を把握 ファイルの中身を取得する API を実行 これによってファイルの中身を取得する API でディレクトリー名を指定してエラーになるのを防ぐことができました。 さらにエラーの発生が抑えられることで探索の繰り返し頻度も少なくなり、生成 AI が実施する探索の時間を非常に短く抑えることに成功しました。 このように、提供される MCP サーバーのツールに網羅性がないと生成 AI は期待通りにシステムを操作してくれません。 さらに生成 AI が別の方法を試そうとするも提供できるツールは同じであるために何度も同じツールをつかってエラーを発生させるということを繰り返すようになってしまいます。 もし MCP サーバーを実装する場合は生成 AI にどんな操作をしてどんな値を返し、どんなエラーを返すのかというところを洗い出しておく必要がありあす。 MCP のツール名について 今回実装してみてツールの名前には名前空間の概念がないことに気が付きました。 そのため、 MCP サーバー側は提供するツールの名前衝突に十分気を付ける必要があります。 GitLab MCP Server を参考にすると、以下のようにツール名で switch している箇所が見受けられます。 /** * ref: https://github.com/modelcontextprotocol/servers-archived/blob/9be4674d1ddf8c469e6461a27a337eeb65f76c2e/src/gitlab/index.ts#L420-L500 */ switch (request.params. name ) { case "fork_repository" : { // ツールの処理 } case "create_branch" : { // ツールの処理 } // ... } たとえば MCP サーバー A がツール名 tool_a を持ち、同時に MCP サーバー B が同じツール名 tool_a を持っている状況で、双方を利用するように設定している場合は、クライアントが正しくツールを使い分けられない可能性があります。 MCP の中には各ツール定義でツールの説明を定義する description というものがあり、こちらを活用することでどういったツールなのかを生成 AI にヒントとして与えることができます。 内容が適切に設定されていれば AI も呼び出すツールを使い分けてくれるはずですが、実装・運用の際には利用する MCP サーバーの各 descirption の重複には気を付けておく必要があります。 /** * ref: https://github.com/modelcontextprotocol/servers-archived/blob/9be4674d1ddf8c469e6461a27a337eeb65f76c2e/src/gitlab/index.ts#L363-L369 */ return { tools: { { // ツール名 name : "create_or_update_file" , // ツールがどんなツールなのかの説明 description: "Create or update a single file in a GitLab project" , // ツールが受け取るパラメータのスキーマを定義 inputSchema: zodToJsonSchema(CreateOrUpdateFileSchema) } , // ... } } まとめ 本記事では 5G コアのオペレーション自動化のノウハウや知見と、その中で実感できた MCP の実体とその注意点について紹介しました。 今回、 MCP によって GitLab の操作や DB からのパラメータ取得を統一したインターフェースで扱えるようになり、実装にかかる時間が少なく済み、提供まで短い期間でありながらもシステムを構築して提供できました。 本来であればシステム毎に API を調査したり、その API を組み合わせて目的を達成するためのコードを書く必要がありますが、 MCP のツールという形によって生成 AI へ提供することで生成 AI 側が実現したいことに合わせてツールを組み合わせて呼び出してくれるため、こちらが作らなければならない API を少なく済ませられました。 また、実際に構築してみることで、以下の MCP の実装における注意点や良い部分などを知ることができました。 MCP による統一したインターフェースの実現 MCP の品質確保について MCP のツール名衝突の可能性 今回作成したものは ShowNet の映像伝送で使われるキャリア5Gのコアネットワークの構築・運用で実際に利用されました。 今回我々は 5G コア自動化オペレーションの部分を構築しましたが、実際に利用されたシステムについては NTTドコモの開発者ブログ にて紹介しております。 是非そちらもご覧ください。 今後も AI による自動化の推進やさまざまなネットワークとクラウドを接続する方法についての検証を進めつつ、サービスへの応用を検討していきます。
アバター
こんにちは。5G&IoTサービス部の池です。IoT向けコネクティビティサービスの販売企画を担当しています。 これまで、「Active Multi-access SIM開発シリーズ」として、2回にわたりその特長や仕組み、開発秘話などをご紹介してきました。最終回となる今回は、モバイル回線に求められていることの変化、そしてActive Multi-access SIM(※)(以下、マルチアクセスSIM)の技術がこれからどのように進化していくのかについてお届けします。 これまでの記事はこちら 第1回: 1枚のSIMでキャリアを冗長化!Active Multi-access SIMの特長と仕組み 第2回: 開発の舞台裏〜Active Multi-access SIMが生まれるまでの挑戦と特許取得 ※ Active Multi-access SIM:SIM1枚でキャリアの冗長化が実現できるIoT向けモバイルサービス。通信の監視と通信障害を検知してキャリアを切り替える機能がSIM自体に実装されています。 市場の動向とお客さまのニーズの変化 マルチアクセスSIMの今後の展望 まとめ 市場の動向とお客さまのニーズの変化 近年、企業や店舗、工場などの通信環境において、モバイル回線の活用が拡大しています。 新しい拠点の立ち上げや、システムの更改をきっかけに、光回線を引かずに無線通信を採用するケースが増えているようです。 例えば、小売店では、これまで光回線を利用してマルチコピー機やATMなどのシステムを運用するのが一般的でした。 しかし最近では、 店舗のレイアウトなどの設置環境に左右されにくく、導入しやすいことから、モバイル回線を活用する動き が広がっています。 デジタルサイネージ(電子看板)も、その流れの1つです。ディスプレイを使って広告や店舗情報、キャンペーンなどを表示するデジタルサイネージは、小売店や商業施設での情報発信に欠かせない存在となっています。 もともとは光回線を前提としたものも多かったのですが、最近では通信料金がサービス料に含まれているモバイル回線対応型が増え、光回線を引く必要がない点が評価されています。これにより、 工事の手間や高額な工事費が不要になるだけでなく、設置場所の自由度も高まりました 。 最近では、そもそもモバイル回線を前提とした施設も増えているようです。 たくさんの店舗がそれぞれ有線で回線を準備しようとすると、配線が複雑になってしまい、管理が大変になります。そのため、施設全体でモバイル回線を活用することで、通信環境の整備や増設をスムーズにしようという流れがあるのかもしれません。 また、キャッシュレス決済の普及も進み、クレジットカードに加えて、2次元バーコード決済などの対応も求められるようになり、決済端末の世代交代が進んでいます。これに伴い、タブレット型のレジを導入する店舗も増え、モバイル回線を使って決済できる環境が整ってきました。 とはいえ、 通信が途絶えてしまうと決済ができなくなるという課題 もあります。 モバイル回線では SIM を用いてキャリアとの通信をしていますが、過去にキャリアの通信障害が発生した際には決済ができなくなり、大きな影響を受けた店舗もありました。最近では、現金を持ち歩かない人が増え、店舗側も(徐々にですが)キャッシュレス決済のみを採用するケースがあるため、「 通信が常に使える状態であること 」は、ますます重要になっています。 マルチアクセスSIMの今後の展望 「 1枚のSIMでキャリアを冗長化!Active Multi-access SIMの特長と仕組み [Active Multi-access SIM開発シリーズ 第1回(全3回)] 」でも述べましたが、マルチアクセスSIM の以下の特徴により上記のようなキャリアの通信障害にも対応できます。 1枚のSIMで2つのキャリアに接続可能 SIMの機能により自動でキャリアの切り替えが可能 IoTソリューションでは、離れた場所にあるデバイスから一定の間隔でデータをアップロードしたいというニーズも多くあります。そのため、万が一通信障害が発生して長時間に及んだ場合でも、現地での操作なしにキャリアを切り替えられる機能が求められています。マルチアクセスSIMは、 ファームウェアの改修などを行わなくても汎用端末で対応できる 点が評価されており、こうした課題の解決に貢献しています。 また、キャリア障害時の切り替えだけでなく、ローカル5G網と公衆モバイル網を自動で切り替えたいというご要望に応えるため検証を進めています。 お客さまのネットワーク環境に応じて適切なモバイル網を選択 できることで、より利便性の高い通信環境を構築できます。さらに、ローカル5G網とのマルチアクセスは、将来的に「IoT向けモバイルデータ通信サービス『 IoT Connect Mobile Type S 』」のオプションメニュー化し、商用提供することを検討しています。これにより、IoTデバイスの通信の柔軟性がさらに向上し、多様な環境での活用が期待されます。 本技術に関しては、こちらの記事「 ローカル5G網と公衆モバイル網への接続を切り替え可能なSIMアプレットの開発 」もぜひご参照ください。 ユースケース1 :鉄道や自動車、バスなど、免許交付がされているローカル5Gエリアと公衆モバイルエリア間を移動する際、人手による操作を介することなくアクセスネットワークを自動で切り替えて通信を継続させる ユースケース2 :ローカル5Gシステム障害のバックアップ回線として公衆モバイル網に自動で切り替えることで、冗長化による高可用性を維持させる 加えて、マルチアクセスSIMでは、サービス開発中から多くの問い合わせが寄せられていた 閉域接続への対応 も進めています。 特に、機密性の高い業務を扱う業種では、クラウド環境へのデータ保管や通信に対して慎重な姿勢を取る企業も少なくありません。製造業はその一例であり、製品設計や製造工程などに関わる独自ノウハウの流出リスクなどを警戒して、従来からニーズがある、 インターネットを経由しないセキュアな通信環境 = 閉域網 を活用しているケースも多く見られます。 また、IoTの特徴として、導入するデバイスの数や種類が多いという点が挙げられます。さらに、IoTデバイスは、低コスト・小型・軽量化が求められるため、セキュリティ機能を十分に搭載しにくい場合があります。そのため、デバイス側だけに依存せず、ネットワーク側でもセキュリティ対策を講じることが重要であり、その手段のひとつとして閉域網の活用が有効です。 昨今では、導入目的や活用範囲そのものが複雑化しているため、それぞれのセキュリティ要件や通信特性に応じたネットワークの対応が求められています。 こうした背景から、マルチアクセスSIMでも閉域接続へ対応することにより、より セキュアな通信環境と、キャリア障害時の自動切り替えによる高可用性の両立 が可能となります。 IoTでは、デバイスが遠隔地や人の手が届きにくい場所に設置されるケースも多く、万一通信障害が発生した際に、すぐに技術者が現地対応することが難しいという運用上の課題もあります。そのため、現地に人が行かなくても自律的に回線を切り替えられる仕組みが求められており、マルチアクセスSIMはこうした要望に応える機能を備えています。 マルチアクセスSIMは、商用サービスの提供開始以降、多くの引き合いをいただいており、フィールドテストなどのご支援をさせていただいています。その中で、一部のOSを搭載した端末においては、やや複雑な端末設定が必要となる場合があることも明らかとなり、ひとつひとつ解決を図ってきました。 こうして蓄積されたナレッジをお客さまに還元していくことで、今後ますます多くのIoTの冗長性の課題にお応えし、お客さまのビジネスの発展やDX推進に貢献できるよう、引き続き取り組んでまいります。 まとめ これまで3回にわたり、マルチアクセスSIMの技術と開発の裏側、そして市場の変化や今後の展望についてお伝えしてきました。 モバイル回線の活用が拡大する中で、通信の可用性はもちろん、柔軟性や導入のしやすさなど、多くのことが求められています。マルチアクセスSIMは、キャリアの冗長化を手軽・簡単に実現するコネクティビティサービスとして注目されています。 これからもお客さまのニーズに応え、より利便性が高く、魅力的な特長を持ったサービスを提供できるよう取り組んでまいります。 今後もNTTコミュニケーションズが提供するサービスにご期待ください! 今回ご紹介したマルチアクセスSIMの詳細情報についてはこちらをご参照ください。 マルチアクセスSIMのオフィシャルサイト Active Multi-access SIM|ドコモビジネス|NTTコミュニケーションズ 法人のお客さま また、本サービスは1枚からWeb購入・検証可能です。まずは試してみたいという方はぜひ以下のページからお申込みください! ドコモビジネスオンラインショップ IoT Connect Mobile® Type S|ドコモビジネスオンラインショップ|NTTコミュニケーションズ 記事に関するお問い合わせは、 iot-connect@ntt.com  までメールでご連絡ください。 ※お手数ですが、@を半角文字に置き換えてください
アバター
DifyのMCPプラグインとZapier MCPを利用してDifyとSnowflakeを連携させ、Snowflakeのデータを自然言語で扱ってみました。本記事では、その連携方法を中心に紹介したいと思います。 はじめに 利用したサービス Dify Zapier Snowflake 構成 連携設定 Snowflake の設定 Zapierの設定 Dify の設定 動作確認 まとめ 参考 はじめに こんにちは。NTTコミュニケーションズの大島です。普段は、クラウドサービスを中心に、データレイクやデータウェアハウスの検証をしています。 最近注目されている MCP (Model Context Protocol) という技術があります。 これはAnthropic が発表したオープンなプロトコルで、AI と外部システムの接続を標準化するものです。 LLMを利用したアプリケーション(MCPクライアント)が、MCPサーバーを介し外部システムのツールを動作させるために利用されます。 私の目線だと、このMCPにより、LLMとデータとの連携が簡単になるかが気になるところです。 そこで今回は、DifyのMCPプラグインとZapier MCPを利用してDifyとSnowflakeを連携させ、Snowflakeのデータを自然言語で扱ってみました。本記事では、その連携方法を中心に紹介したいと思います。 利用したサービス まず、簡単に本記事に登場するサービスについて紹介します。 Dify Dify はLLMアプリケーションをローコードで開発するためのプラットフォームです。LLMを利用したチャットボットやRAG等のアプリケーションが簡単に作成できます。 MCPプラグインを用いることでMCPクライアントとして利用することも可能です。 Zapier Zapier 自体は、Webサービスやアプリケーションを連携させて、作業を自動化できるサービスです。 Zapierは現在β版として、MCPサーバーとしての機能を提供しています。これを使うことで、Zapierを介してSnowflakeを含めた多数の外部サービスが利用できます。 Snowflake Snowflake は、言わずと知れたクラウドのデータウェアハウスサービスです。 構成 今回は、DifyをMCPクライアント、ZapierをMCPサーバーとして利用します。ZapierはMCPクライアントから受けたリクエストを元に、Snowflake に格納したデータを取得します。このような構成をとることで、LLMがSnowflakeのデータを利用できるようになります。 なお、MCPを利用するための選択肢としては、ほかに Claude for desktop 等のローカル環境で動作するアプリケーションもあります。今回は、Difyであれば応用する際のユースケースの幅が広いと考え、これを利用しました。 連携設定 それでは、連携に必要な設定について説明していきます。各サービスのアカウントは保有しているものとして、連携設定にスコープをあてて記述します。 Snowflake の設定 Zapier経由でアクセスさせるための設定をします。具体的にはSnowflakeにOAuthの設定をいれます。(参考: カスタムクライアント用のSnowflake OAuth の構成 | Snowflake Documentation ) まず、 ACCOUNTADMIN ロールを付与したアカウントで、OAuth integration を作成します。 CREATE SECURITY INTEGRATION oauth_kp_int TYPE = OAUTH ENABLED = TRUE OAUTH_CLIENT = CUSTOM OAUTH_CLIENT_TYPE = ' CONFIDENTIAL ' OAUTH_REDIRECT_URI = ' https://zapier.com/dashboard/auth/oauth/return/SnowflakeCLIAPI/ ' OAUTH_ISSUE_REFRESH_TOKENS = TRUE OAUTH_REFRESH_TOKEN_VALIDITY = 86400 ; OAUTH_REDIRECT_URI の値は、Zapier側画面(後述)で指定される値です。ちなみに、 OAUTH_REFRESH_TOKEN_VALIDITYは、トークンの有効期間で単位は秒です。 作成したOAuth integration のclient ID とsecret (Zapier側の設定で必要)は以下のSQLで取得できます。 select SYSTEM$SHOW_OAUTH_CLIENT_SECRETS( ' OAUTH_KP_INT ' ); --実行結果例 { " OAUTH_CLIENT_SECRET_2 " : " nEVL**************************************** " , " OAUTH_CLIENT_SECRET " : " /cId**************************************** " , " OAUTH_CLIENT_ID " : " Tnx1************************ " } secretは 2つ払い出されますが、どちらを使ってもOKです。 Zapierの設定 Zapier MCP 設定ページ にアクセスして、Zapier MCP Server URL を取得します。Zapier MCP Server URLは、MCPクライアント側、つまりDify 側の設定で必要となります。 Edit MCP Actions を押下し、 Add a new action の項目で、文字列 "Snowflake" で検索します。いくつかヒットしますが、今回は Execute SQL を選択しました。 つづいて、Snowflake Acoount のところで、Snowflake の接続情報を入力します。 Connect New を押すと以下の画面が開きます。入力が必須なのは、Account, ClientID, Client Secret です。Account は Snowflakeにアクセスする際のURLの .snowflakecomputing.com より前の部分です。Client ID, Client Secretは、前段 Snowflakeの設定で作成した OAuth Integrationの Client ID, Client Secretを指定します。 これらを設定し Yes, Continue to Snowflake をおすと、Snowflake のログイン画面に遷移するので、ユーザー、パスワード(+必要に応じMFA)でログインすると接続情報が登録されます。 前段の画面に戻りますので、残りの SQL Statement について設定します。選択肢として Set a specific value in this field (SQL文を指定する)と Have AI guess value fields (AIに推測させる)の2つが選べます。AIに推測させてみたいので、 Have AI guess value fields を選択して動作確認することにします。 Dify の設定 まず、MCP SSE プラグインをインストールします。この MCP SSE プラグインはコミュニティから提供されているものではあるものの、Dify公式サイトのベストプラクティス Dify MCPプラグインガイド でも紹介されています。 Dify のマーケットプレイスで "MCP SSE" プラグインを検索してインストールします。 プラグインの認証設定で必要となる MCP Servers Config には、以下の情報を投入します。url には、Zapier 設定の箇所で取得した Zapier MCP Server URL を入力します。 timeout 値などは、必要に応じて適切な値にしてください。 { "server_name": { "url": "https://actions.zapier.com/mcp/*******/sse", "headers": {}, "timeout": 300, "sse_read_timeout": 300 } } 続いて、同様にマーケットプレイスから Agent Strategies (Support MCP Tools) プラグインをインストールします。プラグインのインストールが完了すれば利用可能です。 動作確認 動作確認の目的で、Difyにて以下のようなシンプルな Chatflow を作成しました。 LLM は Azure OpenAI の o1 を利用しました。 Agent ノードでは、AGENTIC STRATEGY の項目で、インストールしたAgent Strategies (Support MCP Tools) の Function Calling (Support MCP Tools) を選択します。 同様に TOOL LIST の項目で、MCP Tools の Fetch MCP Tools と Call MCP Tool を追加します。 MCP SEVERS CONFIG は、プラグインインストール時に設定した値と同じものを設定します。 Snowflake には、Kaggleの Books Dataset のデータを事前に格納してあります。 Snowflake 内にどんなデータがあるか、見てもらいます。 きちんとテーブルやカラムの情報が取得できています。Snowflake のクエリヒストリを見ると、SHOW DATABASES, SHOW SCHEMAS ~, SHOW TABLES ~, DESCRIBE TABLE ~ のクエリを、合計で7クエリ実行していました。 この書籍に関するテーブルから、データを取得してみます。 シェイクスピアの書籍情報を取得してくれました。Snowflakeへのクエリは以下を投げていました。 SELECT * FROM BOOKS_DATASET_RAW WHERE TITLE ILIKE ' %shakespeare% ' OR AUTHORS ILIKE ' %shakespeare% ' ちなみに、Agentノードの設定でMemoryをonにしていますが、これが off では上記のやりとりはうまくいきませんでした。 やりとりを記憶しないので、最初の質問でテーブルのカラム名を調べているにもかかわらず、次の「シェイクスピアの書籍の情報を取得して」の指示に対し、誤ったカラム名(AUTHORS ではなくAUTHOR)のSELECT 文を生成するという結果になりました。 まとめ Dify のMCPプラグインを用いて Zapier MCPを利用することで、LLMにSnowflakeのデータを利用させることが簡単にできました。 今回は、Snowflakeにどんなデータがあるかの確認と、そこからデータを取り出すという初歩的な使い方まででしたが、高度なことができるかさらに調査を進めていきたいと思います。 また、今回Difyを用いて検証を実施しましたが、DifyからMCPを用いてさまざまな外部サービスを利用できるのであれば、Difyのワークフロー機能などと組み合わせることでより複雑なこともできそうです。 変化の激しい今この分野においては、Difyのようなツールを利用して、新しい技術やそれを活用するアイディアなどを、簡単に手早くどんどん試していくのもアリだと思いました。 参考 https://docs.snowflake.com/ja/user-guide/oauth-custom#integration-example) https://docs.dify.ai/ja-jp/plugins/best-practice/how-to-use-mcp-zapier
アバター
みなさんこんにちは、イノベーションセンターの益本 (@masaomi346) です。 Network Analytics for Security (以下、NA4Sec) プロジェクトのメンバーとして活動しています。 この記事では、2025年5月31日に開催されたドコモグループ学生向けテックワークショップでの取り組みについて紹介します。 ぜひ最後まで読んでみてください。 ドコモグループの学生向けテックワークショップについて ドコモグループでは、現場のエンジニアと一緒にハンズオン形式で学ぶことができる1dayのワークショップを毎年開催しています。 過去のテックワークショップ セキュリティ分野のテックワークショップも開催されており、今回は以下のテーマで開催させていただきました。 ドコモグループのセキュリティ業務とフィッシングサイトの仕組みを学ぶ 今回のテックワークショップについて フィッシング詐欺による被害が増加しており、無視できない脅威の1つになっています。 今回開催したテックワークショップでは、フィッシングサイトの構築に使われているツールであるフィッシングキットの分析を通じて、フィッシングサイトがどのように動作しているのかを学ぶ内容になっています。 フィッシングサイトの仕組みについては、過去に書いたブログ記事でも紹介されています。 フィッシングサイトの仕組みを知ることで、フィッシング詐欺を理解する フィッシング詐欺をテーマにしたテックワークショップを開催したのは今回が初めてです。 テックワークショップの内容は以下のようになっています。 ドコモグループでのセキュリティ業務紹介 講義 分析ハンズオン 内容振り返り 懇親会 分析ハンズオンの様子 まず導入として、フィッシング詐欺に関する講義をしました。 フィッシング詐欺がどのように行われているのか、フィッシングサイトがどのように作られているのかを紹介しました。 講義の後に、チームを作ってフィッシングキットを分析してもらいました。 書かれているコードを分析するだけでなく、分析環境の中で実際にフィッシングキットを動かしながら分析してもらいました。 実際に動かしてみることで、どのように動作しているのかより把握しやすくなります。 今回のワークショップは、全体へのアナウンスや参加者同士のやりとりなどはDiscordで行われていました。 以下の画像は、実際のチャットを写したものです。 チームで協力しながら、フィッシングキットにどのような機能が搭載されているのか、どのような情報を窃取するのか分析していただきました。 参加者からはさまざまな反応をいただきました。 攻撃者の「心理誘導テクニック」の巧妙さが非常に興味深かった 実在したフィッシングキットを利用して、フィッシングサイトの調査を行えたのは非常に貴重な経験だった グループで情報交換をしながら分析を進めることができてとても楽しかった etc. さいごに 今回は、学生向けテックワークショップでの取り組みについて紹介しました。 フィッシング詐欺をテーマにしたテックワークショップは初めての試みでしたが、無事に終了できてよかったです。 改善すべき点もあったので、同じようなことをする機会があれば、改良してより良いものにしていきたいです。 今後も引き続き、何かしらの形でセキュリティ業界を盛り上げていくつもりでいます。 おまけ NA4Secでは、攻撃インフラの解明・撲滅に向けたさまざまな活動を実施しています。 対外発信にも力を入れており、ブログ記事や講演などさまざまな形で取り組みが紹介されています。 NA4Secが過去に書いた記事一覧 こんなこと聞いてみたい、この講演やブログ記事の内容が気になるとかがあれば、 出張講演なども前向きに検討しますので興味のある方はNA4Secまでお気軽にご相談ください。
アバター
NTTコミュニケーションズ(以下、NTT Com)を含めたドコモグループでは、この夏に インターンシップ を開催します! この記事では、その中でも NTT Com のリアルな業務を体験できる「 現場受け入れ型インターンシップ 」について紹介します。 現場受け入れ型インターンシップとは 募集ポスト 昨年のインターンシップの様子 まとめ 現場受け入れ型インターンシップとは NTTドコモや NTT Com の社員と一緒に働きながら、実務を体験していただくインターンシップです。 実際の職場で社員と共に、“本当に使われる技術” を用いてプロジェクトに参画。 メンターによる実務レビューを通じて、実践的なスキルと思考を磨き上げます。 インターン終了後は、配属確約ができる「ポスト確約型WILLコース」へのエントリーが可能。 さらに、専門性が高く当社基準を満たす方には、「グレード5」での高待遇入社が提示されます。 100種類以上のエンジニアポストを用意しており、専門性を活かして配属確約での入社をめざす方に最適なインターンシップです。 エンジニアやセールス、ビジネスデザイン、リーガルなど幅広いワークフィールドを取り揃えて、業務体験を通じて仕事の理解を深め、成長機会を提供する内容となっています。 今季は 2025年8月25日(月)~9月5日(金)の土日祝日を除く10日間(2週間) で開催されます。開催場所は、出社+リモートワークのハイブリッド形式です(出社割合はポストにより異なります)。 募集ポスト 以下のワークフィールドが募集をしています。 AIエンジニア・データサイエンティスト セキュリティエンジニア ネットワーク・インフラエンジニア 6G・IOWNエンジニア プロダクト・サービスエンジニア ソリューションエンジニア パートナーコンサルティング(コンシューマ) パートナーコンサルティング(法人) ビジネスデザイン(コンシューマ) ビジネスデザイン(法人) リーガル アカウンティング&ファイナンス 地域エリア 各ワークフィールドの募集ポストについては、「 現場受け入れ型インターンシップ 」サイトの受け入れポスト情報をご覧ください。 記載されているポストのうち、受け入れ会社に NTTコミュニケーションズ と記載されたポストが NTT Com での業務です。 昨年のインターンシップの様子 NTT Com のエンジニア系ポストに参加した学生の方々が、これまで開催したインターンシップの体験記をこの NTT Communications Engineers' Blog に寄稿してくれています。 昨年の様子は以下の記事からご覧いただけます。 ローコード・ノーコードに潜むリスクを攻撃ツールで確かめてみた(インターンシップ体験記) MoQTを活用した双方向VTuberライブデモでアバターのパパになってみた(インターンシップ体験記) 構築から運用まで!脅威インテリジェンス業務を体験(インターンシップ体験記) フィッシングキットの詳細分析に挑戦!(インターンシップ体験記) この他にも、さまざまなインターンシップ体験記事を こちら からご覧いただけます。 「インターンシップでどんなことに取り組むのだろう?」、「インターンシップを通して何が学べるのだろう?」といった疑問を解消する手助けになれば幸いです。 まとめ みなさんもこの夏、ドコモグループのインターンシップに参加して興味分野での実務に挑戦してみませんか? 気になる開催概要とポスト情報はこちらです(再掲)。 現場受け入れ型インターンシップ エントリーは上記ページの募集要項をご確認の上、MYPAGE からお願いします。 【2027新卒】マイページ登録 マイページログイン エントリーシートの提出締め切りは 2025年6月13日(金)12:00 です。 興味のある方は、ぜひ夏のインターンシップをご検討ください。 みなさんのご応募をお待ちしています!
アバター
本記事では、AI異音検知の概要、実装、検証例について、入門的な内容をご紹介します。 はじめに 異音検知とは 検証用データ AIによる異音検知 オートエンコーダーモデル 音データの中身 周波数の世界から音を見る Node-AIでのオートエンコーダーモデル作成 おわりに はじめに こんにちは、NTT Com イノベーションセンターの 中野 です。 普段は時系列データに対応したノーコードAI開発ツール「 Node-AI 」チームで、 お客さまのデータ分析支援やプロダクト開発チームのスクラムマスターとして活動しています。 さて、みなさんは「AI」と聞いて何を想像しますか? 多くの人は、チャットができたり画像が作れる、いわゆる 生成AI (Generative AI)を思い浮かべると思います。 一方、大量の数値データを読み込み、中身を理解して何らかの予測値を返してくれる 予測AI (Predictive AI)もあります。 本記事は予測AIついてのお話です。 例えると、右脳が生成AI、左脳が予測AIに対応するようなものです。 ※この説明だと正確ではない例もありますし、両者の境界はなくなりつつあります。 ざっくりのイメージだけ伝わればと思います。 タイトルにある「変な音の検知」について考えると、 音は音波をマイクで収集・デジタル化(標本化)して数値データにできますし、 その音データを理解して「変」か「変じゃない」かを予測してくれるAIを作れば、 これも予測AIと言えそうです。 異音検知とは 「変な音」は「異音」とも言います。 異音をgoo辞書で調べると、以下の定義となっています。 機械・機器などから出る、通常とは異なる音。「パソコンから—がする」「—を確認しての緊急停車」 何が異音で、何が異音じゃないかは状況に強く依存します。 例えば稀にノイズのような音が混じっているのを異音とすることもあるし、 音の大きさ、音色、音程がいつもと違うことを異音と言うケースもあるでしょう。 辞書の意味からもわかる通り、通常の音(正常データ)があるからこそ、 異音(異常データ)を感じることができます。 異音検知とは、通常の音は「正常」、異音は「異常」と返す数理的な処理のことを指します。 異音というからには何かマズイことが起こっているということで、 事前にトラブルを予知して対策を講じることで メリット(人件費削減、事故防止、機会損失回避など)を享受できます。 検証用データ 今回の検証で利用するデータについて説明します。 「異音検知 データセット」などで検索すると、 有名な異音検知のオープンデータがいくつか見つかります。 しかしそれらはデータ量が数GBなど膨大であったり、 研究用/コンペ用の場合は異音検知の難易度が高いため、 軽く動作確認したい場合には不向きです。 今回はわかりやすさと手軽さを重視して、 簡単な正常データと異常データをそれぞれ1ファイル用意しました。 こちらの Webサイト にある 「冷蔵庫1」を正常データとして利用し、 正常データに「プツプツ1」の異音を短時間に3回重畳したものを異常データとしました。 元のデータは3秒程度なので、処理の都合上10秒程度に繋ぎ合わせています。 ※その関係で繋ぎ部分に若干ノイズも乗ってしまっています。 正常データはこちら。 異常データはこちら。 いかがですか? 明らかに異常データは人間の耳で聞いても異音が含まれているとわかります。 これを検知できるか検証してみます。 AIによる異音検知 オートエンコーダーモデル 異音検知の歴史は長く、さまざまな手法が提案されています。 本記事ではその1つである オートエンコーダー(Auto Encoder) を用います。 オートエンコーダーは深層学習(ディープラーニング)の1手法で、音に限らずさまざまなデータの異常検知に用いられます。 深層学習は人間の脳の構造をヒントに設計された機械学習手法で、入力されたデータを大量の神経細胞が電気信号を伝達するかのように処理します。 下図の丸や丸同士を繋ぐ矢印がそれをコンピューター上で実現するための要素となります。 参考: AIによる蓄電池システムの故障予兆検知技術の開発に成功 オートエンコーダーについて簡単に説明すると「入力されたデータをぎゅっと圧縮し、それをできるだけ元に戻す」というAIモデルです。 モデルの形状としては砂時計を横に倒したものをイメージするといいかもしれません。 この「ぎゅっと圧縮」が重要で、圧縮すると元の情報が失われる(ボヤける)ため、完全には元に戻せません。 そこを何とかできるだけ元に戻すよう頑張る(内部のパラメータを調整する)のが、 正常データを用いた学習フェーズです。 例えると100個の神経細胞でキャッチした情報を10個の神経細胞に詰め込み、 それをまた100個の情報に復元しようということです。 なんだか大変そうですよね。 次に、この学習済みのオートエンコーダーモデルに異常データを入力した場合のことを考えます。 モデルは異常データを圧縮後、できるだけデータを元に戻すように働きますが、 正常データとは異なるパターンが含まれているので精度高く復元ができません。 モデルは、正常データと同じようなデータが入力されることを期待しているためです。 頑張って学習した正常データに近いデータなら「よっしゃこのパターンならこう復元すればいいな!」となるのですが、 そこに未知のデータが来ると「なんだこれ、こんなの知らないからうまく復元できない…」となるのです。 この復元時の誤差(再現誤差、再構成誤差などと言う)を「異常度」とします。 そして、異常度が正常データを入力した時と比較して高い時に「異常が発生した」 と判断するロジックにより、異音検知ができるという仕組みです。 音データの中身 オートエンコーダーが理解できたところで、AIモデルに対してどのように音データを入力するかを考えましょう。 音データは、以下図のように1つの信号として線グラフで描画できます。 実はこの図は、先述した正常データを可視化したものです。 たった10秒でもデータ量が多すぎて潰れて見にくいですね。どれくらいのデータ量なのでしょうか。 音を記録する頻度は サンプリング周波数(単位 Hz: ヘルツ) と呼ばれます。例えば、CDは一般に44,100Hzです。 慣れない方向けに簡単に説明すると、Hzは1秒間に何回分の記録点があるかを示します。 44,100Hzということは、1秒間に4万4千100回もデータが記録されているということになります。 ちなみに、隣り合う記録点の秒間は 1/44100=約0.00002秒となります。 なお、今回の音データは16,000Hzとなっています。 例えば、このデータを1秒(16,000個)区切りにしてあげて、AIモデルに入力することが考えられます。 その1秒の中に異音が含まれていれば異常度が高くなるはずなので、1秒ごとに異音検知ができることになります。 また、このような加工をしていないデータを 生データ 、一定間隔で区切る枠のことを 時間窓 と呼びます。 周波数の世界から音を見る 1秒間に1万6千回も変化するようなデータ、人間が見てもいまいちどんな音なのかイメージがしにくいですよね? 人間が見てもわかりにくいということは、AIモデルにとってもわかりにくい可能性があります(偏見)。 ちなみに異常データの波形はこのようになります。 今回は簡単なので、「異音がどこに含まれるかを当ててください」と言われても当てられるかもしれません。 正解は以下図の赤枠あたりです。 しかし、それ以外の部分も若干異音に見えるところがありますし、もう少し異音が小さい音だと厳しくなってきますよね。 こういった場合に役に立つのが 周波数領域で音を見る という考え方です。 高音はデータの波の頻度が高く、低音は波の頻度が低くなるという特徴があります。 この波の頻度は 周波数 で表現できます。 例えば、1周期が1秒間であるような波は1Hz、1周期が0.5秒であるような波は2Hz、1周期がX秒であるような波は 1/X Hz といった計算になります。 波の頻度と周波数の関係の例を図にしたものがこちらです。 左側が1Hz、右側が2Hzの波となっており、それを周波数の世界から見たのが下側の図になります。 縦軸が周波数(Frequency)になっているのがポイントです。 データがどれだけグネグネしているかを見るよりも、どんな周波数が含まれているかを見るほうがわかりやすいと思いませんか? この周波数変換を今回の検証データである正常データと異常データにかけたものがこちらです。 ※このようなデータを「スペクトログラム」と呼びます。 異常データのほうに何か浮かび上がってますね! ということで、周波数変換すると、異音を見つけやすくなることがあることを示しました。 今回は、この周波数変換したデータをAIモデルに入力することとします。 ここまでの処理を行うPythonコードはこちらです。 import librosa import numpy as np import pandas as pd file_path = "normal.wav" # wavファイルをnumpy形式の生データに変換 audio, sr = librosa.load(file_path, sr= 16000 ) # 生データを周波数領域のデータに変換 freq_data = librosa.amplitude_to_db(np.abs(librosa.stft(audio)), ref=np.max) # 参考: 可視化 librosa.display.specshow(D, sr=sr, x_axis= 'time' , y_axis= 'log' ) # 周波数領域のデータをデータフレームに変換 df = pd.DataFrame(D.T) # カラム名を付与 df.columns = [ "freq_" + str (i) for i in range ( len (df.columns))] # 時刻を付与 # STFTの設定から 512 / 16000 = 0.032秒 が時間間隔となる df.index = pd.date_range(start= "2025-01-01 00:00:00" , freq= "32ms" , periods= len (df)) # CSVファイルに保存 df.to_csv( "normal.csv" ) # 先頭5行を表示 df.head() 作成したCSVデータの先頭5行はこちら。 freq_0~freq_1024までの、1025個のカラムが作られます。これが周波数0Hz~8,000Hzに対応しています。 生データのサンプリング周波数(16,000Hz)の半分の周波数成分(8000Hz)までが抽出されます。 また、時間間隔は周波数変換により0.032秒となります(生データのサンプリング周波数と変換の設定値に依存します)。 生データの1秒間が16,000個の記録点だったのに対し、周波数変換後は1秒で約32個の記録点ということになります。 Node-AIでのオートエンコーダーモデル作成 これまで説明した理論と用意したデータを踏まえ、 実際にオートエンコーダーモデルの実装と検証を実施します。 最近では生成AIによりプログラミングは随分楽になりましたが、 それでも時系列データの機械学習のコーディングは難易度が高く、バグが入りやすいものです。 今回は、時系列データのオートエンコーダーモデルに対応したノーコードツールであるNode-AIを用います。 上述した周波数変換後(スペクトログラム)のCSVデータを使用して、異音検知モデルを作成していきます。 使い方については別の記事で紹介しているので、興味があればご覧ください。 engineers.ntt.com また異常検知の流れを紹介したYouTube動画も公開しています。ご参考に。 今回作成したNode-AIでのモデル作成フローの全体像はこちらです。 Node-AIでは上から下にデータが流れるように動作します。 まず正常データと異常データをそれぞれ前処理(正規化+モデル入力用変換)します。 前処理後の正常データに対して「オートエンコーダー」を用いて「学習」し、異常度を可視化する流れとなります。 「時間窓切り出し(モデル入力用前処理)」では、 「何個のデータを区切ってAIモデルに入力するか」= 時間窓 を設定します。 今回は時間窓を「10」と設定しました(約0.3秒分に相当)。 時間窓は異音の最小単位が含まれているべきで、正常な波形の周期性等も考慮して決めるパラメーターです。 最適な値を探索するのは骨が折れますが、0.3秒というのは直感的には無難な数値でしょう。 次に、オートエンコーダーではモデルの形状や学習の設定します。 パラメータは自動で探索することもできますが、 今回は特にチューニングをするほどの難しいタスクではないので、 エイヤで以下のように設定しました。 層のように重なっている図がオートエンコーダーを示しています。 今回モデルに入力されるデータは1,025個のカラムと10個の時間窓なので、合計10,250個となります。 これを 10,250 → 256 → 64 と圧縮していき、 256→ 10,250 と復元するモデルになります。 異常度可視化(正常時)では、正常データにおける異常度を表示できます。 目的は「正常時には異常と判定せず」「異常時に異常と判定する」ことですから、 正常時には異常判定する「閾値」をギリギリのラインに設定しておきます。 その正常時に設定した閾値を引き継いで異常時の異常度を可視化したものがこちらです。 赤い帯になっているのが、閾値を越えた異常度の箇所です。 思った以上にわかりやすく異常を判定できました! さらに、「どのあたりの周波数が異常度に影響を与えているのか」といったことを知りたい場合もあるでしょう。 ここではNode-AIの「要因分析」機能を使って、調べてみます。 以下の図は横軸が時間、縦軸がカラム名となっていて、各セルが異常度にどれくらい影響を与えたかを示しています。 異常度が高くなっている時間帯の要因を調べていくと…。 freq_380あたりからぼんやりと赤くなっていることがわかります。 これは周波数で言うと3,000Hzあたりを指すので、スペクトログラムでノイズが浮かび上がった箇所と重複していそうです。 このように、人の目で見ても異常が見つけにくい場合でも、要因分析機能により深い考察ができます。 おわりに 異音検知をAIで実現する手法の紹介と、 簡単な異音データでの検証の流れをご紹介しました。 私自身は音処理の専門家ではなく、 今回のブログ執筆を通じて初めて異音検知の技術調査と実装を実施しました。 「こんなことできるかな?」と思い立ったら、 今では生成AIやノーコードツールを使って簡単に検証できる時代になったことを再確認しました。 データは蓄積してるけど活用できてない、分析の仕方がわからない、 といった方は是非ご相談いただければと思います! ご相談は Node-AI の Web サイト 上部の「お問い合わせ」フォームにご連絡ください。
アバター
みなさんこんにちは、イノベーションセンターの益本 (@masaomi346) です。 Network Analytics for Security (以下、NA4Sec) プロジェクトのメンバーとして活動しています。 この記事では、2025年5月17日に開催されたセキュリティカンファレンスBSides Tokyo 2025で登壇したことについて紹介します。 BSides Tokyo ぜひ最後まで読んでみてください。 BSides Tokyoについて BSidesとは、情報セキュリティのコミュニティ主導で開催されているセキュリティカンファレンスです。 2025年5月時点では、1105のBSidesイベントが開催されており、65カ国にまたがる260都市で開催されています。 BSides wiki 今回は、日本で毎年開催されているBSides Tokyoに登壇させていただきました。 日本で開催されているセキュリティカンファレンスでありますが、海外からの参加者もいます。 主催者からの情報によると、今年のBSides TokyoのCFPの応募数は昨年の倍になっていたそうです。 また、参加者も年々増加しており、国内外から注目されているセキュリティカンファレンスになっています。 NA4Secについて 「NTTはインターネットを安心・安全にする社会的責務がある」を理念として、インターネットにおける攻撃インフラの解明・撲滅を目指すプロジェクトです。 NTT Comグループにおける脅威インテリジェンスチームとしての側面も持ち合わせており、有事において脅威インテリジェンスを提供し、意思決定を支援することもあります。 イノベーションセンターを中心として、NTTセキュリティ・ジャパンやエヌ・エフ・ラボラトリーズ(以下、NFLabs.)からもメンバーが参画し、日夜攻撃インフラを追跡しています。 BSides Tokyo 2025での登壇 NA4Secからは、以下のタイトルで登壇させていただきました。 フィッシングキットの特徴による開発者の分類 / イベント登壇情報🎙️ \ セキュリティカンファレンス #BSidesTokyo 2025 に NTT Com の益本が登壇します✨ フィッシングキットの特徴から開発者を分類して得た学びについて報告します🎣 https://t.co/CP72yoCVvA #ドコモビジネス pic.twitter.com/RybzxpRtlS — ドコモビジネス|NTTコミュニケーションズ (@NTTCom_online) 2025年5月13日 フィッシングキットを分類する上で理想なのは、具体的な攻撃者の名前がわかっていて、特定のフィッシングキットと紐づけることができる状態です。 ただ、具体的な攻撃者の名前はわからないことが多いので、それをするのはかなり難しいです。 フィッシングキットを分析すると、異なるブランドでも裏で実行されている処理がまったく同じものもありました。 フィッシングキットの作成はそれなりに手間がかかるので、一度作成したものを再度利用していると思われます。 それが開発者の特徴となるので、分類することが可能になります。 今回の講演では、日本のブランドを騙ったフィッシングキットをいくつかのグループに分類し、それぞれのグループにどのような特徴があるのか紹介しました。 どのようなブランドを騙っているのか どのような機能が実装されているのか また、実際に分類してみて得た学びも共有しました。 どのような機能が実装されている傾向があるのか 複数のグループの特徴を持ち合わせている場合もあること 講演が終わった後も、フィッシングキットの収集方法や最近の攻撃者についてなど、いくつか質問があり反応はそれなりにもらえました。 さいごに 今回もフィッシングネタで登壇させていただきました。 セキュリティカンファレンスを通じて、継続的にセキュリティ業界に貢献しつづけることができて良かったと思っています。 今後も引き続き、何かしらの形でセキュリティ業界を盛り上げていくつもりでいます。 おまけ NA4Secでは、攻撃インフラの解明・撲滅に向けたさまざまな活動を実施しています。 対外発信にも力を入れており、ブログ記事や講演などさまざまな形で取り組みが紹介されています。 NA4Secが過去に書いた記事一覧 こんなこと聞いてみたい、この講演やブログ記事の内容が気になるとかがあれば、 出張講演なども前向きに検討しますので興味のある方はNA4Secまでお気軽にご相談ください。
アバター
こんにちは、イノベーションセンターの鈴ヶ嶺です。 本記事では、 NVIDIA Dynamo や vLLM などの LLM 推論フレームワーク向けに設計された高速・低遅延の抽象化転送ライブラリである NVIDIA Inference Xfer Library (NIXL) について解説します。 また、NVIDIA Dynamo に関してはこちらで解説していますので参考にしていただけると幸いです。 engineers.ntt.com まず、LLM 推論高速化(KV Cache)におけるメモリ転送の背景と課題をご紹介し、それを解決する NIXL の概要を説明します。 NIXL は Plugin により任意の転送方式を実装可能なアーキテクチャとなっています。実際に Custom Plugin を実装する方法についても紹介します。 背景と課題 NVIDIA Inference Xfer Library (NIXL) GPUDirect RDMA による VRAM to VRAM のデータ転送 GPUDirect Storage による VRAM to FILE のデータ転送 Custom Plugin の実装方法について まとめ 背景と課題 LLM の推論高速化は、コスト削減や低遅延な応答によるユーザビリティ向上といったニーズから、さまざまな改良が進められています。中でも「KV Cache」は、過去トークンに対する計算済みのキー・バリュー行列を保持し、次のトークン生成時に再計算を省略することで、推論速度を大幅に向上させる重要な技術です。 1 一方で、KV Cache が保持する状態はシーケンス長に比例して増加するため、メモリ消費も増大します。また、このキャッシュを複数の GPU やノード間で共有する際には、低遅延で転送できる仕組みが求められます。さらに、キャッシュの転送に使用するメモリやストレージの種類によって、最適なプロトコル( NVLink 、 GPUDirect Storage/RDMA など)が異なり、実装の複雑さが課題となります。 これらの課題を解決するため、多彩なメモリ・ストレージ、通信プロトコルを抽象化し、高速・低遅延な転送を可能とするライブラリが求められています。 NVIDIA Inference Xfer Library (NIXL) NVIDIA Inference Xfer Library(NIXL) は、LLM 推論フレームワーク向けに設計された高速・低遅延の転送ライブラリです。特徴として、VRAM や DRAM, FILE, Block, Object Storage など異種のメモリ・ストレージを統一的に抽象化する API を提供し、UCX(Unified Communication X) や GPUDirect Storage といった複数のバックエンドプラグインを動的に選択して最適な通信経路を自動的に構築します(下図参照)。通信方式は Plugin 形式で任意に拡張可能なため、独自のキャッシュシステムを構築可能です。 引用: https://github.com/ai-dynamo/nixl/blob/main/docs/nixl.md#overview 2025 年 5 月時点の対応 Plugin は以下になります。 cuda_gds DMA(Direct Memory Access)により、GPU Memory と Storage 間を高速転送する GPUDirect Storage(GDS)を使用するバックエンド NVMe SSD, NVMe-oF, NFS over RDMA, 分散ファイルシステム(DDN EXAScaler, VAST NFS, WekaFS)などで利用可能 mooncake LLM Serving platform の Kimi で利用される KV Cache System の Mooncake を使用するバックエンド posix libaio や liburing を使用した POSIX 準拠の I/O 処理をするバックエンド ucx 高帯域・低遅延通信の抽象化ライブラリである UCX を使用するバックエンド デフォルトではこの Plugin が設定されます ucx_mo UCX v1.18 では 1 つの UCX Context で複数 GPU をサポートしていないため、 Multi-Object (MO) UCX は GPU ごとに異なる UCX Worker を関連づける実装に改良している 2 NIXL はさまざまな LLM 推論フレームワークへの採用が進んでいます。次の図のように vLLM のサポートが 2025 年 4 月 11 日にアナウンスされました。 Shaping NIXL-based PD Disaggregation in vLLM V1 また、 SGLang でも以下のように NIXL 対応の PR がマージされました。 [PD] Add NIXL transfer backend #5477 NIXL の通信過程は次のようになります。 各ノードのエージェント初期化 VRAM, DRAM の登録 転送のためのメタデータの交換 データ転送 引用: https://github.com/ai-dynamo/nixl/blob/main/docs/nixl.md#example-procedure 次の章で実際に NIXL を実行します。 GPUDirect RDMA による VRAM to VRAM のデータ転送 ここでは NIXL の example を動作させてノード間で GPU メモリを UCX による GPUDirect RDMA を用いて転送します。NIXL は prebuild のものを pip install nixl でインストールできますが、今回は理解やカスタマイズのために自前で build したものを使用します。 事前に cuda や gdrcopy についてはインストールしてください。 まず初めに、UCX をインストールします。 wget https://github.com/openucx/ucx/releases/download/v1. 18 . 0 /ucx-1. 18 . 0 .tar.gz tar xzf ucx-1. 18 . 0 .tar.gz cd ucx-1. 18 . 0 ./configure \ --enable-shared \ --disable-static \ --disable-doxygen-doc \ --enable-optimizations \ --enable-cma \ --enable-devel-headers \ --with-cuda = /usr/local/cuda \ --with-verbs \ --with-dm \ --with-gdrcopy = /usr/ local \ --enable-mt \ --prefix = /opt/ucx-1. 18 . 0 make -j sudo make install # add ~/.bashrc export PATH =/opt/ucx-1. 18 . 0 /bin: $PATH export LD_LIBRARY_PATH =/opt/ucx-1. 18 . 0 /lib: $LD_LIBRARY_PATH export PKG_CONFIG_PATH =/opt/ucx-1. 18 . 0 /lib/pkgconfig: $PKG_CONFIG_PATH 次に NIXL をインストールして、example である blocking_send_recv_example.py を実行します。 target から torch.ones(10, dtype=torch.float32) のメモリを initiator に転送しています。 git clone https://github.com/ai-dynamo/nixl.git cd nixl git checkout 503fe5ccb86b5963b828ee5663672fcba66b92d2 python3 -m venv venv source venv/bin/activate pip install . cd examples/python # UCXにおける、RDMAのNICや通信方法を設置 export UCX_NET_DEVICES = [ RNIC Device ] export UCX_TLS =rc,cuda # target node ./blocking_send_recv_example.py --ip 0 . 0 . 0 . 0 --mode target --use_cuda 1 ## MD listener is listening on port 8888... ## Backend UCX was instantiated ## Initialized NIXL agent: initiator ## initiator Tensors: [tensor([0., 0., 0., 0., 0., 0., 0., 0., 0., 0.], device='cuda:0'), tensor([0., 0., 0., 0., 0., 0., 0., 0., 0., 0.], device='cuda:0')] ## Initiator sending to [Node A IP Address] ## Ready for transfer ## initiator Data verification passed - [tensor([1., 1., 1., 1., 1., 1., 1., 1., 1., 1.], device='cuda:0'), tensor([1., 1., 1., 1., 1., 1., 1., 1., 1., 1.], device='cuda:0')] ## Test Complete. # initiator node ./blocking_send_recv_example.py --ip [ Node A IP Address ] --mode initiator --use_cuda 1 ## MD listener is listening on port 5555... ## Backend UCX was instantiated ## Initialized NIXL agent: target ## target Tensors: [tensor([1., 1., 1., 1., 1., 1., 1., 1., 1., 1.], device='cuda:0'), tensor([1., 1., 1., 1., 1., 1., 1., 1., 1., 1.], device='cuda:0')] ## Waiting for transfer ## Test Complete. 実行すると、上記のようにノード間で GPU メモリが共有されたことを確認できます。 GPUDirect Storage による VRAM to FILE のデータ転送 ここでは GPUDirect Storage(GDS)を用いて GPU メモリを直接ストレージに転送するサンプルを作成して実行します。 DRAM から GDS でストレージに書き込む example である nixl_gds_example.py を参考に VRAM から GDS でストレージに書き込む次のスクリプトを作成します。 nixl_gds_example_vram_to_file.py import os import sys import torch import subprocess import nixl._utils as nixl_utils from nixl._api import nixl_agent, nixl_agent_config if __name__ == "__main__" : agent_config = nixl_agent_config(backends=[ "GDS" ]) nixl_agent1 = nixl_agent( "GDSTester" , agent_config) # init VRAM float32 1.0(0x0000803f) tensors = [torch.ones( 10 , dtype=torch.float32, device= 'cuda:0' )] agent1_vram_descs = nixl_agent1.register_memory(tensors) agent1_xfer_vram = agent1_vram_descs.trim() file_path = sys.argv[ 1 ] agent1_fd = os.open(file_path, os.O_RDWR | os.O_CREAT) assert agent1_fd >= 0 agent1_file_list = [( 0 , tensors[ 0 ].numel() * tensors[ 0 ].element_size(), agent1_fd, "b" )] agent1_file_descs = nixl_agent1.register_memory(agent1_file_list, "FILE" ) assert agent1_file_descs is not None agent1_xfer_files = agent1_file_descs.trim() xfer_handle_1 = nixl_agent1.initialize_xfer( "WRITE" , agent1_xfer_vram, agent1_xfer_files, "GDSTester" ) if not xfer_handle_1: print ( "Creating transfer failed." ) exit() state = nixl_agent1.transfer(xfer_handle_1) assert state != "ERR" done = False while not done: state = nixl_agent1.check_xfer_state(xfer_handle_1) if state == "ERR" : print ( "Transfer got to Error state." ) exit() elif state == "DONE" : done = True print ( "Initiator done" ) nixl_agent1.release_xfer_handle(xfer_handle_1) nixl_agent1.deregister_memory(agent1_vram_descs) nixl_agent1.deregister_memory(agent1_file_descs) os.close(agent1_fd) # check file binary p = subprocess.run([ "hexdump" , "-Cv" , file_path], stdout=subprocess.PIPE) print ( "$ hexdump -Cv" , file_path) print (p.stdout.decode()) 実行した様子が以下のようになります。 ファイル内容を見ると float32 1.0 の 0x0000803f が書き込まれていることが分かります。 > python nixl_gds_example_vram_to_file.py /path/to/gds-support-dir/ones.bin Backend GDS was instantiated Initialized NIXL agent: GDSTester Initiator done $ hexdump -Cv /path/to/gds-support-dir/ones.bin 00000000 00 00 80 3f 00 00 80 3f 00 00 80 3f 00 00 80 3f |...?...?...?...?| 00000010 00 00 80 3f 00 00 80 3f 00 00 80 3f 00 00 80 3f |...?...?...?...?| 00000020 00 00 80 3f 00 00 80 3f |...?...?| 00000028 Custom Plugin の実装方法について NIXL は Plugin により任意の転送方式を実装可能なアーキテクチャとなっています。 ここではローカルで DRAM と FILE による転送が可能なサンプルの Plugin を実装する方法について紹介します。 基本的には、 nixlBackendEngine を継承して、最低でも純粋仮想関数(例: virtual void f() = 0; ) 3 を実装することで新たな転送方法を追加できます。 プロジェクト構成以下のように nixl の plugins 配下に新たに local ディレクトリを作成して実装コードを置きます。 nixl ├── src │   ├── plugins │   │   ├── local │   │   │   ├── local_backend.cpp │   │   │   ├── local_backend.h │   │   │   ├── local_plugin.cpp │   │   │   └── meson.build │   │   ├── meson.build 新たに Plugin を追加するため、 nixl/src/plugins/meson.build に以下を追記します。 subdir('local') 次のように追加した Plugins を登録する実行を追加します。 Plugin の名前、追加のオプションパラメータ、サポート可能なメモリーの種類を設定します。 local_plugin.cpp #include "backend/backend_plugin.h" #include "local_backend.h" static const char * PLUGIN_NAME = "LOCAL" ; static const char * PLUGIN_VERSION = "0.1" ; static nixlBackendEngine* create_local_engine ( const nixlBackendInitParams* init_params) { return new nixlLocalEngine (init_params); } static void destroy_local_engine (nixlBackendEngine* engine) { delete engine; } static const char * get_plugin_name () { return PLUGIN_NAME; } static const char * get_plugin_version () { return PLUGIN_VERSION; } static nixl_b_params_t get_backend_options () { nixl_b_params_t params; return params; } // サポート可能なメモリーの種類 static nixl_mem_list_t get_backend_mems () { nixl_mem_list_t mems; mems. push_back (DRAM_SEG); mems. push_back (FILE_SEG); return mems; } static nixlBackendPlugin plugin = {NIXL_PLUGIN_API_VERSION, create_local_engine, destroy_local_engine, get_plugin_name, get_plugin_version, get_backend_options, get_backend_mems}; #ifdef STATIC_PLUGIN_LOCAL nixlBackendPlugin* createStaticLocalPlugin () { return &plugin; } #else extern "C" NIXL_PLUGIN_EXPORT nixlBackendPlugin* nixl_plugin_init () { return &plugin; } extern "C" NIXL_PLUGIN_EXPORT void nixl_plugin_fini () {} #endif 以降で実際に転送処理を行うロジックを実装します。 postXfer 関数でメモリアドレスやファイルデスクリプタが渡されるためそれらを用いて転送します。 例えば UCX の実装を参考にすると実転送は postXfer 関数で実行されています。 4 local_backend.h #ifndef __LOCAL_BACKEND_H #define __LOCAL_BACKEND_H #include <nixl.h> #include <nixl_types.h> #include <unistd.h> #include "backend/backend_engine.h" class nixlLocalEngine : public nixlBackendEngine { private : public : nixlLocalEngine ( const nixlBackendInitParams *init_params); ~ nixlLocalEngine (); // 通知機能をサポートするかどうか bool supportsNotif () const { return false ; } // 別プロセス、リモートノードへの転送をサポートするかどうか bool supportsRemote () const { return false ; } // 同一のプロセス、ノードへの転送をサポートするかどうか bool supportsLocal () const { return true ; } // 内部に処理のバックグランドスレッドを持つかどうか bool supportsProgTh () const { return false ; } // サポート可能なメモリーの種類 nixl_mem_list_t getSupportedMems () const { nixl_mem_list_t mems; mems. push_back (DRAM_SEG); mems. push_back (FILE_SEG); return mems; } nixl_status_t connect ( const std :: string &remote_agent) { return NIXL_SUCCESS; } nixl_status_t disconnect ( const std :: string &remote_agent) { return NIXL_SUCCESS; } nixl_status_t loadLocalMD (nixlBackendMD *input, nixlBackendMD *&output) { output = input; return NIXL_SUCCESS; } nixl_status_t unloadMD (nixlBackendMD *input) { return NIXL_SUCCESS; } nixl_status_t registerMem ( const nixlBlobDesc &mem, const nixl_mem_t &nixl_mem, nixlBackendMD *&out); nixl_status_t deregisterMem (nixlBackendMD *meta); nixl_status_t prepXfer ( const nixl_xfer_op_t &operation, const nixl_meta_dlist_t &local, const nixl_meta_dlist_t &remote, const std :: string &remote_agent, nixlBackendReqH *&handle, const nixl_opt_b_args_t *opt_args = nullptr ); nixl_status_t postXfer ( const nixl_xfer_op_t &operation, const nixl_meta_dlist_t &local, const nixl_meta_dlist_t &remote, const std :: string &remote_agent, nixlBackendReqH *&handle, const nixl_opt_b_args_t *opt_args = nullptr ); nixl_status_t checkXfer (nixlBackendReqH *handle); nixl_status_t releaseReqH (nixlBackendReqH *handle); }; #endif local_backend.cpp #include "local_backend.h" #include <string.h> #include <sys/stat.h> #include <iostream> nixlLocalEngine:: nixlLocalEngine ( const nixlBackendInitParams *init_params) : nixlBackendEngine (init_params) {} nixl_status_t nixlLocalEngine:: registerMem ( const nixlBlobDesc &mem, const nixl_mem_t &nixl_mem, nixlBackendMD *&out) { // 基本的にローカルでDRAM, FILEの転送をする際にはここで処理はしない形で設計 // 例えばRDMA(ib verbs)ではibv_reg_mrやGDSではファイルハンドラ登録がされることが望ましいと思われる // 別途ここでファイルハンドラなどのメタデータを設定し、prepXferやpostXferを利用するためにはnixlBackendMDを継承したクラスを引数のoutに設定する // 参考: https://github.com/ai-dynamo/nixl/blob/1c979f0999740e4b221d0b9b470efbac793ddcae/src/plugins/cuda_gds/gds_backend.cpp#L95 if (nixl_mem == FILE_SEG || nixl_mem == DRAM_SEG) return NIXL_SUCCESS; return NIXL_ERR_NOT_SUPPORTED; } nixl_status_t nixlLocalEngine:: deregisterMem (nixlBackendMD *meta) { return NIXL_SUCCESS; } nixl_status_t nixlLocalEngine:: prepXfer ( const nixl_xfer_op_t &operation, const nixl_meta_dlist_t &local, const nixl_meta_dlist_t &remote, const std :: string &remote_agent, nixlBackendReqH *&handle, const nixl_opt_b_args_t *opt_args) { // validation if ((local. descCount () != remote. descCount ()) || ((operation != NIXL_READ) && (operation != NIXL_WRITE))) { return NIXL_ERR_INVALID_PARAM; } return NIXL_SUCCESS; } nixl_status_t nixlLocalEngine:: postXfer ( const nixl_xfer_op_t &operation, const nixl_meta_dlist_t &local, const nixl_meta_dlist_t &remote, const std :: string &remote_agent, nixlBackendReqH *&handle, const nixl_opt_b_args_t *opt_args) { // DRAM, FILEの転送処理を実行する if (local. getType () == DRAM_SEG && remote. getType () == DRAM_SEG) { // dram to dram int cnt = local. descCount (); for ( int i = 0 ; i < cnt; i++) { void *dst_addr; void *src_addr; if (operation == NIXL_READ) { dst_addr = ( void *)local[i].addr; src_addr = ( void *)remote[i].addr; } else if (operation == NIXL_WRITE) { dst_addr = ( void *)remote[i].addr; src_addr = ( void *)local[i].addr; } memcpy (dst_addr, src_addr, local[i].len); } } else if (local. getType () == FILE_SEG && remote. getType () == FILE_SEG) { // file to file int cnt = local. descCount (); for ( int i = 0 ; i < cnt; i++) { int in_fd; int out_fd; if (operation == NIXL_READ) { in_fd = remote[i].devId; out_fd = local[i].devId; } else if (operation == NIXL_WRITE) { in_fd = local[i].devId; out_fd = remote[i].devId; } struct stat st; if ( fstat (in_fd, &st) < 0 ) { return NIXL_ERR_INVALID_PARAM; } if ( copy_file_range (in_fd, 0 , out_fd, 0 , st.st_size, 0 ) < 0 ) { return NIXL_ERR_INVALID_PARAM; } } } else if ((local. getType () == FILE_SEG && remote. getType () == DRAM_SEG && operation == NIXL_WRITE) or (local. getType () == DRAM_SEG && remote. getType () == FILE_SEG && operation == NIXL_READ)) { // file to dram int cnt = local. descCount (); for ( int i = 0 ; i < cnt; i++) { int fd = local. getType () == FILE_SEG ? local[i].devId : remote[i].devId; uintptr_t buf = local. getType () == FILE_SEG ? remote[i].addr : local[i].addr; size_t len = operation == NIXL_WRITE ? local[i].len : remote[i].len; if ( pread (fd, ( void *)buf, len, 0 ) < 0 ) { return NIXL_ERR_INVALID_PARAM; } } } else if ((local. getType () == DRAM_SEG && remote. getType () == FILE_SEG && operation == NIXL_WRITE) or (local. getType () == FILE_SEG && remote. getType () == DRAM_SEG && operation == NIXL_READ)) { // dram to file int cnt = local. descCount (); for ( int i = 0 ; i < cnt; i++) { int fd = local. getType () == FILE_SEG ? local[i].devId : remote[i].devId; uintptr_t buf = local. getType () == FILE_SEG ? remote[i].addr : local[i].addr; size_t len = operation == NIXL_WRITE ? local[i].len : remote[i].len; if ( pwrite (fd, ( void *)buf, len, 0 ) < 0 ) { return NIXL_ERR_UNKNOWN; } } } else { return NIXL_ERR_NOT_SUPPORTED; } return NIXL_SUCCESS; } nixl_status_t nixlLocalEngine:: checkXfer (nixlBackendReqH *handle) { // 非同期機能未サポートのため、即時でSUCCESSを返す // 非同期実装については以下参考 // https://github.com/ai-dynamo/nixl/tree/main/src/plugins/posix return NIXL_SUCCESS; } nixl_status_t nixlLocalEngine:: releaseReqH (nixlBackendReqH *handle) { return NIXL_SUCCESS; } nixlLocalEngine::~ nixlLocalEngine () {} 最後に追加した Plugin の meson を次のように記載します。 nixl/src/plugins/local/meson.build if 'LOCAL' in static_plugins local_backend_lib = static_library('LOCAL', 'local_backend.cpp', 'local_backend.h', 'local_plugin.cpp', dependencies: [nixl_infra, nixl_common_dep], include_directories: [nixl_inc_dirs, utils_inc_dirs], install: true, cpp_args: ['-fPIC'], name_prefix: 'libplugin_', install_dir: plugin_install_dir) else local_backend_lib = shared_library('LOCAL', 'local_backend.cpp', 'local_backend.h', 'local_plugin.cpp', dependencies: [nixl_infra, nixl_common_dep], include_directories: [nixl_inc_dirs, utils_inc_dirs], install: true, cpp_args: ['-fPIC'], name_prefix: 'libplugin_', install_dir: plugin_install_dir) if get_option('buildtype') == 'debug' run_command('sh', '-c', 'echo "LOCAL=' + local_backend_lib.full_path() + '" >> ' + plugin_build_dir + '/pluginlist', check: true ) endif endif local_backend_interface = declare_dependency(link_with: local_backend_lib) 追加後は次のようにインストールします。 cd nixl pip install . 追加した Plugin を検証するコードを次に記述しました。 backends として agent に今回追加した LOCAL を設定します。 nixl_local_example.py import os import sys import torch import subprocess from nixl._api import nixl_agent, nixl_agent_config if __name__ == "__main__" : agent_config = nixl_agent_config(backends=[ "LOCAL" ]) nixl_agent = nixl_agent( "LOCAL_TEST" , agent_config) t1 = [torch.ones( 10 , dtype=torch.float32) for _ in range ( 1 )] t1_reg_descs = nixl_agent.get_reg_descs(t1) t1_xfer_descs = nixl_agent.get_xfer_descs(t1) assert nixl_agent.register_memory(t1_reg_descs) is not None t2 = [torch.zeros( 10 , dtype=torch.float32) for _ in range ( 1 )] t2_reg_descs = nixl_agent.get_reg_descs(t2) t2_xfer_descs = nixl_agent.get_xfer_descs(t2) assert nixl_agent.register_memory(t2_reg_descs) is not None t3 = [torch.zeros( 10 , dtype=torch.float32) for _ in range ( 1 )] t3_reg_descs = nixl_agent.get_reg_descs(t3) t3_xfer_descs = nixl_agent.get_xfer_descs(t3) assert nixl_agent.register_memory(t3_reg_descs) is not None print ( "=====Init=====" ) print ( 't1:' , t1) print ( 't2:' , t2) print ( 't3:' , t3) print ( "=====Write t1 to FILE(/tmp/ones.bin)=====" ) fd = os.open( "/tmp/ones.bin" , os.O_RDWR | os.O_CREAT) file_list = [( 0 , t1[ 0 ].numel() * t1[ 0 ].element_size(), fd, "b" )] file_descs = nixl_agent.register_memory(file_list, "FILE" ) assert file_descs is not None file_xfer_files = file_descs.trim() xfer_handle_1 = nixl_agent.initialize_xfer( "WRITE" , t1_xfer_descs, file_xfer_files, "LOCAL_TEST" ) if not xfer_handle_1: print ( "Write t1 to FILE failed." , file =sys.stderr) exit(- 1 ) state = nixl_agent.transfer(xfer_handle_1) assert state == "DONE" # check file binary p = subprocess.run([ "hexdump" , "-Cv" , "/tmp/ones.bin" ], stdout=subprocess.PIPE) print ( "$ hexdump -Cv /tmp/ones.bin" ) print (p.stdout.decode()) print ( "=====Read t2 from FILE(/tmp/ones.bin)=====" ) xfer_handle_2 = nixl_agent.initialize_xfer( "READ" , t2_xfer_descs, file_xfer_files, "LOCAL_TEST" ) if not xfer_handle_2: print ( "Read t2 from FILE failed." , file =sys.stderr) exit(- 1 ) state = nixl_agent.transfer(xfer_handle_2) assert state == "DONE" print ( "t2:" , t2) print ( "=====Write t1 to t3=====" ) xfer_handle_3 = nixl_agent.initialize_xfer( "WRITE" , t1_xfer_descs, t3_xfer_descs, "LOCAL_TEST" ) if not xfer_handle_3: print ( "Read t2 from FILE failed." , file =sys.stderr) exit(- 1 ) state = nixl_agent.transfer(xfer_handle_3) assert state == "DONE" print ( 't3:' , t3) # cleanup nixl_agent.release_xfer_handle(xfer_handle_1) nixl_agent.release_xfer_handle(xfer_handle_2) nixl_agent.release_xfer_handle(xfer_handle_3) nixl_agent.deregister_memory(t1_reg_descs) nixl_agent.deregister_memory(t2_reg_descs) nixl_agent.deregister_memory(t3_reg_descs) nixl_agent.deregister_memory(file_descs) os.close(fd) 実行結果は次のようになります。 t1 , t2 , t3 の 3 つの torch.Tensor がそれぞれファイルに書き込み、読み込みやメモリ間の転送を実施している様子が分かります。 > python nixl_local_example.py Backend LOCAL was instantiated Initialized NIXL agent: LOCAL_TEST ===== Init = ==== t1: [ tensor( [ 1 ., 1 ., 1 ., 1 ., 1 ., 1 ., 1 ., 1 ., 1 ., 1 . ] ) ] t2: [ tensor( [ 0 ., 0 ., 0 ., 0 ., 0 ., 0 ., 0 ., 0 ., 0 ., 0 . ] ) ] t3: [ tensor( [ 0 ., 0 ., 0 ., 0 ., 0 ., 0 ., 0 ., 0 ., 0 ., 0 . ] ) ] ===== Write t1 to FILE ( /tmp/ones.bin ) ===== $ hexdump -Cv /tmp/ones.bin 00000000 00 00 80 3f 00 00 80 3f 00 00 80 3f 00 00 80 3f |...?...?...?...?| 00000010 00 00 80 3f 00 00 80 3f 00 00 80 3f 00 00 80 3f |...?...?...?...?| 00000020 00 00 80 3f 00 00 80 3f |...?...?| 00000028 ===== Read t2 from FILE ( /tmp/ones.bin ) ===== t2: [ tensor( [ 1 ., 1 ., 1 ., 1 ., 1 ., 1 ., 1 ., 1 ., 1 ., 1 . ] ) ] ===== Write t1 to t3 = ==== t3: [ tensor( [ 1 ., 1 ., 1 ., 1 ., 1 ., 1 ., 1 ., 1 ., 1 ., 1 . ] ) ] まとめ 本記事では、LLM 推論フレームワーク向けに設計された、高速かつ低遅延な抽象化転送ライブラリ「NVIDIA Inference Xfer Library(NIXL)」について解説しました。また、example の実行方法や Custom Plugin の実装方法についても紹介しました。 NIXL を導入することで、複数 GPU やマルチノード環境におけるレイテンシの削減やスケールアウトが容易になり、将来的には Plugin を活用した新しいストレージ技術や通信プロトコルへの対応も期待できます。 LLM テクニックの習得: 推論の最適化 - NVIDIA 技術ブログ ↩ https://github.com/ai-dynamo/nixl/commit/b0085154d2aa4347c332bb121293a77ab733a871 ↩ Abstract class - cppreference.com ↩ https://github.com/ai-dynamo/nixl/blob/503fe5ccb86b5963b828ee5663672fcba66b92d2/src/plugins/ucx/ucx_backend.cpp#L892-L898 ↩
アバター
こんにちは。NTTコミュニケーションズの露崎です。本ブログでは2025年3月のGTCで紹介されたNVIDIA社のOSS Dynamoについて紹介します。 はじめに 特徴 インストールと基本動作 Dynamo Run Dynamo Serve 推論グラフとコンポーネント dynamo serveの起動の流れ 1. nats/etcdの起動 2. dynamo serveの起動 3. 動作確認 4. 終了 分散処理の仕組み まとめ はじめに こんにちは。NTTコミュニケーションズの露崎です。本ブログでは2025年3月のGTCで紹介されたNVIDIA社のOSS Dynamoについて紹介します。 NVIDIA Dynamoは発表されて間がなく、開発/変更が盛んに行われています。本ブログでは2025年5月の時点での最新版である0.2.0について紹介しますが、最新情報については 公式 をご参照ください。 NVIDIA Dynamo はNVIDIA社が開発しOSSで公開している推論フレームワークです。LLMの推論時の処理をプロンプトの解釈を実施するPrefill、単語を逐次的に生成するDecodeなどのフェーズに分解し、フェーズ毎の処理を別々のGPUで実行させることにより、推論時の負荷を分散可能な分散推論フレームワークです。 各フェーズ間の通信をNVLINK経由のRDMAなどを用いることによって、より効率的でスケーラブルなAI推論が可能とする設計になっています。 PrefillやDecodeなどLLMの仕組みについての詳細は NVIDIA社のブログ をご参照ください。 NVIDIA Dynamoのアーキテクチャ(出典: NVIDIA Dynamo ) 特徴 NVIDIA Dynamoは、以下のような特徴を持っています。 推論グラフの構築: 自由度の高い推論グラフを構築でき、パイプライン向けのコンポーネントをSDKで提供します。 分散処理: Prefill、Decodeなどのフェーズに分解し、各フェーズ間の通信をRDMAを始めとした通信の抽象化を提供する NIXL 、プロセス間通信向けの nats で実現しています。 基本言語と依存関係: 処理系は Rust言語 で書かれており、推論グラフの構築や分散処理の開発用SDKとして Python言語 のBindingsも提供しています。依存関係として状態管理のためのetcd、推論処理エンジンである vllm 、 SGLang 、 TensorRT-LLM などに依存しています。 このように、NVIDIA Dynamoは推論高速化のためのソフトウェアですが、独自の処理系を提供するわけではなく、さまざまなコンポーネントを組み合わせた最適化を実施するためのオーケストレータのような機能を提供するフレームワークです。 インストールと基本動作 NVIDIA Dynamoはpip packageで配布されており、以下のコマンドでインストールが可能です。 pip install ai-dynamo[all] Python Package Index (PyPI) にはNVIDIA社のpackageを参照するスタブパッケージが提供されており、スタブ経由でNVIDIA社のリポジトリからインストールできます。NIXLなどの通信ライブラリについてもビルド済みのバイナリを梱包したものが配布されています。 分散処理に必要なetcd、natsについてはDynamo Serveの節でより詳細に解説します。 Note v0.2.0では、vllmエンジンとして ai_dynamo_vllm=0.8.4 が必要です。NIXLについてはバージョンの依存関係が定義されていませんが、筆者の環境では 公式 に従って検証時の最新の HEAD をビルドし、動作確認を実施しました。 NVIDIA Dynamoでは基本的な動作確認のためのコマンドとして、 dynamo run を提供するほか、分散処理のための dynamo serve 、 Kubernetes 向けの dynamo deploy などのコマンドを提供しています。 NVIDIA Dynamoの動作環境については 公式 をご確認ください。 本ブログでは主に dynamo run と dynamo serve について紹介します。 Dynamo Run dynamo run は対話形式のshellを起動するコマンドです。以下のようにモデルを読み込み、対話的なシェルを起動します。モデルは HuggingFace のモデルIDに対応している他、ローカルのファイルシステムからも読み込むことができます。 dynamo run out=vllm tokyotech-llm/Llama-3.1-Swallow-8B-Instruct-v0.3 Note dynamo run コマンドの out にはvllm、sglangなど推論を実行するエンジンを指定し、推論エンジン経由でGPUを利用します。outを指定しない場合、CPUが利用されるため、推論処理に時間がかかっている場合には、推論エンジンを正しく設定できているか確認してみてください。 上記のコマンドを実行すると以下のようなシェルが起動し、対話的な推論を実施できます。 $ dynamo run out=vllm tokyotech-llm/Llama-3.1-Swallow-8B-Instruct-v0.3 2025-05-01T07:52:54.065Z WARN __init__.vllm_version_matches_substr: Using ai_dynamo_vllm 2025-05-01T07:52:54.074Z INFO __init__.resolve_current_platform_cls_qualname: Automatically detected platform cuda. 2025-05-01T07:52:54.722Z INFO nixl: NIXL is available Loading safetensors checkpoint shards: 0% Completed | 0/4 [00:00<?, ?it/s] Loading safetensors checkpoint shards: 25% Completed | 1/4 [00:00<00:01, 1.55it/s] Loading safetensors checkpoint shards: 50% Completed | 2/4 [00:01<00:01, 1.49it/s] Loading safetensors checkpoint shards: 75% Completed | 3/4 [00:01<00:00, 2.19it/s] Loading safetensors checkpoint shards: 100% Completed | 4/4 [00:02<00:00, 1.85it/s] Loading safetensors checkpoint shards: 100% Completed | 4/4 [00:02<00:00, 1.82it/s] 2025-05-01T07:53:11.006Z INFO loader.load_model: Loading weights took 2.25 seconds 2025-05-01T07:53:11.207Z INFO model_runner.load_model: Model loading took 14.9596 GiB and 2.389781 seconds 2025-05-01T07:53:11.913Z INFO worker.determine_num_available_blocks: Memory profiling takes 0.57 seconds the current vLLM instance can use total_gpu_memory (79.19GiB) x gpu_memory_utilization (0.90) = 71.27GiB model weights take 14.96GiB; non_torch_memory takes 0.16GiB; PyTorch activation peak memory takes 1.26GiB; the rest of the memory reserved for KV Cache is 54.90GiB. 2025-05-01T07:53:12.045Z INFO executor_base.initialize_cache: # cuda blocks: 28108, # CPU blocks: 2048 2025-05-01T07:53:12.046Z INFO executor_base.initialize_cache: Maximum concurrency for 8192 tokens per request: 54.90x 2025-05-01T07:53:13.427Z INFO model_runner.capture_model: Capturing cudagraphs for decoding. This may lead to unexpected consequences if the model is not static. To run the model in eager mode, set 'enforce_eager=True' or use '--enforce-eager' in the CLI. If out-of-memory error occurs during cudagraph capture, consider decreasing `gpu_memory_utilization` or switching to eager mode. You can also reduce the `max_num_seqs` as needed to decrease memory usage. Capturing CUDA graph shapes: 100%|███████████████████████████████████████████████████████████████████| 35/35 [00:10<00:00, 3.40it/s] 2025-05-01T07:53:23.712Z INFO model_runner.capture_model: Graph capturing finished in 10 secs, took 0.32 GiB 2025-05-01T07:53:23.712Z INFO llm_engine._initialize_kv_caches: init engine (profile, create kv cache, warmup model) took 12.51 seconds 2025-05-01T07:53:25.279Z INFO dynamo_run::input::text: Ctrl-c to exit ? User › 終了時には Ctrl+C でプロセスを終了します。 この他、オプションなどについては 公式 を参照してください。 Dynamo Serve dynamo serve は事前に定義した推論グラフに従って各コンポーネントの起動、オーケストレーションを提供するコマンドです。 各コンポーネント間の通信にはnatsプロトコルが利用され、状態管理をetcdで実施するため、 dynamo serve の実行前にnatsサービス、etcdサービスを起動しておく必要があります。ポートやコンフィグがデフォルト値でよければ、 公式が配布するdocker compose用のyaml を利用するのが最も簡単で、ワーキングディレクトリをdynamoのリポジトリルートとした時に以下のコマンドで各サービスを起動できます。 docker compose -f deploy/docker-compose.yml up -d 推論グラフとコンポーネント 前述のとおり、 dynamo serve を利用するためには事前に分散処理を定義した推論グラフが必要です。ここでは dynamo serve を理解するために必要な推論グラフとコンポーネントについて解説します。 推論グラフは分散推論を行う際のコンポーネントの組み合わせや依存関係を示した定義です。コンポーネントは推論グラフを構成するためのパーツです。 具体的なコンポーネントにはFrontend、Processor、Router、Workerなどがあります。これらのコンポーネントの依存関係を整理し、Frontendで受け付けたリクエストをProcessorがRouterの情報をベースにスケジュール、実際の推論をWorkerで処理する、といった流れを定義するのが推論グラフです。 NVIDIA Dynamoでは LLMに関するコンポーネントと推論グラフのサンプル実装 が公開されています。 推論グラフのサンプルは以下のとおりです。 # From examples/llm/graphs/agg.py from components.frontend import Frontend from components.processor import Processor from components.worker import VllmWorker Frontend.link(Processor).link(VllmWorker) この例ではNVIDIA Dynamo SDKのPython Bindingを利用し依存関係を表現しています。 実際には components に含まれるFrontendやProcessorといったクラスがコンポーネントにあたり、これを実装する必要があります。 コンポーネントのサンプルは以下のとおりです。依存関係はコンポーネント内でも個別に定義できます。 # components/processor.py class Processor(ProcessMixIn): worker = depends(VllmWorker) router = depends(Router) ... 依存関係の定義の仕方については、基本的にはclass側で設定した依存関係に応じて処理系を実装し、実際の起動時の関係を .link で表現するといった使い方になります。 ここからは各コンポーネントでこの依存関係を使ってどのように処理を実装するかを簡単に紹介します。 処理の実装では、コンポーネントで定義した依存関係を以下のようなコードで動的に解決し、依存先のコンポーネントを変数のように扱うことができます。ここでは上記のprocessorの起動時に依存するVllmWorkerを self.worker_client に初期化するコードを掲載します。 comp_ns, comp_name = VllmWorker.dynamo_address() # type: ignore self.worker_client = ( await runtime.namespace(comp_ns) .component(comp_name) .endpoint("generate") .client() ) このように、分散処理におけるコミュニケーションをカプセル化し逐次的に処理を記述できます。ここで初期化された self.worker_client は以下のような形で別のプロセスに処理を引き継がせることができます。 engine_generator = await self.worker_client.generate( vLLMGenerateRequest( engine_prompt=engine_prompt, sampling_params=sampling_params, request_id=request_id, prefix_hit_rate=prefix_hit_rate, ).model_dump_json() ) 初期化の方法や、依存関係の解決の仕方には複数のパターンが存在するため、より詳細にコンポーネントを実装したい方は 公式 を参照してください。 dynamo serveの起動の流れ ここでは前節で紹介したコンポーネントと推論グラフを用いて dynamo serve を使った推論APIサーバを起動する流れを紹介します。 1. nats/etcdの起動 dynamo serve を実行する前に通信ライブラリのnatsと状態管理用のetcdのサービスを起動します。NVIDIA Dynamoのデフォルト値を利用するのであれば前述した通りdocker composeを用いて以下のコマンドで起動します。 docker compose -f deploy/docker-compose.yml up -d 2. dynamo serveの起動 次に dynamo serve コマンドを使って各コンポーネントを起動します。以下はワーキングディレクトリをNVIDIA Dynamoの examples/llm とした時のサンプルです。 dynamo serve graphs.agg:Frontend -f configs/agg.yaml このコマンドでは examples/llm/graphs/agg.py 内に定義されているFrontendを起点とする推論グラフを -f で指定した設定ファイルに基づいて起動するということを実行します。 このサンプルではaggregated modeの依存関係にあるFrontend、Processor、VllmWokerが起動し VllmWorker has been initialized が画面に出力されれば準備完了です。 3. 動作確認 NVIDIA DynamoはOpenAI互換のAPIを提供します。このため、以下のようなOpenAIクライアントを使った推論で動作確認が可能です。 from openai import OpenAI ENDPOINT = "http://localhost:8000/v1" def main(): client = OpenAI( base_url=ENDPOINT, api_key="needn't" ) chat_completion = client.chat.completions.create( messages=[ { "role": "user", "content": "Hello, how are you?", } ], model="tokyotech-llm/Llama-3.1-Swallow-8B-Instruct-v0.3", temperature=0.9, ) print(chat_completion.choices[0].message.content) if __name__ == "__main__": main() 4. 終了 dynamo serve を終了する際は Ctl+C など、親プロセスを終了させることで関連サービスを終了できます。 分散処理の仕組み 最後に dynamo serve を利用して分散処理を実施する場合、どのような分散処理が実行されるかを disaggregatedのサンプル実装 をベースに紹介します。 disaggregatedの実装はFrontend、Processor、VllmWorker、PrefillWorkerの4つのコンポーネントで構成されています。 LLMの推論は、入力された文章からKey/Valueの値を計算するPrefill処理とPrefillされた値から1トークンずつ回答を生成するDecode処理に分けることができます。NVIDIA Dynamoのdisaggregatedの実装では、このPrefill処理をPrefillWorker、Decode処理をVllmWorkerというそれぞれ別のコンポーネントで処理する機能を提供します。 より具体的な流れを解説します。disaggregatedの実装では Processor.link(VllmWorker).link(PrefillWorker) という依存関係を持っています。このため dynamo serve コマンドにより、それぞれの依存関係が解決され、初期化されます。初期化時にVllmWorkerは最大コンテキスト長に応じた計算用のメモリブロックをGPU上に確保します。 実際の推論リクエストがユーザから入力されるとFrontend経由でProcessorがVllmWorkerに対して処理をスケジュールします。この時にPrefillWorkerが利用可能であることを通知します。スケジュールされたVllmWorkerは起動時に確保したメモリブロックで利用可能なものを予約し、nats経由でPrefillWorkerへ通知します。Prefill Workerは通知された内容を元にPrefillを実行し、計算結果をRDMAで依頼元のVllmWorkerのメモリブロックに書き込みます。PrefillWorkerはメモリブロックを書き込んだことをRDMAのnotify機能を使ってメモリを確保しているプロセスに通知します。メモリの書き込み通知を受けたVllmWorkerはPrefillされたメモリブロックの結果を用いてDecode処理を実施します。 以下は、起動から推論リクエストをPrefill、Decodeし処理する流れを図にしたものです。 Note 図にあるように、実際にはVllmWorkerとPrefillWorkerにはDynamoのコンポーネントであるWorkerと実際に推論を処理するVllmWorkerが存在し、コンポーネントとzmq経由でコミュニケーションを取っています。また、これらのPrefill、RDMAの通知を実現するためにOSSのvllmから変更されたソフトウェアを利用しており、変更点については こちら で確認することができます。 まとめ 本ブログではNVIDIA Dynamoについて紹介しました。NVIDIA Dynamoは、効率的でスケーラブルな分散推論を実現するためのフレームワークです。サンプルの実装のままでもLLMの推論を効率化するための分散処理が実現でき、さらに推論グラフやコンポーネントを実装することで複雑な処理形を実装することも可能です。今後、LLMの需要拡大に向けてこうしたGPUの処理効率を向上させるソフトウェア、仕組みを活用することはますます重要になると考えられます。
アバター
こんにちは。イノベーションセンター Generative AI チームの安川です。 今回は私の所属するチームで開発しているrokadocというプロダクトの内部で利用している技術要素に重点を置いて紹介します。 本記事では「ドキュメント変換技術」であるrokadocについて、内部で利用している技術について紹介します。 rokadocはドキュメントをアップロードするとそれを生成AIで扱いやすいテキストへ変換するという機能を持ちます。 ユーザはドキュメントの内容に応じて自身で複雑な処理を考える必要がないというメリットがありますが、一方でその内部ではレイアウト解析やAI-OCRなどの複雑な処理を行っています。 本記事では実例を挙げつつrokadocの内部でどのような処理を行っているのかについて紹介します。 rokadocの基本的な使い方に関しては別途公開している「 生成AI向けのドキュメント変換技術 rokadoc の使い方 」で詳細に解説をしておりますので、そちらをご参照ください。 また パブリックベータ版 を公開しています。 利用回数など一部機能に制限がありますが、無料で利用できますので、気になった方は実際にお試しください。 rokadocが取り組む課題 rokadocを支える技術要素 レイアウト解析 表解析 AI-OCR 画像解析 まとめ おわりに rokadocが取り組む課題 近年、LLM(Large Language Model)やRAG(Retrieval Augmented Generation)などの生成AI関連技術が実用可能なレベルに達しており、これらを事業活用しようという動きが多く起こっています。 しかし実際に事業活用しようと考えると、多くの問題が浮き彫りになってきます。 例えば一般的なRAGにおいて、ドキュメントはテキストベースであることが前提となっています。 しかし実際の業務で用いられるドキュメントの多くは複雑な構造や図表を持ち、単純なテキスト化では情報が欠落しています。 また機密情報を含むドキュメントを扱う場合など、セキュリティの観点からクラウドサービスの利用が制限されるケースも少なくありません。 これらの問題を解決するため、私達のチームでは「 AIの力で埋もれた情報を価値あるものに 」というコンセプトの元、rokadocを開発しています。 そしてrokadocをより良いものにするため、オンプレミス環境でも動作可能な機械学習モデルや、それらに合わせた独自のアルゴリズムの構築を行っています。 rokadocを支える技術要素 今回はこのモデルやアルゴリズムがどのように動作しているのかを紹介していきます。 紹介の中で、大南らの構築したJDocQA *1 というデータセットを利用します。 これはオープンアクセス可能なPDF形式の文書で構成された、視覚情報とテキスト情報の両方を参照する質問応答データセットであり、多種多様な分野・形式のドキュメントを含んでいます。 レイアウト解析 ドキュメントは多種多様なレイアウトを持っています。一枚のページに図表とテキストが混在し、それぞれのまとまりごとに順序を持っています。 特に日本語のドキュメントは、横書き(左から右へ)と縦書き(右から左へ)が混在し、ページ内でも複数の読み進める方向が存在するなど、複雑な構造を持っています。 このような特徴を持ったドキュメントを対象に、文書内にあるテキストや図表を抽出し、段落などのテキストのまとまりを推定し、それぞれの要素の関係性から人間が自然に読み進める順序を導出することをレイアウト解析で行います。 図1. 上:JDocQA public_document00490.pdf 24ページ目を抜粋。下:見出し、テキスト、表の各領域を区別して抽出できている(JDocQA public_document00490.pdf 24ページ目へレイアウト解析の結果を加工して作成) 図2. 見出し、テキスト、図の各領域を区別して抽出できている(JDocQA kouhou00010.pdf 8、9ページ目へレイアウト解析の結果を加工して作成) 色ごとにそれぞれ異なる対象を表しており、タイトルは緑、テキストは黒、図は青、表は赤という形で記されています。 またそれぞれの領域の左上に推定された読み順が付与されており、それぞれ意味的に繋がりのある順序が付与されていることを示唆しています。 表解析 またドキュメントでは表という形でも情報が表現されます。 一般に表は列と行を持ち、それぞれの位置関係で情報ごとの関連性を示します。 しかし、実際の表の構造は多種多様です。 構成要素の数やセル内の情報量は勿論、セル結合、罫線の種類、背景色などもさまざまで、非常に複雑な形式を持つことがあります。 このような特徴を持つ表に対して、表解析を実施し、各セルの位置と範囲を推定します。 図3. 行方向及び列方向の結合セルも含め、表内のセルを抽出できている(JDocQA kouhou00156.pdf 13ページ目へ表解析の結果を加工して作成) セルと判定された領域がそれぞれ赤枠で囲われています。 このセル情報と後述するAI-OCRによるテキスト情報を組み合わせることで、表の構造とその内容を正確に反映したHTML形式のテキストへ変換できます。 AI-OCR ドキュメントに記述されているテキストの取得には、OCR(Optical Character Recognition:光学的文字認識)と呼ばれる技術を利用します。 これは画像として表現されている文字情報を認識し、テキスト化するという処理です。 日本語のドキュメントにはひらがな、カタカナ、漢字といった日本語の文字だけではなく、英語で利用されるアルファベットや数字、その他記号など多様な文字が登場します。 さらに、文字のフォント、サイズ、色、装飾(太字、斜体など)も考慮しなくてはなりません。 これらの多様な文字画像を認識しテキスト化するという処理を、AI-OCRで行います。 図4. 縦書きと横書きを区別し、多様な文字種を判別できている(JDocQA kouhou00024.pdf 11ページ目へAI-OCRの結果を加工して作成) 図5. 図4の一部を抜粋したもの 各行に対する解析結果を青文字で記載しています。 このドキュメントでは縦書きと横書きが混在し、文字の色やフォントもさまざまで、文字自体も多くの種類が存在していますが、問題なくテキスト化する能力があることを示唆しています。 画像解析 また、多くの業務において参照するドキュメントには、テキストや表だけではなくグラフやフローチャートなどの視覚情報も活用した表記や写真などの画像が存在します。 これらの画像情報はここまで紹介した技術を適用するだけでは、その本質的な意味や情報が欠落してしまうという問題があります。 例えば以下の図6に示されるようなドキュメントがあります。 図6. JDocQA public_document_ministry02357.pdf 7ページ目を抜粋 右側のグラフに注目してください。 仮にAI-OCRを用いて文字を取得しても、グラフ内の色と凡例の色との間にある対応付けという情報が落ちてしまいます。 位置関係で取ろうにも、グラフ内と凡例とで順序が異なるので難しく、色ごとに対応付けしようとしても左側のグラフと共通の色が存在するため、意図せず情報が混ざってしまいます。 そこで私達は、画像の内容を理解し、その特徴を捉えた文章へ変換するというアプローチを採用しています。 実際にこのページを変換し、右側のグラフがどのようにテキストで表現されているのかを示します。 キャプション: ### 画像の説明 この画像は、**IPv6導入に関連する課題**についての調査結果を示した棒グラフです。調査対象は「大学等」であり、調査年は「平成30年度」(2018年)とされています。全体のサンプル数は **n=395** です。 #### グラフの構造 - 横軸:課題の種類 - 縦軸:割合(%) - 各色の部分は異なる課題を表しており、それぞれの割合が明確に示されています。 #### 課題の分類と割合 1. **設備の更新にかかる手間及びコスト** - 色:赤 - 割合:35.7% - 最も大きな課題として挙げられており、IPv6導入に伴う設備の更新にかかる手間やコストが多くの人々にとって大きな負担となっていることが示されています。 2. **利用者機器の置き換えにかかる手間及びコスト** - 色:緑 - 割合:12.9% - IPv6に対応していない利用者機器の置き換えにかかる手間やコストが課題として挙げられています。 3. **想定されるトラブル等の情報不足・移行リスク** - 色:紫 - 割合:18.5% - IPv6導入に伴うトラブルやリスクに関する情報不足が課題として挙げられています。 4. **IPv6に詳しい技術者の不足** - 色:青 - 割合:10.1% - IPv6に関する専門知識を持つ技術者の不足が課題として挙げられています。 5. **運用ポリシー等の変更及びそのノウハウ** - 色:オレンジ - 割合:5.6% - IPv6導入に伴う運用ポリシーの変更やそれに必要なノウハウが課題として挙げられています。 6. **インターネット接続サービスの対応** - 色:薄い青 - 割合:1.5% - IPv6に対応したインターネット接続サービスの準備や対応が課題として挙げられています。 7. **対応機器の価格** - 色:濃い青 - 割合:3.5% - IPv6に対応した機器の価格が高いことが課題として挙げられています。 8. **その他** - 色:茶色 - 割合:4.3% - 上記のカテゴリには該当しないその他の課題が挙げられています。 9. **わからない** - 色:濃い緑 - 割合:7.8% - IPv6導入に関連する具体的な課題がわからないという回答が7.8%ありました。 #### legend(凡例) - 各色に対応する課題名が右側に表示されており、それぞれの課題がどの色で表されているかが一目でわかるようになっています。 #### まとめ このグラフは、IPv6導入に際して大学等で直面している主な課題を示しており、特に「設備の更新にかかる手間及びコスト」が最も大きな課題であることがわかります。また、技術者の不足や運用ポリシーの変更、機器の置き換えなども重要な課題として挙げられています。 このように視覚情報として表現された情報を的確に捉えたテキストへと変換することで、後段のRAGなどで正確に検索や生成が可能となります。 まとめ 以上がrokadocで使われている技術要素の紹介でした。 ここまで紹介してきた「レイアウト解析」「表解析」「AI-OCR」「画像解析」といった技術要素をrokadoc内部で適切に組み合わせることで、元のドキュメントの情報を可能な限り保持したまま、構造化されたJSON形式のテキストデータへと変換します。 この出力を用いることで、社内ドキュメントを活用したRAGシステムを精度高く実現できます。 実際に類似した機能を持つ他社製品と比べた場合の結果を以下に示します。 ここではJDocQAを対象に、想定されるユーザ質問から関連文書をどの程度の精度(※)で検索できるかを測定しました。 その結果として、rokadocが最も良い精度となりました。 機械学習モデルやヒューリスティックなアルゴリズムを用いていることから、100%という精度を達成することは難しいのですが、rokadocは対象のドキュメントの情報を効果的に抽出し、検索に適した形式へ変換していることを示唆しています。 (※)解析結果を対象に想定質問で検索を実施。 検索の結果を関連度合いで並べ替えた後、実際に関連しているドキュメントのページが上位三件に含まれていれば1、それ以外を0として評価。 全体の平均を最終的な検索精度とした。 おわりに この記事では、ドキュメント解析技術である rokadoc について、内部で利用されている技術要素に重点を置いて紹介しました。 これらの中には現在開発中の項目も含まれていますが、同様の処理を内部で行っている パブリックベータ版 を現在公開しています。 そして上記で紹介した技術の搭載を始めとした、精度向上や処理高速化のための更新も順次行っています。 利用回数に制限はありますが無料でお試しいただけますので、是非ともお試しください。 またこれらの技術を搭載し、インターネットに接続することなく利用可能なオンプレミス版の開発も進めています。 興味を持っていただけましたら、 rokadocお問い合わせフォーム からお問い合わせください。 それではみなさん、お読みいただきありがとうございました。 *1 : 大南英理, et al, "JDocQA: 図表を含む日本語文書質問応答データセットによる大規模言語モデルチューニング", 言語処理学会 第30回年次大会, 2024
アバター
こんにちは、クラウド&ネットワークサービス部の古賀です。普段はクラウドサービスのサービス企画を担当するかたわら、デザインプロセスを業務に取り入れたサービス改善などの活動に参加しています。 前回の記事「 1枚のSIMでキャリアを冗長化!Active Multi-access SIMの特長と仕組み 」では、Active Multi-access SIMの特長や仕組み、活用シーンなどをご紹介させていただきました。第2回は本サービスの開発秘話について、サービス企画チームのメンバーにインタビューしましたので、その模様をお届けします。 マルチアクセスSIM開発のきっかけ 開発のこだわりポイント マルチアクセス特許取得までの長い道のり 開発は苦労の連続、その先に待っていた達成感 まとめと次回予告 きっかけは数年前に起きたモバイルネットワークサービスの大規模故障。 多くの人が大規模故障の影響を受け、通信できなくなった携帯を手になすすべもなく何時間も復旧を待つしかありませんでした。モバイル通信の冗長化の必要性が浮き彫りになった大規模故障をきっかけに、 「課題解決に向き合おうとした通信事業者NTT Comの技術者の発想力」×「『IoTで日本社会をよくしていきたい』という熱い想い」から生まれたNTT Comの「Active Multi-access SIM(以下「マルチアクセスSIM」)」サービスの開発秘話と、特許を取得するまでの紆余曲折 について、開発に関わったNTT Com 5G&IoTサービス部でサービス企画を担当している永作さん、春原さん、小山さんにお話を伺いました。聞き手は販売企画を担当している高野です。 マルチアクセスSIM開発のきっかけ 高野:マルチアクセスSIMの開発が始まったきっかけを教えていただけますか? 永作さん:一番直接的なきっかけは2022年に他社で発生したモバイルネットワークサービスの大規模故障ですね。こうした故障に備えた対策はデュアルSIMの採用などさまざまな方法が考えられますが、 より手軽にIoTでのモバイルキャリア冗長を実現できる手法を検討しました。 通信障害で業務が止まって大変だったというお客さまの話を聞き、とはいえIoTの環境は冗長化したくなったからといって簡単に機器の入れ替えができるものではない状況の中で、"通信事業者として何か手を差し伸べられるものはないだろうか?"という思いが強くなり、IoT Connect Mobile Type Aで提供しているマルチIMSI方式 1 をうまくキャリア冗長に役立てられないか?と考えたのがきっかけです。 高野:あの障害は一大事で記憶に残っていますし、マルチアクセスSIMはそれがきっかけで生まれたサービスだとぼんやりとは知っていましたが、そういう思いがあったということは初めて知りました、すごいですね。 永作さん:その年の初秋に、マルチIMSI方式での商用提供実績を有するTransatel(トランザテル) 2 R&D担当者と共にマルチIMSI方式に関するワークショップを行い、本方式によるキャリア冗長SIMの実現可能性について議論しました。また、2022年秋冬頃の初期検討以来、マルチアクセス SIMの仕組みと深くかかわるSIMアプレット 3 のチームとは、深く協力・議論をしてここまでやってきました。 開発のこだわりポイント 高野:サービスを実装するときに重視した要件は? 小山さん: 多くの端末で利用できる、SIMが自律的に切り替わり自動で使える、ということを重視しました。 IoTは用途もニーズもさまざまですが、自動化を志向されるお客さまのニーズにこたえるためにも、実装が固まっているものに対して意識することなく使っていただきたい。そこを重視して自動で切り替わる仕組みを採用しています。そしてできるだけ多くの端末で利用できるように、端末の癖も一部吸収しながら動作するように試行錯誤して自律の仕組みを実現しました。 高野:さまざまな要件に対して幅広く対応できるような仕様を意識したということでしょうか? 小山さん:はい、キャリアの障害がきっかけで困ったお客さまも多数いらっしゃったでしょうし、次の開発でどうにか対策をしたいと思いつつ、既存システムの改修の難しさに直面されているお客さまも多いだろうと考えました。冗長化をしたくても簡単ではない、SIMスロットが1つしかないデバイスを使っているケースもあります。そういうお客さまにとって "1つのSIMスロットに挿せば、メインは信頼性の高いフルMVNO回線によるドコモ網接続を使っていただいて、何かあったらサブ回線へ自動的に切り替わり、通信の継続性を確保できる"というサービスはきっと要望に応えられるだろうと考えました。 また、 動作を担保する、というところにも重きを置きました。 このサービスは冗長性を担保するために通信をし続けることが重要です。さらにその上で動作するアプリが動き続けることは、お客さまから絶対に求められているポイントだし、我々もそこを怠ってはいけないと考えていました。いろんな端末でそれぞれ端末の癖があったり用途が違ったり、そういった端末の差分を吸収できる仕様にするところは苦労しました。通信不良にもさまざまなパターンがあったりして、通信ができないっていうところにもいろんなレイヤーでできなくなる部分があるので、そのどのレイヤーで通信ができなくなったとしても対処しちゃんと動き続けるような、仕様の策定にはかなり注力しました。 あらゆるパターンをさまざまなデバイスで検証することを繰り返し、動かなくなるパターンを洗い出してひとつひとつ対処するという地道な作業を繰り返して、どんな状況でも動き続けるようなアプレットに仕上げるというところに注力しました。 高野:結構骨が折れそうな工程ですね! 永作さん: IoT機器における長期間の利用において、途中でアプレットによる疎通確認サイクルが止まってしまうことのないよう、処理シーケンス(順序)を徹底的に議論・検証して安定動作を追求しました。 マルチアクセス特許取得までの長い道のり 高野:マルチアクセスSIMは特許 4 を取得されていますが、取得に至った経緯を教えていただけますか? 永作さん: 独自のサービス提供によって付加価値を高めることを目的に特許出願を進めました。 高野:どの部分で特許を取ることが出来ましたか? 小山さん:切り替わる技術は一般的にあるものですが、マルチアクセスSIMはお客さまのニーズを考慮した設計となっています。それを実現できたところが評価され、特許が取れたポイントになったところです。メイン回線としては通信が安定していて経済的なローカル回線を使っていただきたいという思いがあります。故障が発生した時に通信を継続させるために一時的にローミング通信のサブ回線に切り替わるんだけれども、復旧したら自動的にメイン回線に戻ってくることで お客さまにとっての安定性・経済性を実現できる、お客さまに寄り添った設計を入れている点がポイントでした。 高野:取得まで大変でしたか? 永作さん:特許権の取得に至るまでは、複数回の拒絶理由通知に対してそれぞれ対応策を検討し、手続補正書を提出することとなり、道のりは長かったです。 高野:先ほど拒絶理由通知書の文書の文言を見せていただきましたが、私だと心が折れてしまうと思うので、もうやり切ったことがすごいなあと思いました! 開発は苦労の連続、その先に待っていた達成感 高野:開発において苦労した点はどんなところでしたか? 春原さん:通信障害が実際に起きたらどうなるのか、想像力を働かせて考えました。 例えば障害時に大量のSIMが切替わるとどうなるか?SIMからの接続要求が設備へ集中することになるんですよね。そういうことに対するケアも必要じゃないかと。ほかのモバイルを担当しているクラウド&ネットワークサービス部、開発を担当しているイノベーションセンター、Transatelのメンバーとも議論しました。 結果として、 何かあった時に大量のSIMが同じ時間帯で一斉に接続要求を出さないように、アプレットの中でちょっと時間差で接続するような設定をしました。 自ずと再接続をばらけさせるような仕組みが設けられている、そんなイメージです。 高野:サービスリリース前に無償トライアル提供を実施されたとのことですが、トライアルの結果はいかがでしたか? 小山さん:トライアルを実施して大きく改善した点は2点あります。 1つは切り替わりにかかる待ち時間が少し長い点で、私たちの試験結果でも認識し始めていましたし、お客さまからフィードバックでもいただくことが多かった。先ほど一斉に切り替わらないように時間差で接続する制御をしたと話しましたが、そこが接続時間が長くなる一因でもありました。その 制御の仕組みを再度見直すことで切り替え時間を短縮し、お客さまの操作性の向上につなげることができました。 もう1つは経済的な点で、社内で考えていた価格設定とお客さまがこのサービスにどのくらい支払えるかという価格感について、トライアルを通して大きなずれのないことが確認でき、今後の販売にも自信が持てました。 お客さまのニーズに寄り添って開発をしてきて、お客さまの課題を解決し、経済的にもミートするサービスを開発できたという実感があり、このサービスを活用していただくことでIoTを、日本社会をよくしていくということにつながったかなと考えています。 高野:プロジェクトの中で、一番やりがいを感じた時はいつでしたか? 小山さん:お客さまに価値を実感いただいて購入いただけた時ですね。A社では、お客さまによる導入検討時の動作確認を通じて、自動で切り替わる仕様がユースケースとマッチしました。 お客さまに、課題解決のお役に立てると判断いただけたことが非常にうれしいし、やりがいを感じました。 永作さん:本当に実現できるかどうか、サービス化できるのか分からないという状況で、 リスクある中でも諦めずにチャレンジを続けて、結果的にサービスという形にできた。業界的にも前例がない独自サービスとしてリリースできたことは、関係者全員が達成感を感じている点だと思います。 不安を感じながらも信じてやり続けることで開発を成功させられるということを周囲にも知ってもらうことができ、自分も挑戦しようという気持ちを与えられたのではないかと思っています。 まとめと次回予告 「お客さまの課題解決を実現できるサービスを提供したい」という熱い思いがひしひしと伝わり、筆者自身もお話を聞きながら胸が熱くなる素晴らしいインタビューでした!第3回はマルチアクセスSIMの今後の開発の展望や活用事例などについてお届けする予定です。ぜひ次回の記事も併せてお読みいただけたら幸いです! 今回ご紹介したマルチアクセスSIMの詳細情報についてはこちらをご参照ください。 マルチアクセスSIMのオフィシャルサイト Active Multi-access SIM|ドコモビジネス|NTTコミュニケーションズ 法人のお客さま また、本サービスは1枚からWeb購入・検証可能です。まずは試してみたいという方はぜひ以下のページからお申込みください! ドコモビジネスオンラインショップ IoT Connect Mobile® Type S|ドコモビジネスオンラインショップ|NTTコミュニケーションズ 記事に関するお問い合わせは、 iot-connect@ntt.com  までメールでご連絡ください。 ※お手数ですが、@を半角文字に置き換えてください 1枚のSIMに対して、複数のIMSI(International Mobile Subscriber Identity。世界でユニークとなる携帯電話ユーザーの識別子)を格納する方式のこと ↩ 2019年よりNTTグループに加わったフランスのグローバルコネクティビティプロバイダー ↩ SIMカード上に搭載するアプレット。SIMカードの論理構造は、通信プロファイル領域とアプレット領域に分けられ、従来のSIMカードは通信プロファイル領域にアプレット領域が含まれている。NTT Comは2つの領域を分割する技術であるアプレット領域分割技術を開発している。SIMアプレットに関する記事はこちら( ローカル5G網と公衆モバイル網への接続を切り替え可能なSIMアプレットの開発 - NTT Communications Engineers' Blog ) ↩ 特許第7478277号「SIM、通信装置、切替方法、及びプログラム」に関する発明 ↩
アバター
こんにちは、マネージド&セキュリティサービス部の閏間です。このたび、社内で若手向けセキュリティイベントを開催したのでその紹介をします。草の根的な活動ですが、NTT Comではこういうことも行われているんだということを知ってもらえればと思います。 はじめに イベントの目的は、「業務棚卸しと将来像の明確化」「他組織を知って自業務に活かす」 発表者は幅広い部署から集まりました イベント準備もスムーズに進行しました イベント当日は大変盛り上がりました おわりに はじめに この4月に全社横断で入社2年目のセキュリティ系人材の方に集まってもらい、担当業務や1年間の成果、今後のキャリアプランを発表し合ってもらうというイベントを開催しました。 これは正式な会社の施策ではなく、私が自部署の若手を巻き込んで企画したいわば有志イベントです。会社全体の育成施策としては1年目終了時点ではこのような会はないので、独自に企画してみました。 昨年度自部署の新人の教育係を担当した身として1年目終了時ってけっこう重要なタイミングだと思うので、何もないのはちょっと物足りないなと思ったのですよね。なので、会社の育成施策を補完するような感じで新人にプラスになることが何かできればなあ、と思って企画してみたわけです。 イベントの目的は、「業務棚卸しと将来像の明確化」「他組織を知って自業務に活かす」 イベントの企画内容を検討した結果、以下のような目的で1年間の集大成としての報告会を開催することとしました。その名も「Security Rookies 2024」です!(2024は、2024年度入社の意) 1年間の取り組みを棚卸しして、入社3年目終了時点の目指す姿を明確化する。 他のセキュリティ系組織の業務を知り、自身の担当業務改善/キャリアプランに取り入れられるヒントを得る。 ちなみに企画した側の想いとしては、発表者の皆さんがイベント参加を通して将来的に会社のセキュリティ事業/施策の中核人材に育っていくための何か気付きを得てもらえれば、なんてことも思っていました(ちょっと大げさですけどね!)。 発表者は幅広い部署から集まりました 単に成果報告会をやるだけなら対象をセキュリティ系人材に絞る必要はないのですが、前述の通り今回はセキュリティ系人材を対象としました。理由は2つあって、1つは私が担当した新人がセキュリティ関連の業務を行っていたから、ということ。もう1つは、セキュリティは社内的にも世の中的にも年々その重要度が増しているので、セキュリティという軸があると発表者集めをしやすいと思ったから、です。 今回はあまり大規模なイベントにすることは考えていなかったので、発表者は広く社内に募集をかけるのではなく私の社内セキュリティ関連のツテをたどって集める形としました。私が声をかけた方々はみなさん好意的で、上長の方々の理解もあり、結果的に会社のセキュリティ系部署を多くカバーする形で合計8人の発表者に集まってもらうことができました。会社規模を考えると決して大人数ではありませんが、初めてやる有志イベントとしてはちょうどよい規模だったと思います。 参加者の所属部署のミッションは多岐にわたります。セキュリティ系のサービスやソリューションをつくってお客様に提供するとか、社内のセキュリティ運用を行うとか、セキュリティ技術開発を行うといったものなどです。守る対象でいえば自社とお客様の両方、職種としてはいわゆるセキュリティエンジニアと呼ばれる職種から、サービス企画やコンサル、セールス寄りといった非エンジニアまで、幅広いセキュリティ系人材が集まりました。 イベント準備もスムーズに進行しました 発表者が固まったのち聴講者募集も行いました。社内でどんなセキュリティ系部署があるかについて関心のある人とか、セキュリティ系人材を応援する気持ちのある人に聞いてもらいたいと思ったからです。発表者にとってもギャラリーが多いほうが張り合いがでますしね。 聴講者のほうも募集はゆるい感じで行いました。具体的には発表者の関係者に声をかけたり社内ブログを使って募集しました。イベントはリモート開催としましたが、当日の会議URLを広く公開するのではなく希望者に会議URLをお送りする形にしました。これは、事前に聴講者がどの程度になるか把握したかったためです。結果として約70人弱の方に聴講希望いただきました。 発表者に対しては事前の顔合わせをリモート会議で実施しました。その場では私から目的や発表してもらいたいことを改めて伝え、タイムテーブルの確認や録画OKかなどの事務的な確認も行いました。 また、イベント開催にあたり以下のように多くの方々にご協力いただきました。皆さん協力的で本当に助かりました。 NTT Comのセキュリティエバンジェリストである竹内さんにイベント冒頭でひとこといただきました(堅苦しい会にしようとは思っていませんでしたが、ちょっとした緊張感もあったほうがよいかと思ったため)。 私が所属する部門の責任者の方に後ろ盾になっていただきました(発表者集めにあたって、万一、勝手にこんなことするな的なクレームが入った場合に備えて。その場合は丁寧に趣旨説明などして理解いただこうと思っていましたが、その際に援護いただこうと思っていました。実際にはそのようなクレームはなかったです)。 私の周辺の若手数人にイベント内容のアイデア出しを手伝ってもらいました(小規模なイベントなので自分一人で企画/実施できると思いましたが、いろんな観点でのコメントをもらってイベントの質を高めたかったため)。 イベント当日は大変盛り上がりました 入念な準備をして、いざ当日。 司会者による開会あいさつから始まり、続いてセキュリティエバンジェリスト竹内さんからひとこといただきました。お話の内容としては、セキュリティ対策は攻撃者とのいたちごっこなので終わりがない、前進/改善し続けないといけないがそれがやりがいとなる、今後セキュリティのプロになることを期待している、といったものでした。2年目社員にとっても、セキュリティに関して経験豊富な方からの言葉はよい刺激になったのではないかと思います。 その後各発表者の発表に進みました。各自の持ち時間は、発表10分、質疑5分。2年目社員が主役の会なので質問についてはまずは2年目社員から募り、時間が残ったら他の方からも質問してもらうようにしました。発表者間で自身の業務効率化のためにアドバイスを求めたりするなど、活発に質疑が行われました。 私も聴講しましたが、発表者のみなさんが2年目になったばかりとは思えないくらいスムーズにプレゼンしつつ、自己アピールしている姿が印象的でした。 業務内容についても1年目から行うにはけっこう大変と思える業務を行っている方が多いことは驚きでした。例えば、入社半年後から社内セキュリティ運用(脅威情報収集や脆弱性緊急対応)の輪番業務メンバーになった方とか、1年間で全国の支社/支店に数十回も出張してセキュリティ商材の勉強会開催や提案支援を行った方がいらっしゃいました。 イベント全体の時間は約2時間半、同時参加者数は最大で50人程度でしたが、忙しい業務の合間を縫って行う有志イベントとしては、ちょうどいいサイズだったと思います。 イベント終了後発表者と聴講者の何人かの方に感想を聞きましたが、以下のように前向きなコメントをいただけました。これを読む限り、目的は達成できたかなと思います。 発表者の感想 他の部署の具体的な業務内容を知る機会があまりなかったので、様々な業務について聞けてよかったです。同じセキュリティ系だったとしても、部署が異なると仕事をする上で重視するポイントなどが異なることがプレゼンからわかり、とても興味深かったです。 未経験からセキュリティエンジニアとなった1年間は業務に慣れることに必死で、「成果は何か」という部分まであまり考えられていませんでした。しかし、今回の発表を通じて自身の業務内容を棚卸しし、この1年間の成果と成長を実感することができました。また、他の発表者のお話から新しい視点を得て、自身の不足部分にも気づくことができました。今後も長い社会人生活の中でも記憶に残るような、非常に貴重な経験となりました。 発表者は【セキュリティ】を共通軸として集まりましたが、各自の業務は非常に多彩で、それぞれの発表を通じて視野が大きく広がり、学びと良い刺激を得ました。また、発表に向けた業務の棚卸しや他の方の発表を通じて自身の課題も明確になったため、今年度はその課題を着実に克服し、ドコモグループのセキュリティ中核を担う人材として成長していきたいです。 聴講者の感想 発表者を「セキュリティ」に絞ったことで、年次を重ねた私にとっても、これまで知る機会のなかった各部の業務内容を知ることができ、キャリアを考える上でも非常に有意義な時間となりました。また、発表者のレベルが非常に高く、私自身もより一層努力しなければと刺激を受ける、充実したひとときでした。 内容もレベルが高く、発表者らしさが出ていてとても印象的でした。同じセキュリティ分野であっても普段触れない分野の話も多く、学びや気づきがあり良い刺激になりました。 自分もさらに成長していきたいと感じました。 おわりに 若手向けの1年目成果報告会として、社内セキュリティイベントを開催しました。イベントを通して発表者の方に、将来の目指す姿を明確化するとか、他組織のセキュリティ人材の業務内容やキャリアプランを参考にしてもらうといった成果を得ることができたんじゃないかなと思います。 リモート開催だったので対面での交流はありませんでしたが、これをきっかけに2年目社員間で交流が深まるとうれしいです。 また、こういう草の根活動もいいもんだなとあらためて思いましたので、いつかまたこうイベントをやってみたいです。そして他にもやろうという方が現れて、会社がさらに活性化するとうれしいですね。
アバター
3 月 15 日 ~ 3 月 21 日に開催された IETF 122 で、Media over QUIC Transport(MoQT)の相互接続試験に参加しました。 相互接続試験では、NTT Communications(以下 NTT Com) が OSS で公開している moq-wasm を持ち込み、4 種類の MoQT 実装と接続できました。 本記事では、Media over QUIC Transport(MoQT)の概要や、相互接続試験で遭遇した問題や、相互接続試験の結果について報告します。 はじめに Media over QUIC Transport(MoQT)とは 取り組みの背景 相互接続試験の雰囲気と結果報告 相互接続試験で見つかった問題 1 ( moxygen ↔ moq-wasm ) 相互接続試験で見つかった問題 2 ( aiomoqt ↔ moq-wasm) 相互接続試験で見つかった問題 3 ( 複数の実装 ↔ moq-wasm) 結果を振り返って おわりに はじめに NTT Com で SkyWay の R&D エンジニアをしている 内田 です。 3 月 15 日から 3 月 21 日で開催された IETF 122 の ハッカソンイベントで、Media over QUIC Transport(MoQT)の相互接続試験に参加しました。 本記事では、我々の実装の不具合や、他実装の修正提案、WebTransport ライブラリへの Pull Request など、相互接続試験によって得られた知見や成果を共有します。 同僚の 前田 が参加した IETF 121 の記事も過去に公開しておりますので、こちらも合わせてご覧ください。 Media over QUIC Transport(MoQT)とは Media over QUIC Transport(MoQT)とは、その名の通り、QUIC プロトコルを利用して Media を送受信するためのプロトコルです。 MoQ のプロトコルスタック(出典: 中間会議資料 ) 前回の IETF 121 参加記事 でプロトコルスタックについては詳しく説明しておりますので、本記事では割愛します。 取り組みの背景 NTT Com が開発している SkyWay は、誰でも簡単にビデオ・音声通話が簡単に実装できる、マルチプラットフォームな SDK です。 SkyWay は現在、WebRTC を採用して、ビデオ・音声通話を実現しておりますが、WebRTC はウェブ会議に非常に特化したプロトコルであるため、それ以外のユースケースにおいては WebRTC のカスタマイズ性に関する問題が発生する場合もあります。 そのため SkyWay は、幅広いユースケースで活用できる MoQT による映像・音声・データ配信に注目しており、MoQT のプロトコル実装 moq-wasm の開発や IETF の相互接続試験への参加を通して、MoQT の仕様標準化に貢献していきたいと考えています。 より詳しいモチベーションについては、 MoQ とか勉強会#2 の登壇スライドをご覧ください。 相互接続試験の雰囲気と結果報告 3/15(土) ~ 3/16(日) は ハッカソン の時間として確保されており、3/16 の夕方にはその結果が会場全体に共有されます。 会場はテーブルごとに取り組む内容が分かれており、私は Media over QUIC / RTP over QUIC のテーブルに合流しました。 MoQ のテーブルには、Meta や Cisco、Meetecho、学生の方など、4 人 ~ 8 人程度の方が集まっていました。IETF のセッションで発表予定の方もいたため、土日通してテーブルにいたのは 4 人程度でした。 私は IETF 初参加で初対面の方ばかりだったのですが、打ち解けやすい環境で、非常に楽しく相互接続試験を行えました。 今回の相互接続試験では、IETF 121 での結果に引き続き、多くの MoQT 実装との接続に成功しました。持ち寄られた MoQT 実装は、draft-08 対応している実装と、draft-10 対応している実装で分かれていたものの、moq-wasm は draft-10 に対応していたため、適宜修正しながら相互接続試験を行いました。 MoQT は WebTransport と Raw QUIC の両方に対応しているプロトコルですが、moq-wasm は WebTrasnport のみ対応しているため、WebTransport に対応している moxygen, meetecho, aiomoqt, moqtail などの実装と相互接続試験を行い、接続に成功しました。相互接続試験の結果については、下図をご確認ください。 相互接続試験で見つかった問題 1 ( moxygen ↔ moq-wasm ) Meta が開発している moxygen の client と moq-wasm を接続した際に、MoQT ではなく WebTransport レベルで疎通できないという問題に遭遇しました。 この問題は、moxygen が利用している proxygen という HTTP3(WebTransport)ライブラリと、moq-wasm が利用している wtransport という WebTransport ライブラリが疎通しない問題でした。 wtransport が WebTransport の双方向ストリームを確立する際に、想定されている種別ではない QUIC フレームを受け取ると確立できずに破棄されてしまうのが問題であったため、wtransport にパッチを加えることで解消できました。この変更は wtransport に PR を上げて、現在はマージされています。 この問題は IETF121 での相互接続試験でも発生しており、解消できていない問題でした。 自身で実装していない QUIC / WebTransport ライブラリの問題で、原因究明には 3 日ほどかかってしまいましたが、IETF 期間中に解消でき、moxygen client と疎通が確認できたため良かったです。 相互接続試験で見つかった問題 2 ( aiomoqt ↔ moq-wasm) aiomoqt と moq-wasm を接続した際に、MoQT レベルで疎通しない・特定のメッセージがパースできない問題に遭遇しました。 MoQT レベルで疎通しない問題に関しては、aiomoqt のサーバー実装で受け取った URL が正しくデコードされていない問題であり、こちらは aiomoqt の実装者の方に連絡することで解決できました。 また、特定のメッセージがパースできない問題に関しては、我々の実装である moq-wasm において SUBSCRIBE メッセージの Parameter 情報の処理が誤っていたことが分かったため、修正を加えた所、疎通が確認できました。 相互接続試験で見つかった問題 3 ( 複数の実装 ↔ moq-wasm) 複数の実装から我々の moq-wasm サーバーに SUBSCRIBE メッセージを送信した際に、サーバー側で MoQT メッセージが受け取れず、処理できないという問題が発生しました。 この問題の原因究明には非常に時間がかかりましたが、QUIC のログを確認したところ、「パケットサイズが大きい MoQT メッセージを双方向ストリームで受け取った際にパケットロスが発生し、回復しない」という事象が原因であるとわかりました。 この問題の根本的な原因は未だ分かっていませんが、QUIC のパケットロス検知の閾値の変更や、QUIC パケットのサイズの上限を設定する修正を加えたところ、疎通が確認できました。 結果を振り返って 今回の相互接続試験では、解決に時間を要する問題が複数発生しました。 しかし、前回の IETF121 では接続できなかった moxygen client ↔ moq-wasm server 間での接続も成功し、wtransport に PR を上げることで、WebTransport の相互接続試験という観点でも貢献できました。意義のある取り組みになったと感じています。 今後は QUIC / WebTransport の問題にも迅速に対応できるよう、これらの仕様についても理解する必要がありそうです。 「MoQT 実装は QUIC 対応のみの実装も多い」という事も実感できたため、今後は moq-wasm の Raw QUIC 対応も行っていきたいと考えています。 おわりに 今後も MoQT の仕様変更に追従して moq-wasm の開発を継続し、Raw QUIC 対応や、RTP over QUIC の実装なども行うことで、仕様の標準化に貢献していきたいと思っています。
アバター