読者です 読者をやめる 読者になる 読者になる

The jonki

呼ばれて飛び出てじょじょじょじょーんき

ソニーのラジオICF-A101をArduinoを使って赤外線リモコンで操作してみた

ソニーのラジオICF-A101といういぶし銀なラジオを持っているのですが,リモコンがないので操作が面倒でした.そこで電源だけでもリモコンで操作したいなと思ってこれを作りました.久しぶりの記事のくせに小ネタですみません.

使っているものはこんなところです.Arduinoはminiとか小さい方がスッキリしますが手持ちがなかったのでUNOで.


IR周りのコードを書くのはだるかったのでライブラリを探したらありました.ダウンロードしてArduino/librariesとかのフォルダに突っ込みましょう.
IRremote Library, Send & Receive Infrared Remote Control

サーボは9番ピンに信号線,赤外線受信モジュールは6番ピンへ.それぞれVとGNDにつなぐこともお忘れず.赤外線モジュールは目玉を自分の方に向けた時,右からV,GND,信号線の順につなげます.


コードも適当です.赤外線信号を受信するとメーカーとその信号値を出力するサンプルがあったのでこれを利用します.適当なチャンネルのコマンドの値(私の場合は2fd20dfという値にしました)と比較して見つかった時に,サーボをちょっと回転させることで電源ボタンを押しています.

#include <IRremote.h>
#include <Servo.h>


const int RECV_PIN = 6;
IRrecv irrecv(RECV_PIN);
decode_results results;
Servo myservo;

void setup()
{
  Serial.begin(9600);
  irrecv.enableIRIn(); // Start the receiver
  irrecv.blink13(true);

  myservo.attach(9);
  myservo.write(10);
}

void loop() {
  if (irrecv.decode(&results)) {
    if (results.decode_type == NEC) {
      Serial.print("NEC: ");
    } else if (results.decode_type == SONY) {
      Serial.print("SONY: ");
    } else if (results.decode_type == RC5) {
      Serial.print("RC5: ");
    } else if (results.decode_type == RC6) {
      Serial.print("RC6: ");
    } else if (results.decode_type == UNKNOWN) {
      Serial.print("UNKNOWN: ");
    }
    Serial.println(results.value, HEX);
    String result_val = String(results.value, HEX);
    if(String(results.value, HEX).equals(String("2fd20df"))) {  // あなたの利用したい信号で
      Serial.println("FOUND");
      myservo.write(30); // 電源ボタンのプッシュアクション
      delay(200);
      myservo.write(10); // 元の位置に戻る
    }
    irrecv.resume(); // Receive the next value
  }
}

以上小ネタでした.

cmakeでgoogletestできる環境を作ってみる

はじめに

googletestをcmakeで動かす必要があったのでまとめました.googletestはc++のテストフレームワークで,cmakeはコンパイラに依存しないビルド自動化のソフトです.tjunさんが既に記事を書いてくれていますが,私は生Makefileはゆとりすぎて読めないのでcmakeで焼き直しました.
GoogleTestでC++のコードをテストする - Qiita

テスト用に簡単なサンプルプロジェクトをgithubにアップしました.READMEに最低限書いてますが,それを説明します.
github.com

プロジェクト構成

今回はこのようなフォルダ構成にしてみました.src/の下にmy_mathという自作のライブラリを作ってあります.それをtest/下のテストクラスで叩いてバグがないか確認してみる,という設定です.

.
|-- CMakeLists.txt
|-- src
|   |-- CMakeLists.txt
|   |-- main.cc
|   |-- my_math.cc
|   `-- my_math.h
`-- test
    |-- CMakeLists.txt
    `-- test.cc


この中のmy_mathライブラリのsum関数はあえて間違って実装しています.

#include  "my_math.h"

double sqr(double v) {
  return (v*v);
}

double sum(double v1, double v2) {
  return v1 - v2; // Oops! bug!
}

cmakeファイル

CMakeLists.txtはフォルダ毎に用意して疎に書いておくのが良いと思っています.

ルート直下のCMakeLists.txt
ここでやっていることはadd_subdirectoryで下位フォルダのCMakeLists.txtを読み込みに行きます.またenable_testingでctestを有効にし,add_testで具体的なテストを指定しています.ctestとはcmakeについているテスト実行の支援ツールです.

cmake_minimum_required(VERSION 2.8)
project (MYTEST)

add_subdirectory(src)
add_subdirectory(test)
enable_testing()
add_test(NAME MyTest COMMAND Test)

src/のCMakeLists.txt
ここではmy_mathライブラリを作り,mainのプログラムでlibmy_math.aをリンクしています.リンク時のライブラリ名の指定の時に'lib'のプレフィックスは省略できます.またこのlibmy_math.aはテストにも利用します.

cmake_minimum_required(VERSION 2.8)
add_library(my_math STATIC my_math.cc)

add_executable(demo main.cc)
target_link_libraries(demo my_math)

test/のCMakeLists.txt
少し長いですが,難しいことはありません.googletestライブラリのインクルードディレクトリ及びライブラリディレクトリを指定し,test.ccに対してlibgtest.a,libgtest_main.a,pthreadをリンクしているだけです.pthreadはgoogletestで必要になりますので忘れないように.またGTEST_ROOTで指定しているgoogletestのインストールディレクトリは適宜読み替えてください.

cmake_minimum_required(VERSION 2.8)

# set runtime directory
set (CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})

# declare GTEST_ROOT
set(GTEST_ROOT ~/googletest/googletest/)

# Include files directory
include_directories(${GTEST_ROOT}/include/)

# Library directory
link_directories(${GTEST_ROOT}/build/)

# Generate executable file
add_executable(Test ${CMAKE_CURRENT_SOURCE_DIR}/test.cc)

# Link libraries
target_link_libraries(Test my_math gtest gtest_main pthread)
テストファイルと実行

それではテストファイルです.SqrFuncCheckとSumFuncCheckの2つのテストを用意して,sqrとsum関数をテストしてみます.sum関数の実装は間違っているので,テストは通らないはずです.

#include "../src/my_math.h"
#include <gtest/gtest.h>

namespace {
  class MyLibTest : public ::testing::Test{};

  TEST_F(MyLibTest, SqrFuncCheck) {
    EXPECT_EQ(sqr(3.0), 9.0);
    EXPECT_EQ(sqr(4.0), 16.0);
  }

  TEST_F(MyLibTest, SumFuncCheck) {
    EXPECT_EQ(sum(1.0, 2.0), 3.0);
  }
}

実行してみます.ちゃんと失敗しています.make後にbuild/test/Testを直接叩いても大丈夫です.

$ git clone https://github.com/jojonki/googletest-with-cmake-sample/
$ cd googletest-with-cmake-sample/
$ mkdir build
$ cd build
$ cmake ..
$ make
$ make test # or ctest --verbose
$ ctest --verbose
(略)
test 1
    Start 1: MyTest

1: Test command: /path/to/googletest-with-cmake-sample/build/test/Test
1: Test timeout computed to be: 9.99988e+06
1: Running main() from gtest_main.cc
1: [==========] Running 2 tests from 1 test case.
1: [----------] Global test environment set-up.
1: [----------] 2 tests from MyLibTest
1: [ RUN      ] MyLibTest.SqrFuncCheck
1: [       OK ] MyLibTest.SqrFuncCheck (0 ms)
1: [ RUN      ] MyLibTest.SumFuncCheck
1: /path/to/googletest-with-cmake-sample/test/test.cc:13: Failure
1:       Expected: sum(1.0, 2.0)
1:       Which is: -1
1: To be equal to: 3.0
1:       Which is: 3
1: [  FAILED  ] MyLibTest.SumFuncCheck (0 ms)
1: [----------] 2 tests from MyLibTest (1 ms total)
1: 
1: [----------] Global test environment tear-down
1: [==========] 2 tests from 1 test case ran. (1 ms total)
1: [  PASSED  ] 1 test.
1: [  FAILED  ] 1 test, listed below:
1: [  FAILED  ] MyLibTest.SumFuncCheck
1: 
1:  1 FAILED TEST
1/1 Test #1: MyTest ...........................***Failed    0.00 sec

0% tests passed, 1 tests failed out of 1

Total Test time (real) =   0.01 sec

The following tests FAILED:
	  1 - MyTest (Failed)
Errors while running CTest
まとめ

cmakeでgoogletestを使ったプロジェクトを作ることができるようになりました.どんどんテストしましょう.googletestをもう少し使い込みたいところです.

Q学習で最良経路をPythonで求めてみる

導入Q学習

いきなりですが強化学習の勉強始めています.強化学習は教師なし学習の1つの手法で,与えられた環境に対して様々な行動を試し,一番報酬が得られる行動を学習していきます.その中でQ値と呼ばれるものがあります.これは状態s ,行動 aをとり,方策 \piに従って得られる割引累積報酬の期待値です.ざっくりと言い切ってしまうと, s-aを実行すると将来どれぐらいの報酬が期待できるか?といったことを意味しています.

学習にはQ学習,Actor-Critic,SARSAなどがありますが,今回の記事ではQ学習を取り扱います.これは方策オフ型のTD学習であり,どうのような行動をとるか?という方策に関係なく,行動価値関数の大きな値に合わせていくスタイルです.この更新を繰り返していくことで(学習していくことで)最適な行動価値を導き出します.
{ \displaystyle
Q(s_t,a_t) \leftarrow Q(s_t,a_t) + \alpha(\gamma_{t+1} + \gamma \max_a Q(s_{t+1}, a_{t+1}) - Q(s_t, a_t))
}

今回強化学習に関しての詳しい説明はしません.いずれしたいと思いますが,今日はとてもわかりやすいリンク及び書籍を紹介しておきます.
強化学習の基礎,小池 康晴,鮫島 和行

イラストで学ぶ 人工知能概論 (KS情報科学専門書)

イラストで学ぶ 人工知能概論 (KS情報科学専門書)

手を動かして実装してみる

価値関数を最大化するためのQ学習の良い例題として,素敵なサイトを見つけたので,そこの例題をPythonで解いてみます.英語ですがとてもわかりやすく書いているのでぜひ読んで欲しいのですが,ざっくりと説明すると0〜5の6つの部屋(ステート)があり,ステート5の部屋に辿り着くように報酬を設定することで,Q学習を行っているサンプルです.
mnemstudio.org

このサイトの例だとQ値更新の式がだいぶ簡略化されています. s-aのペアで得られる即時報酬に sの次の状態 s’で報酬を最大化できる行動に \gamma をかけたものを加えて更新されます.
{ \displaystyle
Q(s,a)=R(s,a)+\gamma \max_a [Q(s’, a)]
}

ステート遷移図と行動(矢印の経路をたどること)の報酬はこんな感じです.
f:id:jonki:20160505171911p:plain:w300

今回書いたコードを載せておきます.ランダムで取得したstateから,可能な行動のうちQ値が最大となるような行動をとりつつ,各Q値を更新していきます.ゴールしたらまた適当な地点から開始します.この試行をとりあえず1000回行わせました.開始からゴールまでを1エピソードと数えるのが普通のようで,本来はこれを1000エピソード分,行わせた方がよかったかもしれませんが,まぁこんかいのでも十分に学習できるので特に問題ないと思います.

reinforcement-practice/simple-q-learning.py at master · jojonki/reinforcement-practice · GitHub

# -*- coding: utf-8 -*-
"""
Created on Thu May  5, 2016

@author: jonki
"""

import numpy as np
import random
import sys

# sample ref
# http://mnemstudio.org/path-finding-q-learning-tutorial.htm

# Reward matrix
R = np.array([
[-1, -1, -1, -1,  0,  -1],
[-1, -1, -1,  0, -1, 100],
[-1, -1, -1,  0, -1,  -1],
[-1,  0,  0, -1,  0,  -1],
[ 0, -1, -1,  0, -1, 100],
[-1,  0, -1, -1,  0, 100]
])

# Initial Q-value
Q = np.zeros((6,6))

LEARNING_COUNT = 1000
GAMMA = 0.8
GOAL_STATE = 5

class QLearning(object):
    def __init__(self):
        return
        
    def learn(self):
        # set a start state randomly
        state = self._getRandomState()
        for i in range(LEARNING_COUNT):        
            # extract possible actions in state
            possible_actions = self._getPossibleActionsFromState(state)
            
            # choise an action from possible actions randomly
            action = random.choice(possible_actions)        
            
            # Update Q-value
            # Q(s,a) = r(s,a) + Gamma * max[Q(next_s, possible_actions)]
            next_state = action # in this example, action value is same as next state
            next_possible_actions = self._getPossibleActionsFromState(next_state)
            max_Q_next_s_a = self._getMaxQvalueFromStateAndPossibleActions(next_state, next_possible_actions)
            Q[state, action] = R[state, action] + GAMMA * max_Q_next_s_a
            
            state = next_state
            
            # If an agent reached a goal state, restart an episode from a random start state
            if state == GOAL_STATE:
                state = self._getRandomState()
    
    def _getRandomState(self):
        return random.randint(0, R.shape[0] - 1)
      
    def _getPossibleActionsFromState(self, state):
        if state < 0 or state >= R.shape[0]: sys.exit("invaid state: %d" % state)
        return list(np.where(np.array(R[state] != -1)))[0]
    
    def _getMaxQvalueFromStateAndPossibleActions(self, state, possible_actions):
        return max([Q[state][i] for i in (possible_actions)])
            
    def dumpQvalue(self):
        print Q.astype(int) # convert float to int for redability

    def runGreedy(self, start_state = 0):
        print "===== START ====="
        state = start_state
        while state != GOAL_STATE:
            print "current state: %d" % state
            possible_actions = self._getPossibleActionsFromState(state)
            
            # get best action which maximaizes Q-value(s, a)
            max_Q = 0
            best_action_candidates = []
            for a in possible_actions:            
                if Q[state][a] > max_Q:
                    best_action_candidates = [a,]
                    max_Q = Q[state][a]
                elif Q[state][a] == max_Q:
                    best_action_candidates.append(a)
            
            # get a best action from candidates randomly
            best_action = random.choice(best_action_candidates)
            print "-> choose action: %d" % best_action
            state = best_action # in this example, action value is same as next state
        print "state is %d, GOAL!!" % state
            
if __name__ == "__main__":
    QL = QLearning()
    QL.learn()
    
    QL.dumpQvalue()
    
    for s in range(R.shape[0]-1):
        QL.runGreedy(s)

実行してみる

このプログラムの最後ではステート0〜4から開始して,最良の経路を通っているかテストしています.Greedyとはあらかじめわかっている報酬があるときに,探索はせずに一番高い報酬が期待出来る行動をとります.ε-グリーディ法などあるのですが,これはまたの機会に説明します.各ステートでスタートしてから一番良い経路(報酬が高い)を選択しているのがわかります.最初に出力されている行列は学習されたQ値です.行がステート,列がアクションです.

[[  0   0   0   0 395   0]
 [  0   0   0 316   0 493]
 [  0   0   0 316   0   0]
 [  0 395 252   0 395   0]
 [316   0   0 316   0 493]
 [  0 393   0   0 395 493]]
===== START =====
current state: 0
-> choose action: 4
current state: 4
-> choose action: 5
state is 5, GOAL!!
===== START =====
current state: 1
-> choose action: 5
state is 5, GOAL!!
===== START =====
current state: 2
-> choose action: 3
current state: 3
-> choose action: 1
current state: 1
-> choose action: 5
state is 5, GOAL!!
===== START =====
current state: 3
-> choose action: 4
current state: 4
-> choose action: 5
state is 5, GOAL!!
===== START =====
current state: 4
-> choose action: 5
state is 5, GOAL!!

学習された各ステートのQ値(実行枚に若干異なります)です.マップしてみるとステート5へと続く経路のQ値は他の経路よりも高くなっていることが確認できますね.
f:id:jonki:20160505171920p:plain:w300

まとめ

今回かなり単純化した問題設定のもと,Q学習を行ってみましたが,どうだったでしょうか?私自体,これが初となるQ学習の実装なので誤っているところあればビシバシ指摘していただきたいです.これぐらい簡単な問題にして実装してみることで,肌感でQ学習が何かが徐々にわかってきました.次は他のTD学習などについても触れたいですね.

MOOC (Massive Open Online Course)のススメ

f:id:jonki:20160317223620p:plain

機械学習を勉強していて色々本を漁っている昨今ですが、オンラインでもあるんじゃね?と思い、まさにMOOC (Massive Open Online Course)で良い授業を見つけました。見つけたのはAzure MLを使ったデータサイエンス&機械学習入門の授業で、本日無事テストにパスして修了証もらいました。
授業は約1ヶ月のコースになっていて(計10時間くらい?)、classification(分類)、regression(回帰)、clustering、recommender systemsの4つをMicrosoftのAzure MLという機械学習をノードベースで簡単に扱えるサービスサイトで学べます。Azure ML自体に興味がなくても非常にシンプルになっていて、データサイエンティストがどのようなワークフローでデータを扱い、有益な情報を引き出しているのか、ざっくりと体系的に学べてタメになります。英語が心配!という人でも、この授業では正しい字幕が付与されていること、講師の英語が綺麗でハッキリしているので普通にリスニングとしても良い教材でした。
DAT203x Course Info | edX

MOOCの存在は以前から知っていましたが、「ふーん」ぐらいにしか思っていませんでした。が、やり始めるとこれはハマりそうです。何より留学しなくとも海外の超超一流大学の授業がタダで、しかも字幕付き(これは動画による)で学べるなんて素敵すぎます。最近仕事でも新しい分野の論文など、読んでも前提知識がないぶん苦しんでいたので、こういう授業で体系的に学べるのは本当に良い時代になりました。
大手MOOCに関しては下記のサイト様でとても詳しく説明されています。
edtech-media.com

今はudacityで人工知能系の授業聞いています。面白そうな授業がたくさんあるので是非お試しあれ。
www.edx.org
Coursera - Free Online Courses From Top Universities
www.udacity.com

Xperia Z3 compactのドックアダプターを3Dプリンターで作った

f:id:jonki:20160306123817j:plain

昨日Xperia Z3 compactの白ロムをゲットしたのですが、中古で付属品が何もありませんでした。Z3用のドックはあったのですが、Z3 compactはZ3より少し分厚くアダプターのサイズに合っていません。Z3用のドックをもう2つもってるし、結構ドック高いのでアダプターを3Dプリンターでつくりました。3DプリンターはUP plus 2で印刷しています。


作ったモデルはあげているので寸法等適当にご参考くださいませ。
www.123dapp.com

アダプターの公式サイズなんて情報はなかったので、ノギスで測ってます。ノギスは1台あると色々捗るよ。


f:id:jonki:20160306123819j:plain
f:id:jonki:20160306123821j:plain
f:id:jonki:20160306124603j:plain