まいだいありー

機械学習、技術系、日記など勉強したことのメモを書けたらなと思います。

JWT (JSON Web Token) についてメモ

JWTとは

最近 JWT認証を使う機会があったので, JWTの生成方法についてまとめます.

JWTは, 以下のような構造

{base64Encodingheader}.{base64Encodeingclaim}.{signature}


JWT生成

  • base64Encodingheader

以下のJsonbase64エンコード

{
  "alg":  署名アルゴリズム, RSA-SHA256など
  "typ": "JWT"
}


{
  "sub": ユーザの識別子
  "iat": トークンの発行時間,
  "exp":トークンの有効期限,
    ...etc
}


  • signature(署名)

HeaderとClaimをbase64エンコードした文字列を . で結合したものと秘密鍵 Keyで署名アルゴリズムによって,ハッシュ化.

signature = RSA-SHA256(Key , "{base64Encodingheader}.{base64Encodeingclaim}")


この工程でJWTが生成できます.

JWT = "{base64Encodingheader}.{base64Encodeingclaim}.{signature}"


JWT の比較

JWTを発行するサーバで既にユーザ1は登録済み時, ユーザ1から受信したJWTがユーザ1のものか比較する.


以下を受診した JWT とする

_JWT = "{_base64Encodingheader}.{_base64Encodeingclaim}.{_signature}"


ユーザ1が持ってる秘密鍵 Keyと送信されたJWTの署名部分以外を用いて, 署名アルゴリズムでhash化.

signature = RSA-SHA256(Key , "{_base64Encodingheader}.{_base64Encodeingclaim}")


そして, 受信された署名と今生成した署名を比較することで同じユーザのものか検証できる.

if signature == _signatture:
    return 1
else:
    return 0

React-Django REST FrameWorkのアプリをHerokuにデプロイ

はじめに

React-Django REST FrameWork で作った日報アプリをHerokuにデプロイする時にやることをまとめます.
このアプリについて少し補足すると, ユーザの認証には JSON Web Token (JWT) , 日報の記事管理は REST framework を用いています.

各種version

基盤となるパッケージの version を列挙しておきます.

  • npm 7.3.0
  • React 17.0.2
  • Python 3.7
  • Django 3.2.2
  • djangorestframework 3.12.4
  • django-cors-headers 3.7.0
  • heroku 7.53.1

Herokuにデプロイ

今回は React と Django は別々のプロジェクトとして Heroku へデプロイしました.

  • まずHerokuにログイン
$ heroku login


DjangoをHerokuへデプロイ

Djangoのプロジェクト作成済みのところから話を進めます. DjangoをHerokuへデプロイするときは, 前準備として色々やることがあります.

Djangoの設定

  • 自分のDjangoのプロジェクトに移動
$ cd <ProjectName>
  • Herokuの推奨パッケージをインストール
$ pip install gnucorn django-heroku whitenoise dj-database-url
  • requirements.txt へ必要なパッケージを書き出し
$ pip freeze >  requirements.txt 

ファイルに追加記述

  • [setting.py] dj-database-url の設定
ALLOWED_HOSTS = ["*"]

import dj_database_url
db_from_env = dj_database_url.config(conn_max_age=600)
DATABASES['default'].update(db_from_env)


  • [setting.py] whitenoise の設定
INSTALLED_APPS = [
    'whitenoise.runserver_nostatic',
    ...
]

...

MIDDLEWARE = [
    ...
    'whitenoise.middleware.WhiteNoiseMiddleware',
    ...
]

...

STATIC_URL = '/static/'
STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles') 
STATICFILES_STORAGE = 'whitenoise.storage.CompressedManifestStaticFilesStorage'

STATICFILES_DIRS = (
    os.path.join(BASE_DIR, 'static'),
)

STATICFILES_STORAGE = 'whitenoise.storage.CompressedManifestStaticFilesStorage'


  • [setting.py] djnago-herokuの設定
DEBUG = False
if not DEBUG:
    django_heroku.settings(locals())


  • [setting.py] cors の設定

異なるドメイン間で通信するために以下の設定を忘れると, React側でAPIを叩いても, Djangoで拒否されてしまいます.
<front-AppName> は次の章でReactのアプリをデプロイするHerokuのアプリの名前です.

CORS_ORIGIN_WHITELIST = ( 
    "https://<front-AppName>.herokuapp.com/"
)
  • [.gitignore] pushしないファイルを設定

.gitignoreをプロジェクトの root に作成し, gitignore.io から Django で検索する

<django-project-name>/settings/local.py
staticfiles/

### Django ###
*.log
*.pot
*.pyc
__pycache__/
local_settings.py
db.sqlite3
media

### Python ###
# Byte-compiled / optimized / DLL files
*.py[cod]
*$py.class

# C extensions
*.so

# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST

# PyInstaller
#  Usually these files are written by a python script from a template
#  before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec

# Installer logs
pip-log.txt
pip-delete-this-directory.txt

# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
.hypothesis/
.pytest_cache/

# Translations
*.mo

# Django stuff:

# Flask stuff:
instance/
.webassets-cache

# Scrapy stuff:
.scrapy

# Sphinx documentation
docs/_build/

# PyBuilder
target/

# Jupyter Notebook
.ipynb_checkpoints

# IPython
profile_default/
ipython_config.py

# pyenv
.python-version

# celery beat schedule file
celerybeat-schedule

# SageMath parsed files
*.sage.py

# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/

# Spyder project settings
.spyderproject
.spyproject

# Rope project settings
.ropeproject

# mkdocs documentation
/site

# mypy
.mypy_cache/
.dmypy.json
dmypy.json


  • Procfile を作成 以下を書き込みます
web: gunicorn <ProjectName>.wsgi --log-file -


  • static を生成

プロジェクトのルートに static というフォルダーを作成し, その直下に .gitKeep のファイルを作成しておきます.


これでHerokuへデプロイするための Django の設定はおしまいで, ここからはHeroku.


  • Heroku に App を作成

<back-AppName> は <back-AppName>.herokuapp.com のようにURLに含まれます

$ heroku create <back-AppName>
  • HerokuのAppとDjangoのプロジェクトを結びつける
$ git init
$ heroku git:remote -a <back-AppName> <- HerokuのAppName
  • build-pack 追加
$ heroku buildpacks:set heroku/python  
  • Herokuにpush
$ git add .
$ git commit -m "hoge"
$ git push heroku master


  • Heroku でデータベース設定やsuperuser設定
$ heroku run python manage.py migrate
$ heroku run python manage.py createsuperuser
  • アクセスしてみる

以下を実行し, /admin にアクセスできれば一応成功 (status code 500 になることもある)

$ heroku open


React を Heroku へデプロイ

React のプロジェクトの作成には, 以下を用いました.

$ npx create-react-app <ProjectName>
$ cd <ProjectName>  


ここで, Reactのプロジェクト内にある package-lock.jsonyarn.lock のどちらかを削除します. Herokuにnpm, yarnのどちらを使用するか指定するためです. (今回は, package-lock.json を削除)


また, Reactで叩くAPI のURLを https://<back-AppName>.herokuapp.com に変更するのをお忘れずに ( <back-AppName>DjangoのプロジェクトをデプロイしたHerokuのアプリの名前)


  • Heroku に App を作成

<front-AppName> は <front-AppName>.herokuapp.com のようにURLに含まれます

$ heroku create <front-AppName>
  • HerokuのAppとReactのプロジェクトを結びつける
$ git init 
$ heroku git:remote -a <front-AppName> <- HerokuのAppName
  • build-pack 追加
$ heroku buildpacks:set https://github.com/mars/create-react-app-buildpack.git    
  • Herokuにpush
$ git add .
$ git commit -m "hoge"
$ git push heroku master


push すると長々と処理が流れ, デプロイに成功すると最後にURLが表示されます. 以下でアプリにアクセス.

$ heroku open 


これで自分の場合は React-Django のアプリをHerokuのサーバ上で実行することができました.

参考

ファイルの数行を一斉削除したいとき

大量のテキストファイルがあり、先頭に URLとかいらない文字列が含まれている場合があるときに便利な コマンドをメモ

"0,Nd"は 0~ N行目まで削除し、その同じファイルに上書き保存する。

sed -i -e "0,Nd" ファイル名 ファイル名


大量にファイルがある場合は、以下のコマンドで一斉に処理できる。

for f in `ls` ; do sed -i -e "0,Nd" $f  $f ; done
rm *.tex-e

機械学習を用いたwebアプリケーション作った by Flask

はじめに

「web上で機械学習モデルにデータを投げて、結果を表示する」というサーバーサイド辺りのロジックを知りたかったので、手軽に使えるFlaskを用いて(toy)webアプリケーションを作っていきます。
(完成までの所要時間2時間程度でした)

環境

  • MacOS Mojave 10.14.1
  • Python 3.7.2
  • Flask 1.0.2
  • Pytorch 1.0.1


何を予測すんの?

機械学習のタスクは、右から Taco と Burrito という食べ物の2値分類です。

f:id:kenzo1122:20190327173024j:plainf:id:kenzo1122:20190327172848j:plain


これらのデータであらかじめ学習しておき、学習した重みをファイルに保存しときます。

Flaskでのwebアプリ

まず、Flaskを勉強

といっても Flask を触るのは初めてだったので、youtubeやqiitaでさらっと勉強しましたが、結構直感的に使えるので学習コストは低く感じました。

qiita.com

www.youtube.com


webアプリの概要

今回のFlaskで作成したwebアプリのファイル構成です。

├── classification.py  # 予測するスクリプト
├── main.py              # Flaskのスクリプト
├── model               
│   └── resnet.prm      # 学習済みモデル
├── static
│   └── css
├── templates
│   └── home.html
├── data
│   ├── buritto
│   └── taco
└── upload         # 入力されたデータを格納するファイル


プロセスの流れ

簡単にこのwebアプリのプロセスの流れは以下のようになります。

  1. "main.py" を実行するとサーバー起動ので接続
  2. web上で画像を投げると、"upload"にその画像が保存される。
  3. "classfication.py" の予測モデルにその"upload"に保存された画像を入力し、予測結果を返す。
  4. web上にその画像と予測結果を表示する


DEMO

実際に動かしてみると....

f:id:kenzo1122:20190327192053g:plain


ソースコード

ソースコードはこちらです。

github.com

RankNetの実装

はじめに

ランク学習に興味が湧いたのでRankNetで遊びます。 ランク学習とは、ある入力データをランクモデルに入力することで、任意の順序に並び替えて出力してくるものです.(らしい)

こちらを参考にPytorchで実装してみようかと思います。

RankNetの概要

まず、2つのデータとそれらに対するランク {\displaystyle (x_i,x_j) , \;(U_i, U_j)} をサンプリングします。 これを PairWise手法と言うらしいです。ここで、2つのデータのランクの関係が {\displaystyle U_i > U_j} と仮定します。

次に、あるランクモデル\(f\) を用いて2つのデータのスコア{\displaystyle s_i = f(x_i),s_j = f(x_j) } を計算します。(上記の仮定より {\displaystyle s_i >s_j } となるような関数 {\displaystyle f} を学習します。)

そして、2つのスコアを用いて {\displaystyle U_i > U_j} となる確率を計算し、このモデルの予測値となります.

{\displaystyle P_{ij} = P(U_i > U_j) = \frac{1}{1+e^{- \sigma (s_i - s_j)}}}

直感的には、{\displaystyle s_i} が大きい場合入力は正なので確率は大きくなり、逆に {\displaystyle s_j} が大きい場合入力は負なので、 確率は小さくなることがわかります。

最後におなじみの損失関数(CrossEntropyLoss)を導入し、これを最小化するように学習します。

f:id:kenzo1122:20190304101157p:plain:w300

そして、2つのランクの大小によって以下のようにラベル\(\bar{P_{ij}} \)を定義します。


\bar{P_{ij}} = \frac{1}{2}(1 + S)\;\;\;\;\;,
S \;= \;
\left\{
  \begin{array}{c}
    1 \;\;\;(U_i > U_j) \\
    -1 \;\;(U_i &lt; U_j)\\
    0 \;\;\;(U_i = U_j)
  \end{array}
\right.

実験

今回は猫の画像のランクを出力するモデルを構築します。

データセット

オックスフォード大学が公開している動物画像の Visual Geometry Group - University of Oxfordを用います。

このデータセットには12種類(各200枚ずつ)の猫の画像が含まれており、簡潔化のために3種類に絞った物を学習データとします。

f:id:kenzo1122:20190304203938p:plain (左から、Bengal、Russian Blue、Brimanの3種類)

ランクは、0 : Russian Blue, 1: Brima , 2 : Briman と定義します。なので、ランクモデルにこの3種を入力した時、この順番で出力されることを期待します。

学習モデル

学習モデルも簡潔化のためにResNet18を用いて、最後の全結合層の出力数を1000 → 1 に変えて、転移学習させます。

コードはこちら

github.com

結果

テストとして、3種のネコを2枚ずつ合計6枚をランクモデルに入力した結果が以下となります。

f:id:kenzo1122:20190304205124p:plain

定義したランクの通りに出力されました.

まとめ

今回は RankNetと言うランク学習の1つを実験しました。ネットワーク自体は転移学習でしたのでよくあるパターンのもので、一番の肝は誤差関数をどう設計するかでした。RankNetの後に出た ListNet と言うのもあるので、実装してみたいですね。

混合正規分布の推定(Gibbs Sampling)

はじめに

今回はガウス混合分布の平均と精度が未知の場合のギブスサンプリングについてメモしとこうと思います。 また、解釈の間違いなどがあったら教えていただきたいです。

ガウス混合分布

ガウス混合正規分布の数式は以下の通りで、潜在変数とクラスタごと平均と精度行列を導入します。

{\displaystyle
p(\bf{x_n}| \bf{s_n}, \bf{μ}, \bf{ \Lambda}) = \prod_k^{K} N( \bf{x_n} | \bf{μ}_k , \Lambda_k)^{s_{n,k}}
}

また、 \bf{π} はカテゴリ分布に従います。


{\displaystyle
p(\bf{s_n}| \bf{ π})= Cat(\bf{ s_n} | \bf{π})
}

{\displaystyle \sum_k^{K} \pi_k = 1 }


2つのパラメーターに対して事前分布を導入しますが、この場合の共役事前分布はガウス・ウィシャート分布です。


{\displaystyle
p( \bf{μ} ,\bf{ \Lambda}) = N(\bf{μ}_k | \bf{m}, (\beta \Lambda_k)^{-1})W( \bf{ \Lambda_k}| \nu, \bf{W})
}


ギブスサンプリング

ギブスサンプリングでは、潜在変数と分布に関連するパラメータの事後分布に分けてサンプリングすることを考えます。

{\displaystyle p( \bf{S}, \bf{ μ}, \bf{ \Lambda}, \bf{π}| \bf{X})  }

{\displaystyle \bf{S} \sim p( \bf{S}| \bf{X}, \bf{ μ}, \bf{ \Lambda}, \bf{ π}) }

{\displaystyle \bf{ μ}, \bf{ \Lambda}, \bf{ π} \sim p( \bf{ μ}, \bf{ \Lambda}, \bf{ π}| \bf{X} ,\bf{S} ) }


潜在変数の事後分布

ベイズの定理を使って、 \bf{S} に関係する項だけをとります。


{\displaystyle p( \bf{S}| \bf{X}, \bf{ μ}, \bf{ \Lambda}, \bf{π}) \propto p(\bf{X} | \bf{ μ}, \bf{ \Lambda, \bf{S}})p(\bf{S} | \bf{π}) = \prod_n^{N} p( \bf{x_n} | \bf{s_n} ,\bf{ μ}, \bf{ \Lambda})p( \bf{s_n} | \bf{π}) }


上記の式に対数を取って展開した上で  s_{n,k} でまとめると(1項は指数部以外は定数とする)、


{\displaystyle \log p( \bf{x_n} | \bf{s_n} , \bf{ μ}, \bf{ \Lambda}) = \sum_k^{K}  s_{n,k} \Big \{ \frac{1}{2} ( \bf{x_n} -\bf{μ}_k ) \Lambda_k ( \bf{x_n} - \bf{μ}_k)^{t} + \frac{1}{2} \log | \Lambda_k | + \log \pi_k \Big \} + const  }


よって、カテゴリ分布になります。


{\displaystyle \bf{s_n} \sim Cat ( \bf{s_n} | \bf{η_n}  ) }

{\displaystyle  η_{n,k} \propto  exp \big \{ \frac{1}{2} ( \bf{x_n} - \bf{μ}_k) \Lambda_k ( \bf{x_n} - \bf{μ}_k)^{t} + \frac{1}{2} \log | \Lambda_k |  + \log \pi_k \big \} }

{\displaystyle \sum_k^{K} η_{n,k} = 1 }


パラメータの事後分布

ベイズの定理を用いて変形すると、グラフィカルモデルで考えると分かるのですが、混合率とそれ以外のパラメータの分布は独立に分解できます。

{\displaystyle p( \bf{ μ}, \bf{ \Lambda}, \bf{π}| \bf{X} ,\bf{S} ) \propto p(\bf{X} | \bf{ μ}, \bf{ \Lambda, \bf{S}})p( \bf{ μ}, \bf{ \Lambda})p(\bf{S} | \bf{ π}) p( \bf{ π}) }

潜在変数と混合率に関連した分布の後方の2項は、ポアソン混合分布のと同じなのでそれを用いて、

{\displaystyle p( \bf{S} | \bf{π})p( \bf{π}) =  Dir( \bf{π} | \bf{\hat{α}}) \\
\hat{\alpha_k} = \sum_n^{N}s_{n,k} + \alpha_k }

今回は前方の2項を計算すれば良いので、

{\displaystyle p(\bf{X} | \bf{ μ}, \bf{ \Lambda, \bf{S}})p( \bf{ μ}, \bf{ \Lambda}) = \sum_k^{K} \Big \{ \sum_n^{N}  \big \{ s_{n,k} \log N( \bf{x_n} |\bf{μ}_k, \bf{ \Lambda_k}^{-1}) \big \}N(\bf{μ}_k| \bf{ m_k}, \bf{ ( \beta \Lambda_k})^{-1})W( \bf{ \Lambda_k }| \nu, \bf{W}) \Big \} }


また、以下の関係があるので、最初に平均の分布を求めてから精度を求めます。


{\displaystyle p(μ,\bf{ \Lambda} | \bf{X},\bf{S})=p(μ|\Lambda,\bf{X},\bf{S})p(\Lambda | \bf{X},\bf{S}) \tag{1} }


平均 μ

 \bf{ μ} に関する項以外は定数とみなして平方完成すると、

{\displaystyle - \frac{1}{2} \bf{μ}_k^{t} ( \sum_n^{N} s_{n,k} + \beta) \Lambda_k \bf{μ}_k +( \sum_n^{N} s_{n,k} \bf{x_n} + \beta m )  \Lambda_k \bf{μ}_k + const }

{\displaystyle = - \frac{1}{2} ( \bf{μ}_k - \hat{ \bf{m_k}})^{t} \hat{ \beta_k} \Lambda_k ( \bf{μ}_k- \hat{ \bf{m_k}}) + const }

よって、正規分布になります。

{\displaystyle \bf{μ}_k \sim N(\bf{μ}_k| \hat{ \bf{ m_k}}, ( \hat{ \beta_k} \Lambda_k)^{-1}) }

{\displaystyle \hat{ \bf{ m_k}} = \hat{ \beta_k}^{-1}( \sum_n^{N} s_{n,k} \bf{x_n} + \beta m)}

{\displaystyle \hat{ \beta_k} = \sum_n^{N} s_{n,k} + \beta }


精度 \bf{ \Lambda}

(1)の関係を変形すると、精度を求められる形になります。

{\displaystyle \log p( \bf{\Lambda} | \bf{X},\bf{S})= \log p(μ, \bf{ \Lambda} |\bf{X},\bf{S}) - \log p(μ| \bf{ \Lambda} ,\bf{X},\bf{S}) }

上記の該当する分布を代入し、 \Lambda に関する項以外を定数とみなして、 \Lambda でまとめると、

f:id:kenzo1122:20210517145007p:plain


よって、ウィシャート分布になります。

{\displaystyle  \bf{ \Lambda_k} \sim W( \bf{ \Lambda_k} | \hat{ \nu_k} ,\hat{W_k}^{-1}) }

{\displaystyle \hat{ \nu_k} =  \sum_n^{N} s_{n,k} + \nu }

{\displaystyle \hat{W_k}^{-1} = \sum_n^{N}s_{n,k} \bf{x_n}^t \bf{x_n} + \beta \bf{m}^t \bf{m} + W^{-1}- \hat{ \beta_k} \hat{ \bf{m_k}}^t \hat{ \bf{m_k}} }


これで、実装に必要な要素は揃いました!


実装

実験で用いるデータは、データ数N = 400、クラスタK = 4、次元D = 2、です。

f:id:kenzo1122:20190325172425p:plain


ソースコード

ポアソン混合分布の時より少し複雑だったので、関数を用いて実装しました。

github.com


結果

60回学習を回した結果です。 以下の画像は左から、「元データ」、「元データへの潜在変数によるクラスタリング」、「推定したパラメータによるサンプリング(N=400)」となっています

f:id:kenzo1122:20190325172428p:plain


推定したパラメータによるガウス分布からのサンプリングの結果は、かなり似た値をサンプリングできています。

また潜在変数による色分けは、クラスタ1と3が少し怪しいので試しに個別に見てみると、元データ通りに潜在変数が振り分けられてることがわかります。

f:id:kenzo1122:20190325172616p:plainf:id:kenzo1122:20190325172618p:plain