のんびりしているエンジニアの日記

ソフトウェアなどのエンジニア的な何かを書きます。

PyTorchの研究開発を加速する「pytorch-pfn-extras」を紹介します

皆さんこんにちは
お元気ですか。ついにクーラーが必要になってきました。
電気代が心配ですがなんとかなるでしょう。

本日はPyTorchの研究開発を加速する「pytorch-pfn-extras」を紹介します。

pytorch-pfn-extras

pytorch-pfn-extrasとは

PyTorchを使った研究開発の促進のために開発されているライブラリです。
こちらの開発元はChainerを開発していたPreferred Networks社によるものです。

github.com

Chainerの頃にはあったTrainerに似ている構成(厳密にはクラス構成が異なる)やIgnite連携が用意されており、便利に使えるのでは?と思っています。

インストール

pip install pytorch-pfn-extras

その他igniteのモジュール利用する場合は追加でライブラリが必要です(インストール方法参照)

MNISTサンプル

実装はこちらを参考にしてください、必要なポイントを解説します。
github.com

ニューラルネットワークの定義

ニューラルネットワークの定義はPyTorchの実装とほぼ同じです。
しかし、一つ違う点として、LazyConv2DやLazyLinearと呼ばれるモジュールをppeは独自実装しています。
PyTorchのConv2DやLinearの場合、入力するチャネルや次元数の指定が必要ですが、Lazy-の場合はその指定を省けます。

class Net(nn.Module):
    def __init__(self, lazy):
        super().__init__()
        if lazy:
            self.conv1 = ppe.nn.LazyConv2d(None, 20, 5, 1)
            self.conv2 = ppe.nn.LazyConv2d(None, 50, 5, 1)
            self.fc1 = ppe.nn.LazyLinear(None, 500)
            self.fc2 = ppe.nn.LazyLinear(None, 10)
        else:
            self.conv1 = nn.Conv2d(1, 20, 5, 1)
            self.conv2 = nn.Conv2d(20, 50, 5, 1)
            self.fc1 = nn.Linear(4*4*50, 500)
            self.fc2 = nn.Linear(500, 10)

学習部

ExtensionsManagerと呼ばれるクラスに拡張機能を管理するものがあります。
このManagerに設定することで、いつ、学習を止めるか、レポーティングを行うかなど、設定が可能です。
また、共通的に書かないといけないiteration回数を止める処理などを書く必要がほぼなくなります。

Chainer時代のTrainerだと、学習のメソッドを拡張するなりしなければなりませんが昨今の学習時にいじる系統(MixUpなど)だと不便になってくると思っていました。
train関数に学習中の処理も実装できるのでより一層、研究向きとしては便利になった印象です。

    manager = ppe.training.ExtensionsManager(
        model, optimizer, args.epochs,
        extensions=my_extensions,
        iters_per_epoch=len(train_loader),
        stop_trigger=trigger)

def train(manager, args, model, device, train_loader, optimizer):
    while not manager.stop_trigger:
        model.train()
        for batch_idx, (data, target) in enumerate(train_loader):
            with manager.run_iteration():
                data, target = data.to(device), target.to(device)
                optimizer.zero_grad()
                output = model(data)
                loss = F.nll_loss(output, target)
                ppe.reporting.report({'train/loss': loss.item()})
                loss.backward()
                optimizer.step()

拡張機能

extensionsのモジュールでは指定すれば様々なことが可能です。
このあたりの名前はChainerを継承しているので、元Chainer Userとしてはわかりやすい印象。
重みのスナップショット、統計情報の計算、ログの記録などを可能としています。
個人的にMLFlowあたりと連携してくれるととても嬉しいのですが、対応していただける日は来るのだろうか(「作って」みたいな要望が来そうだ‥)。

    my_extensions = [
        extensions.LogReport(),
        extensions.ProgressBar(),
        extensions.observe_lr(optimizer=optimizer),
        extensions.ParameterStatistics(model, prefix='model'),
        extensions.VariableStatisticsPlot(model),
        extensions.Evaluator(
            test_loader, model,
            eval_func=lambda data, target:
                test(args, model, device, data, target),
            progress_bar=True),
        extensions.PlotReport(
            ['train/loss', 'val/loss'], 'epoch', filename='loss.png'),
        extensions.PrintReport(['epoch', 'iteration',
                                'train/loss', 'lr', 'model/fc2.bias/grad/min',
                                'val/loss', 'val/acc']),
        extensions.snapshot(),
    ]

最後に

PyTorchの便利モジュールが存在すること。
また、色々な拡張機能により、Kaggleの実装など、よりシンプルにかけそうだといった印象で実利用時(これから)に期待を持っています。
研究開発がメインな身としてはより開発が進むと嬉しいと感じています。

Bengali.AIの解説をYoutubeでやってみました

皆さんこんにちは
お元気ですか。動画編集処理スキルがなさすぎて非常に困惑しております。

さて少し前のコンペティションになりますが、Bengali.AIのコンペティションの解説をYoutubeでやってみました。
※Bengali.AIのコンペのページはこちらです。

www.kaggle.com

コロナで集まって反省会するのも難しいご時世ですので、このYoutubeの流れに乗ってみるのもありかなと思ってはじめてみました。
堅苦しく集まって見るよりも、なにかの暇つぶしのお供ぐらいで見てもらえればと思います。

Bengaliはベンガル語の手書き文字画像から3種からなるベンガル語の構成要素を当てるコンペです。
ベンガル語は3つの構成要素から成り立ち、組み合わせで文字が完成します。
学習には存在しない文字があり、工夫しがいのあるコンペだったように思えます。(おかげで、Shake upが激しいコンペの一つになりました)

SlideshareYoutubeは次の通りです。

Slideshare

www.slideshare.net

Youtube
www.youtube.com

気が向いたら他のコンペもやりますので、要望があればぜひ、教えて下さい。(小麦は多分、やります。Tweet?知らない子ですね)
では。また、お会いしましょう。

CMakeを使ってビルドのみしている人のためのCMake入門

皆さんこんにちは
お元気ですか。ついに自宅のパスタがなくなりそうです。

さて、本日のテーマはcmakeです。
大半の人のcmake、実はこんな感じなのではないでしょうか。

  • インストール時にお世話になる(おまじない)けど、何もわからない(文字通り)
  • 仮に見たとしても何をやっているのかわからない。

私もある程度、これらに該当する人なわけです。
しかし、このおまじない系はまると長時間はまってとても困るといった問題がありました。
ということで改めてcmakeをきちんと勉強しておこうと思った次第です。

CMake

CMakeが何かはWikipediaにも書いてあります。

CMakeはコンパイラに依存しないビルド自動化のためのフリーソフトウェアであり、様々なオペレーティングシステムで動作させることができる。CMakeは階層化ディレクトリや複数のライブラリを利用するアプリケーションをサポートするよう設計されている。

環境によってもコンパイル方法(g++/clangなど)が異なるため、それぞれで設定を書く必要があります。
また、C++特有の依存関係を記述していくのは大変です。
それらを一括してCMakeは管理してくれます。

インストール

パッケージ管理ソフト(apt-get/homebrew/yum)でインストール可能です。

Ubuntu
sudo apt-get install cmake
Mac(Homebrew)
brew install cmake
CentOS
sudo  yum install cmake

CMakeを使ってC++コンパイルを行ってみる。

構成の準備

次のサイトのサンプルを参考にします。
derekmolloy.ie

helloworld.cpp/CMakeList.txtは同じディレクトリに配置してください。

1. helloworld.cpp

#include<iostream>

int main(int argc, char *argv[]){
   std::cout << "Hello World!" << std::endl;
   return 0;
}

プログラムはただの「Hello World!」出力用のプログラムです。

2. CMakeLists.txt

cmake_minimum_required(VERSION 2.8.9)
project (hello)
add_executable(hello helloworld.cpp)

cmake_minimum_requiredはcmakeの最低要求のバージョン
projectはプロジェクト名を示します。(挙動には関係ありません。)
今回の場合、add_executableはhelloworld.cppからhelloのファイルを作ります。

CMakeを試す

試してみましょう、次のコマンドで実行します。

mkdir build && cd build
cmake ..

buildディレクトリ作成もおまじないのようにさらっと記述されています。
これは、cmakeした結果のファイルが生成されるので、それ用です。
構成管理ツールでの管理を考えた時に、buildディレクトリを無視すればよいだけなので、管理しやすそうです。

cmakeが完了するとCMakeCache.txtなど、様々なファイルが生成されます。
これらはmakeを使う時に必要なファイル群です。

最後にmakeを実行すれば、実行ファイルの生成がされます。
後は実行ファイルをそのまま実行できます。

その他CMakeについて

CMakeでよく利用されるOption

特によく利用されるOptionは-Dです。
cmakeで環境を示す変数を指定しますが、-D Optionを付与することで、それよりも優先させることができます。
複数の環境を利用していると、デフォルトよりも特定の環境を指定したいこともあります。

その場合に-D Optionを利用して強制的に書き換えることもあります。

どんな変数が設定できるのかは「cmake .. -L」を実行すれば、確認可能です。

CMakeで作成されるMakefileのコマンド

CMakeで作成されるMakefileでよく利用されるコマンドをここでは紹介します
CMakeで作られたMakefileのおまじないの一種として「make -j8」やら「make install」といったコマンドがあります。

「make」では、ソースコードのビルドが行われます。殆どの場合はC++コンパイルですね。
また、「make install」を行うことで、規定のディレクトリに配置されます。これによって外部からでも呼び出し可能になります。

途中までのビルド結果を消したい場合は「make clean」を利用すれば良いでしょう。

ちなみに、よく使うOption「make -j」は並列数を指定できます。許せる限りのCPUコア数を数値として入れると良いでしょう。
ただし、コア数に対してジョブが多すぎると逆効果になるため、慎重に。(一度メモリ不足か何かで誤ってえらい目にあったことがあります)

最後に

おまじない(※僕だけかもしれない)を真面目に解説してみました。
一度はまったときにあれっと思うので、このようなことを勉強しておくのも時には必要でしょう。

その場合にぜひ、見てほしいと思います。

NGBoostを使ってみた

皆さんこんにちは。
お元気ですか。自粛ムードはまだまだ続きそうですね。

本日は少し前の発表された勾配ブースティングの手法NGBoostを紹介しようと思います。

NGBoostについて

NGBoostとは?

NGBoostは予測の不確実性をも推定する勾配ブースティングのアルゴリズムです。
従来までは、例えば温度を推定する場合、30度しか出ませんでした。
しかし、このNGBoostでは、どの程度30度らしいかも推定できます。
Kaggleよりも業務でフォールバックの機能として使えるので、便利で使い方の幅も感じるところです。

github.com

インストール

他のライブラリ同様、pipコマンド一発でインストールできます。

pip install https://github.com/stanfordmlgroup/ngboost.git

使い方

分類

from ngboost import NGBClassifier

from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.metrics import mean_squared_error
from ngboost.distns import k_categorical

X, Y = load_iris(True)
X_train, X_test, Y_train, Y_test = train_test_split(X, Y, test_size=0.2)

ngb = NGBClassifier(Dist=k_categorical(3)).fit(X_train, Y_train)
Y_preds = ngb.predict(X_test)
Y_dists = ngb.pred_dist(X_test)

Distの引数にk_categoricalを与える必要があります。
このk_categorialには、分類数と同じ値を与えることが必要です。
Y_distsで各クラスの確率値を獲得できます。

回帰

from ngboost import NGBRegressor

from sklearn.datasets import load_boston
from sklearn.model_selection import train_test_split
from sklearn.metrics import mean_squared_error

X, Y = load_boston(True)
X_train, X_test, Y_train, Y_test = train_test_split(X, Y, test_size=0.2)

ngb = NGBRegressor().fit(X_train, Y_train)
Y_preds = ngb.predict(X_test)
Y_dists = ngb.pred_dist(X_test)

# test Mean Squared Error
test_MSE = mean_squared_error(Y_preds, Y_test)
print('Test MSE', test_MSE)

# test Negative Log Likelihood
test_NLL = -Y_dists.logpdf(Y_test).mean()
print('Test NLL', test_NLL)

こちらもサンプル通りです。
NGBRegressorには特に引数を与えなくとも、実行ができます。
分類と同様、pred_distメソッドにより分布を計算できます。

最後に

NGBoostをとりあえず使ってみました。
ただ、使ったばかりもあり、性能や精度の比較はまだ検討できていないです。
そのため、有用であればコンペでも使いたいと思っています。

Pythonの環境を管理・再現する

皆さんこんにちは
お元気ですか。3月末というのに、雪積もっていてすごかったです。

さて、本日はPythonでの環境の管理方法を紹介します。
今まではAnacondaを利用しており、それを利用してimport/exportする方法もあります。
これに加えて最近はPipenvも増えたようでもあり、それら紹介をします。

なぜ、環境の管理をする必要があるのか?

誰もが同じ環境を再現し、環境による問題を起こさないためです。
仕事やOSSで複数人でのプロジェクトで環境を統一しないと、依存ライブラリのバージョンによる問題が発生することもあります。
そのため、環境を統一することは非常に重要です。

環境構築の方法

本章では、大きく分けて3つの方法を紹介します。

1. requirement.txt
2. Anacondaの仮想環境
3. Pipenv

requirement.txt

古来からある方式であるrequirement.txtです。
未だにライブラリでも多く採用されている方式の一つです。
requeirement.txtはライブラリの名前が一覧で書いており、それを読み込むことで必要なライブラリをインストールできます。

requirement.txtの作り方は次の通りです。

pip freeze > requirement.txt

読み込み方は次の通りです。

pip install -r requirement.txt

この方法ですが、pipコマンドが実行できれば、実行可能なのがメリットです。
AnacondaやPipenvでもpipが利用できるので使えます。
ただ、Pythonのバージョンは管理できていないので、そこは一つネックになるポイントです。

Anacondaの仮想環境

Anacondaを用いて、仮想環境を作成できます。
基本的な利用方法は次の通りです。
nonbiri-tereka.hatenablog.com

Anacondaによる仮想環境の構築は次のコマンドで作成可能です。

conda create -n py38 python=3.8

その仮想環境の情報を出力するには次のコマンドを実行します。

conda env export > condatest.yml

出力されたファイルを読み込む方法は次の通りです。

conda env create -n py38_re -f condatest.yml

仮想環境ごと環境構築ができるのでPythonのバージョンも管理できるのが強みです。
ただし、Anacondaそのものをインストールせねばならず、ディスクの容量も必要とする点がネックでしょうか。

Pipenv

Pipenvが最近は利用されるようになっています。
まずは、pipenvをインストールします。

pip install pipenv

次にpipenvを利用した環境構築を行います。

pipenv --python 3.7 # python3.7での環境構築
pipenv install numpy scipy matplotlib

逆にPipenvの環境を取り込み、構築する場合は次の通りです。
Pipfileがあるディレクトリで、次のコマンドを入力します。

pipenv install 

pipenvは導入までの時間が非常に早く、手軽に構築できるのが良い点だと感じました。

正直どれが一番なのか

どれも一長一短ですが、Pipenvが楽そうです。
Anacondaだとそのインストール自体も必要になり、導入コストが非常に高いと感じています。
また、requirement.txtのみだと、同一PC内での複数環境での管理が難しくなるので、避けたほうが良いと思っています。

最後に

これらそれぞれに利点欠点もあるため、ケースバイケースで考慮すべきでしょう。
このあたりは使いながら検討する必要があると感じています。