2016年4月8日金曜日

RFIDでディープラーニング その2

前回の投稿ではあまりよい結果が出ませんでしたが、まー、いろいろ端折ってやったしー、みたいなところがあったので、もう少しましな結果が出るように試してみました。

前回ミスったと思われるところは、

・トレーニングのバッチ内のデータが全部 [1.0, 0.0] か [0.0, 1.0] のどちらかに揃っていて、バラバラに入り混じった状態でトレーニングしていない。
・トレーニング数が少ない。

他にも

・学習発散を抑えるために勾配降下のステップ長を学習の進み具合で変化させる。
・ドロップアウト率を学習の進み具合で変化させる。

のようなのもありますがひとまず上側の2つをやってみました。

コードは前回と違うところだけ。データ読み込みの関数を変更。読み込んでリストを返すだけにしました。
※前回同様モバイルでご覧の方はコードが表示されません。すいません。すいません。

# ファイルからデータ読込
def fromFile(file_name, y__):
  rssi = np.zeros([1, X_DATA_SIZE])
  count = 0
  rssilist = open(file_name, 'r')
  for rssivalue in rssilist:
    rssi = np.roll(rssi, 1)
    rssi[0, 0] = float(rssivalue) / 60000.
    count += 1
    if count == X_DATA_SIZE:
      x_list = rssi
      y_list = y__
    if count > X_DATA_SIZE:
      x_list = np.vstack((x_list, rssi))
      y_list = np.vstack((y_list, y__))
  rssilist.close()
  return x_list, y_list
で、データの読み込みと学習を実施。

data_num = 0

# 動いてないときのデータを読み込み
print 'no move phase read'
lists = fromFile('nomove.dat', np.array([[1., 0.]]))

x_all = lists[0]
y_all = lists[1]

# 動いているときのデータを読み込み
print 'move phase read'
lists = fromFile('move.dat', np.array([[0., 1.]]))

x_all = np.vstack((x_all, lists[0]))
y_all = np.vstack((y_all, lists[1]))

print 'total data:' + str(x_all.shape[0])
data_num = x_all.shape[0]

start = 0
for i in range(10000):
  if start + BATCH_SIZE > data_num:
    start = 0
  if start == 0:
    # データをシャッフル
    perm = np.arange(data_num)
    np.random.shuffle(perm)
    x_all = x_all[perm]
    y_all = y_all[perm]
  sess.run(train_step, feed_dict={x: x_all[start:start + BATCH_SIZE], y_: y_all[start:start + BATCH_SIZE], keep_prob: 0.5})
  if i%500 == 0:
    # 正確性と交差エントロピーを表示
    print sess.run(accuracy, feed_dict={x: x_all[start:start + BATCH_SIZE], y_: y_all[start:start + BATCH_SIZE], keep_prob: 1.0})
    print sess.run(cross_entropy, feed_dict={x: x_all[start:start + BATCH_SIZE], y_: y_all[start:start + BATCH_SIZE], keep_prob: 1.0})

学習が終わったら実際にタグが止まっている状態のデータを受信すると...

[ 0.91049373  0.08950625]

キタ―!止まってると認識してますね。

さらにタグを動かしてみます...

[ 0.85994738  0.14005256]
[ 0.78625971  0.21374026]
[ 0.73439723  0.2656028 ]
[ 0.71011215  0.28988785]
[ 0.65806454  0.34193543]
[ 0.27353901  0.72646099]
[ 0.01137005  0.98862994]

※データは見やすいように間引きしています。
おー、徐々に動いてる状態へと変化していきます。すごーい。MNISTのチュートリアルでも十分な精度が出ますねー。
これで何とか足がかりがついた感じです。実際の環境にどれだけ耐えられるかはまだまだ検証が必要ですけどね。



2016年4月1日金曜日

RFIDでディープラーニング その1

RFIDを使ったシステムの特徴の1つはデータがしこたま溜まるということですね。リーダが読み続ける限りアンテナの近くにあるタグは読まれ続け、データが通知され続けるわけです。

となると、

RFID → データいっぱい → ビッグデータ! → ディープラーニング!!

というのはきっと自然の流れですよね。たぶん。

そんなわけでRFIDリーダから得られたデータを使ってディープラーニングにトライしてみました。

リーダから得られるデータですが、基本的にはタグのIDとRSSI値と呼ばれる電波強度が得られます。このRSSI値に注目してやってみました。

ディープラーニングはいろんなツールが出ていますが何となくで Google の TensorFlow を使います。

チュートリアルが単純な順伝搬型のMNISTからはじまって、次がCNNを使ったディープMNISTと進んでいるのでまずはこれを参考にしてみます。

MNISTは手書きの数字の画像を対象としているのですが、こっちはRSSIしかない1次元ですのであんまりしっくりこないですが自然言語処理でも使われてるようなので気にしないでやってみます。

何を学習させるかですが、RSSIのみでとりあえずタグが動いているのか止まっているのかを判別させたいと思います。簡単そうに見えますが、これの難しいところはタグが止まっていても周囲の環境(人が通ったとか)でRSSIもぐぐっと変わるところです。このRSSIの変化をタグが動いているのか周辺のせいなのかを判別させたいとそういうことです。

で、RSSIは連続して得られますので、「過去32個のRSSIの値をみて動いているか止まっているか判別させる」という風にします。

学習用に大量のデータが必要になりますのでデータを溜めます。アンテナの上にタグを吊るしておきます。

止まった状態でデータを採り続ける

扇風機でタグを動かし続けてデータを採り続ける

これでそれぞれ10000個ほどのデータが採れました。

ここから TensorFlow を使ったコード。まずはパラメータの設定とか各処理の関数。関数の部分はチュートリアルとほぼ一緒です。データが1次元なのでプーリングのマトリクスを1×2の1次元にしています。
※モバイルでご覧の方はコードが表示されません。すいません。

import tensorflow as tf
import numpy as np
import socket
from contextlib import closing

X_DATA_SIZE = 32  # RSSIの個数
BATCH_SIZE = 128  # トレーニングのバッチサイズ

FILTER_SIZE = 5   # 畳み込みフィルタのサイズ

CONV_1_CHANNEL_SIZE = 16  # 畳み込み層1の出力チャンネル数
CONV_2_CHANNEL_SIZE = 32  # 畳み込み層2の出力チャンネル数

FC_NEURON_SIZE = 512  # 結合層のニューロン数

# Weightの初期化
def weight_variable(shape):
  initial = tf.truncated_normal(shape, stddev=0.1)
  return tf.Variable(initial)

# Biasの初期化
def bias_variable(shape):
  initial = tf.constant(0.1, shape=shape)
  return tf.Variable(initial)

# 畳み込み
def conv2d(x, W):
  return tf.nn.conv2d(x, W, strides=[1, 1, 1, 1], padding='VALID')

# プーリング
def max_pool_2x1(x):
  return tf.nn.max_pool(x, ksize=[1, 1, 2, 1], strides=[1, 1, 2, 1], padding='VALID')

そしてモデルの作成と初期化。モデルはチュートリアルと同じで畳み込み+プーリングの2層になっています。

# プレースホルダを設定
x = tf.placeholder(tf.float32, [BATCH_SIZE, X_DATA_SIZE])
y_ = tf.placeholder(tf.float32, [BATCH_SIZE, 2])

# xを4次元テンソルに変形
x_image = tf.reshape(x, [-1, 1, X_DATA_SIZE, 1])

# 畳み込み層1つめ
W_conv1 = weight_variable([1, FILTER_SIZE, 1, CONV_1_CHANNEL_SIZE])
b_conv1 = bias_variable([CONV_1_CHANNEL_SIZE])

h_conv1 = tf.nn.relu(conv2d(x_image, W_conv1) + b_conv1)
# プーリング層
h_pool1 = max_pool_2x1(h_conv1)

# 畳み込み層2つめ
W_conv2 = weight_variable([1, FILTER_SIZE, CONV_1_CHANNEL_SIZE, CONV_2_CHANNEL_SIZE])
b_conv2 = bias_variable([CONV_2_CHANNEL_SIZE])

h_conv2 = tf.nn.relu(conv2d(h_pool1, W_conv2) + b_conv2)
# プーリング層
h_pool2 = max_pool_2x1(h_conv2)

# 畳み込み、プーリングが完了したあとのデータサイズを計算
after_conv_size = ((X_DATA_SIZE - (FILTER_SIZE - 1))/2 - (FILTER_SIZE - 1))/2

# 高密度結合層
W_fc1 = weight_variable([after_conv_size * 1 * CONV_2_CHANNEL_SIZE, FC_NEURON_SIZE])
b_fc1 = bias_variable([FC_NEURON_SIZE])

h_pool2_flat = tf.reshape(h_pool2, [-1, after_conv_size * 1 * CONV_2_CHANNEL_SIZE])
h_fc1 = tf.nn.relu(tf.matmul(h_pool2_flat, W_fc1) + b_fc1)

# ドロップアウト
keep_prob = tf.placeholder("float")
h_fc1_drop = tf.nn.dropout(h_fc1, keep_prob)

# ソフトマックスで出力
W_fc2 = weight_variable([FC_NEURON_SIZE, 2])
b_fc2 = bias_variable([2])

y_conv = tf.nn.softmax(tf.matmul(h_fc1_drop, W_fc2) + b_fc2)

# トレーニング
cross_entropy = -tf.reduce_sum(y_ * tf.log(y_conv))
train_step = tf.train.AdamOptimizer(0.0001).minimize(cross_entropy)
#train_step = tf.train.GradientDescentOptimizer(0.002).minimize(cross_entropy)

# 評価
correct_prediction = tf.equal(tf.argmax(y_conv, 1), tf.argmax(y_, 1))
accuracy = tf.reduce_mean(tf.cast(correct_prediction, "float"))

saver = tf.train.Saver(max_to_keep=10)

init = tf.initialize_all_variables()

sess = tf.Session()
sess.run(init)   # 初期化

続いて先ほど溜めこんだRSSIのデータファイルからデータを読み込んで学習する関数。

# ファイルからデータ読み込んでトレーニングする
def fromFile(file_name, y__, batch_size):
  rssi = np.zeros([1, X_DATA_SIZE])
  # 教師用データは先に作っておく
  y_batch = y__
  for i in range(batch_size - 1):
    y_batch = np.vstack((y_batch, y__))
  count = 0
  rssilist = open(file_name, 'r')
  for rssivalue in rssilist:
    rssi = np.roll(rssi, 1)
    rssi[0, 0] = float(rssivalue) / 60000.
    count += 1
    if count == X_DATA_SIZE:
      rssi_batch = rssi
    if count > X_DATA_SIZE:
      rssi_batch = np.vstack((rssi_batch, rssi))
    if count == (batch_size + X_DATA_SIZE - 1):
      # 学習データが揃ったところでトレーニング
      sess.run(train_step, feed_dict={x: rssi_batch, y_: y_batch, keep_prob: 0.5})
      print sess.run(accuracy, feed_dict={x: rssi_batch, y_: y_batch, keep_prob: 1.0})
      count = 0
  rssilist.close()    

リーダからデータを受信しながら評価する関数。トレーニングのバッチサイズを128にしたので評価時も128個必要なんですが面倒なのでRSSIが32個溜まったらそれをコピーして突っ込んでます。

# リーダからのデータを受信して評価してみる
def receiveRSSI():
  host = '192.168.75.107'
  port = 4000
  bufsize = 4096

  rssi = np.zeros([1, X_DATA_SIZE])
  count = 0

  sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  with closing(sock):
    # リーダからのストリーミングを待ちうけ
    sock.bind((host, port))
    sock.listen(1)
    while True:
      conn, address = sock.accept()
      with closing(conn):
        msg = conn.recv(bufsize)
        list = msg.split('\x00')
        for m in list:
          if m.startswith("EPC"):
            epclist = m.split('\r\n')
            for epc in epclist:
              if len(epc) > 0 and epc.startswith("EPC:E200 1AC1 9288"): # 特定のタグで
                tmp = epc.split(',')[2].split(':')[1]
                rssi = np.roll(rssi, 1)
                rssi[0, 0] = float(tmp) / 60000.
                count += 1
                print count
                if count >= X_DATA_SIZE:
                  # バッチサイズ複製して突っ込む
                  rssi_batch = rssi
                  for i in range(BATCH_SIZE - 1):
                    rssi_batch = np.vstack((rssi_batch, rssi))
                  print sess.run(y_conv, feed_dict={x: rssi_batch, keep_prob: 1.0})[1]
                  if count >= 100:  # とりあえず100回でおわり
                    return

で、実際に実行します。止まっているときの教師データは [1.0, 0.0] にして、動いているときの教師データは [0.0, 1.0] にして学習させます。

# 動いてないときのデータで訓練
print 'no move phase start'
fromFile('nomove.dat', np.array([[1., 0.]]), BATCH_SIZE)

# 動いているときのデータで訓練
print 'move phase start'
fromFile('move.dat', np.array([[0., 1.]]), BATCH_SIZE)

# 結果は保存しておく
save_path = saver.save(sess, 'reader_deep', global_step = 0)
print "Model:%s" % save_path

# 実際にリーダから受信して試してみる
print 'valid phase start'
receiveRSSI()


結果
学習後にタグが止まった状態で評価してみると、

[  3.66762222e-04   9.99633193e-01] 

うはー!、ほとんど動いてると思ってますねー(笑 どうも過学習が強いようです。いろいろパラメータ変えてみても、

[ 0.36865199  0.63134807]

とちょっとはましですがどうもというところですね。しかしこの状態でタグを動かすと、0.63 が 0.67 くらいまで上がったりします。より動いてるのでは?という判断はしているようです。ここの判断が大きく値として現れるフィルタが必要なんですかね。

今回はここまで。やはりRNNまで進まないとダメでしょうか。先は長そうです。

2016年2月2日火曜日

レゴマインドストームでお手製の検証装置



先日(って結構まえですが)、

本社@四谷をウロウロしてたら変な音が聞こえてきました。





ピピピッ、ピピピッ、と部屋の片隅で鳴り続ける怪やしい音、


おそるおそる近寄ってみると、見るからに不思議な装置が稼動してました。







なんか観覧車がクルクルと回り続けているんですが、


観覧車の先端にはICカードが取り付けられてますね。


最下部には、リーダーモジュールが潜んでる模様。






ICカードが順次、リーダーモジュールのところに回ってくると、

読み取りに成功して、ピピピッと音が出るわけです。






なるほど。。。


どうやら、リーダーモジュール制御アプリの耐久テスト中っぽい。


開発したプログラムが長時間、問題なく動作してくれるかどうかを確認するために


ひたすら読み取りテストを繰り返していくわけなんですが、


人間がリーダーモジュールにカードを夜通しタッチし続けるのは大変なので、


その辺に転がってたレゴマインドストームで検証装置を作ってみましたよ、


という話でした。






そっかー、 そこまで人手が足りないのかー

いろいろ考えてるわけですね(笑




2016年1月7日木曜日

出展のご案内(第4回アイメックスモバイルソリューションフェア)


全然技術系の内容じゃないので投稿すべきか迷いつつ、、、

なんですが、2016年2月5日(金)に、東京国際フォーラムで開催される、


第4回アイメックスモバイルソリューションフェア
http://www.aimex.co.jp/news/event/entry-000444.html

にて、ブース出展およびセミナー講演をさせて頂くことになりました。



展示のほうは、最近やたらと引き合いの多い「タッチレンタル」のUHF対応版、を中心に、新作も各種とりそろえて、と考えております。



16:10から開始予定の弊社担当セミナーのお題は、



イマ改めて注目を集めるIoT時代のRFID/NFCソリューション


諸外国に比べて停滞気味とされていた国内ユーザのRFID/NFCソリューションの導入が、ここにきて急速に加速しています。その背景にあるIoTの文脈を抑えながら、今日における効果的な導入プロセスをガイドします。





と、30分で話すにはちょっと大掛かりな感じになってます。


何はともあれ立ち寄りやすい有楽町開催です、お時間のある方は是非お気軽にお運びくださいませ。


ちなみに、セミナーは事前申し込み制みたいなので、お早目のお申込みをおススメいたします。


フェアの詳細およびお申し込みは、こちらのリンクよりお願いいたします。
http://www.aimex.co.jp/news/event/entry-000444.html


皆様のお申込み/ご来場を心よりお待ち申し上げております!!

2016年1月5日火曜日

マニアック&攻めすぎの専門誌として紹介される「月刊自動認識」

あけましておめでとうございます!

昨年は何と19回しか更新されなかった本ブログですが、2016年は新たな気持ちでコツコツとRFIDネタをお届けしていきたいものです。

さてさて、年明け早々、敬愛する @koi884 さんのTLに流れてきたのが、下記のtweet。






業界の人間からすると親しみ深い「月刊自動認識」ですが、一般の方々から見ると、まぁ確かにマニアック過ぎますよね。。。とうぜん弊社は割とお世話になってまして、割と高頻度で新製品や事例の紹介で寄稿させていただいたりします。

実は、私が寄稿させて頂いているNFCネタの連載「NFCで始める実践RFID」とか4年目に突入してまして、内容的には、とりあえず身の回りにペタペタ貼ってみたNFCタグについて、ダラダラと解説してみる感じなので、それはまぁ「いわゆる読み物などはまったく見当たらない」とか言われるのもしょうがない気がしないこともないのですが、2016年を迎えるにあたり、もう少し読み応えのある内容へシフトして行きたいと考える次第です。

ちなみに、これまでの連載のagendaは下記のとおりです。ご参考まで。

1). NFCが面白い本当の理由
2). カンタン便利な家庭内NFC
3). オフィス内NFCタギングで目指すストレスフリーな仕事術(1)
4). オフィス内NFCタギングで目指すストレスフリーな仕事術(2)
5). 快適なNFCタギングをスタートするために(1)
6). 快適なNFCタギングをスタートするために(2)
7). 快適なNFCタギングをスタートするために(3)
8). アプリ連携で更に便利なパーソナルNFCタギング(1)
9). アプリ連携で更に便利なパーソナルNFCタギング(2)
10). パーソナルNFCタギング応用事例(育児とNFC)
11). 育児とNFC(玩具編)
12). 育児とNFC(保育園の制作物編)
13). NFCタグを活用したオリジナルアクセサリ(1)
14). NFCタグを活用したオリジナルアクセサリ(2)
15). 介護とNFC
16). キッチンとNFC
17). 掃除とNFC
18). マラソンとNFC
19). 幼児学習とNFC
20). 旅とNFC
21). iBeaconとNFC (1)
22). iBeaconとNFC (2)
23). iBeaconとNFC (3)
24). iBeaconとNFC (4)
25). 通勤とNFC
26). 名刺交換とNFC
27). 語学学習とNFC
28). 謹賀新年とNFC
29). 冷蔵庫とNFC
30). 引っ越しとNFC
31). 新居とNFC
32). 来客とNFC
33). 展示会とNFC
34). 台風とNFC
35). 出張とNFC
36). ホームシックとNFC
37). テレビとNFC (1)
38). テレビとNFC (2)
39). テレビとNFC (3)



というわけで、ブログへの現実逃避はこれぐらいにして本業に戻りましょう。

本年もご愛顧のほど、何卒よろしくお願い申し上げます!


*それにしても、ブログの更新って続かないものですね。
*ちゃんと曜日ごとの担当をきめて、サボったらビールおごるぐらいにしてみませんか?

2015年12月17日木曜日

今日のかざすちゃん 「センサーUHFタグのデモ」

センサー付きUHFタグのデモビデオです。 

このセンサータグ、温度、湿度、圧力が測定できるのですが、それぞれのセンサーを内蔵しているわけではないのが大きな特徴です。普通のUHFタグと同じようにチップとアンテナのみから構成されています。

製品の詳細はこちらです。
https://www.smartrac-group.com/first-passive-uhf-moisture-sensing-inlay-on-the-market.html

これを使って例えば使い捨てのセンサータグとして利用も可能です。


温度はかなり正確に測定できますよ。驚きですねー。

2015年12月2日水曜日

HCEでおしゃべり

今さら感満載ですが、Android 4.4 からは HCE(Host-based Card Emulation) という機能が搭載されています。

通常 NFC のカードエミュレーションを利用するときは、SE(Secure Element) というのが必要で、リーダはこの人とおしゃべりをすることになっています。この人がカードの実体と言っても過言ではないわけですね。

で、SEの変わりを Android(のアプリ)側が務めましょうというのが HCE なわけです。ネットを見ると ACR122U で HCE をやってみた的な事例はたくさんありますので今回はあえてパソリを使ってみます。

HCE は TypeA を使うそうなので、SDK for NFC を使います。そうすると PC側のソースを載せるのがいかがなものかということになるわけですが、まぁ、SDK のサンプルコードをほぼそのまま使いましたということでその辺はお許しください。

Android側はネットにもたくさん情報あるのでその通りに実装しました。

今回は簡単に Android端末がカードのふりをして現在時刻を返すというシンプルな実装にしました。

具体的には

■PC -> パソリ -> HCE
SELECTコマンド
0x00 0xA4 0x04 0x00 0x07 0xF0 0x01 0x02 0x03 0x04 0x05 0x06
(AID:アプリケーションIDが F0010203040506 のやつを選択という意味です。AIDは上記の apduserveice.xml 内に定義してます。これにより動作させるアプリを選択させるわけです)
■HCE -> パソリ -> PC
OK(0x90 0x00)
■PC -> パソリ -> HCE
独自コマンド
0x01 0x02 0x03
■HCE -> パソリ -> PC
時、分、秒+OK (hour minute second 0x90 0x00)

というコマンドの流れです。

これで双方を実行してパソリにAndroid端末をかざすと...


きました! 16進数表記になってますが、時、分、秒が返ってきました。

ちなみにスリープ状態でも読めるか試しましたがダメでした。
ロック画面では読めました。通常のリーダーモードよりは緩い感じですねー。