質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.50%
Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

機械学習

機械学習は、データからパターンを自動的に発見し、そこから知能的な判断を下すためのコンピューターアルゴリズムを指します。人工知能における課題のひとつです。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

Q&A

解決済

3回答

171閲覧

Tensorflowが神経netの係数を学習する仕組み?

OOZAWA

総合スコア45

Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

機械学習

機械学習は、データからパターンを自動的に発見し、そこから知能的な判断を下すためのコンピューターアルゴリズムを指します。人工知能における課題のひとつです。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

0グッド

0クリップ

投稿2019/01/06 12:00

編集2019/01/10 22:41

初めましてOOZAWAと申します。
Tensorflowが神経netの係数を学習する仕組みに関して複数の実装例を見ても理解できませんでした。

その中の典型的な実例として
<<落ちこぼれないためのTensorFlow Tutorialコード>>
https://qiita.com/TomokIshii/items/92a266b805d7eee02b1d
を挙げられます。
ネット係数の変数としての本体と更新場所を特定できないのです。
理論上
tf.train.GradientDescentOptimizer(0.001).minimize(loss)
の中でネット係数の学習(更新)を実施すべきと思いますが、
tf.trainは修飾子で、GradientDescentOptimizer(...)が外在的なメソッドに過ぎず、現在ネットの係数が含まれていないはず。
それでは何処でネット係数の更新が行われるのでしょうか。

NNのlayerの形成コードの典型な例*

python

1last_layer = self.input_x 2for weight,bias in zip(self.W_list,self.encoding_b_list): 3 hidden = tf.sigmoid(tf.matmul(last_layer,weight) + bias) 4 last_layer = hidden 5 6self.hidden = hidden 7

weightはテンポラリー変数で各layer共用で、self.W_listの一部分のコピーです。
for文で各layerの計算を行い、tf.sigmoid(...)に渡したのはself.W_listの断片的なコピー値しかないのです。
因みに、このweightは各layerが共用する変数で、訓練のforward過程において利用できますが、
back propagation段階ではもう消えているはずです。

なので、各layerのweight値に対する最適化のデータ実体が存在しないように感じます。
さて、tensorflowは何を根拠にして、NNの各layerのパラメータarrayを生成し、実体を確保したのでしょうか。これこそ自分にとって一番不思議な処です。

★上記のような各layer変数(array)の実体がなければ、樹状グラフの形成という話もないでしょう?

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答3

0

tensorflowのコードからでは学習する仕組みの理解は難しいと思います。学習する一連の演算をまとめて関数として提供されているので、公式ドキュメントから理解するしかない上、断片的なので全体像の理解には不向きかと思います。
しっかりと裏側の仕組みを理解したいのであれば、「ゼロから作るDeepLearnig」を読破することをお薦めします。これをもとに学習の一連の演算を理解してからtensorflowのコードを眺めると裏側でどのような処理が行われているのかがイメージできるようになります。私自身、tensorflowの解説本から理解しようとしてモヤモヤしていましたが、先の本でベースを固めたおかげでかなりクリアになりました

投稿2019/01/07 04:20

R.Shigemori

総合スコア3376

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

OOZAWA

2019/01/07 09:26

アドバスありがとうございます。
OOZAWA

2019/01/10 22:39

本を探しています
guest

0

ベストアンサー

どのタイミングでということであれば、記事中の以下の行を実行した際に重みが更新されます。

train_step.run({x: batch_xs, y_: batch_ys})

Back Propagation による重みの更新は TensorFlow の内部で行われるため、ライブラリを使う側が重みを更新するコードを記述する必要がありません。そのため、記事中にはそのようなコードはないです。

投稿2019/01/06 13:42

tiitoi

総合スコア21956

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

OOZAWA

2019/01/07 00:09

早速ご教授頂きありがとうございます。 >>記事中の以下の行を実行した際に重みが更新されます。 train_step.run({x: batch_xs, y_: batch_ys}) そうですか。 ところが、train_stepの生成式: train_step = tf.train.GradientDescentOptimizer(0.001).minimize(loss) からすれば、tfはobject(/instance)ではなく、このプログラムの中ではlibraryの標識にすぎません。 そうしますと、tensorflowはユーザーが設計する神経netの構造(例えば, 層数、各層の次元数及びその他の色々な付属処理)をどうやって知ったのでしょうか。そしてtensorflowのどのモジュールの中で神経網の諸ネット係数の実体を保存しているのでしょうか。 ヒントでも頂ければ幸いです。
tiitoi

2019/01/07 03:38

公式リファレンスは参照されましたか? https://www.tensorflow.org/api_docs/python/tf/train/GradientDescentOptimizer minimize(loss) を呼ぶと、loss を最小化する演算を作成します。 この演算を run() で実行することで実際の最小化の計算が行われます。 loss 変数がなにかを辿っていくと、tf.Variable() 等から構成されていることがわかるので、各層の次元数等が含まれていることがわかるかと思います。
OOZAWA

2019/01/09 12:54 編集

公式リファレンスを読みましたが、結局minimize( loss,....)に渡された変数にはネットワークの全体構造に関する情報が一切ないと言えるんではないかと思いますね。 lossは通常下記のような形で設定されます(一つの実例): recon = last_layer cost = 200 * tf.contrib.losses.log_loss(recon, input_x) 要はcostはネットの最後出力とオリジナル入力の『差』で決められたテンソルであり、上記の例からすれば、たかが最後の層の次元と入力データの次元しか取得できないと思います。 因みにlast_layerは各層を形成していくループの最後の出力層で、 各層の次元など情報を持っていないはずです。 いろんな神経網を構成する方法がありますから、コーディングのスタイルも様々で、このような状況で、tensorFlow自身が各層の次元などの情報を一一確定するのは大変で不可能か、信用できないではと思いますが。 それに、何といっても、多くの実例のコードの中にtensorFlowはあくまでメソッドの集まりで、tensorFlow自身はインスタンスを持たないので、神経網の各レヤーの実体がtensorFlowサイドが持っているのは考えにくいのです。 ただ、神経網の構成情報を決まった構造体や変数名でどこかであらかじめ定義するようなお決まり(コーディング上の約束、規則)があれば理解できますが。。。 ないでしょ。
tiitoi

2019/01/07 06:17 編集

> 因みにlast_layerは各層を形成していくループの最後の出力層で、各層の次元など情報を持っていないはずです。 TensorFlow におけるテンソルはただ多次元配列という意味ではなく、グラフにおけるノードですので、あるノードからそれを計算するために必要なノードを辿ることができます。 計算グラフは合成関数と考えてください。 y = f(x) z = g(y) という合成関数であれば、z を計算するためには、まず y = f(x) を計算し、次に z = g(y) を計算すると思います。 計算グラフについて理解されると、TensorFlow がどのような仕組みで動いているかわかるかと思います https://deepage.net/deep_learning/2017/04/02/backpropagation.html
OOZAWA

2019/01/07 09:28 編集

丁寧な解説かりがとうございます。 連鎖的な微分していく以前の問題かなと気がしますね。 もしレヤーの次元や前後関係がはっきり分かっていれば、微分の連鎖でbackpropagationを実現できますが、神経網の表現が決まったコーディング様式で表現しなければ、神経網の各レヤーの次元と前後関係をピックアップするのは人間のようなソースコードを解読する能力がなければ、不可能ではと思いますが。例えば、プログラマーがtf.multiply(input, layer)にすれば、人間は名前でlayerがどれであるかはわかりますが、tf.multiply()自体はそんな能力はないでしょう。 ====メモ==== 以下は恐らく特別な仕組みによって実現されたと仮定して問題にしません。 自分はプログラミング言語的な基本原理しか理解できないーーー そもそもtensorFlowは単にメソッドを提供するだけで、tensorFlowの各メソッドのコールは共通のobjectを経由するのではなく引数以外に繋がっている共通のデータ域がないように思われます。 なので、ネットのレヤー情報の収集、それに対応する変数実体の生成、その各レヤー変数を更新するなど連携プレーは単品のtensorFlowメソッド同志間はどうしてできるのでしょうか。
tiitoi

2019/01/07 09:56 編集

a = tf.constant([[1.0, 2.0], [3.0, 4.0]]) b = tf.constant([[1.0, 1.0], [0.0, 1.0]]) c = tf.matmul(a, b) とした場合、tf.matmul() は a, b という tf.Tensor オブジェクト同士の要素ごとの積のテンソルを作成する関数ですから、この関数を実行した時点でテンソル a, b からテンソル c に対して(グラフの)エッジが作成されます。 ということはテンソル a からテンソル c を辿ることができるし、その逆もできます。 実装するなら、tf.Tensor クラスに入出力先のテンソルの参照を記憶する Attribute として用意しておくとかでしょうかね。 連結リストを実装したことがあれば、ピンとくると思います。 > tf.multiply()自体はそんな能力はないでしょう。 オープンソースなので、実際にどのように実装されているかはコードを読んでください。 https://github.com/tensorflow/tensorflow/blob/r1.12/tensorflow/python/ops/math_ops.py
OOZAWA

2019/01/09 13:38 編集

お返答ありがとうございます。 さすがtensorflowの実装コードを全部解読できないものの、皆さんがご指摘されている基本原理(樹状構造の形成と保存、それから遡るとか)は一応理解しているつもりです。現状では神経網のlayer変数について、樹状構造を形成するための情報が得られないのではないかというのは疑問の原点ですね。 典型的な神経網のlayer形成の実装例を質問窓に置きます。そこで自分の疑問点を述べます。 また宜しくお願いします。
tiitoi

2019/01/09 13:58

グラフが構成されているということはあるノードから別のノードの情報までたどることができるということです。 なので、末端の loss ノードから中間層を表すノードをたどることができ、各ノードは出力数など必要なパラメータを保持しているので、つまり、その情報を得ることができるということです。 文章だとイマイチ理解できないようであれば、実際手を動かしてみて理解したほうがいいかもしれません。 他の回答者さんも勧めていますが、「ゼロから作るDeepLearnig」という本を読んでみてはどうですか。 この本は実際に作りながら、計算グラフやバックプロパゲーションなどDeep Learning のライブラリがどのように作られているか学ぶことができます。難しい内容はでてこないのでおすすめです。
tiitoi

2019/01/09 16:10 編集

追記を見ました。 > 実体を確保したのでしょうか 疑問に思われてる部分は TensorFlow は関係なく、Python の話な気がします。 Python ではオブジェクトの破棄はガベージコレクションで自動で行われるので、質問のコードの例ですと、weight が上書きされたとしてもそのオブジェクトが必要とされていれば破棄されないで、オブジェクトは存在したままです。 a = tf.constant([[1.0, 2.0], [3.0, 4.0]]) b = tf.constant([[1.0, 1.0], [0.0, 1.0]]) c = tf.matmul(a, b) としたあと、 a = 1 b = 2 のように同じ変数 a, b を別に値を記憶するに使ったとしても、元の tf.constant([[1.0, 2.0], [3.0, 4.0]]), tf.constant([[1.0, 1.0], [0.0, 1.0]]) は tf.matmul(a, b) を実行して作成されたテンソルから参照されるようになるので、ガベージコレクションで破棄されず、以前として実体は存在します。 詳しくは「参照カウンタ」「ガベージコレクション」などで調べてください。
OOZAWA

2019/01/11 01:59 編集

なるほど、本当にありがとうございます。 そうしますと、tfサイドが一応ネット係数としてweightをつかんだとします。★ところが、weightはあくまでも『仲介屋』でforward過程においてループ中(for 文)の現在layerの係数のコピー値しか持っていません----要はweightは断片的なネット係数のサイズしか持たず、異なるlayer同志の間で共用していて、値が変動的です。これはforwardにおいて良いですが、back-propagation過程ではネット係数値を最後のlayerから最初(先頭)layerまで『forwardの歴史』を全部遡って更新をしなければならないので、この全layer分の係数を保持するメモリ実体はどこにあるのでしょうか。 質問窓中の実装例だと、変数self.W_listが全layer分の係数を保持していると考えられますが、これは人間がその変数名や神経網の原理からの推測で、tfには知り様がないでは?
tiitoi

2019/01/11 03:06 編集

self.W_list に重みの一覧を入れておかなくてもメモリ上には破棄されない限りあります。 1. tensorflow の関数でモデルを作成していく仮定でグラフが作られる、あるノードは別のノードと結ばれる 2 各ノードは他のノードとエッジで結ばれているので、つまり参照されている。よってそれを表す変数がなくてもガベージコレクションによって破棄されないので、メモリ上に存在したままである 3 順伝搬のときは入力ノードから出力ノードまで辿って計算できる。 4 逆伝搬のときは出力ノードから入力ノードまで辿って計算できる。 以上の説明でもわからないようであれば、実際に Deep Learning ライブラリのソースコード (Tensorflow, pytorch、chainer) を読む、それも難しいようであれば書籍を参考に手を動かして理解するしかないと思います。 他の方も紹介している「ゼロから作るDeep Learning」は以下の本です https://www.amazon.co.jp/dp/4873117585
OOZAWA

2019/01/11 15:12

ご指導有難うございました。 本の情報も有難うございます。 図書館にあるかもしれません、探してみます。
guest

0

例えば掛け算と足し算の2項演算の樹構造を記憶するプログラムはこんな感じ。

Python

1class Var(): 2 def __init__(self, variable_name: str) -> None: 3 super().__init__() 4 self.variable_name = variable_name 5 6 def __str__(self): 7 return self.variable_name 8 9 def __mul__(self, other): 10 return BinOp(self, other, '×') 11 12 def __add__(self, other): 13 return BinOp(self, other, '+') 14 15 16class BinOp(): 17 def __init__(self, a, b, op) -> None: 18 super().__init__() 19 self.left = a 20 self.right = b 21 self.op = op 22 23 def __str__(self) -> str: 24 return ' '.join(map(str, ['(', self.left, self.op, self.right, ')'])) 25 26 def __mul__(self, other): 27 return BinOp(self, other, '×') 28 29 def __add__(self, other): 30 return BinOp(self, other, '+') 31 32 33expr = Var('w') * Var('x') + Var('y') * Var('z') 34print(expr) 35print() 36print(expr.left) # => expr が保持する左の項 37print(expr.op) # => expr が保持する演算子 38print(expr.right) # => expr が保持する右の項 39print() 40print(expr.left.left) # => expr が保持する左の項の左の項 41print(expr.left.op) # => expr が保持する左の項の演算子 42print(expr.left.right) # => expr が保持する左の項の右の項

実行結果

plain

1( ( w × x ) + ( y × z ) ) 2 3( w × x ) 45( y × z ) 6 7w 8× 9x

Node('w') * Node('x') + Node('y') * Node('z')
というコードで計算をしているわけじゃないことに注目。


minimize(loss)に渡るlossは計算結果が格納されているのではなくて、このような、その値をどうやって計算するかという構造を含んでいるオブジェクト。

投稿2019/01/07 12:39

編集2019/01/07 12:41
quickquip

総合スコア11029

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

OOZAWA

2019/01/09 12:48

親切な説明ありがとうございます。 私は非常に理解したような、チンプンカンプンのような感じですね。 やはり頭を買い替えなければならない。^^
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問