2016年12月21日水曜日

ヤフー最悪

これは酷すぎる。あまりにひどすぎるので会社名を実名で書かせていただきます。

身内が亡くなったので、いろいろな解約手続きをしたのですが、どうしても解約できないものがある。それがYahooのサービスの解約。

毎月ヤフーウォレットという会社から請求書が送られてくるのですが、何に対する請求かまったくわからない。
それだけではない。電話番号などが全く書かれていなく、どこで解約手続きをしないといけないのかが全く分からない。

請求書にはQRコードが書いてあるので、どうやらそこにアクセスすればよいんだなと思いそこにアクセスした。

問い合わせのページに行くと。わけのわからん項目がたくさんあり、どこに問い合わせてよいのか全くわからない。

酷すぎる。なんなんだヤフー。だからみんな使わねーんだよ。


いくつか入力するとやっと問い合わせのページが出てきた。

解約したい旨を書き込むと、まったく返事がこない。
一日待ってもこない。
ふざけんなよ。どんなに遅くても24時間以内に返信しろよ。
お前のところのオークションにもよく書かれているだろ。
24時間以内に応答できない人は非常に悪いの評価つけられちゃうんだよ。

ヤフーのサポート対応非常に悪いです。

さらに待ち続けます。

メールが返ってきました。
ここのURLにアクセスして解約手続きをしろと。

ふざけんな、客を散々待たせておいて、また同じようなことを入力しろと。
亡くなった人はお前のところと契約したかも知らないけど、僕はお前のところと契約した覚えはないし、お前に善意で知らせてやってんだよ。
なんでこっちがやんなきゃいけないんだ。
いったい何様のつもりなんだ。

怒りMAX。ふざけんのもいい加減にしろよ!酷すぎる。


そもそもサービスというものはお客様に価値のあるものを提供し、お客様の要望を叶えその対価としてお金をいただくものであり、客の言うことを全く理解しないどころかお客様がわざわざサービスにあわせるものではないはずです。
そんなふざけたサービスには金払わねーよ。とっととやめてやる。

とっととやめたいのに、とっととやめられないんだよ。

怒りを抑えながら、しょうがないのでもう一度同じようなことを入力。
以下の書類が必要ですと書いてある。
そんな書類ねーよ。

書類がない場合どうしたらよいのでしょうか?
先に進めないじゃねーかよ。
メールにはこのメールに返信しても届きませんと書いてある。
また問い合わせをするのかよ。

しょうがなく、とりあえず全く関係ない書類を適当なPDFにして送信ボタンを押す。
画面にはエラーと表示される。
セッションが切れました。時間がたっていますだって。
ふざけんなよ5分で入力しただろうが。
しかももう一度最初から入力してくださいと。

なんでもう一度入力しないといけないんだよ。
怒りを抑えに抑えてもう一度入力します。
また同じようなエラー

もう怒った。

お前のところ以外はすべて何の問題のなくすべて全部解約できたのに、解約できないのはお前のところのサービスと解約の電話を一時間くらい待たせたヤフーBBだけだ。

ヤフー、お前のところのサポートは最悪だ。
もうヤフーのサービスは一生使わない。メルカリに変えるからな。


もう、請求書も何の請求かわからない、解約しようとしても解約できない。
これ詐欺と全く変わらないじゃないか。

続く。




2016年12月7日水曜日

ZMQってなに?

おじさん忙しくてなかなかブログを更新できませんでした。
久々の更新。

車の自動運転のソースコード公開されたんだ
http://gigazine.net/news/20161206-commaai-open-source/

ソースコードでも見てみようかなぁ、
https://github.com/commaai

という軽い気持ちでソースコードを眺めていたら、自動運転ライブラリはZMQというライブラリを介していろんなモジュールが通信していました。

ZMQってなんなんでしょうね?
ということで今日はZMQについて勉強してみました。
ZMQとはどうやら0MQ(ZeroMessageQueue)の略らしいです。
http://zeromq.org/

どうやらIPC、RPCなどプロセス間通信とか分散処理をするライブラリみたいですね。
最近ではこのようなライブラリをつかってPythonとC++などの言語間のブリッジやWindowsとAndroid間など異なるプラットフォーム間の通信を行うんですね。
たしかに、こうすればあらゆるプラットフォームのあらゆる言語で開発できるし、C++のPythonラッパーを作るより便利だよね。

ディープラーニングとか機械学習はPythonで書かれたり、C++で書かれたりしているのでこのようなライブラリを使うんですね。


この辺日本人がなかなか理解できない苦手なところだよね。
全然使い方とか解説のブログないじゃんと思っていたら、ZMQガイドブックの翻訳版がありました。
http://www.cuspy.org/diary/2015-05-07-zmq/


ということで、ZMQのサーバとクライアントを作ってみました。
このライブラリ、軽量でかつ簡単に分散処理とか異なる言語間通信できるので、本当に面白いですね。


------------------------------------------------
// Hello World サーバー
#include <zmq.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <assert.h>
int main (void)
{
 // クライアントと通信を行うソケット
 void *context = zmq_ctx_new ();
 void *responder = zmq_socket (context, ZMQ_REP);
 int rc = zmq_bind (responder, "tcp://*:5555");
 assert (rc == 0);
 while (1) {
  char buffer [10];
  zmq_recv (responder, buffer, 10, 0);
  printf ("Received Hello\n");
  sleep (1); // 何らかの処理
  zmq_send (responder, "World", 5, 0);
 }
 return 0;
}

------------------------------------------------

------------------------------------------------
// Hello World クライアント
#include <zmq.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>

int main (void)
{
 printf ("Connecting to hello world server...\n");
 void *context = zmq_ctx_new ();
 void *requester = zmq_socket (context, ZMQ_REQ);
 zmq_connect (requester, "tcp://localhost:5555");

 int request_nbr;
 for (request_nbr = 0; request_nbr != 10; request_nbr++)
 {
  char buffer [10];
  printf ("Sending Hello %d...\n", request_nbr);
  zmq_send (requester, "Hello", 5, 0);
  zmq_recv (requester, buffer, 10, 0);
  printf ("Received World %d\n", request_nbr);
 }
 zmq_close (requester);
 zmq_ctx_destroy (context);
 return 0;
}
------------------------------------------------







2016年10月17日月曜日

車の自動運転ってどうやってやるんだろ

おじさん、五歳の娘に「アニアは夜になると動くんだよ」と言ったところ、うちの娘はおじさんが寝ている間にアニアをリビングに移動させ、おじさんが起きた時に「アニアがほんとうに動いてた」と言ってました。五歳児の人をまねる能力って本当にすごいです。おじさんがやろうとしていることを想像して先に自分がやるんですね。



これとくらべると最近話題の機械学習も人間のやることをまねしているだけなんですが、猿まねというか五歳児以下ですね。

おじさん、五歳児に嘘を見破られてくやしいので、頭をよくするために車の自動運転のソフトウェア技術を勉強してみました。
しばらく勝手に車の自動運転の技術について勝手に解説しようと思っています。

車の自動運転では、信号とか歩行者や対向車などを判別しないといけないんですが、まずは歩行者などを判別するところまで。

映像の中から物体を識別するには

1. 物体候補領域抽出
2. 画像認識

という二段階が必要らしいです。

映像の中からなんか人とか物がありそうな領域を抽出して、その領域一つ一つに対して画像認識をして、画像が何かを判別するんですね。

2に関しては前回のブログで比較的最先端なものをやったので今回はパス。
今回は1の部分をやってみたいと思います。


物体候補領域抽出アルゴリズムはBING(Binarized Normed Gradients for Objectness Estimation)とかSelectiveSearchとかいうものがあるそうです。

この部分、結構処理が重たいらしく、いろいろな企業や研究機関で高速物体候補領域
抽出の研究がされているようです。

この辺のライブラリ、Pythonで書かれていることが多いのですが、将来商品に入れることを考えると、PCのフルPythonでしか動かないライブラリを使うのではなくて、やはりC++でやりたいですね。

ということで今回はDlibというライブラリでSelectiveSearchをやってみたいと思います。最新の19.2はVisualStudio2015以上でしか動かないので、僕は18.16を取ってきました。

このライブラリも面白いですね。allというディレクトリにあるsource.cppというファイルを一つコンパイルするだけで、ライブラリが使えます。


Selective SearchのC++のソースコードはここにあるのでそのまま取ってきます。
http://nonbiri-tereka.hatenablog.com/entry/2016/07/15/110000


先ほどのファイルと上記のメイン関数のファイル、二つをコンパイルすると実験ができちゃうんです。この10分くらいでサクッとビルドできて実験できちゃう感じがほんと便利。ライブラリの作者の頭の良さが伝わってきます。


ということで、アニアの画像を物体候補領域抽出してみました。


僕のPCでは20秒くらいかかる・・・・。
でもなんか正しいっぽいいじゃん。

2016年9月16日金曜日

TensorFlowのライブラリをC++から使って画像認識してみた

TensorFlowのライブラリをC++から使うにはどうしたらよいのか。


https://www.tensorflow.org/versions/r0.7/tutorials/image_recognition/index.html

上記のドキュメントにはC++のサンプルのビルド方法は書かれているのですが、bazelを使わない自分で作ったアプリのビルド方法はまったくGoogleno公式ドキュメントに書かれていません。

bazelを使わないで、自分のビルドシステムでmakeしたいときはどうすればいいんだろ。
なので調べてみました。



1. Tensorflowをソースからビルドする


まず、tensorflowのsoを作りたいのですが、soを作るにはTesnsorflowをソースからビルドする環境が必要です。以下のサイトをもとに頑張ってTensorflowをソースからビルドします。
https://www.tensorflow.org/versions/r0.10/get_started/os_setup.html#installing-from-sources


2. Tensorflowのsoを作る。

libtensorflow*.soをビルドしたいのですが、ビルド方法が公式ドキュメントには載ってないですよね。bazelのビルドファイルであるtensorflow/BUILDを読むと、なんとなくsoのビルド方法がわかります。

bazel build -c opt //tensorflow:libtensorflow.so
bazel build -c opt //tensorflow:libtensorflow_c.so
bazel build -c opt //tensorflow:libtensorflow_cc.so

上記のように入力するとsoが出来上がります。
bazel/binのフォルダに出来上がるようです。


3. includeファイルを集めてとってくる

これがめんどくさいのですが、以下の四つのフォルダーのヘッダーファイルを
取ってくるかビルド時にパスで指定します。
bazel-tensorflowはシンボリックリンクとかがあるので取ってくるのがめんどくさいです。

bazel-tensorflow
bazel-genfiles
~/.cache/bazel/_bazelユーザー名 /長い文字列/external/eigen_archives
~/.cache/bazel/_bazelユーザー名 /長い文字列/external/protobuf

僕はこれらのファイルをincludeディレクトリを作り、全部入れました。



4. ビルドする

試しに、Tensorflowに入っている画像認識のサンプルをビルドします。
3で作ったファイルをWindowsに持ってきて、前回の記事のbashでビルドしてみたいと思います。
tensorflow/examples/label_image/main.ccをビルドするには以下のように入力

$ g++ main.cc -std=c++11 -Iinclude/eigen_archive -I include/protobuf/src -Iinclude -L. -ltensorflow_cc -Wl,-rpath=.


びるどできたー



5. 学習データを取ってくる

いつものように以下のサイトから学習アルゴリズムと学習結果を取ってきます。

https://storage.googleapis.com/download.tensorflow.org/models/inception_dec_2015.zip



6. 実行させる

$ ./a.out

military uniform (866): 0.647298
suit (794): 0.0477196
academic gown (896): 0.0232406
bow tie (817): 0.0157355
bolo tie (940): 0.0145024


おー、結果が出た。


この写真は軍の制服らしい。
確かにスーツにも見えるね。




これでsoとヘッダーファイルさえとってこれれば、どんなビルドシステムでもコンパイルすることができます。

Windowsのbash on ubuntu on Windsowsはjavaがまだ安定して動かないため、bazelも動かないのですが、ubuntuでビルドしたsoとヘッダーファイルを取ってくれば、Windows上でTensorflowのC++開発も行うこともできます。

Pythonでディープラーニングをすることが流行っていますが、スマホや組み込みではフルスペックのPythonは動きません。
しかし、Tensorflowは演算部分がC++で書かれているので、Tensorflowでグラフを作れば、スマホや組み込みですぐにその学習結果を利用することができるます。
ディープラーニングを使ったアプリを簡単に商品化できるだけでなく、大変時間のかかる学習もGPUを使ったサーバで簡単に分散処理できます。
Tensorflowって深層学習のアプリを実際に商品化する時に発生するたくさんの問題をすべて解決してくれます。
本当によく考えられています。すごいです。




2016年9月8日木曜日

コンピュータにゲームをさせてみた

「コンピュータにゲームをさせてみる」のいよいよクライマックスです。
今日はコンピュータにゲームを学習させたり、その結果でコンピュータにゲームを
させます。

コンピュータにゲームをさせるにはたくさんのライブラリをいれないといけないので、設定がめんどくさくてなかなかハードルが高いです。なので順を追って説明していきます。

ソースコードは学習することも含めて367行しかないのに。

1. OpenAI GYMをインストール

まずOpenAI GYMをWindowsで動くようにします。
Windows用って書いてますが、実はLinuxでもやることは全部同じです。
なので同じやり方でLinuxでもコンピュータにゲームをさせることができます。

以下のサイトを参考にどんどんいろんなソフトを入れていきます。

Windows 10のLinux/Ubuntu互換環境でbashを使う
http://www.atmarkit.co.jp/ait/articles/1608/08/news039.html

Windows上でOpenAI/gymを実行してみる
http://qiita.com/onelittlenightmusic/items/11eefde5ddad57fd0727



2. いろいろインストール

上記で一応OpanAI GYMだけは動くようになったのですが、Atariのゲームや、機械学習のライブラリのTensorflowとKerasがまだ入っていません。なのでインストールします。


apt-get install python-dev
apt-get install python-opengl
apt-get install python-skimage
apt-get install cmake
apt-get install zlib1g-dev
apt-get install tensorflow
apt-get install git

pip install gym
pip install gym[atari]
pip install keras
export TF_BINARY_URL=https://storage.googleapis.com/tensorflow/linux/cpu/tensorflow-0.10.0-cp27-none-linux_x86_64.whl
pip install $TF_BINARY_URL



こんな感じでどんどんインストールします。



3. Kerasのバックエンドを設定

次にKerasはデフォルトではtheanoを使うように設定されているのでtensorflowを使うように設定を変更します。

KerasをTensorFlowバックエンドで試してみた
http://tjo.hatenablog.com/entry/2016/06/09/190000

上記のサイトを参考に
~/.keras/keras.json
を変更します。

変更前
{"epsilon": 1e-07, "floatx": "float32", "backend": "theano"}

変更後
{"epsilon": 1e-07, "floatx": "float32", "backend": "tensorflow"}



4. Atariのゲームをするプログラムと学習結果をとってくる

ゲームをするプログラムと学習データを取ってきます。

DQNをKerasとTensorFlowとOpenAI Gymで実装する
https://elix-tech.github.io/ja/2016/06/29/dqn-ja.html

上記のサイトにコードとデータがあるので取ってきます。

サイトでは
python dqn.py
と入力すればコンピュータがゲームをしてくれると書いてありますが、実際にはデフォルトではゲームの学習を始めてしまいます。
学習はGPUがあるPCでも非常に時間がかかるので今回は省略します。



5. ソースを一部直す

dqn.pyのソースを以下のように変更し、学習結果をテストするモードにします。
学習結果をテストするくらいならばGPUが載っていない普通のPCでもできます。

変更前
LOAD_NETWORK = False
TRAIN = True


変更後
LOAD_NETWORK = True
TRAIN = False


6. コンピュータにゲームをさせる。

python dqn.py
と入力します。



やったーーーーーー。
きちんとゲーム画面が表示されて生意気にもゲームしてるじゃん。
長かったけどついに僕のWindowsマシンでコンピュータにゲームをさせることができました。
冒頭にも書きましたが、ゲームを学習したりプレーする部分のソースコードは367行しかありません。たったそれだけでゲームすることができちゃうんです。深層強化学習って面白いですね。




2016年9月7日水曜日

WindowsでLinuxのアプリを動かしてみた

Windows10をアップデートするとWindows Subsystem for Linuxという機能が使えるようになるらしいので、8月にリリースされたばかりのWindows10 Anniversary Updateを自分のパソコンにいれてみました。

Windows Subsystem for Linux とはどういう機能かというと、WindowsでLinuxの実行形式のファイルをそのまま実行できる機能です。

すごいですね、LinuxのアプリがそのままWindowsで実行できてしまうのです。
Linuxのgccとかpythonとかapt-getとかのバイナリーがそのままみんなWindowsで動くのです。

ということで、Windows Subsystem for Linuxを動かして、gccやxeyesをapt-getでいれて、本当に動くのか実験してみました。


おーすげー。本当に動く。

この機能は、Linuxで数値計算や深層学習をやっているデータサイエンティストや研究者にとっては大変便利な機能でLinuxしかサポートしていないさまざまなソフトをWindowsで動かすことができます。

これでWindowsでOpenAI GYMを動かして、「ゲームをするコンピュータ」を動かすことができる!
ということで次回はWindowsでOpenAI GYMを動かして「ゲームをするコンピュータ」が本当にゲームをするのか見てみたいと思います。

2016年9月6日火曜日

コンピュータをしゃべらせてみた。

忙しくてしばらく放置していましたが、しゃべるロボットを作ろうとしているおじさん。やっとしゃべる部分を作ることができたので、今日はロボットをどうやってしゃべらすかについて書きます。
音声認識の方はいろいろな書籍がそろっているのですが、音声合成の本ってあまり見かけません。不思議です。まぁいいや。

オープンソースのOpenJTalkというライブラリを使うとコンピュータに日本語をしゃべらすことができます。正確に言うとテキストファイルを音声に変えることができます。

http://open-jtalk.sourceforge.net/

しかし、使っておいて文句を言うのも何なんですが、このページとてもわかりずらい。
ドキュメントをよーくよむとどうやら以下のものが必要らしいです。

(1) Open?JTalk本体
open_jtalk-1.xx

(2) 解析用辞書ファイル
open_jtalk_dic-1.xx

(3) 音声合成エンジン
hts_engine_API-1.xx

(4) 音響モデルデータ
hts_voice_nitech_jp_atr503_m001-1.xx

(1)、(2)、(4)は上記のサイトから入手可能なようですが、(3)は以下の別サイトから取ってくるようです。

http://hts-engine.sourceforge.net/


この四つのファイルを全部取ってきて、ビルドします。
Windowsはサポート対象に含まれていないようなんですが、Windowsでも問題なくビルドできるようです。

ビルド方法も通常のLinuxビルドのようにConfigureをかけるほうもあるのですが、使う漢字コードとライブラリのバージョンの#defineを設定すればどのコンパイラでもビルドできるようです。

ぼくはソースを全部同じフォルダにぶち込んで、適当に#defineを設定してVisualStudio2015でビルドしました。

フォルダがたくさんあって大変なんですが、全部同じフォルダにぶち込んでコンパイルすれば意外と簡単にビルドできます。

input.txtに「こんにちは、ばーか」などと打ち込んで、以下のコマンドを実行すれば話すみたいです。

open_jtalk -m nitech_jp_atr503_m001.htsvoice -x dic -ow output.wav input.txt


喋る部分って意外と簡単。
ラズベリーパイなんかでも余裕で動きます。

これで「音声からテキスト」-->「いろいろ処理」--->「テキストから音声」ができるので、人がしゃべったことを理解して何かを処理して結果をしゃべるロボットを作ることができます。

最近なんでもオープンソースなので、オープンソースを使えたり自由に変更できたりするといろんなことができる気がします。オープンソースがどの程度自由に使えるか?これがソフトウェア技術者として重要な気がします。

音声処理飽きたので、次回は、OpenAI GYMで自動制御とかゲームをするコンピュータでも作ってみようかなぁ。

おじさん、大学時代に音声信号処理と自動制御の研究をしていたのですが、最近ロボットの自動制御に深層強化学習を利用していたりして、なんか勝手に10年以上前の技術が旬になっています。でもおじさんはなぜか仕事がありません。誰か雇ってくれないかなぁ。


2016年9月2日金曜日

グラスサウンドスピーカーを聞いてみた。

最近まで、ものすごい忙しくて全然ブログを更新できませんでした。
やっと遅めの夏休み。

機械学習とかの本はほとんど買い尽くして読んで、テレビゲームをやるAIや囲碁をするAIも作り方がわかったので、夏休みはハイレゾオーディオで遊びます。

オラソニックのCD-1とUA-1とソニーのグラスサウンドスピーカーLSPX-S1をつないで聞いてみました。

スピーカーはハイレゾ対応ではないんですが、ハイレゾまでの音は出るようです。
このグラスサウンドスピーカーは音を聞くとその解像度がかなり凄い。
本当にクリアに聞こえます。
シャカシャカしないというか圧迫されないというか疲れない音というかなんというかずっと聴きたくなるような音が出ます。
いろんな音源を聴いてみましたが、ジャズとか女性ボーカルが特に良いです。
スピーカーが1つだと音がモノラルなのでこれ確かにもう1ついるなあ。




2016年7月11日月曜日

人工無能を試してみた。

おじさんには1歳の子供がいるんです。
おじさんのまねをして扉をあけて外にでる動作をしたいらしいんですが、扉を開けるのは簡単にできるので興味はなく、扉を閉める動作に興味があり、扉を開けて自分は動かないで扉を閉めるのでいつまでも外に出れなくて怒っています。

きっとなんかの拍子にたまたま外に出られたときにそれが学習されて扉の開け方を学んでいくんだなぁと思うと、同じことをCNNでロボットに学習させてあげると、扉を開けることができるロボットができるんじゃないかと思ってしまいます。

まぁそれはさておいて、Siriのようなものを作る二回目です。
順序でいうと自然言語処理をするところです。
人工無能とかチャットボットって言われてるところですね。

以前このブログで紹介したWord2Vecと必要な技術と原理は同じなのです。
ざっとの原理はここに書かれているのでこれを参考にしていただければと思います。
http://robotstart.info/2016/05/17/kozaki_shogeki-no20.html



最近この分野のAIが人気で古本が高値を更新しつづけています。


https://www.amazon.co.jp/%E6%81%8B%E3%81%99%E3%82%8B%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%A0%E2%80%95Ruby%E3%81%A7%E3%81%A4%E3%81%8F%E3%82%8B%E4%BA%BA%E5%B7%A5%E7%84%A1%E8%84%B3-%E7%A7%8B%E5%B1%B1-%E6%99%BA%E4%BF%8A/dp/4839917299


https://www.amazon.co.jp/gp/product/4274066649/ref=oh_aui_detailpage_o01_s00?ie=UTF8&psc=1

なんでこれらの本が高いかというと、これらの原理を研究していた方がもう現役でなく、これらの本だけに原理とソースコードが添付してあるのです。
おそろしいですねぇ、こんなところに日本の高齢化社会の歪が。

おじさんもこれらの本を持ってるんですが、たとえ本を持っていても、学習させるのが本当に大変で、個人レベルのPCでは本のようなひどい結果しかでないのです。

上記の人の場合、会話の結果はこんな感じですね
http://robotstart.info/2016/02/23/report-tensorflow2.html


なので、ソースコードがほしい方は本買ってください。
また、よい学習結果がほしい方はamazonのGPUインスタンスを何万台も買ってください。

本をもとにTensorFlowやamazonなどで何万台かのPCで高速分散処理させると、一番上の方のように、とりあえず文字で何か入力すると何か文字を返してくれる、そんなものを作ることができます。

おじさん、前回書いたロボットを作りたい会社の面談で言ったんですよ。NLPのところはまだまだ全然できていないのでこれからどんどん開発していかないといけないと思いますと。そうしたら面接官に言われました。NLPだと喋んないロボットになっちゃうじゃんと。

そうだよね。まだこれだけだとロボット喋んないよね。
次回はロボットがしゃべる方法について書きたいと思います。



2016年7月8日金曜日

Siriみたいな音声認識のとこ作ってみた。

おじさんまた仕事ないです。
おじさんこの前、しゃべるロボットを作りたい会社の役員と面談をして、音声認識なんて一日くらいあれば作れますよっていったんですが、あやしーといわれ全く信用されず。
なので仕事がないんです。

暇なのでアップルのSiriみたいなものって作れるのか試してみました。
基本的に、Siriのようなソフトは、

1. 音声を文字にする。
2. 文字を自然言語処理する。
3. 結果を表示したりしゃべる。

を作ればできそうですよね。
でもいきなり全部はできないので本日のブログでは1の音声を文字に変換することまで。

まず、音声を文字に変換するには、音声をフォルマント分析し・・・・・中略・・・・・隠れマルコフモデルで・・・・・中略・・・・・、モデルを生成するために・・・・・省略・・・・・

理論はとっても説明に時間がかかるので省略します。
まず、ライブラリと学習結果を持ってきます。
juliusっていう音声認識ライブラリはフリーでとてもよく整備されているのでこれを使うことにします。

http://julius.osdn.jp/

このライブラリ本当にすごいです。Anrdoid、iPhone、Windows、Linux、MacなどありとあらゆるOSで動いてしかも学習結果のデータもセットになっていて、精度もかなりでています。ドキュメントもものすごい整備されているので簡単にコンパイルできます。
ラスパイとかでも動くのでロボットなどの組み込みで音声認識をしたい人はもってこいです。Googleもすごいけど、このライブラリ作ってる京都大学もすごいですね。ソースコードからも頭の良さが伝わってきます。

ということでサクッとダウンロードしてとってきます。
どれくらいの精度で認識できるかはネットで試験した人がいるので以下の動画でわかります。

https://www.youtube.com/watch?v=l8sIT7lRh1M


ほら、コンパイルするだけでよいので、一日で作れるでしょ。
実行形式のバイナリーも大体のOSで公開されているので、試してみるだけならばコンパイルもする必要もありません。

本当は正直に一時間で作れるって言わないと採用されないのかなぁ・・・・・。

2016年7月7日木曜日

ninjaのソースコードをみてみた

僕は毎週木曜日にブログを後進する傾向があるらしいのですが、毎週何かしらオープンソースをコンパイルして使っているので、月曜日から勉強し始めて木曜日くらいに勉強し終わるので、木曜に記事を書き終わります。
今週はビルドツールninjaを勉強しています。。

https://ninja-build.org/

最近ソースコードの傾向として、短く、わかりやすく、高速に動作するように書かないといけないようで、ninjaもソースコード自体はとてもコンパクトにできているmakeにかわるビルドツールです。ninja自体はとてもコンパクトなんですが、動作がものすごい高速なので、最も大規模なソースであるChromeやandroidをビルドするために使われています。makeより直感的に依存関係を記述できるし圧倒的に使いやすいです。

このninjaですが、使い方はいろいろなページで紹介されているので置いといて、ソースコードの独立性というか分割というかテストを含めた開発環境もとてもよくできています。

たとえば、構文解析の部分とかはre2cでlexer.hとlexer.ccを生成しているんですが、この二つのファイルとlexter_test.ccで構文解析部分の開発とテストが全部できます。
グラフ部分はgraph.hとgraph.ccとgraph_test.ccで全部テストと開発ができます。
すべてのプラットフォームで動いて、ファイル間の依存関係が最小だし。
コードがコンパクトで美しすぎる。

おじさんのような昔の人間が、C++言語の最新の開発環境やソースの書き方、テストコードの書き方などプログラムの基礎を勉強をするのには最適な教材だと思います。





2016年6月20日月曜日

超軽量JavaScriptエンジンV7をつかってみた


超軽量なJavaScriptエンジンであるV7を使ってみました。
https://github.com/cesanta/v7

ファイルが一つなのでコンパイルも超簡単。
Arduinoなどの組み込みでも動くほど軽量だそうです。
当然Arduinoでなくても、WindowsやLinuxなどのOSでも動きます。
40kバイトくらいでうごくらしいし、こんなに軽量なのにきちんとEcmaScript5.1をみたしているのすごいですね。

C/C++の関数呼び出しとかも簡単にできるので、組み込みなんかの用途だけでなく、ちょとしたサーバプログラムや自動テストなどでPythonの代わりにも使えそうです。




2016年6月17日金曜日

ビッグデータ2.0

最近はビッグデータの処理方法も変わってきていて、ビッグデータ2.0なるものがあるらしいです。MapReduceとか抽象化が偏っているというかなんというか使いづらいしね。
NoSQLとかNon-RDBとか一時は言われたけど、結局またSQLに戻ってきたみたいです。


ということで、GoogleBigQuerlyを使ってみました。
高速だし、WebAPIになってるし、使いやすいし、本当によくできています。
Googleの作っているソフトウェアは、きちんと現状の問題点を的確に分析して、その問題をすらばらしい技術で解決するソフトウェアをすぐにリリースしていて本当にすごいです。
問題解決というのはビジネスの基本なのですが、日本の企業だとソフトウェアで同じことがなかなかできないのがとても残念です。







2016年6月9日木曜日

Googleフォームでメールを自動送信をする。

Googleフォームってアンケートや問い合わせをするようなホームページを簡単に作ることができます。

さらにGoogleフォームでは、GoogleAppsScriptというJavaScriptも動くので、例えば、問い合わせがあった場合に自動でメールを送るってことも簡単にできます。

しかし、問い合わせがあった場合にメールを送るよいJavaScriptのサンプルがなかなか公開されていません。ないので、作ってみました。

https://docs.google.com/forms/d/1si58tjz_6eDW9h7raLEBhR60iKTsrGfCcHjPTzxwTLc/viewform


----------------------------------------

//
// Mail Sender Script for Google Froms Rev 002
//

var g_response=null;
//
// common functions
//

function mysleep(ms)
{
  Utilities.sleep(ms);
}

function print(msg)
{
  try{
    Logger.log(msg);
  }catch(e){}
}

function sendEmail(address,title,body,cc,bcc)
{
  try{
    var ext=null;
    if(cc!=null || bcc!=null){
      ext={};
      if(cc!=null)ext['cc']=cc;
      if(bcc!=null)ext['bcc']=bcc;
    }
    GmailApp.sendEmail(address, title, body,ext);
  }catch(e){}
}

function setResponse(evt)
{
  if(evt!=null){
    g_response=evt.response;
    return;
  }
  var responses = FormApp.getActiveForm().getResponses();
  g_response=responses[responses.length-1];
}

function getSubmitValue(key)
{
  var ret="";
  try{
    var itemresponses=g_response.getItemResponses();
    for(var j=0;j<itemresponses.length;j++){
      var itemresponse=itemresponses[j];
      var title=itemresponse.getItem().getTitle();
      var value=itemresponse.getResponse();
      //print("title="+title);
      //print("value="+value);
      if(key==title)return value
    }
  }catch(e){}
  return ret;
}


//
// Event Trigger
//

function onSubmit(evt)
{
  var to="";
  var body="";

  setResponse(evt);
  to=getSubmitValue("メールアドレス");
  body=getSubmitValue("お問合せ内容");
  print("to="+to);
  print("body="+body);
  sendEmail(to,"test",body,null,null);
}



2016年6月2日木曜日

ヘッダファイルだけのライブラリを作る。

一つ前の記事でEigenのいうライブラリはヘッダーファイルしかなく、ヘッダーファイルをincludeするだけで使えると書いたんですが、グローバル変数とかってどうやって宣言するんだろう?と気になったので、ヘッダーファイルだけのライブラリの作り方を調べてみました。


どうやら論文書いた人がいるようです。

http://www.res.kutc.kansai-u.ac.jp/~ezawa/grad2001/Yamasaki/honbun.pdf

なるほど。
クラスのstaticメソッドのなかにstatic変数を定義して、その参照を返すのね。


ということで、ヘッダーファイルだけのライブラリのテンプレート作ってみました。
Eigenというライブラリは主にGoogleの人が書いてるっぽいんですが、やっぱGoogle賢いわ。

もうライブラリをコンパイルするなんて時代遅れなのね。
おじさんの頭が、C++の書き方が、時代遅れだったわ。

------------------------------------------------
#include <stdio.h>


class HeaderOnly{

public:
static int& getValueA(){
static int a;
return a;
}
static HeaderOnly& getHeaderOnly()
{
static HeaderOnly he;
return he;
}
public:
int m_b;

};


#if 1
void set_headeronly()
{
int& a = HeaderOnly::getValueA();
HeaderOnly& h = HeaderOnly::getHeaderOnly();

a = 5;
h.m_b = 10;
}

void print_headeronly()
{
int& a = HeaderOnly::getValueA();
HeaderOnly& h = HeaderOnly::getHeaderOnly();
printf("a=%d\n", a);
printf("h.m_b=%d\n", h.m_b);

}

int main()
{
set_headeronly();
print_headeronly();
return 0;
}
#endif


Eigenを使ってみた。


最近、機械学習が流行っています。機械学習といっても単純なベクトル演算をしているだけなのですが、Eigenもこの機械学習のコア部分といいますか、ベクトル演算を行うライブラリの一つです。

Eigenのどこがすごいかというと、超高速なんていう他のサイトにも書いてあることは書きません。
個人的に感じるすごいところは、C++のテンプレートだけで実装されているので、ライブラリ自体を別途ビルドしなくても、includeファイルだけをもってくればつかえるところ。

しかもWindowsのMSVC、LinuxやMacOSXのclang、GCCなどありとあらゆるコンパイラーをサポートしているので、ほんとうにファイルをコピペするだけでサクッと自分のコードが書けて実行できます。

OpenCVとかのライブラリはぐちゃぐちゃでほんとひどいもんね。
やっぱGoogleすげー。

ほかのライブラリのビルドとかインストールっていつもすごくめんどくさくて、そのためにmakeなどのビルドツールがあるんですが、そんなの一切必要ありません。

Eigenのフォルダーをソースコードと同じところにおいて
gcc -I. hoge.cpp
とうつだけでコンパイルができます。
ビルドを行わなくても使えるって本当にすごいね。

やっぱり時代はコピペなんだね。
僕もこれからライブラリを作るときはテンプレートにしようっと

で、適当なサイトに落ちていた以下のコードをVisualStudioやCygwinコンパイルして実行してみました。なんか超簡単。

---------------------
#include <iostream>

// Eigenのコア機能とジオメトリ機能を使う
#include <Eigen/Core>
#include <Eigen/Geometry>


int main() {
  // 以下サンプルコードでは名前空間Eigenを省略
  using namespace Eigen;

  // 二次元ベクトルv1の定義。初期値は未定義
  Vector2f v1;

  // 初期値はコンストラクタで指定できる
  Vector2f v2(1.0f, 0.5f);
  Vector3f v3(0.0f, 1.0f, -1.0f);

  // 内容を表示
  std::cout << "v2\n" << v2 << std::endl;
  std::cout << "v3\n" << v3 << std::endl;

  // double型3x3行列の定義。初期値は未定義
  Matrix3d m;

  // 行列の初期値はコンストラクタで指定できないが
  // 値は以下のようにまとめて代入できる
  m << 1.0, 0.0, 0.0,
       0.0, 1.0, 0.0,
       0.0, 0.0, 1.0;

  std::cout << "m\n" << m << std::endl;
}



2016年5月20日金曜日

組み込み用のファイルシステムライブラリを治す


PIC用のライブラリってなぜかバグが多いんですよね。
マイクロチップ社が公式で公開しているSDカードなどにアクセスするファイルシステムライブラリとかもかなりの完成度でできているんですが、なぜかまだバグがたくさんあります。うーんとっても残念。

これではIoTデバイスを開発するときにとっても困るので、マイクロチップ社が公開しているファイルシステムのライブラリのバグを全部修正しておきました。

ついでに機種に依存する部分は全部排除したのでmbedとかarduinoとかPIC以外でも動きます。

ついでにWindowsとかLinuxとかMacでも動くようにしたので、ファイルシステムの勉強とかにも使えます。

https://github.com/yomei-o/pic


sendmail互換アプリを作る

お問合せのWebページを作ろうと思うと、メールを送るのにsendmailかその互換ソフトウェアがいります。

しかし、このsendmail、純正のものは結構サイズが大きく、設定も複雑。さらにメインのサポートはLinuxなので、WebサーバがWindowsやMaxOSXだとよいsendmail互換ソフトウェアがなくて困ります。

いろいろなOSやコンパイラで動くsendmail互換ソフトウェアがないので、MSMTPという高性能でかつコンパクトなsendmail互換のソフトウェアを改良して、いろいろなOSやコンパイラで動くようにしてみました。

とりあえず、gcc、clang、mingw、Cygwin、VisualStudio、XCodeでコンパイルできて動きます。


なにも考えずに
gcc *.c
でコンパイルできます。

sendmailとかは特定のディレクトリにインストールしないと動かないのですが、msmtpはディレクトリに構造に依存しないので、好きなディレクトリに実行形式と設定ファイルを置いて使うことができます。

これでどんなサーバのどんな設定でもメール送れる。


元のライセンスがGPLなのでソースコードを公開します。
https://github.com/yomei-o/msmtp





2016年5月18日水曜日

ホームネットワーク用のWebscoketサーバとクライアントを作ってみた。

外から家の家電を操作するようなものを作りたいとき、技術的にはWebsocketを使うとそのようなことができます。


しかし、Websocketをきちんと実装した軽量なサーバやクライアントがなかなかないので、作ってみました。

https://github.com/yomei-o/easyhttpd

一つ前の記事に書いたSQLエンジンもそうなのですが、ネットワーク系とデータベース系はライブラリを実装して互換性をきちんと試験するのが本当にとても大変です。
結構バグ取りに時間がかかってしまったのですが、今日だいぶまともに動くようになったので公開します。


CSVファイルをSQLで操作する

最近僕の周りではIoTというのが流行っているようで、いろいろなIoTデバイスでいろいろなデータを集めています。

集めたデータは大体CSVファイルになっているのですが、これだとエクセルでしかデータを活用できません。

僕的には集めたビッグデータを機械学習させたいのに・・・。

プログラムから簡単にSQLか何かでCSVファイルにアクセスできるライブラリとかないのかなぁと思ったのですが、意外とCSVファイルをSQL操作できるものってなかなかないのです。

なのでちょろっとCSVファイルをSQL操作するライブラリをつくってみました。
もっと簡単に書けるかなぁと思ったのですが、メインのところだけでも三日間、3000行もかかってしまいました。

https://github.com/yomei-o/csvsql

CSVファイルってSJISだったりするので、日本語対応とかめんどくさい。
SQLのWHEREとかも計算式対応するのがめんどくさい。
だけど大体対応しました。

これで、いろいろなプログラムにIoTデバイスのデータをプログラムから活用できます。

2016年5月11日水曜日

C++でforeachを実装してみる

久しぶりにC++文法の勉強をしています。C++11からforeachができるようになったのですが、自分で作ったクラスをforeachしたことがないので、いろいろ調べてやってみました。

どうやら、iteratorを実装すればできるっぽいのですが、結構めんどくさいのでテンプレートとして書き留めておきます。


------------------------------------------------------------
#include <stdio.h>
#include <string.h>

#include <iterator>

class AIterator;

class A{
friend AIterator;

public:
typedef AIterator iterator;
A();
virtual ~A();

A::iterator begin();
A::iterator end();
char m_str[16];
};

class AIterator : public std::iterator<std::forward_iterator_tag, int>
{
friend A;
private:
private:
AIterator();
AIterator(A* myClass, int index);

public:
AIterator(const AIterator& iterator);

public:
AIterator& operator++();
AIterator operator++(int);

int operator*();

bool operator==(const AIterator& iterator);
bool operator!=(const AIterator& iterator);

private:
int m_index;
A* m_a;

};

//
//
//

A::A()
{
m_str[0] = 0;
strcpy(m_str, "123456789");
}


A::~A()
{
m_str[0] = 0;
}

AIterator::AIterator()
{
m_a = NULL;
m_index = 0;
}

AIterator::AIterator(A* myClass, int index)
{
m_a= myClass;
m_index = index;
}

AIterator::AIterator(const AIterator& iterator)
{
m_a = iterator.m_a;
m_index = iterator.m_index;
}

int AIterator::operator*()
{
int dummy=-1;
int len;

len = strlen(m_a->m_str);
if (m_index < 0 || m_index >= len)return dummy;
dummy = m_a->m_str[m_index] & 255;
return dummy;
}

AIterator& AIterator::operator++()
{
m_index++;
return *this;
}

AIterator AIterator::operator++(int)
{
AIterator result = *this;
m_index++;
return result;
}

bool AIterator::operator!=(const AIterator& iterator)
{
return this->m_a != iterator.m_a || this->m_index != iterator.m_index;
}

bool AIterator::operator==(const AIterator& iterator)
{
return !(*this != iterator);
}

A::iterator A::begin()
{
return AIterator(this, 0);
}

A::iterator A::end()
{
int len;

len = strlen(m_str);
return AIterator(this,len);
}

//
//
//

int main()
{
A a;

for (int i : a){
printf("i=%02x\n",i);
}
return 0;
}



------------------------------------------------------------

2016年4月20日水曜日

Windows10でネットブックで画面の解像度をあげる

僕B5サイズのノートPCが大好きで、家でブラウザを使うのにネットブックを使っています。

東芝のUX23という機種を使っているのですが、Windows7とかWindows10を入れると、画面の解像度が1024*600になってしまい、一部のソフトが画面からはみ出して、ボタンをクリックできず使えません。

WindowsXPのときは液晶サイズより高い解像度を設定できたのに、Windows7以降は液晶サイズより高い解像度の設定ができないようで、とても不便です。

昔は新しいPCを買ってお金で解決したのですが、おじさんのように給料が下がってしまった人は知恵で解決していきます。

なかなかネットに解決方法が載っていないので、どうやったら画面の解像度を上げられるかいろいろ実験して調べてみました。

どうやら、レジストリにスケーリングONの設定をして、インテルのGMA950のグラフィックスドライバを入れれば画面の解像度を上げられるようです。


まず、IntelのGMA950(G945)のグラフィックスドライバ。

https://downloadcenter.intel.com/ja/product/81509/Graphics-Drivers-for-Mobile-Intel-945GM-Express-Chipset-Family

これをいれます。僕の時はバージョン: 15.12.75.4.1930というものを入れました。
これをいれずにレジストリを変更すると解像度を上たとたんにブルーバックになってしまいます。
Windows8以降ではインストーのル途中で古いバージョンのドライバーを入れようとしていますと聞かれますが、強制的に古いバージョンを入れます。


次にレジストリの書き換え。
拡張子が.regのファイルを作り、以下のテキストをファイルに書き込みます。
------------------------------
Windows Registry Editor Version 5.00
[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Class\{4D36E968-E325-11CE-BFC1-08002BE10318}\0000]
"Display1_DownScalingSupported"=dword:00000001
[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Class\{4D36E968-E325-11CE-BFC1-08002BE10318}\0001]
"Display1_DownScalingSupported"=dword:00000001
------------------------------

そして、ファイルをクリックするとレジストリの変更ができ、PCを再起動すると設定が有効になります。

これで1024*600までしか画面の解像度の設定ができなかったPCが1024*768と1152*864の設定ができるようになります。

液晶サイズはもともと1024*600なので1024*768などに設定すると画面が縮小されてしまいますが、いままで大きい画面でないと使えなかったソフトが使えるようになります。

他のネットブックでもたいていIntelのGMA950のグラフィックアクセラレータなので、同様な方法で解像度を上げることができます。
Windows7、Windows8、Windows10で試しましたが、どのバージョンのWindowsでもこの方法が使えます。


2016年4月7日木曜日

C言語でレーベンバーグ・マーカート法(Levenberg-Marquardt)をやってみた

最小二乗法を実験するブログ、最終回の本日は、レーベンバーグ・マーカート法です。
最小二乗法は線形関数を推定する場合は簡単なのですが、非線形の場合とっても難しくなるようです。

ソースコードでいうと、線形の場合、C言語のファイルが一つなのですが、非線形の場合、ファイルが2000個近くになります。

ビッグマックセットに例えると、普通のビッグマックセット650円なのにグランドビックマックセット130万円って感じでしょうか。


とりあえず、C言語でレーベンバーグ・マーカート法をやるには以下の二つのライブラリがいります。

levmar
http://users.ics.forth.gr/~lourakis/levmar/

LAPACK
http://www.netlib.org/lapack/


levmarは名前からもわかるようにレーベンバーグ・マーカート法のライブラリです。
しかもこのライブラリはLAPACKというフォートラン言語で書かれたベクトル演算ライブラリを使っています。さらにLAPACKはBLASというライブラリを使っています。

フォートラン?
フォートランっていう言葉20年ぶりくらいに久しぶりに聞いた。


このLAPACKというライブラリをC言語からどうやって使うかというと

1.コンパイルしてあるライブラリをどっかから持ってきて使う

2.フォートランコンパイラでコンパイルして、ラッパーをCコンパイラでコンパイルして使う

3.f2cでフォートラン部分のソースコードをC言語に変換して、BLASとlibf2cとも一緒にコンパイルして使う。

このどれかの方法でやらないといけません。
一番簡単な1の方法を使おうと思ったのですが、VisualStudio2008用のライブラリしかない・・・・。

仕方がないので3の方法でコンパイルをすることにします。

f2cをかけたC言語のソースコードはCLAPACKというようで、以下のところにあります。
http://www.netlib.org/clapack/

CLAPACKのソースコードを取ってきて、Visual-C2015で頑張ってコンパイルします。
ひたすら2000ファイル近いソースコードをコンパイルし、エラー部分を修正していきます。
なんというめんどくさい作業。

さらに、levmarをCLAPACKに対応するように修正。

それでも半日くらいかければコンパイルすることができます。

http://kivantium.hateblo.jp/entry/20140408/p1

これでやっと上記のソースコードをVisualStudioで動かすことができました。


VisualStudioでも動くようにした、フルC言語のLAPACKとlevmarはこちら。
https://drive.google.com/open?id=0B5M9qMMg3tfQRDVRaWpuVVh1OWc


コンパイルが面倒ですが、一度作ってしまえばWindows版のBLASとLAPACKだけでもしばらくいろいろ遊んで楽しめそう。


2016年4月6日水曜日

勾配降下法をやってみた。

最小二乗法が簡単だったので調子に乗って勾配降下法をC言語で実装してみました。
関数の最小値を求めるもっとも簡単なやつです。
これも実装だけならば簡単。いろいろ実験して遊ぶのには最適です。
関数の傾きを求めてちょっとづつ移動していくやつです。

遊んでわかったことは初期値が平らなところから始まるとこれでもかと思うくらいなかなか値が収束しません。ダメじゃん。傾きを計算するのに導関数もいるしね。

ということでいつか非線形関数の最小二乗法のLevenberg-Marquardt法で最小値を求める実験でもやろうと思っているのですが、Levenberg-Marquardt法、とってもめんどくさいし数学的によくわからないので片手間ではブログ書けないのです。どうしよう・・・・。



---------------------------------------------

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>

#define WIDTH 80
#define HEIGHT 25

//
//myplot
//

static char dot[HEIGHT][WIDTH];
static double off_x = 0.0;
static double off_y = 0.0;
static double scale = 1.0;

static void myplot_init(void)
{
int i;
memset(dot, ' ', HEIGHT*WIDTH);
for (i = 0; i < WIDTH; i++)dot[0][i] = '-';
for (i = 0; i < HEIGHT; i++){
dot[i][0] = '|';
dot[i][WIDTH - 1] = 0;
}
}

static void myplot_plot(double x, double y, char c)
{
int ix, iy;
x = (x - off_x)*scale * 2;
y = (y - off_y)*scale;
ix = (int)x;
iy = (int)y;
if (ix<0 || ix>WIDTH - 2)return;
if (iy<0 || iy>HEIGHT - 1)return;
dot[iy][ix] = c;
}

static void myplot_print(void)
{
int i;
for (i = 0; i < HEIGHT - 1; i++)printf("%s\n", dot[HEIGHT - 2 - i]);
}

void myplot_set_scale(double off_x_, double off_y_, double scale_)
{
off_x = off_x_;
off_y = off_y_;
scale = scale_;
}


//
// main
//
double f(double x)
{
return 20-exp(-x*x/20)*15;
}

double df(double x)
{
return 0.1*x*exp(-x*x / 20) * 15;
}

int main()
{

int i;
double x = 5;
double n = 0.1;

myplot_init();
for (i = 0; i < 10; i++){
myplot_plot(i * 10, 0, '+');
myplot_plot(0, i * 10, '+');
}
myplot_set_scale(-20, 0, 1);
for (i = -100; i < 100; i++)myplot_plot(i, f(i), '*');
//for (i = -100; i < 100; i++)myplot_plot(i, 3*df(i)+10, '@');
myplot_plot(x, f(x), 'S');

for (i = 0; i < 1000; i++){
x = x - n*df(x);
//printf("%f\n", x);
myplot_plot(x, f(x), 'A');
}
myplot_plot(x, f(x), 'E');
myplot_print();


return 0;
}


---------------------------------------------

最小二乗法をやってみた

たまには数値計算でもしてみようかなぁと思い、最近はやりの最小二乗法を作ってみました。
グラフツールとかインストールしていないので、コンソールにグラフを書く簡単なライブラリも作ってみました。


おーなんかあってるっぽいね。

-----------------------------------------------
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>

#define WIDTH 80
#define HEIGHT 25

//
//myplot
//

static char dot[HEIGHT][WIDTH];
static double off_x = 0.0;
static double off_y = 0.0;
static double scale = 1.0;

static void myplot_init(void)
{
int i;
memset(dot, ' ', HEIGHT*WIDTH);
for (i = 0; i < WIDTH; i++)dot[0][i] = '-';
for (i = 0; i < HEIGHT; i++){
dot[i][0] = '|';
dot[i][WIDTH - 1] = 0;
}
}

static void myplot_plot(double x, double y, char c)
{
int ix, iy;
x = (x - off_x)*scale * 2;
y = (y - off_y)*scale;
ix = (int)x;
iy = (int)y;
if (ix<0 || ix>WIDTH - 2)return;
if (iy<0 || iy>HEIGHT - 1)return;
dot[iy][ix] = c;
}

static void myplot_print(void)
{
int i;
for (i = 0; i < HEIGHT - 1; i++)printf("%s\n", dot[HEIGHT - 2 - i]);
}

void myplot_set_scale(double off_x_, double off_y_, double scale_)
{
off_x = off_x_;
off_y = off_y_;
scale = scale_;
}


//
// saisyou2zyou
//

int gauss(double **w, int n, int m, double eps)
{
double y1, y2;
int ind = 0, nm, m1, m2, i1, i2, i3;

nm = n + m;

for (i1 = 0; i1 < n && ind == 0; i1++) {

y1 = .0;
m1 = i1 + 1;
m2 = 0;

for (i2 = i1; i2 < n; i2++) {
y2 = fabs(w[i2][i1]);
if (y1 < y2) {
y1 = y2;
m2 = i2;
}
}

if (y1 < eps)
ind = 1;

else {

for (i2 = i1; i2 < nm; i2++) {
y1        = w[i1][i2];
w[i1][i2] = w[m2][i2];
w[m2][i2] = y1;
}

y1 = 1.0 / w[i1][i1];

for (i2 = m1; i2 < nm; i2++)
w[i1][i2] *= y1;

for (i2 = 0; i2 < n; i2++) {
if (i2 != i1) {
for (i3 = m1; i3 < nm; i3++)
w[i2][i3] -= w[i2][i1] * w[i1][i3];
}
}
}
}

return(ind);
}

double *least(int m, int n, double *x, double *y)
{
double **A, **w, *z, x1, x2;
int i1, i2, i3, sw;

m++;
z = (double*)malloc(sizeof(double)*m);
w = (double**)malloc(sizeof(double*)*m);
for (i1 = 0; i1 < m; i1++)
w[i1] = (double*)malloc(sizeof(double)*(m + 1));
A = (double**)malloc(sizeof(double*)*n);

for (i1 = 0; i1 < n; i1++) {
A[i1] = (double*)malloc(sizeof(double)*m);
A[i1][m - 2] = x[i1];
A[i1][m - 1] = 1.0;
x1 = A[i1][m - 2];
x2 = x1;
for (i2 = m - 3; i2 >= 0; i2--) {
x2 *= x1;
A[i1][i2] = x2;
}
}

for (i1 = 0; i1 < m; i1++) {
for (i2 = 0; i2 < m; i2++) {
w[i1][i2] = 0.0;
for (i3 = 0; i3 < n; i3++)
w[i1][i2] += A[i3][i1] * A[i3][i2];
}
}

for (i1 = 0; i1 < m; i1++) {
w[i1][m] = 0.0;
for (i2 = 0; i2 < n; i2++)
w[i1][m] += A[i2][i1] * y[i2];
}

sw = gauss(w, m, 1, 1.0e-10);

if (sw == 0) {
for (i1 = 0; i1 < m; i1++)
z[i1] = w[i1][m];
}
else
z = NULL;

for (i1 = 0; i1 < n; i1++)
free(A[i1]);
for (i1 = 0; i1 < m; i1++)
free(w[i1]);
free(A);
free(w);

return z;
}

//
// main
//
int main()
{
double x[] = { 10, 15, 20, 26, 32, 40 };
double y[] = { 28.2, 47, 44.4, 32.8, 20.8, 0.8 };

double *z;
double d;
int i, m, n;

m = 2;
n = 6;

myplot_init();
myplot_set_scale(0, 0, 0.5);
for (i = 0; i < 10; i++){
myplot_plot(i * 10, 0, '+');
myplot_plot(0, i * 10, '+');
}
for (i = 0; i < n; i++)myplot_plot(x[i], y[i], '*');

z = least(m, n, x, y);

if (z == NULL)return 0;

for (i = 0; i < 80; i++){
d = z[0] * i * i + z[1] * i + z[2];
myplot_plot(i, d, 'o');
}

myplot_print();

free(z);

return 0;
}

-----------------------------------------------


2016年4月5日火曜日

自動微分をする

よく、コンピュータの理論を勉強する前に基礎である線形代数を勉強してほうが良いといわれています。

この線形代数なのですが、おじさんの時代は主にベクトルとか逆行列とかをやっていました。まぁゲームとかを作る際に座標の計算とかをしないといけないからなのですが。

しかしこの線形代数、時代とともに旬な領域が変化していまして、最近は二重数とか自動微分とかをイギリスの大学で勉強するようです。

おじさんの時代は二乗すると-1になる数はiしかなかったのに。
二乗すると-1になる数三つに増えてるし、近頃は二乗すると0になる二重数があるらしいです。素粒子みたくフェルミ的とかボソン的なんていうらしいです。
二重数の話はまた後ろに書くとして、まずは自動微分について。

自動微分とはプログラムで関数の値の計算をするとなんとその導関数の値も同時に計算できちゃうアルゴリズムというか理論です。

たとえばx=1でいうと
y=x^2を計算すると1ですが、同時にy'=2*xの答え2も計算できてしまう。

ただそれだけなんです。
手動で計算したほうがはやいじゃん!

と思っていたのですが、よく調べてみると、単純な数式ならば手動で微分すればよいんですが、Σとかがある複雑な式の導関数の計算ができたり、ニュートン法と組み合わせれば、逆関数がわからない複雑な方程式が解けたり、計算が高速にできたりします。

早い話が、条件はありますが方程式が高速に解けるのです。
このため、深層学習のとかでも使われていたりします。

この自動微分の計算ですが、二重数と関係があるようで、二重数の虚部というのか実部でない部分の計算と同じなのです。

つまり二重数で関数を計算すると実部が関数の値で、実部でない部分が関数を微分した値になります。


ほんとかよ!素粒子と深層学習が一緒なのかよ!
ということで、確かめてみました。

自動微分の理論からプログラミング
http://kivantium.hateblo.jp/entry/2016/03/25/010320

二重数の理論からプログラミング
http://qiita.com/skitaoka/items/b7d0b19e13a7c6832773

この二つのライブラリで計算値が一緒になるんですね。
あらーびっくり。




2016年3月30日水曜日

IoT用の最軽量httpdを作ってみた。

以前のブログで、軽量httpdのことを書いたのですが、Mongooseのライブラリは軽量といってもソースコードが300キロバイトくらいあります。

簡単に改良できて、貧弱なIoTデバイスとかにも乗せられるように、もっともっと軽量化できないかなと思い、自分でスクラッチで最軽量のhttpdを書いてみました。

丸一日かけて書いたソースコードがこれです。
シングルスレッドでIPV4とIPV6両対応でHTTPの規格であるRFC2616をちゃんと満たしています。さらに一応どんなOSでも動くように書いたつもりです。
500行、10キロバイトのhttpdです。
BSDライセンスがいいのでBSDライセンスにしてみました。
https://github.com/yomei-o/easyhttpd
オープンソース書いたから、メルカリにでも採用応募してみようかな。

easyhttpd.c
------------------------------------------------------------------------
/*
Copyright (c) 2016, Yomei Otani <yomei.otani@gmai.com>
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

The views and conclusions contained in the software and documentation are those
of the authors and should not be interpreted as representing official policies,
either expressed or implied, of the FreeBSD Project.
*/
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<time.h>

#ifdef _WIN32
#include <winsock2.h>
#include <ws2tcpip.h>
#endif /* _WIN32 */
#if defined(unix) || defined(ANDROID_NDK) || defined(__APPLE__)
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#endif /* unix */
#if defined(_WIN32) && !defined(__GNUC__)
#define _CRTDBG_MAP_ALLOC
#include <crtdbg.h>
#endif

#ifdef _MSC_VER
#if _MSC_VER >= 1400
#pragma warning( disable : 4996 )
#pragma warning( disable : 4819 )
#endif
#endif
#if defined(_WIN32) && !defined(__GNUC__)
#pragma comment( lib, "ws2_32.lib" )
#endif

#if defined(unix) || defined(ANDROID_NDK) || defined(__APPLE__)
#define closesocket(s) close(s)
#endif /* unix */

#define MAX_LISTEN 20
#define MAX_FD 20
#define PORT 12345
#define RECV_TIMEOUT 30

#define DIR_WEBSOCKET "websocket"


void smplws_init()
{
#ifdef _WIN32
WSADATA wsaData;
int err;

err = WSAStartup(MAKEWORD(2, 0), &wsaData);
if (err != 0)
{
exit(1);
}
#endif
#if defined(unix) || defined(__APPLE__)
signal(SIGPIPE, SIG_IGN);
#endif
}

void smplws_done()
{
#ifdef _WIN32
WSACleanup();
#endif
}

//
// socket
//
int dualsock_server_create(int port)
{
int err;
struct addrinfo hints;
struct addrinfo* res = NULL;
struct addrinfo* ai;
int sockfd;
char service[16];

sprintf(service, "%d", port);
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_INET6;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE;
err = getaddrinfo(NULL, service, &hints, &res);
if (err != 0) {
return -1;
}
ai = res;
sockfd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
if (sockfd < 0)return -1;

#if defined(_WIN32) || defined(__CYGWIN__)
{
int one = 0;
err = setsockopt(sockfd, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&one, sizeof one);
}
#endif

if (bind(sockfd, ai->ai_addr, ai->ai_addrlen) < 0){
closesocket(sockfd);
return -1;
}
if (listen(sockfd, 20) < 0){
closesocket(sockfd);
return -1;
}
return sockfd;
}


int dualsock_create(const char* hostname, int port)
{
int sockfd;
int err;
struct addrinfo hints;
struct addrinfo* res = NULL;
struct addrinfo* ai;
char service[16];

memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_NUMERICSERV;

sprintf(service, "%d", port);

err = getaddrinfo(hostname, service, &hints, &res);
if (err != 0) {
return -1;
}
for (ai = res; ai; ai = ai->ai_next) {
sockfd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
if (sockfd < 0){
freeaddrinfo(res);
return -1;
}
if (connect(sockfd, ai->ai_addr, ai->ai_addrlen) < 0) {
closesocket(sockfd);
sockfd = -1;
continue;
}
break;
}
freeaddrinfo(res);
return sockfd;
}

int dualsock_accept(int sockfd)
{
int cs;
struct sockaddr_storage sa;
socklen_t len = sizeof(sa);

cs = accept(sockfd, (struct sockaddr*) &sa, &len);
return cs;
}

int dualsock_select(int sockfd, int sec)
{
fd_set readfds;
struct timeval t;
int ret;
//int tmp;

t.tv_sec = sec;
t.tv_usec = 0;

FD_ZERO(&readfds);
FD_SET(sockfd, &readfds);
ret = select(sockfd+1, &readfds, NULL, NULL, &t);
return ret;
}



int dualsock_select_multi(int* fds, int n, int sec)
{
fd_set readfds;
struct timeval t;
int ret;
int i;
int maxfd = 0;
t.tv_sec = sec;
t.tv_usec = 0;

FD_ZERO(&readfds);
for (i = 0; i < n; i++){
if (fds[i] != -1){
if (maxfd < fds[i])maxfd = fds[i];
FD_SET(fds[i], &readfds);
}
}
ret = select(maxfd+1, &readfds, NULL, NULL, &t);
return ret;
}

//
// smplws utils
//


int smplws_readch(int fd)
{
int ret;
unsigned char buf[4];
if (fd == -1)return -1;
ret=recv(fd, buf, 1, 0);
if (ret < 1)return -1;
return buf[0];
}

int smplws_readuntil(int fd,char* buf, int sz, int endch)
{
int ret = 0;
int ch;
//int tmp;

if (fd==-1 || buf == NULL || sz < 1)return -1;

ret = strlen(buf);

while (ret <= sz - 2){
//tmp = dualsock_select(fd,0);
//if (tmp == 0)return 0;
ch = smplws_readch(fd);
if (ch == -1)return -1;
buf[ret] = ch;
buf[ret + 1] = 0;
ret++;
if (ch == endch)break;
}
return ret;
}

int smplws_gettime()
{
return (int)time(NULL);
}

//
// dhild connection
//

enum{
CHILD_STATE_CLOSED = 0,
CHILD_STATE_HTTP_REQUEST = 1,
CHILD_STATE_HTTP_HEADER  = 2,
CHILD_STATE_WEB_SOCKET   = 3,
};

enum{
CHILD_MODE_UNKNOWN = 0,
CHILD_MODE_HTTP = 1,
CHILD_MODE_WEB_SOCKET = 2,
};

struct child_data
{
char buf[256];
char tmp[256];
int state;
int mode;

//http
char content_type[64];
int content_length;
char* body;

//websocket
};


void* smplws_child_init(void)
{
void* ret = NULL;

//printf("child_init()\n");
ret = malloc(sizeof(struct child_data));
if (ret == NULL)return NULL;

memset(ret, 0, sizeof(struct child_data));
((struct child_data*)ret)->state = CHILD_STATE_HTTP_REQUEST;
return ret;

}

int smplws_child_send_error(int fd)
{
int ret;
char* str = "HTTP/1.1 403 Error\r\n"
"Cnnection: close\r\n"
"Content-Length: 0\r\n"
"\r\n";
ret=send(fd, str, strlen(str), 0);
if (ret == -1)return -1;
return ret;
}

int smplws_child_send_websocket_connection(int fd, struct child_data* cd)
{
return 0;
}


int smplws_child_parse_header(int fd, struct child_data* cd)
{
return 0;
}

int smplws_child_send_http_response_parse(int fd, struct child_data* cd)
{
cd->body = malloc(1024);
if (cd->body == NULL)return -1;
cd->body[0] = 0;
strcpy(cd->content_type, "text/html\r\n");
strcpy(cd->body, "<HTML><BODY>It wroks!</BODY></HTML>");
cd->content_length = strlen(cd->body);
return 0;
}

int smplws_child_send_http_response_dealloc(int fd, struct child_data* cd)
{
if (cd->body)free(cd->body);
return 0;
}


int smplws_child_send_http_response_header(int fd, struct child_data* cd)
{
char* str;
int ret;
char tmp[64];

str = "HTTP/1.1 200 OK\r\n"
"Cnnection: close\r\n"
"Pragma: no-cache\r\n"
"Cache-Control: no-cache\r\n";
ret = send(fd, str, strlen(str), 0);
if (ret == -1)return -1;

sprintf(tmp, "Content-Length: %d\r\n\r\n", cd->content_length);
ret = send(fd, tmp, strlen(tmp), 0);
if (ret == -1)return -1;

return 0;
}

int smplws_child_send_http_response_body(int fd, struct child_data* cd)
{
send(fd, cd->body, cd->content_length, 0);
return 0;
}


int smplws_child_data(void* vp, int fd)
{
int ret=0,tmp;
struct child_data* c = (struct child_data*)vp;

//printf("child_data()\n");
switch(c->state){
case CHILD_STATE_HTTP_REQUEST:
ret = smplws_readuntil(fd, c->buf, 256, '\n');
if (ret == -1)return -1;
if (ret == 0)return 0;
//printf(">>%s<<\n", c->buf);
c->tmp[0] = 0;
sscanf(c->buf, "%s", c->tmp);
if (strcmp(c->tmp, "GET") != 0){
smplws_child_send_error(fd);
return -1;
}
c->tmp[0] = 0;
sscanf(c->buf + 3, "%s", c->tmp);
if (strstr(c->tmp, "/" DIR_WEBSOCKET "/") == c->tmp)c->mode = CHILD_MODE_WEB_SOCKET;
else if (strstr(c->tmp, "/") == c->tmp)c->mode = CHILD_MODE_HTTP;
if(c->mode==CHILD_MODE_UNKNOWN){
smplws_child_send_error(fd);
return -1;
}
c->state = CHILD_STATE_HTTP_HEADER;
break;
case CHILD_STATE_HTTP_HEADER:
ret = smplws_readuntil(fd, c->buf, 256, '\n');
if (ret == -1)return -1;
if (ret == 0)return 0;
if (strcmp(c->buf, "\r\n") == 0){
if (c->mode == CHILD_MODE_WEB_SOCKET){
c->state = CHILD_STATE_WEB_SOCKET;
smplws_child_send_websocket_connection(fd, c);
break;
}
if (c->mode == CHILD_MODE_HTTP){
tmp=smplws_child_send_http_response_parse(fd, c);
if(tmp == -1){
smplws_child_send_error(fd);
return -1;
}
smplws_child_send_http_response_header(fd, c);
smplws_child_send_http_response_body(fd, c);
tmp = smplws_child_send_http_response_dealloc(fd, c);
return -1;
}
smplws_child_send_error(fd);
return -1;
}
smplws_child_parse_header(fd, c);
break;
case CHILD_STATE_WEB_SOCKET:
ret = -1;
break;
}

printf(">>%s<<\n", c->buf);
c->buf[0] = 0;

return ret;
}

void smplws_child_fini(void* vp)
{
//printf("child_fini()\n");
if (vp)free(vp);
}

//
// smplws signal
//

static int s_server_stop = 0;

void smplws_server_stop()
{
s_server_stop = 1;
}



//
// smplws_main loop
//
void smplws_server_main(){

int fd[MAX_FD];
void* vp[MAX_FD];
int tm[MAX_FD];
int ret,t;
int i,j;

s_server_stop = 0;
for (i = 0; i < MAX_FD; i++)fd[i] = -1;

fd[0] = dualsock_server_create(PORT);
if (fd[0] == -1)return;
printf("enter main loop\n");
while (1){
if (s_server_stop)break;
dualsock_select_multi(fd,MAX_FD,3);
t = smplws_gettime();
// client time out
for (i = 1; i < MAX_FD; i++){
if (fd[i] != -1 && (t - tm[i])>RECV_TIMEOUT){
closesocket(fd[i]);
smplws_child_fini(vp[i]);
fd[i] = -1;
vp[i] = NULL;
tm[i] = 0;
}
}
// polling sockets
for (i = 0; i < MAX_FD; i++){
if (fd[i] == -1)continue;
ret = dualsock_select(fd[i], 0);
if (ret == 0)continue;
if (i == 0){
int s;
void* v;

s = dualsock_accept(fd[0]);
v = smplws_child_init();
if (v == NULL){
closesocket(s);
}
for (j = 1; j < MAX_FD; j++){
if (fd[j] == -1){
// initial child
fd[j] = s;
vp[j] = v;
tm[j] = t;
break;
}
}
if (j == MAX_FD){
smplws_child_fini(v);
closesocket(s);
}
}else{
//recv child data
tm[i] = t;
ret = smplws_child_data(vp[i], fd[i]);
if (ret == -1){
closesocket(fd[i]);
smplws_child_fini(vp[i]);
fd[i] = -1;
vp[i] = NULL;
tm[i] = 0;
}
}
}
}

// abort
closesocket(fd[0]);
for (i = 1; i < MAX_FD;i++){
if (fd[i]!=-1)smplws_child_fini(vp[i]);
closesocket(fd[i]);
fd[i] = -1;
vp[i] = NULL;
}
return;
}


//
// main
//
#if 1

int main()
{
smplws_init();
smplws_server_main();
smplws_done();
return 0;
}
#endif

------------------------------------------------------------------------