MNISTバイナリファイルから画像とラベルを抽出してみたよ

やあ子供たち。機械学習やってるか。最近は猫も杓子も機械学習だよな。教師データで「トレーニング」をして覚えさせたら今度はその覚えさせたものを使って、「予測」させる。言い換えれば、「これはこう、これはこう、でこういうのはこう」とさんざん教えた挙句、いきなり教えてないものを持ってきて、「じゃこれは?」と聞くというか、そうやって、使うわけだけども。
でいきなり機械学習しよったって、何かいい教師データがないと実験すらできないよな。なのでそういう実験データとして、有名なものがいくつか用意されている。例えばアヤメの分類問題とかもあるけど、今回は、手書き文字画像のセット「MNIST」に焦点をあてた話だよ。
MNISTは、0から9までの数字を手書きで書いた画像と、それに対応する、正解の数字が、セットになった教師データと、「じゃこの画像は何の数字?」と、当てさせる(予測させる)ためのテストデータからなる、有名なサンプルデータなんだけど、本家のサイトで配布しているデータ形式が、画像もラベル(正解の数字のこと)も、独自のバイナリフォーマットになっていて、なんとも中身が見えにくいということになってる。おいおい、画像なら画像ファイルとしてみたいし、数字のラベルならテキストファイルで見たい!と思うのが人の常というものだろ?
●まずはC++コードの紹介なので今回はおじさんが、これらフォーマットを、画像ファイルや、テキストに、分離するための短いC++プログラムを作ったので紹介するぞ。

#define _CRT_SECURE_NO_WARNINGS

#include <iostream>
#include <vector>
#include <cstring>
using namespace std;

#define EXTRACT_IMAGES

// settings
char image_file[] = "<your path>\\train-images.idx3-ubyte";
char label_file[] = "<your path>\\train-labels.idx1-ubyte";
char extracted_image_file[] = "<your path>\\tak_%04d.bmp";
char extracted_label_file[] = "<your path>\\labels.txt";
//

template<class T>
void flip_endian(T& val)
{
	char* b = reinterpret_cast<char*>(&val);
	std::reverse(b, b+sizeof(T));
	return;
}
int main(int argc, char* argv[])
{
#ifdef EXTRACT_IMAGES
	FILE* fp = fopen(image_file, "rb");
#else
	FILE* fp = fopen(label_file, "rb");
#endif
	if (!fp)
	{
		cout << "failed to open file" << endl;
		return 0;
	}
	cout << "hi" << endl;
	unsigned int word;
	fread(&word, 1, 4, fp);
	fread(&word, 1, 4, fp);
	flip_endian(word);
	const int num_entry = word;
#ifdef EXTRACT_IMAGES
	// extract images
	fread(&word, 1, 4, fp);
	flip_endian(word);
	const int width = word;
	fread(&word, 1, 4, fp);
	flip_endian(word);
	const int height = word;
	for (int k = 0; k<num_entry*.01; ++k)
	{
		vector<unsigned char> buff;
		buff.reserve(height*width * 3);
		for (int i = 0; i < height; ++i)
		{
			vector<unsigned char> line;
			for (int j = 0; j < width; ++j)
			{
				unsigned char bt;
				fread(&bt, 1, 1, fp);
				line.push_back(i*8);// ←背景青グラデーション
				line.push_back(bt);
				line.push_back(bt);
			}// j
			reverse(line.begin(), line.end());
			buff.insert(buff.end(), line.begin(), line.end());
		}// i
		std::reverse(buff.begin(), buff.end());
		char ss[234];
		sprintf(ss, extracted_image_file, k);
		write_bmp(ss, width, height, 3, buff.data());
	}// k
#else
	// extract labels
	FILE* fp2 = fopen(extracted_label_file, "w");
	for (int k = 0; k<num_entry*.01; ++k)
	{
		unsigned char label;
		fread(&label, 1, 1, fp);
		flip_endian(label);
		fprintf(fp2, "%d\n", label);
	}// k
	fclose(fp2);
#endif
	if (fp)
	{
		fclose(fp);
	}
	getchar();
	return 0;
}

おっと、write_bmpは、ここからとってきてくれたまえ。(その中で使われているFourBytePaddingのコードもな。まそこの記事読んでくれな)そして上記プログラムからこれら関数を呼び出せるようにしてしまおう。無事にコンパイルを終えたなら、走らせてみよう。(VisualStudio2017でのコンパイルを確認しているよ。新規作成>プロジェクト>C++>空のプロジェクトを作成し、上記コードをmain.cppとして保存しそのままビルドしてみてくれ。)
●画像の抽出(画像化、可視化)さてさて結果の画像だ。
コード冒頭にある//settings以下に定義した、image_fileで指定された、yanさんのサイトからとってきたMNISTの画像バイナリファイル(例のオリジナルバイナリ形式のやつ)を指定して、なおかつ、extracted_image_filesに、抽出した各画像の出力ファイルの場所およびファイル名の書式を指定してくれよな。

●ラベルの抽出次にラベルファイルを抽出してみるぞ。このコード冒頭にある、「#define EXTRACT_IMAGES」という行をコメントアウトして再ビルドし、
コード冒頭にある//settings以下に定義した、label_fileで指定された、yanさんのサイトからとってきたMNISTのラベルバイナリファイル(例のオリジナルバイナリ形式のやつ)を指定して、なおかつ、extracted_label_filesに、抽出した各画像の出力ファイルの場所およびファイル名の書式を指定してくれよな。
実行すると、
ちなみにこの画像と対応するラベル列はlabel.txtにこのように出力されるよ。

5
0
4
1
9
2
1
3
1
4
3
5
3
6
1
7
2

こんな感じにうまく抽出できたなら、今日はこれでおしまいだよ。
チャオ!

力学的運動エネルギー(1/2)mv^2の導出

◆そもそもの動機というか目的
高校物理あるいは大学の教養の物理学で習った「仕事の定義」を、ニュートン運動方程式
m\frac{d^2}{dt^2}\mathbf{x}=\mathbf{F}

の右辺に適用してみようと思い、右辺に適用ということは左辺にも同じことをしてあげないといけないので、
\int_0^X\quad m\frac{d^2}{dt^2}\mathbf{x}\quad d\mathbf{x}=\int_0^X\quad \mathbf{F}\quad d\mathbf{x}

こうなりますと。ま言うまでもなく右辺は原点位置から、位置Xにまで質点mが移動した際に力Fによってなされた仕事を表しているわけだが、これを見ると、この左辺は一体、これは何か、もっと解り易い表記にできないかということでどうにか式をこねくりまわして、(というと言い方が悪いな、ま要するに素直に式の変形をしてね、)で最終的に、あ、これが高校物理で習った運動エネルギーの公式そのものではないかとなったのが46歳(と11カ月)の先日だったので、今日はそこに至るまでの過程を赤裸々に綴っていくので参考にしてほしい。 
そういうわけで右辺はもう意味がわかっているので置いておいて、これは専ら左辺をどう変形していくかという物語だよ。◆左辺の式の変形
 さ、この問題の左辺は、このままでは困ってしまうので、
\mathbf{v}=\frac{d\mathbf{x}}{dt}

とまずは置いてみよう。すると以下のように式は書き換えられる。
\int_0^X\quad m\frac{d}{dt}\mathbf{v}\quad d\mathbf{x}=\int_0^X\quad \mathbf{F}\quad d\mathbf{x}

さらに、積分記号のdxは上記二つ前の式より、
d\mathbf{x}=\mathbf{v}dt

と書けるな。すると以下のようになるね。
\int_0^T\quad m\frac{d}{dt}\mathbf{v}\quad \mathbf{v}dt=\int_0^X\quad \mathbf{F}\quad d\mathbf{x}

おっとxに関する積分をtに関する積分にしたので、積分範囲も変わってるから気を付けてほしい。で並び替えて以下のようになる。
m\int_0^T\quad \quad \mathbf{v}\frac{d}{dt}\mathbf{v}\quad dt=\int_0^X\quad \mathbf{F}\quad d\mathbf{x}

さてさて、この形にきたら思い出さなくてはいけないというか、気づきが必要だそれは、上記被積分項の中に出てくる、
\mathbf{v}\frac{d}{dt}\mathbf{v}

という表記は、vの2乗をtで微分した際に出てくる表記の半分だねと(わからない人は合成関数の微分について復習してくれ)。じゃそれを積分しようってんだから、積分した結果は
0.5*v^2だねと。以下のように書けるねと。
\frac{1}{2}m\left[\mathbf{v}^2\right]_0^T=\int_0^X\quad \mathbf{F}\quad d\mathbf{x}

じゃ、あとは積分定数を決定するべく、どこからどこまで積分したかったんだっけという操作で、時刻t=0で、v=0、時刻t=Tでv=Vとすれば、
\frac{1}{2}m\mathbf{V}^2=\int_0^X\quad \mathbf{F}\quad d\mathbf{x}

●変形した左辺式の解釈〜それはあの、「運動エネルギー」だった。
 ま、ただここで力Fについて大事な条件があるよ。上述の様に式変形をしてきた中で当然といえば当然の条件なのだが、それは、右辺の積分が計算可能な場合のみ、だ。つまり質点が移動する際の始点と終点のみで右辺の値が決まり、途中の経路やまして時刻によってその値が変化しない。位置xのみによって決まる。そのような力を保存力というよ。例えば重力や静電気力そして、質点に繋がれたばねの弾性力なんかも保存力だし、指で一定の力で押し続けるなんていうのも保存力だ(もっともそんなロボットみたいな指があればの話だが)。摩擦力や空気抵抗といったものは保存力ではないな。まそのような保存力Fを受け続けて、質点の速度がゼロからvになったとしたとき、質点mの力学的運動エネルギーは、.
「運動エネルギーは質点の質量mと、その最終的な速度vによってのみ決まり」
\frac{1}{2}m\mathbf{V}^2

と表せると、いうことが以上の考察をもって、解ります。
 でまあ数学的な言い方をすれば、運動方程式の両辺を経路積分すると、左辺に質点mの運動エネルギーを確認することができるねという感じだね。ま敢えて驚愕するとすれば、物体が経てきた経路には一切関係なく、物体の質量mと、速度vのみで、その物体の運動エネルギーは決まる。という事実の再確認だね。●思い返せば〜長い道のり
 思えば、僕が高校物理を習ったのはもう30年も前だけど、「仕事」の定義というものを教えられて、「運動エネルギー」の公式を教えられて、それらが同じになる「保存則」というものがあるんだと教えられただけで、またしかにそれを知っておりさえすればいろいろな問題が解けてしまうのだが、「何故?」という疑問すら、持てなかったなというのが、ま今回自分で式変形をしてみてその疑問を解けた今だからこそ、思うことかな。(ま教えられ方というか、疑問を持てなかった自分が残念だと言いたいだけ。) その公式を知って保存則を知って、ルールを知って、演習問題をたくさんやり訓練された僕の物理の偏差値はいつも70近くだった。大学に入って大学の低学年時代には量子力学が大好きになり水素原子の波動関数を紙と鉛筆さえあれば導出できてしまうようにもなり、それほどまでに物理が大好きだった僕でもこの運動エネルギーの公式の形については深く考えたことは一度もなかった。そのまま物理が得意だと思って46歳になってしまった。
 ちなみに僕は浪人をしてS台予備校に2年通ったわけだけど、そこで当時教えられた物理学というのは、それとは一線を画したもので、上記のような式変形も紹介してくれていたように思う。でも、それも、そもそも「何故?」という疑問が持てなかった当時の自分には馬の耳に念仏というわけだった。(当時、(今もかな?)高校物理で、運動エネルギーの式の導出というのは紹介されたりもしたが、それはFを一定とした場合のみに納得できる内容であり、Fが位置xによって変化する場合でもこの表記になるという証明がされずに、つまり本質がわかりようもない形でしか紹介されない。)

なので、疑問を持つというところが一番大事なのかなと思ったりもする。教えられたものをただ、「そういうものかね」と流してしまうのではなく、腑に落ちないものは、丸暗記を支持された公式に対してさえ、疑問を持ち続ける。そして自分なりに結論を出す。まそういことが大事なんじゃないかと。疑問を持っているから、答えを知りたいから、それを教えられたときに頭に入るものなんじゃないか。
 だから僕もそうはなれず、30年前には「そういうものかね、そう導出するのかふーん」で流してしまったわけだけど、46歳になった日々でま目の前の問題を突き詰めて考えている中、漸くその「何故?」が自然と湧いてきた、そのことは本当に自分でもうれしいと思っているんだ。
じゃ今日はこの辺で。チャオ!

BATファイル:引数なしを許さない

やあ子供たち。冬は若いうちはスキーやスノボや温泉などにばんばん出かけて、その時代、その季節でしか体験できない楽しみを謳歌しないとだめだぞ。そうしないと例えば冬だからって高いスタッドレスタイヤに履き替えたけど都心近辺のみ走っていたら雪なんてちっとも降らないのだから、せっかくの高いタイヤが無駄になってしまうよ。
さて今日も地味なBATファイルの話だよ。
引数%1を受け取ることが前提のBATファイルを書いてしまった場合に気をつけなきゃならないのが、想定外にも引数を与えられずにBATファイルのみで起動されてしまった場合の処理だ。
そういう場合は、例えばデフォルトがカレントディレクトリと勝手にされてしまって、予期せぬ動作をしてしまうことがある。まそういうことがあるので、引数%1が有効かどうかをチェックし、無効なのであれば何もせず、あるいはBATファイル本来の使い方を表示して、処理を終了させたいところだよな。
そんな時にお決まりの書き方となるのが以下のようなBATコードの書き方だ。

@echo off
if [%1]==[] goto usage

(ここにBATファイルの本来の処理内容を記述します)

:usage
@echo usage: 引数1が空です。ていうかこのBATファイルの使い方は云々。。
pause

はいはいはい。大事なんじゃないだろうかこれ。
まこのように、とりあえずBATファイルを実行してみれば使用方法が出てくるようになっていると、安心するし、助かることも多いのではないか。
では今日はこの辺で失礼するよ。

瞬時で終了してしまうので出力メッセージも読めない困ったコマンドプロンプトベースのアプリを許さない便利BAT

やあ子供たち。今年もいよいよ寒くなってきたけど元気でやっているか。
Windowsでよくありがちなのが、プログラム(.exe)を実行すると、黒画面(コマンドプロンプト画面ベースのアプリってこと)が出て、何やら出力文字列やエラーメッセージなどを表示しているっぽいのだけどそれが瞬時で終わってしかも黒画面も閉じてしまうので、何をやったのか、そして何を表示していたのかがさっぱりわからない!、なんていう経験したことはないだろうか。
そこで本日のお題ときたもんだ。題して「瞬時で終了してしまうので出力メッセージも読めない困ったコマンドプロンプトベースのアプリを許さない便利BAT」。さて実際にどんなものなのかを見てみようね。
それは以下のようなたった2行からなるBATファイルだよ。

%1
pause

使い方はとっても簡単。上述したようなプログラムの.exeをこのBATファイルの上にドロップするだけで、そのプログラムが起動し、かつ、終了間際に一時停止される。一時停止されるので、表示された文字はそのままで黒画面もそのまま閉じずに待っていてくれるというわけさ。

やーどうだこれ泣く子もだまるって感じじゃないかそれほどでもないかな。
たとえば、c:\windows\system32\ipconfig.exeをダブルクリックしてみてほしい。一見何もおきないように見えるがこれはコマンドプロンプト画面が出て、その中で何かメッセージを出力して、勝手に黒画面を閉じる、ということが瞬時に行われているのであり、何もおきてないわけじゃないんだ。
そこで今回の日記のBATファイルの便利さがさく裂する場面だ。このipconfig.exeを上記にて作成した、2行からなる、適当な名前をつけたBATファイルの上にドロップしてみてほしい。すると、ipconfig.exeが使い方を説明してくれている様子がわかるぞ。
ま例えばそんな使い方ができるぞ。

ま今日のネタは以上で終了だよ。チャオ!

python日記(配列など)

やあ子供たち。めっきり寒くなってきたけどpython勉強しているか。いつまでもC++にしがみ付いているとおじさんも仕事がなくなってしまうのではないかと本気で心配し始めた今日この頃だよ。さてみんなも勉強したことは自分で確認して、自分メモとして残しておきたいよな。おじさんもそう考えている一人だよ。そこで今日は以下のソースをメモだ。

# coding: utf-8

# ---配列の宣言---
a=[]
v=[1,2,3,4,5,6,7]

# ---配列の内容の構築---
for iv in v:
  a.append(iv*iv)

# ---配列内容の活用---
for x,y in zip(a[1:],a[:-1]):
  print(x),
  print("-"),
  print(y),
  print("="),
  print(x-y)

そして結果は以下のようになった。
(まこれは高校数学とかで一度はやる、隣り合う自然数の自乗どうしの差を順に並べてみると以外にも単なる奇数列になるという、懐かしい事実の証明だよね。)

4 - 1 = 3
9 - 4 = 5
16 - 9 = 7
25 - 16 = 9
36 - 25 = 11
49 - 36 = 13

まこうしてみると、今日は本当にいろいろなことを学んだことがわかるね。まずは文字のエンコーディング指定。続いて配列の宣言方法。空の配列も宣言できるぞ。次にfor文。for文のスコープはインデントのみによって判断されるのがpythonの特徴的なところだね。次に配列への要素の追加。続いてzip構文の使い方と、配列の範囲指定。そして最後はprint文の最後にカンマをつけることで改行が回避されること。
では今日はこの辺でお開きだよ。チャオ!

PowerPointでまっさらな空のページを作成する方法

やあ子供たち。寒くなってきたけど元気でやっているか。もうすぐ紅葉のシーズンだね。
PowerPointの左側のサムネイル表示のところで右クリックして「新しいスライド」を選択したとしても、「タップしてタイトルを追加」「タップしてテキストを追加」という邪魔なテキストボックスつきのページがデフォルトで作成されるので、いちいちこの不要なタイトルを削除してまっさらなページにしてから使う、ということをおじさんはいままでやってきたのだけど、そこにハックを見つけたので今回メモしておくぞ。


でそれはまあPowerPointを開いて、一番最初のスライドを挿入する際に、上の図にあるように画面上部リボンの中の、「新しいスライド」の下の小さな三角をクリックすると、いろいろな種類の空スライドが選択できるので、ここで、まっさらな、「空」と書いてあるやつを選択しよう。ひとたびそれをやれば、それ以降、PowerPointを終了するまでずっと、左のサムネイル画面で右クリックして「新しいスライド」とすると、もうあの邪魔なテキストボックスのないまっさらなページが作成できるようになるぞ。
まこれはPowerPointを開くたびにやらなくてはだめな操作ではあるが、それでもだいぶ楽になるよね。やったな、今日から「タップしてタイトルを追加」とは縁のない人生を送れるね。
チャオ!

TensorFlow日記その1

やあ子供たち。おじさんはTensorFlow(TF)というものを勉強してみたので今日はその内容のメモだよ。
でー、MSやんのWindowsな〜。ま今回いろいろ調べたつもりなんだが、まpythonはいいとして、じゃpythonでこれをしましょうといった場合の、「これ」が結局Windows環境には対応してなかったりあるいはWindowsだとセットアップが面倒だったり、で動いても本来の表示されなかったりするので、何かとWindowspythonやTFを勉強するには難しいな…(まWin10ならUbuntu環境もあるからそういうのでもいいのかも知れないが)と思ったので、勉強の環境としてはMacのコンソールでvimを使ってやってみた。vimは大学4年の自分、Fortranでプログラムを作っていて助教授も先輩もviしか知らない研究室にいた手前、文字通り体で覚えるしかなかったviの経験がそのまま活かせると思ったので今回TFを勉強する傍ら、改めていろいろ復習+αをしたのでそのメモもしておくぞ。
●大学4年の時分の自分ではそこまではいいよと食べず嫌いして調べなかった、今日知ったvimの便利コマンド集

  • カーソル移動
    • (ブロック単位のカーソル移動):eで前方移動、bでバック。
    • (行頭・行末に移動)0で、行頭に移動、$で行末に移動。
  • コピー&ペースト
    • (文字単位)vで編集モード→コピーしたい領域を選択、y(コピー)かd(カット)し、p/Pでペースト。
    • (行単位)yyで行コピー、またはddで行削除、したものを、ppでペースト

●TFの基本
さてさてTFの基本コードは以下だよ。TFは基本的に、種々のノードからなる構造を構築して、それをセッションで実行するという、「構造作成」と「セッション実行」という2段階の手続きを踏むことが基本とされる計算ライブラリのようだ。

import tensorflow as tf

#バージョンの表示
print (tf.__version__);

#ノード作成
node1 = tf.constant(3.0, dtype=tf.float32 )
node2 = tf.constant(4.0)

#ノード情報の表示
print("\n",node1,"\n",node2)

#セッションを実行し、ノードの内容を表示
session = tf.Session()
print(session.run([node1,node2]))
print(session.run([node1]))
print("-->",session.run(node2),"<--")

#足し算(足し算操作もノードなんだって)
node_adder = node1+node2 #or tf.add(node1,node2)
sum = session.run(node_adder)
print(session.run(node1),"+",session.run(node2),"=",session.run(node_adder))

#プレイスホルダー(数値インスタンスを実行時に指定)
#中括弧{}は、辞書引数とでも言うべきもの
node3 = tf.placeholder(tf.float32)
node4 = tf.placeholder(tf.float32)
val3 = session.run(node3,{node3:3.14})
val4 = session.run(node4,{node4:1.2})
print(val3,val4)
addr34 = node3 + node4
print(session.run(addr34,{node3:2.3, node4:1.5}))

上記のコードの出力は以下のようになったよ。

1.3.0

 Tensor("Const:0", shape=(), dtype=float32) 
 Tensor("Const_1:0", shape=(), dtype=float32)
[3.0, 4.0]
[3.0]
--> 4.0 <--
3.0 + 4.0 = 7.0
3.140000104904175 1.2000000476837158
3.8

まーここまではそうですかーといった内容だな。今日はここまで。チャオ!