2015年7月21日火曜日

STL vector の基本操作・サイズ・イテレーター・操作・交換・判定

http://program.station.ez-net.jp/special/handbook/cpp/stl/vector.asp
より転載

可変長配列 std::vector<> を使用する

C++ では、可変長の配列を簡単に利用できる std::vector<> というテンプレートクラスが用意されています。
これを使用することで、要素を追加したり削除したり、挿入したりといったことが簡単にできます。同じようなものに std::list がありますけど、こちらと違ってインデックス番号を使って任意の要素を自由に取り出せるのも魅力です。
テンプレートクラスなので、任意のデータ型を扱えるのも便利です。

使用準備

C++ で std::vector<> を使用するには、次のようにヘッダーファイルをインクルードする必要があります。
#include <vector>
このようにすることで、std::vector<> テンプレートクラスが利用できるようになります。

また、この std::vector<> を操作するのに使う一部の関数は、次のヘッダーに用意されています。
#include <algorithm>
これをインクルードすることで、並び替えなどの外部関数を使うことができます。

初期化

std::vector<> インスタンスは、次のようにして生成します。
std::vector<int> vector;
左側で指定している "std::vector<int>" がデータ型になります。
ここで "<int>" としているのは、この std::vector<> が int 型を扱うということを意味しています。ここを使いたい型に変えることで、任意の型を std::vector<> で扱うことが可能です。
その後の "vector" は、自由につけられる変数名です。

利用できるメソッド

std::vector<> には、動的配列を手軽に操作するためのメソッドがたくさん用意されています。
その中の、良く使いそうなメソッドをいくつか紹介します。

基本操作

TYPE at(sizet index)指定したインデックス番号の要素を取得します。[] 演算子と違って、無効なインデックスを指定した場合は out_of_range 例外が発生するので、間違いを検出しやすくなっています。
void clear()格納されている要素を全て削除します。
void push_back(const TYPE& value)指定した値を最後尾に追加します。新しい値は std::vector<> 内の要素を格納するメモリに代入文で設定されます。このとき、追加済みの全ての要素が再構築されます。
void pop_back()最後尾の要素を削除します。
TYPE front()先頭要素を取得します。
TYPE back()最終要素を取得します。
std::vector<> は、スタック的な動作をするメソッドが用意されています。
push_back で最後尾に値を追加し、pop_back で最後尾の要素を削除します。
このとき、値の追加の際には、既存の全ての要素がコピーコンストラクタを使って再構築され、それまで使っていた値は全て破棄されます。最後尾の削除については、最後の要素が破棄されるだけで、その他の要素はそのままのようです。

最後尾の要素の値を取得したい場合には back を使用します。
また、std::vector<> では任意の場所の値を at メソッドまたは [] 演算子を使って取得できます。
どちらも 0 から始まるインデックス番号で要素を指定することができますが、存在しない要素を指定した場合、at メソッドでは out_of_range 例外が発生します。[] 演算子の場合は、不正のまま続行されます。

状態の取得

size_t size()格納されている要素数を取得します。
void empty()要素が格納されていない場合に true を返します。
std::vector<> では、要素の数を知るための size メソッドの他にも、要素があるかどうかを調べるための empty メソッドも用意されています。

イテレータ

iterator begin()要素の先頭を示すイテレータを取得します。
iterator end()要素の末尾 (EOF) を示すイテレータを取得します。最終要素はこのひとつ前にあります。
reverse_iterator rbegin()要素の末尾を示すリバースイテレータを取得します。
reverse_iterator rend()要素の先頭 (EOF) を示すリバースイテレータを取得します。先頭要素はこのひとつ前にあります。
イテレータというのは、繰り返し処理を支援する仕組みです。
std::vector<>::iterator 型のイテレータでは、アロー演算子 (->) を使うことで、そのイテレータが示す位置にある値のポインタを取り出すことができるようになっています。このイテレータの値を足したり引いたりすることで、位置を前後に移動することもできます。
std::vector<>::reverse_iterator 型のリバースイテレータは、通常のイテレータの逆順に動作するイテレータです。

また、std::vector<> を const 指定で使用している場合、これらのイテレータは読み取り専用のものが取得されます。
その時に begin メソッドや end メソッドなどで取得できるイテレータの型は普段とは異なり、それぞれ std::vector<>::const_iterator と std::vector<>::const_reverse_iterator になります。

特定の要素の操作

iterator erase(iterator pos)
iterator erase(iterator start, iterator end)
イテレータで指定した pos 位置の、または start から end までの要素を削除します。戻り値は削除された要素の次を示します。
iterator insert(iterator pos, const TYPE& value)
void insert(iterator pos, size_t num, const TYPE& value)
イテレータで指定した pos 位置に値 value を挿入します。このとき num が指定されていれば、
イテレータで要素の位置を指定することで、それが示す要素を削除したり、そこに新しい要素を追加したりすることもできます。

要素の交換

void swap(std::vector<>& source)指定された std::vector<> の要素と、自身が保持する要素とを入れ替えます。
2 つの std::vector<> の要素を交換するメソッドも用意されていました。

使用できる演算子

一致の判定

==ふたつの std::vector<> が一致するかを調べます。要素は == 演算子を使って評価されます。
!=ふたつの std::vector<> が一致しないかを調べます。要素は == 演算子を使って評価されます。
std::vector<> での一致判定は、2 つの std::vector<> の .size() が一致するかどうかと、同じインデックス番号の要素がそれぞれ全て一致するかで判定されます。
全てを満たす時に true、どれかが満たされない場合に false となります。
このとき、== でも != でも、要素ごとの判定は "==" が使われるようでしたが、処理系に依存するかもしれません。また、判定は条件が満たされなくなったところで打ち切られるようでした。

順位の判定

<=左の std::vector<> が右の std::vector<> 以下かを調べます。
>=左の std::vector<> が右の std::vector<> 以上かを調べます。
<左の std::vector<> が右の std::vector<> 未満かを調べます。
>左の std::vector<> が右の std::vector<> より大きいかを調べます。
std::vector<> での順位判定は、std::lexicographically_compare 関数のような自書式順序で比較が行われるそうです。
辞書式順序というのは Wikipedia: 辞書式順序 にも詳しく記載されていますが、C 文字列の比較 strcmp と同じと思うと判りやすいと思います。つまり > や < などで途中まで一致していても、最後の要素で大小関係が正しく成り立っている場合は true になります。
これを判定するとき、内部ではどうやら要素を比較するとき、与えられた std::vector<> の順序とそれを逆順にしたのとのそれぞれで < 演算を行うことで、小さいのか、大きいのか、どちらでもない(等しい)のかを判定しているようでしたが、処理系に依存するかもしれません。また、判定は条件が満たされなくなったところで打ち切られるようでした。

要素の操作

[]std::vector<> の指定されたインデックス番号の要素を取得します。
=左の std::vector<> に、右の std::vector<> を代入します。このとき、要素は新しいものが作られてコピーされます。
std::vector<> では [] 演算子を使って指定した要素を取り出せるようになっています。
ただし、存在する要素の範囲を超えたインデックスを指定すると、[] の場合は不正な値を取得して処理を続けてしまいます。
確実に範囲内にある場合はいいでしょうけど、確実でない場合は at() メソッドを使って取得する方が、範囲外にアクセスしたときに out_of_range 例外が発生するので安全です。

代入演算子では、左に指定した std::vector<> に右の要素を代入します。
このとき、もともとあった要素は全て破棄されて、新しく代入される要素はコピーコンストラクタを使って新しい別の値として代入されます。

□ 要素をインデックスを指定して取得するとき

std::vector<> では、インデックス番号を使って要素を取得することができます。
このとき、std::vector<> では [] 演算子を使って要素を取得することもできますが、at() メソッドを使用した方が安全です。
int value = vector.at(5);
指定したインデックスが、std::vector<> が持っている要素を超えた場合、[] 演算子では不正な値を取得して処理が続行されますが、at() メソッドを使用した場合は out_of_range 例外が発生するようになります。
指定するインデックスが、既存の要素を超えないことがあまりにも確実な場合は、もしかすると [] 演算子の方が高速なのかもしれないですけど、処理速度の効率化が余程必要なところでなければ、at() メソッドを使っておいた方が無難です。

□ std::vector<> でクラスインスタンスを扱う場合

クラスの値を扱う場合

std::vector<> ではクラスインスタンスも扱うことができます。
std::vector<CMyClass> vector;
たとえばこのようにすることで、CMyClass 型を扱う std::vector<> を使えます。
この時、push_back メソッドで挿入されるインスタンスは、通常はコピーコンストラクタを使って生成された新しいインスタンスになります。poo_back メソッドで削除されたインスタンスは、デストラクタが呼び出されます。

ここで気になるのが、push_back メソッドを実行した時です。
このとき vector に既に値が格納されていた場合、それらの値が全てコピーコンストラクタを使って生成し直されます。
コピーにそれほどコストがかからないクラスならいいのですけど、メモリを大きく使用するクラスなどの場合は、要素が追加されるたびに全て生成し直されるのは都合が悪い場合があります。

これについては C++11 で実装されたムーブコンストラクタを実装することで、コピーにかかる負荷を最小限に抑えることができます。
それについては 右辺値参照とムーブコンストラクタの使い方 に詳しくまとめておきます。

クラスのポインターを扱う場合

クラスのポインターを持つ std::vector<> を作成した場合は、そこに格納したポインタが指すインスタンスの管理は自分で行う必要があります。
std::vector<CMyClass*> vector;
この場合は、push_back メソッドでの代入時のインスタンス確保と、pop_back メソッドでの破棄前に、必要なインスタンスの構築や破棄を適切に行う必要があります。
その辺りについては 可変長配列 std::vector でインスタンスのポインタを扱っている場合の削除方法 の方に整理しておきました。

□ イテレータを使って繰り返し処理を行う

std::vector<> ではイテレータを使って繰り返し処理を行えます。
このとき、イテレータの型は std::vector<>::iterator になり、これに整数を足したり引いたりすることで任意の場所へ簡単に移動することができるようになっています。
const 指定の std::vector<> の場合には、イテレータは std::vector<>::const_iterator を使います。
// イテレータを格納する変数を準備します。
std::vector<CMyClass>::iterator iterator;

// 最初のイテレータから、最後のイテレータになるまで、イテレータを一つずつ先に進めます。
for (iterator = vector.begin(); iterator != vector.end(); ++iterator)
{
// ここで、要素毎の操作を行います。
CMyClass* element = &(*iterator);

}
このように、イテレータを for 構文の中で使用して、最初の要素の先頭を使って初期化してから、最後を示すイテレータが現れるまでの間、イテレータをひとつひとつ進めることで、全ての要素を繰り返し処理しています。
このとき、end メソッドで取得できる最後のイテレータは、最後の要素の次の位置を示しているので、end メソッドによって得られたイテレータと一致したタイミングでループを抜けられます。

□ std::vector<> を並び替える

std::vector<> の配列を並び替えたい場合には <algorithm> に定義されているソート関数を使用します。
std::sort(vector.begin(), vector.end());
このようにすることで、引数に渡した std::vector<> の要素が昇順に並べ替えられます。
並び替えの判定には、要素間で < 演算子を使って判断されるようでした。また、この関数を使った場合は、値が一致した場合の並び順がどうなるかは決まっていないそうです。
単なる数値のように値が決まるような場合は、値が一致した場合の並び順は考えなくていいですけど、クラスのインスタンスのようにその他の情報も入ったオブジェクトとしてみた場合には、不都合になる場合があります。

値が一致したときに、それらの順番は元のものを維持したい場合は std::stable_sort 関数を使います。
std::stable_sort(vector.begin(), vector.end());
これにより、値が一致した場合の並び順が、元の順番と同じになります。
こちらも、並び替えの際には要素間で < 演算子を使って判断されます。

ちなみに、std::sort でも std::stable_sort でも、並び替えの処理中に、要素のコピーコンストラクタが呼ばれたり、代入演算子が実行されたりするようでした。
コピーの負荷が気になる場合は、push_back のときと同じように、扱うクラスにムーブコンストラクタを実装することで、負荷を最低限に抑えることができます。ムーブコンストラクタについては 右辺値参照とムーブコンストラクタの使い方 に詳しくまとめてあります。

扱う要素がクラスのポインタの場合には、要素の順番は変わっても破棄されることはないので、ポインタが指すインスタンスの確保や管理の心配は必要ありません。

□ std::vector<> を右辺値参照で移譲する

C++11 では、一時変数から保存用の変数に std::vector<> を移すようなときに、不必要なコピー処理を行わなくて済むようにするための右辺値参照という仕組みが規定されました。
それについては 右辺値参照とムーブコンストラクタの使い方 で詳しく紹介してありますが、std::vector<> も右辺値参照を引数に取る代入演算子とムーブコンストラクタが用意されているので、簡単に委譲することができます。
std::vecotr<CMyClass> newVector = std::move(oldVector);
このように、右辺を std::move 関数を使って明示的に右辺値参照とすることで、今回の例では oldVector に格納されている要素をそのまま newVector に移動させることができます。
このとき、右辺に指定した oldVector の内容は破壊されるので、これ以降は使えなくなります。

実際にこれを行ってみると、oldVector が持っている要素をひとつも破棄することなく、newVector に持たせる要素をひとつも新規生成することなく、std::vector<> の値を oldValue から newValue へ移行することができました。
特に std::vector<> でクラスを扱っている場合はコピーのコストが大きくなりがちなので、可能なところは積極的に使って行くのが良さそうです。
ちなみに、関数の戻り値で受け取ったクラスを変数に受けるときは、戻り値は自動的に右辺値参照として扱われるため、明示的に std::move 関数を呼び出さなくて大丈夫です。




2015年7月20日月曜日

OpenCV Mat と Eigen の相互変換

覚書として。参考サイトからの引用です。



cv::MatとEigen::Matrixの相互変換

cv::MatからEigen::Matrixへの変換
#include <iostream>
#include <Eigen/Core>
#include <opencv2/core/core.hpp>
#include <opencv2/core/eigen.hpp>

int
main(int argc, char *argv[])
{
  // 3x3, double の行列
  Eigen::Matrix3d eigen_mat;
  eigen_mat << 1.1, 1.2, 1.3,
               2.1, 2.2, 2.3,
               3.1, 3.2, 3.3;
  cv::Mat cv_mat;
  // convert from Eigen::Matrix to cv::Mat
  cv::eigen2cv(eigen_mat, cv_mat);

  std::cout << "cv_mat:\n" << cv_mat << std::endl << std::endl;
  std::cout << "eigen_mat:\n" << eigen_mat << std::endl << std::endl;
}
実行結果:
cv_mat:
[1.1, 1.2, 1.3;
  2.1, 2.2, 2.3;
  3.1, 3.2, 3.3]

eigen_mat:
1.1 1.2 1.3
2.1 2.2 2.3
3.1 3.2 3.3
Eigen::Matrixからcv::Matへの変換
#include <iostream>
#include <Eigen/Core>
#include <opencv2/core/core.hpp>
#include <opencv2/core/eigen.hpp>

int
main(int argc, char *argv[])
{
  // 3x3 の行列
  cv::Mat cv_mat = (cv::Mat_<double>(3,3) << 1, 2, 3, 4, 5, 6, 7, 8, 9);
  Eigen::Matrix<double, 3, 3> eigen_mat1; // コンパイル時に行列サイズが既知の場合
  // convert from cv::Mat to Eigen::Matrix
  cv::cv2eigen(cv_mat, eigen_mat1);

  std::cout << "cv_mat:\n" << cv_mat << std::endl << std::endl;
  std::cout << "eigen_mat1:\n" << eigen_mat1 << std::endl << std::endl;

  Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic> eigen_mat2; // 動的な行列
  // convert from cv::Mat to Eigen::Matrix
  cv::cv2eigen(cv_mat, eigen_mat2);
  std::cout << "eigen_mat2:\n" << eigen_mat2 << std::endl << std::endl;
}
実行結果:
cv_mat:
[1, 2, 3;
  4, 5, 6;
  7, 8, 9]

eigen_mat1:
1 2 3
4 5 6
7 8 9

eigen_mat2:
1 2 3
4 5 6
7 8 9

参考
cv::Matの基本処理
http://opencv.jp/cookbook/opencv_mat.html#cv-mateigen-matrix

2015年7月19日日曜日

米の山火事上空に撮影試みる無人機、衝突恐れヘリ消火中断

あかんですね。アメリカ・日本ともに法律が厳しくなりそう。

http://news.livedoor.com/article/detail/10367568/



米カリフォルニア州フェラン(CNN) 米カリフォルニア州南部のサンバーナディーノ郡で17日発生し、高速道路にも火勢が達し多数の乗用車などが燃える被害が出た山林火災で、地元の消防当局者は18日、至近距離からのビデオ撮影を試みる無人航空機(ドローン)5機が現場上空に出現したため、ヘリコプターによる消火活動が一時中断されたと報告した。
煙が立ちこめる視界不良の中で空中衝突などの不測の事態を回避するため、放水するヘリの出動は最大20分妨げられたとしている。
同郡の消防当局者はドローンの邪魔がなければ最大20分間の消火活動ができ、住民らへの危険が減じていたかもしれないと述べた。ドローン5機はその後、立ち去り、ヘリの消火作業は再開されていた。
カリフォルニア州では山林火災が発生した場合、現場上空にドローンが出現する例が最近目立ち始めている。消防当局は18日、山林火災の現場へ趣味でドローンを飛ばす行為を非難。米森林局の報道担当者もこの種の行為をやめるよう訴えた。
当局が今回のドローン騒ぎへの調査を開始するのかは不明。米連邦航空局(FAA)の報道担当者によると、FAAはサンバーナディーノ郡での山林火災を受け、現場周辺を一時的な飛行規制下に置いた。許可なければ無人機も飛ばせないことを意味していた。
危険な方法で無人機を飛ばしたことなどが判明した場合、1000~2万5000ドル(約12万4000~310万円)の民事上の罰金を科す可能性もあるとしている。
今回の山林火災は、ラスベカスとロサンゼルスを結ぶ州間道15号線にも達し、車両多数が燃え、運転手らが逃げ惑う混乱も呈していた。消防当局によると、火勢は18日になっても衰えず、焼失面積は約14平方キロに達し、鎮火したのはこのうちの5%のみとなっている。火災で死傷者発生の報告はない。

2015年7月18日土曜日

OpenCV 特徴点検出+マッチング

OpenCVにおけるマッチングは次のプロセスを経て実現されます。

特徴点抽出
  ↓
特徴量記述の計算の実行
  ↓
マッチング

少し突っ込めば、さらに双方向マッチングの実施+マッチング距離の閾値以下の除去があります。それを入れると、

特徴点抽出
  ↓
特徴量記述の計算の実行
  ↓
双方向マッチング
  ↓
マッチング距離の閾値以下の除去

それでは、一気にコードを見てみましょう。(ORBを使用)

// FeatureDetectorオブジェクトの生成
Ptr<FeatureDetector> detector = new ORB(80, 1.25f, 4, 7, 0, 2, 0, 7);

// DescriptionExtractorオブジェクトの生成
Ptr<DescriptorExtractor> extractor = DescriptorExtractor::create("ORB");

// DescriptorMatcherオブジェクトの生成
Ptr<DescriptorMatcher> matcher = DescriptorMatcher::create("BruteForce-Hamming");

// 特徴点情報を格納するための変数
vector<KeyPoint> keypoints1;
vector<KeyPoint> keypoints2;

// Extract feature points
detector->detect(img1, keypoints1);
detector->detect(img2, keypoints2);

// 画像の特徴情報を格納するための変数
Mat descriptor1;
Mat descriptor2;

// compute the feature discriptor
extractor->compute(img1, keypoints1, descriptor1);
extractor->compute(img2, keypoints2, descriptor2);

// 特徴点のマッチング情報を格納する変数
vector<DMatch> dmatch;

// compute matching
matcher->match(descriptor1, descriptor2, dmatch);

// Extract matching which has high matching rate
int count = dmatch.size();
int matchCount = 0;

double min_dist = DBL_MAX;
for (int i = 0; i < (int)dmatch.size(); i++) { // Acquire the minimum distance
double dist = dmatch[i].distance;

if (dist < min_dist) {
min_dist = dist;
}
}
if (min_dist < 1.0)
{
min_dist = 1.0;
}

double threshold = 30; // Fixed value is better than value times min_dist

for (int i = 0; i < (int)dmatch.size(); i++) {
if (dmatch[i].distance < threshold) {
matchCount++;

dmatchClose.push_back(dmatch[i]);
}
//std::cout << dmatch[i].distance << "\t" << min_dist << "\t" << threshold << std::endl;
}




参考:
OpenCVで画像の特徴抽出・マッチングを行う
http://whoopsidaisies.hatenablog.com/entry/2013/12/07/135810
OpenCVでのORBによる特徴点抽出とマッチング(その1)基本的な使い方
http://independence-sys.net/main/?p=2632

OpenGLでGLUTを使って2つのウィンドウを制御する①
http://blog.goo.ne.jp/omty/e/c8a5aee50e3918a1ad669472ec25e462
サブウィンドウ
http://seesaawiki.jp/w/mikk_ni3_92/d/%c8%af%c5%b8%ca%d403

PixelBufferObject
http://seesaawiki.jp/w/mikk_ni3_92/d/PixelBufferObject

Visual Studioでデバッグ時にOpenCVのMat等の画像を表示できるプラグインが便利
http://whoopsidaisies.hatenablog.com/entry/2014/11/28/175637



OpenCV イメージデータ(Mat)のコピー(クローン)

OpenCVのイメージデータをコピーしようとしたら、できない?なんでと思って結構悩んでしまいました。よく考えてみると当たり前なのですが。

そもそも、OpenCV のMat型というのものは、次のようになっている(結構略)。
サイズとかデータの型の情報:実体
画像のデータ:ポインタ

はぅ!!画像のデータも実体で入っているものとばかり思い込んでいた。

ということで本題。

void func(cv::Mat getImage){
    static cv::Mat imageInFunc;
    imageInFunc=getImage;
    return;
}

ってな感じで書いても、ポインタを代入しているだけなのでデータは保存されていない。元のデータが書き変われば、imageInFuncを表示させてみても、データは書き換わり続ける。

で、どうすればいいかというと。cloneってものがあるのね。

void func(cv::Mat getImage){
    static cv::Mat imageInFunc;
    imageInFunc=getImage.clone;
    return;
}

で解決。あほなことに2時間ほど費やしてしまった。

参考:
OpenCVでまずやること『Mat』
知っておきたいMatのゴリ知識
cv::Matの基本処理

2015年7月8日水曜日

人工知能はコメント欄を健全化できるか?

これすごいな。メールのSPAM検出の開発並にすごいのでは

http://blogos.com/article/121140/

Yahoo!ニュースのコメント欄は、荒れることが多い。誹謗中傷、特定の国に対する侮辱的な書き込みなど、ドロドロしたネット民のダークサイドに満ちている。
それは欲求不満の発露であったり、匿名であることで理性や良識が外れてしまうからだろう。リアルで面と向かって、同じようなことがいえる人が、どれほどいるのかは疑問だ。
そんなコメント欄を、人工知能で管理できるサービスが登場した。

人工知能で“荒れない”コメント欄--「議論の場」醸成めざしメディアに無料提供 - CNET Japan
人工知能や自然言語処理を活用したサービスを提供するクーロンは7月7日、機械学習や自然言語処理、行動分析にもとづく独自の人工知能を搭載した、読者が健全な議論を交わすための“荒れない”コメントシステム「QuACS(クアックス)」をウェブメディア向けに提供開始した。人工知能を活用したコメントシステムは世界で初めてだという。

(中略)

「単語」ではなく、同一コメント内の前文の内容、他者のコメントへのレス(会話)の流れなど、文章や文脈を判定しているのが特徴。たとえば、麻薬や覚醒剤の購入を促すような違法取り引きに関するコメントは非表示になるが、「麻薬」「覚醒剤」といった単語が文中で適切に使われる場合には表示される可能性が高い。

若者言葉やネットスラングなど、時代によって新たな言葉が生まれたり言葉の使い方が変わったりするが、機械学習により使えば使うほど精度が高まり適切な状態を保てるという。なお、「何かを表現したくてできなかったことは怒りに直結する」(同社代表取締役社長の佐藤由太氏)との考えから、たとえ非表示になったコメントでも、投稿者本人の画面には表示し、あたかも投稿できたかのように見せかける。
学習機能がどれだけ機能するかだね。ネットスラングは次々に生み出されているし、一部を伏せ字にしたりする手法もあるし、たとえ話で連想させたりもする。
ある意味、人工知能とネット民の知恵比べかもしれない。
「QuACS(クアックス)破り」なんてのが、流行ったりして。

“荒れたコメント欄”は、ネットの一面とはいえる。
そこに露出された過激な言葉は、そういう人間が身近にもいるという警告でもある。世の中は、ダークサイドと隣り合わせなのだと。
過激なコメントを書く人が、リアルな日常の会社や学校でも過激な発言や行動をしているとは限らず、善良な市民の仮面を被っている場合もあるだろう。
日常の人格と、ネット上でのもうひとりの人格という二面性……多重人格を顕現させるのが、ネットの特性でもある。

QuACS(クアックス)がどれほど有効かを実験するには、Yahoo!ニュースのコメント欄が最適だと思うよ(^_^)


ディープラーニングを小学生でも使えるようにしてみる

http://wirelesswire.jp/2015/06/32460/

この一ヶ月で、ディープラーニングが急激に使いやすくなってきています。
Google傘下のディープラーニング研究グループDeep Mindでインターンをしているスタンフォード大学の学生はこんな台詞をツイートしています。
スクリーンショット 2015-06-30 5.57.49
「ディープラーニングに関して、新しくクールな論文が発表される速度は、それを読める速度より速い」
それこそ毎日のようにディープラーニングに関する何らかの新しい話題が出てきます。
それくらい、ディープラーニングは盛り上がっているのです。
「人工知能は人間を超えるか」を記した東京大学の松尾豊先生によれば、「ディープラーニングはWebの発明に匹敵するほどの発明」とのことでしたが、なぜそれほどまでに世界中の研究者が夢中になるのか、筆者も自分がディープラーニングの世界にどっぷりと漬かってみることによつてようやく理解できてきました。
そしてその興味はいまや確信に変わりつつあります。
確かにこれはWebの発明、いや、ひょっとするとコンピュータそのものの発明に匹敵するくらいのインパクトを生み出すのではないかと。
この数十年、人工知能の研究は「機械学習」と呼び替えられ、迷惑メールフィルタやAmazonなどのオススメ商品の推薦といった分野にもっぱら使われてきました。
しかしここに来て突然、ディープラーニングは脚光を浴びることになります。
理由はいくつかあります。
機械学習を理解するには、相当な数学的センスと知識が必要でした。
これは今でもあまり変わっていません。
昨日、ふと秋葉原の書泉ブックタワーにでかけていって、ディープラーニングに関して言及した書籍を探してみると、まず技術書の中で、ディープラーニングを専門的に扱っている本は一冊も見つかりませんでした。
Amazonで検索すると、辛うじて「深層学習(機械学習プロフェッショナルシリーズ)」という本が引っかかります。
そして他の「機械学習」はどれもディープラーニング以前の時代に書かれたものばかりなのです。
こういう光景には、見覚えがあります。
それは1994年の書泉ブックタワーでした。

大学に入学したばかりの私は、当時3Dプログラミングにどっぷりと浸かっていました。
その頃はまだプレイステーションすらも発売されておらず、リアルタイム3Dを駆使したゲームは、片手で数えるほどしかない、そんな時代です。
新潟の片田舎から上京してきた私は、東京の秋葉原の書店ならきっとたくさんの本があるのだろうと期待に胸を膨らませながら書泉ブックタワーに向かいました。
その頃はAmazonのような便利なしくみがなかったので、本を探したければ、足で探すしかなかったのです。この時代はGoogleはおろかYahooすらありません(Konishikiというコードネームでスタンフォード大学のジェリー・ヤンとデビッド・ファイロがYahooの原型を作っていた頃です)。
ところが東京の書店に来ても、リアルタイムの3D処理について言及している書籍はほとんどありませんでした。
肩を落とした私は、ふと、見慣れない書籍がずらりと並ぶコーナーを訪れます。
そこは輸入書のコーナーでした。
するとそこには、リアルタイム3D処理に関する書籍がずらりと並んでいたのです。
私は衝撃を受けました。
日本語の書架をみれば、当時はリアルタイム3D処理に関して日本人の誰もが興味を持っていないことは明らかでした。書籍を書けるほどの知識も蓄積されておらず、学生が書く連載が1,2本、雑誌に載っているだけでした。
一方、海外では、信じられないほど多くの人がリアルタイム3D処理に熱中し、研究し、日本の何年も先を行っていたのです。

この時、私は時代の風を感じました。
今自分が見つめるこの場所が、紛れも無いフロンティアであることを知ったのです。
その年の暮れにプレイステーションが発売され、当初の予想を覆すほどの大ヒットとなりました。

私はゲーム業界に身を投じ、3年後にはリアルタイム3D処理に特化した書籍を執筆し、ちょっとしたベストセラーとなって、それがきっかけで在学中に米Microsoft corpの仕事を引き受けることになります。今やリアルタイム3D処理は、スマートフォンのブラウザーですら当たり前のように用いられています。あの頃、日本人の技術者、プロのエンジニアと呼ばれる人たちがまるごと無視していた領域にこそ、本当のフロンティアがあったのです。
この頃はまさに時代の潮目であり、それを最初に感じたのはあの書泉での体験でした。

ディープラーニングに関して、私は今同じような感覚を感じています。
大半のビジネスマンは未だディープラーニングをバズワードの一種と捉えています。確かにこれまで、似た考え話にたくさんの肩透かしを食らってきました。
文字認識、音声認識、といった分野は今も着々と認識率を上げていますが、それでも100%とは程遠いといった印象です。Siriに一発で自分の言葉を意思どおりに入力できる人はまだ少ないでしょう。
要するに機械学習の一番身近な成果は、Siriの音声認識やWindowのタブレットに搭載されている文字認識であり、それが100%とは程遠いことを人々は体験的に知っているからです。
そしてディープラーニングによる画像分類も、確かに認識率だけ見れば、まだ良くて96%程度と、音声認識が99.8%くらいまで迫っているのに比較するとまだまだという印象を拭えません。

しかし決定的な違いは、ディープラーニングによる分類には汎用性があるのに対し、音声認識や文字認識といった分野は汎用性が狭いことです。文字認識なら文字認識に特化したノウハウが必要ですし、音声認識なら音声認識に特化したノウハウが必要です。
ディープラーニングはそのどちらにも応用可能であるだけでなく、もっと他のことにも応用が期待されます。

一例を挙げれば、例えばPhotoshopなどで使われる画像フィルタの入力画像と出力画像のペアをディープラーニング・ニューラルネットワークに大量に学習させると、そのニューラルネットワークがそれまで見たことがない画像を入力しても、Photoshopのフィルタと全く同じように画像を処理するようになります。これは、単純なアルゴリズムなら、ニューラルネットワークが学習・模倣できてしまうことを意味します。

文字認識や音声認識はクリティカルな使用法を想定するから問題が起きるのであって、クリティカルでない使用法を想定すれば、もっと自由に使えるようになります。

さらにプリファード・ネットワーク(PFN)のオープンソースフレームワーク、Chainerの出現です。

Chainerはそれまで機械学習の専門的知識を持った人のみを対象としていた学習環境に比べて、ディープラーニングによるニューラルネットワークをより簡単に素早く扱えるように設計された新世代のフレームワークです。
Chainerは、それまでディープラーニングの主流フレームワークのひとつであった、カリフォルニア大学バークレー校を中心にGoogle、NVIDIAで使用されているCaffeというフレームワークの1/10の記述量で画像分類のニューラルネットワークを記述することができるというシンプルさがウリです。

さらに、jetpac Deep Belief SDKというオープンソースのライブラリを使用すると、ディープラーニングされた画像認識エンジンをだれでも簡単に扱うことができるようになります。
このSDKでは、「jpcnn_classify_image」という関数をひとつ呼ぶだけで、画像を認識し、その画像に含まれるものをテキストで返します。
いわば画像を入力するとテキストが返ってくるブラックボックスなわけですが、これをツールとしてスマートフォンアプリやRaspberry Piに組み込むことで様々な分野へ応用できるというわけです。
ここまで簡単になると、もはやディープラーニングは研究者の手を離れて、エンドユーザーに近い人々が何か新しい利用法を考える段階と言えます。

そこで試しに、弊社で開発中のiOS向けプログラミング開発環境MOONBlock DXにこの機能を組み込んでみました。

MOONBlock DXはスマートフォンだけで独立してアプリを開発することができる開発環境ですが、例えば画像認識するプログラムはこのように書くことが出来ます。
スクリーンショット 2015-06-30 6.34.43
これは、ボタンが押された時に写真を撮影し、「画像認識」というブロックに「写真」を渡すと、認識された画像の候補をアラートボックスに表示するという簡単なサンプルです。ものの数十秒で作ることが出来ます。
早速実行してみましょう。
スクリーンショット 2015-06-30 6.34.49
起動すると、シンプルに撮影ボタンだけが表示されます。
これをタップするとカメラが起動します。
スクリーンショット 2015-06-30 6.34.59
試しに会社の机にあったディキシーカップを撮影してみます。
すると、数秒も待たずして内蔵されたニューラルネットワークから推定結果が返ってきます。
スクリーンショット 2015-06-30 6.35.04
このDeep Belief SDKに内蔵されたニューラルネットワークは当然海外で教育・訓練を受けたものなので、日本人がイメージするようなものとは違うものを教育されています。それでもディキシーカップを認識したのは優秀だと思います。
他のものも試してみましょう。
スクリーンショット 2015-06-30 6.35.11
筆者のMicrosoft Surface3はどうでしょうか。
スクリーンショット 2015-06-30 6.35.16
とにかくコンピュータと認識されているようです。
このMOONBlock DXに搭載されたニューラルネットワークは、およそ1000種類の画像を判別します。
ですが日本向けにニューラルネットワークを教育しなおしたほうが良い結果が得られそうです。
たとえば郵便ポスト一つとっても各国で形が異なります。
今後はニューラルネットワークのローカライズが重要になってくるでしょう。
しかしここまで来ると、名実ともに、ニューラルネットワークを手軽に使える時代がやってきました。
画像分類タスクはひとつの例に過ぎませんが、今後もディープラーニングを応用した様々なタスクが現れ、そしてこんなふうに簡単に使えるようになっていくでしょう。

こういう時代は控えめに言って、驚くべき時代です。
これまで、プログラミングといえば、プログラマーの独擅場でした。
プログラマーは全てのロジックを理解し、積み上げ、デバッグして納品する、それが当たり前の作り方でした。
これからはそのワークフローが少し変化するかもしれません。
プログラマーがプログラムを組むのはしばらくは変わりませんが、それと並行してプログラマーではない人、たとえば仮にそれを「AIトレーナー」と呼ぶことにしますが、彼または彼女が、チームリーダーの指示にしたがって人工知能を教育するための学習データを収集・整備し、次々と人工知能を教育(トレーニング)していきます。

実際、弊社ではディープラーニング用の学習画像データ・セットを集めているのはアルバイトや事務、秘書など、それまで全くコンピュータやAIに対する専門性を持っていなかった人々です。そして一口に「学習画像データセット」と言っても、無数の可能性があります。インターネットで画像を拾ってくるだけでは、本当の意味での現実世界の認知は不可能です。

つまり学習画像データ・セットを集めるには文字通り教育的なノウハウが必要で、そうしたノウハウはむしろAIの専門家よりも人間の教育の専門家の方がよほど効果的に学習データを集めることができるようになるかもしれません。
それはそれまで存在しなかったタイプのノウハウです。この学習データセットを収集するというのも立派にひとつの仕事として成り立っていくでしょう。

プログラムを納品する段階でトレーニングしたAIと、プログラム本体を一緒に納品し、さらに納品後も次々とAIのトレーニングを続け、精度を上げていきます。
こうした従来型のプログラミングパラダイムと、AIという新しいパラダイムのハイブリッドが、近い将来のソフトウェア像になるのではないでしょうか。

もはやディープラーニングは、対岸の火事ではありません。
全てを飲み込む可能性を秘めた情報産業全体のブレークスルーとなる可能性があります。

IoTのセンサーネットワークやゲーム、映画、検索エンジン、Eコマースなどなど、ありとあらゆる分野にAIが応用されていくでしょう。それはもう夢物語ではなくなってきています。

シンギュラリティ、技術的特異点と呼ばれる世界では、AIを使いこなした人だけが活躍していくことになるでしょう。
そしてAIを使いこなすということは、すなわちAIをプログラミングするということと、AIを教育・訓練することの2つです。
そしてAIを利用したプログラムは、先ほど示したようにとても簡単にできるようになります。

あとはこれをどのように使っていくか、イマジネーションの勝負です。

明日から東京ビッグサイトで開催されるプロダクションEXPO内では、弊社、株式会社ユビキタスエンターテインメント(UEI)の展示ブースでもディープラーニングに関するささやかな参考出品を行う予定です。