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

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

keras2とchainerの使い方をCNNでくらべてみる

目的:keras2とchainerの使い方の違いを知る

まとめ:

  • keras2はmodelの最初の層以外の入力は記述しなくても良い。バックエンドがtheanoとtensorflowで入力の配列が異なる。

  • chainerはmodelの層追加時、入力と出力の数を記入。入力でNoneと記述すると自動的に計算してくれる。Conv->Linearの際に便利。

参考:

出力ノード数を手計算せずにConvolutionの出力をLinearに入力する(chainer 1.15) - Qiita

わかりやすい↓

Chainer: ビギナー向けチュートリアル Vol.1 - Qiita

わかりやすい↓

Keras tips: 様々な画像の前処理をカンタンにやってくれるkeras.preprocessingのまとめ - MATHGRAM

keras2

mnistをcnnで学習するサンプル

'''Trains a simple convnet on the MNIST dataset.
Gets to 99.25% test accuracy after 12 epochs
(there is still a lot of margin for parameter tuning).
16 seconds per epoch on a GRID K520 GPU.
'''

from __future__ import print_function
import keras
from keras.datasets import mnist
from keras.models import Sequential
from keras.layers import Dense, Dropout, Flatten
from keras.layers import Conv2D, MaxPooling2D
from keras import backend as K

batch_size = 128
num_classes = 10
epochs = 12

# input image dimensions
img_rows, img_cols = 28, 28

# the data, shuffled and split between train and test sets
(x_train, y_train), (x_test, y_test) = mnist.load_data()

# backendがtensorflowとtheanoで配列のshapeが異なる
if K.image_data_format() == 'channels_first':
    x_train = x_train.reshape(x_train.shape[0], 1, img_rows, img_cols) # theano
    x_test = x_test.reshape(x_test.shape[0], 1, img_rows, img_cols)
    input_shape = (1, img_rows, img_cols)
else:
    x_train = x_train.reshape(x_train.shape[0], img_rows, img_cols, 1) # tensorflow
    x_test = x_test.reshape(x_test.shape[0], img_rows, img_cols, 1)
    input_shape = (img_rows, img_cols, 1)

x_train = x_train.astype('float32')
x_test = x_test.astype('float32')
x_train /= 255
x_test /= 255
print('x_train shape:', x_train.shape)
print(x_train.shape[0], 'train samples')
print(x_test.shape[0], 'test samples')

# convert class vectors to binary class matrices
y_train = keras.utils.to_categorical(y_train, num_classes)
y_test = keras.utils.to_categorical(y_test, num_classes)

model = Sequential()
model.add(Conv2D(32, kernel_size=(3, 3), # 32:出力を与える
                 activation='relu',
                 input_shape=input_shape)) # input_shape = x_train.shape[1:]としてもよい。最初のみ入力の値(img_rows, img_cols, 1)が必要。
model.add(Conv2D(64, (3, 3), activation='relu')) # 畳み込み。32:出力だけ与える
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Dropout(0.25))
model.add(Flatten())
model.add(Dense(128, activation='relu')) # 全結合相。2:出力だけ与える
model.add(Dropout(0.5))
model.add(Dense(num_classes, activation='softmax'))  # 活性化関数

model.compile(loss=keras.losses.categorical_crossentropy, # 損失関数
              optimizer=keras.optimizers.Adadelta(), # 最適化
              metrics=['accuracy'])

model.fit(x_train, y_train,
          batch_size=batch_size,
          epochs=epochs,
          verbose=1,
          validation_data=(x_test, y_test)) # 学習
score = model.evaluate(x_test, y_test, verbose=0)
print('Test loss:', score[0])
print('Test accuracy:', score[1])

model.save('./model.hdf5')

参考: keras/mnist_cnn.py at master · fchollet/keras · GitHub

予測するサンプル * 写真を一枚指定しそのクラスを出力

# coding: UTF-8

from __future__ import print_function
import keras
from keras.models import load_model
from keras.preprocessing import image

import numpy as np
import sys


model = load_model('model.hdf5')

if len(sys.argv) != 2:
    print("usage: python [image file]")
    sys.exit(1)

filename = sys.argv[1]

img = image.load_img(filename, target_size=(32, 32))
x = image.img_to_array(img)
#x = x / 255.0 #この操作を行うと正しく予測されない(TBD) 

# rows, cols, channels) -> (samples, rows, cols, channels) 
x = np.expand_dims(x, axis=0)

pred = model.predict(x)
print(pred)

参考: KerasでVGG16を使う - 人工知能に関する断創録

chainer

mnistをcnnで学習するサンプル

# -*- coding: utf-8 -*
import numpy as np
import matplotlib.pylab as plt
from sklearn.datasets import fetch_mldata
import chainer
import chainer.links as L
import chainer.functions as F
from chainer import Chain, optimizers, Variable, serializers
 
mnist = fetch_mldata('MNIST original', data_home='.')
mnist.data = mnist.data.astype(np.float32) # image data 784*70000 [[0-255, 0-255, ...], [0-255, 0-255, ...], ... ]
mnist.data /= 255 # to 0-1
mnist.target = mnist.target.astype(np.int32) # label data 70000
N = 60000
x_train, x_test = np.split(mnist.data,   [N])
t_train, t_test = np.split(mnist.target, [N])
 
# to (n_sample, channel, height, width)
x_train = x_train.reshape((len(x_train), 1, 28, 28))
x_test = x_test.reshape((len(x_test), 1, 28, 28))
 
class CNN(Chain):
    def __init__(self):
        super(CNN, self).__init__(
            conv1 = L.Convolution2D(1, 20, 5), # 入力:1 , 出力:20。両方記述。filter 5
            conv2 = L.Convolution2D(20, 50, 5), # 入力:1 , 出力:20。両方記述。filter 5
            l1 = L.Linear(800, 500),  # ここの入力の計算は以下の参考HPを参照。
            l2 = L.Linear(500, 500),
            l3 = L.Linear(500, 10, initialW=np.zeros((10, 500), dtype=np.float32))
        )
    def forward(self, x):
        h = F.max_pooling_2d(F.relu(self.conv1(x)), 2)
        h = F.max_pooling_2d(F.relu(self.conv2(h)), 2)
        h = F.relu(self.l1(h))
        h = F.relu(self.l2(h))
        h = self.l3(h)
        return h
 
model = CNN()
optimizer = optimizers.Adam() # 最適化関数
optimizer.setup(model)
 
n_epoch = 5
batch_size = 1000
for epoch in range(n_epoch):
    sum_loss = 0
    sum_accuracy = 0
    perm = np.random.permutation(N)
    for i in range(0, N, batch_size):
        x = Variable(x_train[perm[i:i+batch_size]])
        t = Variable(t_train[perm[i:i+batch_size]])
        y = model.forward(x)
        model.zerograds()
        loss = F.softmax_cross_entropy(y, t)  # 活性化関数と損失関数
        acc = F.accuracy(y, t)
        loss.backward()
        optimizer.update()
        sum_loss += loss.data*batch_size
        sum_accuracy += acc.data*batch_size
    print("epoch: {}, mean loss: {}, mean accuracy: {}".format(epoch, sum_loss/N, sum_accuracy/N))
 
cnt = 0
for i in range(10000):
    x = Variable(np.array([x_test[i]], dtype=np.float32))
    t = t_test[i]
    y = model.forward(x)
    y = np.argmax(y.data[0])
    if t == y:
        cnt += 1
 
print("accuracy: {}".format(cnt/10000))

serializers.save_npz("model.npz", model)

予測するサンプル1(TBD)

import numpy as np
%matplotlib inline
import matplotlib.pylab as plt
from sklearn.datasets import fetch_mldata
import chainer
import chainer.links as L
import chainer.functions as F
from chainer import Chain, optimizers, Variable, serializers
 
mnist = fetch_mldata('MNIST original', data_home='.')
mnist.data = mnist.data.astype(np.float32) # image data 784*70000 [[0-255, 0-255, ...], [0-255, 0-255, ...], ... ]
mnist.data /= 255 # to 0-1
mnist.target = mnist.target.astype(np.int32) # label data 70000
N = 60000
x_train, x_test = np.split(mnist.data,   [N])
t_train, t_test = np.split(mnist.target, [N])
 
# to (n_sample, channel, height, width)
x_train = x_train.reshape((len(x_train), 1, 28, 28))
x_test = x_test.reshape((len(x_test), 1, 28, 28))
 
class CNN(Chain):
    def __init__(self):
        super(CNN, self).__init__(
            conv1 = L.Convolution2D(1, 20, 5), # 入力:1 , 出力:20。両方記述。filter 5
            conv2 = L.Convolution2D(20, 50, 5), # 入力:1 , 出力:20。両方記述。filter 5
            l1 = L.Linear(800, 500), # ここの入力の計算は以下の参考HPを参照。
            l2 = L.Linear(500, 500),
            l3 = L.Linear(500, 10, initialW=np.zeros((10, 500), dtype=np.float32))
        )
    def forward(self, x):
        h = F.max_pooling_2d(F.relu(self.conv1(x)), 2)
        h = F.max_pooling_2d(F.relu(self.conv2(h)), 2)
        h = F.relu(self.l1(h))
        h = F.relu(self.l2(h))
        h = self.l3(h)
        return h

model = CNN()
serializers.load_npz("model.npz", model)

image_data = (TBD)

x = Variable(np.array([image_data], dtype=np.float32))
y = model.forward(x)
y = np.argmax(y.data[0])

予測するサンプル2 : 機械学習フレームワークchainerを使って1からコードをかいてみる(mnist編) - Qiita

def forwardではなくdef callにすると

class MLP(Chain):
    def __init__(self):
        super(MLP, self).__init__(
            l1=L.Linear(784, 100),
            l2=L.Linear(100, 100),
            l3=L.Linear(100, 10),
        )

    def __call__(self, x):
        h1 = F.relu(self.l1(x))
        h2 = F.relu(self.l2(h1))
        y = self.l3(h2)
        return y

以下のようにpredictorで予測することができる

def predict(model, x_data):
    x = Variable(x_data.astype(np.float32))
    y = model.predictor(x)
    return np.argmax(y.data, axis = 1)

答え合わせ。モデルの保存

def predict(model, x_data):
    x = Variable(x_data.astype(np.float32))
    y = model.predictor(x)
    return np.argmax(y.data, axis = 1)

予測サンプル3 : Chainer: ビギナー向けチュートリアル Vol.1 - Qiita

import numpy as np
from chainer import serializers
from chainer.cuda import to_gpu
from chainer.cuda import to_cpu

model = MLP()
serializers.load_npz('mnist_result/model_epoch-10', model)
model.to_gpu(gpu_id)

%matplotlib inline
import matplotlib.pyplot as plt

x, t = test[0]
plt.imshow(x.reshape(28, 28), cmap='gray')
plt.show()
print('label:', t)

x = to_gpu(x[None, ...])
y = model(x)
y = to_cpu(y.data)

print('predicted_label:', y.argmax(axis=1)[0])

参考:

Chainerでニューラルネットワーク及び畳み込みニューラルネットワークを実装してみた – データ分析エンジニアが気まぐれに更新するブログ

https://github.com/nocotan/chainer-examples/blob/master/examples/mnist-cnn.py

Convolutional Neural Networkを実装する - Qiita

機械学習フレームワークchainerを使って1からコードをかいてみる(mnist編) - Qiita

Chainer: ビギナー向けチュートリアル Vol.1 - Qiita

わかりやすい ↓

Chainerのソースを解析。MNISTサンプルを追ってみる | コード7区

C++のお勉強(2)

一歩ずつ!

型・インスタンス・オブジェクト

構造体は型、型を使って作られた変数はインスタンス。特に、構造体は意味のある情報の塊なので、構造体変数のことをオブジェクトということがある。

アクセスの仕方:①オブジェクト.メンバ ②オブジェクトのポインタ->メンバ

列挙型

何らかのフラグを宣言するときによく用いられる

enum ResultFlag{
  Success = 1,
  Failure = 0
}

三項演算子

a > b ? a : b 真ならばa、偽ならばb

静的変数

プログラムが実行される前にすでに位置がきまっている変数のこと。グローバル変数は静的変数。

初期化はローカル変数であっても一度しか行われない。関数を抜けても値が保存される。

スコープ

::をつけるとグローバル関数にアクセスできる

#inculde <iostream>

int a = 1;
int main(){
  std::cout<< ::a << std::endl;
} 

メモリの動的確保

newとdeleteはセット。newでメモリを動的に確保でき、ポインタで受ける。

int *p = new int;
*p = 0;
cout << *p << endl;

delete p;

配列

int *array;
cin >> size;
array = new int[size]

delete[] array;

ポインタ

アドレスを入れることのできる変数

ポインタと参照の違い

参照は初期化時に一度参照を決めたら変更できないのに対し、ポインタは再代入すれば何度でも書き換えることが可能。その代わりポインタは*をつけないと参照先のメモリにアクセスできない。

const

ポインタや参照の前にconstをつけると参照先の値の変更ができなくなる。

int StrCount(char* str, char ch){
 if (str[i] = ch)
 < contents > 
} //書き換わってしまう

int StrCount(const char* str, char ch){
 if (str[i] = ch)
 < contents > 
} //書き換わらない

本来はstr[i] == chとしたかったので、constとするとエラーにより気づくことができる

const char* p //参照先の値が定数
char* const p = &a //ポインタpが定数
const char* const p = &a //両方定数

 コピーコンストラクタ

参考: コピーコンストラクタ

初期化と代入の違い

b = a ; //代入 コピーコンストラクタが呼ばれない
intArray b = a; //初期化->コピーコンストラクタ
intArray b(a); //初期化->コピーコンストラクタ

継承

privateは派生クラスにさえ公開されない。派生クラスにのみ公開する場合はprotectedにする。

基底クラスからコンストラクタが呼ばれ、派生クラスからデストラクタが呼ばれる。

基底クラスのコンストラクタに引数を持つ、派生クラスのコンストラクタを呼ぶときは以下のように指定する。

コンストラクタ(仮引数リスト) : 基底クラス名(実引数のリスト) , メンバ変数(初期化の値)
{
...
}

補足:基本の方であってもコンストラクタを持っている

int x(3);
int x = 3;

参考:

C++の基礎 : 継承

アップキャスト

派生クラスのオブジェクトは基底クラスへの参照に渡すことができ、この暗黙のキャストをアップキャストという。

オーバーライド

同じ形の関数を派生クラスで再定義することをオーバーライドという。virtualをつけるとアップキャストしても派生クラスのメンバ関数が呼ばれる。

class hoge{
  public:
    virtual bool Set();
};

オーバーライドとアップキャストなどで得られる性質をポリモーフィズムという

純粋仮想関数

protected:
  virtual void HogeBase() = 0

のようにvirtual ~ =0とすると純粋仮想関数になる。これが含まれているクラスは抽象クラスとよばれ、オブジェクトが作れない。

テンプレート

テンプレート引数、実引数

templete <typename TYPE> // <- テンプレート引数
 void Test(TYPE* num, TYPE value)
{
 int a = 1
}

Test(0.1 , 0) //コンパイルエラー ∵double か intかわからない
Test<double>(0.1 , 0) // <-テンプレート実引数

テンプレートクラス

templete <typename TYPE> class Array{...}

int Array::Size() const{...} // コンパイルエラー ∵Arrayはクラステンプレートでクラスではない

int Array<int>::Size() const{...} //テンプレート引数をもって初めてクラスになる
int Array<double>::Size() const{...} //テンプレート引数をもって初めてクラスになる
//↓
templete <typename TYPE> 
  int Array<TYPE>::Size() const {...} //テンプレート引数をもって初めてクラスになる

エラー処理

try {
  if (エラー判定処理){
     throw エラーメッセージ;
   }
}catch(変数宣言){
 エラー処理
  }

throwなどで投げれれた例外は伝播するので、関数内で発生してもそれを用いている上位でtry catchがあれば、処理される。

例外のキャッチ漏れが無いように注意しよう。

例外の再送出

try{
 throw "エラー発生"
catch(...){
 //したい処理をする
 throw;
}

例外はアップキャスト可能

できる限り、基底クラスから離れた例外でcatchするようにしよう。

Exceptionへの参照のキャッチはできる限り使わない。ex:OpenFileExceptionなど基底クラスから離れたものを使う。

参考: ロベールのC++教室 - 第15章 アップキャスト -

メンバ初期化子

参考: メンバ初期化子 - ぷろみん

ロベールのC++教室 - 第22章 メンバ定数 -

イテレータ

イテレータとは? | C++ フリーでぷろぐらみんぐさんから抜粋。わかりやすい。

#include <cstdlib>
#include <iostream>

  vector<int> vec;
  vector<int>::iterator i;
  int sum = 0;

  vec.push_back(1);
  vec.push_back(2);
  vec.push_back(3);

  for(i=vec.begin(); i!=vec.end(); ++i){
    sum += *i;
  }

  cout << sum << endl; // 6

参考: C++ iterator 入門

c++ - イテレータとポインタの違い - スタック・オーバーフロー

イテレータとは? | C++ フリーでぷろぐらみんぐ

分類のお勉強

sklearnを用いた2クラス分類

#!/usr/bin/env python                                                                                                                                                                                        
# coding: utf-8                                                                                                                                                                                              

import numpy as np
from sklearn import datasets
from sklearn import cross_validation
from sklearn import metrics

# DataSetの準備
iris = datasets.load_iris()

# トレーニングデータ <type 'numpy.ndarray'>の(100,4)
data = iris.data[0:100]                                                                                                                                               

# ラベルデータ (100)
target = []
for t in iris.target[0:100]:
    if t==1:
        target.append(1)
    else:
        target.append(-1)

# 全体のデータの2割を検証用
train_x, test_x, train_y, test_y = cross_validation.train_test_split(data, target, test_size=0.2)

# 最小二乗法で学習                                                                                                                                                                                           
w = np.linalg.inv(train_x.T.dot(train_x)).dot(train_x.T).dot(train_y)

# 最小二乗法で推定                                                                                                                                                                                          
for x in test_x:
    if w.dot(x) > 0:
         
pred_y = np.array([1 if w.dot(x) > 0 else -1 for x in test_x])

# テストデータに対する正答率                                                                                                                                                                                 
print metrics.accuracy_score(test_y, pred_y)

参考:

Home · levelfour/machine-learning-2014 Wiki · GitHub

OpenCV 3で犬と猫を分類できるように学習してみる(BOW: Bag Of Visual Words, KNN: k-Nearest Neighbour, k-meansクラスタリング, KAZE) - Qiita

https://kaigi.org/jsai/webprogram/2012/pdf/340.pdf

とにかくCNNに学習させる大量の画像データが欲しいでごわすという人のためのスクリプト - shi3zの長文日記

画像データの2クラス分類

#!/usr/bin/env python
#-*- encoding: utf-8 -*-

from PIL import Image
import numpy as np
import os
import pandas as pd
import pylab as pl
from sklearn.decomposition import RandomizedPCA
from sklearn.externals import joblib
from sklearn.svm import LinearSVC

# STANDARD_SIZE = (300, 167)
STANDARD_SIZE = (480, 640)


def img_to_matrix(filename, verbose=False):
    img = Image.open(filename)
    if verbose:
        print 'changing size from %s to %s' % (str(img.size), str(STANDARD_SIZE))
    img = img.resize(STANDARD_SIZE)
    imgArray = np.asarray(img)
    return imgArray  # imgArray.shape = (167 x 300 x 3)


def flatten_image(img):
    s = img.shape[0] * img.shape[1] * img.shape[2]
    img_wide = img.reshape(1, s)
    return img_wide[0]


def main():
    img_dir = 'images/'
    images = [img_dir + f for f in os.listdir(img_dir)]
    labels = ['takuya' if 'takuya' in f.split('/')[-1] else 'iron_man' for f in images]
    #labels = ['takuya' if 'takuya' in f.split('/')[-1] else 'iron_man' for f in images]

    #print images
    #print labels

    data = []
    for image in images:
        img = img_to_matrix(image)
        img = flatten_image(img)
        data.append(img)

    data = np.array(data)

    #print data.shape

    # dataの数だけ一様乱数を生成し、0.7以下のものだけtrainに
    is_train = np.random.uniform(0, 1, len(data)) <= 0.7

    # np.array(labels) == 'takuya'の時は1を、そうでないときは0を返す
    # yはlabel
    y = np.where(np.array(labels) == 'takuya', 1, 0)

    # train用をぶちこむ
    train_x, train_y = data[is_train], y[is_train]

    # plot in 2 dimensions
    # 主成分分析
    pca = RandomizedPCA(n_components=2)

    # 分析結果を元にデータセットを主成分に変換する
    X = pca.fit_transform(data)

    #print X.shape

    df = pd.DataFrame({"x": X[:, 0], "y": X[:, 1],
                       "label": np.where(y == 1, 'takuya', 'iron_man')})
    colors = ['red', 'yellow']
    for label, color in zip(df['label'].unique(), colors):
        mask = df['label'] == label
        pl.scatter(df[mask]['x'], df[mask]['y'], c=color, label=label)

    pl.legend()
    pl.savefig('pca_feature.png')

    # training a classifier
    pca = RandomizedPCA(n_components=5)
    train_x = pca.fit_transform(train_x)

    # C : ペナルティ項
    svm = LinearSVC(C=1.0)
    svm.fit(train_x, train_y)
    joblib.dump(svm, 'model.pkl')

    # evaluating the model
    test_x, test_y = data[is_train == False], y[is_train == False]
    test_x = pca.transform(test_x)
    print pd.crosstab(test_y, svm.predict(test_x),
                      rownames=['Actual'], colnames=['Predicted'])



if __name__ == '__main__':
    main()

参考:

pythonを使って簡単な画像分類を実現する - stMind

10分でPandasを学ぶ - Qiita

Numpyのwhereで配列インデックスを取得(python) | コード7区

データの次元削減に関する資料集 - ゆるふわめも

PCAとSVDの関連について - Qiita

10分でわかる主成分分析(PCA)

Python: scikit-learn で主成分分析 (PCA) してみる - CUBE SUGAR CONTAINER

scikit.learn手法徹底比較! SVM編 - Risky Dune

SVMについて調べてみる - Qiita

単純なデータをつかって主成分分析(PCA)を理解する - Qiita

画像処理でのPythonの利用

Pythonで主成分分析をするコード – .COM-POUND

オブジェクト指向プログラミング(OOP)のお勉強

C++をベースの考えてみる

c++コンパイルの仕方

参考

robonchu.hatenablog.com

構造化言語では解決できない2つの問題

  1. グローバル変数
  2. 再利用性

参考:いまさら聞けない構造化手法とオブジェクト指向の違い(めっちゃわかりやすい) http://www.ogis-ri.co.jp/casestudy/docs/CaseStudy_ESEC2010_StructOO.pdf

OOPの優れた3つの仕組み

  1. クラス
  2. ポリモーフィズム
  3. 継承

これらは構造化言語の課題を解決することができる。重複した無駄なロジックを排除し、必要な機能を整理整頓する仕組みを提供する。

参考:

新人プログラマに知っておいてもらいたい人類がオブジェクト指向を手に入れるまでの軌跡 - Qiita

クラスとは

関連性の強いサブルーチンとグローバル変数を1つにまとめて粒度の大きいソフトウェアを作る仕組み

  1. サブルーチンと変数をまとめる
  2. クラスの内部だけで使うサブルーチンを隠す
  3. 一つのクラスからインスタンスたくさん作る
クラスの効能1:まとめる

結びつきの強いサブルーチンとグローバル変数を一つのクラスにまとめることができ、以下のメリットが得られる。

  • 部品数が減る
  • メソッドの名前付けが楽になる
  • メソッドが探しやすくなる

まとめ前

  • 準備:test.txtにatomと記入
#include <iostream>
#include <fstream>

using namespace std;

fstream file;
int fileNO;

void openFile(const char* fileName){
  file.open(fileName, ios::in);
}

void closeFile(){
  file.close();
}

char readFile(){
  char str;
  file.get(str);
  return str;
}

int main(){
  char result;
  const char* filename = "test.txt";
  openFile(filename);
  result = readFile();
  closeFile();
  cout << result << endl;  
}

結果:

a

まとめ後

#include <iostream>
#include <fstream>

using namespace std;

class TextFileReader{ //クラスという単位で見ると一つに!また、大きなまとまりになるため探しやすい!!

private:
  fstream file;
  int fileNO;

public:
  void open(const char* fileName){ //名前がシンプルに!!!
    file.open(fileName, ios::in);
  }

  void close(){
    file.close();
  }

  char read(){
    char str;
    file.get(str);
    return str;
  }
};


int main(){
  char ch1;
  char ch2;

  const char* filename = "test.txt";

  TextFileReader reader1;

  reader1.open(filename);
  ch1 = reader1.read();
  reader1.close();

  cout << ch1 << endl;  

  TextFileReader* reader2 = new TextFileReader;

  reader2->open(filename);
  ch2 = reader2->read();
  reader2->close();
  
  delete reader2;

  cout << ch2 << endl;  
}

結果:

a

a

クラスの効能2:隠す

クラスに定義した変数やメソッドを他のクラスから隠すことができる

これにより保守性悪化の原因となるグローバル変数を使わずにプログラムを書くことができる。

上記コードの抜粋↓

class TextFileReader{ 

private: //privateとするとクラス内部にあるメソッドだけがアクセスできる!変更されたないし、勝手にいじらせへんよ〜
  fstream file; 
  int fileNO;

public: //アプリケーションのどこからでもアクセスできる!いつでも呼び出してや〜
  void open(const char* fileName){ 
    file.open(fileName, ios::in);
  }

  void close(){
    file.close();
  }

  char read(){
    char str;
    file.get(str);
    return str;
  }

ちなみになにもしてしないと勝手にprivateになるよ〜

クラスの効能3:たくさん作る

インスタンスはクラスを定義しておけばそこから実行時にいくつでも作る(メモリ領域を確保する)ことができる

そのインスタンスのメソッドの呼び方↓

インスタンスを格納する変数. メソッド名(引数)

インスタンスを格納する変数ポインタ->メソッド名(引数)

  • 準備:robot1.txtにatomと記入 . robot2.txtにbig hero 6 と記入.
#include <iostream>
#include <fstream>

using namespace std;

class TextFileReader{

private:
  fstream file;
  int fileNO;

public:
  void open(const char* fileName){
    file.open(fileName, ios::in);
  }

  void close(){
    file.close();
  }

  char read(){
    char str;
    file.get(str);
    return str;
  }
};


int main(){
  char ch1;
  char ch2;
  char ch3;

  const char* filename1 = "robot1.txt";
  const char* filename2 = "robot2.txt";

  TextFileReader* reader1 = new TextFileReader; //インスタンス一つ目製造
  TextFileReader* reader2 = new TextFileReader; //インスタンス二つ目製造

  reader1->open(filename1);
  ch1 = reader1->read();
  reader1->close();

  reader2->open(filename2);
  ch2 = reader2->read();
  reader2->close();
  
  delete reader1;
  delete reader2;

  // open(filename);
  // result = read();
  // close();

  cout << ch1 << endl;  
  cout << ch2 << endl;  

  TextFileReader reader3; //インスタンス三つ目製造

  reader3.open(filename1);
  ch3 = reader3.read();
  reader3.close();

  cout << ch3 << endl;  
}

結果:

a

b

a

インスタンス変数、グローバル変数、ローカル変数

インスタンス変数
  1. 別クラスからアクセスできないよう隠すことができる
  2. 一度インスタンス化されば破棄されるまでメモリ上に残る

インスタンス変数は「長持ちするローカル変数」または「仲間内だけのグローバル変数

 ポリモーフィズム(多様性)とは(インターフェースの継承)

共通メインルーチンを作るための仕組み、つまり、呼び出す側のロジックを一本化する

ポリモーフィズムはサブルーチンを呼び出す側のロジックを一本化する仕組み、共通サブルーチンを作る仕組み

継承(実装の継承)

クラス定義の共通部分を別クラスにまとめて、コードの重複を排除する仕組み

 型にはまろう

を指定することのメリットは 1. コンパイラにメモリ領域を教える 1. プログラムのエラーを防止する

型にクラスを指定できる

型チェックには強い型付け弱い型付けの2種類の方式がある

C++:強い型付け(コンパイル時点でエラーを検出)

python:弱い型付け(実行時点でエラーを検出)

ポリモ・継承・型はめのサンプル
基底クラス 派生クラス mainクラス
Stream <- InputStream Polymorphism
<- ArrayStream

Stream.h

#ifndef STREAM_H_
#define STREAM_H_

class Stream {
 public :
  double Get() const;
  virtual bool Set() = 0; //純粋仮想関数->このクラス(抽象クラス)は実体をつくることができない。
  //virtual bool Set(); //仮想関数

 private :
  double m_n;
};

#endif

Stream.cpp

#include "Stream.h"
#include <iostream>
using namespace std;

double Stream::Get() const {
  return m_n;
}

// bool Stream::Set() {
//   cout << "Stream::Set" << endl;
//   m_n = -1;
//   return false;
// }

InputStream.h

#ifndef INPUTSTREAM_H_
#define INPUTSTREAM_H_

#include "Stream.h"

class InputStream :
public Stream{ //継承することでGetを定義する必要がなくなる。(コードの重複を排除)
 public:
  bool Set();
};

#endif 

InputStream.cpp

#include "InputStream.h"
#include <iostream>
using namespace std;

bool InputStream::Set(){ //Setをオーバーライド
  cin >> m_n;
  return m_n >= 0;
}

ArrayStream.h

#ifndef ARRAYSTREAM_H_
#define ARRAYSTREAM_H_

#include "Stream.h"

class ArrayStream : 
  public Stream //継承することでGetを定義する必要がなくなる。(コードの重複を排除)
{
 public :
  ArrayStream(const double* array);
  bool Set();

 private:
  const double* m_array;
  int m_i;
};

#endif

ArrayStream.cpp

#include "ArrayStream.h"

ArrayStream::ArrayStream(const double* array){
  m_array = array;
  m_i = 0;
}

bool ArrayStream::Set(){ //Setをオーバーライド
  m_n = m_array[m_i];
  if ( m_n >= 0){
    ++m_i;
    return true;
  }else{
    return false;
  }
}

Polymorphism.cpp

#include "InputStream.h"
#include "ArrayStream.h"
#include <iostream>
using namespace std;

bool Average(Stream& stream){ //こうすることで、InputStreamとArrayStreamr両方のAverage関数を作る必要がなくなる。(呼び出す側のロジックの一本化)
  int count;
  double avr = 0;

  for( count = 0 ; stream.Set(); ++count){ //virtual bool Set() = 0;サブクラスのSetが呼ばれる。virtualがないとスーパークラスのSetが呼ばれる。
    avr += stream.Get(); 
  }
  if (count == 0){
    return false;
  }

  avr /= count;
  cout << "平均値は" << avr << "です。" << endl;
  return true;
}

int main () {
  InputStream istream; //型の**クラス**指定
  Average(istream); //アップキャスト

  static const double ARRAY[] = {0.5, 1.5, -1};
  ArrayStream astream(ARRAY); //型の**クラス**指定
  Average(astream); //アップキャスト
}

コンパイル:

$ g++ Stream.cpp InputStream.cpp ArrayStream.cpp Polymorphism.cpp

結果 :

1

2

3

-1

平均値は2です。

平均値は1です。

パッケージ

クラスをさらにまとめる仕組み

c++でいうと名前空間

#include <iostream>

using namespace std;

namespace { //無名名前空間
  void PowerOn(){
    cout << "Robot Start" << endl;
  }
}

namespace Robot1
{
  void Func(){
    cout << "Robot1::Func" << endl;
  }

  void Mode(){
    cout << "Robot1::Mode" << endl;
  }
}

namespace Robot2
{
  void Func(){
    cout << "Robot2::Func" << endl;
  }
}

void Func(){
  cout << "::Func" << endl;
}

namespace Robot3{
  namespace Robot4{
    namespace Robot5{
      void Attack(){
    cout << "Robot3~5::Attack" <<endl;
      }
    }
  }
}

namespace R345 = Robot3::Robot4::Robot5;

int main()
{
  PowerOn();
  Robot1::Func();
  Robot1::Mode();
  Robot2::Func();
  ::Func();
  R345::Attack();
  
  return 0;
}

例外

戻り値とは違う形でメソッドから特別なエラーを返す仕組み

  1. 例外を宣言しているメソッドを呼び出す側では例外を処理するロジックを正しく書いていないとプログラムはエラーになる
  2. そのまま上位にエラーを伝えるときはメソッドで例外を宣言するだけでよい

Exception.cpp

#include <iostream>
#include <fstream>
#include <cstdlib>
using namespace std;

void Open(ifstream& file, const char* filename){
  file.open(filename);
  if(! file.is_open()){
    throw "ファイルを開けませんでした!"; //メソッドで例外を宣言すると関数外、上位へエラーが伝わる
  }
}

void GetLine(ifstream& file, string& line){
  getline(file, line);
  if (file.fail()){
    throw "ファイルから読み込めませんでした!";
  }
}

int main(){
  try { //OpenやGetLineで投げられたエラーがここで処理される
    ifstream file;
    Open(file, "test.txt");

    string line;
    GetLine(file, line);
    cout << line << endl;
  }catch(const char* error){ //char -> intにするとエラーになる(terminate called after throwing an instance of 'char const*')
    cerr << error << endl;
    return EXIT_FAILURE;
  }
}

test.txtがない場合の結果:

ファイルを開けませんでした!

test.txtが空の場合の結果:

ファイルから読み込めませんでした!

ガベージコレクション

インスタンスを削除する処理をシステムが自動的に実行する仕組み

Scavenge GCとFull GCについては以下参照↓

参考:めっちゃわかりやすい

5分で分かるガベージコレクションの仕組み | geechs magazine

所感

整理整頓の仕組みがたくさんあるな〜。整理整頓上手になりたい。

参考:

const char* const

変数の const 付け方一覧 - Qiita

ロベールのC++教室

ROSのスクリプト構成を考えてみる

C++

pattern1 package

構成: func.h , func.cpp, func_node.cpp

func.h

  1. include <必要なファイル>
  2. namespaceをパッケージ名で作成
  3. Funcクラスを作成
  4. コンストラクタとデストラクタの定義(Public)
  5. pubやsub,callbackを定義(Private)
  6. その他、必要な変数や構造体を定義(Private)

ヘッダには一度だけincludeされるよう以下を書いておこう

#ifndef FUNC_HPP_
#define FUNC_HPP_

<contents>

#endif

func.cpp

  1. include <必要なファイル>
  2. namespace{ } 無名名前空間を作成。このファイルのみで使用する変数や関数を定義する
  3. namespaceをパッケージ名で作成
  4. Funcクラスの内容を記述する
  5. コンストラクタでpubやsubを作成
  6. RunMainLoopにsFunctionのメインの実行文を記述
void Func::RunMainLoop(){
  ros::Rate rate(0.1);
  while (ros::ok()){
    ros::spinOnce();

    <Functionの実装>
  }
}

func_node.cpp

#include func.h

int main(int argc, char* argv[]){
  ros::init(argc, argv, "func");
  ros::start()

  pattern1::Func func;
  func.RunMainLoop();

  return EXIT_SUCCESS;
}
boost libraryとは

参考: Boost - C++入門 boost - ROS Wiki BOOST FOREACH - C++入門

auto

C++11で追加された型推論

#include <iostream>
#include <vector>
using namespace std;
 
int
main(int argc, char const* argv[])
{
        std::vector<int> v;
        v.push_back (1);
        v.push_back (2);
        v.push_back (3);
        for (auto x: v) {
                cout << x << endl;
        }
        return 0;
}

参考: auto - C++入門 C++11 範囲ベース for ループ 入門

find

指定された値を検索する

#include <algorithm>
#include <iostream>
#include <vector>

int main() {
  std::vector<int> v = { 3, 1, 4 };
  auto result = std::find(v.begin(), v.end(), 1);
  if (result == v.end()) {
    std::cout << "not found" << std::endl;
  } else {
    std::cout << "found: " << *result << std::endl;
  }
}

1

参考: find - cpprefjp C++日本語リファレンス

ハンドル

参考: C++ - ハンドル

tmuxの設定をいじっていみる

設定方法

.tmux.confに設定を記入

設定が反映されない時

tmuxのプロセスをkillしてから立ち上げる

設定内容

tmux v2.1~ :マウスの設定変わっている shift + 右クリック tmux v2.1からmouse関連の設定が変わった

tmuxチートシート

tmuxの個人的なオススメメモ

tmux 2.1 以降でもマウスを有効にする方法

ubuntu16.04

##############
# マウス設定 #
##############

set-option -g mouse on

# アクティブなウィンドウを目立たせる
setw -g window-status-current-fg white
setw -g window-status-current-bg red
setw -g window-status-current-attr bright

########
# 外観 #
########

# ステータスバーの色を設定する
set -g status-fg white
set -g status-bg black

# ウィンドウリストの色を設定する
setw -g window-status-fg cyan
setw -g window-status-bg default
setw -g window-status-attr dim
# アクティブなウィンドウを目立たせる
setw -g window-status-current-fg white
setw -g window-status-current-bg red
setw -g window-status-current-attr bright

# ペインボーダーの色を設定する
set -g pane-border-fg green
set -g pane-border-bg black
# アクティブなペインを目立たせる
set -g pane-active-border-fg white
set -g pane-active-border-bg yellow

# コマンドラインの色を設定する
set -g message-fg white
set -g message-bg black
set -g message-attr bright

ubuntu14.04

##############
# マウス設定 #
##############

# 画面ドラッグ時にコピーモードにする
set-option -g mode-mouse on

# マウスでペインを選択できるようにする
set-option -g mouse-select-pane on

# マウスでウィンドウを切り替えられるようにする
set-option -g mouse-select-window on

# マウスでリサイズできるようにする
set-option -g mouse-resize-pane on

# アクティブなウィンドウを目立たせる
setw -g window-status-current-fg white
setw -g window-status-current-bg red
setw -g window-status-current-attr bright

########
# 外観 #
########

# ステータスバーの色を設定する
set -g status-fg white
set -g status-bg black

# ウィンドウリストの色を設定する
setw -g window-status-fg cyan
setw -g window-status-bg default
setw -g window-status-attr dim
# アクティブなウィンドウを目立たせる
setw -g window-status-current-fg white
setw -g window-status-current-bg red
setw -g window-status-current-attr bright

# ペインボーダーの色を設定する
set -g pane-border-fg green
set -g pane-border-bg black
# アクティブなペインを目立たせる
set -g pane-active-border-fg white
set -g pane-active-border-bg yellow

# コマンドラインの色を設定する
set -g message-fg white
set -g message-bg black
set -g message-attr bright

参考:

達人に学ぶ.tmux.confの基本設定

tmuxが快適、かつ .tmux.conf の設定

ペイン関係

  • ペイン番号表示・移動
ctrl+b -> q -> 番号

kerasを動かしてみる~その2:CNN~

tensorflowをバックエンドで動かす

.keras/keras.jsonを以下のように書き換え

{
    "image_dim_ordering": "tf", 
    "epsilon": 1e-07, 
    "floatx": "float32", 
    "backend": "tensorflow"
}

GPUで使う

pip install tensorflow-gpu

(TBD)

CNNのサンプル

GitHub - fchollet/keras: Deep Learning library for Python. Runs on TensorFlow, Theano, or CNTK.

cifar10

from keras.datasets import cifar10

  • CIFAR-10は32x32ピクセルのカラー画像のデータセット
  • クラスラベルはairplane, automobile, bird, cat, deer, dog, frog, horse, ship, truckの10種類
  • 訓練用データ5万枚、テスト用データ1万枚

x_train shape: (50000, 32, 32, 3)

50000 train samples

10000 test samples

y_train shape: (50000, 1)

50000 train samples

10000 test samples

[3] ‘label -> ex: 3 ’

[ 0. 0. 0. 1. 0. 0. 0. 0. 0. 0.] one_hot

DataAugumentation

from keras.preprocessing.image import ImageDataGenerator

参考:画像の前処理 - Keras Documentation

keras-examples/test_datagen3.py at master · aidiary/keras-examples · GitHub

Keras Model

from keras.models import Sequential

from keras.layers import Dense, Dropout, Activation, Flatten

from keras.layers import Conv2D, MaxPooling2D

一層目のみinput_shape=x_train.shape[1:]のようにinput_shapeの指定がいる。

x_train shape: (50000, 32, 32, 3)の後ろ3つを取り出している。

モデルの保存・読み込み

from keras.models import load_model

model.save('my_model.h5')  # creates a HDF5 file 'my_model.h5'
del model  # deletes the existing model

# returns a compiled model
# identical to the previous one
model = load_model('my_model.h5')

参考:[TF]KerasでModelとParameterをLoad/Saveする方法 - Qiita

コード例

'''Train a simple deep CNN on the CIFAR10 small images dataset.
GPU run command with Theano backend (with TensorFlow, the GPU is automatically used):
    THEANO_FLAGS=mode=FAST_RUN,device=gpu,floatx=float32 python cifar10_cnn.py
It gets down to 0.65 test logloss in 25 epochs, and down to 0.55 after 50 epochs.
(it's still underfitting at that point, though).
'''

from __future__ import print_function
import keras
from keras.datasets import cifar10
from keras.preprocessing.image import ImageDataGenerator
from keras.models import Sequential
from keras.layers import Dense, Dropout, Activation, Flatten
from keras.layers import Conv2D, MaxPooling2D
from keras.models import load_model
import numpy as np


batch_size = 32
num_classes = 10
epochs = 2
data_augmentation = True

# The data, shuffled and split between train and test sets:
(x_train, y_train), (x_test, y_test) = cifar10.load_data()
print('x_train shape:', x_train.shape)
print(x_train.shape[0], 'train samples')
print(x_test.shape[0], 'test samples')
print('y_train shape:', y_train.shape)
print(y_train.shape[0], 'train samples')
print(y_test.shape[0], 'test samples')
print(y_test[0], 'label -> ex: 3 ')

# Convert class vectors to binary class matrices.
y_train = keras.utils.to_categorical(y_train, num_classes)
y_test = keras.utils.to_categorical(y_test, num_classes)
print(y_test[0], 'one_hot')

model = Sequential()

model.add(Conv2D(32, (3, 3), padding='same',
                 input_shape=x_train.shape[1:]))
model.add(Activation('relu'))
model.add(Conv2D(32, (3, 3)))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Dropout(0.25))

model.add(Conv2D(64, (3, 3), padding='same'))
model.add(Activation('relu'))
model.add(Conv2D(64, (3, 3)))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Dropout(0.25))

model.add(Flatten())
model.add(Dense(512))
model.add(Activation('relu'))
model.add(Dropout(0.5))
model.add(Dense(num_classes))
model.add(Activation('softmax'))

# initiate RMSprop optimizer
opt = keras.optimizers.rmsprop(lr=0.0001, decay=1e-6)

# Let's train the model using RMSprop
model.compile(loss='categorical_crossentropy',
              optimizer=opt,
              metrics=['accuracy'])

x_train = x_train.astype('float32')
x_test = x_test.astype('float32')
x_train /= 255
x_test /= 255

if not data_augmentation:
    print('Not using data augmentation.')
    model.fit(x_train, y_train,
              batch_size=batch_size,
              epochs=epochs,
              validation_data=(x_test, y_test),
              shuffle=True)
else:
    print('Using real-time data augmentation.')
    # This will do preprocessing and realtime data augmentation:
    datagen = ImageDataGenerator(
        featurewise_center=False,  # set input mean to 0 over the dataset
        samplewise_center=False,  # set each sample mean to 0
        featurewise_std_normalization=False,  # divide inputs by std of the dataset
        samplewise_std_normalization=False,  # divide each input by its std
        zca_whitening=False,  # apply ZCA whitening
        rotation_range=0,  # randomly rotate images in the range (degrees, 0 to 180)
        width_shift_range=0.1,  # randomly shift images horizontally (fraction of total width)
        height_shift_range=0.1,  # randomly shift images vertically (fraction of total height)
        horizontal_flip=True,  # randomly flip images
        vertical_flip=False)  # randomly flip images

    # Compute quantities required for feature-wise normalization
    # (std, mean, and principal components if ZCA whitening is applied).
    datagen.fit(x_train)

    # Fit the model on the batches generated by datagen.flow().
    model.fit_generator(datagen.flow(x_train, y_train,
                                     batch_size=batch_size),
                        steps_per_epoch=x_train.shape[0] // batch_size,
                        epochs=epochs,
validation_data=(x_test, y_test))

"""
# save the model
model.save('./model.hdf5')
"""

"""
# load model 
model = load_model('model.hdf5')

#loss, acc = model.evaluate(x_test, y_test, verbose=0)
#print('Test loss:', loss)
#print('Test acc:', acc)

# only one test
x_test = x_test[0].reshape(1, 32, 32, 3)
print(model.predict(x_test))

# predict 
result = model.predict(x_test)
print(np.argmax(result))
"""
配列の入れ替え

shape: (1,2,3,4) -> (1,4,2,3)

データ構造

keras -> train shape: (50000, 32, 32, 3)

chainer -> train shape: (50000, 3, 32, 32)

import numpy as np

x = np.arange(4*6).reshape(1,2,3,4)
print x.shape

x = np.swapaxes(x,1,3)
y = np.swapaxes(x,1,2)
print y.shape

参考:

KerasでVGG16を使う - 人工知能に関する断創録

Deep learningで画像認識⑧〜Kerasで畳み込みニューラルネットワーク vol.4〜 - IMACEL Academy -人工知能・画像解析の技術応用に向けて-|LPixel(エルピクセル)

Kerasで学ぶAutoencoder