Technology Topics by Brains

ブレインズテクノロジーの研究開発機関「未来工場」で働くエンジニアが、先端オープン技術、機械学習×データ分析(異常検知、予兆検知)に関する取組みをご紹介します。

PowerShellDSCでハマったポイント

すっかり寒くなってきました。
こんにちは、ブレインズテクノロジーの白石です。

今回、業務の中でPowerShellDSCに触れることがありましたので、それについて得た知見を書きます。
というのも、PowerShellDSCの情報ってあんまり載ってないので。。。
こういうやり方あるよ、こういうの知ってるよ、ていうのがあれば是非是非コメントお願いします。

ちなみに、使用するPowerShellのバージョンは5.0以上ですが、一部5.0未満が対象の話題もあります。
特に理由がなければ5.0に上げることをオススメします。

PowerShellDSCとは

PowerShellDSCとは、Windows専用の構成自動化ツールです。
詳しい紹介はこちら。
www.atmarkit.co.jp

Configuration, Node, Resource, ConfigurationData あたりが理解できてると以降の内容は読みやすくなると思います。

管理マシンと構成対象のマシンでPSのバージョンは揃える

そりゃそうだ、という話ですが、直面すると意外に気づきません。
バージョンが異なるとモジュールの配置場所が違ったり仕様が異なったりしていて、あるマシンではうまく動くのに別のマシンでは全く動かない、という奇妙なバグに悩まされます。(体験談)

使用してるマシンのバージョンは $PSVersionTable で確認できるので、DSCで構築を行う前に対象のマシンと管理マシンのバージョンが揃っているか確認しておきましょう。

外部のモジュールは構築対象のマシンにも入れておく

これも言われれば、そりゃそうなんやろね、という感じですが。

PowerShellを使ってると、こういう機能ないの?と色々欲が出ます。そうした場合、同じように考えた人がやはりいて、大抵の場合はライブラリとして提供されているわけです。
そういったライブラリは Install-Module xxx とかで取り込んでいくのですが、
それらは取り込んだマシンでしか使えません。つまりローカルテストでは動きますが、リモートマシンで同じスクリプトを動かそうとすると、そのマシンには対応するライブラリがインストールされてないのでエラーになります。

この辺りは、そのモジュールをインストールするPSスクリプトを書いてリモートで適用するなり、
該当するライブラリをリモートで配置するなりして対応しましょう。

リソースは一意性がある

まず、「リソース」とはなんぞや。
「リソース」とは、PowerShellDSCの用語の1つで、マシンに適用する状態や操作のことだと思ってください。
プログラムを経験してる方には関数といえば何となく伝わるでしょうか。
例えば、フォルダを作る、ファイルを消す、Zipを展開する、環境変数を定める、などなど。

で、このリソースというもの。構成の中で一意性を保証する必要があり、そのためにキーがあります。
なんのこっちゃという感じなのでとりあえず例を。

File CreateDirectory {
  Ensure='Present'
  Type='Directory'
  DestinationPath='C:\sample'
}

これはディレクトリを作るリソース(関数)で、リソース名(=CreateDirectory)がキーとなっております。
もちろん動きます。

さて、もう1個ディレクトリ作りたいなーと思い、このリソースを使い回すことを考えたとします。

File CreateDirectory {
  Ensure='Present'
  Type='Directory'
  DestinationPath='C:\sample'
}

File CreateDirectory {
  Ensure='Present'
  Type='Directory'
  DestinationPath='C:\sample2'
}

エラーになります。2回呼び出しただけなのに、どういうことなのか。
先ほど上でリソースにはリソース名がキーとなっていると申し上げました。
つまり、リソース名(例だとCreateDirectory)は適用する構成全体の中でユニークである必要があるのです。
なのに同じリソース名を使ったため、一意性が失われてエラーになりました。
(ちなみに、リソースが違えばリソース名が同じでも大丈夫です。あくまで同じ種類のリソースで一意ということです。)

なるほど。というわけで、例えば以下のようにして解決します。

$destinationArray=@('C:\sample', 'C:\sample2')
foreach($path in $destinationArray){
 File (CreateDirectory + $path) {
   Ensure='Present'
   Type='Directory'
   DestinationPath=$path
 }
}

リソース名に変数をつけてやれば、リソース名の一意性は保たれます。
foreachとかで回したい時のちょっとしたテクニックです。
関数の感覚で使うと、この仕様はちょっと面食らいます。

キーはリソース名だけじゃない

上の話題は序の口です。
今度は例として、環境変数HOGEを作って削除するという何の意味もない操作を作ることにしましょう。

Environment CreateHoge {
 Ensure='Present'
 Name='HOGE'
 Value='C:\hoge'
}
Environment RemoveHoge {
 Ensure='Absent'
 Name='HOGE'
}

リソース名は別にしたのに怒られます。なぜなのか。
操作をよく見ると、HOGEという環境変数を「存在する」「存在しない」ようにする操作が混在してますね。
このEnvironmentというリソースは、環境変数名であるNameパラメータもキーとなっているため、リソース名だけでなく、Nameもユニークである必要があるのです。
この辺りは各リソースの仕様に依存しているので、設定を見るなり、エラーにぶつかりながら学ぶしかありません。

ちなみに、これを解決するにはScriptリソースを使って無理やり書くしかありません。
もしくはConfigurationそのものを分けるか。


そもそもこういう操作をDSCに求めるのが間違っている、ということだと私は思ってます(それでもやるときはやる)。

Scriptリソース内だと変数展開のタイミングが違う(powershell 5.0未満限定)

まだあるのか!!あるんです。ただし、PowerShell 5.0未満を使っている人だけです。
ここまででリソースにはキーがあり、それはリソース名だけでなく指定するパラメータにもあることを紹介しました。
そして、その関連で最後にハマるであろう箇所がScriptリソースを使用した時です。

本題の前にScriptリソースとはなんぞやという話をしておきましょう。

通常、FileリソースやEnvironmentリソースなど、予め用意されたリソースに所定のパラメータを渡すことによって目的とする操作を行うことができます。
ただ、そうした操作が用意されていない場合、もしくは、もっと細かい操作を書きたい、となった場合、
Scriptリソースを使います。
これ、中の記述で行いたい操作のPowerShellスクリプトを直接書いてしまいます。
詰まる所Scriptリソースさえあれば力づくで何だってできるわけです。
(これをやりだすとべき等性とかどうでもよくなってくる。。。)

話を元に戻します。
Scriptを使って複数のディレクトリを作る処理を書いてみます。
(通常はFileリソース使いますが、説明のためScriptで書きます)

foreach($path in @('C:\test1', 'C:\test2')){
 Script $path {
  GetScript={
   return $null;
  }
  TestScript={
   return (Test-Path $using:path);
  }
  SetScript={
   New-Item $using:path -ItemType Directory;
  }
 }
}

$using:pathとは、Scriptの外から変数を渡す際につける接頭語です。$pathでは解釈されません。
一見うまくいきそうです。少なくとも、Fileリソースで作っていた場合は問題ありませんでした。

これを実行すると以下のようなエラーが出ます。

Add-NodeKeys : キー プロパティの組み合わせ ' (中略) ' が、ノード 'localhost' 内のリソース 'Script' のキー 'GetScript,SetScript,TestScript' で重複しています
。キー プロパティは、ノード内の各リソースで一意になるようにしてください。

なんやそれ。。。
これは、Scriptリソース内で指定した$using:変数の展開がリソースのキーチェックの後に行われるため、キーが重複したと判断されているからです。
これをどう解決すればいいのかというと、一つはPowerShellのバージョンを5.0に上げてしまうことです。
5.0ではmofファイル生成時に$usingで使用している変数について、スクリプトの先頭に代入文が挿入されて解決されるようになっています。
何かしら事情があって5.0へ上げられない人はどうするのか。
安心してほしい。実はこれを解決した先人がいるのです。
www.briantist.com

リンク先にある関数を宣言して、GetScript, TestScript, SetScriptの後ろに " | Replace-Using " と書くと、キーの評価前に変数が展開され、エラーを回避できます。素晴らしい。

コンパイル時の変数展開はコンパイルしたマシン上で行われる

話を変えて、変数の展開、特に環境変数についての話をします。
表題だけ見ると、何言うてんねん、と言いたくなりますが、私はハマってしまいました。
PowerShellスクリプトにおいて、環境変数へは$env:変数名でアクセスすることができます。

で、これは実際に私が書いたコードですが、JAVA_HOME環境変数がnullかどうかでJavaのインストールを行う処理を書いてました。
(パッケージのインストールについてはcChoco(ChocolateyのDSC版)を使うと楽なのですが、当時はライセンス明記が不明瞭だったので使用を避けてました。今はApache v2ライセンスになってるので問題なさそうです。)

if($env:JAVA_HOME -ne null){
 Script InstallJava{
  (Javaインストール処理)
 }
}

ローカルでテストしたら、うまく動いてるようです。
ですが、リモートで別のマシンに適用するとうまく動きません。
ん? そう思ったらうまく動いてる時もあります。一体何がどうなっているのか。

実は、$env:JAVA_HOMEの評価はこのスクリプトコンパイルした際のマシンの値を参照しているのです。
PowerShellDSCでConfigurationファイルをコンパイルする際、変数値は展開され、適用するマシンそれぞれに対応したmofファイルが作られます。
この時、マシン内の情報を参照するような変数を扱った場合、それらはコンパイルしたマシンの情報が適用されてしまいます。

つまり、適用したいマシンのJAVA_HOMEをチェックして書いたつもりが、実はそれはコンパイルを行なったマシンのJAVA_HOMEをチェックしているだけで、適用したいリモートマシンの情報は見れていないのです。

こういう場合、Invoke-Commandなどで事前に情報を取っておいて、
ConfigurationDataに情報を載せるのが良いと思います。
(ただし、結構コードがゴチャゴチャします。もっと楽な方法があるといいのですが・・・。)

ConfigurationDataのNode ‘*’ と、それ以外のNodeのKeyValueについて

PowerShellDSCにはConfigurationDataという、マシンの名前やパラメータの値を書いておく設定ファイルを用意できます。
この時、NodeName=‘*’ とした部分のKeyValueは全ノードに適用されます。

これは、デフォルト値としても利用出来ます。

AllNodes=@(
@{
NodeName=‘*’
Something=‘default’
},
@{
NodeName=‘localhost
Something=‘custom’
}
)

こう書けば、NodeName=‘localhost’の時のSomethingの値はcustomとなっていて、他のノードではデフォルト値としてdefaultが入ります。
ここで注意することは、これがKeyValueの上書きであるということです。
特に、構造体に対して適用する時は本当に注意しましょう。

AllNodes=@(
@{
NodeName=‘*’
Something=@{
arg0=‘sample00.txt’
arg1=‘sample01.txt'
}
},
@{
NodeName=‘localhost
Something=@{
arg0=‘extra.txt’
arg2=‘sample02.txt'
}
}
)

こう書いた場合、localhost のノードでは、Something.arg0の値は上書きされています。
そして、arg2が定義されてるのでそれにもアクセスできるようになります。

ただ、arg1は$null値となります。なぜなら、KeyValueが上書きされてarg1は未定義となるからです。
(PowerShellでは定義されていないKeyにアクセスすると $null が返されます。)
これがバグに繋がっていなければいいですが、いかんせんコンパイル時にエラーにならないのが怖いところです。

マージや継承のような感覚では書けないので注意です。

ConfigurationDataをJSONで書く

上で述べたConfigurationDataですが、
これはコード内に書いて宣言することもできますし、外部ファイルに書いておいて読み出すことも可能です。

いずれにしてもこの設定ファイル、記述がJSONによく似ています。
いっそのことJSONで管理してしまいたい。

JSONを扱うためのCmdletとして、ConvertFrom-JSONが用意されてます。
使い方は以下が詳しいです。
tech.guitarrapc.com


ただし、ConvertFrom-Jsonで読み込んだ場合、その変数の型は PSObject です。
PowerShellDSCでコンパイル時に渡しているConfigurationDataの型は HashTable なので、PSObjectからHashTableに変換する必要があります。
下記リンクを参考に関数を用意。
Topic: Configuration Data for DSC not in JSON

function ConvertPSObjectToHashtable
{
 param (
  [Parameter(ValueFromPipeline)]
  $InputObject
 )

 process
 {
  if ($null -eq $InputObject) { return $null }
  if ($InputObject -is [System.Collections.IEnumerable] -and $InputObject -isnot [string]){
   $collection = @(
    foreach ($object in $InputObject) { ConvertPSObjectToHashtable $object }
   )
   Write-Output -NoEnumerate $collection
  } elseif ($InputObject -is [psobject]){
   $hash = @{}

   foreach ($property in $InputObject.PSObject.Properties){
    $hash[$property.Name] = ConvertPSObjectToHashtable $property.Value
   }
   $hash
  } else {
   $InputObject
  }
 }
}

変換して実行する部分はこんな感じ。$jsonFileはもちろん読み込みたいJSONファイル名

$tmpObject = cat $jsonFile | convertFrom-Json
$hash = $tmpObject | ConvertPSObjectToHashtable

Sample -ConfigurationData $hash
Start-DscConfiguration .\Sample -Wait -Force

PowerShell5.0の便利機能

こちらが大変詳しい。
www.buildinsider.net

詳しすぎて特にここで言うこともないのですが、それでもこれだけは強調しておきます。

Get-DscConfigurationStatus超便利

上に貼ったリンク先でも特にオススメされてる機能ですが、本当に助かります。
PowerShellはうまくいかなかった時、「うまくいきませんでした。」くらいの内容しか返してくれなくて(大げさですが)、調査に時間を取られるのですが、このコマンドを打てばどのリソースが成功してどのリソースが失敗したのかが一発でわかります。

特に複数のConfigurationを適用するときは、合間にこのコマンドを打ってログとして残しておくことでどこまで進んだのかわかりますし、
条件分岐をしていた場合にもどのリソースが実行されたかが記録として残されるので、テストにもってこいです。

終わり

PowerShellDSCはあまり情報が載ってなくて、さらにはエラー内容も比較的不親切なこともあって、結構苦戦しました。
1個1個、解決した内容はメモして再発させないことを心がけると幸せになれると思います。

そして、できればその内容を共有をしていただけると、同じように悩む人も幸せになれるので、是非是非お願いいたします。

Pythonによる時系列データの異常検知

インターン生の松井(B4)です.時系列データの異常検知手法をまとめました.

入門 機械学習による異常検知という本の7章が時系列データの異常検知を扱っています.(本書の内容をまとめたWeb記事もあります.)
www.coronasha.co.jp
この本のサンプルコードはすべてRで書かれているため,Python (+numpy, scikit-learn) で書き直してみました.

後半では,深層学習を用いた時系列データの異常検知手法について,知られている所をまとめました.

k近傍法による異常部位検出

時系列データの異常検知手法の中でも比較的シンプルなやり方です.
訓練用の時系列データをスライド窓によりベクトル化しておき,k近傍法を用いて新たな時系列の異常度を計算します.

k=1として最近傍法を実装したコードがこちら.

# -*- coding: utf-8 -*-
import numpy as np
import matplotlib.pyplot as plt
from sklearn.neighbors import NearestNeighbors

def main():
    data = np.loadtxt("your path/qtdbsel102.txt",delimiter="\t")

    train_data = data[1:3000, 2]
    test_data = data[3001:6000, 2]

    width = 100
    nk = 1

    train = embed(train_data, width)
    test = embed(test_data, width)

    neigh = NearestNeighbors(n_neighbors=nk)
    neigh.fit(train)
    d = neigh.kneighbors(test)[0]

    # 距離をmax1にするデータ整形
    mx = np.max(d)
    d = d / mx

    # プロット
    test_for_plot = data[3001+width:6000, 2]
    fig = plt.figure()
    ax1 = fig.add_subplot(111)
    ax2 = ax1.twinx()

    p1, = ax1.plot(d, '-b')
    ax1.set_ylabel('distance')
    ax1.set_ylim(0, 1.2)
    p2, = ax2.plot(test_for_plot, '-g')
    ax2.set_ylabel('original')
    ax2.set_ylim(0, 12.0)
    plt.title("Nearest Neighbors")
    ax1.legend([p1, p2], ["distance", "original"])
    plt.savefig('./results/knn.png')
    plt.show()


def embed(lst, dim):
    emb = np.empty((0,dim), float)
    for i in range(lst.size - dim + 1):
        tmp = np.array(lst[i:i+dim])[::-1].reshape((1,-1)) 
        emb = np.append( emb, tmp, axis=0)
    return emb

if __name__ == '__main__':
    main()

心電図のデータをサンプルとして用いています.
周期的で正常な心電が見られる部分を訓練データとし,乱れている部分を含むようにテストデータの区間を取りました.
結果のプロットは以下のようになりました.
f:id:Nori_matsu:20170930222409p:plain
異常を含む区間で異常度 (グラフ中distanceと表記) が高くなっていることがわかります.

k近傍法では,注目波形パターンが,訓練データ中の波形パターン群の中でk番目に近いものと,どれだけ異なるかを表しています.
この手法は,綺麗な周期的データが予測される場合には有用ですが,周期的に繰り返しつつも長期的に上昇していくようなトレンドを持つ波形の場合にうまくいきません.

特異スペクトル変換法による変化点検知

一方,ある点の前後の波形を比較して変化点検出を行う特異スペクトル変換法という手法は,波形の長期的なトレンドにも強いと考えられます.
特異スペクトル変換法については,こちらのWebサイトにわかりやすくまとまっています.
numpyを用いて特異スペクトル変換法を実装したコードはこちら.

# -*- coding: utf-8 -*-
import numpy as np
import matplotlib.pyplot as plt

def main():
    data = np.loadtxt("your path/qtdbsel102.txt",delimiter="\t")

    train_data = data[1:3000, 2]
    test_data = data[3001:6000, 2]

    w = 50 # width
    m = 2
    k = w/2
    L = k/2 # lag
    Tt = test_data.size
    score = np.zeros(Tt)

    for t in range(w+k, Tt-L+1+1):
        tstart = t-w-k+1
        tend = t-1
        X1 = embed(test_data[tstart:tend], w).T[::-1, :] # trajectory matrix
        X2 = embed(test_data[(tstart+L):(tend+L)], w).T[::-1, :] # test matrix

        U1, s1, V1 = np.linalg.svd(X1, full_matrices=True)
        U1 = U1[:,0:m]
        U2, s2, V2 = np.linalg.svd(X2, full_matrices=True)
        U2 = U2[:,0:m]

        U, s, V = np.linalg.svd(U1.T.dot(U2), full_matrices=True)
        sig1 = s[0]
        score[t] = 1 - np.square(sig1)

    # 変化度をmax1にするデータ整形
    mx = np.max(score)
    score = score / mx

    # プロット
    test_for_plot = data[3001:6000, 2]
    fig = plt.figure()
    ax1 = fig.add_subplot(111)
    ax2 = ax1.twinx()

    p1, = ax1.plot(score, '-b')
    ax1.set_ylabel('degree of change')
    ax1.set_ylim(0, 1.2)
    p2, = ax2.plot(test_for_plot, '-g')
    ax2.set_ylabel('original')
    ax2.set_ylim(0, 12.0)
    plt.title("Singular Spectrum Transformation")
    ax1.legend([p1, p2], ["degree of change", "original"])
    plt.savefig('./results/sst.png')
    plt.show()


def embed(lst, dim):
    emb = np.empty((0,dim), float)
    for i in range(lst.size - dim + 1):
        tmp = np.array(lst[i:i+dim]).reshape((1,-1))
        emb = np.append( emb, tmp, axis=0)
    return emb

if __name__ == '__main__':
    main()

結果のプロットは以下のようになりました.
f:id:Nori_matsu:20170930230428p:plain
波形が乱れている点において,より鋭く異常度のピークが出ています.

どちらの手法でよりうまく異常検知ができるかはデータの性質に依存しますが,k近傍法の方が計算量は少なくて済むようです.

深層学習を用いた異常検知手法

LSTM (Long short-term memory) を用いた手法

時系列データを深層学習させる手法としては,RNNや,それを発展させたLSTMが知られています.
RNNで来月の航空会社の乗客数を予測する
わかるLSTM ~ 最近の動向と共に

LSTMは,与えられた波形から,次の時刻での値を予測するモデルです.
LSTMを用いて時系列データの異常検知をするアプローチとしては,異常例のデータセットが十分に集められるかそうでないかによって,二種類のアプローチがあります.
異常例のデータセットが少ない場合,直前までの波形から予測された値が,実際の値とどの程度異なるかによって,異常判定ができます.
一方,異常例のデータセットが十分にある場合には,LSTMを直接分類器として用いる方法も用いられるようです.
How to Use LSTM Networks for Time-series Anomaly Detection - Quora

Autoencoder (自己符号化器) を用いた手法

Autoencoderは,ニューラルネットワークを用いて次元圧縮をする手法であり,入力データをそのまま出力 (=復元) させるように教師あり学習をすることで,信号の特徴を抽出することができます.
deepage.net
正常な波形をデータセットとしてAutoencoderを学習させれば,テストデータとして正常波形を用いた時,元の形に近い波形を出力することができます.
一方このモデルに異常な波形を入力した場合,異常な波形に対する特徴を学習していないために,出力波形は大きく異なるでしょう.
このように,部分時系列データをうまく復元できたかどうかによって,異常判定を行います.

結び

時系列データから異常検知を行うためのアルゴリズムとして,k近傍法,特異スペクトル変換法を実装しました.
また,深層学習を用いたアプローチも紹介しました.
Autoencoderによる異常検知アルゴリズムの実装も行いたいところでしたが,時間切れとなってしまったので今回はここまでで...orz
リアルタイムで異常検知を行うためには,精度のみならず,どれだけの計算時間が許されるかという所にも着目して,アルゴリズムを選択する必要があるようです.

SparkのDeep Learning Pipelinesを使ってみた

Impulse開発チームの塚田です。

今回は、DatabricksのDeep Learning Pipelinesを、spark-shell上で触ってみました。 内容はほぼ下記を実行したものなので、英語余裕で読めるぜ!って方はこちらを見てください。

Deep Learning Pipelines — Databricks Documentation

Deep Learning Pipelines

Deep Learning PipelinesはSparkでDeep Learningをスケーラブルに実行するためのhigh-level APIを提供するパッケージです。 TensorFlowやTensorFlowをバックエンドとしたKerasをサポートしています。 現在は画像データに特化した下記を実行することができます。

  • Sparkで画像を操作する
    • 画像をDataFrameとして読み込むAPI
  • 転移学習
    • 学習済みモデルを特徴抽出器として使用
  • Deep Learningのモデルを使ったtrasform
    • 下記を使用してTransformerを構成
      • 学習済みモデル(Inception v3)
      • TensorFlow Graph
      • Keras
  • モデルをSQL functionとしてデプロイする
  • 分散ハイパーパラメータチューニング (comming soon...)

今回は下記を使って、猫と犬の判別をしてみたいと思います。

  • Sparkで画像を操作する
  • 転移学習

実行環境

ハードウェアの概要
機種名 MacBook Pro
機種ID MacBookPro13,2
プロセッサ名 Intel Core i5
プロセッサ速度 2.9 GHz
プロセッサの個数 1
コアの総数 2
二次キャッシュ(コア単位) 256 KB
三次キャッシュ 4 MB
メモリ 16 GB

準備

使用するのは猫と犬の画像です。 下記から取得しました。(Kaggleへの登録が必要です)

https://www.kaggle.com/c/dogs-vs-cats-redux-kernels-edition

Dataからtrain.zipをダウンロードしてください。 解凍後、適当な枚数を選りすぐり下記のようにします。 (猫、犬ともに30枚ずつで実行しました)

- /images/
  - cat/
    - cat.0.jpg
    - ...
    - cat.29.jpg
  - dog/
    - dog.0.jpg
    - ...
    - dog.29.jpg

Pythonパッケージのインストール

下記のインストールが必要です

pip install tensorflow keras h5py

spark-shell実行

pythonAPIしかないので、pysparkを実行します。 packagesにspark-deep-learningを指定します。

pyspark --packages databricks:spark-deep-learning:0.1.0-spark2.1-s_2.11 --driver-memory 2G --executor-memory 2G

画像ファイルから入力データを作成

学習に使う画像データはSparkのDataFrameとして取り込みます。 ディレクトリを指定すると直下にある画像ファイルを読み込みます。

# データの読み込み
from sparkdl import readImages
from pyspark.sql.functions import lit

cat_df = readImages("/images/cat").withColumn("label", lit(1))
dog_df = readImages("/images/dog").withColumn("label", lit(0))
cat_train, cat_test = cat_df.randomSplit([0.6, 0.4], 0)
dog_train, dog_test = dog_df.randomSplit([0.6, 0.4], 0)
train_df = cat_train.unionAll(dog_train)
test_df = cat_test.unionAll(dog_test)

中身を見てみると、画像のサイズ等の情報と中身のバイナリが入ってそうですね。

>>> train_df.show(100, False)
+---------------------------+---------------------------+-----+
|filePath                   |image                      |label|
+---------------------------+---------------------------+-----+
|file:/images/cat/cat.19.jpg|[RGB,223,320,3,[B@5db7511c]|1    |
|file:/images/cat/cat.2.jpg |[RGB,396,312,3,[B@3562ebce]|1    |
|file:/images/cat/cat.23.jpg|[RGB,256,334,3,[B@5d07ef8a]|1    |
|file:/images/cat/cat.25.jpg|[RGB,500,345,3,[B@55547685]|1    |
|file:/images/cat/cat.26.jpg|[RGB,374,500,3,[B@320cf282]|1    |
|file:/images/cat/cat.27.jpg|[RGB,479,370,3,[B@10925396]|1    |
|file:/images/cat/cat.28.jpg|[RGB,270,286,3,[B@720b85b1]|1    |
|file:/images/cat/cat.29.jpg|[RGB,375,499,3,[B@41d352b] |1    |
|file:/images/cat/cat.3.jpg |[RGB,414,500,3,[B@51d05fc8]|1    |
|file:/images/cat/cat.6.jpg |[RGB,303,400,3,[B@53dca887]|1    |
|file:/images/cat/cat.8.jpg |[RGB,345,461,3,[B@7dd49940]|1    |
|file:/images/cat/cat.9.jpg |[RGB,425,320,3,[B@100d5fbc]|1    |
|file:/images/cat/cat.0.jpg |[RGB,374,500,3,[B@43707c6a]|1    |
|file:/images/cat/cat.10.jpg|[RGB,499,489,3,[B@75de6c13]|1    |
|file:/images/cat/cat.11.jpg|[RGB,410,431,3,[B@2c99f571]|1    |
|file:/images/cat/cat.13.jpg|[RGB,315,499,3,[B@5693afe1]|1    |
|file:/images/cat/cat.15.jpg|[RGB,353,405,3,[B@16401a75]|1    |
|file:/images/cat/cat.16.jpg|[RGB,258,448,3,[B@10f8525a]|1    |
|file:/images/dog/dog.19.jpg|[RGB,225,299,3,[B@73a91f49]|0    |
|file:/images/dog/dog.2.jpg |[RGB,199,187,3,[B@388e5a7a]|0    |
|file:/images/dog/dog.23.jpg|[RGB,403,499,3,[B@2e0b6cac]|0    |
|file:/images/dog/dog.25.jpg|[RGB,375,499,3,[B@7cb391b5]|0    |
|file:/images/dog/dog.26.jpg|[RGB,224,300,3,[B@47db9a3d]|0    |
|file:/images/dog/dog.27.jpg|[RGB,375,499,3,[B@d4c4536] |0    |
|file:/images/dog/dog.28.jpg|[RGB,432,287,3,[B@78d7363f]|0    |
|file:/images/dog/dog.29.jpg|[RGB,376,500,3,[B@18093ea9]|0    |
|file:/images/dog/dog.3.jpg |[RGB,375,499,3,[B@39855e7] |0    |
|file:/images/dog/dog.6.jpg |[RGB,488,499,3,[B@6c12c557]|0    |
|file:/images/dog/dog.8.jpg |[RGB,500,469,3,[B@2db57024]|0    |
|file:/images/dog/dog.9.jpg |[RGB,500,368,3,[B@7c98ff4d]|0    |
|file:/images/dog/dog.0.jpg |[RGB,375,499,3,[B@3d6eba54]|0    |
|file:/images/dog/dog.10.jpg|[RGB,292,269,3,[B@472cf8d3]|0    |
|file:/images/dog/dog.11.jpg|[RGB,101,135,3,[B@419e9442]|0    |
|file:/images/dog/dog.13.jpg|[RGB,428,362,3,[B@7ac779ab]|0    |
|file:/images/dog/dog.15.jpg|[RGB,374,500,3,[B@3233c0bd]|0    |
|file:/images/dog/dog.16.jpg|[RGB,380,500,3,[B@12f95c48]|0    |
+---------------------------+---------------------------+-----+

転移学習

Deep Learning Pipelinesにより学習済みモデルを使用して学習を行います。 現状は下記のモデルがサポートされています。

  • InceptionV3

InceptionV3の出力を特徴量とし、決定木により猫と犬を分類します。 DeepImageFeaturizerインスタンスを作ってPipelineを作るだけですね。

# 学習
from pyspark.ml.classification import DecisionTreeClassifier
from pyspark.ml import Pipeline
from sparkdl import DeepImageFeaturizer 

featurizer = DeepImageFeaturizer(inputCol="image", outputCol="features", modelName="InceptionV3")
dt = DecisionTreeClassifier(labelCol="label", featuresCol="features")
p = Pipeline(stages=[featurizer, dt])

p_model = p.fit(train_df)

出来上がったモデルを使ってtransformします。

# テスト
from pyspark.ml.evaluation import MulticlassClassificationEvaluator

tested_df = p_model.transform(test_df)
evaluator = MulticlassClassificationEvaluator(metricName="accuracy")
print("Test set accuracy = " + str(evaluator.evaluate(tested_df.select("prediction", "label"))))
Test set accuracy = 0.75

何も考えずに作った割には合ってる気もしますが、もうちょっと中身について見ていきます。

DataFlameの中身を見てみると、DeepImageFeaturizerの出力がfeaturesカラムに入っています。

>>> tested_df.show()
+--------------------+--------------------+-----+--------------------+-------------+-----------+----------+
|            filePath|               image|label|            features|rawPrediction|probability|prediction|
+--------------------+--------------------+-----+--------------------+-------------+-----------+----------+
|file:/images/cat/...|[RGB,374,500,3,[B...|    1|[0.0,0.8633406162...|   [0.0,17.0]|  [0.0,1.0]|       1.0|
|file:/images/cat/...|[RGB,374,500,3,[B...|    1|[0.0,0.0,0.0,0.0,...|   [0.0,17.0]|  [0.0,1.0]|       1.0|
|file:/images/cat/...|[RGB,499,431,3,[B...|    1|[0.0,0.0,0.229576...|   [0.0,17.0]|  [0.0,1.0]|       1.0|
|file:/images/cat/...|[RGB,345,500,3,[B...|    1|[0.0,0.0516236834...|   [0.0,17.0]|  [0.0,1.0]|       1.0|
|file:/images/cat/...|[RGB,374,500,3,[B...|    1|[0.0,0.0,0.0,0.0,...|   [0.0,17.0]|  [0.0,1.0]|       1.0|
|file:/images/cat/...|[RGB,375,499,3,[B...|    1|[0.12656563520431...|   [0.0,17.0]|  [0.0,1.0]|       1.0|
|file:/images/cat/...|[RGB,144,175,3,[B...|    1|[0.0,0.0,0.195755...|   [18.0,0.0]|  [1.0,0.0]|       0.0|
|file:/images/cat/...|[RGB,499,495,3,[B...|    1|[0.0,0.0,0.0,0.0,...|   [18.0,0.0]|  [1.0,0.0]|       0.0|
|file:/images/cat/...|[RGB,280,300,3,[B...|    1|[0.79150134325027...|   [0.0,17.0]|  [0.0,1.0]|       1.0|
|file:/images/cat/...|[RGB,224,300,3,[B...|    1|[1.65047085285186...|   [0.0,17.0]|  [0.0,1.0]|       1.0|
|file:/images/cat/...|[RGB,267,320,3,[B...|    1|[0.86000078916549...|   [18.0,0.0]|  [1.0,0.0]|       0.0|
|file:/images/cat/...|[RGB,375,499,3,[B...|    1|[0.0,0.0,0.0,0.14...|   [0.0,17.0]|  [0.0,1.0]|       1.0|
|file:/images/dog/...|[RGB,348,215,3,[B...|    0|[0.0,0.1367801278...|   [18.0,0.0]|  [1.0,0.0]|       0.0|
|file:/images/dog/...|[RGB,332,500,3,[B...|    0|[0.21633519232273...|   [18.0,0.0]|  [1.0,0.0]|       0.0|
|file:/images/dog/...|[RGB,499,415,3,[B...|    0|[0.0,0.0,0.884287...|   [18.0,0.0]|  [1.0,0.0]|       0.0|
|file:/images/dog/...|[RGB,371,499,3,[B...|    0|[0.93737035989761...|   [18.0,0.0]|  [1.0,0.0]|       0.0|
|file:/images/dog/...|[RGB,500,274,3,[B...|    0|[0.56643348932266...|    [0.0,1.0]|  [0.0,1.0]|       1.0|
|file:/images/dog/...|[RGB,287,300,3,[B...|    0|[0.10650488734245...|   [18.0,0.0]|  [1.0,0.0]|       0.0|
|file:/images/dog/...|[RGB,376,499,3,[B...|    0|[0.11714683473110...|   [18.0,0.0]|  [1.0,0.0]|       0.0|
|file:/images/dog/...|[RGB,264,299,3,[B...|    0|[0.0,0.0,0.0,0.0,...|    [0.0,1.0]|  [0.0,1.0]|       1.0|
|file:/images/dog/...|[RGB,499,327,3,[B...|    0|[0.21827600896358...|   [0.0,17.0]|  [0.0,1.0]|       1.0|
|file:/images/dog/...|[RGB,161,98,3,[B@...|    0|[0.04027611017227...|   [18.0,0.0]|  [1.0,0.0]|       0.0|
|file:/images/dog/...|[RGB,386,500,3,[B...|    0|[0.0,0.0,1.286264...|   [18.0,0.0]|  [1.0,0.0]|       0.0|
|file:/images/dog/...|[RGB,335,272,3,[B...|    0|[0.0,1.5273251533...|   [18.0,0.0]|  [1.0,0.0]|       0.0|
+--------------------+--------------------+-----+--------------------+-------------+-----------+----------+

featuresの中身は配列になっています。 長さは131072です。

>>> len(tested_df.head()['features'])
131072

決定木は下記の用になっています。

>>> print(p_model.stages[1].toDebugString)
DecisionTreeClassificationModel (uid=DecisionTreeClassifier_46609281014c140920a8) of depth 2 with 5 nodes
  If (feature 38387 <= 0.6593263149261475)
   If (feature 26 <= 0.5600484609603882)
    Predict: 0.0
   Else (feature 26 > 0.5600484609603882)
    Predict: 1.0
  Else (feature 38387 > 0.6593263149261475)
   Predict: 1.0

featuresの数の割に木が短いです。 単純に学習データを多くすれば精度を上げられそうです。 (今回はとりあえず動かすだけなので大分サンプル数を絞っています)

また、indexからfeaturesのラベルを逆引きする手段がDeepImageFeaturizerになさそうなので、26と38387が何の値なのかさっぱりわかりません。 indexとラベルのマッピングが取れる手段が欲しいですね。

おわりに

Deep Learning Pipelinesを使うとDeep Learningのことを大して知らなくても実行できます。

まだ画像しか扱えませんし使える学習済みモデルも少ないですが、その辺の選択肢が増えて来ると夢が広がりますよね。

参考

dockerコンテナのレイテンシ

はじめましてブレインズテクノロジーの貴明です。 今回はDockerにおけるコンテナ間通信のレイテンシについて調べたためその内容について記事にしました。

経緯

「1コンテナ1プロセス」というDockerの原則に基づきコンテナを運用していると、確かに何のコンテナが何の役割を果たしているのか分かりやすいのですが コンテナが増えてくると果たしてそこで発生しているオーバーヘッドがどの程度なのか気になるところ。
基本的にレイテンシが起きるのは、CPU/ディスク/ネットワークとなりますが、今回はまずネットワーク(コンテナ間の通信)において発生しているレイテンシを単純な構成下で計測してみようという試みが本記事となります。
(今回はまず単一ホスト下における計測とし、ホスト間のレイテンシは次回!)

やりたいこと

同コンテナ内から発行されたクエリの処理時間と、別コンテナ経由で発行されたクエリの処理時間差から、コンテナ間通信におけるレイテンシを計測する。
具体的にはPostgreSQLに対してinsert文、およびselect文を複数回実行し、その処理時間差をtimeコマンドにて計測。 (単純なinsert文1,000回, select文10,000回実行) f:id:nakazawa-takaaki:20170817181226p:plain

前提となる環境

  • MacBook Pro (13-inch, 2017,Four Thunderbolt 3 Ports)
  • プロセッサ: 3.1 GHz Intel Core i5
  • メモリ: 16 GB 2133 MHz LPDDR3
  • グラフィックス: Intel Iris Plus Graphics 650 1536 MB
  • OS: MacOS Sierra (10.12.6)
  • Docker version 17.06.0-ce, build 02c1d87
  • docker-compose version 1.14.0, build c7bdf9e
  • Dockerリソース割り当て
    • CPUs: 2
    • Memory: 7.0 GB

結果

結果からいうと以下のようになりました。 やり方によるとは思われますが、今回の試みからはおよそ処理時間にして1%のレイテンシが発生している結果となりました。 実行方法は後述。

処理 同コンテナ処理時間(s) 別コンテナ経由処理時間(s) 処理時間増加比率
insert 11.741 11.908 101.4%
select 6.258 6.351 101.5%

結果詳細

具体的なtimeコマンドによる計測結果は以下の通り。

insert

time type 同コンテナ 別コンテナ経由 処理時間増加比率
real 0m11.741s 0m11.908s 101.4%
user 0m0.000s 0m0.040s -
sys 0m0.110s 0m0.100s 90.9%

select

time type 同コンテナ 別コンテナ経由 処理時間増加比率
real 0m6.258s 0m6.351s 101.5%
user 0m3.160s 0m3.370s 106.6%
sys 0m0.520s 0m0.270s 51.9%

実行方法

まずは以下の内容のDockerfileを用意

FROM postgres:9.6
COPY init.sql /docker-entrypoint-initdb.d/.
COPY create.sh test.sh insert.sql select.sql /tmp/

中でcopyしている各ファイルは以下の通り

  • init.sql: テスト用のDB作成
  • create.sh: テスト用のテーブル作成(今回はint型の1カラムのみとした)
  • insert.sql: テスト用のsql(insert文1,000行)
  • select.sql: テスト用のsql(select文10,000行)

以下のdocker-compose.ymlを用意して

version: '3'

services:
  db:
    build: .
    image: test/postgres:latest
    container_name: db
    ports:
      - "5432:5432"
    tty: true

  outerdb:
    image: test/postgres:latest
    container_name: outerdb
    tty: true

dockerイメージを作成、起動

docker-compose build
docker-compose up -d

sql発行のテストは以下の要領(同コンテナからのsql発行)

docker exec -it db bash
bash /tmp/create.sh
bash /tmp/test.sh localhost

sql発行のテスト(別コンテナからのsql発行)

docker exec -it db bash
bash /tmp/create.sh
exit
docker exec -it outerdb bash
bash /tmp/test.sh db

使ったもの

今回使ったDockerFileやシェルスクリプトなどはgithubへ上げております。

Spark Summit 2017 San Francisco

f:id:brains_aoki:20170607131148j:plain

こんにちは、データアナリストの青木とエンジニアの樋口です。

引き続き、Spark Summit 2017 San Fransiscoの記事です。Keynoteやセッションで特に興味深かったものを紹介していきます。

Keynote

Coming in Spark 2.2

まずは、Spark2.2に関する情報がきました。注目点は以下。

  • コストベースSQLの最適化

  • structured streamingがproduction-readyとなった

  • pip install pyspark が可能となる

すでにgitではv.2.2.0-rc4のtagが打たれていることから、リリース間近なようですね。

続いて大きな発表がありました。

Two new open source from Databricks

おそらくこの発表がSummitの目玉だったようです。 今後Databricksは以下2つについて、特に力を入れていくとのこと。

  1. Deep Learning

  2. Streaming Performance

Deep Learning

みんな大好きDeep Learning。画像分野を中心に華やかな成果を上げてはいますが、TensorFlowやKrasなど既存のDeep LearningオープンソースはまだまだAPIとしてlow-level。これに対してDatabricksはDeep Learning Pipelinesというhigh-levelなAPIを用意するぜ!とのことでした。これにより、

  • よくあるユースケースについては、コード数行でモデルを作成できる

  • Spark上で自動的に分散処理してくれる

  • バッチやストリーミングアプリ、SparkSQLにモデルをデプロイできる

ができるようになるとのことです。以下のDatabricksの記事に詳しい説明があります。

databricks.com

Streaming Performance

Spark Streamingにおけるデータに対し、DataFrameやSparkSQLなど、バッチと同様のhigh-levelのAPIを提供しつつ、レイテンシーを抑えるとのこと。microbatchの処理はやめて、continuous progressという新たな処理仕様になるようですが、既存のコード自体は変える必要はないようです。リアルタイム処理にSpark Streamingをコアとしている弊社にとってはうれしい話かもしれません。

Streaming PerformanceについてもDatabricksの以下の記事が詳しいです。

databricks.com

www.youtube.com

そのほか、DatabricksのServerlessの話もありました。

databricks.com

www.youtube.com

次はセッション。 各セッションの動画については来週末くらいにuploadされるみたいなので、その際にまたlinkと共に更新します。

Sessions

NEXT GENERATION WORKSHOP CAR DIAGNOSTICS AT BMW POWERED BY APACHE SPARK

f:id:mhigu:20170608101515p:plain:w160 f:id:mhigu:20170608101816p:plain:w160 f:id:mhigu:20170608101638p:plain:w160

概要

現在の販売店での車両診断は、手動で生成された決定木(人が作って来た条件分岐)に基づいていて、車種の多様化、車両システムの複雑化(hybrid, connective)に伴い、すでに限界に達しているとのこと。BMWでは、自動車やワークショップから入手できるデータを利用して、交換するべき部品や取るべき行動を予測できるモデルを作成し、そこから得られた結果をWeb-Applicationとして提供して、複雑化した車両整備を現場の人だけで解決できるような形にして出力しているそうです。

感想

これこそ、IoTの事例といった感じでしょうか。このアプリケーションを作り始めた理由としては、概要にも少し書いてありますが、車両システムの複雑化により整備者の知識だけでは交換すべき部品が分からないという背景があったそうです。この傾向は車業界に限らず発生することだと思うので、似たような事例が今後も出てくることになりそうです。impulseは異常検知に特化したソフトウェアとして作っているため、もしかしたら、impulseでも、、、?

DEEP LEARNING IN SECURITY—AN EMPIRICAL EXAMPLE IN USER AND ENTITY BEHAVIOR ANALYTICS (UEBA)

f:id:mhigu:20170608100119j:plain:w160 f:id:mhigu:20170608095516j:plain:w160 f:id:mhigu:20170608095526j:plain:w160

概要

このプレゼンテーションでは、顧客の攻撃検出例を使ってDeep Learningがどのように適用されて、どうセキュリティ問題を解決したのか、経験の共有、また、ディープラーニングや一般的な機械学習をより広範なセキュリティに展開するための課題とガイドラインについても説明していました。Deep Learningについては、以下の2つの実例が挙げられていました。

  • 畳み込みニューラルネットワーク(CNN)を使用するユーザ行動異常検出ソリューション
  • LSTMを使用するステートフルなユーザーリスクスコアリングシステム

感想

セキュリティ業界では、ルールベースの検知が限界とされて久しいですが、今回のプレゼンテーションではDeep Learning(CNN, LSTM)を使って異常検知をした実例を説明してくれました。CNNへの入力は、ユーザの通信状態(プロトコル毎)の画像。出力はどのユーザかのid。idがいつもと違うものになれば異常とする算段なのですが、これだけだと検知精度は良くないらしいです。対策として、CNNと併せてLSTMへ、フィッシングメールがあるかどうか、不審なDNSクエリがあるか、大量データ送信があるかなどをエンコードしたベクトルを入力して各段階でスコアを算出し、実際の検知を行っているとのこと。また、プレゼンの最後にセキュリティの異常検知はDeep Learningだけでは解決不能で企業のルール等も鑑みながらロジックを組み立てており、Deep Learningは異常検知の機能の一部分でしかないことを強調していた事が非常に印象的でした。

RAY: A CLUSTER COMPUTING ENGINE FOR REINFORCEMENT LEARNING APPLICATIONS

概要

UC Berkeleyの学生たちによるSpark をベースとした強化学習のフレームワーク(Ray)についてのプレゼン

機械学習が成熟するにつれて、標準的な教師あり学習は学習データの量と正確性という観点から十分な手段ではなくなりました。

昨今の機会学習アプリケーションでは、静的に学習したモデルから単一の予測を作成して提供する代わりに、一連のアクションを実行した結果を再評価する事で、動的環境の変化に対応する必要性が高まっています。強化学習という手段はこの要求に対して良い成果を出すことが知られています。 ただし、これらのアプリケーションは非常に厳しい計算要件を必要とし、動的なグラフ計算もサポートする必要があります。彼らは、この要件に対してスケールアウトが容易であるSparkをコンピューティングエンジンとして選択しており、そのアーキテクチャー及び一部実装方法についてがプレゼンの内容となっています。

感想

強化学習は違ったパラメータでシミュレーションを行いそれを評価してルールを更新し、再度違ったパラメータでシミュレーションを行い再評価して。。。という事をするのですが、彼らはそれを並列化して大量にシミュレーションを行いたいと、、、これこそ、Sparkの出番!!という感じで、単純にああなるほどね。と納得しました。

また講演では、人の走るという行為を強化学習でシミュレーションするサンプルアプリケーションのデモ動画が流されていて、最初の方は一歩進むのもままならない状態のものが、1歩・2歩と進むようになり、最終的には(不格好ながらも)ずっと走り続けるようになる結果を見て、単純におもしろいなと思いました。

GoogleのAlphaGoが強化学習とDeep Learningを利用して強くなったことはあまりに有名ですが、エンタープライズの世界でも(Sparkをベースとした!?)強化学習によりブレークスルーが起きるか要注目ですね。

BIG DATA AT AUDI: ROOT CAUSE ANALYSIS IN AN AUTOMOTIVE PAINT SHOP USING MLLIB

f:id:brains_aoki:20170608135540p:plain:w160f:id:brains_aoki:20170608135631p:plain:w160 f:id:brains_aoki:20170608135714p:plain:w160

概要

アウディの自動車塗装において取得できる、約2500のセンサーデータを活用した話。塗装工程は非常に複雑かつ、温度や湿度等の影響をうけるため、どの工程でどのような失敗が起きるのかセンサーデータにSparkのDataFrame APIやMLlibを使って原因を突き止めることを試みたとのことです。

感想

個人的に興味深かったのは、時刻調整や補正に関する話です。自動車が塗装されるタイミングにおけるセンサーデータの時刻調整の話や、ある時間間隔におけるデータの代表値を平均か回帰で補正するのかなど、弊社も似たようなことをやっているなと感じました。こういった親近感のような感想を得られたことは、ある意味収穫ではありました。

終わりに

Keynoteでもありましたが、これからSparkはDeep Learningのサポート・ストリーミングのマイクロバッチ廃止など、更に進化していきます。個人的にはDeep Learningの機能は早く使ってみたいですし、とても楽しみにしています。 今後もまだまだ、Sparkの進化から目が離せませんね!

【レポート】Spark Summit 2017 開幕!!!

Spark Summit 2017 San Francisco

f:id:brains_aoki:20170607004557j:plain

こんにちは、データアナリストの青木とエンジニアの樋口です。

6月5日から合計3日間アメリカのサンフランシスコでSpark Summit2017が開催されています。

spark-summit.org

Spark Summit2017では、3,000人以上のエンジニア、データサイエンティスト、研究者やビジネスプロフェッショナルが参加しており、データサイエンスやエンタープライズ、マシンラーニングなどの分野で、170を超えるセッションがあります。

ブレインズテクノロジーではデータ分析・機械学習を事業のコアとしている事もありSparkの動向には常に注目しています。

その中で、CTOから「ちょっと、きみたち行ってきたら?」というありがたい鶴の一声を頂いたのではるばるアメリカへ来ています!

セッションの内容等、順次アップして行く予定ですが、まずは雰囲気だけ!!

f:id:brains_aoki:20170607004445j:plain f:id:brains_aoki:20170607004526j:plain f:id:brains_aoki:20170607010319j:plain

(P.S サンフランシスコ、最高!)

f:id:brains_aoki:20170607010353j:plain

【レポート】ケイ・オプティコム様 IBMユーザ論文「AIを利用したサイレント故障検知の取組み」で金賞受賞!

データ分析サービス・プロダクト担当の藤原です。

弊社のリアルタイム大規模データ分析基盤「Impulse」を導入いただきましたケイ・オプティコム様が、ネットワークインフラ運用において機械学習を活用してサイレント故障の検知を実現した取組みについて、IBMが主催するIBMユーザー研究会の論文を執筆され、「金賞」を受賞されました!
http://www.uken.or.jp/symp/symp55/program/paperlist.shtml

関西エリアを中心として大規模なネットワークインフラの安定稼働のため、ネットワークのサイレント故障の迅速な検知を実現する手段として、機械学習に着目され、弊社Impulseを活用して分析基盤を整備された取組みの詳細がまとめられた内容です。

5月18〜19日には「IBMユーザー・シンポジウム」として、約1,000人の来場者が訪れる大規模なイベントが京都で開催されました。
www.uken.or.jp

機械学習の活用に関する具体的な取組み事例ということもあり、ケイ・オプティコム様のプレゼン会場は超満員。
聴講されていた方々も非常に真剣な様子で、その内容に聞き入っておられました。

今回の金賞受賞は、ケイ・オプティコム様が「AI・機械学習」といった技術を、いち早く「システムインフラ・ネットワーク運用」という分野で、実際の現場に適用されたという先進的な取組みであったという点が高い評価に繋がっているではないかと思います。
そうした貴重な取組みに対して、技術的な面で貢献できたことを弊社としても非常に嬉しく感じております。

今後もケイ・オプティコム様を始めとした多くの導入企業様の事業に貢献できるよう、Impulseの更なる進化に取り組んでいきたいと思います。

f:id:fujiwarakazunari:20170531135705j:plain
※シンポジウムでの表彰後の様子:株式会社ケイ・オプティコム 谷岡様(右)と赤井様(左)