クラウド同上

機械学習を用いたCloud APIで、試験勉強用に英単語Botを作る

Author
Lv:2 Exp:730

モノづくり、時々ハイボール。

中学に通う息子の中間試験が終わり、英語の点数があまり良くなかったので本人に聞いてみたところ、「言葉の壁なんてそのうちテクノロジーが解決するんだから、勉強なんて要らなくね?」と、もっともらしいことを言い出したので、次の期末試験に向けて英単語Botを作ってサポートすることにしました!

ただし、英単語の登録に手間がかかるようだと使ってくれないので、スマホで教科書のページを撮影してBotに送るだけで、英単語登録、和訳、発音の確認まで出来るようなBotにしたいと思います。

ということで、作るBotはLINE Botで、Cloud API(Cloud Vision APIとCloud Text-to-Speech API)とGoogle Apps Scriptを使用し、下記のような構成で作成したいと思います。

Cloud APIとは、Google Cloud Platform ( 以下、GCP ) が提供するサービスの一種で、GCPのあらゆる機能を簡単に使用することが出来ます。
今回は、Cloud Vision APICloud Text-to-Speech API を使用します。
(翻訳には、GASのLanguageAppクラスを使用します。)

Google App Script ( 以下、GAS )とは、Googleが提供するサーバーサイドのスクリプト環境で、Googleの色々なサービスをJavaScriptのコードベースで簡単に操作することが出来ます。

  
さて、今回の英単語Botですが、下記4つの機能があれば実現出来そうです。

■ 問題作成機能

■ 出題機能

■ 音声再生機能

■ 解答機能

それでは、以下の手順で実装を進めて行きたいと思います。
1. GASでLINE Botを作成
2. Cloud Vision APIで英単語を抽出
3. 英単語を翻訳し、スプレッドシートに保存
4. Cloud Text-to-Speech APIで音声データを取得
5. 英単語Botの各機能を実装

早速始めましょう!

1. GASでLINE Botを作成

今回使用するGASですが、非常にBot開発に向いている環境と言えます。
他Googleサービスとの親和性も高いですし、Bot用にサーバーを立てる必要がなかったり、スプレッドシートを簡易的なデータベースとして使ったりも出来るからです。
まずは、LINE Botの設定周りから進めて行きます。

1.1. LINE Developersの設定

LINE Botは、LINE Messaging APIを使用して開発します。
それでは、LINE Developersにて、Botの名前やアイコン画像など必要な情報を設定し、新規チャネルを作成してみましょう。LINE Messaging APIを利用するために必要なChannelIDやトークンが発行されます。

LINE Developersにログインし、新規チャネル作成をクリックします。
apps-gcp-75-6

今回使用するのはMessaging APIなので、真ん中を選びます。
apps-gcp-75-7

必要事項を登録します。
apps-gcp-75-8-2

LINEの各規約に同意の上、チャネルを作成し、アクセストークンを発行してください。
Webhook送信の設定も「利用する」に変更しておきます。
apps-gcp-75-9

自動応答メッセージの設定は「利用しない」に変更します。
apps-gcp-75-10

LINE Developersでの設定は、一旦ここまでです。

1.2. GASでコーディング

次に、Botの本体を作成していきます。
試しに、「頑張れ」と送信すると「僕、頑張るよ!」と返してくれるとっても素直なBotを作ります。

まずはドライブの適当なフォルダに、新規スプレッドシートを作成します。
apps-gcp-75-11

スプレッドシートのメニューから、「ツール」>「スクリプトエディタ」を選びます。
apps-gcp-75-12

スクリプトエディタを開いたら、「ファイル」>「プロジェクトのプロパティ」を開き、
「スクリプトのプロパティ」タブに、LINE developersで発行したアクセストークンを、CHANNEL_ACCESS_TOKENという名前で保存しておきましょう。
apps-gcp-75-13

次に、Bot本体のコードを書いて行きます。
Botを友だち追加したり、Botへメッセージを送ったりすると、後ほど登録するWebhookURLに対してイベントデータが送られて来るようになります。
そのイベントデータを受信するため、doPost()を定義して行きます。

function doPost(e) {

    var json = JSON.parse(e.postData.contents);

    json.events.forEach(function (event) {

        var eventType  = event.type;
        var replyToken = event.replyToken;

        var messages = [];

        if(eventType == "message") {

            var messageId  = event.message.id;
            var messageType = event.message.type;

            if(messageType == "text") {

                var userMessage = event.message.text;

                switch(userMessage) {
                case "頑張れ":
                    messages.push({"type": "text", "text": "僕、頑張るよ!"});
                    break;

                default:
                    messages.push({"type": "text", "text": "・・・"});
                    break;
                }
            }
        }

        sendReplyMessage(replyToken, messages);
    });
}

function sendReplyMessage(replyToken, messages){

  var json = {
    "replyToken": replyToken,
    "messages": messages,
  }

  var payload = JSON.stringify(json);
  var url = "https://api.line.me/v2/bot/message/reply";
  var headers = {
    "Authorization": "Bearer " + getScriptProperty("CHANNEL_ACCESS_TOKEN"),
    "Content-Type": "application/json; charset=UTF-8",
  };

  var options = {
    "method" : "post",
    "headers" : headers,
    "payload" : payload,
  };

  return UrlFetchApp.fetch(url, options);  
}


function getScriptProperty(key){

  return PropertiesService.getScriptProperties().getProperty(key);
}

 
コードを書いたら、「公開」>「ウェブアプリケーションとして導入」を選択し、
「アプリケーションにアクセスできるユーザー」を「全員(匿名ユーザーを含む)」に
設定し、アプリケーションのURLを発行します。
apps-gcp-75-11

最後に、LINE Developersに戻り、上で発行されたウェブアプリケーションのURLをWebhook URLに登録します。
apps-gcp-75-15

以上で、基本的な設定は完了です。

1.3. 実際にLINEで試してみる

一度LINEアプリで試してみましょう。
スマホのLINEアプリから「友だち追加」>「QRコード」を選び、LINE Developersのページに表示されているQRコードを読み込み、友だちになります。

そして、「頑張れ」と入力してみます。
apps-gcp-75-16-2
上手く行きました!
まるで、映画のワンシーンのようですね。

何はともあれ、LINE Botのベースが出来ました。

2. Cloud Vision APIで英単語を抽出

それでは、いよいよCloud APIを使ってみます。
まずは、Cloud Vision APIです。

Cloud Vision APIは、送信した画像やCloud Storage上の画像を機械学習モデルによって解析し、画像の中に含まれる情報を検出するAPIです。
解析のために用意されている機能は以下の通りです。

機能のタイプ 説明
LABEL_DETECTION ラベル検出
TEXT_DETECTION 光学式文字認識(OCR)
DOCUMENT_TEXT_DETECTION 高密度テキスト画像に対してのOCR
SAFE_SEARCH_DETECTION 不適切コンテンツ検出
FACE_DETECTION 顔検出
LANDMARK_DETECTION ランドマーク検出
LOGO_DETECTION ロゴ検出
IMAGE_PROPERTIES 画像プロパティ検出
WEB_DETECTION ウェブ検出
CROP_HINTS クロップヒント検出
OBJECT_LOCALIZATION 複数オブジェクト検出

今回は、TEXT_DETECTIONを使って、
画像内に含まれる文字の検出を行います。

2.1. APIの有効化

Cloud Vision APIを利用するために、GCP側でAPIを有効化します。

GCPコンソールのメニューより、「APIとサービス」>「ライブラリ」を選択します。
apps-gcp-75-11

検索すると「Cloud Vision API」が出てくるのでクリックします。
apps-gcp-75-11

「有効にする」をクリックします。
apps-gcp-75-11

同様に「Cloud Text-to-Speech API」も有効にしておきましょう。

これで、GCP側の設定が完了しました。

2.2. GCPプロジェクトへの紐付け

次に、GASのスクリプトエディタへ戻り、メニューより「リソース」>「Cloud Platform プロジェクト」を選択し、先ほどAPIを有効にしたGCPプロジェクトへGASを紐付けます。
apps-gcp-75-11

2.3. Cloud APIを利用するための認可

GASからCloud APIを利用するために、今回はOauthによる認可を使います。
マニフェストファイルでScopeを設定し、ScriptApp.getOAuthToken()で取得したトークンをリクエストの際に送信するだけでAPIが利用可能となります。

まずは、Scopeの設定を行います。
「表示」>「マニフェストファイルを表示」を選択すると、appsscript.jsが表示されるので、下記のようにScopeを追加します。

  "oauthScopes": [
    "https://www.googleapis.com/auth/script.external_request",
    "https://www.googleapis.com/auth/documents",
    "https://www.googleapis.com/auth/spreadsheets",
    "https://www.googleapis.com/auth/drive",
    "https://www.googleapis.com/auth/cloud-vision",
    "https://www.googleapis.com/auth/cloud-platform"
  ]

 
あとは実際にCloudAPIを利用する際に、Authorization Bearerヘッダにトークンをセットするだけとなります。

2.4. Cloud Vision APIで、画像から文字を抽出する

それでは、Cloud Vision APIを利用して実際に画像から文字を抽出しましょう。
以下のようにしてLINEトーク画面に送られた画像データを取得し、それをCloud Vision APIへ送信し、画像に含まれる文字の情報を取得してみます。

■ LINEトーク画面に送信された画像データを取得

function getLineImageData(messageId) {

    var url = "https://api.line.me/v2/bot/message/" + messageId + "/content/";

    var headers = {
        "Content-Type": "application/json; charset=UTF-8",
        "Authorization": "Bearer " + getScriptProperty("CHANNEL_ACCESS_TOKEN"),
    };

    var options = {
        "method" : "get",
        "headers" : headers,
    };

    return UrlFetchApp.fetch(url, options).getContent();    
}

 

■ Vision APIで画像からテキストデータを抽出

function getOcrText(image) {

    var b64 = Utilities.base64Encode(image);
    var json = {
        "requests": [{
            "image": { content: b64},
            "features": {
                "type": 'TEXT_DETECTION',
                "maxResults": 1
            }
        }]
    }

    var payload = JSON.stringify(json);
    var url = "https://vision.googleapis.com/v1/images:annotate";
    var headers = {
        "Authorization": "Bearer " + ScriptApp.getOAuthToken(),
        "Content-Type": "application/json; charset=UTF-8",
    };

    var options = {
      "method" : "post",
      "headers" : headers,
      "payload" : payload,
    };

    return UrlFetchApp.fetch(url, options).getContentText();
}

 
それでは実際に画像を送信し、画像から抽出した文字列を英単語に区切り、表示をさせてみます。
apps-gcp-75-11
上手く行っているようですね。

気になるVision APIの料金ですが、下記の通りです。(※今回使用しているテキスト検出のみ掲載)

機能 最初の1,000ユニット/月 1,001〜5,000,000ユニット/月 5,000,000〜20,000,000ユニット/月
テキスト検出 無料 $1.5 $0.6

※1,000ユニットあたりの料金です。詳しくはこちらをご確認ください。

個人で使う分には、無料枠で十分収まりそうですね。

3. 英単語を翻訳し、スプレッドシートに保存

続いて、英単語の翻訳をして行きます。
翻訳と言えば、Cloud Translation APIですが、今回はお手軽で無料なGASのLanguageAppクラスを使用します。

3.1. GASのLanguageApp.translateで翻訳

使い方は非常に簡単で、翻訳対象のテキスト、翻訳前と翻訳後の言語コードを設定すると
翻訳後の言語コードに翻訳されたテキストが返ってきます。

LanguageApp.translate(テキスト, 翻訳前の言語コード, 翻訳後の言語コード);

 

3.2. スプレッドシートに保存する

こちらもGASなら簡単に保存出来るのですが、スプレッドシートへの読み書きは非常に遅いということを意識しておく必要があります。
まとめて読む・まとめて書くなどして、SpreadsheetAppクラスの呼び出しはなるべく少ない回数で済ませましょう。

var ss = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("words");
var lastRow = ss.getLastRow();
ss.getRange(lastRow + 1, 1, words.length, words[0].length).setValues(words);

 
それでは、スプレッドシートを見てみましょう。
apps-gcp-75-11

画像から抽出した英単語と、その和訳のセットが正しく保存出来ました。

4. Cloud Text-to-Speech APIで音声データを取得

続いて、Cloud Text-to-Speech APIで英単語の音声データを取得します。
Cloud Text-to-Speechは、極めて人間に近い合成音声でテキストを読み上げてくれる、音声変換サービスです。日本を含めた20以上の言語と方言に対応しています。

4.1. Cloud Text-to-Speech APIで英単語の音声データを取得する

英単語の音声データを取得するために、言語や音声エンコードを指定します。

■ Text-to-Speech APIで音声データを取得

function getTextToSpeechData(word) {

    var json = {
        "audioConfig": {
            "audioEncoding": "MP3",
            "pitch": "0.00",
            "speakingRate": "1.00"
        },
        "input": {
            "text": word
        },
        "voice": {
            "languageCode": "en-US",
            "name": "en-US-Wavenet-D"
        }
    }

    var payload = JSON.stringify(json);
    var url = "https://texttospeech.googleapis.com/v1beta1/text:synthesize";
    var headers = {
        "Content-Type": "application/json; charset=UTF-8",
        "Authorization": "Bearer " + ScriptApp.getOAuthToken(),
    };

    var options = {
        "method": "post",
        "headers": headers,
        "payload": payload,
    };

    return UrlFetchApp.fetch(url, options);    
}

 
主なパラメータは以下の通りです。
その他のパラメータについては、こちらをご確認ください。

パラメータ 説明
text 音声データに変換するテキスト。最大5,000文字まで。
languageCode 音声の言語。(言語コード一覧
name 音声の名前。
audioEncoding 音声のエンコード。
pitch 音声のピッチ。
speakingRate 音声のスピード。

4.2. 取得した音声データをドライブに保存する

同じ単語の音声データを何度も取得するのはもったいないので、取得した音声データについては、ドライブに保存することにします。

var blob = Utilities.newBlob(data, contentType, fileName);
DriveApp.getFolderById(folderId).createFile(blob);

 
なお、Cloud Text-to-Speech APIの利用料金は下記の通りです。

機能 1か月あたりの無料枠 有料使用
標準音声 0〜400 万文字 100 万文字あたり $4.00(USD)
WaveNet音声 0〜100 万文字 100 万文字あたり $16.00(USD)

WaveNetとは非常に高品質な音声で、料金も標準音声と比べて4倍ほど高くなっています。
今回はWaveNetを使用していますが、それでも十分に無料枠で収まりそうです。

以上で、今回のBotに必要な機能が全て揃いました。

5. 英単語Botの各機能を実装

さて、これまでの内容を踏まえて、英単語Botを完成させましょう。
今回は、テキストメッセージ以外にボタンテンプレートやクイックリプライ、音声再生にはLIFFを使用しています。
詳しくは、Messaging API リファレンスをご確認ください。

最終的に画面はこのような形になりました。
apps-gcp-75-11  apps-gcp-75-11

6. まとめ

いかがでしたでしょうか?
Cloud APIを使えば、専門的な知識がなくても色々な機能を簡単に自分のアプリケーションに組み込むことが出来ます。
また、GASを使ったことでWebサーバーやデータベースを用意することなく、Botの開発をスムーズに開始することが出来ました。
何かを試したいと思った時に、すぐに出来るのは嬉しいですね。

今から期末試験の結果が楽しみです!
それでは!

次の記事を読み込んでいます
次の記事を読み込んでいます
次の記事を読み込んでいます