チラ裏備忘録

情報整理

クロスエントロピー誤差【損失関数】

『TensorflowとKerasで動かしながら学ぶディープラーニングの仕組み』から引用・抜粋しました.

概要

2つの値を元に,ウイルスに感染しているか,そうでないかを判定するネットワークを作りたい.
この時,横軸に{x_1},縦軸に{x_2}を取る散布図を考え,感染と非感染を線引きするための境界線,一次関数{f(x)}を以下のように定義する.

{
f(x_1, x_2) = w_1x_1+w_2x_2+b = 0
}

{f(x_1, x_2}が0のときが最も曖昧(境界線上)で,正の方へ大きくなるにつれて非感染,負の方へ大きくなるにつれて感染のプロットが多くなる.

確率への変換

{f(x_1, x_2)}の値が境界線から離れるにつれて±∞に変化する性質を利用し,関数の値を0~1の確率に落とし込むためにシグモイド関数を利用する.

{
\sigma(x) = \frac{1}{1+exp(-x)}
}

f:id:spookyboogie:20200513025737p:plain:w300
sigmoid_function

つまり,2つの値に対して,ウイルスに感染している確率は,次式で計算される.
{
P(x_1,x_2) = \sigma(f(x_1,x_2)) = \sigma(w_1x_1+w_2x_2+b)
}

ここで,n番目のデータに対して正しく予測する確率を考えると,

{
P_n = 
\begin{cases}
P(x_{1n},x_{2n})&&t_n = 1の時\\
1-P(x_{1n},x_{2n})&&t_n = 0の時
\end{cases}
}

となる.
{t_n}は正解データを表しており,感染データに対しては1,非感染データに対しては0がセットで与えられる.

そしてこれを一つの数式にまとめると,

{
P_n = \left\{P(x_{1n},x_{2n})\right\}^{t_n}\left\{1-P(x_{1n},x_{2n})\right\}^{1-t_n}
}

となる.
{t_n}が1の時は,右の大括弧部分は0乗(=1)となり,反対に0の時は左の大括弧部分が0乗(=1)になり,片方だけが残ることから,正しく一つの数式にまとめられていることがわかる.

すべてのデータについて考える

上式はn番目のデータに対する正解率であった.ここで,すべてのデータに対する正解率を考えると以下のようになる.

{
\begin{eqnarray}
P &=& P_1 \times P_2 \times \dots \times P_N\\
&=& \prod_{n=1}^N P_n\\
&=&  \prod_{n=1}^N \left\{P(x_{1n},x_{2n})\right\}^{t_n}\left\{1-P(x_{1n},x_{2n})\right\}^{1-t_n}
\end{eqnarray}
}

この確率Pが,パラメータ{w_1,w_2,b}を評価する基準となる.すなわち,この確率が高くなるようにパラメータを調節する.

このように,「与えられたデータを正しく予測する確率を最大化する」方針でパラメータを調節する手法を,最尤推定と呼ぶ.

大量の掛け算による懸念

上式では,シグモイド関数によって0~1に落とし込まれた大量の数値による掛け算が計算されるが,ここで一つ問題が発生する.それは小さな値同士の掛け算によってアンダーフロー(一つの値を表すための決められたデータ長を超えてしまい,値を正しく表現できなくなること)が起き,精度の低下を招いてしまう.

この問題を解決するため,次式の誤差関数を定義し,これを最小化する問題へと変換する.

{
E = -\log{P}
}

f:id:spookyboogie:20200513033901p:plain
graph of log(x)

※マイナスがついている点に注意(上記グラフを反転した形となっている)

上記グラフのように{y = \log{x}}は単調増加の関数であるから,{P}を最大にすることと,{E}を最小にすることは同値である.

そして,対数法則
{
\log{ab} = \log{a} + \log{b}\\
\log{a^n} = n\log{a}
}
より以下が成り立つ.
{
\begin{eqnarray}
E &=& -\log \prod_{n=1}^N \left\{P(x_{1n},x_{2n})\right\}^{t_n}\left\{1-P(x_{1n},x_{2n})\right\}^{1-t_n}\\
&=& -\sum_{n=1}^N\left[t_n\log{P}(x_{1n},x_{2n})+(1-t_n)\log{\left\{1-P(x_{1n},x_{2n})\right\}}\right]
\end{eqnarray}
}

こうして,よく目にするクロスエントロピーの式が導かれる.

余談

他の書籍におけるクロスエントロピーの解説では主に『上式{E}を最小化する』とだけ書かれている場合が多いが,本書籍では丁寧に解説されていたのでメモしておいた.
最尤推定法という統計的手法が式の導出途中で用いられていたとは驚き.

Numpy sort() argsort()

np.sort(x)

xを昇順でソートしたNumpy配列を返す.

x = np.random.randint(1, 100, size=10)
# array([93, 70, 18, 86, 85, 83, 66, 62, 19, 46])

np.sort(x)
# array([18, 19, 46, 62, 66, 70, 83, 85, 86, 93])
x.sort()の注意点

x.sort()とすると,対象は破壊的に上書きされてしまうので注意.

x.sort() # 戻り値はNone
x
# array([18, 19, 46, 62, 66, 70, 83, 85, 86, 93])

破壊的処理と非破壊的処理 | SaintSouth.NET

np.argsort(x)

xを昇順でソートした際の,インデックスを取得する.

x = np.random.randint(1, 20, size=10)
# array([10,  3, 15, 18,  6,  2, 19, 16,  9,  4])

np.argsort(x)
# array([5, 1, 9, 4, 8, 0, 2, 7, 3, 6], dtype=int64)

# こんな(非効率な)書き方もできる!
for i in np.argsort(x):
    x[i]
# 2
# 3
# 4
# 6
# 9
# 10
# 15
# 16
# 18
# 19

x.sort()と異なり,x.argsort()は非破壊で,戻り値はnp.argsort(x)と同様.

tips(降順にしたい)

argsortメソッドにNumpy配列を渡す際,-x(もしくは-1*x)とすることで降順にインデックスを取得できる.

for i in np.argsort(-x):
    x[i]
# 19
# 18
# 16
# 15
# 10
# 9
# 6
# 4
# 3
# 2

Python スーパークラスとサブクラス 継承

書式

class SubClass(SuperClass):
    def __init__(self):
        # 処理

このように記述することで,サブクラスはスーパークラスの持つメソッドを受け継ぐことができる.
例.

class Parent: # スーパークラス
    def __init__(self):
        pass
    
    def multiply(self, a, b): # 掛け算
        print("{} x {} = {}".format(a, b, a * b))

class Child(Parent): # サブクラス(Parentクラスを継承)
    def __init__(self):
        pass

    def add(self, a, b): # 足し算
        print("{} + {} = {}".format(a, b, a + b))

kodomo = Child() # インスタンス化

kodomo.add(2, 3)
kodomo.multiply(2, 3) # スーパークラスのメソッドを利用できる

子の力だけでは足し算しかできないが,親から掛け算の知識(関数)を継承することで,子は掛け算ができるようになる.

Python 正規表現(matchとsearchの違い,subの使い方)

matchとsearchの違い

違いを簡単に言うと,

match…先頭から探索(文章の途中にマッチするものがあってもNoneとなる)

re.match(r"Hello.*", "aaaaaaaaHello, world!")
# None

search…文章の途中からでもマッチする

result = re.search(r"Hello.*", "aaaaaaaaHello, world!")
result
# <re.Match object; span=(8, 21), match='Hello, world!'>
result.group(0)
# Hello, world!

# 開始位置,終了位置,範囲を取得
result.start() # 8
result.end() # 21
result.span() # (8, 21)

re --- 正規表現操作 — Python 3.8.3rc1 ドキュメント

re.search(pattern, string, flags=0)

string を走査し、正規表現 pattern がマッチを生じさせる最初の場所を探して、対応する マッチオブジェクト を返します。文字列内にパターンにマッチする場所がなければ None を返します。これは文字列のどこかで長さ 0 のマッチを見つけるのとは異なることに注意してください。

re.match(pattern, string, flags=0)

string の先頭で 0 個以上の文字が正規表現 pattern にマッチすれば、対応する マッチオブジェクト を返します。文字列がパターンにマッチしなければ None を返します。これは長さ 0 のマッチとは異なることに注意して下さい。

subの使い方

正規表現を用いて文字列を置換できる.

msg = "test123hello"
re.sub(r"[0-9]*", "", msg)
# testhello

re.sub(pattern, repl, string, count=0, flags=0)

string 中に出現する最も左の重複しない pattern を置換 repl で置換することで得られる文字列を返します。 パターンが見つからない場合、 string がそのまま返されます。 repl は文字列または関数です。 repl が文字列の場合は、その中の全てのバックスラッシュエスケープが処理されます。 \n は 1 つの改行文字に変換され、 \r はキャリッジリターンに変換される、などです。 ASCII 文字のエスケープで未知のものは将来使うために予約されていて、エラーとして扱われます。 それ以外の \& のような未知のエスケープは残されます。 \6 のような後方参照は、パターンのグループ 6 がマッチした部分文字列で置換されます。

line-bot-sdkのメモ

GitHub - line/line-bot-sdk-python: LINE Messaging API SDK for Pythonline-bot-sdk-python/README.rst at master · line/line-bot-sdk-python · GitHubにあるサンプルプログラムの一部を自分用にメモ.

色々読み込み

# linebot.modelsから処理したいイベントをimport
from linebot.models import (
    MessageEvent, TextMessage, TextSendMessage,
)

# MessageEvent…メッセージを受け取った時に実行
@handler.add(MessageEvent, message=TextMessage)
def handle_message(event):
    line_bot_api.reply_message(
        event.reply_token,
        TextSendMessage(text=event.message.text))

@handler.add(MessageEvent, message=TextMessage)
の部分で,どのイベントが起きた時にどういった処理を実行するかを指定している(多分)
イベントには

  • MessageEvent
  • FollowEvent
  • UnfollowEvent
  • JoinEvent
  • LeaveEvent
  • MemberJoinedEvent
  • MemberLeftEvent
  • PostbackEvent
  • BeaconEvent
  • AccountLinkEvent

などがあるらしい.
line-bot-sdk-python/events.py at master · line/line-bot-sdk-python · GitHub
Messaging API reference | LINE Developers

サンプルプログラムの全容解説は以下のブログがわかりやすかった.
line-bot-sdk-pythonを使ってみた - 雑食日誌

オマケ

サンプルプログラム実行時(テキストメッセージを受け取った時)のeventの内容
Messaging APIリファレンス | LINE Developersより

{
  "replyToken": "nHuyWiB7yP5Zw52FIkcQobQuGDXCTA",
  "type": "message",
  "mode": "active",
  "timestamp": 1462629479859,
  "source": {
    "type": "user",
    "userId": "U4af4980629..."
  },
  "message": {
    "id": "325708",
    "type": "text",
    "text": "Hello, world!"
  }
}

event->message->text(受け取ったメッセージ)をそのまま返信することでオウム返ししている.

Flaskの超基本的な使い方

自分なりにまとめるため,【Python】フレームワークFlaskの基本をマスター | 侍エンジニア塾ブログ(Samurai Blog) - プログラミング入門者向けサイトより抜粋させていただきました.

超基本

from flask import Flask
app = Flask(__name__)
 
@app.route('/')
def hello():
    hello = "Hello world"
    return hello
 
if __name__ == "__main__":
    app.run()

実行

$ python3 hello.py

デコレータによって,hello関数(及び戻り値)に何らかの処理がなされてウェブ上に適切に埋め込まれる仕組みなのだろう(適当)

基本(HTML読み込み)

from flask import Flask, render_template
app = Flask(__name__)
 
@app.route('/')
def hello():
    html = render_template('index.html')
    return html
 
if __name__ == "__main__":
    app.run()

ポイント

  • render_templateをimportする
  • ルート直下にtemplateディレクトを作成し,htmlファイルを置く

変数の利用

HTMLファイル内に{{変数名}}という記述を埋め込み,

@app.route('/')
def hello():
    html = render_template('index.html', var="hatena")
    return html

というふうにrender_templateの引数に変数名と値をセットすることで,変数をページに埋め込むことができる.

CSS, Javascriptの適用

  • ルート直下にstaticディレクトを作成
  • そこに適用したいCSSJavascriptファイルを置く
  • HTMLファイルから読み込む(< link rel="stylesheet" href="/static/test.css">)

if __name__ == "__main__"の意味

Pythonでよく見る

if __name__ == "__main__":
    main()

という表記に関して,とてもわかりやすく書かれている記事があったので,自分用にメモさせていただいた.(記事書かずにリンク先読めばよくね?)
Pythonのif __name__ == "__main__" とは何ですか?への回答 - Python学習チャンネル by PyQ

説明

わざわざ上記のような書き方でmain関数を実行する意味は,
importされた時に関数が勝手に実行されないようにするためである.

以下のようなsayHello.pyというファイルが存在するとする.

# sayHello.py

def main():
    print("Hello!")

if __name__ == "__main__":
    main()

このファイルを…

インポート時(import sayHello.py)

__name__は,"sayHello"という文字列
→ main関数は実行されない

コマンドラインから実行した時($ python3 hello.py)

__name__は,"__main__"という文字列
→ if文がTrueになるので,main関数が実行される

つまり,__name__という特殊な変数がimport時とコマンドラインから直接実行した場合で異なることを利用している.


もし仮にsayHello.pyの中身が

# sayHello.py

def main():
    print("Hello!")

main()

のようにif文を除いた場合,import時もmain関数が実行されてしまう.