フレクトのクラウドblog re:newal

http://blog.flect.co.jp/cloud/からさらに引っ越しています

Apexからレポートを実行しCSVで出力する

エンジニアの藤野です。

Salesforceの主要な機能の一つであるレポートは、Apexから実行することもできます。 公式のドキュメントとしては下記のものなどがあります。

この記事では一例として、レポートの実行結果をCSVとして出力するコードを実装し、解説します。

実行環境は下記のとおりです。

使用するレポート

レポートの題材として、Trailheadのレポートの形式設定の「マトリックスレポート」で作成している「Revenue Trend by Type」レポートを使用します。

これは行、列ともにレベル1のグルーピングを行うマトリックスレポートです。

ゴールと出力例

適当なレコードを作成し、「Revenue Trend by Type」実行しました。

レポート実行結果
レポート実行結果

これに対して、ゴールは下記のCSVを出力することです。 CSVファイル名はレポート名をそのまま使うものとします。 また、詳細は出力しません。

Revenue+Trend+by+Type.csv:

完了予定月,種別,-,Existing Customer - Upgrade,Existing Customer - Replacement,Existing Customer - Downgrade,New Customer,合計
2019/01/01,金額 合計:,0.0,0.0,0.0,0.0,1.0E7,1.0E7
2019/02/01,金額 合計:,0.0,0.0,0.0,2000000.0,0.0,2000000.0
2019/03/01,金額 合計:,880000.0,0.0,0.0,0.0,0.0,880000.0
2019/05/01,金額 合計:,0.0,0.0,5000000.0,0.0,0.0,5000000.0
2019/07/01,金額 合計:,0.0,0.0,0.0,0.0,0.0,0.0
2019/08/01,金額 合計:,0.0,0.0,0.0,100000.0,0.0,100000.0
2019/09/01,金額 合計:,0.0,2.0E7,0.0,0.0,0.0,2.0E7
合計,金額 合計:,880000.0,2.0E7,5000000.0,2100000.0,1.0E7,3.798E7

また、このCSVExcelでそのまま表示できるものとします。 Excelで表示した状態:

CSVをExcelで表示
CSVExcelで表示

続きを読む

Heroku NewRelic APMを始めよう!

こんにちは。エンジニアのオクダです。今回のテーマは「Heroku NewRelic APMを始めよう!」です。

必要最小限の工数でNewRelic APMを利用する方法についてご紹介いたします。

Heroku NewRelic APMとは

まず最初に、Heroku NewRelic APMとは、ということで、 この章では、Heroku NewRelic APMで何ができるか? ということについて説明します。

NewRelicで何ができる

サーバーの監視に必要なことは一通り揃っています。 死活管理も追加できます。その場合、New Relic Syntheticsを利用することになります。 ジョブの状態が分かるので、ジョブの謎の死にも気づける可能性もあります。 モニタリングに必要なスクリプトAPM agentが自動で注入してくれるので、設定は不要となっています。 インフラ監視用のNew Relic Infrastructureもありますが、別途料金が発生します。

Heroku NewRelic APMについて、ご説明

NewRelic APMはアプリケーションパフォーマンス監視ツールであります。HerokuにはNewRelic APMがAdd-onsとして用意されています。 SaaSビジネスにおいて、顧客ごとにactivity, 品質保証(SLA), 業績評価(KPI)を監視したいというニーズがあります。

NewRelic 活用事例

使用した模擬的弊社のサービス実装、について説明します。

実装の経緯

模擬的にシステムを作り、データを再生して流し込み、 NewRelicでどんなことができそうか探索してみました。

模擬的実装

使用した模擬的Architectureです。 f:id:flect170:20191118150949p:plain Google BigQueryからデータをamazon Kinesisに送信

amazon KinesisからAmazon DynamoDBに時間ごとに送信

Amazon DynamoDBで受信したサブスクリプションIDやデバイスIDを保存

ClientはAmazon EC2を使用して、Amazon DynamoDBに保存されたデータを取得します

New Relicでそれぞれの経路のトランザクションを確認します

やりたいこと

顧客ごとの「システム健全度」をレポートします。 例えば、Google BigQueryに蓄積されたデータから、顧客毎のDynamoDBへの書き込み処理にどれくらいの所要時間を擁しているか? 全ての書き込み処理が正常に行われ終了しているか?を確認します。

実現方法

Custom Parameterを活用します。 NewRelicではカスタム属性を設定する事により、遅かったり失敗したりしたリクエストに関連するカスタム属性(顧客ID、デバイスID)を追跡することができます。 例えば、顧客ID毎に応答時間を計測できれば、顧客に対する品質保証を証明できたり、デバイスID毎に応答時間を計測できれば、雇用者の業績評価の指標に利用したりできます。

NewRelic APMの使用方法

ここでは、NewRelic APMのインストール アプリケーションの編集について、説明します。

NewRelic APMのインストール

NewRelic APMのインストール画面です。 f:id:flect170:20191118131842p:plain Heroku Add-onsより、NewRelicを追加すると、Install the agentページに移動します。(今回のプロジェクトはPython) 指示通り、Get your license keyで、ライセンスキーを取得します。 Install the NewRelic agentでnewrelicをインストールします。 Generate the configuration using license keyで、先ほど取得したライセンスキーに置き換えて、newrelic.iniファイルを生成します。

NewRelicのデータって?

NewRelicの世界では「トランザクション」が、送信データの1行に相当します。(以下NRTと表記) Webリクエストなどは自動的に一つのNewRelicトランザクションと認識してくれます。 バックグラウンドジョブでは、明示的にNewRelicトランザクションがどの単位になるのか、定めなければなりません。 background taskを明示的にコードで宣言する必要がある。

Custom Parameterとは

NewRelicトランザクションに任意につけられます 設定方法は 既存のアプリケーションコードに、NewRelicのライブラリをインポートします。

import newrelic.agent

エージェントの初期化で、先ほど作成したnewrelic.iniを読み込みます。

import newrelic.agent

あとは、監視したいところにCustom Parameterのキーと値を追加していくのみです。

    application = newrelic.agent.application()
    with newrelic.agent.BackgroundTask(application, name='put_item', group='Task') as tr:
        tr.start_time = starttime
        tr.add_custom_parameter('Subscriptionid', json_dict['Subscriptionid'])
        tr.add_custom_parameter('Deviceid', json_dict['Deviceid'])
        tr.add_custom_parameter('latitude', json_dict['latitude'])
        tr.add_custom_parameter('longitude', json_dict['longitude'])
        tr.add_custom_parameter('Process', 'DynamoDB')
        tr.add_custom_parameter('Error', json_dict['Error'])

Custom Parameterについて、詳しくはこちら

docs.newrelic.com

監視できない嵌りどころ

Main関数自体を監視対象とすることができません。 Local関数が一つしかない場合は、その関数が監視対象となります。 関数が複数存在する場合、監視対象関数を明示的に修飾しなければなりません。 デコレーションbackground_taskのname引数に関数名、groupはオプション、指定しているとAPMトランザクション画面で同じgroupに所属している関数名別のレスポンス時間を表示したりできます。

NewRelic APMを使用して、実際に計測してみる

ここでは、NewRelic INSIGHTS画面でのパフォーマンス監視 代表的なNRQL構文、について紹介します。

NewRelic INSIGHTS画面でのパフォーマンス監視

f:id:flect170:20191118150842p:plain SELECT文で、パフォーマンス監視対象をINSIGHTS画面に表示しています。

代表的なNewRelicクエリ構文

SELECT文、FROM句、WHERE句、AS句、LIMIT句、SINCE句、他サポートされていますが、 監視ツールなので、UPDATE文等の更新処理が入るクエリ構文はありません。 2つのTABLEをJOINしたくなったりしますが、JOIN句は用意されていません。

代表的な集約関数

Count(*)、average関数、latest関数、uniques関数、他にも、最大、最小、ユニークカウントなどが用意されています。

顧客ごと集計クエリの例です。

SELECT average(duration) FROM Transaction WHERE Process = 'DynamoDB' FACET sid LIMIT 30 SINCE 30 minutes ago TIMESERIES 1 minute

上記のクエリでは、NewRelicトランザクションから顧客ごと、最大30件まで、過去30分間の1分毎のDynamoDBへの書き込み時間の平均値をクエリしています。 これにより、当該顧客で影響が出たのかどうかを集計することができました。

どうでしたか?参考になりましたでしょうか?

Heroku NewRelic APMについて、ご紹介させていただきました。

最後まで、読んでいただき有難うございました。

NewRelicの公式サイトもご覧ください。

newrelic.co.jp

自作アプリをHeroku Add-onsにプロビジョンする方法

こんにちは。エンジニアのオクダです。今回のテーマは「自作アプリをHeroku Add-onsにプロビジョンする方法」です。

皆さん、Heroku Add-onsはよく利用しているが、実際にHeroku Add-onsを作成するとなると、って言うことにならないでしょうか?

そこで、今回は「アドオン開発するぞ、の第一歩としてtestクラスのアドオンを作ってみる」をご紹介します。

その前にHeroku アドオンのリリースステージとは

Heroku アドオンには、リリースステージがあり、α版については、審査なしで無料で作成することができます。

devcenter.heroku.com

そのリリースステージ名がtestになります。

Heroku Add-ons自体は、Heroku以外のhttpsサーバーでも実装可能です。

Heroku CLIでHerokuにログイン

$ heroku login

既に別のアカウントにログインしていて、Herokuにログインできない場合は

$ heroku login --interactive

addons-adminプラグインをインストール

$ heroku plugins:install addons-admin

add-onsのルートディレクトリにマニフェストを作成

$ heroku addons:admin:manifest:generate

Input manifest information below: // と聞いてくるので

? Enter slugname/manifest id: <manifest id>    # 一度決めると変更できない
? Addon name (Name displayed to on addon dashboard): (MyAddon) <Addon name>    # Addon nameを入力
? Choose regions to support
  <space> - select
  <a> - toggle all
  <i> - invert all 
  ↑↓ use arrow keys to navigate
 (Press <space> to select, <a> to toggle all, <i> to invert selection)
❯◯ dublin
 ◯ eu
 ◯ frankfurt
 ◯ oregon
 ◯ sydney
 ◯ tokyo
 ◉ us    # 取り敢えず弊社ではUSを選択
 ◯ virginia
? Would you like to generate the password and sso_salt? (Y/n) <Y>    # Yを入力
? This prompt will create/replace addon-manifest.json. Is that okay with you? (Y/n)  <Y>    # Yを入力
Generating add-on manifest... done
The file addon-manifest.json has been saved!    # と表示されたら成功

Heroku Add-onsマニフェストの要件

生成されたHeroku Add-onsマニフェストは下記のようになっています。

{
  "id": <add-on-id>,    # 後ほどBasic認証のuserNameで使用する。
  "api": {
    "regions": [
      "us"
    ],
    "version": "3",
    "password": <add-on-password>,    # 英数32文字の任意の文字列で自動生成されます。後ほどBasic認証で使用するので、覚えておく。後ほどBasic認証のpasswordで使用する。
    "requires": [],
    "sso_salt": "********************************",    # 英数32文字の任意の文字列で自動生成されます。
    "production": {
      "sso_url": "https://<example>.herokuapp.com/sso/login",
      "base_url": "https://<example>.herokuapp.com/heroku/resources"
    },
    "config_vars": [
      <CONFIG_VARS_URL>
    ],
    "config_vars_prefix": <CONFIG_VARS_PREFIX>
  },
  "name": <add-on>,
  "$base": 156626069365144
}

Heroku Add-onsマニフェストにはproduction.sso_url / base_urlが存在し、実在するURLに編集しなければなりません。

sso_urlとbase_urlの要件は...

  • URLの命名規則

    • sso_urlの命名規則は、URLの最後が/sso/loginで終了する
    • base_urlの命名規則は、URLの最後が/heroku/resourcesで終了する
  • httpsサーバーであること

    • manifestにあるproduction.sso_url / base_urlに実在するURLを指定する
    • これらのURLをhttpsサーバーに実装

sso_url: https://example.com/a/b/sso/login

base_url: https://example.com/c/d/e/heroku/resources

"base_url"は、heroku addonsを作成する際に、有効なアドオンであるか確かめる為に、Basic認証を行うページです。

"sso_url"は、heroku addonsを起動する際に、一番最初に訪れるページです。

更新したaddon-manifest.jsonをHerokuにプッシュする

$ heroku addons:admin:manifest:push

アドオンをホストするWebサーバーを下記のコードのように追加する(Python Flaskの場合)

from flask import Flask, render_template, jsonify, redirect
    ︙
    ︙
def basic_auth():
    userName = <add-on-id>    # 先ほどマニフェストで登録していた、アドオンID
    password = <add-on-password>    # 先ほどマニフェストで登録した、パスワード

    app.config['BASIC_AUTH_USERNAME'] = userName
    app.config['BASIC_AUTH_PASSWORD'] = password
    app.config['BASIC_AUTH_FORCE'] = True

    basic_auth = BasicAuth(app)
    ︙
    ︙
@app.route('/heroku/resources', methods=["GET", "POST"])            # addonの認証はPOSTで行われるため、methodにPOSTを追加
def base():
    basic_auth()
    res = {
        "id": "01234567-89ab-cdef-0123-456789abcdef",            # 何でも良い
        "message": "",                            # 空で良い
        "config": { <CONFIG_VARS_URL>: "https://<example>.herokuapp.com/" }    # キーにaddon-manifest.jsonのconfig_varsを指定してあげる。値はアプリのURL
    }
    return jsonify(res)

@app.route('/sso/login', methods=["GET", "POST"])                # base関数同様、methodにPOSTを追加
def sso():
    basic_auth()
    return redirect('/')    # rootパスにリダイレクト

自作Add-onをHerokuアプリに追加する

$ heroku addons:create <add-on>:test

下記のようなログが出てきたら成功です。

Creating <add-on>:test on ⬢ <app-name>... free
Created <add-on>-<alpha-numeric> as <CONFIG_VARS_URL>    # <CONFIG_VARS_URL>は、addon-manifest.jsonのconfig_varsで宣言しているキー
Use heroku addons:docs <add-on> to view documentation

うまく作成されない時は、これをやってみると詳しいログが取得できます

$ DEBUG=* heroku addons:create <add-on>:test --app <app-name> --wait

どうでしたか?参考になりましたでしょうか?

Herokuを利用している場合、使用する機会があるHeroku Add-ons。そのHeroku Add-onsを、作成する方法をご紹介させていただきました。

最後まで、読んでいただき有難うございました。

Herokuアドオンの公式ドキュメントもご覧ください。

devcenter.heroku.com

devcenter.heroku.com

Object Detection and Augmented Reality

みなさんはじめまして。2019年6月にフレクトへ入社した技術開発室の馮 志聖(マイク)です。
I will introduce some use case about AR and Object Detection.

Augmented Reality(AR)

If you want to know about detail please check wiki.

en.wikipedia.org

Recently,JavaScript engines and browsers have become more powerful that building full-blown applications in JavaScript is not only feasible, but increasingly popular.

AR.js

This is AR base on JS.

github.com

And it can customize the target by this website.

jeromeetienne.github.io

This is demo video made by me.
It just take about 5 minutes.


AR.js example

It can use for name card.
Some idea like this flow chart.

f:id:fengchihsheng:20190918185422p:plain
AR name card system flow chart.

AR with Handtrack.js base on TensorFlow.js

TensorFlow is popular AI framework use for Object Detection and image classification.
And google release TensorFlow on JavaScript.

github.com

Handtrack.js prepare the AI model of hand.

github.com

And this AR frame work on JS.

github.com

This is demo made by me.
On this demo it can change the model what ever you want.


handtrack.js + aframe example

IOS ARkit with CoreML

IOS ARkit is powerful.
Check this url.

developer.apple.com

And I do some test base on this url.
It can use some official function import map marker or route.
Depend on how to customize it.

github.com

demo map marker.


IOS ARKit + CoreLocation

demo route.


IOS ARKit + CoreLocation

new augmented reality feature in Google Maps

On this year August Google release AR on Google map.

support.google.com

And if you want to use AR please update Google map application on your phone.
Demo.


new augmented reality feature in Google Maps

AR world-mapping

AR world-mapping can share data for multi user.

f:id:fengchihsheng:20190918192244p:plain
AR world-mapping

developer.apple.com

Use Case is Minecraft Earth.


Minecraft Earth - Official Reveal Trailer (AR Game)


AR体験がすごい! マインクラフト Earth先行レビュー。原寸大、位置ゲーモードに夢中

最後に

Anyway AR(Augmented Reality) with AI(Artificial Intelligence) will bring MR(Mixed Reality) to this world.
And it will build more convenient future.

Azure Bot Serviceを試してみる。

エンジニアのオクダです。こんにちは。今回のテーマは「Azure Bot Service」です。

前回は「Alexaスキルのデザイン・開発・公開」についてお話しさせて頂きました。 今回は「Azure Bot Service」についてお話しさせて頂きます。Azure Bot ServiceとはMicrosoftが提供しているサービスです。 上記のリンクでは「ボット開発向けに特化されたマネージド サービス」と記載されており、自然言語処理は、AzureのLanguage Understandingサービスである、LUISが行っています。 今回はこのLUISについて調査しました。

まず日本語対応ですが、対応言語を見てみると、日本語のサポートされている機能は以下の3つです。

✔︎ 事前構築済みのエンティティ

✔︎ フレーズ リストのレコメンデーション

✔︎ テキスト分析(キー フレーズのみ)

事前構築済みのエンティティとは、単語を変数として格納する器で、エンティティを組み合わせてインテントを作成していきます。

事前構築済みのエンティティの例:

  • job_type = [アプリ開発, フロントエンド, データサイエンス, 組み込み系, …]

  • search = [探します, 探しています, 調べます, 検索しています, どれですか, …]

事前構築済みのエンティティを使用したインテントの例:

  • {job_type}の仕事を{search}

事前構築済みのエンティティが利用可能であれば、プログラムでの自然言語処理が可能です。 例えば「アプリ開発の仕事を探しています。」と言う発話があった場合、

アプリ開発の仕事を探しています。」と言う文章は分解されて解釈され、

{job_type}="アプリ開発"、{search}="探しています"

が変数として取り出されます。

LUISはこのようなインテントを複数個まとめてLanguage Modelを構築しています。 このLanguage Modelがユーザの発話を解析し、どのインテントにヒットしたかをスコア(score)として返します。

エンティティ無しインテントの場合

まず、エンティティ無しで、LUISで英語と日本語のLanguage Modelを作成してデプロイしてみました。 作成した日本語のLanguage Modelに対して「自然言語処理の仕事を探しています」と言う文章を評価した結果は下記の通りです。

{
  "query": "自然言語処理の仕事を探しています",
  "topScoringIntent": {
    "intent": "求人情報を入手する",
    "score": 0.70248
  },
  "intents": [
    {
      "intent": "求人情報を入手する",
      "score": 0.70248
    },
    {
      "intent": "仕事に応募する",
      "score": 0.06822772
    },
    {
      "intent": "None",
      "score": 0.018654
    },
    {
      "intent": "メール送信",
      "score": 0.00519312872
    }
  ],
  "entities": []
}

同じような文章で英語では99%のスコアが出ているのに対し、日本語では70%のスコアしか出ていませんが、topScoringIntentとして狙い通りのインテントが抽出されています。

エンティティを使用してLanguage Modelを作成

それでは、次にエンティティを使用してLanguage Modelを作成しました。作成したModelの評価結果は、下記の通りです。

{
  "query": "メールを3時に送って",
  "topScoringIntent": {
    "intent": "仕事に応募する",
    "score": 0.08809486
  },
  "intents": [
    {
      "intent": "仕事に応募する",
      "score": 0.08809486
    },
    {
      "intent": "求人情報を入手する",
      "score": 0.06960007
    },
    {
      "intent": "None",
      "score": 0.0618566237
    },
    {
      "intent": "メール送信",
      "score": 0.06026428
    }
  ],
  "entities": [
    {
      "entity": "3時",
      "type": "builtin.datetime",
      "startIndex": 4,
      "endIndex": 5,
      "score": 0.9534087
    },
    {
      "entity": "メール",
      "type": "e-mail",
      "startIndex": 0,
      "endIndex": 2,
      "resolution": {
        "values": [
          "メール"
        ]
      }
    },
    {
      "entity": "送っ て",
      "type": "send",
      "startIndex": 7,
      "endIndex": 9,
      "resolution": {
        "values": [
          "送って"
        ]
      }
    }
  ]
}

エンティティは期待通りに取り出されているようです。しかし「メールを3時に送って」のインテントに対し、scoreは「仕事に募集する」が0.088、「メール送信」が0.060でした。当然「メール送信」が選ばれるべきですが、期待外れの結果となりました。これでは、実用的ではありませんね。

Stackoverflowを通じてMicrosoftに問い合わせてみる

上記の問題をStackoverflowに投稿したところ、Microsoftのサポートエンジニアから返事が返ってきました。問い合わせた内容については下記リンクをご覧下さい。

stackoverflow.com

要約すると「日本語はまだ完全にサポートされていません。...途中省略...2019年8月にメジャーアップデートされるでしょう。」と言うことでした。

感想

今回、AzureのLanguage Understandingサービスである、LUISについて調査を行ってみましたが、対応言語の中に日本語が含まれているにも関わらず、事前構築済みのエンティティを使用した場合、正しく自然言語処理されないことがわかりました。 「日本語対応」を謳うからには、もう少し機能強化してほしいものです。 今年8月のメジャーアップデートに期待したいと思います。

Mobilenet v2とInception v4の転移学習

みなさんこんにちは。技術開発室の岡田です。

前回の投稿では、エッジデバイス上での画像認識技術についてご紹介しました。

cloud.flect.co.jp


今回は、この中で紹介した画像認識技術の一つである、Image Classification(物体認識)に用いるモデル(分類機)の作成方法についてご紹介します。

画像認識の分野では転移学習と呼ばれる方法でモデルを作成することが多いです。転移学習は既存のモデルを利用してモデル作成を行うもので、今回も転移学習を用いてモデルを作成しますが、転移学習については多くの文献が存在しています。このため転移学習を用いたモデルの作成方法自体はリファレンスを示すことにし、その中でもあまり文献が発見できなかったMobilenet v2, Inception v4のモデルを転移学習で作成する方法を説明します。また、簡単にではありますが、Mobilenet v1/v2, Inception v3/v4の性能比較もしてみたいと思います。

なお、本記事ではTensorflow のモデル作成になります。

画像認識のニューラルネットワーク

現在、主に画像認識の精度の向上や処理速度の向上、軽量化(容量削減)を目的として、多くの画像認識用のニューラルネットワークが提案されています。
画像認識においては、畳み込みニューラルネットワーク(Convolutional Neural Networks, CNN)の画像認識精度が高いことが知られており、これを組み込んだ構成のニューラルネットワークが一般的に使われるようになっています。代表的なものは下表のようなものがあります。詳細は、良くまとまっている文献が多くあります*1ので、ここでの説明は割愛させていただきます。

ネットワーク ざっくり概要
VGG16/19 畳み込み層とプーリング層から構成されるシンプルなモデル。もともとは層の深さが性能にどのような影響を与えるのかを検証することが目的でシンプルな構成としていた。(たとえば16層のVGG16や19層のVGG19を作って検証するなど) しかし、現在でもそのシンプルさから多くの技術者がこれをベースのネットワークが好んで使われることがあるとのこと。
Inception Inception モジュールと呼ばれるネットワークを重ねてCNNを作っている。このモジュール内では入力を分岐して(層を横に広げて)異なるサイズのフィルターで畳込みを行い、最後にその結果を結合する構成になっている。
ResNet residualモジュールと呼ばれるスキップ構造を導入することで層の深いネットワークにおいて効率的な学習を可能にしている。
Mobilenet 空間方向とチャネル方向に独立に畳み込みを行うことで軽量化を図ったネットワーク。エッジデバイス用にはこのネットワークを用いるのが一般的。

今回は、この中でもInceptionとMobilenetの転移学習を行います。

転移学習

機械学習における学習とはネットワークの最適な重みを探索することです。より良い精度のモデルを作成するためには、多くの学習データ、時間、計算機リソースを用い、より良い重みを探していく必要があります。しかし、多くの場合はこれを行うための十分なデータが集められない、時間や計算機リソースに制約があるなどの問題が発生します。
転移学習は、すでに学習済みのモデルを用いることでより効率的にモデルを作成する方法です。似たような問題領域で学習したモデルは、似たような新しい問題領域にも適応できるであろうという仮説のもと、学習済みモデルの一部を変更して新しい問題を解くモデルを作成します。これにより一から学習を行うよりも少ない時間で精度の高いモデルを作ることができるようになります。「将棋の羽生さんはチェスもかなり強い!(参考)それと同じ!」というのと同じようなことです。・・・と、私は説明されたことがありますが、わかったような、わからないような・・・。とはいえ、転移学習自体は論文等でも多く言及されており、実績もあるものですので、そちらをご確認いただくといいかもしれません*2

ところで、転移学習はその方法で大きく二つにわけることができます(分類の仕方によって三つの場合もある)。
一つは、学習済みのモデルを特徴抽出器として用いて、最後の出力層を学習し直すもの。もう一つは、学習済みモデルの最後の出力層より手前のいくつかの層も学習し直すもの。後者はファインチューニングと呼ばれたりもします。これらの詳細は、使いどころや注意点など含めこのページにわかりやすく記載されていますので一読されると良いかと思います。今回は、前者の学習済みのモデルを特徴抽出器として用いる方で転移学習を行います。

転移学習実践

ここから、実際に転移学習を行っていきますが、はじめに示したとおり、ここはざっくりとした説明とリファレンスのみです。

おそらく、転移学習を行う一番簡単な方法は、Googleがcodelabで公開しているTensorFlow For Poetsを実践することだと思います。これにより、Mobilenet v1, Inception v3の転移学習を行うことができます。まずは騙されたと思って、やってみてください。おそらく1時間もかからずに新しいモデルを作成することができます。このチュートリアルでは、花を分類する例題になっていますが、ご自身で分類させたい画像データをお持ちでしたら、例題を真似て適当なフォルダ構成で画像ファイルを配置してください。そして、'python -m scripts.retrain'の'--image_dir'オプションに画像データのフォルダを指定してあげれば、あなただけの新しいモデルを作成することができます。

いかがでしょうか、できましたでしょうか?
もしこれが初めて作成したモデルでしたら、結構精度高く分類してくれるので驚くのではないかと思います。

では次に、このTensorFlow For Poetsを拡張して、Mobilenet v2, Inception V4の転移学習もできるようにしていきましょう。

tensorflow-for-poets2でMobilenet v2, Inception V4の転移学習

tensorflow-for-poets2は転移学習のためのチュートリアル用のスクリプトではありますが、大変高機能です。たとえば、学習データを増幅させるData Augmentationをサポートしていたり、転移学習を高速化するためのキャッシュ機構を持っていたりします。一方で、現時点で対応しているモデルはMobilenet v1とinception v3のみであり、長い間対応モデルの追加はされていません。このため、より新しい精度の高いモデルの転移学習には使うことができません。

そこで、ここではコードを少しハックして他のモデルも使えるようにしてみることにします。ただし、言うまでもないと思いますが、これはあくまでもチュートリアルスクリプトを実験的に改変しているものです。プロダクション用途に使用できるかは別途検証が必要になることにご注意ください。また、今回は2019/7/7時点で最新のtensorflow-for-poets2のソースを対象とします。

tensorflow-for-poets2をcloneすると、scriptsというフォルダが作成されます。今回はこの中の次のファイルを改変します。

  • retrain.py

retrain.pyの改変

retrain.pyは転移学習を行う処理が記載されたスクリプトです。
色々と難しい内容が含まれていますが、今回変更する部分はcreate_model_info関数です。
この中で if architecture == 'inception_v3'などの条件分岐が記載されているのが、各転移学習の転移元のモデルを定義する部分になっています。
このため、この条件分岐のところに使いたいモデルの情報を追記すれば良さそうです。
Mobilenet v1の定義部分

  elif architecture.startswith('mobilenet_'):
    parts = architecture.split('_')
    <略>
    input_mean = 127.5
    input_std = 127.5

の後ろに次に内容を追記します。

  elif architecture.startswith('inception_v4'):
    data_url = 'https://storage.googleapis.com/download.tensorflow.org/models/inception_v4_2016_09_09_frozen.pb.tar.gz'
    bottleneck_tensor_name = 'InceptionV4/Logits/Logits/BiasAdd:0'
    bottleneck_tensor_size = 1001
    input_width = 299
    input_height = 299
    input_depth = 3
    resized_input_tensor_name = 'input:0'
    model_file_name = 'inception_v4_2016_09_09_frozen.pb'
    input_mean = 128
    input_std = 128
  elif architecture.startswith('mobilenetv2'):
    data_url = 'https://storage.googleapis.com/mobilenet_v2/checkpoints/mobilenet_v2_1.4_224.tgz'
    bottleneck_tensor_name = 'MobilenetV2/Predictions/Reshape:0'
    bottleneck_tensor_size = 1001
    input_width = 224
    input_height = 224
    input_depth = 3
    resized_input_tensor_name = 'input:0'
    model_base_name = 'mobilenet_v2_1.4_224_frozen.pb'
    model_dir_name = './'
    model_file_name = os.path.join(model_dir_name, model_base_name)
    input_mean = 127.5
    input_std = 127.5  

転移学習の開始

スクリプトの変更が完了したら、早速転移学習を行ってみたいと思います。スクリプトの実行方法はオリジナルのtensorflow-for-poet2と同じです。必要なパラメータを書き換えてください。

Inception v4 の例は次のとおりです。

python3.6 -m scripts.retrain \
  --bottleneck_dir=bottlenecks_inc4 \
  --model_dir=models/inceptionv4 \
  --summaries_dir=output/training_summaries_inc4/ \
  --output_graph=output/retrained_graph_inc4.pb \
  --output_labels=output/retrained_labels_inc4.txt \
  --architecture=inception_v4 \
  --image_dir=DATASET/TRAIN \
  --how_many_training_steps=2000


Mobilenet v2の例は次のとおりです。

python3.6 -m scripts.retrain \
  --bottleneck_dir=bottlenecks_mnv2 \
  --model_dir=models/mobilenetv2 \
  --summaries_dir=output/training_summaries_mnv2/ \
  --output_graph=output/retrained_graph_mnv2.pb \
  --output_labels=output/retrained_labels_mnv2.txt \
  --architecture=mobilenetv2 \
  --image_dir=DATASET/TRAIN \
  --how_many_training_steps=2000

推論実行

転移学習が完了したら推論を実行してうまく学習できているかを確認します。こちらもスクリプトの実行方法はオリジナルのtensorflow-for-poet2と同じです。必要なパラメータを書き換えてください。

Inception v4 の例は次のとおりです。

python3.6 -m scripts.label_image \
  --graph=output/retrained_graph_inc4.pb \
  --labels=output/retrained_labels_inc4.txt \
  --image=DATASET/TEST/Abyssinian/Abyssinian_13.jpg \
  --input_layer=input \
  --output_layer=final_result \
  --input_width=299 \
  --input_height=299

Mobilenet v2の例は次のとおりです。

python3.6 -m scripts.label_image \
  --graph=output/retrained_graph_mnv2.pb \
  --labels=output/retrained_labels_mnv2.txt \
  --image=DATASET/TEST/Abyssinian/Abyssinian_13.jpg \
  --input_layer=input \
  --output_layer=final_result \
  --input_width=224 \
  --input_height=224

どうでしょうか、ご期待の推論がなされているのではないかと思います。

性能評価

さて、せっかく転移学習でMobilenet v2もInception v4のモデルも作れるようになりましたので、Mobilenet v1, Inception v3と性能比較してみます。
データセットはObject Detectionのデータセットとしてよく参照されるOxford petを使います。このデータセットには37カテゴリーのかわいいペットの画像が200枚ずつ格納されています。今回は、トレーニング用に180枚、テスト用に20枚という内訳で分割してトレーニングを行います。
f:id:Wok:20190707232607p:plain



また、各モデルの転移元のモデルは次のものを使っています。

モデル 転移元
Inception v3 http://download.tensorflow.org/models/image/imagenet/inception-2015-12-05.tgz
Inception v4 https://storage.googleapis.com/download.tensorflow.org/models/inception_v4_2016_09_09_frozen.pb.tar.gz
Mobilenet v1 http://download.tensorflow.org/models/mobilenet_v1_1.0_224_frozen.tgz
Mobilenet v2 https://storage.googleapis.com/mobilenet_v2/checkpoints/mobilenet_v2_1.4_224.tgz

レーニングは2000エポックで統一しています。本当なら学習曲線で確認しながらが良いですが、今回は簡易評価なので手抜きをしています。

次の表が各モデルの性能評価の結果です。

モデル accuracy precision recall f1
Inception v3 0.93 0.94 0.93 0.93
Inception v4 0.92 0.93 0.92 0.92
Mobilenet v1 0.90 0.91 0.90 0.90
Mobilenet v2 0.94 0.94 0.94 0.94

※各値はmacroの値です。

今回はMobilenet v2がすべての値で成績がよく最も良いモデルになったようです。ただし、上記のとおり、チューニング等甘いところがありますのでご参考程度と考えてください。プロジェクトで使う場合は、実際にいくつかのモデルを作成してみて、どれがユースケースに適しているかを確認して使用するようにしてください。

今回の結果のConfusion Matrixもつけておきます。字が細かくてすみません。

f:id:Wok:20190708031249p:plain
f:id:Wok:20190708031257p:plain

f:id:Wok:20190708031151p:plain
f:id:Wok:20190708031312p:plain

最後に

Image Classificationに用いるモデル(Mobilenet v1/v2, Inception v3/v4)の作成方法についてご紹介しました。作成したMobilenetのモデルは、コンバータを用いてtensorflow liteやtensorflowjsのモデルに変換してエッジデバイス上でも使うことができるようになりますので、興味があれば是非チャレンジしてみてください。(まだ計画していませんが、エッジデバイス上で動かすまでについても投稿するかもしれません。)
今回は、前回に比べていくらか技術的な内容を盛り込めたかと思います。次回もこんな感じで記載していこうと思います。

*1:たとえば、名著として名高い「ゼロから作る Deep Learning(斎藤 康毅著)」など

*2:https://arxiv.org/abs/1411.1792

video要素の再生中のシーンを印刷する

こんにちは、エンジニアの藤野です。

HTMLへの動画の埋め込みにvideo要素が使われるようになって久しいですが、これをブラウザの機能を用いて印刷するとどのようになるかご存知でしょうか?

とあるお客様から「動画の再生中のシーンを印刷したい」という要望を受け、調査したところ、各ブラウザによって結果が異なることが分かりました。

本記事では各ブラウザの動作と、「再生中のシーンを印刷する」方法を紹介します。

各ブラウザでの動作

下記環境で動作を確認しました。

macOS Mojave 上の

Windows 10 上の

  • Edge

確認に用いたコードは下記のとおりです。

<!DOCTYPE html>
<html>
<head>
</head>
<body>
<video controls id="v">
    <source src="v.mp4" type="video/mp4">
    Sorry, your browser doesn't support embedded videos.
</video>
</body>
</html>

v.mp4は、エッジデバイス上での画像認識で弊社岡田が投稿した「AndoroidでImage Classification」の動画ファイルです。

それでは各ブラウザでの動作を見ていきましょう。

Firefox

動画部分が真っ白になります。

ブラウザでの表示: Firefox ブラウザでの表示

印刷時の表示: Firefox 印刷時の表示

Chrome

動画部分が真っ白になります。

ブラウザでの表示: Chrome ブラウザでの表示

印刷時の表示: Chrome 印刷時の表示

ときどき表示できる場合もあったのですが、その条件は分かりませんでした。

Edge

実はEdgeでは再生中のシーンがそのまま印刷できます。 つまり、FirefoxChromeで同様の動作を実装すればよいことになります。

ブラウザでの表示: Edge ブラウザでの表示

印刷時の表示: Edge 印刷時の表示

実装の方針

  1. video要素をソースとしたcanvas要素を作成
  2. 画面と印刷時でvideo要素とcanvas要素の表示を切り替える

Canvasは要素の作成時にvideo要素をソースとすることができます。

https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/drawImage

これを用いることで、video要素の現在の再生中の画像をcanvasとして表示できます。 そして、画面と印刷時でvideo要素とcanvas要素の表示を切り替えることで、再生中のシーンを印刷します。

canvasへの表示

drawImageメソッドでcanvasを描画します。 また、beforeprint eventを使って、再生中のシーンが描画されるようにします。

window.addEventListener('beforeprint', drawCanvas);

function drawCanvas(){
    let video = document.getElementById("v");
    let canvas = document.getElementById("c");
    canvas.width = video.videoWidth;
    canvas.height = video.videoHeight;
    canvas.getContext("2d").drawImage(video, 0, 0, video.videoWidth, video.videoHeight);
}

表示の切り替え

メディアクエリを使って画面(screen)と印刷(print)の表示を切り替えます。 CSS@mediaルールを使うとシンプルに実装できます。

@media screen {
    video { display: block; }
    canvas { display: none; }
}
@media print {
    video { display: none; }
    canvas { display: block; }
}

試してみる

Firefox

ブラウザでの表示: Firefox ブラウザでの表示2

印刷時の表示: Firefox 印刷時の表示2

印刷した瞬間のシーンが表示できました。

Chrome

ブラウザでの表示: Chrome ブラウザでの表示2

印刷時の表示: Chrome 印刷時の表示2

印刷した瞬間のシーンが表示できました。

終わりに

無事、印刷時にvideo要素の表示を再現することが出来ました。 ブラウザでwebページを印刷したときの表示は、配置の調整はあるものの大体そのままという先入観があったので、今回は勉強になりました。

最終的に作成したコードは下記のとおりです。

<!DOCTYPE html>
<html>
<head>
    <style>
        @media screen {
            video { display: block; }
            canvas { display: none; }
        }
        @media print {
            video { display: none; }
            canvas { display: block; }
        }
    </style>
    <script type="text/javascript">
        window.addEventListener('beforeprint', drawCanvas);

        function drawCanvas(){
            let video = document.getElementById("v");
            let canvas = document.getElementById("c");
            canvas.width = video.videoWidth;
            canvas.height = video.videoHeight;
            canvas.getContext("2d").drawImage(video, 0, 0, video.videoWidth, video.videoHeight);
        }
    </script>
</head>
<body>
<video controls id="v">
    <source src="v.mp4" type="video/mp4">
</video>
<canvas id="c"></canvas>
</body>
</html>

本記事の作成にあたって、下記ページを参考にさせていただきました。

videoタグの動画をcanvasを使って表示する