こんにちは、TensorFlow。 Google Machine Learning Library

テンソルフロー







TensorFlowプロジェクトは、あなたが考えているより大きいです。 これがディープラーニングライブラリであり、Googleとの接続により、TensorFlowプロジェクトが大きな注目を集めています。 しかし、誇大広告を忘れた場合、そのユニークな詳細のいくつかはより深い研究に値します









これらはすべてクールですが、特に機械学習を始めたばかりの人にとっては、TensorFlowを理解するのは非常に困難です。







TensorFlowはどのように機能しますか? 各部分がどのように機能するかを理解し、見て、理解してみましょう。 データが通過する計算を決定するデータ移動グラフを調べ、TensorFlowを使用して勾配降下でモデルをトレーニングする方法を理解し、 TensorBoardがTensorFlowの動作を視覚化する方法を理解します。 この例は、産業レベルでの機械学習の実際の問題を解決するのに役立ちませんが、将来作成するものを含め、TensorFlowで作成されるすべての基礎となるコンポーネントを理解するのに役立ちます!







PythonおよびTensorFlowでの名前と実行



TensorFlowが計算を管理する方法は、Pythonが通常行う方法と大差ありません。 どちらの場合も、 Hadley Wickhamを言い換えると、オブジェクトには名前がないことを覚えておくことが重要です(画像1を参照)。 PythonとTensorFlowの原則の類似点と相違点を理解するために、オブジェクトの参照方法と計算の処理方法を見てみましょう。













画像1.名前はオブジェクトを「持っています」が、その逆はありません。 ハドリーウィッカムイラスト、許可を得て使用。







Python変数名は、それらが表すものではありません。 それらは単にオブジェクトを指します。 したがって、Pythonでfoo = []



およびbar = foo



と記述した場合、これはfoo



bar



と等しいことを意味しません。 foo



bar



、両方とも同じリストオブジェクトを指しているという意味です。







 >>> foo = [] >>> bar = foo >>> foo == bar ## True >>> foo is bar ## True
      
      





id(foo)



id(bar)



同じであることを確認することもできid(bar)



。 特にリストのような可変データ構造の場合、このアイデンティティは、誤解されると深刻なバグにつながる可能性があります。







Python内では、すべてのオブジェクトを管理し、変数名と各名前が参照するオブジェクトを追跡します。 TensorFlowグラフは、このタイプのコントロールの別のレイヤーを表します。 後で見るように、Pythonの名前は、TensorFlowグラフのより詳細でより正確に制御された操作に接続されているオブジェクトを指します。







たとえば、インタラクティブインタープリタREPL(Read Evaluate Print Loop)でPythonに式を入力すると、入力したすべての内容がほとんど常に即座に計算されます。 Pythonはあなたが注文したことを熱望しています。 したがって、 foo.append(bar)



を実行するように彼に指示すると、たとえfoo



使用しなくても、すぐに追加されます。







怠ierな代替案は、私がfoo.append(bar)



と言ったことを思い出すことであり、将来のある時点でfoo



を計算する場合、Pythonが追加します。 これは、TensorFlowの動作に近いです。TensorFlowでは、関係の定義は結果の計算とは関係ありません。







TensorFlowは一般に異なる場所で発生するため、計算の定義と実行をさらに分離します。グラフは操作を定義しますが、操作はセッション内でのみ発生します。 グラフとセッションは互いに独立して作成されます。 グラフは一種の青写真であり、セッションは一種の建設現場です。







単純なPythonの例に戻って、 foo



bar



が同じリストを指していることを思い出します。 bar



foo



に追加して、リストを自分の中に挿入しました。 これは、それ自体を指す1つのノードを持つグラフとして想像できます。 入れ子リストは、TensorFlow計算グラフに似たグラフ構造を表す1つの方法です。







 >>> foo.append(bar) >>> foo ## [[...]]
      
      





実際のTensorFlowグラフはさらに興味深いものになります!







最も単純なグラフTensorFlow



トピックに飛び込むために、最も簡単なTensorFlowグラフをゼロから作成しましょう。 幸いなことに、TensorFlowは他のフレームワークよりも簡単にインストールできます。 この例はPython 2.7または3.3+で動作し、TensorFlow 0.8を使用しています。







 >>> import tensorflow as tf
      
      





この時点で、TensorFlowはすでに多くの状態の管理を開始しています。 たとえば、明示的なデフォルトグラフはすでに存在します。 グラフ内のデフォルトは_default_graph_stack



にありますが、直接アクセスすることはできません。 tf.get_default_graph()



を使用します。







 >>> graph = tf.get_default_graph()
      
      





TensorFlowグラフのノードは、操作(「操作」または「操作」)と呼ばれます。 graph.get_operations()



を使用して一連の操作を確認できます。







 >>> graph.get_operations() ## []
      
      





これで、列は空になりました。 TensorFlowライブラリを計算するために必要なものをすべて追加する必要があります。 まず、値が1の単純な定数を追加します。







 >>> input_value = tf.constant(1.0)
      
      





現在、この定数はノード、グラフの操作として存在しています。 Python変数名input_value



間接的にこの操作を指しますが、デフォルトのグラフでも見つけることができます。







 >>> operations = graph.get_operations() >>> operations ## [<tensorflow.python.framework.ops.Operation at 0x1185005d0>] >>> operations[0].node_def ## name: "Const" ## op: "Const" ## attr { ## key: "dtype" ## value { ## type: DT_FLOAT ## } ## } ## attr { ## key: "value" ## value { ## tensor { ## dtype: DT_FLOAT ## tensor_shape { ## } ## float_val: 1.0 ## } ## } ## }
      
      





TensorFlowは内部でプロトコルバッファー形式を使用します。 ( プロトコルバッファはGoogleレベルのJSONのようなものです)。 上記の定数操作のnode_def



の出力は、 node_def



が番号1のプロトコルバッファービューに格納することを示しています。







TensorFlowに慣れていない人は、既存のものの「TensorFlowバージョン」を作成することの本質は何かと疑問に思うことがあります。 TensorFlowオブジェクトを追加で定義する代わりに、通常のPython変数を使用できないのはなぜですか? TensorFlowチュートリアルの1つに説明があります。







Pythonで効率的な数値計算を実行するには、通常NumPyなどのライブラリが使用されます。これは、Pythonの外部で行列乗算などの高価な演算を実行し、別の言語で実装された非常に効率的なコードを使用します。 残念ながら、各操作の後にPythonに切り替えると余分な負荷がかかります。 この負荷は、GPUまたは分散モードで計算を実行する必要がある場合に特に顕著です。データ転送は高価な操作です。



TensorFlowはPython以外でも複雑な計算を実行しますが、余分な作業負荷を回避するためにさらに進んでいます。 TensorFlowは、Pythonから独立して1つの高価な操作を実行する代わりに、Pythonの完全に外部で機能する相互作用する操作のグラフを記述することができます。 同様のアプローチがTheanoとTorchで使用されています。


TensorFlowは多くのすばらしい機能を実行できますが、明示的に渡されたものでのみ機能します。 これは、1つの定数にも当てはまります。







input_value



を見ると、それはゼロ次元の32ビットテンソルとして見ることができます:ただ1つの数字です。







 >>> input_value ## <tf.Tensor 'Const:0' shape=() dtype=float32>
      
      





値が 指定されていないことに注意してください。 input_value



を計算して数値を取得するには、グラフ操作を計算できる「セッション」を作成し、 input_value



明示的に計算または「実行」するinput_value



ます。 (セッションはデフォルトのグラフを使用します)。







 >>> sess = tf.Session() >>> sess.run(input_value) ## 1.0
      
      





定数を「実行」するのは奇妙に思えるかもしれません。 しかし、これはPythonでの通常の式計算と大差ありません。 TensorFlowは、独自のデータ空間(計算グラフ)を管理し、独自の計算方法を備えています。







最も単純なTensorFlowニューロン



簡単なグラフを使用したセッションができたので、1つのパラメーターまたは重みを持つニューロンを作成しましょう。 多くの場合、単純なニューロンにもバイアス項と非同一性活性化関数が含まれていますが、それらはなくても可能です。







ニューロンの重みは一定ではありません。 トレーニングに使用される入力データと出力データの真実に基づいて、トレーニング中に変化することが予想されます。 重みはTensorFlow 変数になります 。 彼女に0.8の初期値を与えます。







 >>> weight = tf.Variable(0.8)
      
      





変数を追加するとグラフに操作が追加されると思うかもしれませんが、実際にはこの行だけで4つの操作が追加されます。 あなたは彼らの名前を見つけることができます:







 >>> for op in graph.get_operations(): print(op.name) ## Const ## Variable/initial_value ## Variable ## Variable/Assign ## Variable/read
      
      





「骨で」各操作をあまりにも長く分解したくないので、少なくとも実際の計算のように見えるものを作成しましょう。







 >>> output_value = weight * input_value
      
      





グラフには6つの演算があり、最後は乗算です。







 >>> op = graph.get_operations()[-1] >>> op.name ## 'mul' >>> for op_input in op.inputs: print(op_input) ## Tensor("Variable/read:0", shape=(), dtype=float32) ## Tensor("Const:0", shape=(), dtype=float32)
      
      





ここでは、乗算の操作が入力データのソースをどのように監視するかを見ることができます。それらはグラフの他の操作からのものです。 グラフ全体の構造を理解するために、人がすべてのつながりを追うことは非常に困難です。 TensorBoardグラフの視覚化は、このために特別作成されました。







乗算の結果を決定する方法は? output_value



操作を「開始」する必要があります。 しかし、この操作は変数weight



依存します。 weight



の初期値は0.8であるべきだと示しましたが、現在のセッションではまだ値が設定されていません。 tf.initialize_all_variables()



関数は、すべての変数(この場合は1つのみ)を初期化する操作を生成し、この操作を開始できます。







 >>> init = tf.initialize_all_variables() >>> sess.run(init)
      
      





tf.initialize_all_variables()



実行結果には、 現在グラフにあるすべての変数の初期化子が含まれます。そのため、新しい変数を追加する場合は、 tf.initialize_all_variables()



再度実行する必要があります。 simple init



には新しい変数は含まれません。







これで、 output_value



操作を開始する準備ができました。







 >>> sess.run(output_value) ## 0.80000001
      
      





これらは、32ビット浮動小数点を含む0.8 * 1.0であり、32ビット浮動小数点数値0.8をほとんど理解しません 。 0.80000001の値は、できる限り近い値です。







TensorBoardでグラフを見る



グラフはまだ非常に単純ですが、ダイアグラムの形で表現を見るのは良いことです。 TensorBoardを使用して、このようなチャートを生成します。 TensorBoardは、各操作に保存されている名前フィールドを読み取ります(これはPython変数名とまったく同じではありません)。 これらのTensorFlow名を使用して、より身近なPython変数名に進むことができます。 tf.mul



を使用することは、上記の例で単に*



を乗算することと同等ですが、ここでは操作の名前を設定できます。







 >>> x = tf.constant(1.0, name='input') >>> w = tf.Variable(0.8, name='weight') >>> y = tf.mul(w, x, name='output')
      
      





TensorBoardは、TensorFlowセッションから作成された出力ディレクトリを調べます。 SummaryWriter



を使用してこの出力に書き込むことができます。1つのグラフ以外に何もしなければ、1つのグラフのみが書き込まれます。







SummaryWriter



作成するときの最初の引数は、必要に応じて作成される出力ディレクトリの名前です。







 >>> summary_writer = tf.train.SummaryWriter('log_simple_graph', sess.graph)
      
      





これで、コマンドラインでTensorBoardを実行できます。







 $ tensorboard --logdir=log_simple_graph
      
      





TensorBoardは、ポート6006でローカルWebアプリケーションとして実行されます(「6006」は上下逆に「goog」です)。 localhost:6006/#graphs



ブラウザーに移動すると、TensorFlowで作成されたグラフのグラフを見ることができます。 画像2のようになります。













画像2.最も単純なTensorFlowニューロンのTensorBoardによる視覚化。







ニューロンを訓練する



ニューロンを作成しましたが、どのように訓練しますか? 入力値を1.0に設定します。 正しい最終値がゼロであるとします。 つまり、非常に単純なトレーニングデータセットがあり、1つの特性を持つ1つの例があります。値は1、マークはゼロです。 単位をゼロに変換するニューロンを教えたいです。







これでシステムは1を取り、0.8を返しますが、これは正しい動作ではありません。 システムがどれほど間違っているかを判断する方法が必要です。 このエラーの測定値を「損失」と呼び、損失を最小限に抑えるようにシステムを設定します。 損失が負の数になる可能性がある場合、最小化は意味をなさないため、損失を現在の入力値と目的の出力値の差の2乗として定義しましょう。







 >>> y_ = tf.constant(0.0) >>> loss = (y - y_)**2
      
      





この時点まで、グラフには何も調査されていません。 トレーニングには、オプティマイザーが必要です。 勾配降下関数を使用して、微分損失の値に基づいて重みを更新できます。 オプティマイザーは、次元の更新を管理するためのトレーニングのレベルを設定する必要があります。0.025を設定します。







 >>> optim = tf.train.GradientDescentOptimizer(learning_rate=0.025)
      
      





オプティマイザーは非常にスマートです。 彼は、ネットワーク全体のレベルで必要な勾配を自動的に検出して使用し、トレーニングのための段階的な後退を行うことができます。







簡単な例でグラデーションがどのように見えるか見てみましょう。







 >>> grads_and_vars = optim.compute_gradients(loss) >>> sess.run(tf.initialize_all_variables()) >>> sess.run(grads_and_vars[1][0]) ## 1.6
      
      





勾配値が1.6なのはなぜですか? 損失値は二乗され、導関数は誤差に2を掛けたものです。 これで、システムは0ではなく0.8を返すため、エラーは0.8になり、2を掛けたエラーは1.6になります。 動作します!







より複雑なシステムでは、TensorFlowがこれらの勾配を自動的に計算して適用することが特に役立ちます。







グラデーションを適用して、バックプロパゲーションを終了しましょう。







 >>> sess.run(optim.apply_gradients(grads_and_vars)) >>> sess.run(w) ## 0.75999999 # about 0.76
      
      





最適化プログラムが勾配を取り去り、トレーニングレベル1.6 * 0.025を掛けて、重みを正しい方向に移動したため、重みは0.04減少しました。







このようにハンドルでオプティマイザを導く代わりに、勾配を計算して適用する操作を行うことができます: train_step









 >>> train_step = tf.train.GradientDescentOptimizer(0.025).minimize(loss) >>> for i in range(100): >>> sess.run(train_step) >>> >>> sess.run(y) ## 0.0044996012
      
      





トレーニングステップが何度も開始された後、重量と最終値は非常にゼロに近くなりました。 ニューロンは学習しました!







TensorBoardの学習診断



トレーニング中に何が起こるかに興味があるかもしれません。 たとえば、学習のすべてのステップでシステムが予測するものを追跡する必要があります。 サイクルの各ステップで値を表示できます。







 >>> for i in range(100): >>> print('before step {}, y is {}'.format(i, sess.run(y))) >>> sess.run(train_step) >>> ## before step 0, y is 0.800000011921 ## before step 1, y is 0.759999990463 ## ... ## before step 98, y is 0.00524811353534 ## before step 99, y is 0.00498570781201
      
      





これは機能しますが、いくつかの問題があります。 数字のリストを理解することは困難です。 スケジュールの方が良いでしょう。 単一の出力値でも、多すぎます。 そして、多くの場合、いくつかの値を追跡する必要があります。 ますます体系的に記録していくといいでしょう。







幸いなことに、グラフを視覚化するために以前に使用されたのと同じシステムには、必要なメカニズムが含まれています。







状態を簡単に説明する操作を計算グラフに追加します。 私たちの場合、操作は、ニューロンの現在の出力であるy



の現在の値を報告します。







 >>> summary_y = tf.scalar_summary('output', y)
      
      





この操作を実行すると、プロトコルバッファー形式の文字列が返されます。これはSummaryWriter



を使用してログディレクトリに書き込むことができます。







 >>> summary_writer = tf.train.SummaryWriter('log_simple_stats') >>> sess.run(tf.initialize_all_variables()) >>> for i in range(100): >>> summary_str = sess.run(summary_y) >>> summary_writer.add_summary(summary_str, i) >>> sess.run(train_step) >>>
      
      





tensorboard --logdir=log_simple_stats



実行すると、 localhost:6006/#events



ページ(画像3)にインタラクティブなグラフが表示されます。













図3.ニューロンの出力値とトレーニングの反復回数のTensorBoardによる視覚化。







進む



これがコードの最終バージョンです。 それほど多くはなく、各部分はTensorFlowの有用な(そして明確な)機能を示しています。







 import tensorflow as tf x = tf.constant(1.0, name='input') w = tf.Variable(0.8, name='weight') y = tf.mul(w, x, name='output') y_ = tf.constant(0.0, name='correct_value') loss = tf.pow(y - y_, 2, name='loss') train_step = tf.train.GradientDescentOptimizer(0.025).minimize(loss) for value in [x, w, y, y_, loss]: tf.scalar_summary(value.op.name, value) summaries = tf.merge_all_summaries() sess = tf.Session() summary_writer = tf.train.SummaryWriter('log_simple_stats', sess.graph) sess.run(tf.initialize_all_variables()) for i in range(100): summary_writer.add_summary(sess.run(summaries), i) sess.run(train_step)
      
      





この例は、Michael NielsenのNeural Networks and Deep Learningの例よりもさらに簡単です。 個人的には、そのような詳細の研究は、単純な構成要素を基礎として使用するより複雑なシステムを理解し、構築するのに役立ちます。







TensorFlowの実験を続けたい場合は、たとえば別の活性化関数を使用して、より興味深いニューロンを作成することをお勧めします。 より興味深いデータでトレーニングを作成できます。 さらにニューロンを追加できます。 さらにレイヤーを追加できます。 より複雑な既製のモデル飛び込んだり 、独自のTensorFlowの マニュアルやガイドの研究にもっと時間を費やしたりできます。 頑張って!








All Articles