Amazon Machine Learning 提供了二元分類別、多值分類別和迴歸分析等預測功能,並可使用 AUC、F1 分數和 RMSE 等指標評估模型效能。開發 Alexa Skill 時,可運用 ASK CLI 和 AWS CLI 建立和佈署 Lambda 函式,實作自定義的語音互動功能。本文以 Java 為例,示範如何建構 Lambda 後端,處理 Alexa Skill 的各種意圖請求,例如自我介紹、啟動、幫助、取消和停止等。透過整合 ASK SDK for Java,開發者可以輕鬆地建立和管理 Alexa Skill 的不同意圖處理器,並將其佈署至 AWS Lambda。此外,文章也說明如何使用 AWS CLI 建立和組態 Lambda 函式,並使用 ASK CLI 建立和管理 Alexa Skill,包含 Skill Manifest 和 Model 的設定。最後,文章簡述了 Alexa 如何利用 ASR 和 NLU 技術處理使用者請求,並提及 Amazon Lex 在對話介面中的應用。

Amazon Machine Learning 與 Alexa Skill 開發

深入理解 Amazon Machine Learning

Amazon Machine Learning(Amazon ML)主要用於以下預測場景:

  1. 二元分類別:將值分類別為兩類別之一,例如真或假(或 1 或 0)。
  2. 多值分類別:將值分類別到多個群組。
  3. 迴歸分析:預測數值。

資料來源物件(DataSource Object)

DataSource 物件可以參照來自不同來源的資料,例如 S3、Redshift 和 RDS。在本範例中,我們使用了 S3 資料來源。對於 CreateMLModel、CreateEvaluation 或 CreateBatchPrediction 等操作,必須指定 DataSource 物件。

接收者操作特徵曲線(Receiver Operating Characteristic, ROC)與曲線下面積(Area Under the ROC Curve, AUC)

ROC 曲線是一個圖表,顯示分類別模型在不同分類別閾值下的效能。AUC 測量整個 ROC 曲線下的二維面積,匯總所有分類別閾值下的效能衡量指標。AUC 值表示模型預測正例得分高於負例的能力,其值範圍從 0 到 1。AUC 值越高,表示機器學習模型越好。我們使用 AUC 來衡量二元分類別模型的品質。

評估模型效能

對於二元分類別模型,我們使用 AUC 值來評估其準確度。對於多值分類別模型,AWS 使用宏平均 F1 分數來評估多類別指標的預測準確度。F1 分數越大,表示預測準確度越好。對於迴歸模型,AWS 使用均方根誤差(RMSE)指標。RMSE 值越小,表示模型的準確度越好。

Alexa Skill 開發

建置與測試 Alexa Skill(使用 Java for Lambda 和 CLI for Alexa Skill)

對話式使用者介面(CUI)是一種支援類別似真實人類對話的平台,讓人類可以使用自然語言與系統互動。Amazon Alexa 是一種虛擬助理,透過其 API,可以擴充套件預設的 Alexa 功能,建立自定義的 Alexa Skill。

在本範例中,我們將建立一個簡單的 Alexa Skill,用於介紹一個人。這種 Skill 可用於公司活動或外部活動。要示範 Alexa Skill,可以使用任何支援 Alexa 的裝置,或使用模擬器,如 echosim.io。

環境準備

  1. 有效的 AWS 帳戶。
  2. 安裝並組態 Alexa Skills Kit CLI。
  3. Echo 裝置或模擬器,如 echosim.io。
  4. 基本的 Java 和 Maven 知識,以及建立和呼叫 Lambda 的步驟。

安裝和組態 ASK CLI

  1. 在 Windows 平台上安裝 Node.js Windows 建置工具。
  2. 使用 npm install -g ask-cli 安裝 ask-cli。
  3. 使用 ask init 初始化 ask-cli,並選擇 AWS 設定檔。
設定 ask-cli

初始化 ask-cli 後,需要選擇 AWS 設定檔並登入 AWS 帳戶。成功後,可以關閉瀏覽器視窗並傳回終端機。

開發 Alexa Skill

建立簡單的 Alexa Skill

本範例將建立一個簡單的 Alexa Skill,當詢問 Alexa 時,它將讀出一個人的介紹。您可以修改範例程式碼並佈署到您的 AWS 帳戶中。

// 以下是一個簡單的 Java 程式碼範例,用於 Lambda 函式
public class IntroductionHandler implements RequestHandler {
    @Override
    public Response handleRequest(HandlerInput input) {
        String speechText = "你好!歡迎來到我們的活動。";
        return input.getResponseBuilder()
                .withSpeech(speechText)
                .withSimpleCard("Introduction", speechText)
                .build();
    }
}

程式碼解析

  1. 實作 RequestHandler 介面:自定義的處理器需要實作 RequestHandler 介面,以處理來自 Alexa 的請求。
  2. 生成回應:使用 HandlerInput 物件生成回應,包括語音回應和簡單卡片。

開發 Alexa Skill 的 Lambda 後端專案(Java)

開發 Alexa Skill 需要建立兩個主要部分:Lambda 後端處理實際的請求和回應,以及在 Alexa 開發者入口網站中的 Alexa Skill 設定,用於解釋使用者請求、與後端溝通並傳回回應。

步驟一:建立 Lambda 專案(Java)

ASK SDK 需要為每個預期的意圖(intent)實作意圖處理器(intent handlers)。我們將為應用程式特定的意圖(如自我介紹意圖)、啟動意圖、幫助、停止和取消意圖等內建意圖建立相應的處理器。此外,還需要建立一個父 Lambda 處理器類別來註冊所有這些意圖處理器。

定義 ASK SDK for Java 的依賴

首先,在 pom.xml 檔案中定義 ASK SDK for Java 的依賴:

<dependency>
    <groupId>com.amazon.alexa</groupId>
    <artifactId>ask-sdk</artifactId>
    <version>${ask.sdk.version}</version>
</dependency>

其中,${ask.sdk.version} 在父專案 serverless-cookbook-parent-aws-javapom.xml 中定義,例如:

<ask.sdk.version>2.11.2</ask.sdk.version>

實作意圖處理器

自我介紹意圖處理器(SelfIntroIntentHandler)

public class SelfIntroIntentHandler implements RequestHandler {
    @Override
    public boolean canHandle(HandlerInput input) {
        return input.matches(Predicates.intentName("SelfIntroIntent"));
    }

    @Override
    public Optional<Response> handle(HandlerInput input) {
        String speechText = "你好,這是 Alexa 為 Heartin Kanikathottu 做的自我介紹。 "
                + "Heartin 是一位資深軟體工程師和部落客,擁有約 11 年的 IT 經驗。 "
                + "他喜歡透過他的部落格(如 CloudMaterials.com 和 Java J EE dot com)分享他的技術知識。 "
                + "他也喜歡指導初學者,並在技術研討會和會議上發表演講。";
        return input.getResponseBuilder()
                .withSpeech(speechText)
                .withSimpleCard("SelfIntro", speechText)
                .build();
    }
}

啟動請求處理器(LaunchRequestHandler)

public class LaunchRequestHandler implements RequestHandler {
    @Override
    public boolean canHandle(HandlerInput input) {
        return input.matches(Predicates.requestType(LaunchRequest.class));
    }

    @Override
    public Optional<Response> handle(HandlerInput input) {
        String speechText = "歡迎使用 Heartin 的自我介紹 Alexa Skill,您可以說 '請說自我介紹'";
        return input.getResponseBuilder()
                .withSpeech(speechText)
                .withSimpleCard("SelfIntro", speechText)
                .withReprompt(speechText)
                .build();
    }
}

幫助意圖處理器(HelpIntentHandler)

public class HelpIntentHandler implements RequestHandler {
    @Override
    public boolean canHandle(HandlerInput input) {
        return input.matches(intentName("AMAZON.HelpIntent"));
    }

    @Override
    public Optional<Response> handle(HandlerInput input) {
        String speechText = "您可以說 '請說自我介紹'!";
        return input.getResponseBuilder()
                .withSpeech(speechText)
                .withSimpleCard("SelfIntro", speechText)
                .withReprompt(speechText)
                .build();
    }
}

取消和停止意圖處理器(CancelandStopIntentHandler)

public class CancelandStopIntentHandler implements RequestHandler {
    @Override
    public boolean canHandle(HandlerInput input) {
        return input.matches(intentName("AMAZON.StopIntent").or(intentName("AMAZON.CancelIntent")));
    }

    @Override
    public Optional<Response> handle(HandlerInput input) {
        return input.getResponseBuilder()
                .withSpeech("再見夥伴")
                .withSimpleCard("SelfIntro", "再見")
                .build();
    }
}

後備意圖處理器(FallbackIntentHandler)

public class FallbackIntentHandler implements RequestHandler {
    @Override
    public boolean canHandle(HandlerInput input) {
        return input.matches(intentName("AMAZON.FallbackIntent"));
    }

    @Override
    public Optional<Response> handle(HandlerInput input) {
        String speechText = "抱歉夥伴,我不知道那個。您可以嘗試說幫助!";
        return input.getResponseBuilder()
                .withSpeech(speechText)
                .withSimpleCard("SelfIntro", speechText)
                .withReprompt(speechText)
                .build();
    }
}

工作階段結束請求處理器(SessionEndedRequestHandler)

public class SessionEndedRequestHandler implements RequestHandler {
    @Override
    public boolean canHandle(HandlerInput input) {
        return input.matches(requestType(SessionEndedRequest.class));
    }

    @Override
    public Optional<Response> handle(HandlerInput input) {
        // 在此處新增清理邏輯
        return input.getResponseBuilder().build();
    }
}

建立 Lambda 處理器類別

最後,建立一個 Lambda 處理器類別,擴充套件 SkillStreamHandler

public class SelfIntroStreamHandler extends SkillStreamHandler {
    private static Skill skill = Skills.standard()
            .addRequestHandlers(
                    new CancelandStopIntentHandler(),
                    new SelfIntroIntentHandler(),
                    new HelpIntentHandler(),
                    new LaunchRequestHandler(),
                    new SessionEndedRequestHandler())
            .build();

    public SelfIntroStreamHandler() {
        super(skill);
    }
}

內容解密:

此段程式碼展示瞭如何使用 ASK SDK for Java 建立 Alexa Skill 的 Lambda 後端。主要步驟包括定義專案依賴、實作各種意圖處理器(如自我介紹、啟動、幫助等),以及建立 Lambda 處理器類別。每個意圖處理器負責處理特定的使用者請求或意圖,並傳回相應的語音回應。Lambda 處理器類別則負責註冊所有意圖處理器並處理傳入的請求。整個流程確保了 Alexa Skill 能夠正確回應使用者的各種請求。

使用 AWS CLI 佈署 Alexa Skill 與 Lambda 函式

步驟 1 - 準備工作

在開始之前,請確保您已具備以下條件:

  • 已安裝 AWS CLI 並組態好相應的許可權
  • 已安裝 ASK CLI 用於管理 Alexa Skill
  • 具備 Java 開發環境,用於編譯 Lambda 函式

步驟 2 - 使用 AWS CLI 組態 Lambda 函式

請按照以下步驟佈署並呼叫 Lambda 函式:

  1. 編譯 Lambda 函式

    mvn clean package
    

    此命令將在 Lambda 專案根資料夾中建立 Uber JAR 檔案。

  2. 上傳 Uber JAR 至 S3 將編譯好的 JAR 檔案上傳至指定的 S3 儲存桶。

  3. 建立 IAM Role 建立一個名為 lambda-alexa-simple-intro-role 的 IAM Role,並設定適當的信任關係。

  4. 建立並附加日誌許可權策略 建立一個具有基本日誌許可權的策略,並將其附加至剛建立的 IAM Role。

  5. 建立 Lambda 函式

    aws lambda create-function \
    --function-name lambda-alexa-simple-intro \
    --runtime java8 \
    --role arn:aws:iam::<account id>:role/lambda-alexa-simple-intro-role \
    --handler tech.heartin.books.serverlesscookbook.SelfIntroStreamHandler::handleRequest \
    --code S3Bucket=serverless-cookbook,S3Key=lambda-alexa-simple-intro-0.0.1-SNAPSHOT.jar \
    --timeout 15 \
    --memory-size 512 \
    --region us-east-1 \
    --profile admin
    

    請將 <account id> 替換為您的 AWS 帳戶 ID。

  6. 授權 Alexa Skill 呼叫 Lambda

    aws lambda add-permission \
    --function-name lambda-alexa-simple-intro \
    --statement-id "12345" \
    --action "lambda:InvokeFunction" \
    --principal "alexa-appkit.amazon.com" \
    --region us-east-1 \
    --profile admin
    

程式碼解析:

  • aws lambda create-function 命令:用於建立一個新的 Lambda 函式,指定了函式名稱、執行環境、執行角色、處理程式等關鍵引數。
  • --code 引數:指定了 Lambda 函式的程式碼來源,此處為 S3 中的 JAR 檔案。
  • aws lambda add-permission 命令:允許指定的 Alexa Skill 呼叫 Lambda 函式。

步驟 3 - 使用 ASK CLI 建立 Alexa Skill

請按照以下步驟建立 Alexa Skill:

  1. 準備 Skill Manifest JSON 檔案

    {
      "manifest": {
        "publishingInformation": {
          "locales": {
            "en-US": {
              "summary": "A simple skill to say introduction for someone",
              "examplePhrases": [
                "Alexa please say intro",
                "say intro",
                "help"
              ],
              "name": "self-intro",
              "description": "Simple Intro Skill"
            }
          },
          "isAvailableWorldwide": true,
          "testingInstructions": "1) Say 'Alexa, say intro'.",
          "category": "ORGANIZERS_AND_ASSISTANTS",
          "distributionCountries": []
        },
        "apis": {
          "custom": {
            "endpoint": {
              "uri": "arn:aws:lambda:us-east-1:<account id>:function:lambda-alexa-simple-intro"
            }
          }
        },
        "manifestVersion": "1.0"
      }
    }
    

    將此檔案儲存為 skill.json

  2. 建立 Skill

    ask api create-skill --file skill.json
    
  3. 準備 Model JSON 檔案

    {
      "interactionModel": {
        "languageModel": {
          "invocationName": "self-intro",
          "intents": [
            {
              "name": "AMAZON.CancelIntent",
              "samples": []
            },
            {
              "name": "AMAZON.HelpIntent",
              "samples": []
            },
            {
              "name": "AMAZON.StopIntent",
              "samples": []
            },
            {
              "name": "SelfIntroIntent",
              "samples": [
                "please say intro",
                "say intro",
                "please say intro for",
                "say intro for",
                "intro",
                "intro for"
              ]
            }
          ]
        }
      }
    }
    

    將此檔案儲存為 en-US.json

  4. 更新 Skill Model

    ask api update-model \
    --skill-id amzn1.ask.skill.ab1fdfac-42eb-42ae-aeae-b761f3c903c1 \
    --file en-US.json \
    --locale en-US
    
  5. 啟用 Skill

    ask api enable-skill \
    --skill-id amzn1.ask.skill.a585bf93-15bb-4361-ab56-ffbdc66027fd
    

程式碼解析:

  • ask api create-skill 命令:根據提供的 skill.json 建立新的 Alexa Skill。
  • ask api update-model 命令:更新指定 Skill 的互動模型。
  • ask api enable-skill 命令:啟用指定的 Alexa Skill,使其可供測試或發布。

如何運作

Alexa 使用自動語音辨識(ASR)和自然語言理解(NLU)來處理使用者請求。在內部,Alexa 利用 Amazon Lex,這是 AWS 的主要服務,用於使用 ASR 和 NLU 建立對話介面。

詳細解析:

  • 自動語音辨識(ASR):將使用者的語音轉換為文字。
  • 自然語言理解(NLU):理解轉換後的文字內容,並識別使用者的意圖。
  • Amazon Lex:AWS 提供的一個服務,用於建立對話介面,可與多種應用程式整合,提供智慧對話體驗。