フィルターを使って、開発速度を上げます。実際に仕事で使ったsort、cut、uniqについて説明します。
sort
テキストファイルをがCSVのようにフォーマットが決まっているものとして高度な並べ替えを実現します。
4列目でソート、4列目が同じ値の場合は3列目でソートする。
% sort -k 4,4 -k 3,3 profile.log
数値としてソートする。
% sort -n -k 4,4 -k 3,3 profile.log
4列目は数値、4列目が同じ値の場合は3列目は文字列としてソートする。
% sort -k 4,4n -k 3,3 profile.log
4列目は数値で逆順にソート、4列目が同じ値の場合は3列目は文字列としてソートする。
% sort -k 4,4nr -k 3,3 profile.log
数値で逆順にソートする。
% sort -r -n -k 4,4 -k 3,3 profile.log
同じ内容の行を出力しない。
% sort -u -r -n -k 4,4 -k 3,3 profile.log
‘:’をデリミタとする。
% sort -t: -u -r -n -k 4,4 -k 3,3 profile.log
ソートキーの先頭にある空白を無視する。
% sort -b -t: -u -r -n -k 4,4 -k 3,3 profile.log
man sortは、くじけずに読めますが、なかなか読む気になれません。
今の潤沢な計算機資源であれば、多くの場合に単純選択ソートでも、入力というか対象が小さい場合は一瞬で処理が完了してしまうので、上記を覚えるより実装した方が気が楽だったりします。RubyにもPythonにもsortが用意されています。
ですが、プロファイラーを使って、結果が出て、そこからさらにソートスクリプトやソートプログラムを作成するのも気が重いです。下記をメモとして取っておくと、仕事が早いと思います。
残念ながらMacではgprofが入っていないので、こちらを使います。
Python プロファイラ
下記のプログラムのプロファイルを取ります。
7層のネットワークを動かす 1
8665063 function calls (8397913 primitive calls) in 866.715 seconds
Ordered by: standard name
ncalls tottime percall cumtime percall filename:lineno(function)
7452/6575 0.090 0.000 0.395 0.000 :1009(_handle_fromlist)
596 0.002 0.000 0.002 0.000 :103(release)
374 0.000 0.000 0.000 0.000 :143(__init__)
374 0.001 0.000 0.006 0.000 :147(__enter__)
374 0.000 0.000 0.002 0.000 :151(__exit__)
596 0.003 0.000 0.004 0.000 :157(_get_module_lock)
372 0.001 0.000 0.001 0.000 :176(cb)
222 0.000 0.000 0.002 0.000 :194(_lock_unlock_module)
493/5 0.000 0.000 0.601 0.120 :211(_call_with_frames_removed)
3582 0.002 0.000 0.002 0.000 :222(_verbose_message)
6 0.000 0.000 0.000 0.000 :232(_requires_builtin_wrapper)
351 0.000 0.000 0.000 0.000 :307(__init__)
351 0.000 0.000 0.000 0.000 :311(__enter__)
351 0.001 0.000 0.002 0.000 :318(__exit__)
1402 0.000 0.000 0.000 0.000 :321()
298 0.001 0.000 0.001 0.000 :35(_new_module)
368 0.001 0.000 0.001 0.000 :369(__init__)
643 0.001 0.000 0.005 0.000 :403(cached)
1147 0.002 0.000 0.002 0.000 :416(parent)
351 0.000 0.000 0.000 0.000 :424(has_location)
10 0.000 0.000 0.000 0.000 :433(spec_from_loader)
351 0.003 0.000 0.010 0.000 :504(_init_module_attrs)
351/349 0.001 0.000 0.138 0.000 :576(module_from_spec)
372 0.001 0.000 0.001 0.000 :58(__init__)
4 0.000 0.000 0.000 0.000 :634(_load_backward_compatible)
355/5 0.003 0.000 0.603 0.121 :663(_load_unlocked)
364 0.000 0.000 0.001 0.000 :719(find_spec)
6 0.000 0.000 0.000 0.000 :740(create_module)
6 0.000 0.000 0.000 0.000 :748(exec_module)
6 0.000 0.000 0.000 0.000 :765(is_package)
596 0.002 0.000 0.002 0.000 :78(acquire)
358 0.000 0.000 0.001 0.000 :792(find_spec)
1088 0.000 0.000 0.001 0.000 :855(__enter__)
1088 0.001 0.000 0.001 0.000 :859(__exit__)
8 0.000 0.000 0.000 0.000 :873(_find_spec_legacy)
364 0.004 0.000 0.055 0.000 :882(_find_spec)
1 0.000 0.000 0.000 0.000 :929(_sanity_check)
373/4 0.002 0.000 0.605 0.151 :948(_find_and_load_unlocked)
374/4 0.003 0.000 0.605 0.151 :978(_find_and_load)
1 0.000 0.000 0.000 0.000 :994(_gcd_import)
47 0.000 0.000 0.000 0.000 :1029(__init__)
47/46 0.000 0.000 0.125 0.003 :1040(create_module)
47 0.000 0.000 0.000 0.000 :1048(exec_module)
上記を1列目でソートします。プロファイルなので、数値でソートして、大きい方が上に来るようにします。
% sort -k 1,1nr profile.log | less
上記を2列目でソートします。
% sort -k 2,2nr profile.log | less
結果を示します。
47416 184.843 0.004 184.843 0.004 {built-in method numpy.core.multiarray.dot}
1600 146.655 0.092 148.888 0.093 util.py:71(col2im)
41044 132.201 0.003 132.201 0.003 {method 'reshape' of 'numpy.ndarray' objects}
4002 74.030 0.018 238.956 0.060 util.py:39(im2col)
800 49.750 0.062 49.775 0.062 optimizer.py:110(update)
66839 45.659 0.001 45.659 0.001 {method 'reduce' of 'numpy.ufunc' objects}
2001 38.843 0.019 268.617 0.134 layers.py:257(forward)
37552 37.820 0.001 37.820 0.001 {method 'copy' of 'numpy.ndarray' objects}
6946 26.139 0.004 26.139 0.004 {built-in method numpy.core.multiarray.zeros}
4002 21.027 0.005 45.638 0.011 layers.py:27(forward)
2001 13.084 0.007 166.618 0.083 layers.py:308(forward)
2001 13.083 0.007 515.776 0.258 simple_convnet.py:75(predict)
2402 12.849 0.005 12.849 0.005 {method 'argmax' of 'numpy.ndarray' objects}
207643 12.115 0.000 12.115 0.000 {built-in method numpy.core.multiarray.array}
800 12.057 0.015 490.175 0.613 simple_convnet.py:144(gradient)
800 9.392 0.012 59.825 0.075 layers.py:327(backward)
1600 6.273 0.004 6.273 0.004 layers.py:36(backward)
2941 5.846 0.002 5.846 0.002 {method 'flatten' of 'numpy.ndarray' objects}
220 4.057 0.018 4.057 0.018 {imread}
4002 3.345 0.001 21.819 0.005 layers.py:73(forward)
3 2.164 0.721 2.164 0.721 {built-in method matplotlib._png.write_png}
1600 1.265 0.001 17.803 0.011 layers.py:87(backward)
4147 1.085 0.000 1.085 0.000 {built-in method numpy.core.multiarray.arange}
436684/360892 0.331 0.000 0.567 0.000 artist.py:230(stale)
9975 0.312 0.000 2.125 0.000 lines.py:290(__init__)
428120 0.307 0.000 0.413 0.000 __init__.py:930(__getitem__)
102246 0.304 0.000 0.304 0.000 transforms.py:88(__init__)
162709 0.244 0.000 0.244 0.000 {method 'items' of 'dict' objects}
23500 0.227 0.000 0.343 0.000 artist.py:87(__init__)
52080 0.205 0.000 45.791 0.001 fromnumeric.py:64(_wrapreduction)
84264/2004 0.175 0.000 0.354 0.000 arrayprint.py:694(recurser)
800 0.172 0.000 847.861 1.060 trainer.py:152(train_step)
4002 0.161 0.000 25.454 0.006 arraypad.py:993(pad)
8004 0.155 0.000 0.205 0.000 stride_tricks.py:115(_broadcast_to)
70409/24013 0.147 0.000 0.233 0.000 transforms.py:142(_invalidate_internal)
2 0.145 0.072 17.377 8.688 trainer.py:65(get_image_and_label)
348058 0.140 0.000 0.203 0.000 {built-in method builtins.isinstance}
220 0.128 0.001 0.128 0.001 {cvtColor}
47/46 0.122 0.003 0.125 0.003 {built-in method _imp.create_dynamic}
4444 0.120 0.000 0.541 0.000 lines.py:649(recache)
3323 0.108 0.000 4.504 0.001 axis.py:74(__init__)
1187 0.106 0.000 0.106 0.000 {method 'set_text' of 'matplotlib.ft2font.FT2Font' objects}
429000 0.106 0.000 0.106 0.000 {function RcParams.__getitem__ at 0x11529f158}
1 0.104 0.104 865.854 865.854 trainer.py:197(train)
10421 0.100 0.000 0.478 0.000 font_manager.py:668(__init__)
3 0.093 0.031 0.093 0.031 {method 'randn' of 'mtrand.RandomState' objects}
37864 0.093 0.000 0.107 0.000 transforms.py:169(set_children)
129112 0.092 0.000 0.102 0.000 {built-in method builtins.getattr}
本の中で出てきたcol2imとim2colと、numpyのdotとreshapeで大半を占めていることがわかります。これで、もう後はsqueezeやるしかなさそう、みたいな判断になります。自分の実装に自信を持って良さそう、という判断にもなります。
上記を3列目でソートします。
% sort -k 3,3nr profile.log | less
ある程度の傾向が見えたら、リダイレクトしておいて、簡単なレポートをまとめておくと、積み重ねていくことができます。何も資料に残していないと、1年後に全部最初からやり直しということになりかねません。
cut
上述のプロファイルの結果から2列目を取り出すには、下記のようにします。
sed -e 's/^ *//g; s/ * / /g' profile.log | cut -d ' ' -f 2
オプション
cutはデフォルトではdelimiterはタブです。
cutのオプションは少ないですが、ここでは2つだけ説明します。
-d | delimiterを指定する。 |
-f | 列を指定する。 |
自分で仕様を決めてcutを利用する
current directoryにプログラム実行の度に下記の様な名前のファイルが生成されるとします。
network_parameters.pkl.1
network_parameters.pkl.2
network_parameters.pkl.3
network_parameters.pkl.4
network_parameters.pkl.5
network_parameters.pkl.6
network_parameters.pkl.7
network_parameters.pkl.8
network_parameters.pkl.9
network_parameters.pkl.10
一番新しいファイルの番号が欲しい場合は下記で取得できます。
% \ls -1 network_parameters.pkl.* | tail -n 1 | cut -d . -f 3
-dでdelimiterを指定します。ここでは.です。-fで何番目の要素を取り出すかを指定します。
cutは、delimiterの連続をそれぞれ分割して認識するために複数のスペースや複数のタブで見やすくなっている場合にsedを使う必要が出てきます。上記のように自分で仕様を決めて(見やすい必要はどこにもない)、cutで切り出すという方法は便利です。しかし、filterそれぞれの仕様を調べるよりも正規表現を使う人の方が多い気がします。
cutでdelimiterに正規表現が使えると幅が広がるのですが。
uniq
行ごとに比較して、連続した行が同一の場合に1行にします。連続した行が同一の場合にその行だけを表示させることもできます。
ソートして、重複した行を消したファイルを取得する場合は、下記となります。
% sort sample_file | uniq > output
広告
IT開発関連書とビジネス書が豊富な翔泳社の通販『SEshop』さくらのレンタルサーバ
ムームードメイン
Oisix(おいしっくす)
らでぃっしゅぼーや
珈琲きゃろっと
エプソムソルト