雷鋒網按:本文轉載自Magicly博客,獲作者授權。閱讀原文請見:。http://magicly.me/2017/04/07/rnn-lstm-generate-name/?utm_source=tuicool&utm_medium=referral
Magicly:之前翻譯了一篇介紹RNN的文章,一直沒看到作者寫新的介紹LSTM的blog,於是我又找了其他資料學習。本文先介紹一下LSTM,然後用LSTM在金庸、古龍的人名上做了訓練,可以生成新的武俠名字,如果有興趣的,還可以多搜集點人名,用於給小孩兒取名呢,哈哈,justforfun,大家玩得開心…
RNN回顧
RNN的出現是為了解決狀態記憶的問題,解決方法很簡單,每一個時間點t的隱藏狀態h(t)不再簡單地依賴於數據,還依賴於前一個時間節點t-1的隱藏狀態h(t-1)。可以看出這是一種遞歸定義(所以循環神經網絡又叫遞歸神經網絡RecursiveNeuralNetwork),h(t-1)又依賴於h(t-2),h(t-2)依賴於h(t-3)…所以h(t)依賴於之前每一個時間點的輸入,也就是說h(t)記住了之前所有的輸入。
上圖如果按時間展開,就可以看出RNN其實也就是普通神經網絡在時間上的堆疊。
RNN問題:Long-TermDependencies
一切似乎很完美,但是如果h(t)依賴於h(t-1000),依賴路徑特別長,會導致計算梯度的時候出現梯度消失的問題,訓練時間很長根本沒法實際使用。下面是一個依賴路徑很長的例子:
1我老家【成都】的。。。【此處省去500字】。。。我們那裡經常吃【火鍋】。。。
LSTM
LongShortTermMemory神經網絡,也就是LSTM,由Hochreiter&Schmidhuber於1997年發表。它的出現就是為了解決Long-TermDependencies的問題,很來出現了很多改進版本,目前應用在相當多的領域(包括機器翻譯、對話機器人、語音識別、ImageCaption等)。
標準的RNN裡,重複的模塊裡只是一個很簡單的結構,如下圖:
LSTM也是類似的鍊表結構,不過它的內部構造要複雜得多:
上圖中的圖標含義如下:
LSTM的核心思想是cellstate(類似於hiddenstate,有LSTM變種把cellstate和hiddenstate合併了,比如GRU)和三種門:輸入門、忘記門、輸出門。
cellstate每次作為輸入傳遞到下一個時間點,經過一些線性變化後繼續傳往再下一個時間點(我還沒看過原始論文,不知道為啥有了hiddenstate後還要cellstate,好在確實有改良版將兩者合併了,所以暫時不去深究了)。
門的概念來自於電路設計(我沒學過,就不敢賣弄了)。LSTM裡,門控制通過門之後信息能留下多少。如下圖,sigmoid層輸出[0,1]的值,決定多少數據可以穿過門,0表示誰都過不了,1表示全部通過。
下面我們來看看每個「門」到底在幹什麼。
首先我們要決定之前的cellstate需要保留多少。它根據h(t-1)和x(t)計算出一個[0,1]的數,決定cellstate保留多少,0表示全部丟棄,1表示全部保留。為什麼要丟棄呢,不是保留得越多越好麼?假設LSTM在生成文章,裡面有小明和小紅,小明在看電視,小紅在廚房做飯。如果當前的主語是小明,ok,那LSTM應該輸出看電視相關的,比如找遙控器啊,換臺啊,如果主語已經切換到小紅了,那麼接下來最好暫時把電視機忘掉,而輸出洗菜、醬油、電飯煲等。
第二步就是決定輸入多大程度上影響cellstate。這個地方由兩部分構成,一個用sigmoid函數計算出有多少數據留下,一個用tanh函數計算出一個候選C(t)。這個地方就好比是主語從小明切換到小紅了,電視機就應該切換到廚房。
然後我們把留下來的(t-1時刻的)cellstate和新增加的合併起來,就得到了t時刻的cellstate。
最後我們把cellstate經過tanh壓縮到[-1,1],然後輸送給輸出門([0,1]決定輸出多少東西)。
現在也出了很多LSTM的變種,有興趣的可以看這裡。另外,LSTM只是為了解決RNN的long-termdependencies,也有人從另外的角度來解決的,比如ClockworkRNNsbyKoutnik,etal.(2014).
showmethecode!
我用的AndrejKarpathy大神的代碼,做了些小改動。這個代碼的好處是不依賴於任何深度學習框架,只需要有numpy就可以馬上run起來!
"""
Minimalcharacter-levelVanillaRNNmodel.WrittenbyAndrejKarpathy(@karpathy)
BSDLicense
importnumpyasnp
#dataI/O
data=open('input.txt','r').read()#shouldbesimpleplaintextfile
all_names=set(data.split("\n"))
chars=list(set(data))
data_size,vocab_size=len(data),len(chars)
print('datahas%dcharacters,%dunique.'%(data_size,vocab_size))
char_to_ix={ch:ifori,chinenumerate(chars)}
ix_to_char={i:chfori,chinenumerate(chars)}
#print(char_to_ix,ix_to_char)
#hyperparameters
hidden_size=100#sizeofhiddenlayerofneurons
seq_length=25#numberofstepstounrolltheRNNfor
learning_rate=1e-1
#modelparameters
Wxh=np.random.randn(hidden_size,vocab_size)*0.01#inputtohidden
Whh=np.random.randn(hidden_size,hidden_size)*0.01#hiddentohidden
Why=np.random.randn(vocab_size,hidden_size)*0.01#hiddentooutput
bh=np.zeros((hidden_size,1))#hiddenbias
by=np.zeros((vocab_size,1))#outputbias
deflossFun(inputs,targets,hprev):
inputs,targetsarebothlistofintegers.
hprevisHx1arrayofinitialhiddenstate
returnstheloss,gradientsonmodelparameters,andlasthiddenstate
xs,hs,ys,ps={},{},{},{}
hs[-1]=np.copy(hprev)
loss=0
#forwardpass
fortinrange(len(inputs)):
xs[t]=np.zeros((vocab_size,1))#encodein1-of-krepresentation
xs[t][inputs[t]]=1
hs[t]=np.tanh(np.dot(Wxh,xs[t])+np.dot(Whh,
hs[t-1])+bh)#hiddenstate
#unnormalizedlogprobabilitiesfornextchars
ys[t]=np.dot(Why,hs[t])+by
#probabilitiesfornextchars
ps[t]=np.exp(ys[t])/np.sum(np.exp(ys[t]))
loss+=-np.log(ps[t][targets[t],0])#softmax(cross-entropyloss)
#backwardpass:computegradientsgoingbackwards
dWxh,dWhh,dWhy=np.zeros_like(
Wxh),np.zeros_like(Whh),np.zeros_like(Why)
dbh,dby=np.zeros_like(bh),np.zeros_like(by)
dhnext=np.zeros_like(hs[0])
fortinreversed(range(len(inputs))):
dy=np.copy(ps[t])
#backpropintoy.see
#http://cs231n.github.io/neural-networks-case-study/#gradifconfused
#here
dy[targets[t]]-=1
dWhy+=np.dot(dy,hs[t].T)
dby+=dy
dh=np.dot(Why.T,dy)+dhnext#backpropintoh
dhraw=(1-hs[t]*hs[t])*dh#backpropthroughtanhnonlinearity
dbh+=dhraw
dWxh+=np.dot(dhraw,xs[t].T)
dWhh+=np.dot(dhraw,hs[t-1].T)
dhnext=np.dot(Whh.T,dhraw)
fordparamin[dWxh,dWhh,dWhy,dbh,dby]:
#cliptomitigateexplodinggradients
np.clip(dparam,-5,5,out=dparam)
returnloss,dWxh,dWhh,dWhy,dbh,dby,hs[len(inputs)-1]
defsample(h,seed_ix,n):
sampleasequenceofintegersfromthemodel
hismemorystate,seed_ixisseedletterforfirsttimestep
x=np.zeros((vocab_size,1))
x[seed_ix]=1
ixes=[]
fortinrange(n):
h=np.tanh(np.dot(Wxh,x)+np.dot(Whh,h)+bh)
y=np.dot(Why,h)+by
p=np.exp(y)/np.sum(np.exp(y))
ix=np.random.choice(range(vocab_size),p=p.ravel())
x[ix]=1
ixes.append(ix)
returnixes
n,p=0,0
mWxh,mWhh,mWhy=np.zeros_like(Wxh),np.zeros_like(Whh),np.zeros_like(Why)
mbh,mby=np.zeros_like(bh),np.zeros_like(by)#memoryvariablesforAdagrad
smooth_loss=-np.log(1.0/vocab_size)*seq_length#lossatiteration0
whileTrue:
#prepareinputs(we'resweepingfromlefttorightinstepsseq_length
#long)
ifp+seq_length+1>=len(data)orn==0:
hprev=np.zeros((hidden_size,1))#resetRNNmemory
p=0#gofromstartofdata
inputs=[char_to_ix[ch]forchindata[p:p+seq_length]]
targets=[char_to_ix[ch]forchindata[p+1:p+seq_length+1]]
#samplefromthemodelnowandthen
ifn%100==0:
sample_ix=sample(hprev,inputs[0],200)
txt=''.join(ix_to_char[ix]forixinsample_ix)
print('----\n%s\n----'%(txt,))
#forwardseq_lengthcharactersthroughthenetandfetchgradient
loss,dWxh,dWhh,dWhy,dbh,dby,hprev=lossFun(inputs,targets,hprev)
smooth_loss=smooth_loss*0.999+loss*0.001
print('iter%d,loss:%f'%(n,smooth_loss))#printprogress
#performparameterupdatewithAdagrad
forparam,dparam,meminzip([Wxh,Whh,Why,bh,by],
[dWxh,dWhh,dWhy,dbh,dby],
[mWxh,mWhh,mWhy,mbh,mby]):
mem+=dparam*dparam
param+=-learning_rate*dparam/\
np.sqrt(mem+1e-8)#adagradupdate
p+=seq_length#movedatapointer
n+=1#iterationcounter
if((smooth_loss<10)or(n>=20000)):
sample_ix=sample(hprev,inputs[0],2000)
predicted_names=set(txt.split("\n"))
new_names=predicted_names-all_names
print(new_names)
print('predictednameslen:%d,new_nameslen:%d.\n'%(len(predicted_names),len(new_names)))
break
viewhostedwithbyrawmin-char-rnn.pyGitHub
然後從網上找了金庸小說的人名,做了些預處理,每行一個名字,保存到input.txt裡,運行代碼就可以了。古龍的沒有找到比較全的名字,只有這份武功排行榜,只有100多人。
下面是根據兩份名單訓練的結果,已經將完全一致的名字(比如段譽)去除了,所以下面的都是LSTM「新創作發明」的名字哈。來,大家猜猜哪一個結果是金庸的,哪一個是古龍的呢?
{'姜曾鐵','袁南蘭','石萬奉','郭萬嗔','蔡家','程伯芷','汪鐵志','陳衣','薛鐵','哈赤蔡師','殷飛虹','鍾小硯','鳳一刀','寶蘭','齊飛虹','無若之','王老英','鍾','鍾百勝','師','李沅震','曹蘭','趙一刀','鍾靈四','宗家妹','崔樹勝','桑飛西','上官公希轟','劉之餘人童懷道','周雲鶴','天','鳳','西靈素','大智虎師','阮徒忠','王兆能','袁錚衣商寶鶴','常伯鳳','苗人大','倪不鳳','蔡鐵','無伯志','鳳一弼','曹鵲','黃賓','曾鐵文','姬胡峰','李何豹','上官鐵','童靈同','古若之','慕官景嶽','崔百真','陳官','陳鍾','倪調峰','妹沅刀','徐雙英','任通督','上官鐵褚容','大劍太','胡陽','生','南仁鄭','南調','石雙震','海鐵山','殷鶴真','司魚督','德小','若四','武通濤','田青農','常塵英','常不志','倪不濤','歐陽','大提督','胡玉堂','陳寶鶴','南仁通四蔣赫侯'}
{'邀三','熊貓開','鷹星','陸開','花','薛玉羅平','南宮主','南宮九','孫夫人','荊董滅','鐵不愁','裴獨','瑋劍','人','陸小龍王紫無牙','連千裡','仲先生','俞白','方大','葉雷一魂','獨孤上紅','葉憐花','雷大歸','恕飛','白雙發','邀一郎','東樓','鐵中十一點紅','鳳星真','無魏柳老鳳三','蕭貓兒','東郭先鳳','日孫','地先生','孟摘星','江小小鳳','花雙樓','李佩','仇珏','白壞剎','燕悲情','姬悲雁','東郭大','謝曉陸鳳','碧玉伯','司實三','陸浪','趙布雁','荊孤藍','憐燕南天','蕭憐靜','龍布雁','東郭魚','司東郭金天','薛嘯天','熊寶玉','無莫靜','柳羅李','東官小魚','漸飛','陸地魚','阿吹王','高傲','蕭十三','龍童','玉羅趙','謝郎唐傲','鐵夜帝','江小鳳','孫玉玉夜','仇仲忍','蕭地孫','鐵莫棠','柴星夫','展夫人','碧玉','老無魚','鐵鐵花','獨','薛月宮九','老郭和尚','東郭大路陸上龍關飛','司藏','李千','孫白人','南雙平','王瑋','姬原情','東郭大路孫玉','白玉羅生','高兒','東珏天','蕭王尚','九','鳳三靜','和空摘星','關吹雪','上官官小鳳','仇上官金飛','陸上龍嘯天','司空星魂','邀衣人','主','李尋歡天','東情','玉夫隨','趙小鳳','東郭滅','邀祟厚','司空星'}
感興趣的還可以用古代詩人、詞人等的名字來做訓練,大家機器好或者有時間的可以多訓練下,訓練得越多越準確。
總結
RNN由於具有記憶功能,在NLP、Speech、ComputerVision等諸多領域都展示了強大的力量。實際上,RNN是圖靈等價的。
1Iftrainingvanillaneuralnetsisoptimizationoverfunctions,trainingrecurrentnetsisoptimizationoverprograms.
LSTM是一種目前相當常用和實用的RNN算法,主要解決了RNN的long-termdependencies問題。另外RNN也一直在產生新的研究,比如Attention機制。有空再介紹咯。。。
Refers
http://colah.github.io/posts/2015-08-Understanding-LSTMs/
http://karpathy.github.io/2015/05/21/rnn-effectiveness/
https://www.zhihu.com/question/29411132
https://gist.github.com/karpathy/d4dee566867f8291f086
雷鋒網雷鋒網https://deeplearning4j.org/lstm.html