當前位置:
首頁 > 知識 > 用金庸、古龍群俠名稱訓練 LSTM,會生成多麼奇葩的名字?

用金庸、古龍群俠名稱訓練 LSTM,會生成多麼奇葩的名字?

用金庸、古龍群俠名稱訓練 LSTM,會生成多麼奇葩的名字?



Magicly:之前翻譯了一篇介紹RNN的文章,一直沒看到作者寫新的介紹LSTM的blog,於是我又找了其他資料學習。本文先介紹一下LSTM,然後用LSTM在金庸、古龍的人名上做了訓練,可以生成新的武俠名字,如果有興趣的,還可以多搜集點人名,用於給小孩兒取名呢,哈哈,justforfun,大家玩得開心…


RNN回顧


RNN的出現是為了解決狀態記憶的問題,解決方法很簡單,每一個時間點t的隱藏狀態h(t)不再簡單地依賴於數據,還依賴於前一個時間節點t-1的隱藏狀態h(t-1)。可以看出這是一種遞歸定義(所以循環神經網路又叫遞歸神經網路Recursive Neural Network),h(t-1)又依賴於h(t-2),h(t-2)依賴於h(t-3)…所以h(t)依賴於之前每一個時間點的輸入,也就是說h(t)記住了之前所有的輸入。

用金庸、古龍群俠名稱訓練 LSTM,會生成多麼奇葩的名字?



上圖如果按時間展開,就可以看出RNN其實也就是普通神經網路在時間上的堆疊。

用金庸、古龍群俠名稱訓練 LSTM,會生成多麼奇葩的名字?



RNN問題:Long-Term Dependencies


一切似乎很完美,但是如果h(t)依賴於h(t - 1000),依賴路徑特別長,會導致計算梯度的時候出現梯度消失的問題,訓練時間很長根本沒法實際使用。下面是一個依賴路徑很長的例子:


1 我老家【成都】的。。。【此處省去500字】。。。我們那裡經常吃【火鍋】。。。


LSTM


Long Short Term Memory神經網路,也就是LSTM,由 Hochreiter & Schmidhuber於1997年發表。它的出現就是為了解決Long-Term Dependencies的問題,很來出現了很多改進版本,目前應用在相當多的領域(包括機器翻譯、對話機器人、語音識別、Image Caption等)。

標準的RNN里,重複的模塊里只是一個很簡單的結構,如下圖:

用金庸、古龍群俠名稱訓練 LSTM,會生成多麼奇葩的名字?



LSTM也是類似的鏈表結構,不過它的內部構造要複雜得多:

用金庸、古龍群俠名稱訓練 LSTM,會生成多麼奇葩的名字?



上圖中的圖標含義如下:


LSTM的核心思想是cell state(類似於hidden state,有LSTM變種把cell state和hidden state合并了, 比如GRU)和三種門:輸入門、忘記門、輸出門。


cell state每次作為輸入傳遞到下一個時間點,經過一些線性變化後繼續傳往再下一個時間點(我還沒看過原始論文,不知道為啥有了hidden state後還要cell state,好在確實有改良版將兩者合并了,所以暫時不去深究了)。

用金庸、古龍群俠名稱訓練 LSTM,會生成多麼奇葩的名字?


門的概念來自於電路設計(我沒學過,就不敢賣弄了)。LSTM里,門控制通過門之後信息能留下多少。如下圖,sigmoid層輸出[0, 1]的值,決定多少數據可以穿過門, 0表示誰都過不了,1表示全部通過。

用金庸、古龍群俠名稱訓練 LSTM,會生成多麼奇葩的名字?



下面我們來看看每個「門」到底在幹什麼。


首先我們要決定之前的cell state需要保留多少。 它根據h(t-1)和x(t)計算出一個[0, 1]的數,決定cell state保留多少,0表示全部丟棄,1表示全部保留。為什麼要丟棄呢,不是保留得越多越好么?假設LSTM在生成文章,裡面有小明和小紅,小明在看電視,小紅在廚房做飯。如果當前的主語是小明, ok,那LSTM應該輸出看電視相關的,比如找遙控器啊, 換台啊,如果主語已經切換到小紅了, 那麼接下來最好暫時把電視機忘掉,而輸出洗菜、醬油、電飯煲等。

用金庸、古龍群俠名稱訓練 LSTM,會生成多麼奇葩的名字?



第二步就是決定輸入多大程度上影響cell state。這個地方由兩部分構成, 一個用sigmoid函數計算出有多少數據留下,一個用tanh函數計算出一個候選C(t)。 這個地方就好比是主語從小明切換到小紅了, 電視機就應該切換到廚房。

用金庸、古龍群俠名稱訓練 LSTM,會生成多麼奇葩的名字?


然後我們把留下來的(t-1時刻的)cell state和新增加的合并起來,就得到了t時刻的cell state。

用金庸、古龍群俠名稱訓練 LSTM,會生成多麼奇葩的名字?



最後我們把cell state經過tanh壓縮到[-1, 1],然後輸送給輸出門([0, 1]決定輸出多少東西)。

用金庸、古龍群俠名稱訓練 LSTM,會生成多麼奇葩的名字?



現在也出了很多LSTM的變種, 有興趣的可以看這裡。另外,LSTM只是為了解決RNN的long-term dependencies,也有人從另外的角度來解決的,比如Clockwork RNNs by Koutnik, et al. (2014).


show me the code!


我用的Andrej Karpathy大神的代碼, 做了些小改動。這個代碼的好處是不依賴於任何深度學習框架,只需要有numpy就可以馬上run起來!

"""


Minimal character-level Vanilla RNN model. Written by Andrej Karpathy (@karpathy)


BSD License


"""


import numpy as np


# data I/O


data = open( input.txt , r ).read() # should be simple plain text file


all_names = set(data.split("
"))


chars = list(set(data))


data_size, vocab_size = len(data), len(chars)

print( data has %d characters, %d unique. % (data_size, vocab_size))


char_to_ix =


ix_to_char =


# print(char_to_ix, ix_to_char)


# hyperparameters


hidden_size = 100 # size of hidden layer of neurons


seq_length = 25 # number of steps to unroll the RNN for


learning_rate = 1e-1


# model parameters


Wxh = np.random.randn(hidden_size, vocab_size) * 0.01 # input to hidden

Whh = np.random.randn(hidden_size, hidden_size) * 0.01 # hidden to hidden


Why = np.random.randn(vocab_size, hidden_size) * 0.01 # hidden to output


bh = np.zeros((hidden_size, 1)) # hidden bias


by = np.zeros((vocab_size, 1)) # output bias


def lossFun(inputs, targets, hprev):


"""


inputs,targets are both list of integers.


hprev is Hx1 array of initial hidden state


returns the loss, gradients on model parameters, and last hidden state


"""

xs, hs, ys, ps = {}, {}, {}, {}


hs[-1] = np.copy(hprev)


loss = 0


# forward pass


for t in range(len(inputs)):


xs[t] = np.zeros((vocab_size, 1)) # encode in 1-of-k representation


xs[t][inputs[t]] = 1


hs[t] = np.tanh(np.dot(Wxh, xs[t]) + np.dot(Whh,


hs[t - 1]) + bh) # hidden state


# unnormalized log probabilities for next chars

ys[t] = np.dot(Why, hs[t]) + by


# probabilities for next chars


ps[t] = np.exp(ys[t]) / np.sum(np.exp(ys[t]))


loss += -np.log(ps[t][targets[t], 0]) # softmax (cross-entropy loss)


# backward pass: compute gradients going backwards


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])


for t in reversed(range(len(inputs))):


dy = np.copy(ps[t])


# backprop into y. see


# http://cs231n.github.io/neural-networks-case-study/#grad if confused


# here


dy[targets[t]] -= 1


dWhy += np.dot(dy, hs[t].T)


dby += dy


dh = np.dot(Why.T, dy) + dhnext # backprop into h


dhraw = (1 - hs[t] * hs[t]) * dh # backprop through tanh nonlinearity


dbh += dhraw


dWxh += np.dot(dhraw, xs[t].T)


dWhh += np.dot(dhraw, hs[t - 1].T)


dhnext = np.dot(Whh.T, dhraw)


for dparam in [dWxh, dWhh, dWhy, dbh, dby]:


# clip to mitigate exploding gradients


np.clip(dparam, -5, 5, out=dparam)


return loss, dWxh, dWhh, dWhy, dbh, dby, hs[len(inputs) - 1]


def sample(h, seed_ix, n):


"""


sample a sequence of integers from the model


h is memory state, seed_ix is seed letter for first time step


"""


x = np.zeros((vocab_size, 1))


x[seed_ix] = 1


ixes = []


for t in range(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 = np.zeros((vocab_size, 1))


x[ix] = 1


ixes.append(ix)


return ixes


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) # memory variables for Adagrad


smooth_loss = -np.log(1.0 / vocab_size) * seq_length # loss at iteration 0


while True:


# prepare inputs (we re sweeping from left to right in steps seq_length


# long)


if p + seq_length + 1 >= len(data) or n == 0:


hprev = np.zeros((hidden_size, 1)) # reset RNN memory


p = 0 # go from start of data


inputs = [char_to_ix[ch] for ch in data[p:p + seq_length]]


targets = [char_to_ix[ch] for ch in data[p + 1:p + seq_length + 1]]


# sample from the model now and then


if n % 100 == 0:


sample_ix = sample(hprev, inputs[0], 200)


txt = .join(ix_to_char[ix] for ix in sample_ix)


print( ----
%s
---- % (txt, ))


# forward seq_length characters through the net and fetch gradient


loss, dWxh, dWhh, dWhy, dbh, dby, hprev = lossFun(inputs, targets, hprev)


smooth_loss = smooth_loss * 0.999 + loss * 0.001


if n % 100 == 0:


print( iter %d, loss: %f % (n, smooth_loss)) # print progress


# perform parameter update with Adagrad


for param, dparam, mem in zip([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) # adagrad update


p += seq_length # move data pointer


n += 1 # iteration counter


if ((smooth_loss < 10) or (n >= 20000)):


sample_ix = sample(hprev, inputs[0], 2000)


txt = .join(ix_to_char[ix] for ix in sample_ix)


predicted_names = set(txt.split("
"))


new_names = predicted_names - all_names


print(new_names)


print( predicted names len: %d, new_names len: %d.
% (len(predicted_names), len(new_names)))


break


viewrawmin-char-rnn.pyhosted with byGitHub


然後從網上找了金庸小說的人名,做了些預處理,每行一個名字,保存到input.txt里,運行代碼就可以了。古龍的沒有找到比較全的名字, 只有這份武功排行榜,只有100多人。


下面是根據兩份名單訓練的結果,已經將完全一致的名字(比如段譽)去除了,所以下面的都是LSTM「新創作發明」的名字哈。來, 大家猜猜哪一個結果是金庸的, 哪一個是古龍的呢?


{ 姜曾鐵 , 袁南蘭", 石萬奉 , 郭萬嗔 , 蔡家 , 程伯芷", 汪鐵志 , 陳衣", 薛鐵 , 哈赤蔡師 , 殷飛虹", 鍾小硯 , 鳳一刀 , 寶蘭", 齊飛虹", 無若之 , 王老英", 鍾 , 鍾百勝 , 師 , 李沅震 , 曹蘭", 趙一刀 , 鍾靈四 , 宗家妹 , 崔樹勝 , 桑飛西", 上官公希轟", 劉之餘人童懷道 , 周雲鶴", 天", 鳳", 西靈素 , 大智虎師 , 阮徒忠 , 王兆能", 袁錚衣商寶鶴", 常伯鳳", 苗人大 , 倪不鳳", 蔡鐵 , 無伯志 , 鳳一弼", 曹鵲 , 黃賓", 曾鐵文 , 姬胡峰 , 李何豹", 上官鐵 , 童靈同 , 古若之 , 慕官景岳 , 崔百真 , 陳官 , 陳鍾 , 倪調峰", 妹沅刀 , 徐雙英", 任通督", 上官鐵褚容", 大劍太", 胡陽", 生 , 南仁鄭 , 南調 , 石雙震 , 海鐵山", 殷鶴真 , 司魚督", 德小 , 若四 , 武通濤 , 田青農 , 常塵英", 常不志 , 倪不濤 , 歐陽", 大提督", 胡玉堂 , 陳寶鶴", 南仁通四蔣赫侯 }


{ 邀三 , 熊貓開 , 鷹星 , 陸開 , 花", 薛玉羅平", 南宮主 , 南宮九 , 孫夫人 , 荊董滅", 鐵不愁 , 裴獨", 瑋劍 , 人", 陸小龍王紫無牙 , 連千里 , 仲先生 , 俞白", 方大 , 葉雷一魂 , 獨孤上紅", 葉憐花", 雷大歸 , 恕飛 , 白雙發", 邀一郎", 東樓", 鐵中十一點紅 , 鳳星真 , 無魏柳老鳳三 , 蕭貓兒", 東郭先鳳", 日孫 , 地先生 , 孟摘星 , 江小小鳳", 花雙樓 , 李佩 , 仇珏", 白壞剎", 燕悲情 , 姬悲雁 , 東郭大 , 謝曉陸鳳", 碧玉伯", 司實三 , 陸浪", 趙布雁 , 荊孤藍 , 憐燕南天", 蕭憐靜 , 龍布雁 , 東郭魚 , 司東郭金天", 薛嘯天 , 熊寶玉 , 無莫靜 , 柳羅李", 東官小魚", 漸飛 , 陸地魚 , 阿吹王 , 高傲", 蕭十三 , 龍童", 玉羅趙", 謝郎唐傲", 鐵夜帝 , 江小鳳", 孫玉玉夜 , 仇仲忍 , 蕭地孫 , 鐵莫棠 , 柴星夫", 展夫人 , 碧玉 , 老無魚", 鐵鐵花", 獨", 薛月宮九 , 老郭和尚 , 東郭大路陸上龍關飛 , 司藏", 李千 , 孫白人 , 南雙平", 王瑋", 姬原情 , 東郭大路孫玉 , 白玉羅生 , 高兒", 東珏天", 蕭王尚 , 九 , 鳳三靜 , 和空摘星 , 關吹雪", 上官官小鳳", 仇上官金飛 , 陸上龍嘯天 , 司空星魂 , 邀衣人 , 主", 李尋歡天 , 東情 , 玉夫隨", 趙小鳳", 東郭滅", 邀祟厚 , 司空星 }


感興趣的還可以用古代詩人、詞人等的名字來做訓練,大家機器好或者有時間的可以多訓練下,訓練得越多越準確。


總結


RNN由於具有記憶功能,在NLP、Speech、Computer Vision等諸多領域都展示了強大的力量。實際上,RNN是圖靈等價的。


1 If training vanilla neural nets is optimization over functions, training recurrent nets is optimization over programs.


LSTM是一種目前相當常用和實用的RNN演算法,主要解決了RNN的long-term dependencies問題。另外RNN也一直在產生新的研究,比如Attention機制。有空再介紹咯。。。


Refers


http://colah.github.io/posts/2015-08-Understanding-LSTMs/


http://karpathy.github.io/2015/05/21/rnn-effectiveness/


https://gist.github.com/karpathy/d4dee566867f8291f086


https://deeplearning4j.org/lstm.html


喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 唯物 的精彩文章:

TensorFlow在工程項目中的應用 視頻+文字轉錄(下)
TensorFlow在工程項目中的應用 公開課視頻+文字轉錄(上)
賈揚清 F8 現場演講:從手機到大型伺服器集群,Caffe2 如何適用於不同設備和場景?
人臉識別+手機推送,從此不再害怕老闆背後偷襲!

TAG:唯物 |

您可能感興趣

RNN-生成古詩
使用 GAN 網路生成名人照片
TFBOYS和偶像練習生同齡,都是十八歲,為什麼偶像練習生成熟很多?
LSGAN:最小二乘生成對抗網路
Chatito-使用簡單的DSL為AI聊天機器人、NLP任務、命名實體識別或文本分類模型生成數據集
EXO團魂最中心,Sohu隊長7年練習生成就EXO的靈魂
五五開穢土轉生成功?盧本偉成功報名CSGO的DH大師賽!
ACM MM最佳論文全文:通過多對抗訓練,從圖像生成詩歌
結合通用和專用NMT的優勢,CMU為NMT引入「語境參數生成器」
遊民App4.8更新!生成你的PSN名片 誰是白金大神?
評價端到端生成式聊天系統,哈工大提出新型數據集 LSDSCC
英偉達MIT黑科技:用AI生成模型快速構建虛擬世界
TreeGAN:為序列生成任務構建有句法意識的GAN
學界 | TreeGAN:為序列生成任務構建有句法意識的GAN
文本挖掘:LDA文檔主題生成模型
見過火系的暴鯉龍嗎?這個項目利用CycleGAN生成不同屬性神奇寶貝
php 生成 RSS 類
用英偉達StyleGAN生成老婆吧,他生成了一百多隻明日香
谷歌AutoML創造者Quoc Le:未來最好的人工智慧模型將由機器生成
UnifyID的研究人員使用神經網路生成風格化圖像擾亂API識別