去る10月、11月とWeb App for ContainersAzure Database for MySQLをつかったLife and Shiftシナリオのコンテンツを2つばかり作ったのでブログにのせておきます。それぞれRailsとPHPの簡単なLAMP構成アプリを題材にWeb App for ContainersAzure Database for MySQLを使ってAzure上で完全マネージドな構成にしましょうというお話です。データストアにMySQLやPostgreSQLをお使いで、シングルコンテナにおさまるアプリであれば、細かい違いはあれどだいたい同じような手順でここで紹介している完全マネージド構成に移行することができると思います。他には、よくある構成としてデータストアにMongoDBのようなNoSQLをお使いのものもありますが、そいつもCosmos DB (Mongo API版) + Web App for Containers構成に載せ替えることも可能かと思います。

これらサービスは、パッチあてなどUpdateとランタイム更新のような面倒な作業は当然ながら、標準で冗長化対策、高負荷対策、セキュリティ対策などをやってくれていて、さらに効率的な監視・アラート、CI/CDを可能にする機能を提供しております。使える状況下ならば絶対に使わないと損なわけです。是非ともこれらサービスを活用してオフロードできるところはオフロードして、その分のリソースを皆さんにしかできない新規開発やビジネスベーションにフォーカスいただくことできっと今までよりも幸せになれるのでははなかいと思います。

Rails + MySQLアプリのLife and Shiftシナリオ


Web App for Containers + MySQLでコンテナ対応したRailsアプリを作ろう! from Yoichi Kawasaki

PHP + MySQLアプリのLife and Shiftシナリオ


Web App for Containers + MySQLでコンテナ対応したPHPアプリを作ろう! from Yoichi Kawasaki

Source Code

上記2シナリオに加えてPython/Django版も加えたアプリソースとCI設定ファイルのレポジトリはこちら

これは9/29 Azure Web Seminar 「Azure サービスを活用して作るフルマネージドな全文検索アプリケーション」のフォローアップ記事です。なかなか暇ができず少々時間が経過してしまいました。


Azure サービスを活用して作るフルマネージドな全文検索アプリケーション from Yoichi Kawasaki

Sample Application & Source Code

セミナーで紹介したサンプルアプリはAzure公式サイトに載せてある代表的なサービスのFAQデータを元にしたHTML/CSS/JavascriptによるQ&Aナレジッジベース検索のシングルページアプリケーションです。検索エンジンにAzure Searchを使い、データソースにCosmos DBを使いAzure SearchのCosmosDB Indexerでクローリングする構成にしてます。ソースコードと設定手順は以下Githubプロジェクトにアップしてあります。もしバグや設定手順等でご質問があればGithubでIssue登録いただければ時間を見つけて対応させていただきます。


Source Code: https://github.com/yokawasa/azure-search-qna-demo/

Demo: AI Digital Media Search

セミナー中に紹介した非構造化データの全文検索デモとして紹介したAI Digital Media Searchアプリケーション。メディア x 音声認識 x 機械翻訳 x 全文検索全てを絡めた面白いアプリケーションなのでこちらでデモ動画とソースコードを共有します。またこのアプリはAzure PaaSサービスを組み合わせてプレゼンテーションレイヤー(Web App for Container)のみならずデータ生成部分(AMS, Functions, Logic App)も全てサーバレスで実現しているのでこのエリアのサンプルアプリとしてもとても良いものになっていると思います。

Source Code: https://github.com/shigeyf/ai-digitalmedia

AzureSearch.js – Azure Search UIライブラリ

AzureSearch.jsはAzure SearchのUIライブラリで、Azure Searchプロダクトチーム主要開発者により開始されたOSSライブラリです。TypeScriptで書かれているのでとても読みやすく、また、ライブラリが提供するオブジェクト操作により非常に短いコードでサーチボックス、結果出力、ページネーション、ファセット、サジェスションなどで構成されるサーチ用UIを簡単に組み立てることが可能です。なかなかいけているライブラリにもかかわらず、あまり世の中に知られていないのはもったいないと思いセミナーの最後で紹介させていただきました。これ使わない手はないです。手っ取り早くは、下記のAzureSearch.jsアプリテンプレートジェネレータページで皆さんのAzure SearchアカウントのQueryKeyとインデックススキーマ(JSONフォーマット)を入力するとAzureSearch.jsアプリの雛形が生成されますので、そこから始めるのがよいかと思います。

END

de:code 2017で私が担当したAzure Searchセッションのスライドと動画が公開されたので、セッション中でもでお見せしたマテリアルと合わせてここでフォローアップさせていただきます。



セッションスライドはこちら。セッション持ち時間が50分と短い中での説明なのでいろいろとカットしたのですが、Appendixという形でカットしたスライドや補足情報を載せてあります。P47〜65です。


[DI08] その情報うまく取り出せていますか? ~ 意外と簡単、Azure Search で短時間で検索精度と利便性を向上させるための方法 from de:code 2017

その他、セッション中のデモで使用したツールやBotのソースコードはGithubにアップしております。

最後に、今回のセッションは検索サイド(クエリーを投げてインデックスにヒットして、ランキング処理されて結果が返却されるまで)に絞ってますが、他にもインデックスデータを生成するまでの話、モニタリングやオペレーション的な話、もう少しデープに言語処理的な話など取り上げたいトピックがまだまだあるので時間を見つけて私なりにブログやスライドに整理していきたいと思うております。

Azure Searchのアナライザーによるテキスト解析結果を出力する(だけの)ツールを作ってみたのでここで紹介します。その名もazure-search-ta(ta=Test Analyzer)。中身はAzure SearchのAnalyzer APIの出力結果を整形して表示させていているだけの単純なものでありますが、Azure Searchの全文検索チューニングやキーワードにヒットしない原因調査をする際には役に立つと思ってます。「どうしてこのキーワードがひっかからないの?」を突き詰めるには最終的にアナライザのテキスト解析結果と突き合わせる必要があるのと、アナライザーを選択する際にテキスト解析が視覚化されていると判断しやすいだろうと。ツールは2種類で (1)Web UIツールと(2)コマンドラインツール

Web UI Tool


azure-search-ta-capture
https://github.com/yokawasa/azure-search-ta

インストールは超簡単。(1)Githubからazure-search-taをclone (2)azure-search-ta/ui 配下のファイルをPHPが動くWebサーバにコピー (3)analyze-api.phpをエディタで開いてお使いのAzure Searchカウント名とAzure Search API Adminキーの値を設定ください。あとはazure-search-ta-ui.htmlにアクセスいただければ上記のようなUIが出力されるはずです。余談ですが、どうしてHTML/JSだけではなく間にPHPを挟んでいるのかについて、Azure SearchのAnalyze APIや管理系APIリクエストに位置付けられており、管理系APIはvia CORSでのリクエストを受け付けていないからなのです。

$ git clone https://github.com/yokawasa/azure-search-ta.git

$ vi azure-search-ta/ui/analyze-api.php

$azureSearchAccount="<Azure Search Service name>";
$azureSearchApiKey = "<Azure Search API Admin Key>";

Command-Line Tool

1. インストールと設定

pipでazure-search-taパッケージをインストール。既に古いバージョンをインストール済みでアップデートする際は――upgradeをつけて実行ください。

$ pip install --user azure-search-ta

次に、search.confにお使いのAzure Searchカウント名とAzure Search API Adminキーの値を設定ください。

# Azure Search Service Name ( never put space before and after = )
SEARCH_SERVICE_NAME=<Azure Search Service name>
# Azure Search API Admin Key ( never put space before and after = )
SEARCH_API_KEY=<Azure Search API Admin Key>

2. 使い方

いくつか使い方を紹介します。

usage: azure-search-ta [-h] [-v] [-c CONF] [-i INDEX] [-a ANALYZER]
                          [-t TEXT] [-o OUTPUT]

This program do text analysis and generate formatted output by using Azure
Search Analyze API

optional arguments:
  -h, --help            show this help message and exit
  -v, --version         show program's version number and exit
  -c CONF, --conf CONF  Azure Search Configuration file. Default:search.conf
  -i INDEX, --index INDEX
                        Azure Search index name
  -a ANALYZER, --analyzer ANALYZER
                        Azure Search analyzer name
  -t TEXT, --text TEXT  A file path or HTTP(s) URL from which the command line
                        reads the text to analyze
  -o OUTPUT, --output OUTPUT
                        Output format ("simple" or "normal"). Default:normal

ja.microsoftアナライザーによるテキスト解析結果をnormalモードで出力する例。Analyzer APIはパラメータにインデックス名が必要なので、なんでもいいのでご自分のアカウントに設定されているインデックス名を指定ください。ここではインデックスtaでsimple.txtに対象のテキストを記入して実行しています。

$ cat sample1.txt
吾輩は猫である

$ azure-search-ta -c ./search.conf -i ta -a ja.microsoft --t sample1.txt
INPUT: 吾輩は猫である
TOKENS: [吾輩] [猫] [ある]

Azure Searchにビルトインされているアナライザーでけでなく皆さんが作成したカスタムアナライザーによるテキスト解析結果も当然出力可能です。以下は、インデックスtacustomにNグラム分割のカスタムアナライザーmy_ngramを作成したとしてmy_ngramアナライザーによるテキスト解析結果を出力する例です。カスタムアナライザーの定義はGithubページのほうに詳しく書いているのでよかったらどうぞ。

$ cat sample1.txt
吾輩は猫である

$ azure-search-ta -c ./search.conf -i tacustom -a my_ngram --t sample1.txt -o simple
'吾輩' '吾輩は' '吾輩は猫で' '吾輩は猫' '輩は猫であ' '輩は' '輩は猫' '輩は猫で' 'は猫であ' 'は猫で' 'は猫' 'は猫である' '猫であ' '猫で' '猫で ある' 'である' 'であ' 'ある'

他には、azure-search-taはインターネット上のページのテキスト解析機能も付いているので、そいつを試してみます。ja.luceneアナライザを使ってhttp://www.yahoo.co.jpトップページの内容を解析します。

$ azure-search-ta -c ./search.conf -i ta -a ja.lucene --t http://www.yahoo.co.jp -o simple
'html' 'public' 'w' '3' 'c' 'dtd' 'html' '4' '01' 'transitional' 'en' 'http' 'www' 'w' '3' 'org' 'tr' 'html' '4' 'loose' 'dtd' 'yahoo' 'japan' 'ヘルプ' 'yahoo' 'japan' 'トップページ' '機能' '正しく' 'ご' '利用' 'いただく' '下記' '環境' '必要' 'windows' 'internet' 'explorer' '9' '0' '以上' 'chrome' '最新' '版' 'firefox' '最新' '版' 'microsoft' 'edge' 'macintosh' 'safari' '5' '0' '以上' 'internet' 'explorer' '9' '0' '以上' 'ご' '利用' '場合' 'internet' 'explorer' '互換' '表示' '参考' '互換' '表示' '無効' '化' '試し' 'くださる' '東北' '自転車' 'イベント' '参加' '方法' '事前' 'チェック' 'こだわり' 'ご' '当地' 'スイーツ' '取り寄せる' '上海' 'マージャン' '定番' 'ゲーム' '無料' '遊ぶ' 'ニュース' '11' '時' '1' '分' '更新' '韓国' '北' '太陽' '政策' '回帰' '娘' '放置' '熱中' '症' '死なす' '逮捕' '保育' '死亡' '事故' '睡眠' '中' '注意' '三菱' 'ufj' '法人' '融資' '銀行' '集約' 'フレーバ' '水' '人気' '続く' '各国' '大' '規模' 'サイバ' '攻撃' '西武' '菊池' '沢村' '超' '驚異' '被' '打率' '寺島' 'しのぶ' '長男' '超' '英才' '教育' 'もっと' '見る' '記事' '一覧' 'ゴミ' '収集' '車' '子育て' '5' '月' '13' '日' '7' '時' '55' '分' '配信' '産経新聞' 'ショッピング' 'ヤフオク' '旅行' 'ホテル' '予約' 'ニュース' '天気' 'スポーツナビ' 'ファイナンス' 'テレビ' 'gyao' 'y' 'モバゲ' '地域' '地図' '路線' '食べる' 'ログ' '求人' 'アルバイト' '不動産' '自動車' '掲示板' 'ブログ' 'ビューティ' '出会い' '電子' '書籍' '映画' 'ゲーム' '占い' 'サービス' '一覧' 'ログイン' 'id' 'もっと' '便利' '新規' '取得' 'メール' 'メールアドレス' '取得' 'カレンダ' 'カレンダ' '活用' 'ポイント' '確認' 'ログイン' '履歴' '確認' '会社' '概要' '投資' '家' '情報' '社会' '的' '責任' '企業' '行動' '憲章' '広告' '掲載' '採用' '情報' '利用' '規約' '免責' '事項' 'メディア' 'ステートメント' 'セキュリティ' '考え方' 'プライバシ' 'ポリシ' 'copyright' 'c' '2017' 'yahoo' 'japan' 'corporation' 'all' 'rights' 'reserved'

もう少しだけGithubのほうには詳しく書いてあるのでそちらも参考にしてください。

最後に

de:code 2017というマイクロソフトの開発者向けイベントでAzure Searchのセッション(DI08)を担当します。「その情報うまく取り出せていますか? 〜 意外と簡単、Azure Search で短時間で検索精度と利便性を向上させるための方法」と少々長いセッションタイトルでありますが、つまるところAzure Searchでの全文検索はどのような流れで結果をとってきて精度が向上させるにはどういった方法があるのか?について50分と短い時間ではあるもののできるだけ体系的にお話しようと思ってます。de:code参加される方よろしかったら是非。



decode2017-azure-search


https://www.microsoft.com/ja-jp/events/decode/2017/sessions.aspx#DI08

Python Easter Egg = Pythonの隠しクレジットとはいってもPython基礎本などでよく紹介されているものなので既にご存知かもしれないが背景が面白いのでここで紹介。

Pythonにはthisモジュールという「The Zen of Python」(Note 1)を出力するだけのモジュールがある。このモジュール、中身(Note 2)を見てみると分かるが、総ステップにしてわずか28行、ROT13暗号化(Note 3)された文字列を復号化するだけの単純で取るに足らないものかもしれないがこのモジュールが作られた背景は面白い。Barry Warsaw氏が記事「import this and The Zen of Python」でthisモジュールが誕生にまつわる面白い話を紹介している。

import this and The Zen of Python」の一部簡訳

2001年秋、Foretec Seminar社はのInternational Python Conference #10(以下IPC10、Pyconの前身となるカンファレンス)の準備をしておりPythonコミュニティからそのカンファレンスのスローガンを求めていた。スローガンはTシャツにもプリントされる予定だった。Guideや、Fred、Jeremyや著者達はかつてはForetec Seminar社に所属していたがPythonlabsを結成する2000年に同社を去っている。そしてPythonlabsはPythonコミュニティからのスローガン応募の審査と勝者の選定を担当することになった。応募は500くらいあったが、どれもひどいものだった。Timと著者は1つに絞られるまで何度となく選別作業を行い
最終的に”import this”を選んだ。理由は”import this”という言葉の持つふざけた、小バカにしたようなトーンが好きだったからという。

著者たちはこの”import this”をスローガンに選んですぐにthisモジュール(this.py)を実装した。モジュールは「The Zen of Python」を出力するだけのものだったが途中TimやGuidoの提案でrot13で暗号化して内容を少し難読化する工夫がされたりもした。IPC10が終わってすぐ、彼らはこのイベントを記念してthisモジュールをPython2.2.1ブランチにコミットした。この時、著者の提案で他の誰にも知られないようにするためにソース管理システムのチェックイン通知機能を停止し、こっそりこのモジュールをPython2.2.1のブランチに含めたのだ。これらのことは彼ら以外に誰にも知らせず内緒で行われた。著者いわく、この彼らの仕込んだeaster egg(thisモジュールのこと。ソフトウェアでいうeaster eggとは隠しコマンドとか、隠しクレジットのようなもの)が誰かに見つかるまではしばらく時間がかかったそうだ。

Barry Warsaw氏が同記事を「That was all back in the day when the Python community had a sense of humor」という一文で締めくくっているように、この記事を読むと当時のPythonコミュニティがいかにユーモア溢れたものだったのかが感じられる。phython-2.2.1がリリースされたのは2002年4月10日で、それからどれくらい経ってこのthisモジュールが発見されたのか分からないが初めて発見した人は絶対ほっこりしたことだろう。

Note 1: import this

The Zen of Python」はPythonハッカー、Tim Petersによって書かれた有名な文章でPython設計哲学を要約したようなものと言われている。 Barry Warsaw氏の記事によると起源はTim Peters氏による1999年6月4日のPython-listへのこの投稿のようだ。以下、Pythonインタラクティクモードでimport thisを実行し「The Zen of Python」を表示させた内容:

$ python
Python 3.4.3 (default, Oct 14 2015, 20:28:29)
[GCC 4.8.4] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import this
The Zen of Python, by Tim Peters

Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let's do more of those!
>>>

Note 2: this.py

this.pyの中身。 意味不明なコードをROT13 (Note 3)で複合化することで「The Zen of Python」を出力している。
/usr/lib/python3.4/this.py

s = """Gur Mra bs Clguba, ol Gvz Crgref

Ornhgvshy vf orggre guna htyl.
Rkcyvpvg vf orggre guna vzcyvpvg.
Fvzcyr vf orggre guna pbzcyrk.
Pbzcyrk vf orggre guna pbzcyvpngrq.
Syng vf orggre guna arfgrq.
Fcnefr vf orggre guna qrafr.
Ernqnovyvgl pbhagf.
Fcrpvny pnfrf nera'g fcrpvny rabhtu gb oernx gur ehyrf.
Nygubhtu cenpgvpnyvgl orngf chevgl.
Reebef fubhyq arire cnff fvyragyl.
Hayrff rkcyvpvgyl fvyraprq.
Va gur snpr bs nzovthvgl, ershfr gur grzcgngvba gb thrff.
Gurer fubhyq or bar-- naq cersrenoyl bayl bar --boivbhf jnl gb qb vg.
Nygubhtu gung jnl znl abg or boivbhf ng svefg hayrff lbh'er Qhgpu.
Abj vf orggre guna arire.
Nygubhtu arire vf bsgra orggre guna *evtug* abj.
Vs gur vzcyrzragngvba vf uneq gb rkcynva, vg'f n onq vqrn.
Vs gur vzcyrzragngvba vf rnfl gb rkcynva, vg znl or n tbbq vqrn.
Anzrfcnprf ner bar ubaxvat terng vqrn -- yrg'f qb zber bs gubfr!"""


d = {}
for c in (65, 97):
    for i in range(26):
        d[chr(i+c)] = chr((i+13) % 26 + c)

print "".join([d.get(c, c) for c in s])

Note 3: ROT13

ROT13は定められた置き換えマップにもとづいて文字を置き換えるだけの単純な暗号方式。次の変換マップに基づいて文字を変換するので例えばA→N、B→O、C→Pのように変換される。

ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz
                            ↓↑
NOPQRSTUVWXYZABCDEFGHIJKLMnopqrstuvwxyzabcdefghijklm

ちなみにthis.pyではこの変換マップをモジュール中で生成しているがPython2系、3系ではROT13の実装は標準で組み込まれているので次のように直接decode関数に’rot13’を指定することでROT13で暗号化された文字列sを複合化することができる。

>>> this.s
"Gur Mra bs Clguba, ol Gvz Crgref\n\nOrnhgvshy vf orggre guna htyl.\nRkcyvpvg vf orggre guna vzcyvpvg.\nFvzcyr vf orggre guna pbzcyrk.\nPbzcyrk vf orggre guna pbzcyvpngrq.\nSyng vf orggre guna arfgrq.\nFcnefr vf orggre guna qrafr.\nErnqnovyvgl pbhagf.\nFcrpvny pnfrf nera'g fcrpvny rabhtu gb oernx gur ehyrf.\nNygubhtu cenpgvpnyvgl orngf chevgl.\nReebef fubhyq arire cnff fvyragyl.\nHayrff rkcyvpvgyl fvyraprq.\nVa gur snpr bs nzovthvgl, ershfr gur grzcgngvba gb thrff.\nGurer fubhyq or bar-- naq cersrenoyl bayl bar --boivbhf jnl gb qb vg.\nNygubhtu gung jnl znl abg or boivbhf ng svefg hayrff lbh'er Qhgpu.\nAbj vf orggre guna arire.\nNygubhtu arire vf bsgra orggre guna *evtug* abj.\nVs gur vzcyrzragngvba vf uneq gb rkcynva, vg'f n onq vqrn.\nVs gur vzcyrzragngvba vf rnfl gb rkcynva, vg znl or n tbbq vqrn.\nAnzrfcnprf ner bar ubaxvat terng vqrn -- yrg'f qb zber bs gubfr!"
>>> this.s.decode('rot13')
u"The Zen of Python, by Tim Peters\n\nBeautiful is better than ugly.\nExplicit is better than implicit.\nSimple is better than complex.\nComplex is better than complicated.\nFlat is better than nested.\nSparse is better than dense.\nReadability counts.\nSpecial cases aren't special enough to break the rules.\nAlthough practicality beats purity.\nErrors should never pass silently.\nUnless explicitly silenced.\nIn the face of ambiguity, refuse the temptation to guess.\nThere should be one-- and preferably only one --obvious way to do it.\nAlthough that way may not be obvious at first unless you're Dutch.\nNow is better than never.\nAlthough never is often better than *right* now.\nIf the implementation is hard to explain, it's a bad idea.\nIf the implementation is easy to explain, it may be a good idea.\nNamespaces are one honking great idea -- let's do more of those!"

おわり。

UPDATED 2017-02-15: changed docker run command example due to Issue#4


Update Note azuresshconfig-0.2.3

(記事はここから)
以前「azuresshconfigの紹介 – Azure上でのSSH生活を少しだけ快適にする」の投稿でazuresshconfigの紹介をさせていただいたが、ツールをリリースして以来、数少ない貴重な利用者様からインストールがコケるんだけど何とかしろというクレームをいただいていた。そこでインストールマニュアルを充実させようかとか、インストーラーをプラットフォーム別に充実させようかとか考えたものの、ここは流行りのコンテナ実行できるようしたほうがいいだろうということでDocker対応することにした。

今回の対応によりpipインストールや、プラットフォーム別にprerequisiteなランタイム、ヘッダファイル、ライブラリといった面倒なインストールが不要となり、Mac、Windows、Linux(Ubuntu、CentOS、その他distro)関係なくシンプルにdocker runコマンドでの実行が可能となった。

しかも超軽量LinuxディストリビューションであるAlpine Linuxの上にPythonランタイムとツールを載せているだけであるためサイズはたったの155MBとかなり軽め

$ docker images azuresshconfig
REPOSITORY                          TAG                 IMAGE ID            CREATED             SIZE
azuresshconfig                     latest              7488bef4343f        7 minutes ago       155 MB

実行例

$ docker run -v $HOME:/root --rm -it yoichikawasaki/azuresshconfig \
    --output stdout --user yoichika --identityfile ~/.ssh/id_rsa > $HOME/.ssh/config

$HOME/.ssh/config

Dockerfileをダウンロードしてビルド・実行はこちら

$ curl https://raw.githubusercontent.com/yokawasa/azure-ssh-config/master/Dockerfile -o Dockerfile
$ docker build -t azuresshconfig .
$ docker run -v $HOME:/root --rm -it yoichikawasaki/azuresshconfig \
    --output stdout --user yoichika --identityfile ~/.ssh/id_rsa > $HOME/.ssh/config

LINKS

Enjoy SSH life on Azure with dockerized azuresshconfig!

Logstash is an open source, server-side data processing pipeline that ingests data from a multitude of sources simultaneously, transforms it, and then sends it to your favorite destinations. Here is a list of logstash plugins for Microsoft Azure Services.

Plugin NameTarget Azure ServicesNote
logstash-input-azureeventhubEventHubLogstash input plugin reads data from specified Azure Event Hubs
logstash-input-azureblobBlob StorageLogstash input plugin that reads and parses data from Azure Storage Blobs
logstash-input-azuretopicService Bus TopicLogstash input plugin reads messages from Azure Service Bus Topics
logstash-input-azuretopicthreadableService Bus TopicLogstash input plugin reads messages from Azure Service Bus Topics using multiple threads
logstash-output-applicationinsightsApplication InsightsLogstash output plugin that store events to Application Insights
logstash-input-azurewadtable Table StorageLogstash input plugin for Azure Diagnostics. Specifically pulling diagnostics data from Windows Azure Diagnostics tables
logstash-input-azurewadeventhubEventHubLogstash input plugin reads Azure diagnostics data from specified Azure Event Hubs and parses the data for output
logstash-input-azurewadtable Table StorageLogstash input plugin reads Azure diagnostics data from specified Azure Storage Table and parses the data for output
logstash-output-documentdbDocumentDBlogstash output plugin that stores events to Azure DocumentDB
logstash-output-azuresearchAzure Searchlogstash output plugin that stores events to Azure Search
logstash-output-azure_loganalyticsLog Analyticslogstash output plugin that stores events to Azure Log Analytics
logstash-input-jdbc
SQL Database, Azure Database for MySQL/PostgreSQLInput plugin to ingest data in any database with a JDBC interface into Logstash that support most of major RDBMS such as MySQL、PostgreSQL、OracleDB、Microsoft SQL, etc

(as of Dec 29, 2016)



logstash

過去に本ブログでビデオコンテンツを切り口とした音声認識OCR技術を利用したデモを紹介したが、ここではビデオコンテンツの中の人物出現箇所に連動して人物名を字幕で表示させるデモとその実装方法を紹介したい。人物識別にはAzureのCognitive ServicesのFace APIを使っていて、これで動画の中に出現する顔の検出を行い、予め登録している人物リストとのマッチングにより実現している。
Cognitive Serivcesとは視覚、音声、言語、知識などマイクロソフトがこれまで研究を通じて開発してきたさまざまな要素技術をAPIとして提供しているサービスのことで、最近巷で人工知能(AI)だとかインテリジェンスとかいうキーワードをよく耳にするのではないかと思うがAzure利用シナリオでそういったインテリジェンス(知能/知性)を兼ね備えたアプリを作る場合は間違いなく中核となるサービスの1つである。Face APIはその中でも顔の検出・識別や、顔にまつわる感情、特徴などメタデータ抽出に特化したAPIである。



Video Summarization and Face Detection Demo Screenshot

( デモサイト | Source Code )

主要テクノロジーと機能

下図は今回のデモ作成のために行っている処理フローと主要テクノロジーを表している。やっていることは大きく分けて3つ: (1) 動画コンテンツをAzure Media Encoder Standardを使ってフレームごとの静止画像の作成, (2) Cognitive ServicesのFace APIを使って1より得られた静止画像から顔の検出を行い予め登録している人物リストとマッチング(最も類似度が高いものを本人とみなす)して人物を識別, (3) 2で得られた各フレーム中の人物情報を時間順に並べて字幕(Closed Caption)用のデータファイルを生成。以下、各処理の詳細について説明する。


VideoFramesFaceFecognition_case1

1. Azure Media Encoder Standardでフレームごとの静止画生成

残念ながらFace APIはビデオコンテンツから直接顔検出することができないため、一旦ビデオコンテンツから各フレームごとの静止画を生成してその静止画を対象に処理を行う必要がある。ここでは各フレームごとの静止画生成にAzure Media Encoder Standard(MES)を利用する。MESを使うことでエンコードタスクとしてビデオコンテンツに対して様々な処理を行うことができるのだが、MESにはそのエンコードタスクの1つとしてサムネイル生成のためのタスクが用意されており、今回はこのサムネール生成タスクを利用する。他のエンコードタスク同様にサムネイル生成タスクについてもプリセットと呼ばれるエンコードに必要な情報を記述した XML または JSON形式ファイルを用意する必要がある。今回は1秒フレームごとにJPEG形式の静止画(サムネイル)を生成するために次のようなプリセット(amsmp-thumbnail-config.json)を用意した。

{
  "Version": 1.0,
  "Codecs": [
    {
      "Start": "00:00:00",
      "Step": "00:00:01",
      "Type": "JpgImage",
      "JpgLayers": [
        {
          "Quality": 90,
          "Type": "JpgLayer",
          "Width": 640,
          "Height": 360
        }
      ]
    }
  ],
  "Outputs": [
    {
      "FileName": "{Basename}_{Index}{Extension}",
      "Format": {
        "Type": "JpgFormat"
      }
    }
  ]
}

MESによるサムネイル処理実行方法やプリセットの詳細については「Media Encoder Standard を使用した高度なエンコード」や同ページの「サムネイルを生成する」項を参照ください。尚、今回のサムネイル生成のためのエンコーディング処理は小生自作の「azure-media-processor-java」を利用してバッチ実行している。

2. Cognitive Services Face APIによる顔の検出と人物の識別

ここではCognitive ServicesのFace APIを使って1で得られたフレームごとの静止画像に対して顔検出を行い、予め登録している人物リスト(Face APIでいうところのPerson Group)と比較して最も類似度の高い人物(Face APIでいうところのPerson )をその本人として識別する。

2-1. 人物リスト(Person Group)の作成

人物リスト(Person Group)の作成で必要な作業とFace APIの利用インターフェースは次の通り:

2-2. 静止画像中の顔認識と人物識別

ここで行う処理の流れとFace APIの利用インターフェースは次の通り:

3. 字幕(Closed Caption)データファイルの生成

2で得られた各フレーム中の人物情報と各フレームの時間を元に字幕用のデータフォーマットであるWebVTTフォーマットファイルを生成する。以下、6秒~30秒までの字幕出力を期したWebVTTファイルのサンプルであるが、見ていただいてわかる通りフレームの時間(最小秒単位)とそこで得られた人物名をセットで記述するとても単純なフォーマットとなっている。

00:00:06.000 --> 00:00:07.000
Satya Nadella(0.73295)

00:00:07.000 --> 00:00:08.000
Satya Nadella(0.6313)

00:00:27.000 --> 00:00:28.000
Bryan Roper(0.68094)

00:00:29.000 --> 00:00:30.000
Bryan Roper(0.54939)

各フレームの時間について、今回のビデオコンテンツのフレームは1秒ごとに取得しており、フレームごとの静止画像ファイルにはフレームの順番がPostfixとしてファイル名に含まれているため単純にファイル名からフレームの時間が特定できるようになっている(例, 10番目のファイル= videoassetname_000010.jpg)。もし今回のような機械的なルールがない場合はフレーム用画像ファイル名と時間のマッピングが必要となる。

ビデオコンテンツと字幕の再生は「ビデオコンテンツの音声認識デモ」でも紹介したようにHTML5のtrackタグエレメントによるビデオファイルの字幕表示機能使って人物名の字幕表示を実現している。本デモではHTML5に下記のようにビデオファイル(MP4)をVideoソースとしてtrackエレメントにWebVTTファイル(build2016keynote.vtt)を指定している。

<video id="Video1" controls autoplay width="600">
    <source src="KEY01_VideoThumbnail.mp4" srclang="en" type="video/mp4">
    <track id="trackJA"  src="build2016keynote.vtt"  kind="captions" srclang="ja" label="Person Name" default>
</video>

デモデータ作成手順

GithubプロジェクトページVideoFramesFaceRecognition-Pythonの1. Preparationと2. Batch executionを実施いただければFace APIで識別した各フレームごとの人物名を元に字幕データ*.vttファイルが生成されデモページ表示のための準備は完了する。最後に表示用の静的ページを生成すれば完了。本デモの表示用ページデータはこちらで、基本的にindex.htmlの変更のみでいけるはず。

本デモコンテンツについて何か問題を発見した場合はこちらのGithub IssueページにIssueとして登録いただけると幸いである。

Azure Media Analytics Face Detectorを活用した処理の効率化

今回の人物識別ではビデオコンテンツの全てのフレームに対してFace APIを使って顔検出処理を行っているが、これでは顔出現フレームが少ないコンテンツの場合には無駄なFace APIリクエストが大量に発生してしまうため効率的な処理とは言えない。ということで、ここではAzure Media Face Detectorを活用して処理を効率化する方法を紹介したい。
Azure Media Face DetectorはAzure Media Servicesのメディアプロセッサ(MP)の1つで、ビデオコンテンツから顔の検出や感情の検出をすることができる。残念ながらAzure Media Face DetectorはFace APIのように顔の識別を行うことはできないものの、ビデオコンテンツから直接顔を検出することができる、即ちビデオコンテンツから直接顔が存在するフレームを特定することができる。よって、この機能を利用して一旦Azure Media Face Detectorで顔が検出されたフレームのみに絞り込んでからFace APIを使ってフレームの静止画像に対して顔検出・顔識別を行うことで無駄なFace APIリクエストを減らして処理の効率化を図ることができる。処理フローとしては次のようなイメージ。


VideoFramesFaceFecognition_case2

おまけ: Video Summarization

デモページをみていただくとお分かりのように今回のデモでは3分のビデオコンテンツを題材としているが、元ネタはChannel9で公開されている計138分のBuild 2016のキーノートセッションである。このキーノートのセッションはデモコンテンツとしてはあまりに長かったのでこれをAzure Media Video Thumbnailsメディアプロセッサ(MP)を使って3分に要約している。Azure Media Video Thumbnailsはアルゴリズムベースで特徴シーンの検出とそれらを結合(サブクリップ)してビデオコンテンツを指定した長さに要約することができるMPで、現在Public Previewリリース中(2016年12月現在)。

参考までに、要約(3分:180秒)に使用したAzure Media Video Thumbnailsのタスクプリセットは以下の通り:

{
    "version": "1.0",
    "options": {
        "outputAudio": "true",
        "maxMotionThumbnailDurationInSecs": "180",
        "fadeInFadeOut": "true"
    }
}

END

In this article, I’d like to introduces a solution to collect events from various sources and send them into HTTP Trigger function in Azure Functions using fluent-plugin-azurefunctions. Triggers in Azure Functions are event responses used to trigger your custom code. HTTP Trigger functions allow you to respond to HTTP events sent from fluentd and cook them into whatever you want!


fluent-plugin-azurefunctions

[note] Azure Functions is a (“serverless”) solution for easily running small pieces of code, or “functions,” in Azure. Fluentd is an open source data collector, which lets you unify the data collection and consumption for a better use and understanding of data. fluent-plugin-azurefunctions is a fluentd output plugin that enables to collect events into Azure Functions.

Pre-requisites

Setup: Azure Functions (HTTP Trigger Function)

Create a function (HTTP Trigger). First, you need to have an function app that hosts the execution of your functions in Azure if you don’t already have. Once you have an function app, you can create a function. Here are instructions:

A quick-start HTTP trigger function sample is included under examples/function-csharp in Github repository. You simply need to save the code (run.csx) and configuration files (function.json, project.json) in the same Azure function folder. Explaining a little bit about each of files, the function.json file defines the function bindings and other configuration settings. The runtime uses this file to determine the events to monitor and how to pass data into and return data from function execution. The project.json defines packages that the application depends. The run.csx is a core application file where you write your code to process Your jobs. Here is a sample run.csx:

Setup: Fluentd

First of all, install Fluentd. The following shows how to install Fluentd using Ruby gem packger but if you are not using Ruby Gem for the installation, please refer to this installation guide where you can find many other ways to install Fluentd on many platforms.

# install fluentd
sudo gem install fluentd --no-ri --no-rdoc

# create fluent.conf
fluentd --setup <directory-path-to-fluent-conf>

Also, install fluent-plugin-azurefunctions for fluentd aggregator to send collected event data into Azure Functions.

sudo gem install fluent-plugin-azurefunctions

Next, configure fluent.conf, a fluentd configuration file as follows. Please refer to this for fluent-plugin-azurefunctions configuration. The following is a sample configuration where the plugin writes only records that are specified by key_names in incoming event stream out to Azure Functions:

# This is used by event forwarding and the fluent-cat command
<source>
    @type forward
    @id forward_input
</source>

# Send Data to Azure Functions
<match azurefunctions.**>
    @type azurefunctions
    endpoint  AZURE_FUNCTION_ENDPOINT   # ex. https://<accountname>.azurewebsites.net/api/<functionname>
    function_key AZURE_FUNCTION_KEY     # ex. aRVQ7Lj0vzDhY0JBYF8gpxYyEBxLwhO51JSC7X5dZFbTvROs7uNg==
    key_names key1,key2,key3
    add_time_field true
    time_field_name mytime
    time_format %s
    localtime true
    add_tag_field true
    tag_field_name mytag
</match>

[note] If key_names not specified above, all incoming records are posted to Azure Functions (See also this).

Finally, run fluentd with the fluent.conf that you configure above.

fluentd -c ./fluent.conf -vv &

TEST

Let’s check if test events will be sent to Azure Functions that triggers the HTTP function (let’s use the sample function included in Github repo this time). First, generate test events using fluent-cat like this:

echo ' { "key1":"value1", "key2":"value2", "key3":"value3"}' | fluent-cat azurefunctions.msg

As both add_time_field and add_tag_field are enabled, time and tag fields are added to the record that are selected by key_names before posting to Azure Functions, thus actual HTTP Post request body would be like this:

{
    "payload": '{"key1":"value1", "key2":"value2", "key3":"value3", "mytime":"1480195100", "mytag":"azurefunctions.msg"}'
}

If events are sent to the function successfully, a HTTP trigger function handles the events and the following logs can be seen in Azure Functions log stream:

2016-11-26T21:18:55.200 Function started (Id=5392e7ae-3b8e-4f65-9fc1-6ae529cdfe3a)
2016-11-26T21:18:55.200 C# HTTP trigger function to process fluentd output request.
2016-11-26T21:18:55.200 key1=value1
2016-11-26T21:18:55.200 key2=value2
2016-11-26T21:18:55.200 key3=value3
2016-11-26T21:18:55.200 mytime=1480195100
2016-11-26T21:18:55.200 mytag=azurefunctions.msg
2016-11-26T21:18:55.200 Function completed (Success, Id=5392e7ae-3b8e-4f65-9fc1-6ae529cdfe3a)

Advanced Senarios

1. Near Real-time processing

Function Apps can output messages to different means or data stores. For example, fluentd collects events generated from IoT devices and send them to Azure Function, and the the HTTP trigger function transforms the events and processes the data to store in a persistent storage or to pass them to different means. Here are some of options available at the time of writing:

2. Background jobs processing

If the jobs are expected to be large long running ones, it’s recommended that you refactor them into smaller function sets that work together and return fast responses. For example, you can pass the HTTP trigger payload into a queue to be processed by a queue trigger function. Or if the payload is too big to pass into the queue, you can store them onto Azure Blob storage at first, then pass only limited amount of the data into a queue just to trigger background workers to process the actual work. These approaches allow you to do the actual work asynchronously and return an immediate response.

LINKS

END

OCRとはOptical Character Recognitionの略で日本語にすると光学文字認識と訳されており、ざっくりと画像の中の文字をテキストに変換する技術のことを指す。テキストに変換されるということは勘が鋭い皆さんはお気づきだと思うが、テキストの全文検索であったり、テキストから音声への変換、さらには機械翻訳を使って多言語への変換といった展開が考えられる。そんな可能性を秘めたOCRであるが、ここではそのOCRの技術を使ってビデオファイルから抽出したテキストデータを元にビデオに字幕表示したり、動画中に表示される文字を全文検索をするデモを紹介したい。内容的には「Azure Media & Cognitiveデモ:Speech-To-Text」で紹介したデモのOCR版といったところ。



Video OCR Demo Screenshot

( デモサイト | Source Code )

主要テクノロジーと機能

Azure Media OCRメディアプロセッサによるテキスト抽出

このデモではAzure Media OCRメディアプロセッサー(MP)を使用してビデオファイル内のテキストコンテンツを検出してテキストファイルを生成している。OCRメディアプロセッサーは入力パラメータによりビデオ解析の挙動を調整することができる。主なパラメータとしては検索対象テキストの言語(日本語もサポート)、テキストの向き、サンプリングレート、ビデオフレーム内のテキスト検出対象のリージョンがあるが、本デモでの入力パラメータ(Video-OCR-Search-Python/src/ocr-detectregion.json)は以下の通り検索対象言語は日本語、1秒おきのサンプリングレート、テキスト検出対象のリージョンからビデオフレーム内の上部1/4を省く設定(検出対象をフレームトップから85 pixel以下を対象)にしている。

{
    "Version":"1.0",
    "Options":
    {
        "Language":"Japanese",
        "TimeInterval":"00:00:01.000",
        "DetectRegions":
        [
            {"Left":"0","Top":"85","Width":"1280","Height":"635"}
        ]
    }
}

そして、Azure Media OCRメディアプロセッサはビデオで検出された文字を下記のような表示時間に基づいてセグメント化された形で結果出力する。結果ファイルの完全版はこちら(azuresubs.json)を参照ください。

{
    "fragments": [
        {
            "start": 0
            "interval": 319319,
            "duration": 319319,
            "events": [
                [
                    {
                        "language": "Japanese",
                        "text": "Azure の 契 約 内 容 を 変 更 す る Microsoft Azure"
                    }
                ]
            ]
        },
        {  /* fragment1 */ },
        {  /* fragment2 */ },
        ...
        {  /* fragmentN */ }
    ],
    "version": 1,
    "framerate": 29.97,
    "height": 720,
    "width": 1280,
    "offset": 0,
    "timescale": 30000
}

入力パラメータと出力形式共に詳細はこちらのドキュメントを参照いただくとしてAzure Media OCRメディアプロセッサ利用の注意点として次の2つがある:

字幕(Closed Caption)データフォーマットへの変換

まず上記Azure Media OCRメディアプロセッサー(MP)から出力されたJSONファイルの内容を元に字幕用のデータフォーマットであるWebVTTフォーマットファイルを生成している。そして「Azure Media & Cognitiveデモ:Speech-To-Text」でも紹介したようにHTML5のtrackタグエレメントによるビデオファイルの字幕表示機能使ってOCRの内容の字幕表示を実現している。本デモではHTML5に下記のように動画(TransferanAzuresubscriptionJP.mp4)をVideoソースとしてtrackエレメントにWebVTTファイル(azuresubs.vtt)を指定している。

<video id="Video1" controls autoplay width="600">
    <source src="TransferanAzuresubscriptionJP.mp4" srclang="en" type="video/mp4">
    <track id="trackJA"  src="azuresubs.vtt"  kind="captions" srclang="ja" label="OCR Subtitle" default>
</video>

Azure Searchによる全文検索

デモページ上部にある検索窓にキーワードを入力してGoボタンを押すとビデオコンテンツからOCR抽出されたテキストを元に生成された字幕データを全文検索してキーワードにマッチしたテキストとその表示時間に絞り込むことができる。仕組みは「Azure Media & Cognitiveデモ:Speech-To-Text」と全く同じで、Azure Searchを使用して字幕データを解析して字幕表示時間とその対応テキストを1ドキュメントレコードとしてAzure Searchにインジェストしてその生成されたインデックスに対してキーワードを元に全文検索することで実現している。検索用のインデックススキーマもまったくおなじで次のように字幕表示時間とその対応テキストをレコード単位となるように定義している。

{
    "name": "ocr",
    "fields": [
        { "name":"id", "type":"Edm.String", "key": true, "searchable": false, "filterable":false, "facetable":false },
        { "name":"contentid", "type":"Edm.String","searchable": false, "filterable":true, "facetable":false },
        { "name":"beginsec", "type":"Edm.Int32", "searchable": false, "filterable":false, "sortable":true, "facetable":false },
        { "name":"begin", "type":"Edm.String", "searchable": false, "filterable":false, "sortable":false, "facetable":false },
        { "name":"end", "type":"Edm.String", "searchable": false, "filterable":false, "sortable":false, "facetable":false },
        { "name":"caption", "type":"Edm.String", "searchable": true, "filterable":false, "sortable":false, "facetable":false, "analyzer":"ja.microsoft" }
     ]
}

デモデータ作成手順

GithubプロジェクトページVideo-OCR-Search-Pythonの1. Preparationと2. Batch executionを実施いただければOCR抽出されたテキストを元に字幕データ*.vttファイルが生成され、そのテキストがAzure Searchに格納されてデモページ表示のための準備は完了する。最後に表示用のページを生成すれば完了。本デモの表示用ページデータはこちらで、基本的にindex.htmlとsearch.jsの変更のみでいけるはず。

本デモコンテンツについて何か問題を発見した場合はこちらのGithub IssueページにIssueとして登録いただけると幸いである。

Enjoy Video OCR demo!