Amazon TranscribeでCustom vocabulary登録時にエラー

Amazon Transcribeで文字起こしの精度を上げるために、固有名詞をCustom vocabularyに登録したいと思うことがあると思います。
しかし、そこで予期せぬエラーが発生したのでメモ書きを残しておきます。

登録に使ったファイルはテーブル形式の以下のデータです。

Phrase[tab]IPA[tab]SoundsLike[tab]DisplayAs
てすとかぶしきがいしゃ[tab][tab][tab]テスト株式会社

Create vocabularyから各種設定を行い作成しました。

f:id:masakiz_blog:20200506154455p:plain
Custom vocabulary作成画面

そうすると以下のエラーメッセージが表示されました。

The vocabulary that you’re trying to create contains invalid characters or incorrectly formatted terms. See the developer guide for more information.

色々と調べていたところ、理由は説明資料に書かれておりました。
https://docs.aws.amazon.com/ja_jp/transcribe/latest/dg/transcribe-dg.pdf

Amazon Transcribe コンソールを使用してカスタム語彙をアップロードする場合は、語彙テーブ
ルの代わりに語彙リストを使用する必要があります。コンソールを使用してカスタム語彙を語彙
テーブルで作成するには、ソースファイルが Amazon S3 バケットに存在する必要があります。

つまり、
テーブル形式のCustom vocabularyを登録する場合はS3を利用すること。
リスト形式の場合は「File upload」、「S3 location」のどちらでも登録できました。

RailsのサイトにMauticを導入する方法

f:id:masakiz_blog:20180611001026p:plain Railsで作られたアプリケーションにMauticを導入してマーケティングオートメーションの環境を構築する方法について説明する。
Mauticとは?
https://jp.mautic.org/

Google アナリティクスのようにWebサイトへのアクセスデータを収集し、ユーザの行動からメール等でコンタクトを取ることができるようになる。
SalesforceなどCRMとの連携はプラグインがあるため簡単に連携できるが、RailsなどカスタムサイトはAPI経由で連携する必要があるため、その方法を記載する。

Mauticサーバの立ち上げ

  • Dockerを利用して立ち上げ
    Dockerfile
version: '3'
services:
  ma:
    image: mautic/mautic
    volumes:
      - mahtml:/var/www/html
    ports:
      - "8081:80"
    links:
      - madb:mysql
    environment:
      - MYSQL_ROOT_PASSWORD=password
  madb:
    image: mysql:5.6
    volumes:
      - madata:/var/lib/mysql
    environment:
      - MAUTIC_DB_HOST=madb:3306
      - MAUTIC_DB_USER=root
      - MAUTIC_DB_PASSWORD=password
      - MAUTIC_DB_NAME: mauticdb
volumes:
  mahtml:
    external: true
  madata:
    external: true
  • Mauticサーバ起動
docker-compse up

Mauticの設定

  • APIの有効化 Mauticにログイン後、設定画面から「API Settings」メニューを選択し、「API enabled?」をONに変更する。 f:id:masakiz_blog:20180607013626p:plain
  • API Credentialsを新規作成
    Redirect URIはhttp://localhost:8080/mautic/connections/:ID/oauth2を指定する。
    ID:Rails側のmautic_connectionsテーブルに追加したレコードのIDであり、ひとまず"1"を設定する。 f:id:masakiz_blog:20180607013228p:plain
  • カスタムフィールド追加
    RailsのUser IDを設定するためのカスタムフィールドを追加する。
    Label=user_id, Object=Contact f:id:masakiz_blog:20180608011518p:plain

Railsの設定

  • Gemfileに以下の行を追加
gem 'mautic'
  • Mauticをインストール
bundle install
  • config/routes.rbに以下の行を追加
mount Mautic::Engine => "/mautic"
  • config/initializers/mautic.rbを作成
Mautic.configure do |config|
  # This is for oauth handshake token url. I need to know where your app listen
  config.base_url = "http://localhost:8080"
  # *optional* This is your default mautic URL - used in form helper 
  config.mautic_url = "http://localhost:8081"
end
  • DBにMautic接続用のテーブル追加
rake db:migrate
  • データを追加(作成した際に自動採番されるIDがMauticの管理画面で必要となる)
Mautic::Connection.create(type: 'Mautic::Connections::Oauth2', url: 'http://localhost:8081', client_id: '1_637m4pv6tqg4kwcw88oo8ckggg4s8wcc8ww8w000gso8oo4og8', secret: '4iq54rrzot2c0sg4kk4s0g0k0k0oo0gw44owckokg8w8ks44c0')

localhost:8081はMauticサーバだが、Docker環境で動かしているとコンテナ内にアクセスしてしまうため、Mauticサーバにアクセスできない。
linkを利用するように設定し、url:'http://ma'のような感じでデータを登録する必要がある。

RailsとMautic間でユーザ情報連携

Railsに登録されたユーザをMauticに連携する。
* MauticのタグをRailsに埋め込む Mauticの設定画面からタグを出力し、app/views/layouts/application.html.erbに貼り付ける f:id:masakiz_blog:20180608013941p:plain ※GTMでもよい

  • 埋め込んだタグを一部修正 ユーザがログイン状態のときは、Railsのユーザ情報をMauticに送信する
    あとでRailsからユーザ情報を連携する際の検索条件に利用する
mt('send', 'pageview', {'user_id': '<user.id>', 'email': '<user.email>' })
  1. バッチ等でRailsからMauticへユーザ情報を連携 user_idまたはemailを検索キーとしてMauticから情報を取得し、Railsのデータで名前等の個人情報を補完し、Mauticに保存する
# Mauticに接続する
conn = Mautic::Connection.last

# 実際はallではなく条件を絞り込んだほうがよい
User.all.each do |user|
  # ucdまたはemailが一致しているデータをMauticへ連携する
  contact = conn.contacts.where(search: "user_id: #{user.id}").first
  contact = conn.contacts.where(search: "email: #{user.email}").first if contact.blank?

  # ユーザ情報を格納する
  contact.firstname = user.first_name
  contact.lastname = user.last_name

  # ユーザ情報をMauticに保存する
  if contact.save then
    Rails.logger.info('success')
  else
    Rails.logger.error("error: #{contact.errors}")
  end
end

エラー対応

  • データを取得しようとすると404エラーが発生する

    Mautic::RecordNotFound: 404 エラーに遭遇したようです (エラー #404)。もし同じことが起こったらシステム管理者に通報してください。

Mauticサーバのログは200で正常に返っている場合、キャッシュに問題があるため削除する。

rm -rf app/cache/*

あとキャッシュディレクトをWeb rootディレクトリ以外に変更する。 f:id:masakiz_blog:20180610235830p:plain

  • AWS ELBを利用しSSL経由でアクセスすると一部のURLでhttpが使われる
    ELBにSSLを設定してアクセスした場合、外部からは443ポートでアクセスできるが、EC2側は80ポートでアクセスされる。
    そのため、mtc.jsのURLが"http://www.hogehoge.com/mtc.js"になってしまいブラウザでエラーが発生する。
    回避方法としてMauticのコードを一部修正する。
    修正ファイル:/var/www/html/index.php
    以下の行を追加する
if (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https') {
    $_SERVER['HTTPS'] = 'on';
}

今すぐ安価で始められる機械学習環境の構築

  • 試しに機械学習のプログラムを動かしてみたい
  • 機械学習をするためにGPUがあれば高速に学習できるが、値段が高いから躊躇してる

という方におすすめ
超高速なGPUで機械学習のプログラミングができる環境を、月々3,828円で構築する手順

利用するインスタンス

AWS EC2 p3.2xlarge

搭載しているGPUはNVIDIA Tesla V100 販売価格126万 ※http://www.gdep.co.jp/products/list/v/59ce00a4ee5f6

月々の料金

3,828円

月々20日、1日2時間利用を想定
スポットインスタンスの金額 $0.87(2017/12/3時点)
20日 x 2時間 x $0.87 x 110円 = 3,828円/月

構築手順

EC2インスタンス立ち上げ

通常のインスタンスを作成する

f:id:masakiz_blog:20171207000854j:plain 「NVIDIA Volta Deep Learning AMI」を選択する
このイメージはnvidia-dockerがデフォルトで入っているため、環境構築が簡単

f:id:masakiz_blog:20171207000855j:plain 「NVIDIA Volta Deep Learning AMI」を選択している場合、p3系しか選択できなくなる

f:id:masakiz_blog:20171207000856j:plain 特に必要なければデフォルトで問題ない

f:id:masakiz_blog:20171207003511j:plain 種類や容量は好きなように設定する

f:id:masakiz_blog:20171207002753j:plain 特に指定は無いが、わかりやすいようにAIとしている

f:id:masakiz_blog:20171207002357j:plain Jupyterは8888ポート、TensorBoardは6006ポートを利用する

f:id:masakiz_blog:20171207002358j:plain

機械学習環境構築

SSHでEC2にログイン

ユーザ名:ubuntu
SSHキー:xxx.pem(EC2の構築で設定したキーペア)

日本時間に変更

$ sudo ln -sf /usr/share/zoneinfo/Asia/Tokyo /etc/localtime

アップデート

$ sudo apt update -y
$ sudo apt upgrade -y

機械学習の設定が済んでいるDockerイメージを作成
1.5時間近く作成に時間がかかる
※DockerfileでOpenCV系を除外すれば早くなる

$ git clone https://github.com/masakiz/tensorflow.git
$ cd tensorflow
$ nvidia-docker build --no-cache --rm -t masakiz/tensorflow:latest .

Dockerイメージを起動

$ cd /home/ubuntu/tensorflow
$ mkdir -p notebooks/logs
$ nvidia-docker run --name tensorflow -p 6006:6006 -p 8888:8888 -v $PWD/notebooks:/notebooks -e LOGDIR=/notebooks/logs -e LANG=ja_JP.UTF-8 --device=/dev/fuse:/dev/fuse --privileged masakiz/tensorflow:latest

http://IPアドレス:8888/

でJupyterにアクセスできれば完了
機械学習の開発環境が整う

【オプション】S3フォルダをマウントする

goofysをインストール

$ sudo add-apt-repository -y ppa:longsleep/golang-backports
$ sudo apt-get update
$ sudo apt-get install -y golang-go
$ export GOPATH=/home/ubuntu/work
$ go get github.com/kahing/goofys
$ go install github.com/kahing/goofys

AWSの設定

$ sudo apt-get install -y python python-pip
$ sudo pip install awscli
$ aws configure
AWS Access Key ID [None]: AKIAIOSFODNN7EXAMPXX
AWS Secret Access Key [None]: wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKXX
Default region name [None]: ap-northeast-1
Default output format [None]: json

S3フォルダマウント

$ mkdir /home/ubuntu/tensorflow/notebooks/workspace
$ chown -R ubuntu:ubuntu /home/ubuntu/tensorflow/notebooks
$ sudo /home/ubuntu/work/bin/goofys -o allow_other --uid 1000 --gid 1000 --region ap-northeast-1 <S3のバケット名> /home/ubuntu/tensorflow/notebooks/workspace

AMIイメージを作成

f:id:masakiz_blog:20171207010401j:plain 作成したAIインスタンスを選択し、「アクション」→「イメージ」→「イメージの作成」を選択する
作成する前にインスタンスを停止しておいたほうがよい

f:id:masakiz_blog:20171207010405j:plain イメージ名やイメージの説明を入力して作成
これも1時間以上時間がかかる

イメージの作成が完了したら、EC2インスタンスは削除してよい
※そのままにしておくと、通常のレートでどんどん課金されていくので注意

スポットインスタンスの設定

f:id:masakiz_blog:20171207012055p:plain

① 作成したAMIイメージを選択
② インスタンスタイプは機械学習環境構築で使ったp3.2xlargeを選択
③ EBSボリューの削除にチェック
④ セキュリティグループは機械学習環境構築で設定したセキュリティグループを選択
⑤キーペアは機械学習環境構築で設定したキーペア名
⑥インスタンス起動時にコマンドを実行させたい場合は、ここにシェルを記載(#!/bin/shから始める)
 起動時にjupyterも同時に起動しておくことも可能
⑦リクエスト有効期間の終了は24時間に設定するなどしてスポットインスタンス停止忘れ防止

KotolinとDeeplearning4jで日本語解析入門

概要

KotolinとDeeplearning4jで日本語を解析し、カテゴライズ化する。

やりたいこと

文章で書かれたデータはそのままでは分析に利用しづらい。
その為、文章から様々な属性を抽出してカテゴライズする。

処理結果

データ総数: 5,506件
トレーニング:80%
テスト:20%

20未満、20代、30代、40代、50代、60以上で分類

精度31%

プログラム

形態素解析

構文解析にkuromojiを利用する。

    val tokenizer = Tokenizer()
    val wordSequenceMap = hashMapOf<String, Int>()
    var sequence = 0
    val wordX = csvX.map { x ->
        tokenizer.tokenize(x).map { info ->
            val word = info.getSurface()
            if (!wordSequenceMap.contains(word))
                wordSequenceMap.put(word, sequence++)
            word
        }
    }
    val sequencesX = wordX.map { x -> x.map { word -> wordSequenceMap.getOrDefault(word, 0).toFloat() } }
    val fillZeroX = sequencesX.map { x -> x + Collections.nCopies(maxWordNum - x.size, 0.0f) }

LSTM

簡略化のため、LSTM一層で構成されたモデルを作成

    val conf = NeuralNetConfiguration.Builder()
            .seed(seed)
            .iterations(iterations)
            .optimizationAlgo(OptimizationAlgorithm.STOCHASTIC_GRADIENT_DESCENT)
            .list()
            .layer(0, GravesLSTM.Builder()
                    .nIn(maxWordNum)
                    .nOut(lstmLayerSize)
                    .activation(Activation.TANH)
                    .build())
            .layer(1, RnnOutputLayer.Builder(LossFunctions.LossFunction.MCXENT)
                    .nIn(lstmLayerSize)
                    .nOut(outputNum)
                    .weightInit(WeightInit.XAVIER)
                    .activation(Activation.SOFTMAX)
                    .updater(Updater.RMSPROP)
                    .build())
            .backprop(true).pretrain(false).build()

    val model: MultiLayerNetwork = MultiLayerNetwork(conf)
    model.init()
    model.setListeners(Arrays.asList(ScoreIterationListener(listenerFreq) as IterationListener))

ソースコード

github.com

資料

どこでもKotlinで発表した際の資料

speakerdeck.com