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

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

ただいまの
回答率

90.42%

  • Python

    9750questions

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

  • Python 3.x

    7878questions

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

  • 機械学習

    798questions

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

  • TensorFlow

    796questions

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

解決済

回答 3

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 188

OOZAWA

score 3

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

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

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

last_layer = self.input_x
for weight,bias in zip(self.W_list,self.encoding_b_list):
    hidden = tf.sigmoid(tf.matmul(last_layer,weight) + bias)
    last_layer = hidden

self.hidden = hidden 


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)の実体がなければ、樹状グラフの形成という話もないでしょう?

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

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

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

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 3

checkベストアンサー

+2

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

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

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

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/01/07 09: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のどのモジュールの中で神経網の諸ネット係数の実体を保存しているのでしょうか。

    ヒントでも頂ければ幸いです。

    キャンセル

  • 2019/01/07 12:38

    公式リファレンスは参照されましたか?
    https://www.tensorflow.org/api_docs/python/tf/train/GradientDescentOptimizer

    minimize(loss) を呼ぶと、loss を最小化する演算を作成します。
    この演算を run() で実行することで実際の最小化の計算が行われます。
    loss 変数がなにかを辿っていくと、tf.Variable() 等から構成されていることがわかるので、各層の次元数等が含まれていることがわかるかと思います。

    キャンセル

  • 2019/01/07 14:58 編集

    公式リファレンスを読みましたが、結局minimize( loss,....)に渡された変数にはネットワークの全体構造に関する情報が一切ないと言えるんではないかと思いますね。
    lossは通常下記のような形で設定されます(一つの実例):
    recon = last_layer
    cost = 200 * tf.contrib.losses.log_loss(recon, input_x)
    要はcostはネットの最後出力とオリジナル入力の『差』で決められたテンソルであり、上記の例からすれば、たかが最後の層の次元と入力データの次元しか取得できないと思います。
    因みにlast_layerは各層を形成していくループの最後の出力層で、
    各層の次元など情報を持っていないはずです。

    いろんな神経網を構成する方法がありますから、コーディングのスタイルも様々で、このような状況で、tensorFlow自身が各層の次元などの情報を一一確定するのは大変で不可能か、信用できないではと思いますが。
    それに、何といっても、多くの実例のコードの中にtensorFlowはあくまでメソッドの集まりで、tensorFlow自身はインスタンスを持たないので、神経網の各レヤーの実体がtensorFlowサイドが持っているのは考えにくいのです。
    ただ、神経網の構成情報を決まった構造体や変数名でどこかであらかじめ定義するようなお決まり(コーディング上の約束、規則)があれば理解できますが。。。
    ないでしょ。

    キャンセル

  • 2019/01/07 15:12 編集

    > 因みに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

    キャンセル

  • 2019/01/07 18:28 編集

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

    キャンセル

  • 2019/01/07 18:50 編集

    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

    キャンセル

  • 2019/01/09 22:35 編集

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

    キャンセル

  • 2019/01/09 22:58

    グラフが構成されているということはあるノードから別のノードの情報までたどることができるということです。
    なので、末端の loss ノードから中間層を表すノードをたどることができ、各ノードは出力数など必要なパラメータを保持しているので、つまり、その情報を得ることができるということです。

    文章だとイマイチ理解できないようであれば、実際手を動かしてみて理解したほうがいいかもしれません。
    他の回答者さんも勧めていますが、「ゼロから作るDeepLearnig」という本を読んでみてはどうですか。
    この本は実際に作りながら、計算グラフやバックプロパゲーションなどDeep Learning のライブラリがどのように作られているか学ぶことができます。難しい内容はでてこないのでおすすめです。

    キャンセル

  • 2019/01/10 01:09 編集

    追記を見ました。

    > 実体を確保したのでしょうか

    疑問に思われてる部分は 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) を実行して作成されたテンソルから参照されるようになるので、ガベージコレクションで破棄されず、以前として実体は存在します。

    詳しくは「参照カウンタ」「ガベージコレクション」などで調べてください。

    キャンセル

  • 2019/01/11 07:03 編集

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

    キャンセル

  • 2019/01/11 12:05 編集

    self.W_list に重みの一覧を入れておかなくてもメモリ上には破棄されない限りあります。

    1. tensorflow の関数でモデルを作成していく仮定でグラフが作られる、あるノードは別のノードと結ばれる
    2 各ノードは他のノードとエッジで結ばれているので、つまり参照されている。よってそれを表す変数がなくてもガベージコレクションによって破棄されないので、メモリ上に存在したままである
    3 順伝搬のときは入力ノードから出力ノードまで辿って計算できる。
    4 逆伝搬のときは出力ノードから入力ノードまで辿って計算できる。

    以上の説明でもわからないようであれば、実際に Deep Learning ライブラリのソースコード (Tensorflow, pytorch、chainer) を読む、それも難しいようであれば書籍を参考に手を動かして理解するしかないと思います。

    他の方も紹介している「ゼロから作るDeep Learning」は以下の本です
    https://www.amazon.co.jp/dp/4873117585

    キャンセル

  • 2019/01/12 00:12

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

    キャンセル

+2

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

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/01/07 18:26

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

    キャンセル

  • 2019/01/11 07:39

    本を探しています

    キャンセル

+1

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

class Var():
    def __init__(self, variable_name: str) -> None:
        super().__init__()
        self.variable_name = variable_name

    def __str__(self):
        return self.variable_name

    def __mul__(self, other):
        return BinOp(self, other, '×')

    def __add__(self, other):
        return BinOp(self, other, '+')


class BinOp():
    def __init__(self, a, b, op) -> None:
        super().__init__()
        self.left = a
        self.right = b
        self.op = op

    def __str__(self) -> str:
        return ' '.join(map(str, ['(', self.left, self.op, self.right, ')']))

    def __mul__(self, other):
        return BinOp(self, other, '×')

    def __add__(self, other):
        return BinOp(self, other, '+')


expr = Var('w') * Var('x') + Var('y') * Var('z')
print(expr)
print()
print(expr.left)  # => expr が保持する左の項
print(expr.op)  # => expr が保持する演算子
print(expr.right)  # => expr が保持する右の項
print()
print(expr.left.left)  # => expr が保持する左の項の左の項
print(expr.left.op)  # => expr が保持する左の項の演算子
print(expr.left.right)  # => expr が保持する左の項の右の項

実行結果

( ( w × x ) + ( y × z ) )

( w × x )
+
( y × z )

w
×
x

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


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

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/01/09 21:48

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

    キャンセル

同じタグがついた質問を見る

  • Python

    9750questions

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

  • Python 3.x

    7878questions

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

  • 機械学習

    798questions

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

  • TensorFlow

    796questions