VSCodeのMarkdown Preview Enhanced の色設定

  • コマンドパレットにて(Alt-x), Markdown Preview Enhanced: Customize CSSにて設定ファイルを開く。
  • 設定ファイルに下記を追記する。
.markdown-preview.markdown-preview {
  // modify your style here
  // eg: background-color: blue;  
  background-color: #222;
  color: white;
  h1, h2, h3, h4, h5, h6 {
    color: wheat;
  }
}

gifアニメーション(convert)

やりたいこと

評価結果などをアニメーション表示したい時がある。リアルタイムなグラフのプロットならGnuplotでも良いけれど,ゲームプレイ画像などを表示したい場合には画像からアニメーションを作れると良い。

方法

  1. "連番"の画像ファイルを用意する。注意は0埋めしてファイルの順序通りに読み込まれるようにする。つまり,1〜99までの画像がある場合,01〜99にする。pythonだとformat({:03})みたいな記述するだけ。
  2. convert(ImageMagic)でgif化する。注意はLayer optimizeしないとフィアルサイズが大きくなること。loop=0指定で無限ループするgifになる。delayはmilli secオーダのアニメーション時間間隔。

サンプル

  1. 連番画像ファイルを生成する。ここでは少しずつ移動する(位相が進む)sin波のアニメーション用に,./fig以下にfig_0001.png - fig_0100.pngを生成している。 Matplotlibでアニメーションを検索すると,Matplotlibの中だけでgif生成までやる例があるけれど,画像サイズの調整やリサイズなども後から出来るので,このように一度連番画像を生成するのが良い気がする。 注意点として,20枚以上を一度に描画しようとするとエラーメッセージが出されるため,plt.close()にて毎回閉じる。
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation

fig = plt.figure()
x = np.arange(0, 10, 0.1)

fig_dir = './fig/'

for i in range(1,101):
    plt.clf()
    y = np.sin(x - i/10.0)
    plt.plot(x, y, "r")
    plt.savefig(fig_dir+'fig_{:04}'.format(i)+'.png')
    plt.close()
  1. convertする。
$ cd ./fig
$ convert -layers optimize -loop 0 -delay 10 fig_*.png animation.gif

f:id:nobUnaga:20180607081215g:plain

機械学習の前処理でのデータの正規化/標準化

前処理の目的

  • 体重と身長など複数の異なる特徴量を生で扱ってしまうと、出力に対して使いやすい(大まかにFitしやすい)方を優先してしまう問題がある。

内容

  • 問題解決の単純な方法は異なるカテゴリのデータを同じ範囲のデータに変換する。
  • 変換は入力だけでなく、出力も変換する。
  • また、一般的には各特徴量毎に変換する。ただし、画像のように2次元(行/列)ではあるが、意味が全ての次元で等しい場合には、全体で変換することも考えられる。

具体的な変換方法

  • 一般的には正規化と標準化(ZScore化とも言われる)が良く使われる。
  • 正規化は、変換後のデータの最小値, 最大値を0, 1変換する。背景にデータが一様分布に従う場合を想定している。
  • 標準化は、平均0, 標準偏差1に変換する。背景にデータが正規化分布に従うことを想定している。

実践

  • 2次元データ(n_batch, n_feature)のデータであればSklearnのpreprocessingモジュールで簡単に変換できる。
  • 3次元データ、例えば(n_batch, n_object_type, n_object_feature)のような場合にはSklearnが使えない。その場合は下記のような関数を使う。
import sys
import numpy as np
import logging

logger = logging.getLogger(__name__)


class Scaler():
    '''
    Sklearn-API like simple scaler for data preprocessing.
    '''
    def __init__(self, mode):
        if not(mode in ['maxmin', 'standard']):
            logger.error("The mode must be 'maxmin' or 'standard'.")
            sys.exit(1)
        self.mode = mode
        self.maxs = None
        self.mins = None
        self.means = None
        self.stds = None

    def fit(self, data, axis=0):
        '''
        Args:
          data: Ndim numpy array .
          axis: scaling axis, usually batch dimension.
        '''
        if self.mode == 'maxmin':
            self.maxs = data.max(axis=axis)
            self.mins = data.min(axis=axis)
        elif self.mode == 'standard':
            self.means = data.mean(axis=axis)
            self.stds = data.std(axis=axis)
        return

    def transform(self, data):
        if self.mode == 'maxmin':
            d = (data - self.mins)/(self.maxs - self.mins)
        elif self.mode == 'standard':
            d = (data - self.means)/(self.stds)
        return d

    def fit_transform(self, data, axis=0):
        self.fit(data, axis)
        d = self.transform(data)
        return d

    def inverse_transform(self, data):
        if self.mode == 'maxmin':
            d = data*(self.maxs - self.mins) + self.mins
        elif self.mode == 'standard':
            d = (data - self.means)/(self.stds)
            d = data*self.stds + self.means
        return d        

    
if __name__ == '__main__':
    d0 = np.random.randn(100,4,2)

    s = Scaler('maxmin')
    s.fit(d0)
    d1 = s.transform(d0)
    d2 = s.inverse_transform(d1)

    s = Scaler('standard')
    s.fit(d0)
    d1 = s.transform(d0)
    d2 = s.inverse_transform(d1)

ArgparserとConfigparser

Deep Learning関連のプログラムを試していると、やたらと設定パラメタが多い。 これまではargparseを使ってきたけど、コードが煩雑になるのでconfigファイルの扱い方を調べてみた。

argparse

  • まずはargparseの基本的な使い方。
import argparse

parser = argparse.ArgumentParser()
parser.add_argument('--hoge', action='store', type=int, default=0)
args = parser.parse_args()

print('hoge:', args.hoge)  # python3 xxx.py --map 2 #=> 'hoge: 2'

ConfigParser

  • 次のようなconfig.iniファイルがあるとする。
  • 基本的にはセクションとパラメタ名を書いていくだけ。
  • 注意点として,値がブランクのものは,hoge=Noneではなく,'='を書かずにhogeだけ。
# config.ini
[model] ; Section名
type = MLP
input_size = 81
output_size = 81
hidden_sizes = 64,32

[learning]
data_file = /home/hoge/project/data/xxx.csv
train_ratio = 0.8
n_epoch = 30
lr = 0.0001
hoge # Noneの場合は'='を書かない
  • 使う側の例
import configparser

if __name__ == '__main__':
    config = configparser.ConfigParser()
    config.read('./config.ini')
    # Parameters
    train_ratio = config.getfloat('learning', 'train_ratio')
    lr = config.getfloat('learning', 'lr')
    n_epoch = config.getint('learning', 'n_epoch')
    file_path = config.get('learning', 'data_file')
    
    # Make model and set optimizer
    if config.get('model','type') == 'MLP':
        input_size = config.getint('model', 'input_size')
        output_size = config.getint('model', 'output_size')
        hidden_sizes = list(map(int, config.get('model', 'hidden_sizes').split(',')))
        model = models.MLP(input_size, output_size, hidden_sizes)
...

pythonでのログ(logging)

ロガーは名前で管理される。逆に、同じ名前のロガーは同じものとして扱われる。 それを利用するために、モジュール側ではモジュール名(name)をロガーの名前にしておいて、ユーザ側はモジュールの名前を指定することで当該のロガーを取得して、個別に設定することが可能。 例えば、hoge/foo/bar.pyモジュールの名前はhoge.foo.barになる。

ライブラリ側

  • モジュール(ファイル)の先頭でloggerを生成して、適時出力するだけ。
  • configはトップから渡されることを想定する。
import logging

logger = logging.getLogger(__name__)  # __name__はモジュール名

class Hoge:
    def __init__(self):
        logger.info('A Hoge instance is generated.')

    def foo(self):
        logger.debug('Hoge/foo is called.')

ユーザ側

  • ライブラリ側のloggerのconfigを与える。
  • configにはログのレベル、フォーマット、出力先ファイルなどを設定。デフォルではlogging.WARNINGが設定されている。
  • ロガー毎に指定するなら、package.moduleでモジュール毎にロガーを取得しても良いが、プログラムとして一括で設定する場合には、rootロガーに設定すれば良い。ルートロガーはgetLoger("")で取得可能。
import logging

logging.basicConfig()  # confiを設定
# package/module.pyのロガーをDEBUGで動かす
logging.getLogger('package_name.module_name').setLevel(level=logging.DEBUG)
# 一括して設定したい場合にはrootロガーに設定する。
logging.getLoger("").setLevel(logging.DEBUG)

config

  • formatterのメタ文字は、asctime(ASCII時刻)、filename(ファイル名), funcName(関数名)、module(モジュール名)、message(メッセージ)
# レベル設定
logging.setLevel(logging.DEBUG)
# 出力フォーマット
formatter = logging.Formatter('%(levelname)s:(%(module)s/%(name)s/%(lineno)d:"%(message)s"')
# ファイル名設定
fh = logging.FileHandler('hoge.log')
fh.setFormatter(formatter)
logging.addHandler(fh)
# 標準出力
sh = logging.StreamHandler()
sh.setFormatter(formatter)
logging.addHandler(sh)

Pythonでのインターフェイス(ABCモジュール)

  • pythonインターフェイスのようなことをやりたい場合にはABCモジュールというのが使えるようだ。
  • でも、あんまり使っている印象はなく、raise NotImplementedErrorで簡易的に代用している場合が多いようだ。

ABCモジュールを使わない場合

  • 簡易的にraise NotImplemntedError例外を出す。
  • インスタンスの生成自体はできてしまう。
  • エラーが発生するのはf()の実行時。
class Parent:
    def f(self):
        raise NotImplementedError

class Child(Parent):
    def __init__(self):
        super(Child, self).__init__()
        pass

if __name__ == '__main__':
    c = Child()   # fを実装していなくても生成できる
    c.f()             # これは当然エラー

ABCモジュールを使う場合

  • 使わない場合との違いは、f()を実装しないとインスタンスの生成時点でエラーが発生する。
from abc import ABCMeta, abstractmethod

class Parent(metaclass=ABCMeta):
    @abstractmethod
    def f(self):
        raise NotImplementedError

class Child(Parent):
    def __init__(self):
        super(Child, self).__init__()
        pass

if __name__ == '__main__':
    c = Child()  # エラー発生

pythonのパッケージとimport

パッケージの構成

  • リポジトリ名とパッケージ名は同じになる(ことが多い)。パッケージは小文字で、アンダースコアも(なるべく)使わず。モジュールは小文字でスネーク。(クラスはキャメルケース)
  • 基本的にはtestはpackageの各ディレクトリ、ファイルと対応する。よって、1モジュールに1テストファイル。
 package(repo)
    - README.md
    - LICENSE
    - setup.py
    - docs
    - examples
    - test
    - package
        - __init__.py
        - subpakcage
            - __init__.py
            - hoge.py
            - foo.py
        - hoge.py
        - foo.py

importの書き方

  • 相対importは基本的に使わずに絶対importを使い、トップパッケージから書く。
  • 例えば、pakcage/subpackage/init.pyの書き方は下記。
from pakcage.subpackage.hoge import xxx # xxxはimport対象
from package.subpackage.foo import yyy
  • package/hoge.pyは例えば下記。
from pakcage.foo import xxx
from package.subpackage.foo yyy

注意事項

  • この絶対importの書き方だと各モジュールを開発している時にそのモジュールのディレクトリで実行しても実行はできない。