這麼多深度學習框架,選擇的時候著實很頭疼。最早我是用Keras,後來隨著寫的模型越來越複雜,發現Keras實在是不夠靈活,太過於抽象了,就想找個偏底層的框架。在PyTorch與TensorFlow之間糾結了一段,最後因為PyTorch對跨平臺支援的不好,決定用TensorFlow(囧),不過後來又發現利用TensorBoard來畫圖真的很棒。
在使用TF的時候不時遇到一些問題,不得不說TF的API實在有些亂,而且tutorials寫的太不友好,上手比其他框架要難一些,但熟悉了以後發現還是很好用的。這篇文章總結一下遇到的一些問題,解決方案以及一些有趣的功能。
資料的匯入
由於我的資料量比較大,想利用TF的資料匯入機制來讀,這樣的話比較節省記憶體,而且TF還支援各種Format的decode函式,比較方便,其實主要還是比較懶不想自己寫dataloader。具體使用的是r1。2新新增的
tf.contrib.data
的API。程式碼也很簡單,就這麼點
def
input_pipeline
(
filenames
,
batch_size
):
# Define a `tf。contrib。data。Dataset` for iterating over one epoch of the data。
dataset
=
(
tf
。
contrib
。
data
。
TextLineDataset
(
filenames
)
。
map
(
lambda
line
:
tf
。
decode_csv
(
line
,
record_defaults
=
[[
‘1’
],
[
‘1’
],
[
‘1’
]],
field_delim
=
‘
\t
’
))
。
shuffle
(
buffer_size
=
10000
)
# Equivalent to min_after_dequeue=10。
。
batch
(
batch_size
))
# Return an *initializable* iterator over the dataset, which will allow us to
# re-initialize it at the beginning of each epoch。
return
dataset
。
make_initializable_iterator
()
filenames
=
[
‘1。txt’
]
batch_size
=
300
num_epochs
=
10
iterator
=
input_pipeline
(
filenames
,
batch_size
)
# `a1`, `a2`, and `a3` represent the next element to be retrieved from the iterator。
a1
,
a2
,
a3
=
iterator
。
get_next
()
with
tf
。
Session
()
as
sess
:
for
_
in
range
(
num_epochs
):
# Resets the iterator at the beginning of an epoch。
sess
。
run
(
iterator
。
initializer
)
try
:
while
True
:
a
,
b
,
c
=
sess
。
run
([
a1
,
a2
,
a3
])
(
a
,
b
,
c
)
except
tf
。
errors
。
OutOfRangeError
:
# This will be raised when you reach the end of an epoch (i。e。 the
# iterator has no more elements)。
pass
# Perform any end-of-epoch computation here。
(
‘Done training, epoch reached’
)
這個API是在
tf.train.string_input_producer
基礎上的一些改進,較為好用一些。可以在epoch的開始利用
sess.run(iterator.initializer)
進行重新shuffle。
但在用的過程中,我發現這種shuffle機制並不真的是全資料集進行shuffle。以上面的程式碼舉例說明TF的機制:首先設定buffer_size=10000代表將檔案中的前10000行讀入快取,然後根據batch_size=300隨機取出300,這時候,快取區只有9700個數據,於是又從檔案中取出300行填充進快取區,然後再shuffle取batch...
這種方法不僅沒法在全資料集上隨機,而且每取一次都需要shuffle buffer導致在跑起來很慢。最後我使用的還是自己寫的dataloader,相比TF提供的方法速度反而提高了五倍。
具體見在Stack Overflow上的討論 How to use TensorFlow tf。train。string_input_producer to produce several epochs data?
引數共享
拿部落格Text Matching(II)中的模型來說,如果模型需要對兩個輸入共享引數(如Question和Answer),就需要設計Graph的時候小心一些。通常是使用
tf.get_variable()
來宣告引數,然後將呼叫語句放在同一個variable_scope中宣告變數可以reuse,這樣TF在建圖的時候會自動檢測變數是否已被使用過。簡單地來寫一下就是
def
nets
(
sequence
):
W
=
tf
。
get_variable
(
‘W’
,
shape
,
initializer
=
tf
。
contrib
。
layers
。
xavier_initializer
())
pass
def
inference
(
question
,
answer
):
with
tf
。
variable_scope
(
“nets”
)
as
scope
:
q
=
nets
(
query
)
scope
。
reuse_variables
()
a
=
nets
(
answer
)
利用TensorBoard畫圖
使用了TensorBoard以後發現利用它來視覺化簡直太方便了,基本不用自己畫圖了。Tensorboard中提供一個tf。summary的API,其中常用的包含
Scalar:可以直接看到每一個step loss,accuracy等的變化情況
Distribution,Histogram:可以直接看引數在學習過程中的分佈變化,根據這個可以判斷自己的模型有沒有充分的學習
Graph:直接定義出模型的視覺化架構,方便看到建圖的過程。例如上面說的引數共享如果實現了的話,在Graph中我們就會看到question和answer使用的是同一個module
Embedding:可以利用PCA降維,將輸入對映到低維空間,很炫酷
這是強烈建議使用的功能,細節參考summaries_and_tensorboard,這個特性tutorials介紹的還是比較詳細的。