解決方案: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的概念
本文可以分爲四個部分
- 簡單的介紹一下graph的概念;
- 詳細的介紹一下Session的概念;
- 介紹Session和graph之間的關係和交互概念
- 介紹使用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_default
和tf.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_default
和tf.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一下。