2020年6月30日火曜日

算数の問題を解く

おじさん、子供の公文式の分数の計算問題があまりにめんどくさくてできません。
帯分数を仮分数に直して通分して計算して約分して仮分数を帯分数に直さないといけないのです。
もうなんなんだこのめんどくささ。

小学生の子供にどうやって問題を解いているのか聞いたら、Photomathというアプリをタブレットに入れて、写真を撮れば答えがわかるよと言われました。

どうも最近はスマホで算数の問題の写真を撮ると答えが出るらしい。
しかもきちんと計算の順序まで出る。
おじさん、最近の小学生のタブレット学習についていけない・・・。


これ、どういう仕組みになってるの?
調べてみると、im2markupというもので、数式の写真を文字列に変換し、計算しているらしい。

im2markup
http://arxiv.org/pdf/1609.04938v1.pdf

日本語のページが一つもヒットしないのは、きっと日本人は誰もやってないのね。
さみしいねぇ。これだけディープラーニングと言われている中、おじさんしかいないのかよ。

最近はなんでもLSTMですね。
LSTMって完全チューリングマシンなので、人間ができてLSTMでできないことなどないはず。
でもTensorFlowもTorchもLSTMはGPUがないと動かない・・・・

これをおじさんのパソコンでもうごかしたいので、本日はim2markupをWindowsのGPUがないマシンでも動くように移植します。
LSTMでできないことはないはずだけど、おじさんもC言語で実装できないものはない!


ディープラーニングって、論文や公開されているソースコードを動かそうとしても、だいたいそのままじゃ動きません。
使っているライブラリのバージョンが合わないとか、論文が間違っているとか、本当に大変な作業です。
これを自分のところで動くようになるまでにだいたい一か月くらいかかります。

よくディープラーニングのエンジニア募集のところにも書いてあるもんね、論文を読んで実装できることと。
これをさくさくできる人、日本にいるんだろうか?

論文とソースコードをにらめっこして、ひたすらおかしいところを直し、img2markupをWindowsのGPUなしでも動くC言語のコードにしてみました。

オリジナルはLuaJitコンパイラでLua言語で書かれている部分をコンパイルしないと動かないのですが、インタープリタで動くFFIライブラリも移植して、C言語とLuaのスクリプトファイルだけで動きます。
これでどんなマシンでもim2markupが動く。たぶんほかのLSTMのものも動く。

ここでいつもは学習済みデータをとってくるのですが、torchで書かれたものはGPUがあるマシンで学習させると、GPUがあるマシンでないと認識のテストができません。
CPU版の学習済みデータがない・・・。
なので本日は頑張ってGPUがないマシンで、im2markupの学習も行います。

おじさん、学習もできるんです。
サンプルの学習は数式が1000個、GPUなしで1エポック8時間かかります。
1日に3エポックしか進まないじゃないか。
15エポック学習するには5日間もかかるので、とりあえず3エポック学習させてみました。

ソースコードと学習済みデータはこちら。
https://drive.google.com/file/d/1VF_hckbuAH28UjtoAjMeUK2OW7hfCtvk/view?usp=sharing

この画像を文字列に変換してみます。


----------------
\Gamma ( z + 1 ) = \int _ { 0 } ^ { \infty } \, \, d x \, \, e ^ { - x } x ^ { z }
----------------

おー、認識に数分かかるけどなんかあってる。
3エポックしか学習させていないのに。

これを全データで120時間かけて学習させるときっとかなりの確率で数式を認識するんだと思います。このあとテキストに変換された数式を数式の計算ソフトに食わせればよいのね。

LSTMほんとすごいね。
本日はLSTMは時間かかるけどGPUがないマシンでもできるよっていう話でした。


2020年6月15日月曜日

深度推定をする

カメラの画像から物体までの距離を推定する、単眼深度推定(mono depth estimation)をしてみました。
この分野は盛んに研究されているらしく、vid2depth、monodepth、fcrndepthなど、いろいろなアルゴリズムがあります。
最新の研究だと、Tensorflowにデフォルトにないオペレーターとかを使うから、動かすのがとってもめんどくさい。
さらに、精度の良いアルゴリズムは、学習済みデータでも100Mくらい、元データは何百GバイトもあってとてもおじさんのPCでは実験できません。
この人たちどういうPCで研究しているんだろね。
そもそもおじさんのPCはGPUないし。

一年位前から手軽にさくっとできる単眼深度推定ないかなぁと論文を探していたところ、以下の論文を見つけました。
"Unsupervised CNN for single view depth estimation: Geometry to the rescue"
https://arxiv.org/pdf/1603.04992.pdf

学習はステレオカメラだけど、推定は一枚の画像でできるようです。

https://github.com/Ravi-Garg/Unsupervised_Depth_Estimation
学習済みデータも数十メガバイトしかないし。
なんかResNet50ベースって書いてあるから、これならば、いつもの最小のOpenCVでもさくっと実行できるかも。

ということで、github上の学習済みデータとテストコードを見ながら頑張って数時間かけて上記の論文のプログラムをC++で作ってみました。

以前作った最小限のOpenCVもとってきます。
http://yomeiotani.blogspot.com/2019/07/pose-estimation.html




なんとなくそれっぽい結果が。
-------------------------------
#include<stdio.h>
#include<string>
#include<vector>
#include<iostream>

#define STB_IMAGE_IMPLEMENTATION
#define STB_IMAGE_WRITE_IMPLEMENTATION


#include "opencv2_core.hpp"
#include "opencv2_imgproc.hpp"
#include "opencv2_imgproc_imgproc_c.h"
#include "opencv2_dnn.hpp"

#include "stb_image.h"
#include "stb_image_write.h"

void changeb_g(unsigned char* p, int x, int y, int c)
{
int ct = x*y;
int i;
unsigned char t;
for (i = 0; i < ct; i++) {
t = p[0];
p[0] = p[2];
p[2] = t;
p[3] = 255;
p += c;
}
}



#if 1

int main()
{
unsigned char* p;
int x=-1, y=-1, n=-1;

// Specify the paths for the 2 files
std::string protoFile = "deploy_resnet50by2_pool.prototxt";
std::string weightsFile = "train_iter_40000.caffemodel";


// Read the network into Memory
cv::dnn::Net net = cv::dnn::readNetFromCaffe(protoFile, weightsFile);

//
p = stbi_load("1.png", &x, &y, &n, 4);
//p = stbi_load("mask5.jpg", &x, &y, &n, 4);
if (p == NULL || x < 1 || y < 1)return 1;
changeb_g(p, x, y, 4);
cv::Mat color = cv::Mat(y, x, CV_8UC4);
memcpy(color.data, p, x * 4 * y);
stbi_image_free(p);

cv::Mat img;
cv::cvtColor(color, img, CV_BGRA2BGR);
cv::resize(img, img, cv::Size(608,160));

cv::Mat inpBlob = cv::dnn::blobFromImage(img, 1.0);

// Set the prepared object as the input blob of the network
net.setInput(inpBlob);


cv::Mat output = net.forward();
std::vector<cv::Mat> outImg;
cv::dnn::imagesFromBlob(output, outImg);
cv::Mat out = outImg[0];
unsigned char* pp = new unsigned char[608 * 160 * 4];
p = pp;
float* ff = (float*)out.data;

for (int i = 0; i < 160 * 608; i++) {
//if(i%608==0)printf("i=%d ff=%f\n",i,*ff);
float a = *ff++*5;
if (a > 255)a = 255;
if (a <0)a = 0;
*p++ = a;
*p++ = a;
*p++ = a;
*p++ = 255;
}

stbi_write_png("result.png", 608, 160, 4, pp, 4 * 608);

delete[] pp;
return 0;
}

#endif
-------------------------------

おじさん、次はLSTMをやりたいんだよなぁ。
機械翻訳のseq2seqとか、数式の写真をとると答えを計算してくれるim2latexとかをやってみたい。
opencvだとオペレーターが足りないんだよなぁ。


2020年6月12日金曜日

マスク着用を判定する。

おじさんの会社では、入り口のゲートのところにカメラが置いてあって、マスクをしているかチェックされます。
間違ってマスクを会社に置いたまま外に出ると、二度と社内に入れなくなります。

AIがマスク着用判定、注意も 東大発ベンチャーがシステム開発
https://www.sankeibiz.jp/business/news/200317/bsc2003170500006-n1.htm

いいなぁ、東大生が作ると新聞に載るけど、おじさんが一時間くらいで作っても誰も褒めてくれない・・・。おじさんさみしい。

世の中にあるマスク着用判定プログラムは、いろいろなライブラリが必要で、なかなか軽量なものがない。
そこで、他のライブラリを必要としない単体で動くコンパクトなものを作ってみました。


まず、いつもの通り学習済みデータを取ってきます。

https://github.com/didi/maskdetection
ここのmodelのフォルダにある学習済みデータを取ってきます。
100Mバイトもある。
そう思っていたけど、ResNet50でできているのね。

次に、以前作った最小限のOpenCVもとってきます。
http://yomeiotani.blogspot.com/2019/07/pose-estimation.html

ソースコードはこんな感じ。
一時間くらいでサクッと作れます。
----------------------------------------------------
#include<stdio.h>
#include<string>
#include<vector>
#include<iostream>

#define STB_IMAGE_IMPLEMENTATION
#define STB_IMAGE_WRITE_IMPLEMENTATION


#include "opencv2_core.hpp"
#include "opencv2_imgproc.hpp"
#include "opencv2_imgproc_imgproc_c.h"
#include "opencv2_dnn.hpp"

#include "stb_image.h"
#include "stb_image_write.h"

void changeb_g(unsigned char* p, int x, int y, int c)
{
int ct = x*y;
int i;
unsigned char t;
for (i = 0; i < ct; i++) {
t = p[0];
p[0] = p[2];
p[2] = t;
p[3] = 255;
p += c;
}
}

int main()
{
unsigned char* p;
int x=-1, y=-1, n=-1;

// Specify the paths for the 2 files
std::string protoFile = "deploy.prototxt";
std::string weightsFile = "face_mask.caffemodel";


// Read the network into Memory
cv::dnn::Net net = cv::dnn::readNetFromCaffe(protoFile, weightsFile);

//
p = stbi_load("nomask5.jpg", &x, &y, &n, 4);
//p = stbi_load("mask5.jpg", &x, &y, &n, 4);
if (p == NULL || x < 1 || y < 1)return 1;
changeb_g(p, x, y, 4);
cv::Mat color = cv::Mat(y, x, CV_8UC4);
memcpy(color.data, p, x * 4 * y);
stbi_image_free(p);

cv::Mat img;
cv::cvtColor(color, img, CV_BGRA2BGR);
cv::resize(img, img, cv::Size(224, 224));

cv::Mat inpBlob = cv::dnn::blobFromImage(img, 1.0, cv::Size(224, 224), cv::Scalar(104, 117, 123));

// Set the prepared object as the input blob of the network
net.setInput(inpBlob);


cv::Mat output = net.forward();
std::cout << output<<std::endl;

float result = 0.0;
result = output.at<float>(0);

if (result > 0)printf("mask\n");
else printf("nomask\n");

return 0;
}

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

結果は以下の通り。
10枚くらいためしましたが全部正解。

----------------------------------------------------
Attempting to upgrade batch norm layers using deprecated params: face_mask.caffemodel
Successfully upgraded batch norm layers using deprecated params.
[-0.98020822]
nomask

Attempting to upgrade batch norm layers using deprecated params: face_mask.caffemodel
Successfully upgraded batch norm layers using deprecated params.
[1.4664154]
mask
----------------------------------------------------

なんだぁマスク装着判定ってこんな簡単に作れるのか。
これはいい商売になるねぇ。



2020年6月11日木曜日

会社のパソコンの電源をリモートでいれる。

おじさんも最近テレワークしています。
家から会社のパソコンにアクセスしているのですが、たまにその会社のパソコンがハングして固まってしまいます。
その固まったパソコンを再起動するために会社にいかないといけないのです。

とってもめんどくさいので、スマートプラグを使用して家から会社のパソコンの電源をリモートでON/OFFできるようにしてみました。


1.機材の準備

broadlinkという会社から発売されているスマートプラグ、「eplug」というものを買います。

amazonで購入

この機材を買うとスマホからコンセントのON/OFFを行うことができます。
日本でもLinkJapanという会社から同じものが発売されています。
日本だと少し割高ですが、3000円くらいで買えるようです。
おじさんは秋葉原のヨドバシカメラで買ってきました。

このスマートプラグは同じアカウントでログインすれば複数のスマホから同じ機材を操作できます。
なれればとても便利なのですが、中国製だけあって、最初のうちはアプリがバグだらけでとても使いづらいです。
どうやらiphoneとAndroidでアクセスするサーバが違うらしく、両方の端末から使いたい場合、二回アカウントをつくらないといけないみたいです。

だいたいこういう製品は、ネットの接続が一時的に切れると通電もOFFになったりして実用的に使えないことが多いのですが、この商品はネットの接続が切れても通電状態をOFFにすることなく、現在の状態をそのまま保持します。
また、通電状態で停電になると、復帰時にも通電状態になるようです。
この辺はしっかりできてますね。

あと、コンセントにさすものは発火すると危ないので、PSEマークという認証を取らないといけないのですが、この会社の製品はきちんとPSEマークを取っているようです。

2.パソコンの設定

通常パソコンはコンセントに電源をさしただけでは動かない設定になっています。
大手メーカーのパソコンはデータセンターなどで停電時したときに自動で電源が入るように、BIOSや環境設定で、コンセントの電源ONと同時にOSがブートして起動するような設定ができます。僕の使っている、HP、Dell、Macどれもこの設定を行うことができました。
たぶんほかのパソコンでもできるものと思います。


3.アプリのダウンロードと設定

何種類かアプリがあって、どれを使えばよいのかよくわからないのですが、
iphoneは「ehome」、Androidは「home link」というアプリをダウンロードすれば、上記のスマートプラグをスマホからコントロールすることができるようになります。
アプリからデバイスを登録すればスマートプラグが使えるようになるのですが、なぜか2/3くらいの確率で登録が失敗します。
なので新しいデバイスを登録するときはいつも3回くらいデバイスのリセットボタンを押して登録をします。
先ほども書いたように、iphoneとAndroid両方の端末で使いたい場合、この作業を2回行わないといけないので、6回くらい?デバイスリセットを繰り返して両方のスマホに登録します。

4. Alexaの設定

これはやらなくてもよいのですが、alexaアプリをいれてスマートスピーカーの設定をすると、おうちで「アレクサ、PCの電源をいれて」というと、会社のスマートプラグのPCの電源が入るようになります。
子供の前でやると、子供が何回もPCを再起動して大変なことになります。


これでスマホからスマートプラグを操作して会社のパソコンの電源をON/OFFできるようになります。
ハマるポイントさえわかればとっても便利。

世の中の大部分の家電はコンセントにさしただけでは待機状態で電源がONにはなりません。この場合、同じメーカーから発売されている「eRemote」という商品を使えばリモコンコードを送信することができ、家電を操作することもできます。