Speech SDK for C# を使用して音声の意図を認識する方法

Azure AI サービス Speech SDKLanguage Understanding サービス (LUIS) と統合して意図認識 機能を提供します。 意図は、航空機の予約や天気のチェック、あるいは電話を掛けるなどのユーザーが実行したい行動です。 ユーザーは、自然だと思われるどのような用語でも使用できます。 LUIS は、ユーザー要求を定義した意図にマップします。

Note

LUIS アプリケーションでは、認識する意図およびエンティティを定義します。 これは Speech サービスを使用する C# アプリケーションとは別のものです。 この記事では、「アプリ」は LUIS アプリを意味し、「アプリケーション」は C# のコードを意味します。

このガイドでは、Speech SDK を使用して、デバイスのマイクを通したユーザーの発話から意図を引き出す C# コンソール アプリケーションを開発します。 学習内容は次のとおりです。

  • Speech SDK NuGet パッケージを参照する Visual Studio プロジェクトを作成する
  • 音声構成を作成して意図認識エンジンを取得する
  • LUIS アプリのモデルを取得して必要な意図を追加する
  • 音声認識の言語を指定する
  • ファイルから音声を認識する
  • 非同期のイベント ドリブンの継続的な認識を使用する

前提条件

このガイドを開始する前に、次の項目を用意する必要があります。

LUIS および音声認識

LUIS は音声から意図を認識するために Speech サービスと統合しています。 Speech サービスのサブスクリプションは不要で、LUIS だけでかまいません。

LUIS では 2 種類のキーを使用します。

キーの種類 目的
Authoring LUIS アプリをプログラムで作成および変更できる
予測 実行時に LUIS アプリケーションにアクセスするために使用される

このガイドには、予測キーの種類が必要です。 このガイドでは、事前構築済みホーム オートメーション アプリの使用に関するクイックスタートに従って作成できるホーム オートメーション LUIS アプリのサンプルを使用します。 独自の LUIS アプリを作成した場合は、代わりにそれを使用することができます。

LUIS アプリを作成すると、オーサリング キーが自動的に生成されるため、テキスト クエリを使用してアプリをテストできるようになります。 このキーでは Speech サービスとの統合が有効にならないため、このガイドでこれを使用することはできません。 Azure ダッシュボードで LUIS リソースを作成して LUIS アプリに割り当ててください。 このガイドでは、無料のサブスクリプション階層を使用することができます。

Azure ダッシュ ボードで LUIS のリソースを作成した後、LUIS ポータルにログインし、 [マイ アプリ] ページで自分のアプリケーションを選択し、アプリの [Manage](管理) ページに切り替えます。 最後に、サイドバーで [Azure リソース] を選択します。

LUIS ポータルのキーとエンドポイントの設定のスクリーンショットを示します。

[Azure リソース] ページで、次の操作を実行します。

キーの横にあるアイコンを選択して、それをクリップボードにコピーします。 (どちらのキーも使用することができます)。

プロジェクトを作成してワークロードを追加する

Windows 開発用に Visual Studio プロジェクトを作成するためには、プロジェクトの作成、.NET デスクトップ開発用の Visual Studio 設定、Speech SDK のインストール、ターゲット アーキテクチャの選択が必要となります。

まず、Visual Studio でプロジェクトを作成し、.NET デスクトップ開発用に Visual Studio を設定します。

  1. Visual Studio 2019 を開きます。

  2. [開始] ウィンドウで、 [新しいプロジェクトの作成] を選択します。

  3. [新しいプロジェクトの作成] ウィンドウで、[コンソール アプリ (.NET Framework)] を選択してから、[次へ] を選択します。

  4. [新しいプロジェクトの構成] ウィンドウの [プロジェクト名] に「helloworld」と入力し、保存先となるディレクトリのパスを選択するか作成して、[作成] を選択します。

  5. Visual Studio のメニュー バーから、 [ツール]>[ツールと機能を取得] の順に選択して Visual Studio インストーラーを開き、 [変更中] ダイアログ ボックスを表示します。

  6. [.NET デスクトップ開発] ワークロードが利用できるかどうかを確認します。 このワークロードがインストールされていない場合は、その横にあるチェック ボックスをオンにし、[変更] を選択してインストールを開始してください。 ダウンロードとインストールに数分かかる場合があります。

    [.NET デスクトップ開発] の横のチェック ボックスが既にオンになっている場合は、[閉じる] を選択してダイアログ ボックスを終了します。

    .NET デスクトップ環境を有効にする

  7. Visual Studio インストーラーを閉じます。

Speech SDK のインストール

次の手順は、コード内で参照できるように、Speech SDK NuGet パッケージをインストールすることです。

  1. ソリューション エクスプローラーで helloworld プロジェクトを右クリックし、 [NuGet パッケージの管理] を選択して NuGet パッケージ マネージャーを表示します。

    NuGet パッケージ マネージャー

  2. 右上隅で [パッケージ ソース] ドロップダウン ボックスを探し、[nuget.org] が選択されていることを確認します。

  3. 左上隅で [参照] を選択します。

  4. 検索ボックスに「Microsoft.CognitiveServices.Speech」と入力し、Enter キーを押します。

  5. 検索結果から [Microsoft.CognitiveServices.Speech] パッケージを選択し、[インストール] を選択して最新の安定バージョンをインストールします。

    Microsoft.CognitiveServices.Speech NuGet パッケージをインストールする

  6. すべての契約とライセンスに同意して、インストールを開始します。

    パッケージがインストールされると、 [パッケージマネージャー コンソール] ウィンドウに確認が表示されます。

ターゲット アーキテクチャを選択する

次に、コンソール アプリケーションをビルドして実行する目的で、お使いのコンピューターのアーキテクチャに合ったプラットフォーム構成を作成します。

  1. メニュー バーから、 [ビルド][構成マネージャー] の順に選択します。 [構成マネージャー] ダイアログ ボックスが表示されます。

    [構成マネージャー] ダイアログボックス

  2. [アクティブ ソリューション プラットフォーム] ドロップダウン ボックスで [新規] を選択します。 [新しいソリューション プラットフォーム] ダイアログ ボックスが表示されます。

  3. [新しいプラットフォームを入力または選択してください] ドロップダウン ボックスで:

    • 64 ビット Windows を実行している場合、x64 を選択します。
    • 32 ビット Windows を実行している場合、x86 を選択します。
  4. [OK][閉じる] の順に選択します。

コードの追加

次に、プロジェクトにコードを追加します。

  1. ソリューション エクスプローラーProgram.cs ファイルを開きます。

  2. ファイルの先頭にある using ステートメントのブロックを次の宣言に置き換えます。

    using System;
    using System.Threading.Tasks;
    using Microsoft.CognitiveServices.Speech;
    using Microsoft.CognitiveServices.Speech.Audio;
    using Microsoft.CognitiveServices.Speech.Intent;
    
  3. 用意されている Main() メソッドを、次のように対応する非同期メソッドに置き換えます。

    public static async Task Main()
    {
        await RecognizeIntentAsync();
        Console.WriteLine("Please press Enter to continue.");
        Console.ReadLine();
    }
    
  4. 次に示すように空の非同期メソッド RecognizeIntentAsync() を作成します。

    static async Task RecognizeIntentAsync()
    {
    }
    
  5. この新しいメソッドの本文に、このコードを追加します。

    // Creates an instance of a speech config with specified subscription key
    // and service region. Note that in contrast to other services supported by
    // the Cognitive Services Speech SDK, the Language Understanding service
    // requires a specific subscription key from https://www.luis.ai/.
    // The Language Understanding service calls the required key 'endpoint key'.
    // Once you've obtained it, replace with below with your own Language Understanding subscription key
    // and service region (e.g., "westus").
    // The default language is "en-us".
    var config = SpeechConfig.FromSubscription("YourLanguageUnderstandingSubscriptionKey", "YourLanguageUnderstandingServiceRegion");
    
    // Creates an intent recognizer using microphone as audio input.
    using (var recognizer = new IntentRecognizer(config))
    {
        // Creates a Language Understanding model using the app id, and adds specific intents from your model
        var model = LanguageUnderstandingModel.FromAppId("YourLanguageUnderstandingAppId");
        recognizer.AddIntent(model, "YourLanguageUnderstandingIntentName1", "id1");
        recognizer.AddIntent(model, "YourLanguageUnderstandingIntentName2", "id2");
        recognizer.AddIntent(model, "YourLanguageUnderstandingIntentName3", "any-IntentId-here");
    
        // Starts recognizing.
        Console.WriteLine("Say something...");
    
        // Starts intent recognition, and returns after a single utterance is recognized. The end of a
        // single utterance is determined by listening for silence at the end or until a maximum of 15
        // seconds of audio is processed.  The task returns the recognition text as result. 
        // Note: Since RecognizeOnceAsync() returns only a single utterance, it is suitable only for single
        // shot recognition like command or query. 
        // For long-running multi-utterance recognition, use StartContinuousRecognitionAsync() instead.
        var result = await recognizer.RecognizeOnceAsync().ConfigureAwait(false);
    
        // Checks result.
        if (result.Reason == ResultReason.RecognizedIntent)
        {
            Console.WriteLine($"RECOGNIZED: Text={result.Text}");
            Console.WriteLine($"    Intent Id: {result.IntentId}.");
            Console.WriteLine($"    Language Understanding JSON: {result.Properties.GetProperty(PropertyId.LanguageUnderstandingServiceResponse_JsonResult)}.");
        }
        else if (result.Reason == ResultReason.RecognizedSpeech)
        {
            Console.WriteLine($"RECOGNIZED: Text={result.Text}");
            Console.WriteLine($"    Intent not recognized.");
        }
        else if (result.Reason == ResultReason.NoMatch)
        {
            Console.WriteLine($"NOMATCH: Speech could not be recognized.");
        }
        else if (result.Reason == ResultReason.Canceled)
        {
            var cancellation = CancellationDetails.FromResult(result);
            Console.WriteLine($"CANCELED: Reason={cancellation.Reason}");
    
            if (cancellation.Reason == CancellationReason.Error)
            {
                Console.WriteLine($"CANCELED: ErrorCode={cancellation.ErrorCode}");
                Console.WriteLine($"CANCELED: ErrorDetails={cancellation.ErrorDetails}");
                Console.WriteLine($"CANCELED: Did you update the subscription info?");
            }
        }
    }
    
  6. このメソッド内のプレース ホルダーを、次のようにして実際の LUIS リソース キー、リージョン、およびアプリ ID で置き換えます。

    プレースホルダー 置換後の文字列
    YourLanguageUnderstandingSubscriptionKey 実際の LUIS リソース キー。 前と同様に、この項目を Azure ダッシュボードから取得する必要があります。 LUIS ポータルの、アプリの[Azure リソース] ページ ([管理] の下) で見つけることができます。
    YourLanguageUnderstandingServiceRegion LUIS リソースが存在するリージョンを示す短い識別子で、たとえば米国西部の場合は westus です。 リージョンを参照してください。
    YourLanguageUnderstandingAppId LUIS アプリ ID。 LUIS ポータルのアプリの [Settings](設定) ページで確認できます。

これらの変更を行ってから、アプリケーションをビルド (Ctrl + Shift + B) および実行 (F5) できます。 入力を求められたら、PC のマイクに向かって "Turn off the lights (照明を消して)" と言ってみてください。 アプリケーションによって、結果がコンソール ウィンドウに表示されます。

次のセクションでには、コードの詳細について説明します。

意図認識エンジンを作成する

まず、LUIS の予測キーとリージョンから音声構成を作成する必要があります。 音声構成は、Speech SDK のさまざまな機能のための認識エンジンを作成するために使用できます。 音声構成では、使用するリソースを指定する複数の方法があり、ここでは、リソース キーとリージョンを受け取る FromSubscription を使用します。

Note

Speech リソースではなく LUIS リソースのキーおよびリージョンを使用してください。

次に、new IntentRecognizer(config) を使用して意図認識エンジンを作成します。 構成では、使用するリソースが既にわかっているため、認識エンジンの作成時にキーを再び指定する必要はありません。

LUIS モデルをインポートして意図を追加する

ここで、LanguageUnderstandingModel.FromAppId() を使用して LUIS アプリからモデルをインポートし、認識エンジンの AddIntent() メソッドを経由して認識する LUIS の意図を追加します。 これら 2 つの手順により、ユーザーが自身の要求で使用すると思われる単語を示すことで音声認識の精度が向上します。 アプリケーション内でそのすべての意図を認識する必要がないのであれば、それらをすべて追加する必要はありません。

意図を追加するには、LUIS モデル (model という名前)、意図名、意図 ID の 3 つの引数を指定する必要があります。 ID と名前の違いは次のとおりです。

AddIntent() 引数 目的
intentName LUIS アプリ内で定義される意図の名前。 この値が LUIS の意図名と正確に一致する必要があります。
intentID Speech SDK によって認識された意図に割り当てられた ID。 この値には任意のものを使用できます。LUIS アプリで定義されている意図名に対応させる必要はありません。 同じコードによって複数の意図が処理される場合、それらに対して同じ ID を使用できます。

ホーム オートメーション LUIS アプリには、2 つの意図があります。1 つはデバイスをオンにするための意図、もう 1 つはデバイスをオフにするための意図です。 以下の行はこれらの意図を認識エンジンに追加します。 RecognizeIntentAsync() メソッド内の 3 つの AddIntent 行をこのコードで置換します。

recognizer.AddIntent(model, "HomeAutomation.TurnOff", "off");
recognizer.AddIntent(model, "HomeAutomation.TurnOn", "on");

個々の意図を追加する代わりに、AddAllIntents メソッドを使用して、モデル内のすべての意図を認識エンジンに追加することもできます。

認識を開始する

認識エンジンを作成して意図を追加したら、認識を開始できます。 Speech SDK は、単発の認識および継続的な認識の両方をサポートしています。

認識モード 呼び出すメソッド 結果
単発 RecognizeOnceAsync() 1 回の発話後に認識された意図を返します (ある場合)。
継続的 StartContinuousRecognitionAsync()
StopContinuousRecognitionAsync()
複数の発話を認識し、結果が得られた場合はイベント (例: IntermediateResultReceived) を出力します。

このアプリケーションでは単発モードが使用されるため、RecognizeOnceAsync() を呼び出して認識を開始します。 結果は、認識された意図に関する情報を含む IntentRecognitionResult オブジェクトです。 LUIS の JSON 応答は、次の式を使用して抽出します。

result.Properties.GetProperty(PropertyId.LanguageUnderstandingServiceResponse_JsonResult)

アプリケーションは JSON の結果を解析しません。 JSON テキストをコンソール ウィンドウに表示するだけです。

単一の LUIS 認識の結果

認識言語を指定する

既定では、LUIS は意図を米国英語 (en-us) で認識します。 音声構成の SpeechRecognitionLanguage プロパティにロケール コードを割り当てることで、意図を他の言語で認識することができます。 たとえば、認識エンジンを作成する前にアプリケーションに config.SpeechRecognitionLanguage = "de-de"; を追加すると、意図はドイツ語で認識されます。 詳細については、LUIS の言語サポートに関するページを参照してください。

ファイルからの継続的な認識

次のコードは、Speech SDK を使用した意図認識のさらに 2 つの機能を示しています。 1 つ目は、前に述べた継続的な認識です。この機能では、結果が使用可能になると認識エンジンがイベントを出力します。 これらのイベントは、指定したイベント ハンドラーによって処理されます。 継続的な認識では、RecognizeOnceAsync() の代わりに認識エンジンの StartContinuousRecognitionAsync() メソッドを呼び出して認識を開始します。

もう 1 つの機能では、処理の対象となる音声を含むオーディオを WAV ファイルから読み取ります。 実装では、意図認識エンジンを作成するときに使用できるオーディオ構成を作成する必要があります。 ファイルはサンプリング速度が 16 kHz の単一チャネル (モノラル) である必要があります。

これらの機能を試すには、RecognizeIntentAsync() メソッドの本文を削除するかコメントにし、その位置に次のコードを追加します。

// Creates an instance of a speech config with specified subscription key
// and service region. Note that in contrast to other services supported by
// the Cognitive Services Speech SDK, the Language Understanding service
// requires a specific subscription key from https://www.luis.ai/.
// The Language Understanding service calls the required key 'endpoint key'.
// Once you've obtained it, replace with below with your own Language Understanding subscription key
// and service region (e.g., "westus").
var config = SpeechConfig.FromSubscription("YourLanguageUnderstandingSubscriptionKey", "YourLanguageUnderstandingServiceRegion");

// Creates an intent recognizer using file as audio input.
// Replace with your own audio file name.
using (var audioInput = AudioConfig.FromWavFileInput("YourAudioFile.wav"))
{
    using (var recognizer = new IntentRecognizer(config, audioInput))
    {
        // The TaskCompletionSource to stop recognition.
        var stopRecognition = new TaskCompletionSource<int>(TaskCreationOptions.RunContinuationsAsynchronously);

        // Creates a Language Understanding model using the app id, and adds specific intents from your model
        var model = LanguageUnderstandingModel.FromAppId("YourLanguageUnderstandingAppId");
        recognizer.AddIntent(model, "YourLanguageUnderstandingIntentName1", "id1");
        recognizer.AddIntent(model, "YourLanguageUnderstandingIntentName2", "id2");
        recognizer.AddIntent(model, "YourLanguageUnderstandingIntentName3", "any-IntentId-here");

        // Subscribes to events.
        recognizer.Recognizing += (s, e) =>
        {
            Console.WriteLine($"RECOGNIZING: Text={e.Result.Text}");
        };

        recognizer.Recognized += (s, e) =>
        {
            if (e.Result.Reason == ResultReason.RecognizedIntent)
            {
                Console.WriteLine($"RECOGNIZED: Text={e.Result.Text}");
                Console.WriteLine($"    Intent Id: {e.Result.IntentId}.");
                Console.WriteLine($"    Language Understanding JSON: {e.Result.Properties.GetProperty(PropertyId.LanguageUnderstandingServiceResponse_JsonResult)}.");
            }
            else if (e.Result.Reason == ResultReason.RecognizedSpeech)
            {
                Console.WriteLine($"RECOGNIZED: Text={e.Result.Text}");
                Console.WriteLine($"    Intent not recognized.");
            }
            else if (e.Result.Reason == ResultReason.NoMatch)
            {
                Console.WriteLine($"NOMATCH: Speech could not be recognized.");
            }
        };

        recognizer.Canceled += (s, e) =>
        {
            Console.WriteLine($"CANCELED: Reason={e.Reason}");

            if (e.Reason == CancellationReason.Error)
            {
                Console.WriteLine($"CANCELED: ErrorCode={e.ErrorCode}");
                Console.WriteLine($"CANCELED: ErrorDetails={e.ErrorDetails}");
                Console.WriteLine($"CANCELED: Did you update the subscription info?");
            }

            stopRecognition.TrySetResult(0);
        };

        recognizer.SessionStarted += (s, e) =>
        {
            Console.WriteLine("\n    Session started event.");
        };

        recognizer.SessionStopped += (s, e) =>
        {
            Console.WriteLine("\n    Session stopped event.");
            Console.WriteLine("\nStop recognition.");
            stopRecognition.TrySetResult(0);
        };


        // Starts continuous recognition. Uses StopContinuousRecognitionAsync() to stop recognition.
        await recognizer.StartContinuousRecognitionAsync().ConfigureAwait(false);

        // Waits for completion.
        // Use Task.WaitAny to keep the task rooted.
        Task.WaitAny(new[] { stopRecognition.Task });

        // Stops recognition.
        await recognizer.StopContinuousRecognitionAsync().ConfigureAwait(false);
    }
}

コードを修正して、LUIS 予測キー、リージョン、アプリ ID を含め、以前と同様にホーム オートメーションの意図を追加します。 whatstheweatherlike.wav を、録音済みのオーディオ ファイルの名前に変更します。 次に、ビルドを実行し、オーディオ ファイルをビルド ディレクトリにコピーして、アプリケーションを実行します。

たとえば、録音済みのオーディオ ファイル内で、"Turn off the lights (照明を消して)" と言ってから一呼吸置き、"Turn on the lights (照明を付けて)" と言った場合、次のような結果がコンソールから出力されます。

オーディオ ファイルに対する LUIS の認識結果

Speech SDK チームでは、多数の例をオープンソース リポジトリで積極的に管理しています。 ソースコードリポジトリのサンプルについては、GitHub の Azure AI 音声 SDK を参照してください。 C++、Java、 C#Python、Objective-C、Swift、JavaScript、UWP、Unity、および Xamarin 用のサンプルが用意されています。 この記事のコードを samples/csharp/sharedcontent/console フォルダーから探します。

次のステップ

クイック スタート: マイクから音声を認識する