空飛ぶロボットのつくりかた

ロボットをつくるために必要な技術をまとめます。ロボットの未来についても考えたりします。

Numpy / pandasのお勉強

numpy

配列も@で行列のように計算できる。python3.5以降。

In [7]: import numpy as np

In [8]: A = np.array([[1,2],[3,4]])

In [9]: B = np.array([[5,6],[7,8]])

In [10]: C = A @ B @ A.T

In [11]: C
Out[11]: 
array([[ 63, 145],
       [143, 329]])

以下は同じ意味

In [12]: A = np.array([[1,2],[3,4]])

In [13]: B = np.array(((1,2),(3,4)))

いろいろな配列の作り方があるよ

In [24]: np.zeros((2,2))
Out[24]: 
array([[ 0.,  0.],
       [ 0.,  0.]])

In [25]: np.arange(1,100,20)
Out[25]: array([ 1, 21, 41, 61, 81])

メソッド

In [26]: A.shape
Out[26]: (2, 2)

行列計算

In [28]: A
Out[28]: 
array([[1, 2],
       [3, 4]])

In [29]: np.linalg.inv(A)
Out[29]: 
array([[-2. ,  1. ],
       [ 1.5, -0.5]])

In [30]: np.linalg.det(A)
Out[30]: -2.0000000000000004

In [31]: np.linalg.matrix_rank(A)
Out[31]: 2

配列の操作

In [34]: matrixA = np.arange(9).reshape(3,3)

In [35]: matrixA
Out[35]: 
array([[0, 1, 2],
       [3, 4, 5],
       [6, 7, 8]])

In [36]: a = matrixA[1][1:];a
Out[36]: array([4, 5])

In [37]: a[1] = 10

In [38]: matrixA
Out[38]: 
array([[ 0,  1,  2],
       [ 3,  4, 10],
       [ 6,  7,  8]])

In [39]: matrixA[0,1]
Out[39]: 1

In [40]: matrixB = matrixA[1:,1:];matrixB
Out[40]: 
array([[ 4, 10],
       [ 7,  8]])

In [41]: matrixB[:,:]=100

In [42]: matrixA
Out[42]: 
array([[  0,   1,   2],
       [  3, 100, 100],
       [  6, 100, 100]])

In [44]: matrixC = np.random.rand(2,2);matrixC
Out[44]: 
array([[ 0.47235181,  0.13044348],
       [ 0.86500587,  0.17257454]])

In [45]: maskC = matrixC > 0.2; maskC
Out[45]: 
array([[ True, False],
       [ True, False]], dtype=bool)

In [46]: matrixD = matrixC[maskC]

In [47]: matrixD
Out[47]: array([ 0.47235181,  0.86500587])

↓すごくわかりやすい!!! 参考:

www.slideshare.net

C++のお勉強(1)

やりたいこと

C++スキルアップ

教科書

本を書きました。『基礎からしっかり学ぶC++の教科書』 | 配電盤

環境構築

$ sudo apt-get install g++

ビルド方法(単体)

$ g++ Sample.cpp 

or

makefileを以下のように記載し、$ make

Sample: Sample.cpp
    g++ -o Sample Sample.cpp
    ##g++ -std=c++11 -o Sample Sample.cpp

ビルド方法(複数)

$ g++ -c Sample1.cpp -o Sample1.o
$ g++ -c Sample2.cpp -o Sample2.o
$ g++ Sample1.o Sample2.o

or

Sample: Sample1.o Sample2.o
    gcc -o Sample Sample1.o Sample2.o

Sample1.o: Sample1.cpp
    gcc -c Sample1.cpp

Sample2.o: Sample2.cpp
    gcc -c Sample2.cpp

参考:

ヘッダーと.cppを作ってコンパイル

メイクファイルをじわーっと作ってみる

ターミナルでC++のコンパイル - Qiita

デバッグツール

gdbの使い方

  • ビルド時に-gをつけて実行
$ g++ -g -O0 Sample.cpp
  • gdb立ち上げ
$ gdb a.out
  • 使ってみる:ステップ実行
$ break main
$ run
$ next
$ next
...
$ quit

参考:  gcc+gdbによるプログラムのデバッグ 第1回 ステップ実行、変数の操作、ブレークポイント

ポインタ

Sample1

#include <iostream>
#include <string>
using namespace std;

class Robot{
  string name;
public:
  Robot(){}
  Robot(string n):name(n){}
  void SetName(string n){
    name = n;
  }
  void Speak() const;
};

void Robot::Speak() const {
    cout << "My name is " << name << "!" <<endl;
}


int main (){

  Robot dora;
  Robot *p2;
  p2 = &dora;
  cout << p2 << endl;
  
  p2->SetName("Atom");  //(*p2).SetName("Atom");
  p2->Speak();   //(*p2).Speak();

  Robot *x;
  x = new Robot;
  x -> SetName("Tetsujin");
  x -> Speak();
  delete x;

  Robot *y;
  y = new Robot("Chappy");
  y -> Speak();
  delete y;

  int i;
  int num;
  string temp;
  Robot *r;
  cout << "Please a number of Robot you want to make."<<endl;
  cin >> num;

  r = new Robot[num];

  for (i =0 ; i<num;i++){
    cout << "Please type robot's name." <<endl;
    cin >> temp;
    r[i].SetName(temp);
  }

  for (i=0;i<num;i++){
    r[i].Speak();
    }

  delete [] r;
}

Sample2

#include <iostream>
using namespace std;

int main(){
  int x = 1;
  int* px = &x;
  int** ppx = &px;
  *px = 10;
  cout << x <<endl;
  cout << **ppx <<endl;
}

10
10

スマートポインタ

メモリリークを自動的に防いでくれる

#include <iostream>
#include <complex>
#include <memory>
using namespace std;

int main(){
  using cpdb = complex<double>;
  
  shared_ptr<cpdb> x = make_shared<cpdb>();
  cout << *x << endl;

  shared_ptr<cpdb> y = make_shared<cpdb>(1.1,2);
  cout << *y << endl;

  shared_ptr<cpdb> z = make_shared<cpdb>(*y);
  cout << *z << endl;
}

継承

#include <iostream>
using namespace std;

class Robot
{
  string name;
public:
  void SetName(string n){name = n;}
  string GetName() const{ return name;}
  void InputData();
  void ShowData() const;
};

void Robot::InputData(){
  cout << "input Name:";
  cin >> name;
}

void Robot::ShowData() const{
  cout << "name:"<<name<<endl;
}

class FRobot : public Robot
{
  int power;
public:
  void SetPower(int p){ power = p;}
  int GetPower() const{ return power;}
  void InputData();
  void ShowData() const;
  int FullPower() const { return power * 10;}
};

void FRobot::InputData(){
  Robot::InputData();
  cout << "input power:"<<endl;
  cin >> power;
}

void FRobot::ShowData() const{
  Robot::ShowData();
  cout << "FullPower is :" << FullPower() << "!!!" << endl;
}

int main()
{
  FRobot f[3];
  string temp;
  int i,sum;

  for (i=0;i<3;i++){
    f[i].InputData();
  }
  cout << endl;

  cout << "display all robot power" << endl;
  for(i = 0; i< 3; i++){
    cout <<"Mark" <<i + 1 << "!"<<endl;
    f[i].ShowData();
    cout << "----------------" <<endl;
  }

  sum = 0;
  for (i = 0 ; i <3 ; i++){
    sum += f[i].FullPower();
  }
  cout << "ALL ROBOT FULL POWER :" << sum <<"!!!"<<endl;
}

参照引数の関数

参照引数の場合、引数にコピーしないため軽量

void func(X& a){...}
or
void func(const X& a)

上は、引数のデータが書き換わる可能性あり

下は、書き換わることを防ぐ

関数の引数の種類

  1. int → void func1 (int x) {}
  2. const int → void func2 (const int x) {}
  3. int& → void func3 (int& x) {}
  4. const int& → void func4 (const int& x) {}
  5. int&& → void func5 (int&& x) {}
  6. const int&& → void func6 (const int&& x) {}
  7. int → void func7 (int x) {}
  8. const int → void func8 (const int x) {}

これだけ考えられるが基本的にはfunc1~4を考えれば良い。

参考:右辺値参照・ムーブセマンティクス - cpprefjp C++日本語リファレンス

テンプレート

include <iostream>
#include <string>
using namespace std;

template <typename T>
void SwitchValue(T& x, T& y){
  T temp = x;
  x = y;
  y = temp;
}

int main(){
  int a = 1;
  int b = 2;
  SwitchValue(a,b);
  cout << a << endl;
  cout << b << endl;

  string c = "1";
  string d = "2";
  SwitchValue(c,d);
  cout << c << endl;
  cout << d << endl;

}

参考:第 4 章 テンプレート (C++ プログラミングガイド)

イテレータ

参考:C++ iterator 入門 C++ イテレータ入門 - プログラミングの教科書を置いておくところ

別名

using uint = unsigned int; //typedef unsigned int uint;
uint x = 0; //  equal unsigned int x = 0

キャスト

#include <iostream>
using namespace std;

int main(){
  int x = 0;
  x = static_cast<int>(1.11);
  cout << x << endl;
  x = (int)1.11;
  cout << x << endl;
  x = int(1.11);
  cout << x << endl;
}

ビット演算

#include <iostream>
using namespace std;

int main(){
  int x = 1;
  int y = 5;
  cout << (y >> 1) << endl; //2
  cout << (y << 1) << endl; //10
  cout << (y >> 2) << endl; //1
  cout << (y << 2) << endl; //20
  cout << (x & y) << endl; //1
  cout << (x | y) << endl; //5
  cout << (x ^ y) << endl; //4
  cout << (~x) << endl; //-2
}

列挙型

#include <iostream>
using namespace std;

int main(){
  enum robots { Atom, Tetsujin, Dora };
  robots x = Tetsujin;
  cout << (x==Tetsujin ? "Mark21": "Mark0")<<endl;
}

静的変数

一度だけ初期化され、その後共有される

以下の例ではstaticがないと、出力は1,1,1になる

#include <iostream>
using namespace std;

int increment(){
  static int x = 0;
  ++x;
  return x;
}

int main(){
  cout << increment() <<endl; //1
  cout << increment() <<endl; //2
  cout << increment() <<endl; //3
}

名前空間

#include <iostream>
using namespace std;

int x;

namespace NAME1{
  int x = 1;
}

namespace NAME2{
  int x = 2;
}

int main(){
  int x = 3;
  cout << x << endl;//3
  cout << ::x << endl;//0
  cout << NAME1::x << endl;//1
  cout << NAME2::x << endl;//2
}

コンテナ

#include <iostream>
#include <complex>
#include <vector>
using namespace std;

int main() {

  using cpdb = complex<double>;
  vector<int> v1;
  v1.push_back(2);
  v1.push_back(3);
  v1.push_back(4);
  v1.push_back(5);

  vector<int> v2(4);
  v2[0]=6;
  v2[1]=7;
  v2[2]=8;
  v2[3]=9;

  vector<int> v3{10,11,12,13};

  for (int i = 0 ; i < v1.size() ;i++){
    cout << v1[i] << endl;
    cout << v2[i] << endl;
    cout << v3[i] << endl;
  }

  v3.pop_back();
  v3.erase(v3.begin() + 1);

  for (int i = 0 ; i < v3.size() ;i++){
    cout << v3[i] << endl;
  }

  vector<int> v4(v1);

  for (int i = 0 ; i < v4.size() ;i++){
    cout << v4[i] << endl;
  }

  vector<cpdb> v5;
  v5.reserve(10);
  v5.emplace_back(1.11,2.0);
  cout << v5.capacity() << endl;
  
  vector<vector<int>> v6{{1,2,3},{4,5,6}};

  cout << v6.size() <<endl;
  cout << v6[0].size() <<endl;
  cout << v6[0][2] << endl;
}

例外

#include <iostream>
using namespace std;

void DoSomething(){
  throw runtime_error("error!");
}

int main(){
  try{
    DoSomething();
  }
  catch (exception& e){
    cerr << e.what() <<endl;
  }
  cout << "Normal Stop.";
}

kerasを動かしてみる~その1~

TJOさんの記事を参考にKerasを動かして見ました。感謝です。

参考: KerasをTensorFlowバックエンドで試してみた:「もっと多くの人に機械学習とDeep Learningを」という時代の幕開け - 六本木で働くデータサイエンティストのブログ

tensorflowのインストー

参考のInstalling with virtualenvをそのまま実行

参考:Installing TensorFlow on Ubuntu  |  TensorFlow

$source ~/tensorflow/bin/activeでtensorflow環境へ切り替え

kerasを動かすときはこの環境へ切り替えておくこと

Kerasのインストー

$sudo pip install kerasでインストー

Kerasの実行

TJOさんの「KerasのCNNでMNIST短縮版の分類をやってみる」のコードを一箇所書替えて実行

score = model.evaluate(x_test, y_test, show_accuracy=True, verbose=0)
↓
score = model.evaluate(x_test, y_test, verbose=0)

実行結果:Test accuracy: 0.976

とりあえず動きましたー

構造を変更したり、データオーギュメンテーションとかして理解を深めるぞ〜!

これから読んで勉強したい記事:

KerasをTensorFlowバックエンドで試してみた:「もっと多くの人に機械学習とDeep Learningを」という時代の幕開け - 六本木で働くデータサイエンティストのブログ

はじめての Deep Learning - Keras で MLP for MNIST · m0t0k1ch1st0ry

[Python]強化学習(DQN)を実装しながらKerasに慣れる - Qiita 

DeepLearning系ライブラリ、Kerasがあまりにも便利だったので使い方メモ - プロクラシスト

Tensorflowの上位ラッパーライブラリ Kerasを試してみる - デジタル・デザイン・ラボラトリーな日々

深層学習やってみる - Kerasで手書き数字認識(MNIST) - ポンダッドの日記

Raspberry Pi 深層学習ライブラリで物体認識(Keras with TensorFlow・Open CV) - Qiita

SSD: Single Shot MultiBox Detector 高速リアルタイム物体検出デモをKerasで試す - Qiita

KerasでCIFAR-10の一般物体認識 - 人工知能に関する断創録

SSDによる物体検出を試してみた - TadaoYamaokaの日記

Kerasで学ぶAutoencoder

Implementation of FCN via Keras - MATHGRAM good!

aidiary.hatenablog.com

【ディープラーニング】kerasで多クラス分類 - testtest2222’s blog

DeepLearning系ライブラリ、Kerasがあまりにも便利だったので使い方メモ - プロクラシスト

www.slideshare.net

pythonのテストを書いてみる2

数値微分

プロダクションコード

# -*- coding:utf-8 -*-                                                          

# 2次関数の数値微分を行うクラス                                                 
class Diff:
    # 2次関数                                                                   
    def f(self,x):
        y = x**2
        return y

    # 数値微分                                                                  
    def numerical_diff(self,f,x,dx):
        if dx == 0:
            raise ValueError('division by zero')
        else:
            x_diff = (f(x+dx)-f(x-dx))/(2*dx)
            return x_diff

if __name__ == '__main__':
    diff = Diff()
    print diff.numerical_diff(diff.f,3,0.001)

テストコード

# -*- coding:utf-8 -*-                                                 
import unittest, numerical_diff

class TestDiff(unittest.TestCase):
    # 前処理                                                           
    def setUp(self):
        self.diff = numerical_diff.Diff()

    # 後片付け                                                         
    def tearDowm(self):
        pass

    # 3の2乗のテスト                                                   
    def test_func1(self):
        self.assertEqual(self.diff.f(3),9)

    # 4の2乗のテスト                                                   
    def test_func2(self):
        self.assertEqual(self.diff.f(4),16)

    # 数値微分ができているかのテスト                                   
    def test_numerical_diff(self):
        self.assertAlmostEqual(self.diff.numerical_diff(self.diff.f,3,0.001),6.0, delta=1e-8)
        self.assertEqual(round(self.diff.numerical_diff(self.diff.f,3,0.001)),6.0) # ダメな例。検証したい値に操作を加えている。

    # 0で割られたときの例外のテスト                                    
    def test_numerical_diff_zero_error(self):
        with self.assertRaises(ValueError) as cm:
            self.diff.numerical_diff(self.diff.f,3,0.0)
        exception = cm.exception
        self.assertEqual(exception.message, 'division by zero')

文字の変換

プロダクションコード

#-*- coding:utf-8 -*-
import re

class StringUtils:
    def to_snake_case(self,text):
        if len(text) is 0:
            raise ValueError('snake case error')
        else:
            snake = re.sub("([A-Z])",lambda x:"_" + x.group(1).lower(),text)
            return re.sub("^_","",snake)

    def to_camel_case(self,text):
        if len(text) is 0:
            raise ValueError('camel case error')
        else:
            camel = re.sub("_(.)",lambda x:x.group(1).upper(),text)
            return re.sub("^(.)",lambda x:x.group(1).upper(),camel)

if __name__ == '__main__':
    camel = "CamelCase"
    snake = "snake_case"
    string_utils = StringUtils()
    print string_utils.to_snake_case(camel)
    print string_utils.to_camel_case(snake)

参考: lambda式はすごく面白い - 元理系院生の新入社員がPythonとJavaで色々頑張るブログ

【技術】pythonでのキャメルケースとスネークケースの変換について - エンジニアリングとお金の話

Pythonで文字列を置換する:replace(), re.sub() | UX MILK

テストコード

# -*- coding:utf-8 -*-
import unittest, string_utils

class TestStringUtils(unittest.TestCase):
    # 前処理
    def setUp(self):
        self.su = string_utils.StringUtils()

    # 後片付け
    def tearDowm(self):
        pass

    # camel->snake, snake->camelへの変換テスト
    def test_to_snake_case1(self):
        self.assertEqual(self.su.to_snake_case("CamelCase"),"camel_case")

    def test_to_snake_case2(self):
        self.assertEqual(self.su.to_snake_case("camelCase"),"camel_case")

    def test_to_snake_case3(self):
        self.assertEqual(self.su.to_snake_case("camelcase"),"camelcase")

    def test_to_camel_case1(self):
        self.assertEqual(self.su.to_camel_case("snake_case"),"SnakeCase")

    def test_to_camel_case2(self):
        self.assertEqual(self.su.to_camel_case("Snake_Case"),"SnakeCase")

    def test_to_camel_case3(self):
        self.assertEqual(self.su.to_camel_case("SnakeCase"),"SnakeCase")


    # 文字がない場合の例外のテスト
    def test_snake_error(self):
        with self.assertRaises(ValueError) as cm:
            self.su.to_snake_case("")
        exception = cm.exception
        self.assertEqual(exception.message, 'snake case error')

    def test_camel_error(self):
        with self.assertRaises(ValueError) as cm:
            self.su.to_camel_case("")
        exception = cm.exception
        self.assertEqual(exception.message, 'camel case error')

カウント

プロダクションコード

# -*- coding:utf-8 -*-

class Counter:
    def __init__(self):
        self.count = 0
    def increment(self):
        self.count += 1
        return self.count

if __name__ == '__main__':
    counter = Counter()
    for i in range(10):
        print counter.increment()

テストコード

# -*- coding:utf-8 -*-
import unittest, counter

class TestCounter(unittest.TestCase):
    # 前処理
    def setUp(self):
        self.ct = counter.Counter()

    # 後片付け
    def tearDowm(self):
        pass

    # カウントできているかのテスト
    def test_count_init(self):
        self.assertEqual(self.ct.increment(),1)

    def test_count_2(self):
        self.ct.increment()
        self.assertEqual(self.ct.increment(),2)

    def test_count_100(self):
        for i in range(100):
            self.ct.increment()
        self.assertEqual(self.ct.increment(),101)

偶数判定

プロダクションコード

# -*- coding:utf-8 -*-

class NumberUtils:
    def even(self,num):
        return num % 2 == 0

if __name__ == '__main__':
    number = NumberUtils()
    print number.even(2)
    print number.even(3)

テストコード

# -*- coding:utf-8 -*-
import unittest, number_utils

class TestStringUtils(unittest.TestCase):
    # 前処理
    def setUp(self):
        self.nu = number_utils.NumberUtils()

    # 後片付け
    def tearDowm(self):
        pass

    def test_even(self):
        self.assertTrue(self.nu.even(2))

    def test_odd(self):
        self.assertFalse(self.nu.even(3))

物品管理

プロダクションコード

# -*- coding:utf-8 -*-

class Item:
    def __init__(self,name,price):
        self.name = name
        self.price = price

    def get_name(self):
        return self.name

    def get_price(self):
        return self.price

class ItemStock:
    def __init__(self):
        self.item_dic = {}

    def add(self, item):
        num = self.item_dic.get(item.name)
        if num is None:
            num = 0
        num +=1
        self.item_dic[item.name] = num

    def get_num(self, item):
        num = self.item_dic.get(item.name)
        if num is not None:
            return num
        else:
            return 0

if __name__ == '__main__':
    drone = Item("drone",6800)
    robot = Item("robot",10000)
    item_stock = ItemStock()
    item_stock.add(drone)
    item_stock.add(drone)
    item_stock.add(robot)
    print drone.get_name(),drone.get_price()
    print item_stock.get_num(drone)
    print robot.get_name(),robot.get_price()
    print item_stock.get_num(robot)

テストコード

# -*- coding:utf-8 -*-
import unittest, item_stock

class TestItemSocket(unittest.TestCase):
    # 前処理
    def setUp(self):
        self.itemstock = item_stock.ItemStock()
        self.drone = item_stock.Item("drone",6800)
        self.robot = item_stock.Item("robot",10000)

    # 後片付け
    def tearDowm(self):
        pass

    def test_init(self):
        self.assertEqual(self.itemstock.get_num(self.drone),0)

    def test_one_object_add_1(self):
        self.itemstock.add(self.drone)
        self.assertEqual(self.itemstock.get_num(self.drone),1)

    def test_one_object_add_2(self):
        self.itemstock.add(self.drone)
        self.itemstock.add(self.drone)
        self.assertEqual(self.itemstock.get_num(self.drone),2)

    def test_two_object_add_2_and_1(self):
        self.itemstock.add(self.drone)
        self.itemstock.add(self.drone)
        self.itemstock.add(self.robot)
        self.assertEqual(self.itemstock.get_num(self.drone),2)
        self.assertEqual(self.itemstock.get_num(self.robot),1)

スレッド

(TBD)

プロダクションコード

テストコード

FizzBuzz

プロダクションコード

# -*- coding:utf-8 -*-

class FizzBuzz:
    def __init__(self):
        pass

    def create_fizz_buzz_list(self,size):
        list = []
        for i in range(1,size):
            if i % 15 == 0:
                list.append("FizzBuzz")
            elif i % 3 == 0:
                list.append("Fizz")
            elif i % 5 == 0:
                list.append("Buzz")
            else:
                list.append(str(i))
        return list

if __name__ == '__main__':
    fizzbuzz = FizzBuzz()
    print fizzbuzz.create_fizz_buzz_list(20)

テストコード

# -*- coding:utf-8 -*-
import unittest, FizzBuzz

class TestFizzBuzz(unittest.TestCase):
    # 前処理
    def setUp(self):
        self.cfb = FizzBuzz.FizzBuzz()

    # 後片付け
    def tearDowm(self):
        pass

    # FizzBuzzのテスト
    def test_fizz_buzz(self):
        actual = self.cfb.create_fizz_buzz_list(17)
        self.assertEqual(len(actual),16)
        self.assertEqual(actual[0],"1")
        self.assertEqual(actual[1],"2")
        self.assertEqual(actual[2],"Fizz")
        self.assertEqual(actual[3],"4")
        self.assertEqual(actual[4],"Buzz")
        self.assertEqual(actual[5],"Fizz")
        self.assertEqual(actual[6],"7")
        self.assertEqual(actual[7],"8")
        self.assertEqual(actual[8],"Fizz")
        self.assertEqual(actual[9],"Buzz")
        self.assertEqual(actual[10],"11")
        self.assertEqual(actual[11],"Fizz")
        self.assertEqual(actual[12],"13")
        self.assertEqual(actual[13],"14")
        self.assertEqual(actual[14],"FizzBuzz")
        self.assertEqual(actual[15],"16")

ゴリゴリすぎるので、カスタムMatcherでも書いてみたい。。。 (TBD)

カスタムMatcher: Python: Matcher フレームワーク PyHamcrest を使ってみる | CUBE SUGAR STORAGE  GitHub - hamcrest/PyHamcrest: Hamcrest matchers for Python

参考

Python コメント、Pydoc - @//メモ Pythonで学ぶ 基礎からのプログラミング入門 (32) マルチスレッド処理を理解しよう(前編) | マイナビニュース

robonchu.hatenablog.com

テストを学ぼう (1) ! ~ユニットテストについて~

ユニットテストってなんでするの?

書いたコードがちゃんと動くか不安だから。

テストを書いておくと安心を得ることができます。安心大事。

また、大胆にリファクタリングだってできちゃう。

以下の考え方はユニットテストが基盤

  1. 継続的テスト
  2. テスト駆動開発
  3. ビヘイビア駆動開発

継続的テストとは

テストを早い段階から(できれば自動的に)回すことが大事。理由は、手戻り、リグレッション(テストに失敗→修正→新たな問題が発生)が発生するからです。

ユニットテストは最小クラスの部品などを対象とするため、早い段階から回すことができます。また、自動化がしやすく、ローコストで繰り返し行うことができます。

こうすることで、修正による影響がすぐにフィードバックされ、安心してリリースができる。

参考:継続的テスト: IBM の見解  継続的テストの利点

テスト駆動開発とは

プロダクションコードを書く前にテストを書く方法。テストファースト。目的が明確になるため、目的にあったすっきりとしたコードがかける。

参考:@IT:特集 「テスト駆動開発」はプログラマのストレスを軽減するか?

ビヘイビア駆動開発とは

仕様を起点とした開発手法。テストファーストに対してスペックファーストという。

pythonでは「behave」という振舞駆動開発ツールがある。

参考: 振る舞い駆動開発入門  ビヘイビア駆動開発 - Wikipedia  Welcome to behave! — behave 1.2.5 documentation  ビヘイビア駆動開発ツール 

ユニットテストを書いてみよう

pythonでunittestを用いるものとする

参考: 26.4. unittest — ユニットテストフレームワーク — Python 3.6.1 ドキュメント Pythonでエラーが発生することをテストする - Misc Notes

以下のプロダクトコードとテストコードを同じフォルダに配置する

プロダクトコード

数値微分をしてみる

以下をnumerical_diff.pyとして保存

# -*- coding:utf-8 -*-                                                          

# 2次関数の数値微分を行うクラス                                                 
class Diff:
    # 2次関数                                                                   
    def f(self,x):
        y = x**2
        return y

    # 数値微分                                                                  
    def numerical_diff(self,f,x,dx):
        if dx == 0:
            raise ValueError('division by zero')
        else:
            x_diff = (f(x+dx)-f(x-dx))/(2*dx)
            return x_diff

if __name__ == '__main__':
    diff = Diff()
    print diff.numerical_diff(diff.f,3,0.001)

テストコード

以下をtest_numerical_diff.pyとして保存

# -*- coding:utf-8 -*-                                                 
import unittest, numerical_diff

class TestDiff(unittest.TestCase):
    # 前処理                                                           
    def setUp(self):
        self.diff = numerical_diff.Diff()

    # 後片付け                                                         
    def tearDowm(self):
        pass

    # 3の2乗のテスト                                                   
    def test_func1(self):
        self.assertEqual(self.diff.f(3),9)

    # 4の2乗のテスト                                                   
    def test_func2(self):
        self.assertEqual(self.diff.f(4),16)

    # 数値微分ができているかのテスト                                   
    def test_numerical_diff(self):
        self.assertAlmostEqual(self.diff.numerical_diff(self.diff.f,3,0.001),6.0, delta=1e-8)
        self.assertEqual(round(self.diff.numerical_diff(self.diff.f,3,0.001)),6.0) # 良くない例

    # 0で割られたときの例外のテスト                                    
    def test_numerical_diff_zero_error(self):
        with self.assertRaises(ValueError) as cm:
            self.diff.numerical_diff(self.diff.f,3,0.0)
        exception = cm.exception
        self.assertEqual(exception.message, 'division by zero')

if __name__ == "__main__":
    unittest.main()

参考:2. 組み込み関数 — Python 3.6.1 ドキュメント

実行方法

以下を上記フォルダ内で実行

python -m unittest test_numerical_diff

もしくはターミナルで

$ ipython
$ run test_numerical_diff.py

実行結果

....
----------------------------------------------------------------------
Ran 4 tests in 0.000s

OK

 今後勉強したいツール

nose

小型USBにubuntu16.04をいれてみる(macbookから起動できるように編)

myenigma.hatenablog.com

MyEnigmaさんの記事に刺激を受け、小型USBにubuntu16.04を入れてみました。

手順は少し異なります。

準備

  1. 適当USB 4GB
  2. 小型USB 64GB↓

安い!小さい!

https://www.amazon.co.jp/SANDISK-USB3-0%E3%83%95%E3%83%A9%E3%83%83%E3%82%B7%E3%83%A5-64GB-SDCZ43-064G-ULTRA/dp/B00LLEODCK/ref=pd_bxgy_147_img_2?_encoding=UTF8&psc=1&refRID=CJARWHRR2CQR1XTV6RKC

手順

  1. UNetbootin - Homepage and Downloadsのインストール
  2. ubuntu16.04のISOファイルのダウンロード
  3. unetbootinを用いてISOファイルを適当USBへイン
  4. この適当USBと小型USBをMacbookに指しoptionキーを押しながら再起動
  5. ubuntuを選択し、インストールなしで実行を選択
  6. Ubuntu14.04LTSをUSBメモリにインストールする - 余白を参考に小型USBのパーティションを分ける
  7. ここで、分けたパーティションにubuntu16.04をインストール
  8. 再起動
  9. rEFIndを小型USBに作成したFAT32パーティションへコピー(上記MyEnigmaさんのブログ参考)
  10. リカバリーモード、command+rキーを押しながら再起動
  11. ターミナルを開いて$ /Volumes/~/refind-installを実行
  12. 再起動でUbuntuMacを選択できるように!!!

以上の手順でMacbookの環境を汚さずにUSBからUBUNTUが起動できるようになりました。 やった〜!!!

便利なツール

SDカードフォーマッター - SD Association

Ubuntu14.04(CPU only)でSegNetを使ってみる

手順

  • caffe-segnetのダウンロード
git clone https://github.com/alexgkendall/caffe-segnet.git
  • 以下の手順でインストー

Caffe | Installation: Ubuntu

CPU only の設定に変更

Caffe | Installation

For CPU-only Caffe, uncomment CPU_ONLY := 1 in Makefile.config.

caffe-segnetの中のpythonフォルダ

export PYTHONPATH={適切なpathの設定}/caffe-segnet/python:$PYTHONPATH 
cd /
git clone https://github.com/alexgkendall/SegNet-Tutorial.git
mv SegNet-Tutorial SegNet
cd /SegNet/Example_Models
wget http://mi.eng.cam.ac.uk/projects/segnet/models/segnet_weights_driving_webdemo.caffemodel
  • サンプルプログラムを以下のように修正

SegNet/Script/webcam_demo.pyを写真から読みこむ設定に変更

名前はpicture_demo.pyに変更

import numpy as np
import matplotlib.pyplot as plt
import os.path
import scipy
import argparse
import math
import cv2
import sys
import time

sys.path.append('/usr/local/lib/python2.7/site-packages')
# Make sure that caffe is on the python path:
caffe_root = '/SegNet/caffe-segnet/'
sys.path.insert(0, caffe_root + 'python')
import caffe

# Import arguments
parser = argparse.ArgumentParser()
parser.add_argument('--model', type=str, required=True)
parser.add_argument('--weights', type=str, required=True)
parser.add_argument('--colours', type=str, required=True)
args = parser.parse_args()

net = caffe.Net(args.model,
                args.weights,
                caffe.TEST)

#caffe.set_mode_gpu()

input_shape = net.blobs['data'].data.shape
output_shape = net.blobs['argmax'].data.shape

label_colours = cv2.imread(args.colours).astype(np.uint8)

cv2.namedWindow("Input")
cv2.namedWindow("SegNet")

cap = cv2.VideoCapture(0) # Change this to your webcam ID, or file name for your video file

rval = True

start = time.time()
frame = cv2.imread('rimocon.jpg', 1)

end = time.time()
print '%30s' % 'Grabbed camera frame in ', str((end - start)*1000), 'ms'

start = time.time()
frame = cv2.resize(frame, (input_shape[3],input_shape[2]))
print frame.shape
input_image = frame.transpose((2,0,1))
print input_image.shape
input_image = np.asarray([input_image])
end = time.time()
print '%30s' % 'Resized image in ', str((end - start)*1000), 'ms'

start = time.time()
out = net.forward_all(data=input_image)
end = time.time()
print '%30s' % 'Executed SegNet in ', str((end - start)*1000), 'ms'

start = time.time()
segmentation_ind = np.squeeze(net.blobs['argmax'].data)
segmentation_ind_3ch = np.resize(segmentation_ind,(3,input_shape[2],input_shape[3]))
segmentation_ind_3ch = segmentation_ind_3ch.transpose(1,2,0).astype(np.uint8)
segmentation_rgb = np.zeros(segmentation_ind_3ch.shape, dtype=np.uint8)

cv2.LUT(segmentation_ind_3ch,label_colours,segmentation_rgb)
segmentation_rgb = segmentation_rgb.astype(float)/255

end = time.time()
print '%30s' % 'Processed results in ', str((end - start)*1000), 'ms\n'


cv2.imshow("Input", frame)
cv2.imshow("SegNet", segmentation_rgb)

while rval:
    key = cv2.waitKey(1)
    if key == 27: # exit on ESC
        break
cap.release()
cv2.destroyAllWindows()
  • サンプルの実行
python picture.py --model /SegNet/Example_Models/segnet_model_driving_webdemo.prototxt --weights /SegNet/Example_Models/segnet_weights_driving_webdemo.caffemodel --colours /SegNet/Scripts/camvid12.png
  • 実行結果

参考:

SegNet

OpenCV 備忘録: SegNetをUbuntuで試してみた