Raspberry Pi・Arduino・電子工作

2017年3月27日 (月)

IchigoJamがRaspberry Pi上で動作

Raspberry PiとIchigoJam。一見相容れないこの両者が一体になると・・・

IchigoJamがRaspberry Pi 3向けに移植 - PC Watch

Raspberry Pi上で動く「IchigoJam RPi」を使ったプログラミング教育を進めるとIOデータが発表しました。

IchigoJamは1500円でハードが提供されてますが、コンポジット出力にPS/2キーボードと、今となっては確保が難しいハードが必要だったりしてややハードルが高いのが難点でした。

が、Raspberry PiならHDMIにUSBデバイスが使える上、電子工作との相性も抜群ということもあって、Raspberry Pi上でIchigoJamを使えるようにしようということになったみたいです。

Raspberry Piもなかなかいいハードですけど、難点は”Linux”なこと。確かに、プログラム言語の自由度が高いんですが、プログラミング学習用に使うにも覚えなきゃいけない操作(コマンド、エディタ、パッケージインストール方法・・・)が多すぎ。

その点、IchigoJamはBASICですから、比較的簡単です。

思えば私もX1でBASICからスタートしたんですよね。

あの時いきなりLinuxだったら、多分挫折してたかなぁ・・・

今どきの言語の基礎にBASICはどうかという声もあるようですけど、プログラミングの基本はBASICであれPythonであれ同じですからねぇ。私は初等プログラム教育にはBASICでいいんじゃないかと思ってます。

AIだのIoTだのとIT分野の目覚ましい発展により、2045年にも第4の”シンギュラリティ”(人工知能が人間を超える分岐点)を迎えるとされる近未来。一方でIT技術者が不足気味という事情もあって、プログラミング教育に力を入れたいと思う人が増えてるみたいです。

論理性の育成にもつながり、私はいい傾向だと思いますが、その前にいまのIT業界の過酷な労働環境、プログラミング業務の軽視を何とかしないと、明るい未来が訪れないように思いますね。ほんと、何とかしてほしい・・・

Raspberry Pi3 Model B ボード&ケースセット 3ple Decker対応 (Element14版, Clear)-Physical Computing Lab

2017年3月26日 (日)

Raspberry Piを屋外で安定動作させるための太陽光パネル付き屋外キット

Raspberry Piを屋外で使いたいという方も増えつつあると思いますが、こちらはなかなか本格派。安心の国産品です。

ラズベリーパイ屋外稼動キット販売開始| IoT/M2M機器/Raspberry Pi用周辺機器-メカトラックス株式会社

福岡にあるメカトラックス社が提供しているこのRaspberry Pi屋外稼働キット。

電源として太陽光パネルに鉛蓄電池、3G回線が使える3GPi、そしてこれらを収める防水ボックスからなるキット。他にも電源やRaspberry Piの監視モジュールも搭載されてます。

当然ですが、このキットだけで単独稼働可能。Raspberry Piのほかに、多少のセンサー類を入れることも可能ですね。

広大な畑のど真ん中で気温、天候、そして監視カメラをつけての観測などもこれを使えば可能になりますね。

お値段は税抜きで198,000円。Raspberry Piのお値段を考えると高い気もしますが、監視機能や堅牢そうな電源を搭載していることを思えば、かなりお手軽な金額かもしれません。

真夏の炎天下の中でも動作するんですかね?気になるところはたくさんありますが、それだけこの製品が気になっているってことでしょうね。ちょっと欲しい気もします。今のところ、私には使い道がありませんが。

Raspberry Pi 3でつくる IoTプログラミング

2017年3月16日 (木)

湿度が下がったら自動的に加湿器をオンしてくれるように改造

家電機器の改造は危険なので、要注意です。が、こういう家電がそろそろ出てもいいんじゃないかと。

湿度が下がったら電源オンになるよう加湿器を改造してみた - 週刊アスキー

ASCII倶楽部にて、湿度が48%以下になったら自動的に起動、52%を超えると停止する加湿器の製作を紹介しているようです。

確かに加湿器ってあまりつけっぱなしにするとカビが生える原因になったりしますし、何よりもタンクが空になって補充するのが面倒。

てことで作られた電子工作事例のようですが、私がやるなら直接ではなく、Raspberry Pi+赤外線リモコンセンサーの組み合わせでやるかなぁと。これなら家電を直接改造しなくてもできますし。

そうでなくても、最近は”AI執事”なるものが登場しつつあります。Amazonのやつも今年中には日本上陸らしいです。加湿器だけでなく、照明、エアコン、扇風機あたりを自動で動かせる日もそう遠くはなさそうです。

LinkJapan 「eRemote mini」 イーリモートミニ スマホで家電をコントロール 外からも可能

2017年3月15日 (水)

かなり危険な自作火炎放射ロボット

なんだかシュールすぎる動画なので思わず紹介。

火炎放射ロボットを作ってみた。 | Maker is You!

Arduinoと3Dプリンター、そして簡易の火炎放射器を組み合わせて作った自作ロボット。

テストに使った人形といい、雑な消火作業といい、やる気があるのやらないのやら。

頑張れば作れそうな電子工作ですね。ただし、頑張る理由が見当たりませんが・・・

Arduinoをはじめよう 互換キット UNO R3対応互換ボード 初心者専用実験キット 基本部品セット20 in 1 Arduino sidekick basic kit

2017年3月13日 (月)

Raspberry Pi Zero向け小型カメラモジュール

Raspberry Pi Zero Ver.1.3と、海外では発売されたばかりのZero Wでは、純正のカメラモジュールが使えますが、通常サイズのRaspberry Piに合わせたモジュールではちょっと大きすぎ。

ところが、Raspberry Pi Zeroサイズのカメラモジュールというのがあるようです。

ラズベリーパイの調理法 Pi カメラをもっと小さく - Pi Zero 用ミニカメラモジュール

これなら確かにちょうどいいサイズですよね。

通常のカメラモジュールだと、普通のRaspberry Piで使うにもちょっと大きすぎるんですよね。これRaspberry Pi 3で使う方法ないんでしょうかね?ちょっと気に入りました。

肝心のRaspberry Pi Zero Ver.1.3やWが入手できていないため、うちではまだ使えませんが、いずれそのあたりを手に入れた暁には・・・

Raspberry Pi Zero Camera Module - ミニ カメラ モジュール for Pi Zero

TensorFlowで「けものフレンズ」の”フレンズ判別器”作ってみた

先日の記事(OpenCVによる”アニメ顔”検出)はこの伏線だったんです。

子供向けのような絵柄なのに、出てくるキャラがあまりにもIQ低め能天気なのに、背景にある世界観があまりにもシビアで意味深すぎて話題の「けものフレンズ」。

私もすっかりはまってます。サーバルちゃんが可愛すぎて死にそう、毎週楽しみで夜も眠れません。

ところがだんだんと登場人物・・・いやフレンズが増えてきたため、そろそろどの顔が何のフレンズさんかわからなくなってきた今日この頃。

こういう悩みにこそ”人工知能”の出番

てことで、TensorFlowを使って「けものフレンズ」の顔の画像を読み込むと何のフレンズかを答えてくれる判別器を作ってみました。

さて、それじゃあ今まで使ったTensorFlowのプログラムコードを使って…といきたいところですが。

実は今回使い勝手を大幅に向上させてます。

これまでは「TensorFlowで歴代「クラウン」の画像を判別させてみた」の記事でも参照させていただいた「TensorFlowでアニメゆるゆりの制作会社を識別する - kivantium活動日記」のコードをほぼそのまま流用させていただいてます。

これを使って”画像認識”の学習データを作るためには

(1) プログラムコードとTensorFlow実行環境を準備

(2) 教師データとなる画像と、その画像がなんの画像かをラベル分け

(3) (2)で集めた画像を訓練用(train)とテスト用(test)に分類(大体9対1)

(4) (3)の一覧リストを書いたファイル(train.txt、test.txt)を作成

という流れで、この最初の準備が大変でした。

おまけにデータを追加しようとすると、また(3)、(4)もやり直す羽目になり、これがなかなか面倒。

(1)、(2)は省きようがありませんけど、今回この面倒な(3)、(4)の部分を自動化することができました。

教師データ(今回は”けもフレ”キャラ顔画像)を準備し、それぞれがどんなキャラなのかを書いたフォルダに入れれば、勝手に(3)、(4)をやってくれるようにしました。

環境構築からの流れを以下に書きます。

(1) Windows上にTensorFlow実行環境を作成

TensorFlowがWindowsサポートしたのでインストールしてみた - デジタル・デザイン・ラボラトリーな日々

現状では、上のリンクのように「Anaconda for Windows」を入れて「TensorFlow」を入れるのが最も楽です。

今回のコードはOpenCVも使うため、上の操作に加えて

> conda install -c https://conda.binstar.org/menpo opencv3

という操作もお忘れなく。

今回はAnaconda 4.2.0 64ビット版 + TensorFlow 0.12.0が前提になってます。最新版では未確認です。

(2) ”教師データ”画像を集める

画像データを集めます。けもフレのフレンズさんの顔の部分を正方形に切り取ったものをひたすら集めてください。

動画やネットからひたすら手作業で集めてももちろんOKですが。

せっかくなんで、先回書いた「OpenCVによる”アニメ顔”検出」の記事にある通り、動画から自動的に画像を取り出させました。

取り出した画像を、フレンズさんの名前を書いたフォルダごとに分類。

Tfkemofr01

フォルダ名ですが、Windows版Anaconda+TensorFlowでは日本語が認識できないため、上のように英語表記でお願いします。

訓練させるためには、1キャラ当たり最低30枚は準備してください。正面向きの、目を閉じていないやつをチョイス。

Tfkemofr02

そのフォルダを「data」フォルダに入れて、学習用コード(cnn_train_56_a.py)と同じ階層に配置します。

注意点が2つ。

1、教師データのフォルダには画像データ以外入れない

2、各フォルダには最低でも10枚(できれば30枚以上)の画像データを入れる

これをやらないと、次の(3)のプログラムが動きません。ご注意を。

(3) 訓練実行

> python cnn_train_56_a.py

と実行すれば、各教師データからプログラムが勝手に訓練用・テスト用(9:1に分割)に分けてくれます(プログラム本体は下に載せてます)。

まさにプログラム実行者はただ教師データを準備すればいいんです!

ディープラーニングというよりスピード〇ーニングっぽいフレーズですね。

(4) 判別実行

上の訓練データを用いて、早速判別をさせてみます。

Tfkemofr03

訓練用コード「cnn_train_56_a.py」と同じ階層に、上の二つのファイル・フォルダを入れておきます。

cnn_app_56_a.py」はコード本体。「analysis」フォルダには上の教師データとは違う画像を入れておきます(コードは下に載せてます)。

Tfkemofr04

ネット上から適当に集めました。

実行結果はのちほど。

ここで訓練用コード「cnn_train_56_a.py」と判別用コード「cnn_app_56_a.py」を載せておきます。

◆ cnn_train_56_a.py

#!/usr/bin/env python

import glob
import os
import sys
import cv2
import numpy as np
import tensorflow as tf
import tensorflow.python.platform

path=os.getcwd()+'/data/'
class_count = 0
folder_list=os.listdir(path)

for folder in folder_list:

  class_count = class_count+1

NUM_CLASSES = class_count
IMAGE_SIZE = 56
IMAGE_PIXELS = IMAGE_SIZE*IMAGE_SIZE*3

flags = tf.app.flags
FLAGS = flags.FLAGS

flags.DEFINE_string('label', 'label.txt', 'File name of label')
flags.DEFINE_string('train_dir', './', 'Directory to put the training data.')
flags.DEFINE_integer('max_steps', 100, 'Number of steps to run trainer.')
flags.DEFINE_integer('batch_size', 20, 'Batch size'
                     'Must divide evenly into the dataset sizes.')
flags.DEFINE_float('learning_rate', 1e-4, 'Initial learning rate.')

def inference(images_placeholder, keep_prob):

    def weight_variable(shape):
      initial = tf.truncated_normal(shape, stddev=0.1)
      return tf.Variable(initial)

    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='SAME')

    def max_pool_2x2(x):
      return tf.nn.max_pool(x, ksize=[1, 2, 2, 1],
                            strides=[1, 2, 2, 1], padding='SAME')

    x_image = tf.reshape(images_placeholder, [-1, 56, 56, 3])

    with tf.name_scope('conv1') as scope:
        W_conv1 = weight_variable([3, 3, 3, 32])
        b_conv1 = bias_variable([32])
        h_conv1 = tf.nn.relu(conv2d(x_image, W_conv1) + b_conv1)

    with tf.name_scope('pool1') as scope:
        h_pool1 = max_pool_2x2(h_conv1)

    with tf.name_scope('conv2') as scope:
        W_conv2 = weight_variable([3, 3, 32, 64])
        b_conv2 = bias_variable([64])
        h_conv2 = tf.nn.relu(conv2d(h_pool1, W_conv2) + b_conv2)

    with tf.name_scope('pool2') as scope:
        h_pool2 = max_pool_2x2(h_conv2)

    with tf.name_scope('conv3') as scope:
        W_conv3 = weight_variable([3, 3, 64, 128])
        b_conv3 = bias_variable([128])
        h_conv3 = tf.nn.relu(conv2d(h_pool2, W_conv3) + b_conv3)

    with tf.name_scope('pool3') as scope:
        h_pool3 = max_pool_2x2(h_conv3)

    with tf.name_scope('fc1') as scope:
        W_fc1 = weight_variable([7*7*128, 1024])
        b_fc1 = bias_variable([1024])
        h_pool3_flat = tf.reshape(h_pool3, [-1, 7*7*128])
        h_fc1 = tf.nn.relu(tf.matmul(h_pool3_flat, W_fc1) + b_fc1)

        h_fc1_drop = tf.nn.dropout(h_fc1, keep_prob)

    with tf.name_scope('fc2') as scope:
        W_fc2 = weight_variable([1024, NUM_CLASSES])
        b_fc2 = bias_variable([NUM_CLASSES])

    with tf.name_scope('softmax') as scope:
        y_conv=tf.nn.softmax(tf.matmul(h_fc1_drop, W_fc2) + b_fc2)

    return y_conv

def loss(logits, labels):

    cross_entropy = -tf.reduce_sum(labels*tf.log(logits))

    tf.summary.scalar("cross_entropy", cross_entropy)
    return cross_entropy

def training(loss, learning_rate):

    train_step = tf.train.AdamOptimizer(learning_rate).minimize(loss)
    return train_step

def accuracy(logits, labels):

    correct_prediction = tf.equal(tf.argmax(logits, 1), tf.argmax(labels, 1))
    accuracy = tf.reduce_mean(tf.cast(correct_prediction, "float"))
    tf.summary.scalar("accuracy", accuracy)
    return accuracy

if __name__ == '__main__':

    count=0
    folder_list=os.listdir(path)

    train_image = []
    train_label = []
    test_image = []
    test_label = []

    f = open(FLAGS.label, 'w')
    for folder in folder_list:
      subfolder = os.path.join(path,folder)
      file_list = os.listdir(subfolder)

      filemax = 0

      for file in file_list:
        filemax = filemax + 1

# train : test = 9 : 1
      file_rate = int(filemax/10*9)

      i = 0

      for file in file_list:

        img = cv2.imread('./data/' + folder + '/' + file)
        img = cv2.resize(img, (IMAGE_SIZE, IMAGE_SIZE))
        if i <= file_rate:
           train_image.append(img.flatten().astype(np.float32)/255.0)
           tmp = np.zeros(NUM_CLASSES)
           tmp[int(count)] = 1
           train_label.append(tmp)
        else:
           test_image.append(img.flatten().astype(np.float32)/255.0)
           tmp = np.zeros(NUM_CLASSES)
           tmp[int(count)] = 1
           test_label.append(tmp)

        i = i + 1

      label_name = folder + '\n'
      f.write(label_name)
      count=count+1
    f.close()

    train_image = np.asarray(train_image)
    train_label = np.asarray(train_label)
    test_image = np.asarray(test_image)
    test_label = np.asarray(test_label)

    with tf.Graph().as_default():

        images_placeholder = tf.placeholder("float", shape=(None, IMAGE_PIXELS))
        labels_placeholder = tf.placeholder("float", shape=(None, NUM_CLASSES))

        keep_prob = tf.placeholder("float")

        logits = inference(images_placeholder, keep_prob)

        loss_value = loss(logits, labels_placeholder)

        train_op = training(loss_value, FLAGS.learning_rate)

        acc = accuracy(logits, labels_placeholder)

        saver = tf.train.Saver()

        sess = tf.Session()

        sess.run(tf.global_variables_initializer())

        summary_op = tf.summary.merge_all()
        summary_writer = tf.summary.FileWriter(FLAGS.train_dir, sess.graph)

        for step in range(FLAGS.max_steps):
            for i in range(int(len(train_image)/FLAGS.batch_size)):

                batch = FLAGS.batch_size*i

                sess.run(train_op, feed_dict={
                  images_placeholder: train_image[batch:batch+FLAGS.batch_size],
                  labels_placeholder: train_label[batch:batch+FLAGS.batch_size],
                  keep_prob: 0.5})

            train_accuracy = sess.run(acc, feed_dict={
                images_placeholder: train_image,
                labels_placeholder: train_label,
                keep_prob: 1.0})
            print ("step %d, training accuracy %g"%(step, train_accuracy))

            summary_str = sess.run(summary_op, feed_dict={
                images_placeholder: train_image,
                labels_placeholder: train_label,
                keep_prob: 1.0})
            summary_writer.add_summary(summary_str, step)

    print ("test accuracy %g"%sess.run(acc, feed_dict={
        images_placeholder: test_image,
        labels_placeholder: test_label,
        keep_prob: 1.0}))

    save_path = saver.save(sess, "./model.ckpt")

◆ cnn_app_56_a.py

#!/usr/bin/env python

import glob
import os
import sys
import numpy as np
import tensorflow as tf
import cv2

path=os.getcwd()+'/analysis/'
file_list=os.listdir(path)

i = 0
label_name = []

flags = tf.app.flags
FLAGS = flags.FLAGS
flags.DEFINE_string('label','label.txt','File name of label')

f = open(FLAGS.label,'r')
for line in f:
  line = line.rstrip()
  l = line.rstrip()
  label_name.append(l)
  i = i + 1

NUM_CLASSES = i
IMAGE_SIZE = 56
IMAGE_PIXELS = IMAGE_SIZE*IMAGE_SIZE*3

def inference(images_placeholder, keep_prob):

    def weight_variable(shape):
      initial = tf.truncated_normal(shape, stddev=0.1)
      return tf.Variable(initial)

    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='SAME')

    def max_pool_2x2(x):
      return tf.nn.max_pool(x, ksize=[1, 2, 2, 1],
                            strides=[1, 2, 2, 1], padding='SAME')

    x_image = tf.reshape(images_placeholder, [-1, 56, 56, 3])

    with tf.name_scope('conv1') as scope:
        W_conv1 = weight_variable([3, 3, 3, 32])
        b_conv1 = bias_variable([32])
        h_conv1 = tf.nn.relu(conv2d(x_image, W_conv1) + b_conv1)

    with tf.name_scope('pool1') as scope:
        h_pool1 = max_pool_2x2(h_conv1)

    with tf.name_scope('conv2') as scope:
        W_conv2 = weight_variable([3, 3, 32, 64])
        b_conv2 = bias_variable([64])
        h_conv2 = tf.nn.relu(conv2d(h_pool1, W_conv2) + b_conv2)

    with tf.name_scope('pool2') as scope:
        h_pool2 = max_pool_2x2(h_conv2)

    with tf.name_scope('conv3') as scope:
        W_conv3 = weight_variable([3, 3, 64, 128])
        b_conv3 = bias_variable([128])
        h_conv3 = tf.nn.relu(conv2d(h_pool2, W_conv3) + b_conv3)

    with tf.name_scope('pool3') as scope:
        h_pool3 = max_pool_2x2(h_conv3)

    with tf.name_scope('fc1') as scope:
        W_fc1 = weight_variable([7*7*128, 1024])
        b_fc1 = bias_variable([1024])
        h_pool3_flat = tf.reshape(h_pool3, [-1, 7*7*128])
        h_fc1 = tf.nn.relu(tf.matmul(h_pool3_flat, W_fc1) + b_fc1)
        h_fc1_drop = tf.nn.dropout(h_fc1, keep_prob)

    with tf.name_scope('fc2') as scope:
        W_fc2 = weight_variable([1024, NUM_CLASSES])
        b_fc2 = bias_variable([NUM_CLASSES])

    with tf.name_scope('softmax') as scope:
        y_conv=tf.nn.softmax(tf.matmul(h_fc1_drop, W_fc2) + b_fc2)

    return y_conv

if __name__ == '__main__':
    test_image = []
    test_filenm = []

    for file in file_list:
        test_filenm.append(file)

        img = cv2.imread('./analysis/' + file )
        img = cv2.resize(img, (IMAGE_SIZE, IMAGE_SIZE))
        test_image.append(img.flatten().astype(np.float32)/255.0)

    test_image = np.asarray(test_image)

    images_placeholder = tf.placeholder("float", shape=(None, IMAGE_PIXELS))
    labels_placeholder = tf.placeholder("float", shape=(None, NUM_CLASSES))
    keep_prob = tf.placeholder("float")

    logits = inference(images_placeholder, keep_prob)
    sess = tf.InteractiveSession()

    saver = tf.train.Saver()
    sess.run(tf.global_variables_initializer())
    saver.restore(sess, "./model.ckpt")

    for i in range(len(test_image)):
        accr = logits.eval(feed_dict={
            images_placeholder: [test_image[i]],
            keep_prob: 1.0 })[0]
        pred = np.argmax(logits.eval(feed_dict={
            images_placeholder: [test_image[i]],
            keep_prob: 1.0 })[0])

        pred_label = label_name[pred]
#        print (pred,accr)
        print (test_filenm[i],' , ',pred_label)

あまりきれいなコードではありませんが・・・これをそれぞれコピペしてお使いください。

ちなみに、ベースは「TensorFlowの画像認識コードを高解像度化してみた: EeePCの軌跡」で使った56×56のを使ってます。

さすがに今回の教師用データを配布するわけにはいかないので、こちらはご自分でご用意願います。

で、今回用意したフレンズさんの種類は27種類

8話までの画像を使ったんですが、すでにこの数です。

30枚以上の顔画像が取れたフレンズさんしか使わなかったんですけど、いったい全部で何種類出てるんでしょうかね?

なお、「OpenCVによる”アニメ顔”検出」を使った顔画像検出でほとんどのキャラは抽出できたんですが、ハシビロコウさんライオンさんマーゲイさんはほとんど抽出できず。結局、手で集めました。

ハシビロコウさんは特に顔の表情がなさすぎるので、画像集めには結構苦労します。

サーバルちゃんとカバンちゃんはさすがに全話登場しているため何百枚でも集まってしまうのですが、他とのバランスで100枚程度まで絞ってます。

27種類で200サイクルの学習を実行、これをメインPC(Corei3-3220 3.3GHz)で学習させたら2時間かかりました。うわーったいへんだねー。

で、判別した結果は

Kemono04

左が画像のファイル名、右が判定結果。

黄色が正解だった項目を示してます。

31枚中、正解は21枚。判別率67.7%。

同人系の画像の判別率が半分程度と低かったですね。もうちょっと教師データがあれば認識率上がるかもしれませんが。ハカセと助手も認識率低く、ほとんど外れでした。

なぜか”マーゲイ”さんに判定される事例が多いのが気になるところ。

あの顔って、もしかしてけものフレンズでは”標準形”なんでしょうか?

で、ちょっと調子に乗ってこんな実験も。

Kemono06

デジカメで撮った画像から取り出したリアルな人の顔画像で判別させてみました。

幼稚園の運動会の画像を使ったせいか、様々な顔を大量に取得(約100枚)。

Kemono05

結果はこんな感じ(名前の後ろの数字は気にしないでください)。

カバンちゃん、コウテイペンギンさん、イワトビペンギンさんが多いですね。スナネコ、ライオンもちょくちょく。

なお、私の顔写真が3枚混じってましたが、判定は「カバン」×2、「コウテイペンギン」×1。私はどうやら”カバンちゃん”らしいです。

なぜか次男は”トキさん”に分類されることが多いですね。確かに音痴ですが。

妻と長男は「コウテイペンギンさん」と無難な(?)分類結果に。

先のOpenCVでの顔検出のコード等と組み合わせて、顔を撮影すると「君は〇〇なフレンズだね!」って言ってくれるやつを作ろうかとも考えてますが。

正直、大して似ていない画像に判別されることが多いので、ちょっとモチベーション下がってます。

なお、まだ「けものフレンズ」は終わっていないので、今後画像を増やすことも可能。まだ8話までのデータですし。

今回、いちいち学習データの一覧リストを作らなくても「data」フォルダに画像ファイルを入れたフォルダを放り込んだら勝手に訓練できるようになったので、判別器のアップデートがずいぶん楽になりました。

同じコードを私の職場にも送って使ってますが、画像データが増えたらすぐ実行すればいいだけなので、運用も楽になりました。もっとも、私しか使ってませんけど。

将来的には、画像が増えたら所定の場所に登録してもらい、毎晩訓練コードを自動起動させて、翌日にはアップデートされた画像認識が使える、なんて運用も可能にできます。

ちょっと手軽にWindows上でTensorFlowの画像認識を実験してみたいという方、ぜひこちらのコード、おすすめです。

けものフレンズBD付オフィシャルガイドブック (1)

2017年3月11日 (土)

128×128の1.5インチOLED付Arduino互換ボード”Pixel 2.0”

小型ながらOLEDついてるため、これ単体でいろいろとできそうですね。

Pixel 2.0 Arduinoボードには1.5インチのOLEDスクリーンが載ってるからウェアラブルのゲーム機なども作れる | TechCrunch Japan

1.5インチ 128×128の65k色のOLED搭載のArduino互換機”Pixel 2.0”がKickstarterにて資金募集中です。

Pixel 2.0 - The Arduino compatible smart display! by Rabid Prototypes — Kickstarter

ごらんのとおり、OLEDパネルが引っ付いた基板に、microSDカードリーダーまでついてます。

小型のArduino(と互換機)にありがちなPCとの接続性や取り回しの悪さはなく、うまくいろいろな機能を集約してますね。

このモニター、Raspberry Piも見習ってほしいところですね。

Arduinoを使ったゲーム機なんてのもあるくらいですから、これなら立派な超小型ゲーム機が作れそう。もっとも、老眼の入り始めた(私をはじめとする)おっさんにはつらいですけどね。

一つ75ドル(約8500円)から。すでに目標の5,000ドルを突破済み。うまくいけば今年の6月には発送開始だそうです。

HiLetgo Mini USB Nano V3.0 ATmega328P CH340G 5V 16M マイクロコントローラーボード Arduinoと互換

TensorFlowの画像認識コードを高解像度化してみた

Re:ゼロから始めるディープラーニング: EeePCの軌跡で宣言した通り、まったりとディープラーニングの勉強を進めてます。

ついでに職場にある私のワークステーションにもTensorFlowをインストールして、CAEがらみの画像認識実験もちょくちょくやっております。

といっても、コードは以下の記事で使用したものをほぼそのまま使ってるだけ。

TensorFlowで”日本のお城”を識別させてみた: EeePCの軌跡

このコード、画像を28×28で読み込んで学習・認識しているため、ちょっと低解像度過ぎるのが難点。

元々MNISTの手書き文字の認識用のためこの解像度でも行けたんでしょうが、私が使うレベルの画像認識にはもう少し解像度が欲しいところ。

ということで、縦横それぞれ倍の56×56に拡張してみました。

ただ、今までのコードの「28×28」の部分を「56×56」にしただけでは、画像認識の精度がかえって下がります。

このため、この辺りの記事を参考に畳み込み層+プーリング層のペアを一つ増やしてみました。

続・TensorFlowでのDeep Learningによるアイドルの顔識別 - すぎゃーんメモ

TensorFlowによるももクロメンバー顔認識(中編) - Qiita

これで高解像度化&認識率アップできます。

元々が「畳み込み層+プーリング層」×2+結合層×2の6層でしたが。

高解像度化に伴い「畳み込み層+プーリング層」×3+結合層×2の8層になりました。

◆学習用コード「cnn_train_56.py」

#!/usr/bin/env python
import sys
import cv2
import numpy as np
import tensorflow as tf
import tensorflow.python.platform

NUM_CLASSES = 7
IMAGE_SIZE = 56
IMAGE_PIXELS = IMAGE_SIZE*IMAGE_SIZE*3

flags = tf.app.flags
FLAGS = flags.FLAGS
flags.DEFINE_string('train', 'train.txt', 'File name of train data')
flags.DEFINE_string('test', 'test.txt', 'File name of train data')
flags.DEFINE_string('train_dir', '/mnt/c/linux/data', 'Directory to put the training data.')
flags.DEFINE_integer('max_steps', 300, 'Number of steps to run trainer.')
flags.DEFINE_integer('batch_size', 20, 'Batch size'
                     'Must divide evenly into the dataset sizes.')
flags.DEFINE_float('learning_rate', 1e-4, 'Initial learning rate.')

def inference(images_placeholder, keep_prob):

    def weight_variable(shape):
      initial = tf.truncated_normal(shape, stddev=0.1)
      return tf.Variable(initial)

    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='SAME')

    def max_pool_2x2(x):
      return tf.nn.max_pool(x, ksize=[1, 2, 2, 1],
                            strides=[1, 2, 2, 1], padding='SAME')

    x_image = tf.reshape(images_placeholder, [-1, 56, 56, 3])

    with tf.name_scope('conv1') as scope:
        W_conv1 = weight_variable([3, 3, 3, 32])
        b_conv1 = bias_variable([32])
        h_conv1 = tf.nn.relu(conv2d(x_image, W_conv1) + b_conv1)

    with tf.name_scope('pool1') as scope:
        h_pool1 = max_pool_2x2(h_conv1)

    with tf.name_scope('conv2') as scope:         W_conv2 = weight_variable([3, 3, 32, 64])         b_conv2 = bias_variable([64])         h_conv2 = tf.nn.relu(conv2d(h_pool1, W_conv2) + b_conv2)     with tf.name_scope('pool2') as scope:
        h_pool2 = max_pool_2x2(h_conv2)

    with tf.name_scope('conv3') as scope:
        W_conv3 = weight_variable([3, 3, 64, 128])
        b_conv3 = bias_variable([128])
        h_conv3 = tf.nn.relu(conv2d(h_pool2, W_conv3) + b_conv3)

    with tf.name_scope('pool3') as scope:
        h_pool3 = max_pool_2x2(h_conv3)

    with tf.name_scope('fc1') as scope:
        W_fc1 = weight_variable([7*7*128, 1024])
        b_fc1 = bias_variable([1024])
        h_pool3_flat = tf.reshape(h_pool3, [-1, 7*7*128])
        h_fc1 = tf.nn.relu(tf.matmul(h_pool3_flat, W_fc1) + b_fc1)

        h_fc1_drop = tf.nn.dropout(h_fc1, keep_prob)

    with tf.name_scope('fc2') as scope:
        W_fc2 = weight_variable([1024, NUM_CLASSES])
        b_fc2 = bias_variable([NUM_CLASSES])

    with tf.name_scope('softmax') as scope:
        y_conv=tf.nn.softmax(tf.matmul(h_fc1_drop, W_fc2) + b_fc2)

    return y_conv

def loss(logits, labels):

    cross_entropy = -tf.reduce_sum(labels*tf.log(logits))

    tf.summary.scalar("cross_entropy", cross_entropy)
    return cross_entropy

def training(loss, learning_rate):

    train_step = tf.train.AdamOptimizer(learning_rate).minimize(loss)
    return train_step

def accuracy(logits, labels):

    correct_prediction = tf.equal(tf.argmax(logits, 1), tf.argmax(labels, 1))
    accuracy = tf.reduce_mean(tf.cast(correct_prediction, "float"))
    tf.summary.scalar("accuracy", accuracy)
    return accuracy

if __name__ == '__main__':

    f = open(FLAGS.train, 'r')
    train_image = []
    train_label = []
    for line in f:
        line = line.rstrip()         l = line.split()
        img = cv2.imread(l[0])
        img = cv2.resize(img, (56, 56))
        train_image.append(img.flatten().astype(np.float32)/255.0)
        tmp = np.zeros(NUM_CLASSES)
        tmp[int(l[1])] = 1
        train_label.append(tmp)
    train_image = np.asarray(train_image)
    train_label = np.asarray(train_label)
    f.close()
    f = open(FLAGS.test, 'r')
    test_image = []
    test_label = []
    for line in f:
        line = line.rstrip()
        l = line.split()
        img = cv2.imread(l[0])
        img = cv2.resize(img, (56, 56))
        test_image.append(img.flatten().astype(np.float32)/255.0)
        tmp = np.zeros(NUM_CLASSES)
        tmp[int(l[1])] = 1
        test_label.append(tmp)
    test_image = np.asarray(test_image)
    test_label = np.asarray(test_label)
    f.close()

    with tf.Graph().as_default():

        images_placeholder = tf.placeholder("float", shape=(None, IMAGE_PIXELS))

        labels_placeholder = tf.placeholder("float", shape=(None, NUM_CLASSES))

        keep_prob = tf.placeholder("float")
        logits = inference(images_placeholder, keep_prob)
        loss_value = loss(logits, labels_placeholder)
        train_op = training(loss_value, FLAGS.learning_rate)
        acc = accuracy(logits, labels_placeholder)

            saver = tf.train.Saver()
        sess = tf.Session()

        sess.run(tf.global_variables_initializer())

        summary_op = tf.summary.merge_all()
        summary_writer = tf.summary.FileWriter(FLAGS.train_dir, sess.graph)

        for step in range(FLAGS.max_steps):
            for i in range(int(len(train_image)/FLAGS.batch_size)):

                batch = FLAGS.batch_size*i

                sess.run(train_op, feed_dict={
                  images_placeholder: train_image[batch:batch+FLAGS.batch_size],
                  labels_placeholder: train_label[batch:batch+FLAGS.batch_size],
                  keep_prob: 0.5})

            train_accuracy = sess.run(acc, feed_dict={
                images_placeholder: train_image,
                labels_placeholder: train_label,
                keep_prob: 1.0})
            print ("step %d, training accuracy %g"%(step, train_accuracy))

            summary_str = sess.run(summary_op, feed_dict={
                images_placeholder: train_image,
                labels_placeholder: train_label,
                keep_prob: 1.0})
            summary_writer.add_summary(summary_str, step)

    print ("test accuracy %g"%sess.run(acc, feed_dict={
        images_placeholder: test_image,
        labels_placeholder: test_label,
        keep_prob: 1.0}))

    save_path = saver.save(sess, "./model.ckpt")

◆認識用コード「cnn_app2_56.py」

#!/usr/bin/env python

import sys
import numpy as np
import tensorflow as tf
import cv2

NUM_CLASSES = 7
IMAGE_SIZE = 56
IMAGE_PIXELS = IMAGE_SIZE*IMAGE_SIZE*3

flags = tf.app.flags
FLAGS = flags.FLAGS
flags.DEFINE_string('analysis', 'analysis.txt', 'File name of analysis data')

def inference(images_placeholder, keep_prob):

    def weight_variable(shape):
      initial = tf.truncated_normal(shape, stddev=0.1)
      return tf.Variable(initial)

    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='SAME')

    def max_pool_2x2(x):
      return tf.nn.max_pool(x, ksize=[1, 2, 2, 1],
                            strides=[1, 2, 2, 1], padding='SAME')

    x_image = tf.reshape(images_placeholder, [-1, 56, 56, 3])

    with tf.name_scope('conv1') as scope:
        W_conv1 = weight_variable([3, 3, 3, 32])
        b_conv1 = bias_variable([32])
        h_conv1 = tf.nn.relu(conv2d(x_image, W_conv1) + b_conv1)

    with tf.name_scope('pool1') as scope:
        h_pool1 = max_pool_2x2(h_conv1)

    with tf.name_scope('conv2') as scope:
        W_conv2 = weight_variable([3, 3, 32, 64])
        b_conv2 = bias_variable([64])
        h_conv2 = tf.nn.relu(conv2d(h_pool1, W_conv2) + b_conv2)

    with tf.name_scope('pool2') as scope:
        h_pool2 = max_pool_2x2(h_conv2)

    with tf.name_scope('conv3') as scope:
        W_conv3 = weight_variable([3, 3, 64, 128])
        b_conv3 = bias_variable([128])
        h_conv3 = tf.nn.relu(conv2d(h_pool2, W_conv3) + b_conv3)

    with tf.name_scope('pool3') as scope:
        h_pool3 = max_pool_2x2(h_conv3)

    with tf.name_scope('fc1') as scope:
        W_fc1 = weight_variable([7*7*128, 1024])
        b_fc1 = bias_variable([1024])
        h_pool3_flat = tf.reshape(h_pool3, [-1, 7*7*128])
        h_fc1 = tf.nn.relu(tf.matmul(h_pool3_flat, W_fc1) + b_fc1)
        h_fc1_drop = tf.nn.dropout(h_fc1, keep_prob)

    with tf.name_scope('fc2') as scope:
        W_fc2 = weight_variable([1024, NUM_CLASSES])
        b_fc2 = bias_variable([NUM_CLASSES])

    with tf.name_scope('softmax') as scope:
        y_conv=tf.nn.softmax(tf.matmul(h_fc1_drop, W_fc2) + b_fc2)

    return y_conv

if __name__ == '__main__':

    test_image = []
    test_filenm = []
#    for i in range(1, len(sys.argv)):
#        img = cv2.imread(sys.argv[i])
#        img = cv2.resize(img, (56, 56))
#        test_image.append(img.flatten().astype(np.float32)/255.0)
#    test_image = np.asarray(test_image)
    f = open(FLAGS.analysis, 'r')
    for line in f:
        line = line.rstrip()
        l = line.split()

        test_filenm.append(l[0])
        img = cv2.imread(l[0])
        img = cv2.resize(img, (56, 56))
        test_image.append(img.flatten().astype(np.float32)/255.0)
    test_image = np.asarray(test_image)
    f.close()

    images_placeholder = tf.placeholder("float", shape=(None, IMAGE_PIXELS))
    labels_placeholder = tf.placeholder("float", shape=(None, NUM_CLASSES))
    keep_prob = tf.placeholder("float")

    logits = inference(images_placeholder, keep_prob)
    sess = tf.InteractiveSession()

    saver = tf.train.Saver()
    sess.run(tf.global_variables_initializer())
    saver.restore(sess, "./model.ckpt")

    for i in range(len(test_image)):
        accr = logits.eval(feed_dict={
            images_placeholder: [test_image[i]],
            keep_prob: 1.0 })[0]
        pred = np.argmax(logits.eval(feed_dict={
            images_placeholder: [test_image[i]],
            keep_prob: 1.0 })[0])
        print (test_filenm[i],pred,accr)

一見すると何の進歩もなさそうに見えますが、以前よりちょくちょくパラメータ値が変わってるところがあります。

まず、Anaconda 4.2.0 for Windows(64bit)+TensorFlow 0.12.0でWarningが出てたのを全部修正しました。

また、認識用コードは複数のファイルを読めるようにしました。

analysis.txt」というファイルに認識させたい画像ファイル名を書き込んでおくと、その中にあるファイルをいっぺんに処理してくれます。

相変わらず学習用コードには”教師データファイル名+ラベル”が書かれた訓練用ファイル一覧「train.txt」とテスト用ファイル一覧「test.txt」の2つも必要です。この辺は先の記事を参考に。

早速、これを使って画像認識させてみました。

使ったのはTensorFlowで”日本のお城”を識別させてみた: EeePCの軌跡で使った画像データ。

Castle02

Anaconda for Windowsのコマンドラインで

> python cnn_train_56.py

と入力して実行。

縦横2倍、データ量にして4倍に増えたため、さすがに時間かかります。

うちのメインPC(Corei3 3220 2コア4スレッド 3.3GHz)で300サイクル回すと40分ほどかかりました。以前は数分で終了。データは4倍ですが、時間は4倍以上かかります。

というのもデータ量が増えただけでなく、収束するまでのサイクルも増加。

以前は70~100サイクルも回せば十分だったのが、今回は最低でも150サイクルは必要で、余裕を見て300サイクル回してます。

学習が終了したら、今度は”認識”させてみます。

認識用に用意したこちらの画像。

Tf_casle01

各お城 3枚づつのデータを準備。

それぞれのお城のラベル(0~6)を各ファイル名の頭につけておきました。

この画像ファイル名を一覧にしたファイル「analysis.txt」を準備して

> python cnn_app2_56.py

と実行。

返ってきた結果は

Tf_casle02

こんな感じになりました。

「判定」の黄色に塗った部分が正解だった画像。

横の%でかかれた数字は、それぞれのラベルの判定率。最も数値の大きかったところをオレンジで塗ってます。

黒枠のところにオレンジが収まっているのが正解。ですが、熊本城、犬山城、大坂城でそれぞれはずれが出てますね。

全21枚中、当たりは17枚。正解率 81.0%。

特に”犬山城”のはずれっぷりが目立ちますが、犬山城の画像のうち、2枚だけは学習に使った画像よりちょっと斜め上から見たものを認識させたんです。

天守閣中央に特徴的な屋根の形があるので少々ずれてても認識するんじゃねぇ?と思って入れたんですが、やはりだめでしたね。

犬山城をのぞけば高解像度化のおかげか、まあまあの正解率。実質9割くらいじゃないかなぁと。なかなかやりますね。

ちなみに職場では「畳み込み層+プーリング層」をさらに一つ増やして112×112まで解像度を増やしたコードを作成してます。

ただ、自宅で用意した教師データは64×64になっているため、自宅用は56×56どまり。

実は112×112にしても正解率がかえって下がってしまってるため、現在いろいろとチューニングの真っ最中。うまく行ったら、またブログにて公開します。

ただ人の顔やお城くらいなら、56×56の解像度でも大丈夫っぽいですね。

しかし勉強の途中でコードをいじりだして大丈夫か?と思われそうですが。

ただ、この動くコードというのは、プログラミングを勉強する上では結構大事。

「ゼロから作るDeep Learning」を読みながらこのコードを読むと、例えば”loss”っていう関数が一体何をやってるのか?など理解が進みます。

本を読むだけでは眠くなっちゃいますが、手を動かしてコードを書き換えてみて実際の動きを確認しながら読み進めると、いろいろとわかって面白いです。

「ゼロから作るDeep Learning」自体はTensorFlowを使っているわけではありませんが、やってることは同じなので、TensorFlowのコードでも非常に参考になるんです。

とりあえず実際に動くコードを手元において、教材片手にいじりながら読むのはおすすめです。

ゼロから作るDeep Learning ―Pythonで学ぶディープラーニングの理論と実装

2017年3月 8日 (水)

お米の量をスマホに知らせてくれる米びつセンサー

米ライフというところが「米びつセンサー」なるものを作ってます。

米の残量をアプリで確認し、タイムリーに注文できるサービス開始 - 週刊アスキー

この米びつセンサー、米びつにあるお米の量を測定するもので、少なくなるとスマホから直接注文できるというサービスのためのセンサーみたいです。

まさにIoTな感じのセンサーですが、頑張れば作れそうな気もします。でも、結構コンパクトですねぇ。電源はどうするんでしょうか?

センサー自体の値段が書かれてませんが、お米は5kgあたり3,000円とあるので、こしひかりあたりでしょうかね?

AmazonもDash Buttonを作って、なくなったらすぐに注文できる仕組みをリリースしてますが、いちいち買いに行かなくても手元まで届けてくれるのは便利ですよね。

一方で、こうした通販の荷物量が増加したおかげで、宅配業者の負担が増加しつつあるのが現状。ヤマト運輸あたりが大変なことになってます。

私はお米や洗剤くらいは自分の足で買いに行くようにしたいですね。Raspberry Piや電子部品など、どうしても手に入らないものはしょうがないですが。

【精米】お米の横綱 魚沼産コシヒカリ 5kg (国産) 平成28年産

2017年3月 7日 (火)

Windows 98スマートウォッチを自作したユーザー登場

スマートなんだか、レトロなんだかわからないネタですが。

Windows 98が動くスマートウォッチをRaspberry Piで作成した猛者が登場 - GIGAZINE

Raspberry PiベースにWindows 98を手首に収めてしまった人が登場です。

ずいぶん小型ですが、使われたのはRaspberry Pi ZeroではなくてRaspberry Pi A+。2.4インチタッチスクリーン液晶に1000mAhバッテリー、スイッチ、ボタンで構成されてます。

もちろんRaspberry PiでWindows 98が動くわけはありません。エミュレータです。QEMUというのを使ってるそうな。

QEMU

さすがに実用速度とはいいがたいようですけど、これがWindows 98の出た19年前にあったら大騒ぎですね。

あのころはLibretto 20でも”小型PC”だったのに、腕につけた画面にWindowsが動いていたら卒倒ものです。

タイムスリップもののラノベ・漫画・アニメって多いですが、そろそろ80年代にRaspberry PiとiPhoneを持った主人公が現れて・・・なんてのが出てきても面白いかも。なぜかスマホの電波だけはその時代でも使えて、未来の変態技を80年代で再現させて・・・いや面白いか?そんなの。

そういえば、今年中にはフルのWindows 10が載ったARMチップの端末が登場するらしいですし、ガチでこういう端末が作れそうです。Windows 10で。

cocopar 4インチIPSハイビジョンLCD ディスプレイ タッチパネル タッチペン付

より以前の記事一覧

当ブログ内検索

  • カスタム検索

スポンサード リンク

ブログ村

無料ブログはココログ