基于Tensorflow 1.4自己寫一個LSTM Language Model

LSTM公式

定義需要用到Variable

先來個初始化用的對象,玄學(xué)初始化:

initializer = tf.contrib.layers.xavier_initializer()
全局變量:seq_length, embedding_size, rnn_size

定義那些矩陣

##tensorflow中的實現(xiàn)好像是把x_t和h_t-1拼起來了峰鄙,這里簡單點浸间,分開算
Wi=tf.get_variable('Wi', shape=(embedding_size, rnn_size), initializer=initializer)
Ui=tf.get_variable('Ui', shape=(rnn_size, rnn_size), initializer=initializer)

Wf=tf.get_variable('Wf', shape=(embedding_size, rnn_size), initializer=initializer)
Uf=tf.get_variable('Uf', shape=(rnn_size, rnn_size), initializer=initializer)

Wo=tf.get_variable('Wo', shape=(embedding_size, rnn_size), initializer=initializer)
Uo=tf.get_variable('Uo', shape=(rnn_size, rnn_size), initializer=initializer)

Wc=tf.get_variable('Wc', shape=(embedding_size, rnn_size), initializer=initializer)
Uc=tf.get_variable('Uc', shape=(rnn_size, rnn_size), initializer=initializer)
# 如果要做weight normalization可以接著寫.....

LSTM Cell

def lstm_cell(x, h, c):
    it = tf.sigmoid(tf.matmul(x, Wi) + tf.matmul(h, Ui))
    ft = tf.sigmoid(tf.matmul(x, Wf) + tf.matmul(h, Uf))
    ot = tf.sigmoid(tf.matmul(x, Wo) + tf.matmul(h, Uo))
    ct = tf.tanh(tf.matmul(x, Wc) + tf.matmul(h, Wc))

    c_new = (ft * c) + (it * ct)
    h_new = ot * tf.tanh(c_new)

    return c_new, h_new

展開LSTM

在tensorflow中這個過程是用tf.nn.static_rnntf.nn.dynamic_rnn實現(xiàn),實際上寫個循環(huán)就行了吟榴。(ps: tf.nn.dynamic_rnn是用tf.while實現(xiàn)的魁蒜,不同batch可以有不同的seq_length,而tf.nn.static_rnn的time_step數(shù)量定義好了就不能改了)

def transform(x):
    # 處理一下輸入數(shù)據(jù)煤墙,rnn的batch和cnn有些不同
    embedding_outputs = embedding(x) # embedding函數(shù)梅惯,需自己定義
    shape = tf.shape(embedding_outputs)
    embedding_inputs = tf.nn.dropout(embedding_outputs, 0.5,
                                     noise_shape=[1, shape[1], shape[2]])
    # (batch_size, seq_length, embeding_size)
    inputs_split = tf.split(embedding_inputs, seq_length, axis=1)
    # it's a list: seq_length x (batch_size, embedding_size)
    list_inputs = [tf.squeeze(input_, [1]) for input_ in inputs_split]
    return list_inputs


def unroll_lstm(lstm_cell, x, length):
    # length是序列的真實長度
    # x.shape = (batch_size, seq_length), 這個seq_length是padding后的
    batch_size = tf.shape(x)[0]
    # 對x做embedding
    input_list = transform(x)
    outputs = []
    # unrolled lstm loop
    # 定義output & state來接輸出結(jié)果
    output = tf.tile(tf.expand_dims(tf.Variable(tf.zeros(cell_size),
                     trainable=False), 0), [batch_size, 1])
    state = tf.tile(tf.expand_dims(tf.Variable(tf.zeros(cell_size),
                     trainable=False), 0), [batch_size, 1])
    for ipt in input_list:
        state, output = lstm_cell(ipt, output, state)
        outputs.append(output)
    # 使用mask來截掉大于序列真實長度的部分(置為0)
    mask = tf.sequence_mask(length, seq_length)
    out_tensor = tf.stack(outputs, axis=1)
    outputs = tf.where(tf.stack([mask] * cell_size, axis=-1), out_tensor,
                       tf.zeros_like(out_tensor))
    return outputs, state

輸出的截取

前面lstm輸出的結(jié)果為(batch_size, seq_length, rnn_size),batch中某些句子的長度可能比seq_length要短仿野,這時需要使用tf.gather_nd函數(shù)去截取真實長度的輸出铣减。

# 計算真實輸出部分的indices
# 這里我添加了一個記錄batch中句子長度的placehoder: ph_length, shape: (batch_size, )
output_indices = tf.stack([tf.range(tf.shape(ph_length)[0]),
                          ph_length - 1], 1)
# (batch_size, rnn_size)
lstm_out_with_len = tf.gather_nd(lstm_outs, output_indices)

關(guān)于Language Model的Loss

基于LSTM的Language Model就是對于句子(x_1,x_2,\dots,x_n)(其中x_i是句子的分詞結(jié)果),使用(x_1,\dots,x_i)去預(yù)測第i+1個詞x_{i+1}是什么脚作。如果是一整片文章葫哗,沒有加Padding和句子末尾標(biāo)記<EOS>,那這個工作還是比較簡單的球涛;若加上Padding劣针,在計算loss的時候需要對輸入和輸出做一些處理,Padding部分需要截取掉亿扁。
Loss參考代碼:https://github.com/sherjilozair/char-rnn-tensorflow/blob/master/model.py捺典,其中l(wèi)oss函數(shù)用了sequence_loss_by_example有點迷,感覺用cross_entropy就夠了从祝,看了下API:https://github.com/tensorflow/tensorflow/blob/r1.13/tensorflow/contrib/legacy_seq2seq/python/ops/seq2seq.py襟己。sequence_loss_by_example計算了batch_size x sequece_lengthsparse_softmax_cross_entropy_with_logits,最后放在了一個list里面牍陌。直接寫的話擎浴,這樣子:

# 輸入: 
#   outputs: LSTM每個timestep的輸出,shape = (batch_size, sequence_len, lstm_cell_size)
#   length: 這個batch_size中每個句子的實際長度毒涧,shape = (batch_size, )
#   max_seq_len: 最大句子長度
#   (optional) embed_mat: embedding使用的Lookup Table矩陣 (vocabulary_size, lstm_cell_size)


# mask tensor representing the first N positions of each cell
mask = tf.sequence_mask(length, max_seq_len)
# 提取非Padding位置的LSTM輸出
output = tf.boolean_mask(outputs, mask) # (?, lstm_cell_size)

# 構(gòu)造預(yù)測的target部分贮预,例如 “落 霞 與 孤 鶩 齊 飛”其對應(yīng)的target為
# "霞 與 孤 鶩 齊 飛 <EOS>" → [20, 11, 38, 79, 3, 7, 0] (假設(shè)"<EOS>"的id表示為0)
# 這個工具最好預(yù)處理的時候做,tensorflow的tensor不支持assignment操作,不好實現(xiàn)仿吞。滑频。
# input_y: 這個batch句子處理后的id化表示 shape = (batch_size, max_seq_len)
target = tf.boolean_mask(input_y, mask)

decoder_matrix = tf.get_variable(shape=[lstm_cell_size, vocabulary_size], initializer=
                                 tf.random_uniform_initializer(-1., 1.))
logits = tf.matmul(output, decoder_matrix)
# 如果想要節(jié)約內(nèi)存,減少一些參數(shù)茫藏,可以復(fù)用embedding matrix
logits = tf.matmul(output, tf.transpose(embed_mat))

loss = tf.reduce_mean(tf.nn.sparse_softmax_cross_entropy_with_logits(logits=logits, labels=target))

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末误趴,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子务傲,更是在濱河造成了極大的恐慌,老刑警劉巖枣申,帶你破解...
    沈念sama閱讀 218,451評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件售葡,死亡現(xiàn)場離奇詭異,居然都是意外死亡忠藤,警方通過查閱死者的電腦和手機挟伙,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,172評論 3 394
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來模孩,“玉大人尖阔,你說我怎么就攤上這事≌ジ溃” “怎么了介却?”我有些...
    開封第一講書人閱讀 164,782評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長块茁。 經(jīng)常有香客問我齿坷,道長,這世上最難降的妖魔是什么数焊? 我笑而不...
    開封第一講書人閱讀 58,709評論 1 294
  • 正文 為了忘掉前任永淌,我火速辦了婚禮,結(jié)果婚禮上佩耳,老公的妹妹穿的比我還像新娘遂蛀。我一直安慰自己,他們只是感情好干厚,可當(dāng)我...
    茶點故事閱讀 67,733評論 6 392
  • 文/花漫 我一把揭開白布李滴。 她就那樣靜靜地躺著,像睡著了一般萍诱。 火紅的嫁衣襯著肌膚如雪悬嗓。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,578評論 1 305
  • 那天裕坊,我揣著相機與錄音包竹,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛周瞎,可吹牛的內(nèi)容都是我干的苗缩。 我是一名探鬼主播,決...
    沈念sama閱讀 40,320評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼声诸,長吁一口氣:“原來是場噩夢啊……” “哼酱讶!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起彼乌,我...
    開封第一講書人閱讀 39,241評論 0 276
  • 序言:老撾萬榮一對情侶失蹤泻肯,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后慰照,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體灶挟,經(jīng)...
    沈念sama閱讀 45,686評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,878評論 3 336
  • 正文 我和宋清朗相戀三年毒租,在試婚紗的時候發(fā)現(xiàn)自己被綠了稚铣。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,992評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡墅垮,死狀恐怖惕医,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情算色,我是刑警寧澤抬伺,帶...
    沈念sama閱讀 35,715評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站剃允,受9級特大地震影響沛简,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜斥废,卻給世界環(huán)境...
    茶點故事閱讀 41,336評論 3 330
  • 文/蒙蒙 一椒楣、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧牡肉,春花似錦捧灰、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,912評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至饲窿,卻和暖如春煌寇,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背逾雄。 一陣腳步聲響...
    開封第一講書人閱讀 33,040評論 1 270
  • 我被黑心中介騙來泰國打工阀溶, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留腻脏,地道東北人。 一個月前我還...
    沈念sama閱讀 48,173評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親圃酵。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,947評論 2 355

推薦閱讀更多精彩內(nèi)容