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

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

機械学習のお勉強(特徴量)

TBD

Haar-Like

物体の局所的な明暗差の組み合わせにより、画像を判別する

LBP(Local Binary Pattern)

物体の局所的な輝度の分布の組み合わせにより、画像を判別する

HOG(Histogram of Oriented Gradients)

物体の局所的な輝度の勾配方向の分布の組み合わせにより、画像を判別する

SHIFT

移動、回転に加えてズーム対してロバストな特徴を持つ

SURF

SIFTは計算量が多く、低速だったため、速度を改良したアルゴリズム

ORB

移動、回転、ズームの3つに対してロバストで、計算速度も速い

AKAZE

計算量がORBよりもやや多いものの、低周波領域、高周波領域の抽出精度がORBよりも優れている

OpenCV3とPython3で特徴点を抽出する(AgastFeature, FAST, GFTT, MSER, AKAZE, BRISK, KAZE, ORB, SimpleBlob, SIFT) - Qiita

主成分分析

OpenCV3とPython3で形状のある物体の輪郭と方向を認識する(主成分分析:PCA、固有ベクトル) - Qiita

エッジ検出

OpenCVでエッジ検出してみる - Qiita

カラーヒストグラム

類似画像検索システムを作ろう - 人工知能に関する断創録

領域分割

画像を同じ特徴を持つ複数の領域に分ける方法 (1/2):CodeZine(コードジン)

pythonで画像領域分割を実装 (Union-Find) - Qiita

http://www.riken.jp/brict/Yoshizawa/Lectures/Kyuusyu/Lectures2011_05.pdf

解決済み: 画像から色指定で領域抽出する方法 - NI Community

opencv.jp - OpenCV: 画像分割,領域結合,輪郭検出(Image Segmentation, Connected Components and Contour Retrieval)サンプルコード -

複数の特徴量の組み合わせ

https://www.toshiba.co.jp/tech/review/2010/07/65_07pdf/f02.pdf

http://www.me.cs.scitec.kobe-u.ac.jp/~takigu/pdf/2009/C-03.pdf

http://www.vision.cs.chubu.ac.jp/MPRG/F_group/F074_fujiyoshi2008.pdf

参考:

深層学習 を 用いた 異常値検知 手法まとめ 〜 (Denosing) AutoEncoder, LSTM, TDA(Topological Data Analysis) + CNN - Qiita

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

http://www.vision.cs.chubu.ac.jp/features/PPT/SSII2009/090610_SSII2009_Tutorial.pdf

http://www.vision.cs.chubu.ac.jp/cvtutorial/PDF/02SIFTandMore.pdf

http://www.vision.cs.chubu.ac.jp/sift/PDF/pcsjimps2008_ppt.pdf

https://www.pro-s.co.jp/engineerblog/opencv/post_6231.html

OpenCV 3とPython 3で特徴量マッチング(A-KAZE, KNN) - Qiita

機械学習のお勉強に役立つサイト↓ http://qiita.com/daxanya1/items/218f2e3b922142550ef9?utm_content=buffer40ad9&utm_medium=social&utm_source=facebook.com&utm_campaign=buffer

機械学習のお勉強(最小二乗法、最尤推定法、パーセプトロン)

最小二乗法

ex: 観測値と真値の二乗誤差を最小にするようにn次多項式を解く。

データセットがn以上あると解析的に平方根平均二乗誤差が0の多項式を導くことができる。

平方根平均二乗誤差:解いて求めた多項式から推定する値とデータセットとの値が平均的にどの程度異なっているか

パラメトリックモデルの解き方 1. パラメータを含むモデルを設定する

  1. パラメータを評価する基準を定める

  2. 最良の評価を与えるパラメータを決定する

参考:ヘッセ行列 - Wikipedia

オーバフィッティングの検証

仮説/検証を行う。

  1. 利用可能なデータセットをトレーニング用とテスト用に分ける

  2. トレーニングデータセットで仮説をたてる(モデルを作る)

  3. テストデータセットで検証を行う

これによってモデルの汎化能力がわかる。

オーバーフィッテイング:トレーニングデータセットだけに特化したチューニングが発生する状況

クロスバリデーションによる汎化性能の検証

参考: 交差確認(交差検証、Cross-Validation)の簡単な説明 - 具体例で学ぶ数学

交差検定(クロスバリデーション)など機械学習の評価方法まとめ : 新規事業のつくり方

最尤推定

データの背後にある関数に加えて、データに含まれる誤差を併せて推定する手法。

ex: データから多項式の係数と正規分布に基づいているとして標準偏差を求める

尤度関数:「トレーニングデータセットのデータが得られる確率」をパラメータ(ex1:観測値、標準偏差 , ex2:平均、標準偏差)の関数とみなしたもの

この確率が最大になるようにパラメータを決定する方法を最尤推定法と呼ぶ。

多項式の係数の推定値は最小二乗と同じになり、標準偏差の推定値は平方根平均二乗誤差と同じになる。

異なるアプローチをとったにもかかわらず同じ多項式が得られる。つまり、最小二乗法は最尤推定法の中でも、正規分布の誤差を仮定した特別な場合に対応するとみなすことができる。

定量の一致性と不偏性

  • データ数を大きくしていくことで真の値に近づいていくことを一致性と呼ぶ
  • 不偏性を持つ推定量はデータが少ない場合、真の母数から外れる可能性があるが、大きい方と小さい方にはずれる場合が均等にあるということを意味する。

参考: 18-3. 推定量の性質 | 統計学の時間 | 統計WEB

不偏推定量と一致推定量の意味 - 具体例で学ぶ数学

パーセプトロン

単純パーセプトロンからの機械学習入門 - Qiita

pandas

10分でPandasを学ぶ - Qiita

Pandas でデータフレームを作ってみよう – Python でデータサイエンス

[Python] pandasの使い方まとめ - Qiita

Pythonでpandasを使う - 計算物理屋の研究備忘録

scipy

【Python入門】絶対に知っとくべきライブラリscipyの基本的な使い方 - Qiita

1.5. Scipy: 高水準の科学技術計算 — Scipy lecture notes

問題を解きながら学ぶ

PFNさんが公開してくださっている機械学習の問題↓

GitHub - pfnet/intern-coding-tasks: Coding tasks for PFN internship

ネットワークのお勉強その2

SSIDとは

Service Set IDentifier! 

SSIDとは、無線LANのアクセスポイントを識別するための名前のこと。

参考:

SSIDとは - IT用語辞典 Weblio辞書

無線LANルーターのSSIDとは?-ELECOM WEB SITE!

アクセスポイントとは

「アクセスポイント」は、Wi-Fiの通信における”中継点”となる場所。

参考:

アクセスポイントとは - IT用語辞典

マルチSSID

マルチSSIDとは、1つのWi-Fi親機で複数のSSIDが使える機能。1つの親機が複数のアクセスポイントを持つ。

メリット

それぞれのアクセスポイントで別々の設定ができる(特に、セキュリティ方式を分けることができるのが魅力)

問題:最大何個のマルチSSIDを同時に使用できるの?

Ciscoだと最大16個までできるよ。

参考:

マルチSSIDとは - Wi-Fiの使い方初心者用ガイド

最大何個のマルチSSIDを同時に使用できますか(法人向けアクセスポイント) - アンサー詳細 | BUFFALO バッファロー

無線LAN - Cisco無線LANソリューション : 基本設計と基本構成

Wi-Fiのセキュリティ機能 - Wi-Fiの使い方初心者用ガイド

ステルスID

SSIDステルス機能とは、アクセスポイントが発信する電波にSSIDを含めないようにする機能のこと。

参考:

SSIDのステルス設定-Wi-Fi入門講座

アクティブスキャンとパッシブスキャン

アクティブスキャンをすればステルスIDにも接続することができる。

参考:

無線LAN - ビーコン、プローブ、認証、アソシエーション

http://www0.info.kanagawa-u.ac.jp/~matsuo/theses/pdf/matsuo_lab_20140202_kawashima_abst.pdf

wpa_supplicant.conf

wpa_supplicantは、無線LANアクセスポイントに対し、WPAで接続要求を行うためのコマンド。

ステルスIDに接続したい時

scan_ssid = 1 でOK!

# scan_ssid:
#       0 = do not scan this SSID with specific Probe Request frames (default)
#       1 = scan with SSID-specific Probe Request frames (this can be used to
#           find APs that do not accept broadcast SSID or use multiple SSIDs;
#           this will add latency to scanning, so enable this only when needed)
# disabled:
#   0 = this network can be used (default)
#   1 = this network block is disabled (can be enabled through ctrl_iface,
#       e.g., with wpa_cli or wpa_gui)
key_mgmt: accepted authenticated key management protocol

無線LANのステルスAPにLinux(wpa_supplicant)から接続する - Dマイナー志向

設定例:

ctrl_interface=DIR=/var/run/wpa_supplicant
update_config=1

network={
        scan_ssid=1
        key_mgmt=WPA-PSK
        ssid="xxxx"
        psk=xxxx
        disaxled=1
}

network={
        scan_ssid=1
        key_mgmt=WPA-PSK
        ssid="xxxx"
        psk=xxxx
}

参考:

WPA supplicant - ArchWiki

wpa_supplicant

http://www.cs.upc.edu/lclsi/Manuales/wireless/files/wpa_supplicant.conf

無線LANのセキュリティ パソコン初心者講座

ネットワーク管理の基本Tips:無線LANにWPAで接続するには? wpa_supplicantコマンド - @IT

wpa_cli

以下のコマンドでSSIDに接続できる

$ sudo wpa_cli select_network 0 //一番目に設定したSSIDに接続
$ sudo wpa_cli select_network 1 //二番目に設定したSSIDに接続

DNS

DNS(Domain Name System)」とは、、コンピュータ名やドメイン名などの名前を表す文字列とIPアドレスの対応を管理したり、検索したりする階層構造のサービス。

Google Public DNS「8.8.8.8」「8.8.4.4」

参考:

インターネット10分講座 - JPNIC

超入門DNS:DNSとは - @IT

DNS(Domain Name System)とは-インターネット入門ガイド

VLAN

VLANとは、「Virtual LAN」の略で以下の図のように、論理的に1台のスイッチに独立した複数のLANを持たせることが出来る機能

参考:

VLANの基礎 - ネットワークエンジニアを目指して

 ネットワーク全般

参考:

わかりやすい! ネットワークエンジニアを目指して - ネットワーク技術の解説

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++教室