DeepLearning 0.1 文書: 始めに(翻訳/要約)
* DeepLearning 0.1 documentation: Getting Started の簡単な要約です。
ダウンロード
各学習アルゴリズムのページで、該当するファイルをダウンロードできます。
同時にそれら全てをダウンロードしたい場合には、チュートリアルの git レポジトリを clone できます :
git clone https://github.com/lisa-lab/DeepLearningTutorials.git
データセット
MNIST データセット
MNIST データセットは手書き数字画像から成り、訓練セットのための 60,000 サンプルとテストのための 10,000 サンプルに分割されます。このチュートリアルや多くの論文では、(学習率とモデルのサイズのようなハイパー・パラメータの選択のために)60,000 の公式の訓練セットは 50,000 サンプルの実際の訓練セットと 10,000 の検証サンプルに分割されます。全ての数字画像は 28 x 28 ピクセルの固定サイズ画像にサイズ的に正規化がされて中心化されています。元のデータセットでは画像の各ピクセルは 0 と 255 の間の値で表され、ここで 0 は黒色、255 は白色、そしてその間のものは異なる灰色の影です。
MNIST 数字の幾つかの例を示します :
![]()
![]()
![]()
![]()
![]()
便利のために python で簡単に使えるようにするためにデータセットを pickle 化しました。(訳注: pickle はシリアリゼーションのための python モジュール。)ここ でダウンロードできます。pickle 化されたファイルは3つのリストのタプルを表します: 訓練セット、検証セットそしてテストセットです。3つのリストのそれぞれは画像リストと個々の画像のためのクラスラベルのリストから成るペアです。画像は 0 と 1(0 は黒色を、1 は白色を表します)の間の 784 (28 x 28) float 値の numpy 1-次元配列として表されます。ラベルは、画像がどの数字を表しているかを示す 0 と 9 の間の数字です。下のコードブロックはデータセットをどのようにロードするかを示します。
import cPickle, gzip, numpy # データセットをロード f = gzip.open('mnist.pkl.gz', 'rb') train_set, valid_set, test_set = cPickle.load(f) f.close()データセットを使う時、通常はそれをミニバッチに分割します(確率的勾配降下 – Stochastic Gradient Descent 参照)。データセットを共有変数にストアして、固定された知られたバッチサイズが与えられたとして、ミニバッチのインデックスに基づいてアクセスすることを奨励します。共有変数の裏の理由は GPU の使用に関係します。GPU メモリにデータをコピーする時に大きなオーバーヘッドがあります。共有変数を使用しない時、要求時(各ミニバッチ個々が必要な時)にデータをコピーする場合には、このオーバーヘッドによって、GPU コードは CPU コードよりもそれほど速くならないでしょう(多分遅くなります)。けれどもデータを Theano 共有変数で持つとき、共有変数が構築される時、一つの呼び出しで GPU 上の全てのデータをコピーする可能性を Theano に与えます。結局 GPU は共有変数からスライスすることにより任意のミニバッチにアクセスできます。CPU メモリからどのような情報もコピーする必要なく、そしてそれゆえにオーバヘッドを迂回できます。データポイントとそれらのラベルは通常は異なる性質を持ちます(ラベルは通常は整数でデータポイントは実数)ので、ラベルとデータのためには異なる変数を使用することを提案します。また訓練セット、検証セットそしてテストセットのために異なる変数を使うことも勧めます、これはコードを読みやすくするためです(結果的に 6 つの異なる共有変数になります)。
データが一つの変数にあり、そしてミニバッチがその変数のスライスとして定義されるので、ミニバッチをそのインデックスとサイズを示すことで定義することはより自然です。私たちのセットアップではバッチサイズはコードの実行を通して定数にとどまるので、それ故に関数はどのデータポイントで作業しているかを識別するのに実際にはインデックスのみを必要とします。下のコードはデータをどのようにストアしてミニバッチにどのようにアクセスするかを示します :
def shared_dataset(data_xy): """ データセットを共有変数にロードする関数 データセットを共有変数にストアする理由は Theano に(コードが GPU 上で実行された時に) それを GPU メモリにコピーすることを可能にするためです。 GPU へのデータコピーは遅いので、ミニバッチを必要となるたびにコピーすること (データが共有変数にない場合のデフォルトの挙動)は パフォーマンス上の大きな劣化につながります。 """ data_x, data_y = data_xy shared_x = theano.shared(numpy.asarray(data_x, dtype=theano.config.floatX)) shared_y = theano.shared(numpy.asarray(data_y, dtype=theano.config.floatX)) # GPU 上にデータをストアする時、それは float としてストアされなければなりませんので # ラベルも ``floatX`` としてストアします(``shared_y`` はまさにそれを行ないます)。 # しかし計算の間は int としてのそれらを必要とします # (ラベルを index として使用します、それらが float ならば意味をなしません) # それゆえに ``shared_y`` を返す代わりにそれを int にキャストしなければなりません。 # この小さなハックによりこの問題を回避できます。 return shared_x, T.cast(shared_y, 'int32') test_set_x, test_set_y = shared_dataset(test_set) valid_set_x, valid_set_y = shared_dataset(valid_set) train_set_x, train_set_y = shared_dataset(train_set) batch_size = 500 # ミニバッチのサイズ # 訓練セットの3番目のミニバッチにアクセスする data = train_set_x[2 * batch_size: 3 * batch_size] label = train_set_y[2 * batch_size: 3 * batch_size]
データは GPU 上で float としてストアされなければなりません。(GPU 上にストアするための正しい dtype は theano.config.floatX で与えられます。)ラベルに対するこの欠点を回避するためにそれらを float としてストアし、それから int にキャストします。
[Note] コードを GPU 上で実行してそして使用しているデータセットがメモリにフィットするに大きすぎる場合、コードはクラッシュします。そのようなケースではデータを共有変数にストアするべきです。データ(幾つかのミニバッチ)の十分に小さな塊を共有変数にストアして訓練の間それを使うことができます。塊を通り抜けた時にはストアする変数を更新します。この方法で CPU メモリと GPU メモリ間のデータ転送の数を最小化します。
表記
データセット表記
データセットを とします。区別が重要な時は、訓練、検証、そしてテストセットを
,
そして
として示します。検証セットはモデル選択とハイパー・パラメータの選択を遂行するのに使用され、一方でテストセットは最終的な汎化性能を評価して異なるアルゴリズムをバイアスなしで比較するために使用されます。
チュートリアルの殆どは分類問題を扱い、そこでは各データセット はペア
の集合としてインデックスされます。添字は訓練セット・サンプルを識別するために使用します:
はそれゆえに次元
の i-番目の訓練サンプルです。同様に、
は入力
に割り当てられた i-番目のラベルです。これらのサンプルを
が他の型を持つものに拡張することは単純です。(e.g. Gaussian for regression, or groups of multinomials for predicting multiple symbols).
数学的な慣習
: upper-case シンボルは特に指定がなければ行列を参照します。
: 行列
の i-番目の行と j-番目の列にある要素
: ベクトル、行列
の i-番目の行
: ベクトル、行列
の j-番目の列
: lower-case シンボルは特に指定がなければベクトルを参照します。
: ベクトル
の i-番目の要素
シンボルと acronyms のリスト
: 入力次元の数。
:
-番目の層の隠れユニットの数。
,
: モデル
に関連する分類関数で、
として定義されます。
は多くの場合ドロップしますので注意してください。.
- L: ラベルの数。
: データセット
上のパラメータ
で定義されたモデルの対数尤度 (log-likelihood)。
: データセット
上の
でパラメータ化された予測関数 f の経験的な (empirical) 損失。
- NLL: 負の対数尤度 (negative log-likelihood)
: 与えられたモデルのための全てのパラメータの集合
Python 名前空間
チュートリアル・コードは次の名前空間を多くの場合使用します :
import theano import theano.tensor as T import numpy
深層学習のための教師あり最適化 (Supervised Optimization) 入門
深層学習についての興味深いものの多くは深層ネットワークの教師なし学習です。
しかし教師あり学習もまた重要な役割を果たします。教師なし事前訓練の有用性は多くの場合教師あり調整後に達成された性能をベースに評価されます。この章では分類モデルのための教師あり学習の基礎をレビューして、ミニバッチ確率的勾配降下アルゴリズムをカバーします、これは深層学習チュートリアルにおいて多くのモデルを調整することに使用されます。
分類器を学ぶ
0-1 損失 (Zero-One Loss)
これらの深層学習チュートリアルで表されるモデルの多くは分類のために使用されます。分類器の訓練の目的は unseen サンプル上のエラーの数(zero-one 損失)を最小化することです。もし が予測関数であるならば、損失は次のように書けます:

ここで は訓練セットであるかまたは
(検証の評価またはテスト・エラーにバイアスをかけることを回避するために)。
は以下で定義される指示関数です :

このチュートリアルでは、 は次で定義されます :

python では、Theano を使ってこれは次のように書けます :
# zero_one_loss は 0-1 損失のシンボリック式を表す Theano 変数です; # 実際の値を得るためにはこのシンボリック式は Theano 関数にコンパイルされなければなりません。 zero_one_loss = T.sum(T.neq(T.argmax(p_y_given_x), y))
負の対数尤度損失 (Negative Log-Likelihood Loss)
0-1 損失は微分可能ではないので、大規模なモデル(数千あるいは数百万のパラメータ)のために最適化することは(計算的に)法外に高コストです。そのため訓練セットで全てのラベルが与えられたとして私たちの分類器の対数尤度を最大化することにします。

正しいクラスの尤度は正しい予測の数と同じではありませんが、ランダムに初期化された分類器の観点からは良く類似しています。尤度と 0-1 損失は異なる目的関数であることを忘れないでください; それらは検証セット上で相関関係はありますが、時々片方があがる一方で他方が下がります、or vice-versa。
損失関数を最小化するという観点で通常語りますが、学習は、次で定義される負の対数尤度 (negative log-likelihood (NLL)) を最小化することを試みます :

私たちの分類器の NLL は 0-1 損失の微分可能な代用品で、訓練データ上この関数の勾配を、分類器の深層学習のための教師あり学習のシグナルとして使用します。
これは次の行のコードを使って計算可能です :
# NLL はシンボリック変数です ; NLL の実際の値を得るには、 # このシンボリック式は Theano 関数にコンパイルされなければなりません。 NLL = -T.sum(T.log(p_y_given_x)[T.arange(y.shape[0]), y]) # シンタックス上の注意: T.arange(y.shape[0]) は整数のベクトル [0,1,2,...,len(y)] です。 # 2つのベクトル [0,1,...,K], [a,b,...,k] による行列 M の indexing は # 要素 M[0,a], M[1,b], ..., M[K,k] をベクトルとして返します。 # ここで、このシンタックスを正しいラベル y の対数確率を取得するために使います。
確率的勾配降下 (SGD – Stochastic Gradient Descent)
標準勾配降下とは何でしょう?これは単純なアルゴリズムで、そこでは幾つかのパラメータを持つ損失関数で定義されたエラー平面上で繰り返し小さなステップを下方に取ります。標準勾配降下の目的のために訓練データを損失関数に流し込むことを考えます。
そしてこのアルゴリズムの擬似コードは次のように記述されます :
# 勾配降下 (GRADIENT DESCENT) while True: loss = f(params) d_loss_wrt_params = ... # 勾配を計算する params -= learning_rate * d_loss_wrt_params if <stopping condition is met>: return params
確率的勾配降下 (Stochastic gradient descent (SGD)) は標準勾配降下と同じ原理に従って動作します。しかし全ての訓練セットの代わりに一度に 2,3 のサンプルから勾配を見積もることによりより迅速に処理を進めます。その純粋な形として、一度に単一のサンプルからのみ勾配を見積ります。
# 確率的勾配降下 (STOCHASTIC GRADIENT DESCENT) for (x_i,y_i) in training_set: # imagine an infinite generator # that may repeat examples (if there is only a finite training set) loss = f(params, x_i, y_i) d_loss_wrt_params = ... # 勾配を計算する params -= learning_rate * d_loss_wrt_params if <stopping condition is met>: return params
深層学習のための推奨する変形は “ミニバッチ” と呼ばれるものを使って確率的勾配降下を更に捻ったものです。ミニバッチ SGD (MSGD) は SGD と同様に動作しますが、勾配の各見積りのために一つ以上の訓練サンプルを使用する点が違います。このテクニックは勾配の見積りにおいて食い違いを減少させて多くの場合現代的なコンピュータの階層メモリシステムを上手く使います。
for (x_batch,y_batch) in train_batches: # imagine an infinite generator # that may repeat examples loss = f(params, x_batch, y_batch) d_loss_wrt_params = ... # theano を使用して勾配を計算する params -= learning_rate * d_loss_wrt_params if <stopping condition is met>: return params
ミニバッチ・サイズ の選択にはトレードオフがあります。最適な
はモデル、データセット-、そしてハードウェア依存で、1 から多分数百の間のどの値も取れます。このチュートリアルではそれを 20 に設定しますが、この選択はほとんど恣意的(適当)です(害はありませんが)。
[Note]
もしエポックの固定数のために訓練するのであれば、ミニバッチ・サイズは重要です、何故ならばそれはパラメータに行なわれる更新数を制御するからです。バッチサイズ 1 を使用して 10 エポックのために同じモデルを訓練することは、バッチサイズ 20 で同じ 10 エポックのために訓練することと比較して完全に異なる結果を生みます。
上述の全てのコード・ブロックはアルゴリズムがどのようなものかを示す擬似コードでした。そのようなアルゴリズムの Theano での実装は次のようになされます :
# ミニバッチ確率的勾配降下 (Minibatch Stochastic Gradient Descent) # 損失は、シンボリック変数 params(共有変数), x_batch, y_batch が与えられた # 損失関数のシンボリックな表現を仮定します : # params に関する損失勾配を計算する d_loss_wrt_params = T.grad(loss, params) # MSGD ステップを theano 関数にコンパイルする updates = [(params, params - learning_rate * d_loss_wrt_params)] MSGD = theano.function([x_batch,y_batch], loss, updates=updates) for (x_batch, y_batch) in train_batches: # ここで x_batch と y_batch は train_batches の要素で # 従って numpy 配列; 関数 MSGD もまた params を更新します print('Current loss is ', MSGD(x_batch, y_batch)) if stopping_condition_is_met: return params
正則化 (Regularization)
機械学習には最適化以上の意味があります。データからモデルを訓練する時、既に見たサンプル上だけではなく、新しいサンプル上で上手くやるために準備しようとします。MSGD のための上の訓練ループはこれを考慮していませんので、訓練サンプルに過剰適合するかもしれません。過剰適合と戦うための方法は正則化を通してです。正則化のためには幾つかのテクニックがあります; ここで説明するものは L1/L2 正則化と early-stopping (早期の打ち切り)です。
L1 と L2 正則化
L1 と L2 正則化は損失関数に特別な項を追加することを含みます、これは幾つかのパラメータ configurations にペナルティを与えます。形式的には損失関数は :

そして正則化された損失は :

あるいは私たちのケースでは

ここで

これは の
ノルムです。
はハイパーパラメータで、正則化パラメータの相対的な重要性を制御します。一般に p のために使用される値は 1 と 2 で、そのため L1/L2 という用語になります。もし p = 2 ならば、regularizer (正則化項) はまた “weight decay (重み減衰)”とも呼ばれます。
In principle, adding a regularization term to the loss will encourage smooth network mappings in a neural network (by penalizing large values of the parameters, which decreases the amount of nonlinearity that the network models). More intuitively, the two terms (NLL and R(\theta)) correspond to modelling the data well (NLL) and having “simple” or “smooth” solutions (R(\theta)). Thus, minimizing the sum of both will, in theory, correspond to finding the right trade-off between the fit to the training data and the “generality” of the solution that is found. To follow Occam’s razor principle, this minimization should find us the simplest solution (as measured by our simplicity criterion) that fits the training data.
Note that the fact that a solution is “simple” does not mean that it will generalize well. Empirically, it was found that performing such regularization in the context of neural networks helps with generalization, especially on small datasets. The code block below shows how to compute the loss in python when it contains both a L1 regularization term weighted by \lambda_1 and L2 regularization term weighted by \lambda_2
# symbolic Theano variable that represents the L1 regularization term L1 = T.sum(abs(param)) # symbolic Theano variable that represents the squared L2 term L2 = T.sum(param ** 2) # the loss loss = NLL + lambda_1 * L1 + lambda_2 * L2
Early-Stopping
Early-stopping combats overfitting by monitoring the model’s performance on a validation set. A validation set is a set of examples that we never use for gradient descent, but which is also not a part of the test set. The validation examples are considered to be representative of future test examples. We can use them during training because they are not part of the test set. If the model’s performance ceases to improve sufficiently on the validation set, or even degrades with further optimization, then the heuristic implemented here gives up on much further optimization.
The choice of when to stop is a judgement call and a few heuristics exist, but these tutorials will make use of a strategy based on a geometrically increasing amount of patience.
# early-stopping parameters patience = 5000 # look as this many examples regardless patience_increase = 2 # wait this much longer when a new best is # found improvement_threshold = 0.995 # a relative improvement of this much is # considered significant validation_frequency = min(n_train_batches, patience/2) # go through this many # minibatches before checking the network # on the validation set; in this case we # check every epoch best_params = None best_validation_loss = numpy.inf test_score = 0. start_time = time.clock() done_looping = False epoch = 0 while (epoch < n_epochs) and (not done_looping): # Report "1" for first epoch, "n_epochs" for last epoch epoch = epoch + 1 for minibatch_index in range(n_train_batches): d_loss_wrt_params = ... # compute gradient params -= learning_rate * d_loss_wrt_params # gradient descent # iteration number. We want it to start at 0. iter = (epoch - 1) * n_train_batches + minibatch_index # note that if we do `iter % validation_frequency` it will be # true for iter = 0 which we do not want. We want it true for # iter = validation_frequency - 1. if (iter + 1) % validation_frequency == 0: this_validation_loss = ... # compute zero-one loss on validation set if this_validation_loss < best_validation_loss: # improve patience if loss improvement is good enough if this_validation_loss < best_validation_loss * improvement_threshold: patience = max(patience, iter * patience_increase) best_params = copy.deepcopy(params) best_validation_loss = this_validation_loss if patience <= iter: done_looping = True break # POSTCONDITION: # best_params refers to the best out-of-sample parameters observed during the optimization
以上