以上結果表明,該模型在學習複雜句法結構方面表現得相當不錯。這些結果令人印象深刻,我的實驗夥伴( Justin Johnson )和我打算在結構上再深入研究,我們使用這本關於代數棧/幾何的 書 。我們下載了Latex的源文件(16MB),然後訓練了一個多層的LSTM。令人驚訝的是,由Latex產生的樣本幾乎是已經匯總好了的。我們不得不介入並手動修復了一些問題,這樣你就得到了合理的數學推論,這是相當驚人的:
代數幾何樣本(假的), 真正的PDF文件在這 。
這是另一份樣本:
產生了更多假的代數幾何,嘗試處理圖像(右)
正如上面你所看到的那樣,有些時候這個模型試圖生成LaTeX圖像,但很明顯它並不明白圖像的具體意思。同樣我很喜歡模型選擇跳過證明過程的那部分(「Proof omitted」,左上角)。當然,Latex有相對困難的結構化句法格式,我自己都還沒有完全掌握。為舉例說明,下面是模型中的一個原始樣本(未被編輯):
\begin{proof}We may assume that $\mathcal{I}$ is an abelian sheaf on $\mathcal{C}$.\item Given a morphism $\Delta : \mathcal{F} \to \mathcal{I}$is an injective and let $\mathfrak q$ be an abelian sheaf on $X$.Let $\mathcal{F}$ be a fibered complex. Let $\mathcal{F}$ be a category.\begin{enumerate}\item \hyperref[setain-construction-phantom]{Lemma}\label{lemma-characterize-quasi-finite}Let $\mathcal{F}$ be an abelian quasi-coherent sheaf on $\mathcal{C}$.Let $\mathcal{F}$ be a coherent $\mathcal{O}_X$-module. Then$\mathcal{F}$ is an abelian catenary over $\mathcal{C}$.\item The following are equivalent\begin{enumerate}\item $\mathcal{F}$ is an $\mathcal{O}_X$-module.\end{lemma}這份樣本來自一個相對合適的模型,其中顯示出了一些常見錯誤。比如,模型打開了一個\begin{proof}環境,但是卻以\end{lemma}結束。這個問題我們必須手動修復,這可能是由於依賴關係太長而導致的:當這個模型做完了 proof過程,它就忘記了它是在做proof還是lemma了。同樣的,當模型打開了一個\begin{enumerate}環境,然後卻忘記關閉它了。我們發現,當我們使用更大/更好的模型的時候,這種情況出現得就較少了,儘管如此,這些問題還是會出現。
Linux原始碼我想看看這個模型處理結構化數據的極限,所以為了這一終極目標,我打算開始編寫一些代碼,特別的,我瀏覽了 Github上Linux repo 所有的原始資料和頭文件,把它們串在一起形成一個單一的巨大文件(474MB大小的C代碼)(我本來打算只訓練內核,但是它只有16MB)。然後,我花了幾天時間訓練出了大小剛好適合我GPU的3層LSTM網絡。這些模型有大約1000萬個參數,然而這對於RNN網絡來說還是微不足道的。結果非常有趣:
/* * Increment the size file of the new incorrect UI_FILTER group information * of the size generatively. */static int indicate_policy(void){ int error; if (fd == MARN_EPT) { /* * The kernel blank will coeld it to userspace. */ if (ss->segment < mem_total) unblock_graph_and_set_blocked(); else ret = 1; goto bail; } segaddr = in_SB(in.addr); selector = seg / 16; setup_works = true; for (i = 0; i < blocks; i++) { seq = buf[i++]; bpf = bd->bd.next + i * search; if (fd) { current = blocked; } } rw->name = "Getjbbregs"; bprm_self_clearl(&iv->version); regs->new = blocks[(BPF_STATS << info->historidac)] | PFMR_CLOBATHINC_SECONDS << 12; return segtable;}這代碼整體上看起來非常棒。我認為它不會馬上就能編譯通過,但是當你滾動查看這些代碼的時候給你的感覺是這是一個非常強大的C代碼庫。注意到,RNN到處隨機生成代碼片段和注釋。它很少會犯語法錯誤。比如,它適當的使用了字符類型、指針概念等。同樣它正確打開和關閉了代碼塊{[,並且學習將代碼縮進。一處常見的錯誤就是它不能跟蹤變量名:它常常會使用未定義的變量(比如上面出現的rw),或者聲明它從來不用的變量(比如上面的int error),或者返回一個不存在的變量。讓我們來看看更多的案例。下面是另外一個代碼片段,顯示了RNN學習操作數組的情況:
/* * If this error is set, we will need anything right after that BSD. */static void action_new_function(struct s_stat_info *wb){ unsigned long flags; int lel_idx_bit = e->edd, *sys & ~((unsigned long) *FIRST_COMPAT); buf[0] = 0xFFFFFFFF & (bit << 4); min(inc, slist->bytes); printk(KERN_WARNING "Memory allocated x/x, " "original MLL instead\n"), min(min(multi_run - s->len, max) * num_data_in), frame_pos, sz + first_seg); div_u64_w(val, inb_p); spin_unlock(&disk->queue_lock); mutex_unlock(&s->sock->mutex); mutex_unlock(&func->mutex); return disassemble(info->pending_bh);}static void num_serial_settings(struct tty_struct *tty){ if (tty == tty) disable_single_st_p(dev); pci_disable_spool(port); return 0;}static void do_command(struct seq_file *m, void *v){ int column = 32 << (cmd[2] & 0x80); if (state) cmd = (int)(int_state ^ (in_8(&ch->ch_flags) & Cmd) ? 2 : 1); else seq = 1; for (i = 0; i < 16; i++) { if (k & (1 << 1)) pipe = (in_use & UMXTHREAD_UNCCA) + ((count & 0x00000000fffffff8) & 0x000000f) << 8; if (count == 0) sub(pid, ppc_md.kexec_handle, 0x20000000); pipe_set_bytes(i, 0); } /* Free our user pages pointer to place camera if all dash */ subsystem_info = &of_changes[PAGE_SIZE]; rek_controls(offset, idx, &soffset); /* Now we want to deliberately put it to device */ control_check_polarity(&context, val, 0); for (i = 0; i < COUNTER; i++) seq_puts(s, "policy ");}注意到,在第二個函數中,模型進行了這樣的比較tty == tty,這是永遠成立的。但是,至少這次變量tty只在這個函數中起作用!最後一個函數,注意到代碼沒有返回任何值,但是這是正確的因為函數的返回值聲明成了void。但是,前兩個函數同樣聲明成了void返回類型,卻返回了數值。這又是一個由於長期迭代導致的常見錯誤。
有些時候模型會決定現在是時候產生一個新的文件了。這通常是有趣的部分:首先模型一個一個字符記住GNU許可證,然後include幾個頭文件,聲明一些宏,然後就開始生成代碼部分:
/* * Copyright (c) 2006-2010, Intel Mobile Communications. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published by * the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */#include <linux/kexec.h>#include <linux/errno.h>#include <linux/io.h>#include <linux/platform_device.h>#include <linux/multi.h>#include <linux/ckevent.h>#include <asm/io.h>#include <asm/prom.h>#include <asm/e820.h>#include <asm/system_info.h>#include <asm/setew.h>#include <asm/pgproto.h>#define REG_PG vesa_slot_addr_pack#define PFM_NOCOMP AFSR(0, load)#define STACK_DDR(type) (func)#define SWAP_ALLOCATE(nr) (e)#define emulate_sigs() arch_get_unaligned_child()#define access_rw(TST) asm volatile("movd %%esp, %0, %3" : : "r" (0)); \ if (__type & DO_READ)static void stat_PC_SEC __read_mostly offsetof(struct seq_argsqueue, \ pC>[1]);static voidos_prefix(unsigned long sys){#ifdef CONFIG_PREEMPT PUT_PARAM_RAID(2, sel) = get_state_state(); set_pid_sum((unsigned long)state, current_state_str(), (unsigned long)-1->lr_full; low;}這裡有太多有趣的地方可以講述了,關於這部分我可能可以寫一篇完整的博客來講述。但是在這裡我就省略這部分了,如果你有興趣查看,請點擊 1MB of sampled Linux code
了解到底發生了什麼我們可以看出,最終訓練出的結果是令人印象深刻的,但是這一切都是如何工作的?讓我們通過運行兩個快速的實驗來簡要探索下。
訓練中樣本的演變
首先,探索模型訓練過程中樣本文本是如何演變的是件有趣的事,比如,我用列夫·託爾斯泰的《戰爭與和平》訓練了一個LSTM網絡,然後每100次迭代訓練就生成樣本。100次迭代後模型生成了隨機、混亂的樣本:
tyntd-iafhatawiaoihrdemot lytdws e ,tfti, astai f ogoh eoase rrranbyne 'nhthnee e plia tklrgd t o idoe ns,smtt h ne etie h,hregtrs nigtike,aoaenns lng但是,注意到模型最少知道了要在每個「單詞」間加上空格。有些時候會加上兩個空格。同樣模型不知道逗號後面通常跟著空格。300次迭代後我們可以看到模型開始有了引號和句號的概念:
"Tmont thithey" fomesscerliundKeushey. Thom heresheulke, anmerenith ol sivh I lalterthend Bleipile shuwy fil on aseterlomecoaniogennc Phe lism thond hon at. MeiDimorotion in ther thize."這次單詞之間同樣由空格隔開,而且模型開始知道在句子末尾加上句號。500次迭代:
we counter. He stutn co des. His stanted out one ofler that concossions and was to gearang reay Jotrets and with fre colt otf paitt thin wall. Which das stimn現在模型會拼寫最短、最常見單詞,比如「we」,「He」,「His」,「Which」,「and」等。700次迭代的時候,我們得到了更像英語的文本:
Aftair fall unsuch that the hall for Prince Velzonski's that me ofher hearly, and behs to so arwage fiving were to it beloge, pavu say falling misfort how, and Gogition is so overelical and ofter.在經過1200次迭代後,我們發現模型開始使用引號和問號/感嘆號了,與此同時也學會了更長的單詞:
"Kite vouch!" he repeated by herdoor. "But I would be done and quarts, feeling, then, son is people...."經過2000次迭代,模型能夠正確拼寫的單詞,引號,名字等:
"Why do what that day," replied Natasha, and wishing to himself the fact theprincess, Princess Mary was easier, fed in had oftened him.Pierre aking his soul came to the packs and drove up his father-in-law women.大體上我們可以這麼認為,首先模型發現了單詞-空格這樣普遍的結構,然後迅速學會拼寫單詞;從學習拼寫簡單單詞開始到最後學習拼寫長單詞。在單詞中顯現出主題內容(一般依賴於長期性)在很多次迭代後才出現。
RNN預測結果和神經元激活可視化
可視化有趣的另一個方面是可以按字符來觀察預測分布。在下面的可視化圖中,我們向Wikipedia RNN模型中輸入了校驗好的數據集(藍色/綠色的行)中的字符,然後在每個字符下面,我們列出了(紅色部分)模型預測會出現的前5個字符,顏色深淺根據它們概率大小決定(深紅:預測準確,白色:不準確)。比如,注意到有一連串字符是模型非常有信心能夠預測準確的(對http://www.序列模型置信度非常高)。
輸入字符序列(藍色/綠色)的顏色取決於RNN隱藏層中隨機選擇的神經元的激活情況。定義綠色=非常興奮,藍色=不是那麼興奮(對於那些熟悉LSTMs細節的人來說,這些就是隱藏向量中[-1,1]範圍內的值,也就是經過門限操作和tanh函數的LSTM單元狀態)。直觀的,下圖顯示了在RNN「大腦」讀取輸入序列時一些神經元的激活情況。不同的神經元可能有不同的模式;下面我們將會看到我找到的4個不同的神經元,我認為這幾個是有趣的,並且是可解釋的(許多其他的並不容易解釋):
此圖中高亮的神經元似乎對URL極度興奮,URL以外的地方就不那麼興奮。LSTM似乎會用這種神經元來記住它是否在URL之中。
在這張圖中,當RNN在[[]]標記之中時,高亮的神經元表現極度興奮,所以在這種標記之外就沒那麼興奮,在神經元碰到字符「[」的時候不會表現得興奮,它一定要等到出現第二個「[」才會激活。計算模型是否碰到了一個還是兩個「[」的任務似乎可以用一個不同的神經元完成。
在這裡,我們可以看出在[[]]環境中,神經元有著線性的變化。換句話說,它的激活函數給了RNN中[[]]範圍的一種基於時間的坐標系統。RNN可以通過這些信息或多或少的依賴於字符在[[]]中出現的早/晚來生成不同的字符(有可能是這樣)。
這是另外一個神經元,它有著更個性化的行為:它整體上比較平淡無常,但是碰到「www」中第一個「w」的時候突然就開始變得興奮。RNN可能可以使用這種神經元來計算「www」序列的長度,這樣它就知道是否應該再添加一個「w」還是開始添加URL。
當然,由於RNN隱藏狀態的龐大性,高維度性和分布式特性,很多這樣的結論都稍微要加上特別說明才能理解。
原始碼我希望通過我上面的講述,你會覺得訓練一個字符級語言模型是一次有趣的練習。你可以使用我在Github(擁有MIT許可)上發布的 char-rnn code 來訓練你自己的模型。它將大量文本文件作為輸入,訓練出一個字符級模型,然後你就可以從中得到樣本文件。同時,如果你有一個GPU的話會對你有所幫助,否在在CPU上訓練的話會花大約10多倍的時間。不管怎樣如果你完成了訓練並且得到了有趣的結果,請告知我!如果你在使用Torch/Lua代碼庫的時候碰到困難,請記住,這只是 100-line gist 的一個版本。
一些題外話。代碼是由 Torch7 編寫的,它最近成了我最喜歡的深度學習框架。我是最近幾個月才開始使用Torch/Lua的,它們並不簡單(我花了大量時間來閱讀Github上Torch源碼,向它的發布者提問才掌握它),但是一旦你掌握了足夠的知識,它就會給你帶來很大的靈活性和速度提升。我以前同樣使用過Caffe和Theano,我認為Torch並不完美,但是它的抽象層次和原理比其他的要優秀。在我看來,一個有效框架應該具有以下功能:
有許多功能(slicing,array/matrix等操作)的CPU / GPU透明的Tensor庫。
一個完全獨立的代碼庫,它基於腳本語言(最好是 Python),工作在Tensors上,實現了所有深度學習方面的東西(前饋/後饋,圖形計算等)。
它應該可以輕鬆共享預訓練模型(Caffe在這方面做的很好,其他幾個則存在不足),這也是至關重要的。
沒有編譯過程(或者說不要像Theano目前那樣做)。深度學習是朝著更大更複雜的網絡發展,所以在複雜圖算法中花費的時間會成倍增加。重要的是,長時間或者是在開發階段不進行編譯所帶來的影響是非常巨大的。而且,進行編譯的話就會丟失可解釋性和有效進行日誌記錄/調試的能力。如果為提高效率在圖算法開發好後立即編譯,那麼這樣做是可取的。
延伸閱讀
在結束這篇文章之前,我還想再介紹更多關於RNNs的東西,並大致提供目前的研究方向。最近在深度學習領域,RNNs頗受歡迎。和卷積網絡一樣,它們已經存在了幾十年,但它們的潛力最近才開始得到廣泛的認可,這在很大程度上是因為我們不斷增長的計算資源。下面是一些最近事態發展的簡要介紹(這絕對不是完整的列表,很多這樣的工作讓研究界好像回到了上世紀90那種研究熱潮,來看看相關的工作部分):
在NLP/語音領域,RNNs將 語音轉錄成文本 ,使用 機器翻譯 , 生成手寫文本 ,當然,它們已經被用來當做強大的語言模型( Sutskever等人 )( Graves )( Mikolov等人 )(都是在字符和單詞層面)。目前看來,單詞級的模型比字符級的模型要更好,但這肯定是暫時的。
計算機視覺。在計算機視覺方面,RNNs也很快成為了無處不在的技術。比如,我們會見到RNNs在幀層面 分類視頻 , 添加圖片字幕 (同樣包括我和其他人的工作), 添加視頻字幕 ,最近又用來 視覺問答 。我個人最喜歡的RNNs計算機視覺方面的論文是 Recurrent Models of Visual Attention ,這是因為它有著高層次方向特性(glance順序處理圖像)和低層次建模(REINFORCE學習規定它是強化學習中一個特定的梯度方法,可以訓練出進行不可微計算的模型(以圖像周邊glance處理為例))。我相信,這種混合模型類型——由CNN形成的原始感知器再加上RNN glance策略,將會在感知器中普遍存在,特別是對於比分類更複雜的任務。
歸納推理,存儲和關注模塊。另一個非常令人興奮的研究方向是面向解決Vanilla遞歸網絡的局限性。它的一個問題是RNNs不是數學歸納的:它們能夠非常好的記住序列,但是並不一定總能夠得到正確的結果(我會舉幾個例子來具體說明)。第二個問題是,它們不必每步都將代表大小和計算數量結合起來。具體來說,如果你把隱藏狀態向量的大小增加一倍,由於矩陣乘法,每步中FLOPS的數量會變成原來的四倍。理想情況下,我們想保持龐大的代表性/存儲(比如,包含維基百科所有的內容或者多個中間狀態變量),而同時保持計算的每個時間步長固定的能力。
在這些方向上,第一個有說服力的例子在DeepMind上的 Neural Turing Machines 這篇論文中講述了。這篇論文描述了在計算開始時,模型在大的、外部的存儲陣列和更小寄存器之間執行讀寫操作的方法(把它想像成是你自己的工作存儲器)。至關重要的是,這篇論文也同樣介紹了有趣的內存尋址機制,這是用一個(平緩的,完全可微的)關注模塊實現的。平緩關注模塊的概念是一個強大的建模特徵,同時也是 Neural Machine Translation by Jointly Learning to Align and Translate 這篇文章再機器翻譯上的一大特色,網絡存儲則用來問答。事實上,我不得不說:
關注模塊的概念在最近的神經網絡架構創新中是最有趣的。現在,我不想講太多的細節,但是內存尋址的平緩關注模塊機制是非常方便的,因為它讓模型完全可微,但不幸的是,這會犧牲一些效率,因為所有可以被關注的都被關注了(平緩的)。你可以把它想像成C語言中的指針,它不指向具體的地址,而是定義了在整個內存中分布的所有地址,並將指針返回的所指向內容的加權總和值非關聯化(這個操作代價會很大!)。這促使多位作者在給多塊內存進行關注操作的時候使用平緩關注方式而不是猛烈關注方式(比如,對某些內存單元進行讀取操作,而不是從內存單元中讀出/寫入到某些地方)。這種模型有著更顯著的哲學吸引力,可擴展性和高效率性,但是不幸的是,它是不可微的。這就要使用強化學習文獻(比如REINFORCE)中使用到的技術來解決,人們將它完美地用於不可微的模型之中。這是正在進行的工作,但是針對這些困難的關注模型進行了探討,你可以在 Inferring Algorithmic Patterns with Stack-Augmented Recurrent Nets ,Reinforcement Learning Neural Turing Machines , Show Attend and Tell 這幾篇論文中了解到。
人物。如果你願意閱讀我推薦的 Alex Graves , Ilya Sutskever 和 Tomas Mikolov 所寫的文章。你可以從 David Silver 或 Pieter Abbeel 的公開課中了解到更多的關於REINFORCE,強化學習和策略梯度方法的知識。
代碼。如果你想實踐實踐訓練RNNs,我推薦你使用Theano的 keras 和 passage ,也可以使用連同這篇文章一同發布的Torch 代碼 ,或者使用我先前寫好的numpy原始碼 要點 ,它實現了一個有效率的、批量處理的LSTM前饋和後饋處理。你也可以看看我基於numpy的 NeuralTalk ,它用了RNN/LSTM來給圖片加字幕,或者你可以使用Jeff Donahue寫的 Caffe 。
總結
我們已經學習了RNNs是如何工作的,它們為什麼變得至關重要,我們在幾個有趣的數據集上訓練了一個字符級的RNN模型,並且我們看到了RNNs的執行情況。你完全可以毫無顧忌的用RNNs進行大量創新,並且我相信它們會在智能系統中成為一種普遍存在並且至關重要的組成部分。
最後,在這篇文章中加上一些元數據,我用這篇文章的源文件訓練了一個RNN。不幸的是,我只有46K的字符,沒有足夠的字符給RNN,但是返回的樣本(使用低溫度以得到更典型的樣本)如下所示:
I've the RNN with and works, but the computed with program of the RNN with and the computed of the RNN with with and the code好了,這篇文章是講述關於RNN和它工作狀況的,很明顯它工作良好 :)。我們下回見!
原文連結: http://karpathy.github.io/2015/05/21/rnn-effectiveness/(譯者/劉翔宇 審校/劉帝偉、朱正貴、李子健 責編/周建丁)
關於譯者: 劉翔宇,中通軟開發工程師,關注機器學習、神經網絡、模式識別。
本文為CSDN原創翻譯,未經允許不得轉載,如需轉載請聯繫market#csdn.net(#換成@)
點擊「閱讀原文」可查看專題並參與討論