Taste of Tech Topics

Taste of Tech Topics

Acroquest Technology株式会社のエンジニアが書く技術ブログ

Normalizerを利用してkeyword型のデータを加工する

こんにちは、ノムラです。
この記事はElastic Stack (Elasticsearch) Advent Calendar 2019の9日目の記事になります。

はじめに

データを可視化、集計する際以下のようなデータが別々のデータとして扱われ困ったことはないでしょうか?

  • 〇〇(株) と 〇〇株式会社
  • 斎藤 と 齋藤
  • Elasticsearch と elasticsearch
(株) と 株式会社で困った例

f:id:acro-engineer:20191209001620p:plain

本来であれば、アクロクエストテクノロジー(株)とアクロクエストテクノロジー株式会社は同一の会社名として扱いたいです。
一見、ElasticsearchにはAnalyzer機能があるため、上記問題は簡単に解決することができるように思えます。
しかし上記のAnalyzer機能はText型にのみ適用可能な機能です。
そのため、上の例のような会社名や苗字の集計時によく設定されるkeyword型には適用することができません。
※Text型として扱うと、〇〇株式会社 ⇒ 〇〇 + 株式会社 の2単語として集計されてしまうため集計で不都合が生じます

そのような場合は、Normalizerを使いましょう。

Normalizerについて

Normalizerとは

Normalizerはkeyword型に適用可能な、Analyzerに相当する機能になります。
www.elastic.co

Normalizerには、char_filterとfilterを定義することができ、
異体字の変換や、大文字化小文字化等の加工ができます。
tokenizerは、keyword tokenizerが適用されます。
※Normalizerはインデキシング時だけでなく、検索時にも入力キーワードへ適用されます。

Normalizerの設定方法

Index Mappingのsettingsに下記のように設定します。

PUT sample_index
{
  "settings": {
    "analysis": {
      "normalizer": {
        "my_normalizer": {
          "type": "custom",
          "char_filter": [
            "my_char_filter"
          ],
          "filter": [
            "lowercase"
          ]
        }
      },
      "char_filter": {
        "my_char_filter": {
          "type": "mapping",
          "mappings": [
            "(株) => 株式会社",
            "齋 => 斎"
          ]
        }
      }
    }
  },
  "mappings": {
    "properties": {
      "company_name": {
        "type": "keyword",
        "normalizer": "my_normalizer"
      },
      "first_name": {
        "type": "keyword",
        "normalizer": "my_normalizer"
      },
      "product_name": {
        "type": "keyword",
        "normalizer": "my_normalizer"
      }
    }
  }
}

char_filterのmappingsで具体的にどのように変換するかを定義します。
ここでは、

  • (株) を 株式会社 へ
  • 齋 を 斎 へ

と変換を定義しています。
また、filterでlowercaseへの変換を定義しています。

データの登録

サンプルデータとして下記のデータを登録します。

# (株) ⇒ 株式会社
PUT sample_index/_doc/1
{
  "company_name": "アクロクエストテクノロジー(株)"
}
PUT sample_index/_doc/2
{
  "company_name": "アクロクエストテクノロジー株式会社"
}

# 齋 ⇒ 斎
PUT sample_index/_doc/3
{
  "name": "斎藤"
}
PUT sample_index/_doc/4
{
  "name": "齋藤"
}

# 大文字小文字
PUT sample_index/_doc/5
{
  "product_name": "Elasticsearch"
}
PUT sample_index/_doc/6
{
  "product_name": "elasticsearch"
}

適用結果

それぞれ以下のように、

でそれぞれ統一されて集計されています。

(株) ⇒ 株式会社

# 会社名で集計
GET sample_index/_search
{
  "size": 0, 
  "aggs": {
    "company_name": {
      "terms": {
        "field": "company_name",
        "size": 10
      }
    }
  }
}

# 集計結果
{
  "took" : 1045,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 6,
      "relation" : "eq"
    },
    "max_score" : null,
    "hits" : [ ]
  },
  "aggregations" : {
    "company_name" : {
      "doc_count_error_upper_bound" : 0,
      "sum_other_doc_count" : 0,
      "buckets" : [
        {
          "key" : "アクロクエストテクノロジー株式会社",
          "doc_count" : 2
        }
      ]
    }
  }
}

齋 ⇒ 斎

# 名前で集計
GET sample_index/_search
{
  "size": 0, 
  "aggs": {
    "name": {
      "terms": {
        "field": "name",
        "size": 10
      }
    }
  }
}

# 集計結果
{
  "took" : 8,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 6,
      "relation" : "eq"
    },
    "max_score" : null,
    "hits" : [ ]
  },
  "aggregations" : {
    "name" : {
      "doc_count_error_upper_bound" : 0,
      "sum_other_doc_count" : 0,
      "buckets" : [
        {
          "key" : "斎藤",
          "doc_count" : 2
        }
      ]
    }
  }
}

大文字 ⇒ 小文字

# 製品名で集計
GET sample_index/_search
{
  "size": 0, 
  "aggs": {
    "name": {
      "terms": {
        "field": "product_name",
        "size": 10
      }
    }
  }
}

# 集計結果
{
  "took" : 8,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 6,
      "relation" : "eq"
    },
    "max_score" : null,
    "hits" : [ ]
  },
  "aggregations" : {
    "name" : {
      "doc_count_error_upper_bound" : 0,
      "sum_other_doc_count" : 0,
      "buckets" : [
        {
          "key" : "elasticsearch",
          "doc_count" : 2
        }
      ]
    }
  }
}

おわりに

今回はNormalizerを利用して、keyword型にchar_filterとfilterを適用し異体字や、大文字小文字を統一する方法について書きました。
私自身、Normalizerについて知るまではLogstashやスクリプト側で加工する必要があると思っており、
その点NormalizerはElasticsearchの設定のみで可能なので簡単で、とても便利だと感じました。

上記の例以外にも様々なことに利用可能だと思うので皆さんも是非触ってみてください。

10日目はhttps://qiita.com/froakie0021さんです。お楽しみに。

Acroquest Technologyでは、キャリア採用を行っています。

  • ディープラーニング等を使った自然言語/画像/音声/動画解析の研究開発
  • Elasticsearch等を使ったデータ収集/分析/可視化
  • マイクロサービス、DevOps、最新のOSSを利用する開発プロジェクト
  • 書籍・雑誌等の執筆や、社内外での技術の発信・共有によるエンジニアとしての成長

 
少しでも上記に興味を持たれた方は、是非以下のページをご覧ください。

Kaggle Masterと働きたい尖ったエンジニアWanted! - Acroquest Technology株式会社のエンジニアの求人 - Wantedlywww.wantedly.com

Micrometerで取得したデータをKibanaで可視化してみました

こんにちは、oogiです。

この記事はElastic Stack (Elasticsearch) Advent Calendar 2019の8日目の記事になります。

はじめに

Spring Bootアプリケーションの監視にMicrometerを使うことは多いと思います。
収集したデータはレジストリの切り替えによりPrometheusやElasticsearchなど保存先を選んで利用することができます。

で、収集したら当然可視化してみたいわけですが、残念ながらMicrometerで収集したデータをKibanaで可視化するためのダッシュボードが存在していないため、結局のところPrometeus&Grafanaが選ばれることが多いです。

Elastic Stackを活用しているAcroquestとしてはせっかくElasticsearchにも保存できるのにこれはもったいない…なら、うちで作るしかない!
ということでMicrometer用のKibanaダッシュボードを作ってみました。

Micrometer用Kibanaダッシュボード

早速ですが、作ったダッシュボードがこちらです。

Spring Boot Statistics

f:id:acro-engineer:20191203101247g:plain

Spring Boot Application

f:id:acro-engineer:20191203102105g:plain

作成にあたってはGrafanaの画面を参考にし、同様に以下の情報が見えるようにしました。

Spring Boot Statistics
  • 起動時刻、CPU使用率
  • メモリ使用量
  • GC
  • DBコネクション
  • HTTPリクエス
  • Tomcatのメトリクス
  • ログ出力
Spring Boot Application
  • リクエストおよびログ出力
  • URL別のリクエスト数およびレスポンスタイム
  • 別サービスへのHTTPアクセス

それぞれホスト名やアプリケーション名でフィルタできます。

今回作ったKibanaのダッシュボードを、利用したい環境でインポートすれば使えるように以下で公開していますので、ElasticsearchでMicrometerのデータを可視化してみたい方はお試しください。
Micrometer Kibana Dashboard

Elastic APM

さて、ここまでMicrometerで収集したデータをKibana上で可視化するダッシュボードを紹介しましたが、Elastic Stackを用いたアプリケーション監視と言えばElastic APMがあります。

Elastic APMはその名の通りElastic社が出しているAPMで、OSSで利用できます。
Javaを始めとするさまざまな言語に対応したAgentが存在し、Java Agentの場合は適用する際にソースコードの変更を必要としません。

また、RUMというフロントエンド向けのAgentも提供されており、Angularインテグレーションを使うことでフロントエンドの監視もできます。
しかもバックエンド側のエージェントとトレースIDを共有することができ、フロントエンドとバックエンドの分散トレーシングが可能です。

さらにElastic APM 7.4からはJavaのLogging libraryが提供されています。
これを既存のログ設定に追加することでAPMのAgentが設定するトレースIDがログにも連携され、APMとLogs UIとの連携もできるようになります。

これらについても試してみたかったので、今回はMicrometer用ダッシュボードに加えてElastic APMも同時に試してみることのできるデモ環境をSpring Petclinicベースで作成してみました。
Micrometer & Elastic APM Demo

REST APIに対するMicrometerでの監視に加えて、上記のフロントエンドとバックエンドの分散トレーシングやLogsとの連携も見ることができます。

f:id:acro-engineer:20191203102759p:plain

f:id:acro-engineer:20191203102826p:plain

ぜひMicrometerとElastic APMによる監視を試してみてください。

おわりに

今回はMicrometerで収集したデータをKibanaで可視化するダッシュボードと、そのMicrometer用ダッシュボードおよびElastic APMを利用したアプリケーション監視のサンプルを紹介しました。

最近はJavaよりも新しい言語がいろいろと出てきていますが、Javaにはこのように言語自体の機能だけでなく周辺ツールを含めた開発・運用環境が充実しているという特長があります。
その点を考慮すれば、開発環境から運用まで考慮したシステム開発においてJavaを選択するというのは十分なメリットがあるのではないかと思います。


9日目はbob_nomuさんです。お楽しみに。

Acroquest Technologyでは、キャリア採用を行っています。

  • ディープラーニング等を使った自然言語/画像/音声/動画解析の研究開発
  • Elasticsearch等を使ったデータ収集/分析/可視化
  • マイクロサービス、DevOps、最新のOSSを利用する開発プロジェクト
  • 書籍・雑誌等の執筆や、社内外での技術の発信・共有によるエンジニアとしての成長

 
少しでも上記に興味を持たれた方は、是非以下のページをご覧ください。

Kaggle Masterと働きたい尖ったエンジニアWanted! - Acroquest Technology株式会社のエンジニアの求人 - Wantedlywww.wantedly.com

GiNZA+Elasticsearchで係り受け検索の第一歩

急に冷え込んできてお布団が恋しい季節になってきました。
こんにちは。@Ssk1029Takashiです。
この記事は自然言語処理 Advent Calendarの6日目の記事になります。
qiita.com

全文検索システムは単語検索であることが多いですが、単語検索だけだと困ることもあります
症例検索を例にとって見てみましょう。
検索エンジンに以下の2つの文章が登録されているとします。
「ずっと胃がキリキリと痛い。ただ、熱は無く平熱のままだ。」
「昨日からとても頭が痛い。おまけに胃がむかむかする。」
この時、「胃が痛い」と検索したとき、通常の単語検索の場合だと両方ともヒットしてしまいますが、下の文章は意味としては異なる文章のためゴミになります。

この記事では、GiNZAとElasticsearchを使って意味的に正しい上の文章だけを拾ってくる仕組みを簡単に実現してみようと思います。

どうやって解決するか

概要は以下の図のようになります。

f:id:acro-engineer:20191203000159p:plain

1. 投入時には文章を係り受け解析をして、主語・述語のペアを抜き出します。
 例えば、「昨日から胃がキリキリと痛い。ただし、熱はない」という文章からは、「主語:胃、述語:痛い」「主語:熱、述語:ない」という2つのペアを抽出します。
 また、今回は修飾語や目的語などの係り受け関係は考慮せず、主語述語のみとして、修飾語は次回の課題とします。

2. 検索時には入力として、文章を想定します。
 入力された文章を係り受け解析して、投入時と同じく主語・述語のペアにして、同じペアを持つ文書を検索するようにします。
 また、同時に主語・述語以外の単語でも絞るために、単語検索も同時に実行します。

GiNZAとは

詳細に入る前にGiNZAについて簡単に説明します。
megagonlabs.github.io

GiNZAはリクルートさんと国立国語研究所さんが共同で開発した自然言語処理用のライブラリです。
欧米でよく使用されている自然言語処理ライブラリであるspaCyを日本語に対応させたものになります。

特長は以下の3点だと思います。
1. 形態素解析・依存構造解析・固有表現抽出・埋め込みベクトル等一つのライブラリで様々なタスクに対応している。
2. pip install一行でインストールが可能。
3. 高精度なモデルがプリセットとして提供されている。

簡単に言うなら、 一行でインストールできる高精度・高機能な日本語処理ライブラリです。

個人的には形態素解析にSudachiが使用されており、表記揺れなどにもデフォルトで対応できるのがとてもありがたいです。

係り受け解析

それでは、実際に文書から主語・述語を抽出していきます。
主語・述語を抽出する手順は以下のようになります。
1. 日本語用モデルをロードする
2. 入力された文章を解析する。(この段階で形態素解析・依存構造解析を行う)
3. 解析結果から主語・述語のペアを抜き出す。

準備

まずは、GiNZAを使う準備からです。
以下のコマンドでインストールします。

pip install "https://github.com/megagonlabs/ginza/releases/download/latest/ginza-latest.tar.gz"

モデルのロード・日本語文章の解析

まず試しに、モデルのロード・解析だけをやってみましょう。

import spacy

nlp = spacy.load('ja_ginza')
doc = nlp('昨日から胃がキリキリと痛い。')

for sent in doc.sents:
    for token in sent:
        print(token.i, token.orth_, token.lemma_, token.pos_, token.tag_, token.dep_, token.head.i)
    print('EOS')

出力は以下のようになります。

0 昨日 昨日 NOUN 名詞-普通名詞-副詞可能 nmod 6
1 から から ADP 助詞-格助詞 case 0
2 胃 胃 NOUN 名詞-普通名詞-一般 nsubj 6
3 が が ADP 助詞-格助詞 case 2
4 キリキリ きりきり NOUN 名詞-普通名詞-一般 nmod 6
5 と と ADP 助詞-格助詞 case 4
6 痛い 痛い ADJ 形容詞-一般 ROOT 6
7 。 。 PUNCT 補助記号-句点 punct 6

表示している項目は、左から単語・見出し語・品詞タグ・品詞情報・依存関係ラベル・係り先の単語インデックスとなっています。
これらの項目をトークンとごとに出力しています。
この中にある、NOUN・ADPや、nmod・nsubjという文字列はUniversal Dependencyというプロジェクトで定義されているラベルになります。
係り受け関係を可視化すると次のようになります。
f:id:acro-engineer:20191203013929p:plain

Universal Dependencyとは

軽くUniversal Dependency(以下UD)について説明しておきたいと思います。
UDとは、構文構造を多言語間で統一しようという世界的な活動のことを差します。
その中で、NOUN(名詞)やVERB(動詞)などの品詞ラベルや、 nsubj(主語述語関係)・dobj(目的語関係)などの係り受け関係を定義しています。
GiNZAはデフォルトではUDで定義されたラベルで依存構造解析を行います。
日本語でのUDについては以下の論文で詳しく解説されています。図を眺めるだけでもイメージを掴めるためぜひ読んでみてください。
https://www.anlp.jp/proceedings/annual_meeting/2015/pdf_dir/E3-4.pdf

主語述語のペアを抜き出す

それでは、主語述語のペアを抜き出していきます。
上で述べたように、nsubjというラベルが主語述語の係り受けなので、nsubjのラベルを持っているトークンと係り受け先のトークンをペアとします。
ただし、今報告されている問題として、依存関係のラベルがnsubjのものがiobjになってしまう課題があるので、ここではiobjも対象にします。

実装は以下のようになります。

def parse_document(sentence, nlp):
    doc = nlp(sentence)
    tokens = []

    ## 参照しやすいようにトークンのリストを作る
    for sent in doc.sents:
        for token in sent:
            tokens.append(token)

    ## 主語述語ペアのリスト
    subject_list = []

    for token in tokens:
        ## 依存関係ラベルがnsubj or iobjであれば「<見出し語>:<係り先の見出し語>」をリストに追加する。
        if token.dep_ in  ["nsubj", "iobj"]:
            subject_list.append(f"{token.lemma_}:{tokens[token.head.i].lemma_}")
    
    return subject_list

試しに「昨日から胃がキリキリと痛い。ただし、熱はない」という文章を引数にすると以下のようになります。

nlp = spacy.load('ja_ginza')
print(parse_document("昨日から胃がキリキリと痛い。ただ、熱は無い。", nlp))

出力

['胃:痛い', '熱:無い']

主語述語のペアが取れていますね。

Elasticsearchでの準備

次はElasticsearchへの投入と検索の部分を作っていきます。
バージョンとプラグインは以下のようになっています。
バージョン:7.4
プラグイン:analysis-sudachi

残り必要なことは以下の3つです。
①Mappingを決めて、データを投入する
②クエリを決める
③入力された検索文章からクエリに変換する

Mapping

Mappingについては、元文章は単語検索の対象になるためtext型で、主語述語のペアは完全一致のためkeyword型の配列で持つようにします。
また、形態素解析を使った単語検索のためにはtext型のフィールドにはanalyzerを設定しておきます。

クエリ

次に検索クエリを作成します。
例えば「胃が痛い」という入力のときには、以下のようなクエリになります。

{
  "query": {
    "bool": {
      "must": [
        {
          "query_string": {
            "default_field": "content",
            "query": "胃が痛い",
            "analyzer": "sudachi_analyzer"
          }
        },
        {
          "terms": {
            "subjects": [
              "胃:痛い"
            ]
          }
        }
      ]
    }
  }
}

クエリとしては、単語検索と主語述語の組み合わせをAND条件で取得するようにしています。

試してみる

それではサンプルデータを入れて検索してみましょう。

今回はサンプルのデータとして以下の2件をElasticseachに登録しておきます。(サンプルなので若干意図的な文章にしていますが)
「ずっと胃がキリキリと痛い。ただ、熱は無く平熱のままだ。」
「昨日からとても頭が痛い。おまけに胃がむかむかする。」

例として「胃が痛い」という文章で単純な単語検索と今回作成した検索を比べてみましょう。

単語検索の場合
クエリはquery_stringクエリを使用して、contentフィールドをします。

GET content/_search
{
  "query": {
    "query_string": {
      "default_field": "content",
      "query": "胃が痛い",
      "analyzer": "sudachi_analyzer"
    }
  }
}

結果としては、2件ともヒットします。
ただ、2文目のほうは検索意図からは外れているので、結果としてほしいものではありません。

次に今回作成した検索の結果を見てみましょう。
結果

{
    "hits": [
        {
            "_id": "DDzRwW4BGThQze84g2Hm",
            "_index": "content",
            "_score": 1.3560746,
            "_source": {
                "content": "ずっと胃がキリキリと痛い。ただ、熱は無く平熱のままだ。",
                "subjects": [
                    "胃:痛い",
                    "熱:侭"
                ]
            },
            "_type": "_doc"
        }
    ],
    "max_score": 1.3560746,
    "total": {
        "relation": "eq",
        "value": 1
    }
}

今回は一つ目の文書、つまり結果としてほしい結果のみがヒットしています。
このように、GiNZAの依存構造解析と検索エンジンと組み合わせることで、より意味的に欲しいものを検索することができます。

まとめ

GiNZAとElasticsearchを使って、簡易的にですが、係り受け検索を実現してみました。
GiNZAを使うことで、少ないコードで簡単に実現することができるので本当に便利だと思います。
今回は主語述語の関係のみに注目しましたが、他にも形容詞や否定語など考慮することは多くあるので次回への課題としようと思います。
皆さんもぜひGiNZAで日本語処理ライフを。

明日はg-kさんの記事になります。
それではまた。


Acroquest Technologyでは、キャリア採用を行っています。


  • ディープラーニング等を使った自然言語/画像/音声/動画解析の研究開発
  • Elasticsearch等を使ったデータ収集/分析/可視化
  • マイクロサービス、DevOps、最新のOSSを利用する開発プロジェクト
  • 書籍・雑誌等の執筆や、社内外での技術の発信・共有によるエンジニアとしての成長

 
少しでも上記に興味を持たれた方は、是非以下のページをご覧ください。

Kaggle Masterと働きたい尖ったエンジニアWanted! - Acroquest Technology株式会社のエンジニアの求人 - Wantedlywww.wantedly.com

Elastic Stack 7.5 リリース、注目の Kibana Lens が新登場!

こんにちは、@shin0higuchiです😊

本日Elastic Stackの7.5がリリースされました。
今回は個人的に興味をひかれた新機能に絞って紹介します。
どんな機能が追加されたのか、早速見てみましょう。

リリースノートはこちら
https://www.elastic.co/guide/en/elasticsearch/reference/current/release-notes-7.5.0.htmlwww.elastic.co

Kibana Lens

Kibanaのvisualizationをより感覚的に作成するための機能、Lensがbetaリリースされました。
データをドラッグ&ドロップで追加しながら、グラフの種別切り替えも簡単です。
これは今回の目玉機能と言えると思います。

f:id:acro-engineer:20191203030132p:plain:w600
Kibana Lens画面

画面左のフィールド一覧から、画面右のエリアにドラッグすることで作成するようなUIになっています。

f:id:acro-engineer:20191203030441p:plain:w600
Kibana Lens画面

種別を切り替えた時に、どのような見た目になるのかも一覧でき、Kibanaの操作に慣れていないユーザーでも簡単に利用できそうです。

data frame analysisにclassificationが追加

classificationおよび、その評価を行うevaluation APIが実装されました。
※今の所、Elasticsearch側のAPIは実装されていますが、Kibanaの画面から指定できるのはoutlier_detectionとregressionのみのようです。

簡単に試すために、titanicのデータセットを取り込みます。

f:id:acro-engineer:20191203032038p:plain:w600
csv import
f:id:acro-engineer:20191203032157p:plain:w600
取り込み完了

data frame analyticsのジョブを作成します。
titanic-dataを分析し、結果はtitanic-data-predictionに入ります。
今回のポイントは、analysisの中に"classification"を指定できるようになった点です。

PUT _ml/data_frame/analytics/titanic-data
{
  "source": {
    "index": "titanic-data" 
  },
  "dest": {
    "index": "titanic-data-prediction" 
  },
  "analysis":
    {
      "classification": { 
        "dependent_variable": "Survived",
        "num_top_classes": 2,
        "training_percent":80
      }
    }
}


ジョブをスタートし...

POST _ml/data_frame/analytics/titanic-data/_start

trainingに利用されたデータを省いて結果を取得します。

GET titanic-data-prediction/_search
{
  "query": {
    "bool": {
      "filter": {
        "term": {
          "ml.is_training": false
        }
      }
    }
  }
}

Hitsは次のようになります。
ml以下に分析結果が格納されていることがわかります。

{
        "_index" : "titanic-data-prediction",
        "_type" : "_doc",
        "_id" : "07LNx24BGrho6pjYL9EM",
        "_score" : 0.0,
        "_source" : {
          "Survived" : 0,
          "Pclass" : 1,
          "Siblings/Spouses Aboard" : 0,
          "Parents/Children Aboard" : 0,
          "Sex" : "male",
          "ml__id_copy" : "07LNx24BGrho6pjYL9EM",
          "Age" : 64,
          "Name" : "Mr. Arthur Ernest Nicholson",
          "Fare" : 26,
          "ml" : {
            "top_classes" : [
              {
                "class_probability" : 0.7037370235841935,
                "class_name" : "0"
              },
              {
                "class_probability" : 0.2962629764158065,
                "class_name" : "1"
              }
            ],
            "Survived_prediction" : "0",
            "is_training" : false
          }
        }
      }

evaluate APIについても試してみます。

POST _ml/data_frame/_evaluate
{
   "index": "titanic-data-prediction",
   "evaluation": {
      "classification": { 
         "actual_field": "Survived", 
         "predicted_field": "ml.Survived_prediction.keyword", 
         "metrics": {
           "multiclass_confusion_matrix" : {} 
         }
      }
   }
}

評価結果は下記の通り。

{
  "classification" : {
    "multiclass_confusion_matrix" : {
      "confusion_matrix" : [
        {
          "actual_class" : "0",
          "actual_class_doc_count" : 545,
          "predicted_classes" : [
            {
              "predicted_class" : "0",
              "count" : 483
            },
            {
              "predicted_class" : "1",
              "count" : 62
            }
          ],
          "other_predicted_class_doc_count" : 0
        },
        {
          "actual_class" : "1",
          "actual_class_doc_count" : 342,
          "predicted_classes" : [
            {
              "predicted_class" : "0",
              "count" : 71
            },
            {
              "predicted_class" : "1",
              "count" : 271
            }
          ],
          "other_predicted_class_doc_count" : 0
        }
      ],
      "other_actual_class_count" : 0
    }
  }
}

ちなみに、リリースノートでmulti classと書いてありましたが、下記のドキュメントによると、現状は2クラス分類のみがサポートされているようです。
Data frame analytics job resources | Elasticsearch Reference [7.5] | Elastic

今後の更新が楽しみな機能の一つですね。

enrich processor

ingest pipelineにenrich processorが追加されました。
このprocessorは、他のindexにあるdataを使って、情報を付加することが可能です。
logstashでのtranslate filterに近いものですね。

他のprocessorに比べると使い方が複雑になっています。
事前にenrich policyというenrich設定を登録しておき、processor側でそれを呼び出す形になります。

次のenrich policyは公式ドキュメントの例ですが、emailフィールドでマッチしたドキュメントの、["first_name", "last_name", "city", "zip", "state"] フィールドを取得する設定になります。

PUT /_enrich/policy/users-policy
{
    "match": {
        "indices": "users",
        "match_field": "email",
        "enrich_fields": ["first_name", "last_name", "city", "zip", "state"]
    }
}


processorは下記のように指定します。

"processors" : [
    {
      "enrich" : {
        "policy_name": "users-policy",
        "field" : "email",
        "target_field": "user",
        "max_matches": "1"
      }
    }
  ]

個人的に割と欲しかった機能なので、非常に嬉しいです😊

詳細については、
Enrich your data | Elasticsearch Reference [7.5] | Elastic
をご参照ください。

SLM(Snapshot Life cycle Management)でsnapshotの管理が可能に

Kibanaの画面から、snapshotの自動取得および削除を設定できるようになりました。
Managementのタブから設定することができます。

f:id:acro-engineer:20191203113938j:plain:w600
SLM

まとめ

今回も大きな機能追加がいくつもありました。
ここで扱わなかったものも数多くあります。詳しくはリリースノートをご覧ください。
お読みいただきありがとうございました。

Acroquest Technologyでは、キャリア採用を行っています。


  • ディープラーニング等を使った自然言語/画像/音声/動画解析の研究開発
  • Elasticsearch等を使ったデータ収集/分析/可視化
  • マイクロサービス、DevOps、最新のOSSを利用する開発プロジェクト
  • 書籍・雑誌等の執筆や、社内外での技術の発信・共有によるエンジニアとしての成長

 
少しでも上記に興味を持たれた方は、是非以下のページをご覧ください。

世界初のElastic認定エンジニアと一緒に働きたい人Wanted! - Acroquest Technology株式会社のエンジニアの求人 - Wantedlywww.wantedly.com

SORACOM S+ Cameraであんパンの品切れをチェックしてみた

機械学習エンジニアのhayakawaです。
弊社には社内で菓子パンを扱うミニ販売コーナーがございます。
私は好物のあんパンをよく買うのですが、他の社員にも人気があるのですぐ売り切れます。早い。
あんパンを多めに仕入れてもらいたいのですが、
欠品よりも売れ残りを嫌うパン購入担当社員を説得するには、
パンにかける熱意よりも、パンがいつ売り切れたかの統計が必要です。たぶん。

そこで
SORACOM S+ Camera(サープラスカメラ) (以下、S+ Camera)
という製品を使って簡単な在庫管理システムを組んでみました。
※弊社はS+ CameraのAIパートナーです。

f:id:acro-engineer:20191119110256g:plain f:id:acro-engineer:20191119140643j:plain

S+ Cameraとは

S+ Cameraはソラコム社のエッジ処理カメラです。

soracom.jp

この筐体に

が入っています。
これにプログラムをデプロイすると、
電源を入れると撮影した画像を処理してSORACOM プラットフォームに送ったり、
何か別の通信(アラートとか)をすることができます。
カメラ向けにプログラムをデプロイするのもプラットフォーム経由でできます。

構成

構成、というほどの内容もないですが、こんな感じです。

f:id:acro-engineer:20191121181552j:plain

一番上の
Torrentio Videoは、弊社の画像・映像解析AIソリューションです。
www.site.torrentio.tech
特定の物体検出などを、学習モデルの作成から推論まで、簡単に実現できるようになっています。

カメラ内にはRaspberry Piが入っており、
そこにはインストールされる"SORACOM Mosaic(プライベートベータ)"というエッジプロセッシングサービス上で
アプリケーションを動かすことになります。

流れとしては次のようになります。

  1. S+ Cameraがカメラで撮影
  2. Torrentio Videoカメラ画像を取得
  3. Torrentio Videoが画像内からパンの欠品を検知
  4. 欠品していたらSORACOMのデータ通信越しに通知

プログラム

まずデータサイエンスの基本作業、アノテーションのお時間です。
今回やりたいのは物体検知のタスクなので、画像内からパンの位置を長方形で指定する作業をひたすらにやります。
f:id:acro-engineer:20191122145917j:plain

だんだんおなかがすいてきました。夕食前にやる作業ではない。

それが終わったらRaspberry Piでも動作可能な軽量モデルで転移学習。
プログラムから呼べるようにします。

また弊社は社内チャットにMS Teamsを採用していますので、
このプログラムの通知もMS Teamsに投げるようにしました。

f:id:acro-engineer:20191121194859j:plain

最初、投稿を5分間隔にしたのですが、
「あんパンが欠品しています」「あんパンが欠品しています」「あんパン…」「あ…」
と1時間に10回以上のペースで延々とつぶやき続けることになってしまいました。呪いか。
作りたいのは在庫管理システムでありスパムBotではないので、
在庫に変化があったときだけ投稿するように改修しました。

物理

f:id:acro-engineer:20191119140351j:plain

電源ケーブルをつなげばとりあえず動きます。
現在はトライアルパッケージ提供のため、
SORACOM ユーザーコンソールの方でSIMカードを登録する必要がありますが、
それさえ済んでいればデータ通信の準備が整います。

筐体が横倒しになっているのは、パンの棚が縦長なのに対して、カメラの撮影画像が横長だったからです。雑ですね。
なお取り付け台は、会社の倉庫にあったUSBディスプレイを載せる台にブックエンドをワイヤーでしばりつけた物です。雑ですね。

結果

で、冒頭のGIFのようになりました。

f:id:acro-engineer:20191119110256g:plain


下のキャプションは、実際はMS Teamsに投稿されている内容です。
毎回、売り切れたものすべてがメッセージに載る仕様です。

なお、判定処理は正面からパンの有無だけを見るようにしました。
本来は在庫管理ですので残り個数をカウントしたかったのですが、
現状の社内のパンのキャビネですとパンと上の棚の隙間が狭すぎて上からの撮影できず、
今回はカウントはあきらめました。

実際のMS Teamsへの投稿はこんな感じです。

f:id:acro-engineer:20191119131404j:plain

MS TeamsはWebhookをかんたんに構成できるので、楽に接続できました。
あとはこのチャネルをパン購入担当にフォローしてもらえば、売り切れ状況をすぐ把握してもらえます。
良い時代になりましたね。

感想

エッジ機器+カメラ+データ通信が1つの筐体に収まっているので、
電源さえつながれば場所を選ばないので便利です。

また、SORACOMプラットフォーム経由でS+ Cameraにデプロイする仕組みがあるので、
TorrentioVideoで学習したモデルを、遠くの機器ですぐ試せるのは良いと思いました。

今回は弊社のTorrentioVideoをインストールしましたが、
何をさせるかは自由に入れ替えられるので、
「会議室に予約時間を越えて居座っている社員を警告音で追い出すシステム」
「御手洗いの清掃中の札が消えたら教えてくれるシステム」
など、ニッチすぎて市販されていない用途のものでも気軽に自作できそうです。

色々と使ってみましょう。

Acroquest Technologyでは、キャリア採用を行っています。

  • ディープラーニング等を使った自然言語/画像/音声/動画解析の研究開発
  • Elasticsearch等を使ったデータ収集/分析/可視化
  • マイクロサービス、DevOps、最新のOSSを利用する開発プロジェクト
  • 書籍・雑誌等の執筆や、社内外での技術の発信・共有によるエンジニアとしての成長

 
少しでも上記に興味を持たれた方は、是非以下のページをご覧ください。

Kaggle Masterと働きたい尖ったエンジニアWanted! - Acroquest Technology株式会社のエンジニアの求人 - Wantedlywww.wantedly.com

JJUG CCC 2019 Fallに登壇しました

こんにちは、しんどーです。
最近、トロとパズルにハマっています。この前覚えさせた言葉は「rm -rf /」です。

さて、先日11/23に行われたJJUG CCC 2019 Fallに登壇してきました。

JJUG CCCは日本Javaユーザグループ主催のカンファレンスで、参加者は登録数で1200名を超えたそうです。 自分は参加は9回目で、そのうち登壇は3回目になります。

資料

https://speakerdeck.com/rshindo/jjug-ccc-2019-fall

内容について

今回Java初心者を対象とした「ステップアップセッション」枠でソースコードリーディングについて話しました。

自分も1、2年目のころはOSSのコードを読むのにハードルを感じていて、同じ感覚を持っている人が結構いるんじゃないかなと思ったのがこのネタにした理由です。また、自分の中でコードを読むときに重要視していることを言語化したかったというのもあります。

裏話

ヒイヒイ言いながら資料頑張って作ったところ作り過ぎてしまい、発表では時間が足りなくなり一部カットになってしまいました。聞きに来ていただいた皆さん、すみませんでした。事前練習の繰り返しホント大事ですね!

今回取り上げた2つのライブラリ「Commons DBUtils」と「Javalin」を選んだ理由ですが、

  • コードが大きくない
  • 抽象化が控えめ = 読むのが難しくない
  • DBアクセス、Webといった多くのひとになじみのある領域

といった大体この3点です。

実際、これらのライブラリは機能としてはプリミティブですが、逆を言えばMyBatisやSpring MVCといった高機能なライブラリがコアでやっていることを抑えているとも言えます。大きなライブラリを読むための肩慣らしとしては最適かなーと思っています。ただDBUtilsを知ってる人が皆無だったのは誤算でしたw

f:id:acro-engineer:20191125101347j:plain
満員御礼!

まとめ

次回予告

12/18(水)に行われるSpring Fest 2019で登壇します!

springfest2019.springframework.jp

Spring Boot ActuatorとMicrometerでの運用監視についてお話しします。ぜひ来てね!

Acroquest Technologyでは、キャリア採用を行っています。

  • ディープラーニング等を使った自然言語/画像/音声/動画解析の研究開発
  • Elasticsearch等を使ったデータ収集/分析/可視化
  • マイクロサービス、DevOps、最新のOSSを利用する開発プロジェクト
  • 書籍・雑誌等の執筆や、社内外での技術の発信・共有によるエンジニアとしての成長

 
少しでも上記に興味を持たれた方は、是非以下のページをご覧ください。
Java/PythonでモダンなWeb開発をしたいエンジニアWanted! - Wantedlywww.wantedly.com

ServerlessDays Tokyo 2019参加記

こんにちは、新人エンジニアの@yktm31です。
先日開催された、ServerlessDays Tokyo 2019に参加したのですが、レベルもボリュームも最高だったのでレポートをしたいと思います!

ServerlessDaysは、サーバーレスを使って得た知見や経験を共有するカンファレンスで、
10/21(Sun)と10/22(Mon)の2日間に渡り開催されました。
初日はハンズオン/ワークショップ、2日目はカンファレンスという日程で、私は両日参加しました。

会場の様子はこんな感じでした。
f:id:acro-engineer:20191118172113p:plain

1日目

Microsoftさんによるワークショップと、@toshi0607さんによる「Knativeで作るDIY FaaS」というワークショップを受けました。

1. Microsoftさんによるワークショップ

2コースに別れていて、一つはテキストに従って進めるハンズオン形式、もう一つはチームで課題をクリアするワークショップ形式がありました。
私はその中のワークショップを選びました。

コンテンツとしては、サーバレスなアプリケーションを構築する上で主に利用するサービスを一気に学べる、充実した内容でした。
Azure Functionsのみならず、Cosmos DB・Logic App・Application Insightsを組み合わせて実際的に作るという点が面白かったです。
個人的には、Azure Functionsのデータバインディングなど、実践的な使い方も学ぶことができたのがよかったです。

当日の資料は以下のリンクから飛べます。
github.com

2. @toshi0607による「Knativeで作るDIY FaaS」

GCP上でk8sクラスタを作成し、Lambdaっぽいことができるプラットフォームを構築しました。
丁寧かつわかりやすい資料(下記リンク)に非常に助けられて、k8s初心者の私でも無事構築できました。
github.com

2日目

2日目はカンファレンス、一つ一つのセッションが先進的かつプラクティカルな知見の塊で、非常に濃密でした。
この記事では、その中でも特に、印象に残ったものを取り上げて紹介したいと思います。
当日の様子は、togetterも参考になると思います。
togetter.com


1. 10x Serverless Product Development for a Startup with Microsoft Azure


EBILABさんは、伊勢の老舗食堂のゑびやさんが立ち上げた、データ活用のためのスタートアップだそうです。
EBILABさんがサーバレスアーキテクチャを採択し、10倍の業務効率改善をしたという話でした。

学習コストが低いPython × Azure Functionでシステムを構成。
協力会社に依頼できる部分は依頼し、エンジニアが本当に必要な箇所の設計・開発に注力できるようにしているそうです。
Pythonであればローコンテキストでコーディングができ、サーバレスだと責務分離がしやすいので、協力会社に作業を渡しやすいとのことでした。

その結果、3,4人ほどの小規模なチームで開発を高速に回せるようになり、
事業を進めるうえで「なるべくエンジニアがボトルネックにならないように」を実現しているそうです。
スタートアップという形にも沿うような、システムづくりやサービス形態を実現できるのはサーバレスのいいところだと感じました。

2. Keynote: Infinite Scaling, Finite Failures: Serverless Resiliency Patterns and Lessons Learned

AzureのDurable Functions 2.0(プレビュー)を使えば簡単に障害回復を実現できるという話でした。
サーバレスシステムでは、データソース、サードパーティAPI、ネットワークなど
依存関係がある箇所どこでも障害が起こり得ます。

サーバレスで障害が起きたとき、回復の手段として多く取られる手法はRetry(再試行)です。
しかし、再試行ではうまく行かないケース、(例えば、削除処理がある関数実行中に障害が起きるなど)があります。

一つ一つの関数がidempotency(冪等性)を担保するために、サーキットブレーカーパターンと言うものがあり、
closed/open/half-open という状態を持って、関数の稼働を制御するそうです。

サーキットブレーカ自体の実装は大変ですが、AzureのDurable Functions 2.0(プレビュー)を使えば簡単に実現できるとのことでした。
今はプレビュー段階ですが、是非触ってみたい機能ですね。

3. グローバル展開のコネクティッドカーを支える大規模サーバーレスシステム事例

実は、弊社マネージャの鈴木 貴典(@takanorig)が、トヨタさん・AWSさんと一緒に登壇していました!
(このセッションを聞くことが目的の一つでもありましたw)

発表されていた内容は、「コネクティッドカー」として、
車と、ドライバーや各種サービスとを繋ぐシステムを、サーバレスで構築した事例の話でした。

サーバレスを用いた理由として、徹底的にムダを削除し、価値ある仕事にコミットするため、というのが印象に残っています。

・多くの車が動くのは日中の時間帯であり、昼と夜とでアクセス数の上下が激しい
・購入された車は数年以上乗り続けられるため、それに合わせたサービスの保守が発生する
といった、「車ならではの特性」を踏まえ、サーバレスを利用してリソースの最適化と運用コストの削減を実現。

運用コストが減った分、新規サービス開発や既存サービスの改善に時間をさけられるようになったようです。

また、大規模サーバレスシステムのリアルな構成・運用の話もあり、かなり実践的な内容だったと思います。

今回、トヨタさんのシステムが発表されるだけでもレアなケースとのこと。
業務的にかなりリアルな内容を含んでいるため、詳細については残念ながらオフレコになっています。
togetterで雰囲気を感じてもらえたらと思います!

4. 空調設備向けIoTシステムにおけるクラウドランニングコスト

空調同士をインターネットに繋いでIoT化・プラットフォーム化するというプロジェクトについての話でした。

システム規模は500万台、1min毎にデータ発生し、同時アクセス9万人という大規模IoTデータを扱うにあたり、
IoT×サーバレスでは、リクエストの数によって青天井にスケールしてしまう可能性がある。
その問題に対処すべく、DynamoDB設計の試行錯誤が、失敗例も含め具体的に発表されていました。
資料はこちらにアップされています。

サーバレスも一つ使い方を誤ると、膨大なコストにつながってしまうことがわかりました。
明日は我が身として、サーバレスを使うときのコストが最小になるような設計が必要だと感じました。

まとめ

今回、色々な企業で実際に利用されている事例が多く発表されていました。
その中でも印象に残ったことは、サーバーレスなシステムにより、無駄がなくなり、サービスの本質的な価値創造によりコミットできるようになるということです。

個人的には、サーバーレスの目指す姿は、
弊社が目指す「人は『人にしかできないこと』にリソースを使うビジネスの世界」に近いと感じました。
そういったシステムの開発やサービスの提供に、自分も携わっていけたら、エンジニア冥利に尽きると感じます。

これだけ刺激的な内容のカンファレンスを準備していただいた運営スタッフの皆さんには感謝です。
また来年も参加したいと思いました!

Acroquest Technologyでは、キャリア採用を行っています。


  • ディープラーニング等を使った自然言語/画像/音声/動画解析の研究開発
  • Elasticsearch等を使ったデータ収集/分析/可視化
  • マイクロサービス、DevOps、最新のOSSを利用する開発プロジェクト
  • 書籍・雑誌等の執筆や、社内外での技術の発信・共有によるエンジニアとしての成長

大阪勤務、Pythonでサーバレスアーキテクチャを採用した開発メンバ募集! - Acroquest Technology株式会社のWeb エンジニア中途・新卒の求人 - Wantedlywww.wantedly.com


また、弊社エンジニアのインタビュー記事や、カルチャーについての記事も公開しています。
是非ご一読ください!
・最強の検索エンジンこそが、未来を切り拓く鍵 ~3年目・若きデータサイエンティストの挑戦~
https://www.wantedly.com/companies/acroquest/post_articles/180008
・働きがい全国1位企業に根付く文化 ~全社員で会社を創り上げる~
https://www.wantedly.com/companies/acroquest/post_articles/189283