はじめに
機械学習に興味を持ち、Andrew Ng先生の受け持つcourseraのMachine Learningコースを進められ、修了しました。
内容がとても良かったのでそのまま有料講座のdeep learingも受講することにしましたが、せっかくなので学習した内容のメモとして書き残していきます。
動画を見てからしばらくして書いているときもあるので、理解が間違っていたら指摘していただけるとありがたいです。
deep learning 講座について
- 全5コースで組まれているカリキュラムでコースは次の要領で進んでいきます
①動画で学習
②選択式テスト
③プログラミング課題
machine learningのときと同様に、通勤電車で①,②を行い、平日夜&土日に③を済ませる作戦です - 最初の1週間は無料でうけられます
- 自分の場合、開始日は2020/1/18ということで、修了目標は3/24!→ 4/18日現在、CNNまでしか終われてません...
Machine learning 講座のサマリー
感想
- 機械学習の全体について話してくれる。logistic regresson,NNについてが一番力は入っている
- 基本的な理論の理解を手助けする意味でのプログラミング課題なので、実務上の実装力が身につくわけではない
- 教師あり学習については、logistic regressionを例にしてアルゴリズムの部分にも割と深く突っ込む
- NNは、logistic regressionの延長として解説. backprobpagationについてこのビデオだけで理解するのは難しいが、導入と直感的理解を与えることに努めてくれている
- NNのアルゴリズムだけでなく、現実問題どうやってブラッシュアップしていくかについて6週目で詳しく解説
- 7週目以降は割と駆け足.さらっと概要について知るには良い
1週目
- 機械学習のイントロ(教師あり学習と教師無し学習があるとか
- 線形回帰のcost function,gradient descentについて
- 線形代数の復習
2週目
- 複数変数の線形回帰
- octave, matlavbについて( octaveはライセンス上の縛りがないので、こちらを採用)
3週目
- logistic regressionの導入
- logistic regressionのcost function
- one vs allによる複数クラスへの分類
- 過学習についてとregulizationによる回避について
4週目
- NNの導入
- 複数クラスへの分類
5週目
- NNのアルゴリズム
- Back propagationとdebug方法
- random initialization (rogistic regressionのように初期値がzerosでは機能しない)
6週目
- 機械学習を適用する際のアドバイス
- バイアスと過学習のトレードオフ
- learning curve
- 精度向上のために学習データを増やすか、モデルを変えるかについての方針の得方
7週目
- サポートベクトルマシン(SVM)
- 目的関数について(rogistic regressionとは異なる関数)
- マージンについて
- カーネルについて
8週目
- 教師なし学習
- k-meansと目的関数
- initializationと分類クラス数について
- PCA(特徴ベクトルの次元削減)
- 次元削減にあたってみるべき尺度について(情報の残存率)
9週目
- 異常検知(複数変数のガウス分布のかけあわせによる)
- ECサイトなどのRecomender system。協調フィルタリングによる学習
10週目
- 大規模なmachine learning
- stochastic gradient descent(確率的最急降下法)
- mini batch : すべての学習セットを一気に使わない ⇔ batch
11週目
- 画像内の文字認識を例にとったpipeline
- sliding window 法 : 画像内の枠をスライドさせ、文字を発見する。枠のアスペクト比も変化させる
- distortionを加えることで教師データを複製する : ランダムノイズを乗せても学習結果は変わらない
deep learning 講座
Neural Networks and Deep Learning
1週目
この週は完全にイントロです。
2週目
ロジスティック回帰をシンプルなNNの例として紹介しています。
MLのときは、直接微分しにいく書き方でしたが、今回はFP,BPを使った形式で解説しています。
また、後半はpythonでベクトル形式で実装する利点について解説しています。for loopを使ってはいけない。
プログラミング課題は、画像を猫orNOTにロジスティック回帰で分類します。正解率は70%にもなります。
思ったよりも精度が出ていて驚きました
3週目
層の浅いNNについてです。
NNに対してBPを適用するので、そこについての解説がメインに思いました。
また、ロジスティック回帰と異なりweightをランダムに初期化する必要性について述べられています。バイアス項は0でもいいです。
また、sigmoidは学習速度を落とすため、中間層で使うことはないということも述べられています。
同じ文脈で、初期化の値も小さい値にすることで学習速度を上げられることにも言及されています
4週目
最初のビデオではFPについて行列形式でかきくだすやり方についておさらいしています。
for loopは各層のFPをする分については使ってよいし、それ以外のやり方はないという点も指摘しています。基本的には、MLの講座のおさらいといった内容です
Improving Deep Neural Networks: Hyperparameter tuning, Regularization and Optimization
1週目
まずは、各パラメータの説明とhyper parameterについて述べています。
項目 | 例 |
---|---|
parameter | W b |
hyper parameter | 層数 隠れ層の次元 learning rate 活性化関数 |
underfitting(high bias), over fitting(high variance)の概念について説明しています。
実装して試す中で、どのようにNNの状態を診断するのかについて述べています。
train error, test errorを計算して診断を下します。
train error | test error | condition |
---|---|---|
high | high | high variance or high bias & high variance (under fitting) |
low | high | high variance (over fitting) |
low | low | optimal |
また、その対処法についても述べています
condition | how to prevent |
---|---|
under fitting | 層数を増やす 多次元化する(logistic regression) |
over fitting | regulization 学習データを増やす |
regulizationについてはMLの講座でも述べられており、weightの要素の2乗和をcost functionに加えることでweightの各項が大きくなることを避け、underfit方向に向かわせるという手法です。
ただし、今回はその手法だけでなく、drop-outというランダムにweightの要素を0にするという方法が紹介されました。直感的には、トレーニング中はランダムに重みが0になるので、ある特定の特徴量に依存することがなくなり、overfittingが避けられます。
まとめると以下の二つになります。
L2 regurization
- 重みの要素の2乗和をcost functionに加える
- 微分項に影響を与えるので、それを忘れないこと
drop out
- training中のみ、ランダムに重みをゼロアウトする
- keep_probというゼロにならずに残す確率を定義する
- 計算値Al,dAlをkeep_probで割り算する(0にして全体として小さくなった値を補完する)
- test dataに対しては、dropoutを行わない
- 実装上は、ゼロアウトするためにWlと同じサイズのDlという行列を準備し、要素を0~1までのランダム値に初期化する。そして1-keep_probよりも小さい項は0にして、Alとの要素掛け算を行う(イタレーションごと)
- 注意点は、ゼロアウトするのはあくまでtraining中だけであって、 予測を立てる際にはすべての重みを使用する
また、gradient checkingについても述べています。
バックプロパゲーションは実装ミスが発生しやすいため、計算で差分をとった値と比べてみよう、ということです。コンセプトとしては、簡単なのですが、実装がやや面倒だと思ったのでメモします。
gradient checkingの手順
- 重みの一つを+e、-eだけずらしてJを計算します
- J(+e)-J(-e)/2eを計算します
- 重みの行列は、コード中では辞書として与えられています。一つずつずらしていきたいので、辞書を一つのベクトルthetaに書き下します
- dtheta_approxはthetaと同じ次元を持ちます
- Jを計算する関数の引数は、辞書型のパラメータを引数にとるので、parametar -> thetaへの型変換関数とその逆を作ります
- 同様にバックプロップ後の微分値も辞書型なのでこちらをベクトル形式に書き直します
- ベクトル形式のdthata_approxとベクトル形式に直したdthetaを比較する
- |dtheta - dtheta_approx|/(|dtheta| + |dtheta_approx|) < 10-7を目安に判定する (e = 10-7とする)
2週目
mini-batch gradient descent
- batch gradient descent → すべてのデータを一度に与えてから、backprop、gradient descentをする
- mini-batch gradient descent → データをmini batchに分けて計算する。mini batchごとにパラメータを更新するが、すべてのmini-batchを更新し終えてtraining dataを一周する単位を1epocという
- stochastic gradient descent(確率的勾配降下法) → ミニバッチのサイズを1にする
mini-batch, stochasticはデータがかなり大きい場合に限って有効な手法です。
すべてのデータを入れてからgradient descentを行うと計算にとても時間がかかり、パラメータの更新が極めて遅くなります。
mini-batch, stochastic gradient descentでは、すべてのデータを入れることにこだわらず、さっさとパラメータを更新してしまいます。
この結果、最適値に向かう経路はジグザグになり、毎エポックごとにコスト関数が減少するとは限りませんが、結果的に収束が速くなります。
momentum gradient descent
- gradient descentよりも収束の速いロジック
- 今回のイタレーションで得た勾配をそのまま用いるのではなく、前回、前々回・・・の勾配と平均化を行って勾配降下法に代入する
- gradient descentでは、毎BP毎の勾配は、収束したい方向にストレートに進むのではなく、ジグザグしながら進んでいく
- ジグザグはランダムに起こるので、平均化をすることによってジグザグ成分が消え去り、極値に向かう上で有効な成分のみが残る
- 平均の仕方は、exponentially weighted avarageと呼ばれるもの
- v(n) = βΘ(n) + (1-β)v(n-1) , v(0) = Θ(0)
- 指数的に最初の項の影響が小さくなっていく定式化の仕方なので、exponentially weighted
- この平均化を勾配に対して当てはめて、gradient descentの修正項に代入する
adam(adaptive momentum) optimization
- モメンタムは1次のexponential weighted aveを使っていますが、2次の項を使うやり方もあります
- 1次と2次の手法を組み合わせたのが、adam
3週目
ハイパーパラメタを選択する
- ベビーシッターのように、一つのトレーニングの様子を見ながら臨機応変にやっていくスタイル(パンダスタイル)といくつものパラメーターを同時に試していいものを見つける(キャビアスタイル)を紹介
- どちらのスタイルを選ぶかは、コンピューターのリソースによる
- パラメーターを決める場合には、grid状にポイントを選ぶのではなく、ランダムに選ぶ。これによって、ある特定のパラメタが結果に依存しなかったときに、他のパラメタの振りが少なくなることを避けられる
batch normalization
- データの正規化を行いますが、それをミニバッチごとに実施します
- 平均0にして、標準偏差で割るのが簡単な方法だが、ちょっと複雑なやり方で
- 正規化のために、γ、βという新しい変数を導入する
- 上記はパラメタなので、最適化の対象になる
プログラミング課題
- 今回からtensor flowを使う
- 6種類の画像を振り分けするone-hot方式の分類
- tensor flowでは、先に式を作り、後からそれに値を代入して計算を行う
- 変数の型としては、tf.Variableとtf.placeholderがある
- tf.VariableにはW,bなどの変数を入れる。tf.placeholderはトレーニングセット、テストセットなどのdataを入れる
- 計算を行うには、Sessionを作る必要がある
- Valiableの例
※4/18追記 後から知りましたが、これtensorflow1の書き方ですね。コース内ではtensorflow1を使うので課題に取り組む分には意味があるかもしれませんが、実務では使わないと思われます
y_hat = tf.constant(36, name='y_hat') # Define y_hat constant. Set to 36.
y = tf.constant(39, name='y') # Define y. Set to 39
loss = tf.Variable((y - y_hat)**2, name='loss') # Create a variable for the loss
init = tf.global_variables_initializer() # When init is run later (session.run(init)),
# the loss variable will be initialized and ready to be computed
with tf.Session() as session: # Create a session and print the output
session.run(init) # Initializes the variables
print(session.run(loss)) # Prints the loss
- placeholderの例。sessionを走らすときに、feed_dictの形で値を与える 参考リンク
x = tf.placeholder(tf.int64, name = 'x')
print(sess.run(2 * x, feed_dict = {x: 3}))
sess.close()
- Sessionの作り方は2通り。withを使うとクローズはしなくていい
sess = tf.Session()
# Run the variables initialization (if needed), run the operations
result = sess.run(..., feed_dict = {...})
sess.close() # Close the session
with tf.Session() as sess:
# run the variables initialization (if needed), run the operations
result = sess.run(..., feed_dict = {...})
# This takes care of closing the session for you :)
- one-hot encodeingへの直し方 参考リンク depthはクラス数 列が一つのdataセットを表すのであれば、axis =0
tf.one_hot(labels, depth, axis)
Convolutional Neural Networks
1週目
Convolutional Neural Networks(畳み込みニューラルネットワーク) :CNNについての基礎を勉強します。
これまで扱ってきた方法では、画像のピクセルをすべて展開し、ひとつのベクトルとしてNNに当てはめてきました。
しかし、例えば1920 * 1080のフルカラー画像があった場合に、ベクトルの次元は1920*1080*3 = 6.2*10^6にもなります。これは入力層のベクトルの次元であり、仮に第一層が入力層と同じ次元をもつとすると、これの2乗、すなわち10^13のオーダーの数の重みが存在することになります。
重みが一つ1byteだったとしても、そんなメモリをどこに確保するんだという感じだと思います。
なので、MNISTのようなかなり小さい画像であればNNで扱える(この場合はlogistic regressionでも8割あてることができる)一方で、実用的な画像解析には別の手法を用いる必要があるわけで、それが畳み込みニューラルネットワーク(CNN)です。
CNNでは、畳み込み層とプーリング層というものが登場します。
畳み込み層では、画像のサイズよりも小さな「窓」を用意し、その窓に収まる範囲のピクセル情報を1つのスカラーに集約します。
この層で変数となるのは窓に含まれる変数だけなので、例えば窓の大きさが5*5*3の場合には、75次元のベクトルだけで済みます。これを画像に1マスずつずらしながら適用する(実際には何マスずらすかはストライドというハイパラ)ことで、画像のサイズを変えながら次の層へ移動することができます。
また、プーリング層というのは変数を持たない層で、プーリング用の窓の中におさまっている画像の要素に対して一定の処理を行います。例えば、最大要素を取り出してそのほかの値は無視したり、平均をとるなどします。情報を削り落とす役割を担っている層です。
基本的なCNNの構造としては、畳み込み層数層に対してプーリング層が続く、という構成をとります。
最終的には、分類問題を解くために、出来上がった中間層を一つの列ベクトルにまとめてソフトマックス関数などを適用して1hot形式に帰着させます。この層を全結合層(Fully connected layer: FC)と呼びます。
課題
一つ目のプログラミング課題では、畳み込み層とプーリング層を用いたFoward propをライブラリ無しで実装します。
2つめの課題では、tensor flowを使って同じ課題を実装します。
ライブラリのありがたみがいよいよ増してくる段階だと感じます。
2週目
具体例としてRes Net, Inception Netについて言及しています。
Resnetは、層を2つ飛ばしする経路を設けることによって層を深くすることに成功しました。
通常のCNNを深くするだけではエラーが小さくなることに結びつきませんでしたが、層をとばすことで(残差を最適化することに相当するようです)154層まで深くし、エラーも下げることにも成功しているという内容です。
3週目
Yolo(You only look once)という物体認識アルゴリズムにの解説が主です。
MLの講座でも触れられていましたが、これまでのアルゴリズムは画像をに対して以下の作業を行うものでした。
- 画像にスライディングボックスをという枠を当てはめる
- ボックスに対してフォワードプロパゲーションを適用し、物体があるか判断する
- スライディングボックスを動かしながら、上記二つの作業を繰り返す
- スライディングボックスの大きさを変更し、上記を繰り返す
このように、何度も解析をかける必要があるため、リアルタイムな処理は難しい状況でした。
一方で、Yoloでは名前の通り画像に対して一度だけ処理を行います。
画像はいくつかの領域に分割され、その中にバウンディングボックスという枠が複数設けられます。
解析が走ると、それぞれのバウンディングボックスに対して物体のクラスとそれに対応した確率のリストが返却されます。
一つの領域内のバウンディングボックスで、最も物体を含んでいる確率が高いもの以外は棄却されます。
つまり、領域ごとに一つのバウンディングボックスとクラス、確率が返されます。
このままだと、大量のバウンディングボックスが画像内に重複した状態になるので、二つの処理を行ってこれらをすっきりした状態にします
Threshold
確率に対する閾値です。設定可能な閾値で、バウンディングボックスを確率で間引きます
例えば、threshold 0.4とすると、クラスの確率の最大値が0.4未満(以下かも。これは公式ドキュメントを見てください)になっているバウンディングボックスは棄却されるという具合です。IoU(Intersection over Union)
バウンディングボックスの重なり具合に関する閾値です。重なり合っているバウンディングボックスをまとめるかどうか決めます
例えば、一人の人物に二つのバウンディングボックスが重なっている場合は一つを消したいですが、人が二人いてそのバウンディングボックス同士が重なっている場合は残したいという場合もあります。
このような場合に、バウンディングボックスを残すか消すかの判断に、バウンディングボックスの重なり具合(IoU)というパラメータを使います。
IoUの閾値を大きくとっておけば、多少重なっていても消さないし、小さくとればちょっとでも重なっていれば消すという動作になります。
4週目
Face Recognition
顔認識について、baiduのシステムを例に挙げて説明しています。
例えば、今まで学習したやり方で従業員の顔認証を行おうとすると下記のような手順になります。
- 従業員の顔写真を大量に集める(一人当たり大量)
- CNNをトレーニングする。最終層のfully connected layerは出力層は従業員数と同次元で、ソフトマックスによる 出力を行う
上記のやり方の課題
- データ収集の難しさ ⇒ 個人の顔写真を何千枚と集めることは難しいし、プライバシー的にもできない
- 人物を追加する難しさ ⇒ 一人従業員が増えるとfully connected layerの次元が1つ増える。その度にCNNを再トレーニングすることはコスト的に到底見合わない
解決手法
このような従業員の画像は一枚だけ渡され、追加でトレーニングをすることを許されないone shot learningの問題に対応するために、顔認識の分野では二つの画像の類似度を評価するCNNをトレーニングする という手法がとられています。つまり、コスト関数として二つの関数の類似度を評価しそれを最小化する手法です。
具体的には、以下のステップです。
1. 入力画像をI,同一人物の画像をP,異なる人物の画像をNとする
2. 画像の出力fに対して類似度を下記の距離で定義する。
|f(I)-f(P)|^2,|f(I)-f(N)|^2
で定義する。小さい方が似ている
3. トレーニングがうまくいっている場合
|f(I)-f(P)|^2 <|f(I)-f(N)|^2
となるfが生成されるので、コスト関数を
max(|f(I)-f(P)|^2 - |f(I)-f(N)|^2, 0)
で定義する
4. 上記が基本的な考え方だが、fがすべての入力に対して0を返却するようになってしまうと(重みづけがみんな0になっているような状況)意味がないので、それを回避するためのパラメタαを導入し、
max(|f(I)-f(P)|^2 - |f(I)-f(N)|^2 + α, 0) α > 0
とする。これをtriplet lossと呼ぶ。
このようなやり方をすれば、従業員の写真は一枚でもそれに対する類似度を計算するNNが訓練されているのでうまく認識を行うことができます。
訓練するときのNの選び方ですが、明らかに違う画像よりもやや似ている(女性同士、男性同士など?)を使ったほうがより精度が高まるとの話もしています。
Neural Style Transfer
ある写真をゴッホの絵風にするとか、アニメ風に変換するとかいうアプリが最近ありますが、そこに使われている技術に関しての説明です。
Contents(変換したい写真)とStyle(ゴッホの絵)を入力しG(Generation)出力を得ます。
評価軸としてContentsとGの類似度、StyleとGの類似度があるので、これらから算出したコスト関数(に係数をかけて)を足し合わせたものが全体のコスト関数になります。
cost関数は、特にstyleが複雑なので必要に応じて動画を参照することにしここではこの程度の記述にとどめておこうと思います。書かないのは、実際面倒なんですが、自分の興味からちょっと離れてるのもありますね(笑)