tkherox blog

データサイエンスおよびソフトウェア開発、たまに育児についての話を書いています

DjangoのORMにおけるコネクション管理

事の経緯

DjangoのORMについて深く理解しておらず,Djangoにおけるコネクションの管理とかってどうなっているのだろと気になったので調べてみました.
加えて,このままなんとなくで利用していたら,いつか絶対バグを生み出しかねないと不安になったのも調べたいと思った1つの要因です.

本記事は自身の備忘録としてまとめようと思います.
そのため,詳細な解説等は省いてしまう部分もあるかと思いますが,Djangoのデータベースに関する記事って意外と少なくて探すのに苦労するので,少しでも他の方の参考になれば幸いです. (公式ドキュメント読めって話ですが...)

Django ORMとは

Djangoではデータベースの操作をORMと呼ばれるデータベースとプログラムミング言語間のオブジェクトを変換するプログラミング技法が使われております.利用者はこのORMを介してデータベースを操作することで効率的な開発やセキュアプログラミングを実現することができます.
例えば,以下のようなモデルを定義していた場合を想定してみましょう.

from django.db import models

class Post(models.Model):
    author = name=CharField(max_length=255) 
    title = models.CharField(max_length=200)
    text = models.TextField()
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

このモデルに対してORMを利用することでSQLを意識することなくデータベースの情報を取得してオブジェクトとして扱うことが可能になります.

from django.http import HttpResponse
from .models import Post

def index(request):
    post = Post.objects.all()  # データベースにあるPostを全て取得
    return HttpResponse(post)

非常に便利な機能ですよね.
ORMを利用することでMigration等も実施できるため類似機能としてSQLAlchemyやStormといった別のツールがありますが,Djangoを利用するのであればDjango ORMを利用した方が良いかと思います.

Connection管理

さて,本題のDjangoのORMにおけるコネクションの管理についてです. DjangoのORMでコネクションを接続するタイミングはいつなのでしょうか.

答えはリクエストを受けてから 初めてSQLが発行されるタイミング の時です.
以下で説明するとHTTPリクエストを受けてルーティングによってupdateメソッドが呼び出された時の1回目のPost.objects.get(id=id)を実行したタイミングでデータベースのコネクション接続が行われます.以降は初回に接続したコネクションを利用してSQLを実行するため,2回目のPost.objects.get(id=id)ではコネクション接続はせずにSQLを実行しています.

from django.http import HttpResponse
from .models import Post

def index(request):
    posts = Post.objects.all()  # データベースにあるPostを全て取得
    return HttpResponse(posts)

def update(request, id):
    # 1回目の変更
    post = Post.objects.get(id=id)
    post.title = 'first change!'
    post.save()
    # 2回目の変更
    post = Post.objects.get(id=id)
    post.text = "second change!"
    post.save()

    return HttpResponse(post)

ここで,ごく稀にDBのコネクション接続をSQL毎に実施したいという要望があるかと思います.その場合は以下のように実装して強制的にコネクションをクローズするように実装します.

from django.db import connection
from django.http import HttpResponse
from .models import Post

def update(request, id):
    # 1回目の変更
    post = Post.objects.get(id=id)
    post.title = 'first change!'
    post.save()
    connection.close()    # コネクションのクローズ

    # 2回目の変更
    post = Post.objects.get(id=id)
    post.text = "second change!"
    post.save()

    return HttpResponse(post)

ただし,これはリソースの観点からは推奨されません.
というのもデータベースへの接続を再確立することになるため接続のオーバーヘッドが増加することになります.リクエストで接続を開始してからコネクションを継続するのはこのオーバーヘッドを回避するための仕様になります.そのため,必要がない限りはコネクションを開始してからはリクエストが終了するまで継続して利用するようにしましょう.

まとめ

今回はDjangoのコネクションに関する内容を記載しました.
CONN_MAX_AGEConnection Poolなど他にも残しておきたい備忘録は多々ありますが,今回はコネクション管理のみについて簡単にまとめておきました. また,別の機会にDjango ORMに関する情報は継続的に記載していこうと思います.

応用情報技術者試験に合格するまで

応用情報技術者試験とは

応用情報技術者とはIPAが試験を運営実施するIT系の国家資格です.
応用情報技術者試験の対象者は「高度IT人材となるために必要な応用的知識・技能をもち,高度IT人材としての方向性を確立した者」とされています. 試験は選択式の午前と記述式の午後の2つの構成から成り,一般的な合格率は約20%程度となっています.
特に受験資格等はないことから誰でも受験することができます.ITスキル保有の証明としては有効かと思いますのでこれから様々な業界でIT化が進んでいくと想定される中で取得して損はない資格だと思います.次の高度試験の午前試験を一部免除できるのでその点もメリットです.

www.jitec.ipa.go.jp

受験日までのロードマップ

ちゃんと勉強を開始し始めたのは試験日の2020年の1月からです.
当初は令和2年春期の試験を受験しようと考えていたので,大まかに以下のようなロードマップで試験対策を実施していこうと思っていました.

期間 対策内容
2020/1月 勉強開始・午前対策
2020/2月 午後対策
2020/3月 苦手分野の対策
2020/4月 直前の詰め込み

ですが,令和2年春期の試験はコロナの影響で中止となってしまったため,当初計画よりも勉強期間を長めに確保できるようになったことから以下の流れで勉強を実施しました.

期間 対策内容
2020/1月 勉強開始・午前対策開始
2020/2月 午前対策(教本)
2020/3月 午後対策開始(問題演習)
2020/4月~8月 午前対策(1問1問)・午後対策(問題演習)
2020/9月 過去問演習
2020/10月 直前の詰め込み

ちなみに私のバックボーンを簡単に補足しておくとITに関する職務経験は4年半ほどあります.フロントエンド・バックエンドの開発からインフラ構築まで運用以外の業務を中心に行ってきて,最近はデータサイエンスの領域を中心に作業するといった経歴のためアドバンデージがある状態からのスタートでした.学生の頃を含めるとITに触れていた期間はもっと長いので文系初学者ではないので参考にされる方はご注意ください.

書籍やツール

私が試験に向けて利用した書籍とツールは以下の3つです.

三種の神器ではないですが応用情報では技術に関する知識の幅と深さを求められますので,「応用情報技術者 合格教本」で幅を「応用情報技術者 午後問題の重点対策 2019」で深さを補うという意識で学習を実施していきました.
また,「応用情報技術者試験ドットコム」は応用情報に関する情報を公開している無料サイトで,コンテンツとして1問1問形式で過去問を解くことができるため午前試験を確実に突破するために利用しました.

2019応用情報技術者午後問題の重点対策 (重点対策シリーズ)

2019応用情報技術者午後問題の重点対策 (重点対策シリーズ)

  • 作者:小口達夫
  • 発売日: 2018/10/31
  • メディア: 単行本(ソフトカバー)

www.ap-siken.com

だだ,正直なところ午前試験の勉強のために「応用情報技術者合格教本」を購入しましたが,振り返ると必要なかったなと感じます.というのも「応用情報技術者試験ドットコム」の1問1答が非常に有力なツールだからです.1問1答の問題は過去の午前試験からの出題になるのですが,解説が1つ1つに丁寧に設けられているため,合格教本を買わずとも問題を問いて解説を読むという工程を繰り返すだけで合格に必要な知識が身につきます.そのため,ある程度知識がある人は合格教本を買わずに「応用情報技術者試験ドットコム」と「応用情報技術者 午後問題の重点対策 2019」だけで乗り切れた方がコスパが良い気がします.

具体的な勉強法

勉強の方法について記載します.
社会人のため基本的にはまとまった学習時間を確保することが難しいためコツコツ勉強するスタイルで学習しました.他のブログやネット記事では応用情報は1ヶ月勉強すれば受かるみたいな記事を見かけたりしますが,私の感覚としてはまとまった時間が確保できる人向けの勉強法だと思います.社会人であれば少なくとも3ヶ月くらいは勉強時間を確保した方が無難かと思います.

では,午前対策,午後対策,直前対策の3つに分けて個別に勉強方法を説明していきます.

【午前対策】通勤時間での1問1答

午前試験対策の開始時期は2020年2月から教本にてはじめました.
しかし,1ヶ月で教本を軽く読んで全体像を掴んだそれ以降の2020年3月からは教本にはほとんど触れず,通勤時間を活用して出勤と帰宅の時間で合計20問を解くという方法で勉強を繰り返しました.期間的には7ヶ月の間で約3000問を解いてましたが,最後の方は過去問と回答を覚えてしまうくらいには取り組んだかと思います.
この方法は習慣化してしまえば苦なく取り組めるので,長時間机に向かうのが苦手な人にはオススメです.

【午後対策】仕事終わりの1時間で午後対策

午後試験対策は仕事から帰宅後に「応用情報技術者 午後問題の重点対策 2019」の演習問題を1問を解くという方法で勉強していました.こちらも午前対策と同様に苦なく勉強するためには良いと思います.私自身は休日にまとまった時間を取ることができない訳ではなかったのですが,不定期に予定があったりして習慣化するのが難しいと思ったために平日にコツコツやるようにしました.
また,午後試験では11問から5問を選択して回答するため,解く問題を事前に決めて分野を絞って勉強する方がいますが,私は敢えて重点対策の演習問題の全てを解いていました.理由は大きく2つあって,試験時に難しい問題に直面した時に別の問題を解けるようにというリスクヘッジと午後試験の特徴である記述問題に対する慣れを獲得するためです.これは非常に効果があると思っていて,実際に本試験でも受験前はネットワークの分野を解く予定だったのですが,受験中に問題を見て意外と回答しづいらい問題だなと感じたために別の問題を解くことにしました.こういったイレギュラーな対応にも焦ることなく試験に臨むことができるように準備しておくのが良いと思います.

【直前対策】過去問演習

2020年9月の試験1ヶ月前からは過去問を使った演習を時間が取れる範囲で実施していました.午前と午後のどちらも実施することで問題の時間配分や試験イメージを掴むという感じです.
直近の3年分の過去問を解きましたが,午前試験は過去問と同じ問題が出題されることもあるので多く解くことにも意味があるかと思いますが,午後試験は同じ問題が出題されることはないので試験傾向を掴むという意味では1年分をしっかり解くだけでも十分かと思います.(重点対策の本で対策は実施しているため)

試験

さて,最後に実際の試験についてです.

令和2年秋期の試験形式は 2015年以降の形式からの変更はなく以下のような時間と問題数になります.

午前 午後
試験時間 9:30~12:00(150分) 13:00~15:30(150分)
出題形式 多肢選択式(四肢択一) 記述式
出題数
解答数
出題数: 80問
回答数: 80問
出題数: 11問
解答数: 5問

令和2年秋期試験は午前試験が難化したとネットでは騒がれていました.しかし,個人的にはあまり難化したという感覚はなくこれまで通りの難易度だと感じました.過去試験で出題された問題と全く同じ問題も約10問くらいあり,午前対策をしっかり実施していればこれらを高速に解くことで安定して点数を確保することができると思います.また,他の問題にしっかり時間をかけて解くことができるようにもなるため過去問をたくさん解くことは重要なポイントだと言えます.

続いて午後試験ですが,私は11問中から以下の5問を選択して回答をしました.

当初の予定では前章でも記載した通りネットワークを回答する予定だったのですが,非常に取り掛かりづらかったため組み込みシステム開発を別途切り替えて選択してます.
令和2年秋期のネットワーク問題ではVDIを導入する問題を取り扱っていたのですが,セッション管理などの仕組みに関しても試験では問われていたため確実に得点する自信がなかったことが要因です.
ちなみに私の個人的な感覚ですが,やはり実務経験がある方が午後試験を解く際に有利に働くのは往々にしてあると思います.今回のVDIのテーマも昨今のテレワーク導入の話と近しい内容で実務経験がある方の方が試験問題の全体イメージが湧きやすく解きやすかったかと言えます.また,情報システム開発における問題ではスクラム開発をテーマとして扱っており,スクラム開発を経験したことがある方であれば問われている内容が基本的なことばかりで,非常に簡単な問題でした.
選択問題の5つのうち2つを確実に得点できるようであれば合格率が格段に上がるので,何かしらでIT関連の経験がある人はまずは自分の経験と関連する分野を選択することをお勧めします.

合格判定

令和2年秋期の 合格発表日は2020年12月25日 となっております.
まだ合格発表はこれからなので解答速報をベースに採点した結果になりますが,自己採点結果は以下となっています.

  • 午前試験: 90点
  • 午後試験: 74点

現時点では合格ラインは超えていると思っておりますが,早く合格発表して欲しいところですね.特に午後試験は記述ということもあり,採点者がどのような基準で採点をしているのかによって多少の前後があるかと思うので...
実際の合格発表の結果はこちらで記載しています.

まとめ

今回は令和2年秋期の応用情報技術者に関する勉強法と試験傾向について記載しました.
これから勉強・受験する人の手助けに少しでもなればと思います.特に社会人でまとまった時間が取れない人や長期間でコツコツ勉強するタイプの人の参考になれば幸いです.
試験もあったため少しブログ更新の頻度が下がっていたのでこれからはデータ分析や開発関連の情報を定期的に発信して行こうと思います.

SHAPでモデルを解釈してみた

はじめに

XAI(Explainable AI)という言葉を聞いたことはありますでしょうか.
日本語では「説明可能なAI」と呼ばれていて,構築した学習モデルが入力に対してどのように出力に起因したのかというモデルの解釈性を示す分野として注目を集めております.機械学習のモデルはブラックボックスとなりがちなため,なぜそのような結果になったのかという解釈は一般的には難しい側面があります.こう言った課題はアカデミックな領域ではあまり問題とならない場合もあるのですが,ビジネスの現場では機械学習アルゴリズム導入の妨げとなる要因になります.というのもシステムを構築してサービスを提供する企業において,想定していない挙動をとることに対して信頼性や安全性と言った観点で難色を示す場合が往往にしてあるからです.
そのため,今回は機械学習の解釈性という非常に重要なトピックを題材に,モダンな機械学習アルゴリズムと親和性が高く扱いやすいSHAPについて構築したモデルにおける活用方法を含めて説明します.

SHAPとは

SHAPとはブラックボックスしがちな予測モデルの各変数の寄与率を求めるための手法で,各特徴量が予測モデルの結果に対して正負のどちらの方向に対してどれくらい寄与したかを把握することによって予測モデルの解釈を行えるようになります. SHAPの理論は主に協力ゲーム理論におけるShaply Valueが由来となっており,協力ゲーム理論におけるプレイヤーを特徴量に置き換えて特徴量毎の貢献度(Shapley Value)を求めています.shapley valueの計算式は以下のように定義されます.

\displaystyle{
\phi_i = \sum_{S ⊂ F\ \{i\}} \frac{|S|!(|F| - |S| - 1)!}{N!} [ f_{S∪\{i\}}\left(x_{S∪\{i \}}\right)-f_{S}\left(x_{S}\right)] 
}

この式の\frac{|S|!(|F| - |S| - 1)!}{N!}の部分が組み合わせ数の逆数となっており,後半部分の [ f_{S∪{i}}(x_{S∪{i}})-f_S(x_S)]はある組み合わせにおけるプレイヤーiの貢献度合いを示していることより,全ての組み合わせにおける貢献度合いの平均を算出していることになります. このようにして全ての組み合わせにおける貢献度を求めることでShapley Valueが計算されることがわかります.
ただし,全ての場合でshapely valueを計算することができれば良いのですが,現実的な問題として特徴量が増えていった際に,特徴量同士の組み合わせ数が膨大になり有限な時間で計算が終わらないといった事象が発生します.そのため,実際には近似値を計算するなどの工夫が行われています.

より詳細な中身のロジックについては本家の論文を参照してみてください.

ライブラリについて

ここまで述べてきたSHAPの理論についてですが,この理論を実装したSHAPと言うライブラリがあります.

このライブラリでは最適化された計算手法やモデル解釈のための可視化メソッドが用意されており,我々が生成したモデルに対して簡単にSHAP Valueを計算することをサポートしてくれます.モデルにおける各特徴量の貢献度を計算する仕組みが内部実装されている関係で,サポートされていないアルゴリズムやライブラリでのモデルは扱うことができませんが,有名なアルゴリズムは既にサポートしているので特殊な場合を除きすぐに実装して計算することができます.

SHAPライブラリで解釈を行うことが可能な対応しているアルゴリズム(ライブラリ)について紹介します.
まず,木系のアルゴリズムでは以下のモデルがSHAPで扱うことができます.

  • XGBoost
  • LightGBM
  • CatBoost
  • scikit-learn tree model
  • pyspark tree model

SHAPライブラリでは上記の木系アルゴリズムを高速に処理するためにC++ライブラリとして実装されています. その他にもニューラルネットワークフレームワークではTensorflowとKerasをサポートしており,一部の機能ではPytorchも利用できるようになっています.

実際にSHAPライブラリの具体的な利用方法を知りたい場合は本家のGithubを参照することをお勧めします.というのも実装例として多くのJupyter NotebookによるサンプルをREADMEに用意していることから参照することで実装と出力イメージが両方掴めるため,これからSHAPで初めて解釈を実施してみようと言う方は一読してみてください.

github.com

インストール

SHAPライブラリのインストール方法を実行環境の情報と併せて以下に記載します.

ProductName:  Mac OS X
ProductVersion: 10.14.6
Python:  3.6.9

今回はLightGBMとXgboostのモデルに対してSHAPによる解釈を実施していくため,まず下準備としてLIghtGBMとXgboostのライブラリをインストールします.

$ pip install lightgbm==2.3.1 xgboost==1.0.0

続いて,本命のSHAPをインストールです.

$ pip install shap==0.35.0

インストール自体はpipで非常に簡単にインストールすることができるので導入しやすいと思います.

データセット

扱うデータセットUCI machine learning repositoryで公開されているデータセット一覧の中から「Wine Quality Data Set」を利用します.このデータセットはワインの品質に関する情報を含んでおり,白ワインと赤ワイン別にサンプルデータが分割されているためフラグを自身で付与することで2値分類用のデータセットを作成することができます.

https://archive.ics.uci.edu/ml/datasets/Wine+Quality

本記事ではこのデータセットを用いて品質に関する情報から白ワインと赤ワインの2値分類の問題としてモデルを作成し,SHAPによるモデルの解釈を行います.

具体的なカラムを参考情報として以下にまとめておきます.

カラム名 データ型 説明
sulfur dioxide float64 二酸化硫黄
chlorides float64 塩化化合物
density float64 濃度
sulphates float64 亜硫酸塩
volatile acidity float64 揮発酸(酢酸)
pH float64 水素イオン指数
residual sugar float64 残糖量
alcohol float64 アルコール
fixed acidity float64 酒石酸濃度
free sulfur dioxide float64 遊離亜硫酸濃度
citric acid float64 クエン酸濃度
quality int64 ワインの味

モデル作成

続いてモデル作成を実際のコードと併せて説明していきます.

今回はLightGBMとXgboostの2つのそれぞれでモデルを作成します.
この2つのモデルを選択した深い意味は特にはないのですが,私がベースラインのモデルを作成する際に頻繁に利用するアルゴリズムということで2つのモデルを利用しました.

まずは各種必要なライブラリのインポートです.
基本的な分析関連のライブラリをインポートしておきます. 可視化ライブラリはmatplotlib,データ操作に関してはpandas,numpy,scikit-learnを利用します.

import numpy as np
import pandas as pd
import xgboost as xgb
import lightgbm as lgb
import seaborn as sns
import matplotlib.pyplot as plt
from sklearn.model_selection import train_test_split
from sklearn.metrics import roc_auc_score, roc_curve, auc
from sklearn.metrics import classification_report

先ほど解説したUCIのワインに関するデータをpandasを利用してDataFrame形式で読み込みます.白ワインと赤ワインを識別するカラムはデフォルトのcsvファイルには含まれていないため,自分で作成する必要があります.そのため,今回は新たに class というカラムを用意して赤ワインと白ワインを識別するフラグを用意しています.

# データの読み込み
wine_red = pd.read_csv("../data/winequality-red.csv", sep=";")
wine_white = pd.read_csv("../data/winequality-white.csv", sep=";")

# classカラムを追加( 赤ワインが 0, 白ワインが 1 )
wine_red["class"] = 0
wine_white["class"] = 1

# 2つのデータセットを結合
wine = pd.concat([wine_red, wine_white], axis=0)

作成したデータを学習データセットとテストデータに分割します.
全体の2割をテストデータとして利用します.

# 固定パラメータの指定
SEED = 42
TEST_SIZE = 0.2
target = "class"
predictors = [i for i in wine.columns if i not in target]

# データの分割
X_train, X_test, y_train, y_test = train_test_split(
    wine[predictors],
    wine[target],
    test_size=TEST_SIZE,
    random_state=SEED
)

LightGBM

ここからはLightGBMでのモデル学習と精度確認をしていきます.
ハイパーパラメータは2値分類で学習するための値を指定して学習します. イテレーションの回数は5000回として,early stoppingとして100を設定してモデルを学習します.

# Lightgのハイパーパラメータ
lgb_params = {
    'task': 'train',
    'max_depth': 10,
    'metric': 'auc',
    'objective': 'binary',
    'boosting': 'gbdt',
    'learning_rate': 0.005,
    'seed': SEED,
    'num_threads': 8,
    'early_stopping_round': 100,
    'subsample': .8,
    'min_gain_to_split': .1, 
    'lambda_l1': 1,
    'lambda_l2': 0,
    'min_child_weight': .8,
    'feature_fraction': .8,
    'num_leaves': 31
}

# モデルの学習
lgb_train = lgb.Dataset(X_train, y_train)
lgb_eval = lgb.Dataset(X_test, y_test)
lgb_model = lgb.train(
    params=lgb_params,
    train_set=lgb_train,
   num_boost_round=5000,
    valid_sets=lgb_eval
)

# テストデータによる推論
y_pred = lgb_model.predict(X_test, num_iteration=lgb_model.best_iteration)

# 評価指標の計算
fpr, tpr, thresholds = roc_curve(y_test, y_pred)
roc_auc = auc(fpr, tpr)

# 重要変数とROC-Curveの可視化
fig, axes = plt.subplots(nrows=1, ncols=2, figsize=(13,6))
feat_imp = pd.Series(lgb_model.feature_importance(), index=X_test.columns).sort_values(ascending=False)
feat_imp = feat_imp.head(30)
feat_imp = feat_imp.sort_values(ascending=True)
feat_imp.plot(kind='barh', title='Feature Importance', ax=axes[0], color=sns.color_palette()[0])
axes[0].set_xlabel('Feature Importance Score')
axes[1].plot(fpr, tpr, label='ROC curve (area = %0.3f)' % roc_auc)
axes[1].plot([0,1], [0,1], 'k--')
plt.xlabel('False Positive Rate')
plt.ylabel('True Positive Rate')
plt.title('ROC Curve')
legend = axes[1].legend(frameon=True, loc='lower right', fontsize = 'medium') # 凡例
frame = legend.get_frame()
frame.set_facecolor('white')
frame.set_edgecolor('gray')

学習したモデルの重要変数とROCを可視化したグラフは以下に示す通りになりました.ROC-AUCは99.7%と高精度なモデルを学習できているようです. 続いて,重要変数を見てみると最も予測結果に寄与している変数はtotal sulfur dioxideということでワインの酸化防止剤として活用されて二酸化硫黄の含有率がワインの識別に最も関係があることが結果として見て取れます.逆に quality はワインの識別には関係ないことがわかりますね.

f:id:takaherox:20200701232345p:plain
LightGBM Feature Importance

Xgboost

続いてXgboostによるモデル作成と精度確認の部分になります.
ハイパーパラメータなどはLightGBMとほぼ同様になるように設定しています.

# xgboostのハイパーパラメータ
xgb_params = {
    "booster": "gbtree",
    "max_depth": 10,
    "objective": "binary:logistic",
    'eval_metric': 'auc',
    "eta": 0.005,
    "seed": SEED,
    "nthread": 8,
    "subsample": 0.8,
    "gamma": 0,
    "alpha": .1,
    "lambda": 0,
    "min_child_weight": .8,
    "colsample_bytree": .8,
    "max_leaves": 31
}

# モデルの学習
xgb_train = xgb.DMatrix(X_train, y_train)
xgb_test = xgb.DMatrix(X_test, y_test)
evals_result = {}
xgb_model = xgb.train(
    xgb_params,
    xgb_train,
    num_boost_round=5000,  
    early_stopping_rounds=500,
    evals = [(xgb_test, 'eval'), (xgb_train, 'train')],
    evals_result=evals_result
)

# テストデータによる推論
y_pred_xgb = xgb_model.predict(xgb_test)

# 評価指標の計算
fpr, tpr, thresholds = roc_curve(y_test, y_pred_xgb)
roc_auc = auc(fpr, tpr)

# 重要変数とROC-Curveの可視化
fig, axes = plt.subplots(nrows=1, ncols=2, figsize=(13,6))
feat_imp = pd.Series(xgb_model.get_fscore()).sort_values(ascending=False)
feat_imp = feat_imp.head(30)
feat_imp = feat_imp.sort_values(ascending=True)
feat_imp.plot(kind='barh', title='Feature Importance', ax=axes[0], color=sns.color_palette()[0])
axes[0].set_xlabel('Feature Importance Score')
axes[1].plot(fpr, tpr, label='ROC curve (area = %0.3f)' % roc_auc)
axes[1].plot([0,1], [0,1], 'k--')
plt.xlabel('False Positive Rate')
plt.ylabel('True Positive Rate')
plt.title('ROC Curve')
legend = axes[1].legend(frameon=True, loc='lower right', fontsize = 'medium') # 凡例
frame = legend.get_frame()
frame.set_facecolor('white')
frame.set_edgecolor('gray')

当然ですが,こちらもLightGBMの結果と同様にほとんど同じ結果になっています.精度は誤差の範囲ですが99.9%とLightGBMより高い精度のモデルになりました.

f:id:takaherox:20200701232426p:plain
Xgboost Feature Importance

SHAP Value

ここからが本題のSHAPになります.
それぞれのモデルでSHAP Valueの計算と可視化をしてみます. TreeExplainerクラスにてモデルを入力してインスタンスを生成し,shap_valuesメソッドを実行するだけで簡単にSHAP Valueを計算することができます. SHAPライブラリ内ではLightGBMのpredictメソッドを実行しており,その際にパラメータの pred_contrib を指定してSHAP Valueを求めています.また,Xgboostも同様の方法でSHAP Valueを計算しています.

# LightGBMのSHAP Valueの計算
lgb_explainer = shap.TreeExplainer(lgb_model, X_train)
lgb_shap_values = lgb_explainer.shap_values(X_train)

# XgboostのSHAP Valueの計算
xgb_explainer = shap.TreeExplainer(xgb_model)
xgb_shap_values = xgb_explainer.shap_values(X_train)

SHAP Valueを計算した後は可視化をします.
可視化にはいくつかの種類がありますので自身で適切な可視化を選択する必要があります.試しにViolinPlotとDependencePlotで結果を見てみます.SHAPライブラリでは複数の可視化パターンを用意しているためGithubを参考にして実施したい解釈に合わせて適切な可視化パターンを選択してみてください.

Violin Plot

# LightGBMでのViolin Plotの可視化
shap.summary_plot(lgb_shap_values, X_train)

# XgboostでのViolin Plotの可視化
shap.summary_plot(xgb_shap_values, X_train)

f:id:takaherox:20200704234439p:plainf:id:takaherox:20200705105807p:plain
Violin Plot(左がLightGBM, 右がXgboost)

Violinプロットを見てみるとパラメータの値が結果に対してどのように寄与しているのかを把握することができます.最上位にきている total sulfer dioxide を見てみると,この値が大きいほど予測結果を白ワインである確度が高くなるように作用していることがわかります.一方で chlorides を見てみると逆に作用しており,値が大きいほど赤ワインである方向に作用しているようです.このように属性情報の値の大きさがどちらに作用しているのかを確認することでモデルの入力と出力の関係を理解することに繋げられます.

Dependence Plot

# LightGBMでのPartial Dependence Plotの可視化
shap.dependence_plot("rank(0)", lgb_shap_values, X_train)

# XgboostでのPartial Dependence Plotの可視化
shap.dependence_plot("rank(0)", xgb_shap_values, X_train)

f:id:takaherox:20200704234449p:plainf:id:takaherox:20200705105852p:plain
Partial Dependency Plot(右がLightGBM, 左がXgboost)

Dependence Plotを見てみると,単一の特徴量に対して全てのパラメータがどのように予測結果に影響しているのかを理解することができます.また,別のパラメータとの交互作用も合わせて確認することができます.交互作用を確認したいパラメータを指定する場合は interaction_index を引数で指定してあげれば簡単に可視化できます.
今回の結果では total sulfer dioxide の値が大きくなるとSHAP Valueが大きくなっていることが見て取れます.LightGBMもXgboostでも同様の結果になっていますね.このように単一の特徴量における予測結果への影響が線型的に単調増加している場合は解釈が容易なのですが,必ずしもそういう結果になるとは限りません.次の項目では解釈をしやすくするためのMonotonic Constraints(単調制約性)について簡単に記載しておきます.

Monotonic Constraints

Monotonic Constraintsとは単調制約性のことを指します. つまり,一般的に特徴量が与えるモデルへの影響は複雑な作用をする場合が往々にしてありますが,特徴量がモデルへの与える影響を単調増加(減少)になるように制約を与えるということになります.

今回の例では単調制約性を与えてないない場合は citric acid は以下のように0.4まではSHAP Value値が大きくなりますが,0.4以降ではSHAP Valueが小さくなっていきます.このような場合にMonotonic Constraintsを与えた場合にどのような効果が得られるのかを確認して見ましょう.

f:id:takaherox:20200705202243p:plain
Xgboost Dependence Plot - cirtic acid

Monotonic Constraintsを設定する方法は簡単で学習時のパラメータに制約項目を指定するだけです.事前に特徴量が目的変数に対してどのような相関があるのかを調べておき,負の相関,無相関,正の相関のそれぞれに応じて -1, 0, 1 を指定します.今回は正の相関を指定するため1を指定します.

# Xgboostのハイパーパラメータ
xgb_params = {
    "booster": "gbtree",
    "max_depth": 10,
    "objective": "binary:logistic",
    'eval_metric': 'auc',
    "eta": 0.005,
    "seed": SEED,
    "nthread": 8,
    "subsample": 0.8,
    "gamma": 0,
    "alpha": .1,
    "lambda": 0,
    "min_child_weight": .8,
    "colsample_bytree": .8,
    "max_leaves": 31,
    "monotone_constraints": "(0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 )"  #  単調制約性
}

# モデルの学習
xgb_model_mono = xgb.train(
    xgb_params,
    xgb_train,
    num_boost_round=5000,  
    early_stopping_rounds=500,
    evals = [(xgb_test, 'eval'), (xgb_train, 'train')],
    evals_result=evals_result
)

# SHAP Valueの可視化
xgb_mono_explainer = shap.TreeExplainer(xgb_model_mono)
xgb_mono_shap_values = xgb_mono_explainer.shap_values(X_train)
shap.dependence_plot("rank(9)", xgb_mono_shap_values, X_train)

先ほどの図と比べても変化は一目瞭然ですね.
パラメータで指定した正の相関のように値の増加に伴ってSHAP Valueが単調増加していることが分かります.このように解釈をしやすいようにモデルに制約を与えることもできます.この制約を与えてモデルを作成した場合には予測精度が低下する場合もあるため,モデル精度を下げたくない場合は注意してください.

f:id:takaherox:20200705202252p:plain
Xgboost Dependence Plot - cirtic acid monotonic

まとめ

今回はSHAPの簡単な理論と木系のアルゴリズムにてSHAPの扱い方をモデルの作成からSHAP Valueの可視化までを通じて説明しました.XAIはデータ分析においても非常に重要なトピックでありますし,ビジネスサイドでも役に立つということで是非活用してみてください.
SHAPライブラリはDeepLearningのフレームワークでも扱うことができるため,今後はそちらも試して見たいと思います.

リモートワーク効率化に向けて自宅環境の整備をしてみた

今回の記事は本ブログの趣旨から外れるため投稿するか迷いましたが,是非皆さんも自宅整備を実施してもらいたいと思ったため例外的な記事として投稿します.まだ,自宅環境を整備していない方はこの機会に是非整備してみてください.

自宅環境は整備しよう

結論から述べるとテレワークにおける自宅環境は絶対に整備するべきだと思います.

世界中に大きな影響を及ぼしてるCOVID-19により,私たちの生活は今現在大きな変革を求められています.例えば,集団感染を未然に防ぐために学校ではリモート授業などの導入によって教育の仕方が変わり,企業では時差出勤やリモートワークなど働き方に変化が起きています.日本企業では皮肉にもこのパンデミックによって,大きくIT化が推進されたと言うのも事実だと思います. また,日本という国のIT化における課題も垣間見れましたね.特別給付金のオンライン申請が実は手動だったという真実には驚きを隠せませんでした.これは,システムを実際に使っている人に問題があるのではなく,システム導入を主導していた側の目的に問題があると個人的には思うので,今回の結果を踏まえて改善していってくれることを期待します.

さて,新しい生活様式としてリモートワークが主軸になりつつ過程で,改めてオフラインでの業務が重要だと感じた人や在宅だと誘惑が多くて集中できないなど様々な価値観を皆さんも抱いていると思います.
ですが,私がここで私が言いたいことはオフィスと自宅の環境が異なる状況下でリモートワークで生産性が落ちたという安直な解に落とし込まないでということです.普段慣れ親しんだ作業環境と異なる環境で作業すれば,生産性が落ちるのは必然です.オフィスでの作業を想定してここでは記載しますが,少なくともデスク,パソコン,ディスプレイ,マウス,キーボードの最低限この5つだけでもオフィスと同じ水準の環境を揃えることが重要だと思います.

本記事ではリモートワークのメリットが多いということに気づいたにも関わらず,COVID-19が収束したら全てをオフラインに戻すと言った安直な考え方をしないで,少しづつでもオンライン化が進んで新しい技術やツールが生み出される世の中になってくれることを期待して私が整備した自宅環境と環境整備による効果をまとめました.

購入した機器

早速,私が実際に購入した備品と機器について記載していきます.
私がリモートワークを実施する中で揃えた物は以下の5つです.

  • デスク
  • ディスプレイ
  • マウス
  • キーボード
  • PCスタンド

あまりコストは掛けたくないとは思っていたので必要最低限の物だけにしました.また,デスクに関しては家の間取りとの兼ね合いで選んでおりますが,その他の物はオフィスで利用していた物に寄せて選んでおります. どうしてもコストを掛けたくないという方はディスプレイとマウスとキーボードを揃えればQoLが格段に上がりますので騙されたと思って整備してみましょう.
次からは具体的に購入した詳細を記載していきます.

デスク

私が購入したデスクはニトリシステムデスク(ザッキー 110 WW)です.

サイズは幅110cm×奥行53cm×高さ115cmでありデスクスペースと本棚スペースを同時に確保できる点が購入の決め手になっています.
やはり業務上たくさんのメモをとったり書籍を閲覧することからデスク1つで収納も兼ねているのはポイントが高いです.デスクの下にも物が置けるスペースがあるのでデスクを常にスッキリした状態に保てるので綺麗好きな人にもおすすめです.

www.nitori-net.jp

ディスプレイ

ディスプレイの選択は非常に悩みましたがコスパを考慮してLG モニター ディスプレイ 29WL500-B 29インチにしました.

業務では上位機器にあたる「LG モニター ディスプレイ 34WL850-W 34インチ」を利用しており,同じ物にしたかったのですが34インチのディスプレイをデスクにおいた際の圧迫感を気にしてインチが小さいディスプレイにしました.
結果としては29インチにしてして良かったです.ディスプレイのサイズは幅69.8cm×奥行20.9cm×高さ41.1cmとなるため幅には若干の余裕があるのですが,実際に置いてみると29インチでも多少の圧迫感があるので34インチだったらと想像すると恐ろしいです.

マウスとキーボード

続いてマウスとキーボードです. マウスは端末がMacbookProということもあり,MagicTrackPad2を利用しています.キーボードはHappy Hacking Keyboard Lite2(英語配列)ですね.

この2つに関しては基本的には会社で利用しているものと同じ物にしています.
HHKBに関してはProfessionalの方が絶対に良いとの声が聞こえてきそうですが,個人的には打鍵した際の感覚と少し音がうるいさい方が好きなのでLiteを利用しています.

Apple Magic Trackpad 2 - シルバー

Apple Magic Trackpad 2 - シルバー

  • 発売日: 2015/10/14
  • メディア: Personal Computers

www.pfu.fujitsu.com

PCスタンド

最後にPCスタンドですが,こちらは LAMPOクラムシェル ノートパソコンスタンド マックブック PCスタンド にしました.

業務では利用していないのですが,購入したデスクがそこまで広くはないことからディスプレイとMacbookProを置いてしまうとほとんどスペースがなくってしまうためMacbookProを据え置きの端末として利用するためにスタンドを購入・利用しています.
ディスプレイがワイドモニターであるためMacBookProの画面がなくても2画面での作業と同等の環境が再現できるのでMacのディスプレイは使わなくても問題ないですね.

自宅整備を実施して良かったこと

ここまでで購入した備品・機器について話してきましたが,ここからはそれらを導入して良かったことについて記載していきます.

生産性

やはり環境を整備する前と後では作業効率が格段に上がりました.
特にディスプレイを導入してからは作業スピードが2倍ほど向上したように思います.
調べ物をしながら作業を行うという方は絶対にディスプレイを導入して2画面の環境を用意することをオススメします.複数のアプリケーションを移動する度に目がちらついて作業に集中できないなんてこととはさよならしましょう.

モチベーション維持

これは全員に当てはまるとは言い切れませんが,私はリモートワークにおけるモチベーション維持に繋がりました.
家では誰かに常に監視されている訳ではないため集中して業務を遂行し続けるには個人の強い意思が必要になってきます.少しでもその意思を手助けするためにデスク環境が綺麗で作業しやすいことが起因すると感じます.
また,デスクに座ることで作業を開始するぞという気持ちが切り替えられるようになったので,リモートワークが始まるまでに気持ちが切り替えられないと言った人にもデスクが整備されているとオフィスに来て作業しているかのように気持ちの切り替えがスムーズに行える副次的な効果もあると思います.

個人ワーク時間が増加

良かったことの3つ目はリモートワークの環境を整備することで個人ワークの時間が増加したことです.
昨今ではプライベートの時間でスキルアップを図っている方が多いと思いますが,私もその内の一人で隙間時間や休日等を使って勉強したりしています.リモートワークの環境を整備したことによって机に向かう習慣を強制されたのか,意識することなく机に向かい個人ワークの時間が捗るようになりました.知識や技術は常に進歩しており,すぐに陳腐化するのでキャッチアップする時間を多く設けられるようになったことは個人的には思わぬ収穫でした.

まとめ

リモートワークの効率化に向けた自宅環境について,オフィスと同じ機器を整備するという観点で作業効率が向上するということを述べてきました.思わぬ収穫が誰しもあるはずなので,是非皆さんもリモートワークにメガティブな印象を持つのではなくどうすればリモートワークで生産性を高められるかを考えて行動してみてください.
今回はデータサイエンスやソフトウェアエンジニアリング以外の内容について記事を書きましたが,次はデータサイエンス関連の記事を書きたいと思います.

Pythonデータ分析100本ノックを実践【後半】

はじめに

本記事は過去記事のPythonデータ分析100本ノックを実践【前半】の続きの内容になっています.Pythonデータ分析100本ノックの書籍内容については前半にて言及しておりますので,Pythonデータ分析100本ノックとは?という方はまずはそちらを読んでみて下さい.
では早速,後半の50本ノックの内容を中心に書籍を実践してみた感想について記載していこうと思います.

後半部分の内容について

Pythonデータ分析100本ノックの後半では主に以下の3つのトピックを扱います.

大まかに後半の内容をまとめると最適化問題と分析結果のシミュレーションが中心の内容となっていて,画像処理や自然言語処理はおまけといった内容です.

少しだけ画像処理と自然言語処理の内容について言及しておくと,画像処理についてはOpenCVを用いた内容のみとなっておりHOG特徴量などといった人に対しての物体検出のみを扱っています.昨今の画像処理では当たり前となったCNNなどによる一般物体認識などの部分までは扱いません.
また,自然言語処理についても形態素解析が中心の内容となっております.この点については書籍内でも基本的な部分のみであることを明言しており,より広い知識を身に付けたいという方には専門書を読むことを推奨しています.

実践してみて

実践してみた所感を述べていきたいと思います.

まずは最適化問題と数値シミュレーションの部分についてです.
本書の中で私が一番取り組んでよかったと感じたのが最適化問題に関するノックの部分です. というのもデータ分析を実施する中であまり最適化問題を扱う機会が多くはなく,学びが多いノックだったことが背景としてあります.データ分析や機械学習というとネットなどの情報ソースを見ても教師あり学習教師なし学習,深層学習といった情報量が多く,あまり数理最適化問題を目にする機会がそもそも少ない気がしています(人によってはそんなことないと思いますが...).そのため,PuLPCBCのインストールや数理モデル定式化の実装といった最適化問題を実践で扱うために技術を学習するツールとしては非常に良質のコンテンツだと思います.
また,最適化問題では解きたい問題の定式化がポイントだと思うのですが,数式が苦手な方でも問題なく知識を習得できるように本書は設計されているとも感じました.と言うのも,いきなり最適化問題による数式を定義するのではなく,アドホックに分析したプロセスを経て最終的に便利なツールとして数理最適化問題による解法を実践していく流れなので利便性を感じながら着実に知識を習得できるでしょう.

続いて,数値シミュレーションの項目です.
本書では最適化問題で得た解を検証する位置付けで数値上のシミュレーションを題材として取り扱っています.実現場におけるデータ分析においてもA/Bテストや予測モデルのオフライン検証など,モデリンングと併せて検証工程はデータ分析でも非常に重要なプロセスとなっています.数値シミュレーションにおけるノックの内容はその検証工程の重要性を訴える内容になっており,本書の中でも割と重要なポイントの1つかなと思います.
さらに,個人的な学びとしてはあるサービスの会員間のネットワークを可視化するといったお題に対して,複雑ネットワークの学問観点を用いて分析結果をアクションに繋げると言うアプローチが大きな学びとなりました.複雑ネットワークより扱うデータ傾向がスケールフリー型なのかスモールワールド型なのかといったインサイトを導出して次のアクション繋げると言うアプローチの方法は現場のデータ分析でも活用できるシーンが容易に想像できますね.

ja.wikipedia.org

最後に画像処理と自然言語処理についてですが,ここは一言で述べてしまうと物足りないに尽きると思います.
やはり,昨今のトレンドを抑えた内容になっていない点は実際の現場で活きるデータ分析スキルからは程遠い内容だと感じました. 画像処理や自然言語処理の各領域で確立された技術は異なる領域の問題にも適応されることが頻繁に見受けられ,実際の現場でデータ分析に取り組んでいると様々なドメインの技術を複合的に扱って課題を解決しようとします. この点では以下で示すような別の100ノックがありますのでそちらも併用して実践してスキルを身につけていくことが必要のようにも思います.

https://yoyoyo-yo.github.io/Gasyori100knock/yoyoyo-yo.github.io

http://www.cl.ecei.tohoku.ac.jp/nlp100/#ch1www.cl.ecei.tohoku.ac.jp

意外と苦労したポイント

ここでは本書の後半部分を取り組むに当たって少し苦労した点について記載しておきます.

1点目は最適化問題の実践における環境構築です.
以下に私が利用していた環境を記載します.

私自身はPuLPを使って最適化問題に関するノックを実践していましたが,振り返ってみるとノックの実践よりも環境構築に時間を取られてしまいました.
そこで,私は環境構築が容易でかつ高機能なソルバーが使えるGoogle社提供のOR-Toolsを利用することをおすすめします.本書で扱っているライブラリとは異なるため書籍に記載している解法とは若干異なってしまいますが,ライブラリで出来ること及び実現場を想定した技術スキルという観点ではOR-Toolsを用いた方が得策だと思います.是非これから100本ノックを初める方は最適化問題はOR-Toolsを用いて解いてみてください.

インストールもPipで簡単に行えますし,線形の最適化問題であれば以下のように直感的にコーディングすることができます.

  • インストール
$ python -m pip install -U --user ortools
  • 実装例
from __future__ import print_function
from ortools.linear_solver import pywraplp

# ソルバーの定義
solver = pywraplp.Solver('simple_lp',
                             pywraplp.Solver.GLOP_LINEAR_PROGRAMMING)

# 変数の定義
# xとyは0から無限の値を取りうる整数という定義
x = solver.IntVar(0.0, infinity, 'x')
y = solver.IntVar(0.0, infinity, 'y')

# 制約条件①
# x + 2y <= 14.
constraint0 = solver.Constraint(-solver.infinity(), 14)
constraint0.SetCoefficient(x, 1)
constraint0.SetCoefficient(y, 2)

# 制約条件②
# 3x - y >= 0.
constraint1 = solver.Constraint(0, solver.infinity())
constraint1.SetCoefficient(x, 3)
constraint1.SetCoefficient(y, -1)

# 目的関数
# Objective function: 3x + 4y.
objective = solver.Objective()
objective.SetCoefficient(x, 3)
objective.SetCoefficient(y, 4)
objective.SetMaximization()

# 最適化実行
solver.Solve()
opt_solution = 3 * x.solution_value() + 4 * y.solution_value()

developers.google.com

2点目はシミュレーションの項目における処理時間についてです.
本書の解法ではPythonでループを多用しているところが垣間見られ,4重のループにて解いている解法などがありました.実際のところ問題自体は解けるのですが,処理時間として30分ほど計算している処理などもありました.本の目的からすると処理時間は目的外のため考慮していないのかもしれませんが,実際のデータ分析の世界では処理時間は結構シビアに捉える必要があると感じています.本書で扱っているデータは大きくないため問題はそれほど大きくはないですが,数GBにもなるデータを扱うようになると処理スピードは非常に大きな問題へと発展します.
なので,もう少し本書でもその点について言及してくださる良かったと感じます.皆さんもPythonにおけるコーディングスキルや高速化の実装方法は平行して勉強していきましょう.

まとめ

今回はPythonデータ分析100本ノックにおける後半50本ノックの内容を中心に実践してみた感想について記載しました.100本という限られた条件の中で幅広いドメインを扱っており,体系的な知識獲得にはとても良いコンテンツだと思います.
対象者は以前に記載した前半部分の内容の通り,データ分析を実践し始めた方や知識を再確認したい人が適当だと思いますので,該当する方や興味がある方は是非100本ノックに取り組んでみてください.

MixConvの論文を読んでみた

MixConvとは

MixConv1とはConvolutional Nueral Networkのにおいてパラメータ数を減らしつつ,高精度を実現する新しい畳み込みを提案した手法のことです.本手法はGoogleBrainのMingxingTanとQuoc V. Leが発明したものになります. MixConvがどのように優れているのかというとdepthwise convolutionにおけるカーネルサイズの増加に伴う精度低下という課題を改善した部分にあります.以下ではMixConvの論文内容をまとめていこうと思います.

概要・イントロ

通常のConvNets(畳み込み)は画像分類やセグメンテーション,多くの応用で広く使われています.
最近のConvNetsにおけるトレンドは精度と効率の両方を改善することだそうです.このトレンドに従って,MobileNetやShuffleNet,NASNet,AmoebaNet,MnasNet,EfficientNetのようにdepthwise convolutionが取り入れられ現在のConvNetsにおいてますますより利用が増えていってると論文では記載されています.
通常の畳み込みと違って,depthwise convolutionのカーネルはそれぞれの個々のカーネルに適応され,これに従ってチャネル数の計算機的なコストを削減します.しかし,depthwise convolutionのカーネルを用いてConvNetsを設計することは重要と見なされる一方で,カーネルサイズの要素は見落とされがちです.通常の畳み込みの学習は3×3のカーネルが一般的に利用されますが,最近の研究結果では5×5や7×7のような大きいカーネルサイズが潜在的にモデルの精度と効率性を改善できることを示しています.
そこで,論文内では単純にカーネルサイズを大きくすると精度が向上するのかといった疑問について言及しており,MobileNetを題材に単純にカーネルサイズを大きくしていった場合の精度の遷移を確認しています.実際の結果は以下のようなグラフになっておりカーネルサイズが大きくなるとモデルサイズが大きくなり,より多くのパラメータが利用され,非常に大きなカーネルサイズは精度と効率の両方を損なう可能性があることを示しています.

カーネルサイズを変化させた際の精度変化

論文では、モデルの精度と効率性を高めるには高解像度のパターンをキャプチャする大きなカーネルと低解像度のパターンをキャプチャする小さなカーネルの両方が必要だと述べた上で,さまざまな解像度でさまざまなパターンを簡単にキャプチャできるように、単一のたたみ込み演算でさまざまなカーネルサイズを混合する混合深さ方向たたみ込み(MixConv)を提案しています.下の図はMixConvの構造を表しており,複数のグループにチャネルを分割,異なるカーネルサイズをチャネルのそれぞれのグループに適応する仕組みを図示したものになります.

MixConvの概要

関連研究

関連研究ではEfficient ConvNetsとMulti-Scale Networks and Features,Nueral Architecture Searchの3つの観点で述べており,Efficient ConvNetとMulti-Scale Network and Featuresではそれぞれの観点でMixConvにおける差異を記載しています.そして,Nueral Architecture Searchの項目ではニューラルネットワークの設計プロセスを自動化してMixConvを用いた手動構築よりも優れたモデル(MixNet)を作成したとも述べています. 具体的な内容は割愛しますが気になる方は本家の論文を読んでみて下さい.既存研究との差異やMixNetをより詳細に理解できると思います.

MixConvの手法

MixConvの手法を簡単に述べるとチャネル(のグループ)ごとに異なる大きさのフィルターを使うようにしたDepthwise Convと言い換えられます.処理のイメージを簡略化したものを以下に示します.畳み込みの処理をg個のグループに分けて,グループごとに異なるカーネルサイズを適応して畳み込んだものをマージします.処理としては非常にシンプルでわかりやすいですよね.

MixConvの処理イメージ

また,論文内にはtensorflowを利用したmixconvの実装方法を紹介していますので,直ぐに自らの問題に適応することができます.pytorchではdepthwise_conv2dといったメソッドはないのでconv2dのパラメータにgroupのオプションがあるのでそれを利用することで再現できるようです.

def mixconv(x, filters, **args):
    G = len(filters)
    y = []
    for xi, fi in zip(tf.split(x, G, axis=-1), filters):
        y.append(tf.nn.depthwise_conv2d(xi, fi, **args))
    return tf.concat(y, axis=-1)

MixConvにおけるデザイン設計

論文では以下に示す大きく4つの観点別にパラメータやTipsに関する言及をしています.

  • グループの数
  • グループ毎のカーネルサイズ
  • グループ毎のチャネル数
  • Dilated Convolution

それぞの項目について内容を簡単に述べると,グループサイズについてはMobileNetを題材としてグループ数を4に設定することが一般的に安全としています.また,Neural Architecture Searchによってグループ数は1から5の間の範囲が安定して精度と効率性をより高める様子です.

次に各グループのカーネルサイズですが,基本的にグループには任意のカーネルサイズを設定しても良い一方で,各グループは異なるカーネルサイズを設定する必要があるとのことです.さらに,パラメータ数と計算量を減らす関係で3×3のカーネルサイズから初めて,グループ毎とに2i+1のカーネルサイズを設定するとのことです.つまり,4つのグループを想定した場合は{ 3×3, 5×5, 7×7, 9×9} といったカーネルサイズを設定するようです.

グループ毎のチャネル数は主に2つの設定方法があるそうです.1つは均一分割でそれぞれのグループは同じ数のフィルタを設定する方法で2つ目が指数分割でチャネル数の合計を 2^{-i} の指数をかけて分割した値を設定するやり方です.具体例を用いて説明すると,32のチャネル数と4つのグループを想定した場合には均一分割では(8, 8, 8, 8)で指数分割では(16, 8, 4, 4)となります.

そして,最後のDilated Convolutionについては,より大きいカーネルサイズを扱うことによって増加するパラメータ数と計算量に対するTipsで,Dilated Convolutionを使用することで効率性を向上させることができると紹介しています.一方で精度については若干低くなるという懸念があると状況を鑑みて導入する必要がありそうです.

パフォーマンス

評価はMobileNetを題材に3×3のdepthwise convolutionをMixConvに置き換えた場合の分類タスクにおいて検証を実施しています.
ImageNetを用いた分類問題のパラメータ数と計算量,精度をまとめた表を以下に記載します.

Network MobileNet V1
(#Prams, #FLOPS, mAP)
MobileNet V2
(#Prams, #FLOPS, mAP)
baseline 3×3 5.12M, 1.31B, 21.7 4.35M, 0.79B, 21.5
depthwise 5×5
mixconv 35(ours)
5.20M, 1.38B, 22.3
5.16M, 1.35B, 22.2
4.47M, 0.87B, 22.1
4.41M, 0.83B, 22.1
depthwise 5×5
mixconv 357(ours)
5.32M, 1.47B, 21.8
5.22M, 1.39B, 22.4
4.64M, 0.98B, 21.2
4.49M, 0.88B, 22.3

これをみてわかる通り,MixConvはより少ないパラメータ数とFLOPSであるにも関わらず,depthwise convolutionと同様もしくはそれ以上の精度を叩き出しています.
また,depthwise convolutionはより大きいカーネルサイズを用いることで精度低下という問題に直面するのに対して,MixConvはカーネルサイズの大きさによる精度低下の感度を鈍化させることが示唆されるため安定した精度を実現できるようです.

MixNet

さて,最後になりますが論文内ではnueral architecture serachを活用してMixConvに基づいたモデルとしてMixNetを提言しています.MixNetの詳細はここでは割愛しますが,以下のようなネットワークアーキテクチャを備えたモデルでMixConvの恩恵を簡単に受けることができます.ImageNetにおける評価ではSOTAで78.9%という高い数値を出しているため是非試しに使用してみて下さい.

MixNet-Mのアーキテクチャ

まとめ

今回はMixConvについて論文の内容をまとめてみました.
MixConvの論文を読むことでConvNetにおける動向のキャッチアップを通じながら,MixConvの活用におけるポイントを理解することができました.論文を読むことは自身が取り組んでいる課題に対しての新しい解決策を得られる機会になることもしばしばあるので継続的に読むことをおすすめします.特に自らの専門領域とは直接的に関係ない内容であったとしても読むことで新しい発見があると感じます.
今後もまた別の論文を読んだ際には内容をまとめていこうと思います.

Pythonデータ分析100本ノックを実践【前半】

Pythonデータ分析100本ノックって?

Pythonデータ分析100本ノックとは秀和システムから出版されている書籍です.現場を想定したデータ分析に必要なスキルや処理方法を問題形式で習得することができる内容になっております.また,書籍内で扱うサンプルデータなどはWebからダウンロードすることができるので書籍を購入してすぐにデータ分析の問題を解くことが可能です.
今回は100本ノックの前半50本のノックを中心に書籍を実践してみた感想について記載していこうと思います.

書籍構成

書籍で扱う内容は大きく以下になっています.

ノック数の割合についてはデータ加工から機械学習の部分が大体半分を占めており,最適化問題が4分の1くらいで残りの4分の1で画像処理と自然言語処理といったボリューム感になっています.後半部分のそれぞれの項目をもっと深掘りして問題を解きながら理解したいという人には画像処理100本ノックや言語処理100本ノックといった有志で作成されているコンテンツがWeb上に公開されているのでそちらに取り組むのが良いと思います.

https://yoyoyo-yo.github.io/Gasyori100knock/yoyoyo-yo.github.io

http://www.cl.ecei.tohoku.ac.jp/nlp100/#ch1www.cl.ecei.tohoku.ac.jp

対象者はどれくらいの層なのか?

まずは前提となる必須スキルについて記載すると,Python操作に関する知識はマストで保有していないと厳しいと思います.
例えば,Pythonのインストール方法であったりとかPythonの基本文法などはデータ分析に関する書籍のため,書籍中では解説されておりません.いきなりライブラリをインポートする処理が最初から記載されているため,標準ライブラリ以外のデータ分析用のライブラリを利用したいことがない人が本書に取り組んだとしたらImportErrorで初めからつまづくと思います.

import numpy as np
import pandas as pd

次にあると好ましいスキルとしてはデータ分析関連のライブラリに関する知識です.PandasやNumpyといったデータ分析において基本的なライブラリについては事前にどういうものかはある程度は把握しておいた方がベターだと言えます.
例えば,以下のようなcsvデータをDataFrameオブジェクトとして読み込むためのコードはデータ分析では一般的だと思いますが,ライブラリを扱ったことがない人はライブラリが何をしているのかを調べながら書籍を進めることになるためデータ分析スキルに注力することが難しいと思います.

import pandas as pd

sample = pd.read_csv('test.csv', index_col=['number'])
sample.head()

また,データ分析における基本的な理論(教師あり学習教師なし学習強化学習,分析評価指標等)が全くわかっていない人にとっても本書を進めるのはなかなか難しいのかなと思います.理由としてはデータ分析における理論的な話は一部記載されておりますが,十分な解説は記載されていないためソースコードを実践して動いたという感覚だけで満足してしまう可能性があるからです.

以上を踏まえて「Pythonデータ分析100本ノック」がどのように人に適しているのかをまとめると,やはりデータ分析の基礎を身に付けた人がこれから本格的に学習したいという人や日常的にデータ分析に取り組んでおり知識の再確認をする人向けの書籍だと感じます.

前半部分を実践してみて

それでは「Pythonデータ分析100本ノック」を実践してみた前半の50本のノックにおける個人的な感想を記載します.

まず,よかった点を述べるとデータ加工における前処理について自身が普段実装する方法よりも効率的に実装する方法を知れたことです.リアルな世界におけるデータ分析においては各種データは時系列データとなることが多いです.その点でデータフレームにおける日付の扱いなどを綺麗なソースコードで丁寧に解説・実践できる内容は素晴らしいと思いました.また,前処理を重点的に取り扱っている内容についても良かったこととして挙げられます.異なるスケールのデータフレームを加工してジョインし,学習用のデータセットを作成する内容については現場でデータ分析を実践しているかのような錯覚さえありました.
一方で,ここは微妙だなと思った点としては機械学習の評価の観点です.例えば製品需要を予測する回帰問題を想定した場合には機械学習によって得られた結果をMAEやMSE,MAPEといった評価指標で精度を確認していきます.しかし,この書籍では評価指標に関する具体的な言及はされないまま内容が進んでいくため,実際に現場で活用できる学習モデルを評価するためのスキルを身につけるためには別の学習が必要だと感じました.

ここまでで散々個人的な意見を述べてはきましたが,限られたページ数で分かりやすくデータ分析の実践方法を学ぶことができる書籍であることは間違い無いです.特に,データ分析における実践で役立つプロセスを体系的に学ぶことができる内容は素晴らしいと思いますので興味がある方は購入してみることをお勧めします.

まとめ

今回は「Pythonデータ分析100本ノック」を実践してきた中での前半部分における感想について記載しました.データ分析をこれから本格的に学習する人やデータ分析はある程度実践していて知識を再確認するといった人にとっては有益な書籍だと思うので皆様も是非読んでみてください.
後半の残り50本ノックについてはまた後日まとめていこうと思います.