天天看點

從原理到實戰 英偉達教你用PyTorch搭建RNN(上)

從 siri 到谷歌翻譯,深度神經網絡大步推動了機器對自然語言的了解。

迄今為止,大多數模型把語言看作是字詞的平面序列(flat sequence),使用時間遞歸神經網絡(recurrent neural network)來處理。但語言學家認為,這并不是看待語言的最佳方式,應把其了解為由短語組成的的分層樹狀結構( hierarchical tree of phrases)。由于對該類結構的支援,大量深度學習研究投入到結構遞歸神經網絡(recursive neural network)之中。在業内,這些模型有非常難以執行、運作起來效率低下的名聲。

但對于今年 facebook 開源的新深度學習架構 pytorch ,業内人士人認為它的一大貢獻是:搭建結構遞歸神經網絡以及其它複雜自然語言處理模型,變得更簡便。

結構遞歸神經網絡,是展示 pytorch 靈活性的一個不錯的例子。但同時,pytorch 是一個對于各類深度學習任務有完備功能的架構,雖然特别适于計算機視覺問題。它誕生于 facebook ai 研究院 fair 的研究人員之手,pytorch 把 torch7 中高效、靈活的 gpu 加速後端算法庫,和直覺性的 python 前端結合到一起,并具有快速建立原型機的能力、高度可讀性的代碼、以及對于各類深度學習模型的支援。

本文将帶領大家在 pytorch 上,實作一個有 recurrent tracker 和 treelstm 節點的結構遞歸神經網絡,即 spinn。對于相當多的主流深度學習架構,這是一個很難搭建的自然語言處理模型。這裡我描述的實作部分做了 batch,是以能夠利用 gpu 加速的性能,比不用 batch 的版本要快得多。

這裡的任務,是把成對的語句分類為三組類别:假設第一個句子是對某幅使用者無法看到的圖像的準确注解,第二個句子同樣是對該圖像的注解,那麼第二句話到底是 (a) 絕對準确 (b) 可能準确還是 (c) 絕對不準确的?舉個例子,假設第一句話是 “two dogs are running through a field”(兩條狗穿過一片農田)。那麼,讓這組語句“絕對正确”的句子可以是““there are animals outdoors”(戶外有動物);讓它們“可能準确”的,可以是“some puppies are running to catch a stick”(一群幼犬跑着去接一個木棒);讓它們“絕對不準确”的可以是 “the pets are sitting on a couch”(寵物們坐在沙發上)。

導緻 spinn 誕生的研究,為實作其目标要在決定句子之間的關系之前,把每句話編碼為固定長度的矢量表達(還有其他方式,比如注意力模型)。

資料集包含機器生成的文法樹( syntactic parse trees),後者把每句話裡的詞組合為短語和子句,每一個都有獨立涵義,并且有兩個詞或 sub-phrases 組成。許多語言學家認為,人類了解語言,是通過把涵義以層級(hierarchical)方式組合起來,就像這樣的樹狀結構。是以,建立一個以同樣方式運作的神經網絡或許是必要的。下面的例子是一個資料集裡的句子,它的文法樹以括号結構表示:

用支援文法樹結構的神經網絡對這個句子編碼,方法之一是建立一個神經網絡層 reduce,把詞組(以 glove 這樣的 word embedding 來表示)或短語組合起來,然後将這一層循環應用,把上一個 reduce 操作的結果,作為句子的編碼:

但如果,我想讓神經網絡以更“人性化”的方式運作呢?能從左到右閱讀,保持語境,同時使用文法樹把短語組合起來?或者,如果我想要訓練一個神經網絡,讓它在看到這句子時,基于讀到的詞語建立它自己的文法樹?這是一個同樣的文法樹,隻是寫出來的方式稍稍有差別:

第三種方法,仍然是一回事:

我所做的,僅僅是去除括号,用“s” 代表“shift”來标記詞語, 并用“r”代表“reduce”替代右括号。現在,資訊可作為操作堆棧(stack) 和類似堆棧的 buffer 的一系列指令從左讀到右,與上文描述的循環方式有同樣的結果:

将文本導入 buffer。

從 buffer 的首詞“the” pop 出去,把 push 入棧,這時“the”應該在“church”前面。

pop 最上面的兩個堆棧值,應用 reduce,把結果 push 入棧。

從 buffer pop 出“has”,push 到入棧,随後 “cracks”,再“in”,再“the”,再“ceiling”。

重複四次:pop 最前面的兩個堆棧值,應用 reduce,push 結果。

從緩存 pop 出“.”再 push 入棧。

重複兩次:pop 最前面的兩個堆棧值,應用 reduce,push 結果。

pop 剩下的堆棧值,作為句子編碼傳回。

我還想維持語境,照顧到其他資訊——句子中系統已讀取的部分,并在句子的之後部分上進行 reduce 操作。是以,我将把兩個參數(two-argument)的 reduce 函數,用三個參數的函數來替代,後者導入左子樹、右子樹短語以及目前語境狀态。該狀态由第二個神經網絡層生成——一個名為 tracker 的循環機關。給定現有句子語境狀态,tracker 在堆棧操作的每一步生成一個新狀态(讀取每個詞語和右括号之後),buffer 最頂端的 entry b 和堆棧中最頂端的兩個 entries s1、s2:

可以想象一下用你最喜歡的程式設計語言寫這些東西:對于需要處理的每個句子,它會從 buffer 中加載下一個詞,運作 tracker,檢查是否要 push 入棧或者進行 reduce,操作後不斷重複,直到整個句子處理完畢。當應用在單個句子上面,這個過程由大且複雜的深度神經網絡運作,網絡上的兩個可訓練層一遍遍按照 stack manipulation 規定的方式執行。

但如果你對 tensorflow、theano 等傳統深度學習架構很熟悉,你就知道執行這類動态過程有多麼費勁。這值得我們花點時間多想想,為什麼它們處理這種任務力不從心,以及 pytorch 是否能提供不一樣的東西。

從原理到實戰 英偉達教你用PyTorch搭建RNN(上)

本質上,深度神經網路隻是有海量參數的複雜函數。深度學習的目标也僅僅是通過計算 partial derivatives(梯度)、衡量損失來優化這些參數。如果該函數以計算圖結構來表示,反着運作能去除計算梯度的不必要工作。所有現代深度學習架構都是基于這一反向傳播概念,作為結果,每個架構都需要找到一種方式來表示計算圖。

大多數的主流深度學習架構,比如 tensorflow、theano、keras 以及 torch7 的 nngraph 算法庫,它們的計算圖都是事先建立好的靜态物體。該圖由看起來像是數學表達的代碼來定義,但它的變量其實是還沒有賦予任何數值的占位符(placeholder)。由占位符标量組成的圖,編譯為一個函數,然後重複在訓練資料 batch 上運作,生成輸出和梯度。

這種靜态計算圖在 cnn 上的效果很好,後者的結構一般是固定的。但對于許多應用,開發者需要讓神經網絡圖的機構能随資料修改。在自然語言進行中,研究人員通常希望把時間遞歸神經網絡展開,最好輸入有多少詞,就有多少時間步(timestep)。上文提到的 spinn 模型的 stack manipulation,非常倚賴控制流,比如“for”和“if”statement,來定義某個特定句子的計算圖結構。在更複雜的例子裡,你也許想要搭建結構取決于子網絡輸出的模型。

有的想法可以被硬塞進靜态圖系統,但不是全部 ,而且幾乎總是以更糟的透明度、看不懂的代碼作為代價。架構需要給計算圖添加代表了程式設計基本指令(loops and conditionals)的特殊節點,使用者需要學習、使用這些節點,而不是程式語言中的 “for”和“if” statement。這是因為任何程式員使用的控制流 statement  均隻能使用一次,在建立圖時寫死(hard coding)一條計算通道。

比如說,詞語(從初始狀态 h0 開始)中的矢量上,運作一個時間遞歸神經網絡(rnn_unit)需要 tf.while_loop,一個特殊的控制流節點。在 tensorflow 運作時擷取詞語長度需要一個額外特殊節點,這是由于代碼運作的時候它隻是一個占位符。

一個在根本上與之差別的方式,是動态計算圖,這在幾十年前的學界就已展開研究,又被稱為“define-by-run”。哈佛大學研發出來的 kayak 、autograd,以及研究導向的架構 chainer and dynet 都基于動态計算圖。在這樣的架構中,計算圖在運作時才被建立出來或重新建立。進行前饋通道運算的代碼,也為反向傳播建立所需的資料結構。該方式生成更直覺的代碼,因為控制流使用标準的“for”和“if”來寫。修補漏洞也變得更簡單,因為運作時的斷點、堆棧蹤迹(stack trace)讓你直接找到寫的代碼,而不是執行引擎裡的編譯函數。同樣變量長度的時間遞歸神經網絡,可用簡單的 python “for”循環在動态架構裡實作。

pytorch  是第一個在性能、靈活性上媲美靜态圖架構的 “define-by-run”深度學習架構。這使它适合于開發幾乎所有模型,從标準的卷積網絡到最離譜的強化學習想法。下篇中,我們将一起看看 spinn 的代碼實作。

====================================分割線================================

本文作者:三川

繼續閱讀