オルトプラスエンジニアの日常をお伝えします!

改めて「ITエンジニアのための機械学習理論入門」を改めて読む 〜第2章前編〜

こんにちは。オルトプラスラボに入って2週間と4日の橘です。

今回は前回に引き続き、2章を読んでいきます。
2章は機械学習の基礎中の基礎である2乗誤差についてです。
基礎とは言え、微分や行列が出てくるため、その辺をじっくり見ていきたいと思います。

多項式近似と誤差関数の設定

多項式近似とは何か、を見ていく前に、多項式近似のイメージを次の画像で見てみましょう。



多項式近似とは、「適当な線を引き、学習させたい点(データ)に近づくように線を曲げる」ようなイメージです。まず、これを頭のなかに入れておいてください。

f:id:s_tachibana:20170711175337p:plain


それでは数学的な説明に移ります。多項式とは、

 f(x) = w_{0} + w_{1} x + w_{2} + x^{2} + ... + w_{M} x^{M}

を言います。上の図でいうところの青い線が多項式です。つまり、この多項式を色々動かしてデータを分析したり予測したりしていくわけです。

ちなみに、予測したい各点を  (x_{1},t_{1}), (x_{2}, t_{2}), ... , (x_{N}, t_{N}) と表します。例えば、 x_{1}地点の f(x_{1})との差は次のように見ることができます。

f:id:s_tachibana:20170711185733p:plain

この差を縮めるように、線である多項式を調整していきます。この時、多項式の中の xはデータの値のため、変更できません。そのため、変更できるパラメータは w_{0}, w_{1}, ..., w_{M}です。つまり、機械学習で求めていく値はこの w_{0}, w_{1}, ..., w_{M}ということになります。このことはとても重要なため、よく覚えておいてください。

各データの点と線の差が次の式です。2乗誤差といいます。

 E_{D} = \frac{1}{2} \sum_{n=1}^{N} \{ f(x_{n}) - t_{n} \}^2

なぜ  \frac{1}{2}しているのかというと、後々の計算を楽にするためです。 \frac{1}{2}しても誤差が0になるわけではないので、多めに見てください。各データと線の差である f(x_{n}) - t_{n} を2乗しているのは、すべての差を正の値にするためです。なぜ正の値にする必要があるかというと、例えば3つの点の誤差を見たときに(-0.5, 1, -0.5)だったとき、誤差を足してしまうと0になってしまい、正確にデータを予測しているように見えてしまいます。こうなることを防ぐために、すべての差を2乗しているわけです。この誤差が小さくなるように、 w_{0}, w_{1}, ..., w_{M}の値を求めていきます。


ここからがP64の数学徒の小部屋の解説に入ります。ここで微分が登場します。なぜ微分が登場するかは、たまたま偶然ミラクルに同じ苗字の橘氏がデータホテル様のテックブログで解説してくれているので、そちらをご参照ください。いやー、偶然ってあるんだなー。

datahotel.io

要するに、「ある地点で微分した結果が0のとき、その地点で最大値か最小値になる」という性質があります。これを、各 w_{0}, w_{1}, ..., w_{M}について求めていくわけです。 w_{0}, w_{1}, ..., w_{M}の中のある w_{m'}で微分したときに0になると仮定します。(あえて本の中での m m'とが逆にしています。)

 \sum_{n=1}^{N} ( \sum_{m=0}^{M} w_{m} x_{n}^{m} - t_{n} ) x_{n}^{m'} = 0

一見複雑に見えますが \sumは足し算ですので、順番を変更する事ができます。本の中でも「作為的」と書いてありますが、この計算を行列で表現するための工夫です。

 \sum_{m=0}^{M}w_{m} \sum_{n=0}^{N} x_{n}^{m} m_{n}^{m'} - \sum_{n=0}^{N} t_{n} x_{n}^{m'} = 0

この式は m'について微分した結果ですが、各mについて微分した結果を行列の形に表したものが、次の式です。

 w^{T}\Phi^{T}\Phi - t^{T} \Phi = 0

各行列は本の65ページのとおりです。なぜ、こうなるか困惑した方も多いのではないかと思います。ですので、N=2、M=2のときで計算を試してみました。行列の演算の仕方はデータホテル様のテックブログを御覧ください。


datahotel.io


f:id:s_tachibana:20170720125117j:plain

f:id:s_tachibana:20170720130323j:plain


行列の中の式が

 \sum_{m=0}^{M}w_{m} \sum_{n=0}^{N} x_{n}^{m} m_{n}^{m'} - \sum_{n=0}^{N} t_{n} x_{n}^{m'}

と同じになっています。行列の各行が w_{0}, w_{1}, w_{2}について微分した式と一致していることがわかるかと思います。これをmが0からMのときまで実行しても同じ結果が得られます。この

 w^{T}\Phi^{T}\Phi - t^{T} \Phi = 0

はwについての方程式ですので、w に関して求めると次のような結果になります。

 w = (\Phi^{T} \Phi)^{-1} \Phi^{T}t

 \Phi  x_{1}, x_{2}, ..., x_{n}からなる行列ですので、既にわかっているデータです。また、 tも各 t_{1}, t_{2}, ..., t_{n}からなるベクトルですので、既にわかっているデータです。つまり、この行列の計算をしてしまえば、最も適切な w_{0}, w_{1}, ..., w_{m} が求められます。


(行列の正定値性、ヘッセ行列に関しては次回解説します。)

サンプルコード

Numpyのnp.polyfitメソッドを使うことで、簡単に多項式近似できます。

import numpy as np
import matplotlib.pyplot  as plt
import pandas as pd
from pandas import Series, DataFrame
from numpy.random import normal

# データセットを用意
def create_dataset(num):
    dataset = DataFrame(columns=["x", "y"])
    for i in range(num):
        x = float(i)/float(num-1)
        # 平均0.0、分散0.3の正規分布を誤差として与えている
        y = np.sin(2 * np.pi * x) + normal(scale=0.3)
        dataset = dataset.append(Series([x, y], index=["x", "y"]), ignore_index=True)
    return dataset

if __name__ == "__main__":
    // データセットを作る
    df = create_dataset(10)

    X = df.x.values
    y = df.y.values
    x = np.arange(0, 1.1, 0.01)

    // データセットをプロットする
    plt.scatter(X, y)
    // 近似した線を引く
    plt.plot(x, np.poly1d(np.polyfit(X, y, 4))(x), color="r")
    plt.show()


実行結果は以下のとおりです。

f:id:s_tachibana:20170720211226p:plain


次回

次回は第2章の後半と今回紹介できなかった正定値性、ヘッセ行列に関して解説します。

改めて「ITエンジニアのための機械学習理論入門」を改めて読む 〜第1章〜

みなさんこんにちは。オルトプラスラボに入ったばかりの橘です。
よろしくお願いします。

もう機械学習、すごいですね。
機械学習と言うか、ディープラーニングというか、人工知能というか。
すべてトレンドの域を通り越して、ベーシックになっています。


さて、機械学習の入門書にこのような名著があります。


ITエンジニアのための機械学習理論入門

ITエンジニアのための機械学習理論入門

現在、Googleでご活躍されている中井悦司さんが執筆された「ITエンジニアのための機械学習入門」という本です。この本は機械学習に必要な数学のセンテンスを解説しつつ散りばめつつ、あまり数学的な理解とかけ離れないようにソースコードが書かれているため、入門書としては最適な本だと思います。そんな本を不躾ながら更に噛み砕いてみようというのがこの連載です。この本を手元に、数学的な理解の支えをできたら幸いです。手元にない方はポチりましょう!

また、本の中のソースコードは数学的な理解を目的にアルゴリズムを1から実装していますが、面倒なので 楽したいなので 怠けたいので とても便利なライブラリである「numpy」や「scikit-learn」がありますので、そちらで簡単に実装してみようと思います。


今回は第1章です。第1章は正規分布についてお話したいと思います。

正規分布とは

正規分布とは、次のような数式です。


 N( x | \mu, \sigma^2 ) = \frac{1}{\sqrt(2 \pi \sigma^2 )} \mathrm{e}^{ - \frac{(x - \mu)^2}{2 \sigma^2}}

上の式を見てわかった方は、ブラウザタブをそっ閉じしてください。


正規分布がなんなのかと言われると、「データって結構、平均値に集まるよね〜」というようなものをちょっとだけ難しく書いている式です。ちょっとだけ難しく書いているのにも理由があって、正規分布はとても便利な式になっています。
という話は後に取っておいて、正規分布をグラフで表現するとよく見覚えのあるかと思われるグラフになります。



f:id:s_tachibana:20170707175833p:plain

このグラフは平均が0の正規分布です。ですので、ある確率的に起こる事象がこの正規分布に従うときというのは、「0の付近の値を取りやすいよ」という意味になります。また、分散という考え方があります。分散というのは「どれくらい平均より遠い値に散らばりやすいか」を表す値です。平均より遠くの値を多くとるようなデータの場合は分散の値が大きくなり、グラフがなだらかになります。


f:id:s_tachibana:20170707181254p:plain


逆にデータがある値に集中するときは先の尖ったグラフになります。

f:id:s_tachibana:20170707181400p:plain


ここで確率あるあるのサイコロの例に一度移ります。サイコロは、どの目も同じ確率で出るサイコロです。この時、「2〜5の値を取る確率は?」と言われると、

 \frac{1}{6} + \frac{1}{6} + \frac{1}{6} + \frac{1}{6} = \frac{2}{3}

であることはおそらくすぐにわかると思います。同じように正規分布も「足す」ことで、ある値からある値までを取る確率を求めることができます。具体的にはある値からある値まで面積を求めていきます。

f:id:s_tachibana:20170707184201p:plain


また、サイコロも「1〜6の値をとる確率は?」といわれると当然1になりますが、ここが正規分布の便利なところで、正規分布もすべての面積を求めると1になります。ここが正規分布が便利と言われる所以です。

f:id:s_tachibana:20170707185238p:plain


ちなみに、1章や2章などでデータに誤差を与えている部分に正規分布が使われていますが、これは次の図のように誤差を与えています。

f:id:s_tachibana:20170707190013p:plain

見づらいかもしれませんが、極端な誤差を与えることがないよう、極力平均0に近い範囲で誤差を出しているわけです。正規分布はこのような場面でも使われます。

まとめ

この連載は、少なくとも「ITエンジニアのための機械学習入門」が終わるところまでは続けます!(宣言)この記事はあくまでも「ITエンジニアのための機械学習入門」の補助になるといいなぁ、というつもりで書いていますので、本をざっと読み、対応する記事を読んだあとにもう一度本を読むことで理解が進むはずです!(希望)今からでも遅くはありません。一緒に機械学習を学びましょう!(切望)

ブロックチェーン記事の執筆について

みなさんこんにちは。オルトプラスラボの竹田(id:mitsutaka-takeda)です。

今回、技術評論社が出版しているSoftware Designにブロックチェーンの記事を書く機会を頂いたので、その時の経験を書こうと思います。

ブロックチェーンってなに?

ぜひ、Software Design 2017年5月号をご購入して、「いまから学ぶブロックチェーンのしくみ」をご覧ください。まだブロックチェーンに触れたことがない方向けの内容になっております。ブロックチェーンに関する重要なコンセプト、技術的な概要、応用面が書かれた記事です。

http://gihyo.jp/magazine/SD/archive/2017/201705

執筆の流れ

執筆が決まったのは2月頭ぐらいでした。そして2月末には目次を提示して3月の中頃に原稿の提出。その後2週間ほどかけて原稿の訂正を行いました。

今回の記事では、弊社嶋田(id:cimadai)と2人での共同執筆でした。章ごとに分担を決め、各自担当の章を書き、お互いにレビューを行うという形で作業を進めていきました。

執筆作業について

今回の記事では、1ページ1000文字で10ページ、約1万文字を目安に執筆を行いました。私は雑誌記事への投稿は初めてで、執筆がどのくらいの大変さになるか想像できませんでした。共同執筆者の嶋田は過去に技術書を執筆した経験があり、彼から「大変だよ」と聞いていたので、書き始める前はかなり不安でした。実際は、1度書き始めると逆に文量が想定より多すぎて最終的には削ることになりました。2人で10000文字という少なめのボリュームのおかげかもしれません。

執筆を行う上で気を付けたことは、なるべく読者に前提知識を要求しないことです。普段の開発チームでのコミュニケーションでは、同じ知識を共有しているものとして専門用語でも何気なく使ってしまいます。導入向けの記事では、何気ない専門用語でも(全体から見れば重要ではない用語であっても)、理解の妨げになる可能性があります。専門用語を多用しない、または、使用する専門用語は初出時に適切な説明を付けるようにしました。難しかったのは何を専門知識や用語とするかです。1度知ってしまうと何が難しくて理解しづらかったのか忘れてしまい、自分たちだけでレビューしてもなかなか理解しづらいことに気づけません。今回はブロックチェーンに関しては聞いたことがあるが中身は知らないエンジニアの方にレビューをお願いしました。

共同執筆について。技術書執筆の経験者がおり、執筆作業の流れについて知っていたため、スムーズに作業を進めることが出来ました。原稿の共有、バージョン管理はgitです。構成や書くことに詰まった時、相談できる相手がいるのは心強かったです。

最後に

今回は2か月ほどで10ページという余裕のある(?)スケジュールでしたが、連載を持たれてるエンジニアの方は、どのように連載をこなしているのか頭が下がる思いです。

共同執筆は執筆の敷居を下げてくれる大きな要因でした。書く機会はあるけど1人ではなかなか始めることができないという方は共同執筆できそうな人を探してみるのはいかがでしょうか。

Software Designの記事を通じて、ブロックチェーンについてより多くのエンジニアに興味を持ってもらえることを願っています。