【轉】Tensor Tensor("dense_2/Softmax:0", shape=(?, 192), dtype=float32) is n

解決方案:https://cloud.tencent.com/developer/article/1167171

from django.shortcuts import render
from keras.models import load_model
from PIL import Image
import numpy as np

import tensorflow as tf

graph = tf.get_default_graph()

model = load_model('./model.h5')


# Create your views here.

def index(request):
    result = ''
    if request.POST:
        image = request.FILES['file']
        with open('dd.png', "wb+") as destination:
            for chunk in image.chunks():
                destination.write(chunk)
        arr = np.array(Image.open('./dd.png').convert('L'))
        arr.resize([28, 28, 1])
        global graph
        with graph.as_default():
            result = '預測結果:{}'.format(np.argmax(model.predict(np.array([arr]))))

    return render(request, 'index.html', {'result': result})

在學習一下tensorflow之Graph和Session的概念

本文可以分爲四個部分

  1. 簡單的介紹一下graph的概念;
  2. 詳細的介紹一下Session的概念;
  3. 介紹Session和graph之間的關係和交互概念
  4. 介紹使用keras的時候,session和graph是如何指定的

本文假設讀者對於tensorflow 的工作流程有一個大致的認知,而非小白。因爲在實際使用的過程中,在不同python文件中調來調去,我總是會不清楚session和graph的對應關係,想要弄清楚,因此寫了這篇文章。裏面的很多內容來自網絡的博文,也有部分來自於官網的介紹,代碼基本都是經過測試的。如果有理解不正確的地方,非常希望大家指正。

一、 Graph的概念

當tensorflow庫被加載時,它會自動創建一個Graph對象,並將其作爲默認的數據流圖。因此,在Graph.as_default()上下文管理器之外定義的任何op, tensor對象都會自動放置在默認的數據流圖中。

#如果希望獲得默認的數據流圖的句柄,可使用:
default_graph = tf.get_default_graph()

在大多數Tensorflow程序中,只使用默認圖就可以了

二、Session的概念

1. 簡單的session介紹

最常見的初學者模式中,我們這麼使用session:

import tensorflow as tf
import numpy as np
a=tf.constant([[1,2],[3,5]])
b=tf.constant([[1,2],[3,4]])
c=tf.add(a,b)
with tf.Session() as sess:
    sess.run(tf.global_variables_initializer())
    print(sess.run(c))
    d=tf.matmul(a,b)
    print(sess.run(d))

也就是說先在默認圖中定義操作節點,然後生成一個session,在session中執行初始化以及執行圖的流式操作。

2. tf.Session()tf.Session().as_defaulttf.get_default_session()

在tensorflow中關於session,還有一個默認session的概念,我們可以通過tf.get_default_session()來獲得當前線程的session,也可以通過tf.Session().as_default()來指定某個sess爲新進程的默認session。那麼問題來了,tf.Session(),tf.Session().as_default,tf.get_default_session()三者之間有什麼區別和聯繫呢?

2.1 說說tf.Session()tf.Session().as_default

tf.Session()創建一個會話,當上下文管理器退出時會話關閉和資源釋放自動完成。

tf.Session().as_default() ,看起來好像是創建了一個默認的session,這種session和不使用這種方法的session的區別是會話在上下文管理器退出時會話沒有關閉,還可以通過調用會話進行run()和eval()操作。但是一定不要忘記手動關閉這個sess

import tensorflow as tf
a = tf.constant(1.0)
b = tf.constant(2.0)
with tf.Session() as sess:
   print(a.eval())   
print(b.eval(session=sess))
#------------------------------
#1.0
# RuntimeError: Attempted to use a closed Session.
import tensorflow as tf
a = tf.constant(1.0)
b = tf.constant(2.0)
with tf.Session().as_default() as sess:
   print(a.eval())   
print(b.eval(session=sess))
# ------------------------------
# 1.0
# 2.0

2.2 說說tf.Session().as_defaulttf.get_default_session()

那麼通過tf.Session().as_default指定的sess就是當前線程的默認sess了嗎?不是的。通過一段代碼來說明

import tensorflow as tf

with tf.Graph().as_default() as g:
    with g.name_scope("myscope") as scope:  # 有了這個scope,下面的op的name都是類似myscope/Placeholder這樣的前綴
        sess = tf.Session(target='', graph=g, config=None)  # target表示要連接的tf執行引擎
        a = tf.placeholder("float")
        b = tf.placeholder("float")
        y1 = a*b  # 也可以寫成a * b
        operations = g.get_operations()
        print(sess.run(y1, feed_dict={a: 3, b: 3}))  # 9.0 feed_dictgraph中的元素和值的映射)
        assert tf.get_default_session() is not sess
        with sess.as_default():  # 把sess作爲默認的session,那麼tf.get_default_session就是sess, 否則不是
            assert tf.get_default_session() is sess
print("================ default sess str ================")
assert tf.get_default_session() is not sess
print(tf.get_default_session().sess_str)

可以發現,在sess.as_default()這上下文中,assert tf.get_default_session() is sess判定正確。但是出了和這個上下文之後,通過assert tf.get_default_session() is not sess可知,當前線程的默認session並不是sess了。

在前面我們看到,TensorFlow會自動生成一個默認的計算圖,如果沒有特殊指定,運算會自動加入這個計算圖中。TensorFlow中的會話也有類似的機制,但是TensorFlow不會自動生成默認的會話。

在tf的源碼中是這麼說的

@tf_export("get_default_session")
def get_default_session():
  """Returns the default session for the current thread.

  The returned `Session` will be the innermost session on which a
  `Session` or `Session.as_default()` context has been entered.

  NOTE: The default session is a property of the current thread. If you
  create a new thread, and wish to use the default session in that
  thread, you must explicitly add a `with sess.as_default():` in that
  thread's function.

這裏的意思是說,在最外層的tf.get_default_session()得到的session是當前線程默認的session,是我們創建的tf.Session()上下文的入口,是當前線程的一個屬性。當我們創建一個tf.Session(),實際啓動了一個新的線程,我們指定的````with sess.as_default()```上下文實際上是一個新的線程並且指定了這個新線程的默認session。

這就解釋了了爲什麼,在某個我們定義的session的上下文環境中,我們使用tf.get_default_session的判定sess是對的,而離開這個上下文環境,使用tf.get_default_session is not sess進行判定又是錯的了。那麼我們之所以可以sess.as_default()上下文管理器外面執行run()和eval(),是因爲我們在另一個線程中執行的代碼,所以手動關閉sess,也就是關閉這個線程。

三、Session和graph之間的關係和交互概念

最簡單的一種情況,我們在默認圖中定義tf的操作,然後在一個定義的sess中執行默認圖的初始化操作和執行流式操作,得到結果。這種情況只有一個session和一個graph.

實際代碼中碰到的情況遠比這種教材上的情況複雜。我們會使用多個模型,有多個圖,有多個會話。想要使用不同的模型就要將這些模型加載到不同session中,並且聲明使用的時候申請是哪個session,從而避免由於session和想使用的模型不匹配導致錯誤,而使用多個graph就需要爲每個graph使用不同的session,但是每個graph也可以在多個session中使用,這個時候就需要在每個session中使用的時候明確使用的graph。

在我們需要構建多個圖的時候,我們可以將操作定義在不同圖的上下文環境中。

import tensorflow as tf

graph = tf.get_default_graph()
g1 = tf.Graph()  # 加載到Session 1的graph
g2 = tf.Graph()  # 加載到Session 2的graph

sess1 = tf.Session(graph=g1)  # Session1
sess2 = tf.Session(graph=g2)  # Session2

# 加載第一個模型
with sess1.as_default():
    assert tf.get_default_graph() is graph
    with g1.as_default()():
        assert tf.get_default_graph() is g1
# 加載第二個模型
with sess2.as_default():  # 1
    with g2.as_default():
        assert tf.get_default_graph() is g2

with sess1.as_default():
    with sess1.graph.as_default():  # 2
        assert tf.get_default_graph() is g1

with sess2.as_default():
    assert tf.get_default_graph() is graph
    with sess2.graph.as_default():
        assert tf.get_default_graph() is g2
# 關閉sess
sess1.close()
sess2.close()

在使用as_default使session在離開的時候並不關閉,在後面可以繼續使用直到手動關閉,由於有多個graph,所以sess.graph與tf.get_default_value的值是不相等的,因此在進入sess的時候必須sess.graph.as_default()明確什麼sess.graph爲當前默認graph,否則會報錯.

通過tensorflow源碼上的註釋,我們再來講講上面的這段代碼。

sess1 = tf.Session(graph=g1)這個代碼指定了創建session的執行的默認圖。當在同一段進程中使用超過一個圖的時候,建議對每個圖使用不同的session,但是每個圖可以在多個session中被使用。因此通過這種顯式的方式來傳遞graph參數,可以讓代碼更加易讀。

在sess.as_default的代碼註釋中,是這麼說的

    *N.B.* Entering a `with sess.as_default():` block does not affect
    the current default graph. If you are using multiple graphs, and
    `sess.graph` is different from the value of @{tf.get_default_graph},
    you must explicitly enter a `with sess.graph.as_default():` block
    to make `sess.graph` the default graph.

當使用with sess.as_default():進入一個新的線程模塊的時候,並不會影響當前的默認圖,我們只能通過顯式的聲明with g1.as_default():或者with sess1.graph.as_default():才能進入這個sess指定的graph

四、使用keras.tensorflow_backend

如果用tensorflow寫的model,一般來說每個model都有自己的session和graph

但是在keras,會經常忽略掉session和graph,這時候需要添加session和好幾個地方加with graph,僞代碼如下:


seg_graph = tf.Graph()
sess = tf.Session(graph=seg_graph)
K.set_session(sess)
K.get_session().run(tf.global_variables_initializer())
 
#保證代碼
with seg_graph.as_default():
 
     self.keras_model = self.build(mode=mode, config=config)
     #上面一行代碼會調用KM.Model
     
     以及這類函數
      topology.load_weights_fromXXX()
     
     以及predict函數

在GPU上執行的時候,發現K.get_session().run(tf.global_variables_initializer())很重要,如果沒有這一步,那麼在啓動的時候,一直會包內存溢出問題,到底是什麼原因,暫時還沒有弄清楚,先mark一下。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章