丁香五月天婷婷久久婷婷色综合91|国产传媒自偷自拍|久久影院亚洲精品|国产欧美VA天堂国产美女自慰视屏|免费黄色av网站|婷婷丁香五月激情四射|日韩AV一区二区中文字幕在线观看|亚洲欧美日本性爱|日日噜噜噜夜夜噜噜噜|中文Av日韩一区二区

您正在使用IE低版瀏覽器,為了您的雷峰網(wǎng)賬號安全和更好的產(chǎn)品體驗(yàn),強(qiáng)烈建議使用更快更安全的瀏覽器
此為臨時(shí)鏈接,僅用于文章預(yù)覽,將在時(shí)失效
人工智能開發(fā)者 正文
發(fā)私信給AI研習(xí)社
發(fā)送

1

致敬趙雷:基于TensorFlow讓機(jī)器生成趙雷曲風(fēng)的歌詞

本文作者: AI研習(xí)社 2017-02-17 17:17
導(dǎo)語:用模型算法去詮釋對趙雷的熱愛,用實(shí)際行動(dòng)表達(dá)其他文章表達(dá)不了的情懷!

致敬趙雷:基于TensorFlow讓機(jī)器生成趙雷曲風(fēng)的歌詞

寫在技術(shù)算法前面的話:

我們基本上收集了趙雷所有唱過的歌曲的歌詞。

致敬趙雷:基于TensorFlow讓機(jī)器生成趙雷曲風(fēng)的歌詞

【無法長大】共收錄了10支單曲:

《朵》、《八十年代的歌》、《無法長大》、《瑪麗》、《阿刁》、《鼓樓》、《孤獨(dú)》、《成都》、《窯上路》、《再見北京》

致敬趙雷:基于TensorFlow讓機(jī)器生成趙雷曲風(fēng)的歌詞

【吉姆餐廳】共收錄了10支單曲:

《吉姆餐廳》、《少年錦時(shí)》、《夢中的哈德森》、《我們的時(shí)光》、《理想》、《三十歲的女人》、《家鄉(xiāng)》、《浮游》、《小屋》、《北京的冬天》

致敬趙雷:基于TensorFlow讓機(jī)器生成趙雷曲風(fēng)的歌詞

【趙小雷】共收錄了12支單曲:

《人家》、《未給姐姐遞出的信》、《畫》、《不開的唇》、《趙小雷》、《南方姑娘》、《Over》、《開往北京的火車》、《背影》、《媽媽》、《南方姑娘(彈唱版)》、《民謠》

致敬趙雷:基于TensorFlow讓機(jī)器生成趙雷曲風(fēng)的歌詞

【其他單曲】共22支單曲:

《19歲時(shí)候的歌》、《已是兩條路上的人》、《再也不會(huì)去麗江》、《讓我偷偷看你》、《咬春》、《難受》、《辭行》、《保存》、《雪人》、《青春無處安放》、《憑什么說愛你》、《過年》、《何必》、《2012年之前》、《別》、《愛人你在哪里》、《夏天》、《飛來飛去》、《逆流而上》、《花朵》、《罪》、《不能自主》

共計(jì)53首單曲,1560行歌詞。

1.原理回顧

機(jī)器作詞是序列建模(以下簡稱seq2seq)的典型應(yīng)用,其基本思想就是給定序列A,機(jī)器負(fù)責(zé)產(chǎn)生序列B,并且再將序列B作為輸入,機(jī)器負(fù)責(zé)生成序列C...如此循環(huán)下去即可生成無限長度的序列。seq2seq模型圖如下所示,左邊是編碼器,右邊是解碼器。

致敬趙雷:基于TensorFlow讓機(jī)器生成趙雷曲風(fēng)的歌詞

假設(shè)問題是從序列A到序列B之間的映射,那么seq2seq模型的工作流程如下:

  • 序列A中的每一個(gè)單詞通過word_embedding操作以后,作為input進(jìn)入編碼器,編碼器可以是一個(gè)多層RNN結(jié)構(gòu),編碼器輸出一個(gè)向量;

  • 訓(xùn)練的時(shí)候,解碼器的輸入跟編碼器的輸入是一樣的,然后解碼器的輸出與序列B之間的交叉熵作為模型的目標(biāo)函數(shù);

  • 生成的時(shí)候,首先給定一個(gè)種子序列作為編碼器的輸入,并且解碼器的上一時(shí)刻的輸出作為下一時(shí)刻的輸入,如此循環(huán)往復(fù),直到生成給定數(shù)量的序列。

本文建立的模型就是基于以上原理。

2.模型代碼設(shè)計(jì)

要完成機(jī)器生成歌詞的工作看上去是一個(gè)生成模型,而生成模型一般都是無監(jiān)督問題,但是我們需要將它轉(zhuǎn)化成有監(jiān)督問題,原因是使用有監(jiān)督學(xué)習(xí)可以發(fā)現(xiàn)數(shù)據(jù)內(nèi)在的關(guān)聯(lián)性,比如上下文的銜接,然后用預(yù)測學(xué)習(xí)來代替無監(jiān)督學(xué)習(xí)。

就有監(jiān)督學(xué)習(xí)而言,通常我們需要準(zhǔn)備好具有映射關(guān)系的數(shù)據(jù)集:X和Y。這里我們事先只有周杰倫的歌詞文本,它是一個(gè)整體,如何確定X和Y?雖然它是一個(gè)整體,但是這個(gè)整體是序列組成的,序列與序列之間會(huì)有一定的時(shí)序關(guān)系。比如對于

讓我掉下眼淚的 不止昨夜的酒

我們是不是可以把“讓我掉下眼淚的 ”看作X,把“ 不止昨夜的酒”看作Y,如果我們將X輸入進(jìn)網(wǎng)絡(luò),而網(wǎng)絡(luò)輸出的是Y,那就說明我們構(gòu)建的網(wǎng)絡(luò)已經(jīng)具備寫歌詞的能力了。這就是我們劃分?jǐn)?shù)據(jù)集為X和Y的原理。一般情況下,數(shù)據(jù)需要?jiǎng)澐譃橛?xùn)練集和測試集,由于時(shí)間的緣故,這里沒有劃分測試集了。

當(dāng)我們把數(shù)據(jù)預(yù)處理做好了,接下來就是構(gòu)建模型了,構(gòu)建模型主要是圍繞seq2seq模型,而在編碼器和解碼器部分,我們可以自由構(gòu)造,如可以選擇不同的rnn_cell,或者選擇不同的層數(shù)、神經(jīng)元個(gè)數(shù),具體情況因數(shù)據(jù)量大小而定。構(gòu)建有監(jiān)督學(xué)習(xí)模型的最重要部分就是目標(biāo)函數(shù),并且要確保目標(biāo)函數(shù)對于所有要訓(xùn)練的參數(shù)是可微的,這樣我們就可以構(gòu)建端對端的基于后向誤差更新的深度學(xué)習(xí)系統(tǒng)。

當(dāng)有監(jiān)督學(xué)習(xí)訓(xùn)練的模型的誤差已經(jīng)滿足我們的要求了,就可以把參數(shù)保存下來,以便利用這個(gè)模型去生成歌詞。生成模型的構(gòu)建其實(shí)就是一個(gè)抽樣的過程,給定種子序列,選好特定的抽樣方法,即可生成無限多個(gè)漢字組成的序列。

為了了解訓(xùn)練過程中的誤差更新趨勢,我們還需要建立日志記錄以及日志可視化的部分,這樣以便于我們做后期的模型性能分析,本文中會(huì)粗略提及。

本項(xiàng)目的文件結(jié)構(gòu)如下圖所示:

致敬趙雷:基于TensorFlow讓機(jī)器生成趙雷曲風(fēng)的歌詞

它們的功能分別如下:

  • 主目錄下面的utils.py是公共函數(shù)庫,preprocess.py是數(shù)據(jù)預(yù)處理代碼,seq2seq_rnn.py是模型代碼,sample.py是抽樣生成過程,train.py是訓(xùn)練過程;

  • log目錄中存儲(chǔ)的是訓(xùn)練過程中的日志文件;

  • save目錄中存儲(chǔ)的是訓(xùn)練過程中的模型存儲(chǔ)文件;

  • data目錄中存放的是原始歌詞數(shù)據(jù)庫以及處理過的數(shù)據(jù)庫;

  • result目錄中存放的是生成的序列;

  • analysis目錄中存放的是用于可視化的代碼文件。

3.數(shù)據(jù)預(yù)處理

原始歌詞文件是網(wǎng)絡(luò)中下載的,其中包括了一些不必要的文本,由此我們過濾了所有的非中文字符,并且使用空格分隔相鄰句子。例如下面一段代碼的作用就是剔除源歌詞文件中的多余空格以及非中文字符:

reg = re.compile(ur"[\s+]")

c = reg.sub(' ',unicode(c))

reg = re.compile(ur"[^\u4e00-\u9fa5\s]")

c = reg.sub('',unicode(c))

c = c.strip()

將源歌詞文件處理成連續(xù)的句子文件以后,下一步就是將這么多句子劃分成很多對訓(xùn)練樣本。首先我們需要統(tǒng)計(jì)歌詞中所有不同漢字的總數(shù)(包括一個(gè)空格),并且對這些漢字進(jìn)行索引,可將原文由漢字變成整型數(shù)組,這樣訓(xùn)練的時(shí)候讀取數(shù)組就可以了;另外,索引還可以用來進(jìn)行word_embedding,即將每個(gè)單詞映射成一個(gè)特征向量。下面一段代碼就是建立詞典以及上下文的過程:

def build_dataset(self):
   ''' parse all sentences to build a vocabulary         

       dictionary and vocabulary list    

  '''
   with codecs.open(self.input_file, "r",encoding='utf-8') as f:
       data = f.read()
   wordCounts = collections.Counter(data)
   self.vocab_list = [x[0] for x in wordCounts.most_common()]
   self.vocab_size = len(self.vocab_list)
   self.vocab_dict = {x: i for i, x in enumerate(self.vocab_list)}    
   with codecs.open(self.vocab_file, 'wb',encoding='utf-8') as f:
       cPickle.dump(self.vocab_list, f)
   self.context = np.array(list(map(self.vocab_dict.get, data)))
   np.save(self.context_file, self.context)

然后確定我們要建立的每對樣本的長度以及訓(xùn)練時(shí)候的batch_size大小,進(jìn)而把數(shù)據(jù)集分成很多個(gè)mini-batch,可以在訓(xùn)練的時(shí)候依次讀取。這里需要注意的是,為了預(yù)處理方便,我們選擇了固定長度作為樣本序列的長度,并且讓X和Y的長度一致,從數(shù)據(jù)集中選取X和Y的時(shí)候每次滑動(dòng)步長為1,間隔也為1,如下代碼所示:

def init_batches(self):
   ''' 

        Split the dataset into mini-batches,

         xdata and ydata should be the same length here

        we add a space before the context to make sense.

    '''

   self.num_batches = int(self.context.size / (self.batch_size * self.seq_length))
   self.context = self.context[:self.num_batches * self.batch_size * self.seq_length]
   xdata = self.context
   ydata = np.copy(self.context)
   ydata[:-1] = xdata[1:]
   ydata[-1] = xdata[0]
   self.x_batches = np.split(xdata.reshape(self.batch_size, -1), self.num_batches, 1)
   self.y_batches = np.split(ydata.reshape(self.batch_size, -1), self.num_batches, 1)
   self.pointer = 0

可以看到Y(jié)的最后一個(gè)數(shù)是設(shè)置為X的第一個(gè)數(shù),因此我們在數(shù)據(jù)集的開頭插入了一個(gè)空格使得整體連貫。pointer是作為標(biāo)記來用的,它的作用是標(biāo)記當(dāng)前訓(xùn)練的是哪一個(gè)mini-batch,如果所有mini-batch都訓(xùn)練過了,即完成了一個(gè)Epoch,那么pointer將置零,如下面代碼所示:

def next_batch(self):
   ''' pointer for outputing mini-batches when training

   '''
   x, y = self.x_batches[self.pointer], self.y_batches[self.pointer]
   self.pointer += 1
   if self.pointer == self.num_batches:
       self.pointer = 0
   return x, y

4.編寫基于LSTM的seq2seq模型

數(shù)據(jù)預(yù)處理完成以后,接下來就是建立seq2seq模型了。建立模型主要分為三步:

  • 確定好編碼器和解碼器中cell的結(jié)構(gòu),即采用什么循環(huán)單元,多少個(gè)神經(jīng)元以及多少個(gè)循環(huán)層;

  • 將輸入數(shù)據(jù)轉(zhuǎn)化成tensorflow的seq2seq.rnn_decoder需要的格式,并得到最終的輸出以及最后一個(gè)隱含狀態(tài);

  • 將輸出數(shù)據(jù)經(jīng)過softmax層得到概率分布,并且得到誤差函數(shù),確定梯度下降優(yōu)化器。

由于tensorflow提供的rnncell共有三種,分別是RNN、GRU、LSTM,因此這里我們也提供三種選擇,并且每一種都可以使用多層結(jié)構(gòu),即MultiRNNCell,如下代碼所示:

if args.rnncell == 'rnn':
   cell_fn = rnn_cell.BasicRNNCellelif args.rnncell == 'gru':
   cell_fn = rnn_cell.GRUCellelif args.rnncell == 'lstm':
   cell_fn = rnn_cell.BasicLSTMCellelse:    
   raise Exception("rnncell type error: {}".format(args.rnncell))

cell = cell_fn(args.rnn_size)

self.cell = rnn_cell.MultiRNNCell([cell] * args.num_layers)

選擇好了cell的結(jié)構(gòu)以后,接下來就是將輸入數(shù)據(jù)傳遞的seq2seq模型中了,tensorflow的seq2seq.py文件中提供了多個(gè)用于建立seq2seq的函數(shù),這里我選擇了兩個(gè),分別是rnn_decoder以及attention_decoder,下面以rnn_decoder為例。從tensorflow源碼中可以看到,rnn_decoder函數(shù)主要有四個(gè)參數(shù),它們的注釋如下:

decoder_inputs: A list of 2D Tensors [batch_size x input_size].
initial_state: 2D Tensor with shape [batch_size x cell.state_size].
cell: rnn_cell.RNNCell defining the cell function and size.
loop_function: If not None, this function will be applied to the i-th output
in order to generate the i+1-st input, and decoder_inputs will be ignored,
except for the first element ("GO" symbol). This can be used for decoding,
but also for training to emulate [1506.03099] Scheduled Sampling for Sequence Prediction with Recurrent Neural Networks.
Signature -- loop_function(prev, i) = next
* prev is a 2D Tensor of shape [batch_size x output_size],
* i is an integer, the step number (when advanced control is needed),
* next is a 2D Tensor of shape [batch_size x input_size].

可以看到,decoder_inputs其實(shí)就是輸入的數(shù)據(jù),要求的格式為一個(gè)list,并且list中的tensor大小應(yīng)該為[batch_size,input_size],換句話說這個(gè)list的長度就是seq_length;但我們原始的輸入數(shù)據(jù)的維度為[args.batch_size, args.seq_length],是不是感覺缺少了一個(gè)input_size維度,其實(shí)這個(gè)維度就是word_embedding的維度,或者說word2vec的大小,這里需要我們手動(dòng)進(jìn)行word_embedding,并且這個(gè)embedding矩陣是一個(gè)可以學(xué)習(xí)的參數(shù):

self.input_data = tf.placeholder(tf.int32, [args.batch_size, args.seq_length])with tf.variable_scope('rnnlm'):

softmax_w = build_weight([args.rnn_size, args.vocab_size],name='soft_w')

softmax_b = build_weight([args.vocab_size],name='soft_b')

word_embedding = build_weight([args.vocab_size, args.embedding_size],name='word_embedding')

inputs_list = tf.split(1, args.seq_length, tf.nn.embedding_lookup(word_embedding, self.input_data))

inputs_list = [tf.squeeze(input_, [1]) for input_ in inputs_list]

initial_state是cell的初始狀態(tài),其維度是[batch_size,cell.state_size],由于rnn_cell模塊提供了對狀態(tài)的初始化函數(shù),因此我們可以直接調(diào)用:

self.initial_state = self.cell.zero_state(args.batch_size, tf.float32)

cell就是我們要構(gòu)建的解碼器和編碼器的cell,上面已經(jīng)提過了。最后一個(gè)參數(shù)是loop_function,其作用是在生成的時(shí)候,我們需要把解碼器上一時(shí)刻的輸出作為下一時(shí)刻的輸入,并且這個(gè)loop_function需要我們自己寫,如下所示:

def loop(prev, _):
   prev = tf.matmul(prev, softmax_w) + softmax_b
   prev_symbol = tf.stop_gradient(tf.argmax(prev, 1))
   return tf.nn.embedding_lookup(embedding, prev_symbol)

最后,我們就可以構(gòu)建好seq2seq的模型了,將上面參數(shù)傳入rnn_decoder函數(shù)即可:

outputs, last_state = seq2seq.rnn_decoder(inputs_list, self.initial_state, self.cell, loop_function=loop if infer else None, scope='rnnlm')

其中outputs是與decoder_inputs同樣維度的量,即每一時(shí)刻的輸出;last_state的維度是[batch_size,cell.state_size],即最后時(shí)刻的所有cell的狀態(tài)。接下來需要outputs來確定目標(biāo)函數(shù),而last-state的作用是作為抽樣生成函數(shù)下一時(shí)刻的狀態(tài)。

tensorflow中提供了sequence_loss_by_example函數(shù)用于按照權(quán)重來計(jì)算整個(gè)序列中每個(gè)單詞的交叉熵,返回的是每個(gè)序列的log-perplexity。為了使用sequence_loss_by_example函數(shù),我們首先需要將outputs通過一個(gè)前向?qū)樱瑫r(shí)我們需要得到一個(gè)softmax概率分布,這個(gè)在生成中會(huì)用到:

output = tf.reshape(tf.concat(1, outputs), [-1, args.rnn_size])

self.logits = tf.matmul(output, softmax_w) + softmax_b

self.probs = tf.nn.softmax(self.logits)

loss = seq2seq.sequence_loss_by_example([self.logits],
      [tf.reshape(self.targets, [-1])],
      [tf.ones([args.batch_size * args.seq_length])],
      args.vocab_size)

    # average loss for each word of each timestepself.cost = tf.reduce_sum(loss) / args.batch_size / args.seq_length

最后就是建立一個(gè)op,以便訓(xùn)練,例如var_op、var_trainable_op、train_op、initial_op、saver:

self.lr = tf.Variable(0.0, trainable=False)

self.var_trainable_op = tf.trainable_variables()

grads, _ = tf.clip_by_global_norm(tf.gradients(self.cost, self.var_trainable_op),
          args.grad_clip)optimizer = tf.train.AdamOptimizer(self.lr)

self.train_op = optimizer.apply_gradients(zip(grads, self.var_trainable_op))

self.initial_op = tf.initialize_all_variables()

self.saver = tf.train.Saver(tf.all_variables(),max_to_keep=5,keep_checkpoint_every_n_hours=1)

self.logfile = args.log_dir+str(datetime.datetime.strftime(datetime.datetime.now(),'%Y-%m-%d %H:%M:%S')+'.txt').replace(' ','').replace('/','')

self.var_op = tf.all_variables()

train_op即為訓(xùn)練時(shí)需要運(yùn)行的。

5.編寫抽樣生成函數(shù)

如上所述,在抽樣生成的時(shí)候,我們首先需要一個(gè)種子序列,同時(shí)在第一步的時(shí)候,我們需要向網(wǎng)絡(luò)傳入一個(gè)0的初始狀態(tài),并通過種子序列的第一個(gè)字得到下一個(gè)隱含狀態(tài),然后再結(jié)合種子的第二個(gè)字傳入下一個(gè)隱含狀態(tài),直到種子序列傳入完畢:

state = sess.run(self.cell.zero_state(1, tf.float32))

for word in start:
   x = np.zeros((1, 1))
   x[0, 0] = words[word]
   feed = {self.input_data: x, self.initial_state:state}
   [probs, state] = sess.run([self.probs, self.final_state], feed)

種子序列運(yùn)行完畢以后,接下來就進(jìn)入真正的抽樣過程了,即拿上一時(shí)刻的state以及上一時(shí)刻輸出probs中的最佳單詞作為下一時(shí)刻的輸入,那么給定了一個(gè)所有單詞的概率分布probs,該時(shí)刻的最佳單詞如何定義呢?這里我列舉了三種情況:

  • argmax型:即找出probs中最大值所對應(yīng)的索引,然后去單詞表中找到該索引對應(yīng)的單詞即為最佳單詞;

  • weighted型:即隨機(jī)取樣,其工作流程如下:首先,計(jì)算此probs的累加總和S;其次,隨機(jī)生成一個(gè)0~1之間的隨機(jī)數(shù),并將其與probs的總和相乘得到R;最后,將R依次減去probs中每個(gè)數(shù),直到R變成負(fù)數(shù)的那個(gè)probs的索引,即為我們要挑選的最佳單詞;

  • combined型: 這里我把a(bǔ)rgmax和weighted結(jié)合起來了,即每次遇到一個(gè)空格(相當(dāng)于一句歌詞的結(jié)尾),就使用weighted型,而其他時(shí)候都使用argmax型。

這三種的實(shí)現(xiàn)方式如下所示:

def random_pick(p,word,sampling_type):
   def weighted_pick(weights):
       t = np.cumsum(weights)
       s = np.sum(weights)
       return(int(np.searchsorted(t, np.random.rand(1)*s)))    
   if sampling_type == 'argmax':
       sample = np.argmax(p)    
   elif sampling_type == 'weighted':
       sample = weighted_pick(p)
   elif sampling_type == 'combined':        
       if word == ' ':
           sample = weighted_pick(p)
       else:
           sample = np.argmax(p)
   return sample

最后,抽樣生成過程的具體代碼如下所示,其中start是種子序列,attention是判斷是否加入了注意力機(jī)制。

word = start[-1]for n in range(num):
   x = np.zeros((1, 1))
   x[0, 0] = words[word]
   if not self.args.attention:
       feed = {self.input_data: [x], self.initial_state:state}
       [probs, state] = sess.run([self.probs, self.final_state], feed)
   else:
       feed = {self.input_data: x, self.initial_state:state,self.attention_states:attention_states}
       [probs, state] = sess.run([self.probs, self.final_state], feed)
   p = probs[0]
   sample = random_pick(p,word,sampling_type)
   pred = vocab[sample]
   ret += pred
   word = pred

ret數(shù)組即為最終的生成序列。

6.編寫訓(xùn)練函數(shù)

訓(xùn)練函數(shù)需要完成的功能主要有提供用戶可設(shè)置的超參數(shù)、讀取配置文件、按照mini-batch進(jìn)行批訓(xùn)練、使用saver保存模型參數(shù)、記錄訓(xùn)練誤差等等,下面將列舉部分代碼進(jìn)行說明。

首先,我們使用argparse.ArgumentParser對象進(jìn)行解析命令行參數(shù)或者設(shè)置默認(rèn)參數(shù),例如:

parser.add_argument('--rnn_size', type=int, default=128,
            help='set size of RNN hidden state')

設(shè)置了rnn_size默認(rèn)大小為128,而用戶也可以在命令行使用類似于以下這種方式來指定參數(shù)大小:

python train.py --rnn_size 256

其次,我們需要提供是否繼續(xù)訓(xùn)練的判斷,也就說是從頭開始訓(xùn)練還是導(dǎo)入一個(gè)已經(jīng)訓(xùn)練過的模型繼續(xù)訓(xùn)練,即下面的語句:

if args.keep is True:
   print('Restoring')
   model.saver.restore(sess, ckpt.model_checkpoint_path)        

else:
   print('Initializing')
   sess.run(model.initial_op)

然后就是將X和Y數(shù)據(jù)feed到模型中去運(yùn)行op并得到誤差值:

x, y = text_parser.next_batch()

feed = {model.input_data: x, model.targets: y, model.initial_state: state}

train_loss, state, _ = sess.run([model.cost, model.final_state, model.train_op], feed)

訓(xùn)練過程比較簡單,基本上就是設(shè)置參數(shù)-導(dǎo)入數(shù)據(jù)-導(dǎo)入模型-運(yùn)行op-得到誤差-下一個(gè)Epoch繼續(xù)訓(xùn)練,直到滿足要求為止。

7.編寫日志

這里說的日志可以理解為保存參數(shù)、保存訓(xùn)練過程中的誤差以及訓(xùn)練時(shí)間等等,僅作拋磚引玉的說明。為了使得每一次訓(xùn)練都不會(huì)白白浪費(fèi),我們需要設(shè)置好參數(shù)保存,如可以設(shè)置訓(xùn)練了多少個(gè)樣本就保存一次參數(shù)、訓(xùn)練了多少個(gè)Epoch就保存一次:

if (e*text_parser.num_batches+b)%args.save_every==0 、
       or (e==args.num_epochs-1 and b==text_parser.num_batches-1):
   checkpoint_path = os.path.join(args.save_dir, 'model.ckpt')
   model.saver.save(sess, checkpoint_path, global_step = e)
   print("model has been saved in:"+str(checkpoint_path))

記錄訓(xùn)練誤差也是很重要的一步,很多時(shí)候我們需要分析cost曲線隨時(shí)間或者是迭代次數(shù)的變化趨勢,因此這里我們建立了一個(gè)logging函數(shù)(在utils.py文件中),并且在每一個(gè)Epoch訓(xùn)練結(jié)束的時(shí)候就記錄一次該Epoch的平均誤差、運(yùn)行時(shí)間等等:

delta_time = end - start

ave_loss = np.array(total_loss).mean()

logging(model,ave_loss,e,delta_time,mode='train')

8.編寫可視化函數(shù)

由于時(shí)間的關(guān)系,這里僅對日志文件做了初步的可視化,即提取日志文件中的Epoch以及對應(yīng)的誤差,從而得到一條Cost-Epoch曲線,可視化的函數(shù)的部分代碼如下:

if line.startswith('Epoch'):        
   if 'validate' in line:
       index2 = index2 + 1
       cost = line.split(':')[2]
       indexValidateList.append(index2)
       validateCostList.append(float(cost))
   elif 'train error rate' in line:
       index1 = index1+1
       cost = line.split(':')[2]
       indexCostList.append(index1)
       costList.append(float(cost))

然后使用matplotlib庫進(jìn)行作圖:

def plot(self):
   title,indexCostList,costList = self.parse()
   p1 = plt.plot(indexCostList,costList,marker='o',color='b',label='train cost')
   plt.xlabel('Epoch')
   plt.ylabel('Cost')
   plt.legend()
   plt.title(title)    
   if self.saveFig:
       plt.savefig(self.logFile+'.png',dpi=100)
       #plt.savefig(self.logFile+'.eps',dpi=100)
   if self.showFig:
       plt.show()

9.設(shè)置訓(xùn)練超參

超參的選擇一直是訓(xùn)練深度學(xué)習(xí)的一個(gè)難點(diǎn),無論是循環(huán)神經(jīng)元的個(gè)數(shù)、層數(shù)還是訓(xùn)練樣本批處理的大小,都沒有一個(gè)固定的判斷準(zhǔn)則,超參設(shè)置因問題而已,而且很多時(shí)候論文中使用的經(jīng)驗(yàn)規(guī)則,而我這里也只能根據(jù)我們做語音識(shí)別系統(tǒng)的經(jīng)驗(yàn)設(shè)置的參數(shù)。

我們選擇了兩層LSTM,每層包含128個(gè)神經(jīng)元作為seq2seq模型的cell,詞向量word_embedding的大小為100,批處理大小設(shè)置為32,序列長度為16,并且使用了Adam隨機(jī)梯度下降算法,學(xué)習(xí)率設(shè)置為0.001,一共訓(xùn)練了230個(gè)周期。

10.訓(xùn)練環(huán)境

本次訓(xùn)練的環(huán)境是Ubuntu 16.04操作系統(tǒng),使用的tensorflow版本是r0.11,所使用的python版本為2.7,所用的GPU是Nvidia GeForce GTX 960M。

11.結(jié)果展示

我們現(xiàn)在選擇了《我們》作為種子序列,然后讓機(jī)器生成了長度為200的歌詞,如下所示,其中有幾句看起來似乎有押韻的意思,整體上看起來不知道要表達(dá)什么(選取幾個(gè)示例):

趙雷1   num:200

我們是不是我的壓抑掙住。我愛這世間美貌的女子??墒撬齻儾荒埽c心里。生命運(yùn)說的太多。那是我快樂。社會(huì)更想你。說愛你在你滴話我來需要在家遠(yuǎn)。春天的陽光會(huì)讓它消失無影蹤。我醒來時(shí)看到白茫茫一片。堆起的雪人誰能賦予它靈魂。他睜著眼笑著看冬天走遠(yuǎn)。春天的陽光會(huì)讓它消失無影蹤。它變成泥土像紛飛的葉子。它去浪擁有這個(gè)世界。讓我吧邊朝你我的心事。我只能做在街頭發(fā)地的那路是堵著??催^去這樣像躲不去自己??偘咽サ墓薁€。

趙雷2   num:200

我們的話不是不是醉的我易世界。那啊堅(jiān)反些等待下太多。長大后的腳都在星星下。我愛那個(gè)黃昏有幾個(gè)可以走遠(yuǎn)的樹子。再也不會(huì)有明月,再也不能與我希望。天空很下,歲女你。有沒有你還一些部。還是你的故事。十年的年前是是再時(shí)看身影子。她是個(gè)不剛有點(diǎn)個(gè)世界。你不是不是我的唱和你。我知道我一直知決在石橋。一個(gè)人和人留下一個(gè)春天。再見昨夜里面雷小雨停盡。我也哭了我的故事還是星星傷。挑剔著,輪換著,你動(dòng)越來越來的就把我陰了。

趙雷3   num:200

我們是不是我的家鄉(xiāng)。即使死在,我的生活,不懂難開。昨日的好狠卸倒在樹。偶爾的走一走,在我性。就不會(huì)委屈了你。一切會(huì)好。為這么多的懷抱。總是覺得工作太忙。常常被冷眼灼傷。不會(huì)在這個(gè)城市我這個(gè)世界。這也清知明天愛的星空是在回家。我也只有你小屋你裝滿了寬恕。我的小屋我喜歡給你唱歌我喜歡坐在看頭??墒悄悴皇窃谶@世間愛的,像路在陰暗空的活。我在來飛的太好。趙小雷小趙,哦哦,否習(xí)慣,不藍(lán)。沉睡,不會(huì)讓你往東。你敢。

趙雷4   num:200

我們是原因我是個(gè)不老的混蛋了。一看到你的淚不用可以光著腳。沈浸在雨后的長路,沒有人來往。那是我的自由,那是黃昏收獲的麥香。那時(shí)樹影下的母親在為我縫補(bǔ)衣裳。那是她的天堂,那是再見之后。一路的冬天太冷我已不能體會(huì)任希望的街頭。我穿著蒼瑕的愛情,再也不會(huì)被子。給你一好的你。一定是你希望。如果我真的哭了,是舍不得北京。帶不走在這個(gè)城市我的心。為見北京還那樣往北京的火車。路上的笑站在樹,誰久何時(shí)間世界。讓我添上。

12.優(yōu)化改進(jìn)

現(xiàn)在我們加入了林夕的所有歌詞作為語言模型的預(yù)訓(xùn)練語料,通過訓(xùn)練好基于LSTM-seqseq的語言模型之后,再只使用趙雷的歌詞來在此模型上接著訓(xùn)練了40個(gè)Epoch。

編輯部為了這次優(yōu)化,想了很多名詞作為歌曲的開頭。分別是:

“平淡” : 趙雷為人就是怎么平易近人

“有你們的愛”: 給所有愛趙雷的人們

“游戲而已”: 趙雷在歌手說過的一句話

“我就是我”: 趙雷你是不一樣的煙火

“北方姑娘”: 趙雷南方姑娘的對立

“我的姑娘你在哪兒”: 希望趙雷找到自己的幸福

好啦,接下就讓我們一起看看結(jié)果吧!

作為種子序列生成的長度為200的趙雷風(fēng)格的歌詞如下(為了美觀,所有逗號改成了空格,句號改成了換行):

平淡


平淡

花多好戲地說再見

身回最這樣如何其實(shí)再邊竟會(huì)唔于動(dòng)人

世界最外表演天事

借給你肩膀才能證明我會(huì)過怎知道

我也有想你聽過這一切

獻(xiàn)出自己事

難受傷懂得體去寂寞

別等別留給你奏禮

待著過和人生或者都給你我的模糊

有人有一個(gè)人的對子

是場游戲的歌

而我這個(gè)世界我

在誰要天際

無法自己

只有紅它卻如進(jìn)了

就走在笑著花兒

星星灰銀色

你說很少把春把月亮

站在你去在走

隨時(shí)流過無時(shí)笑過

昨天的媽在這夜空沒


有你們的愛


有你們的愛太簡單

讓你擁有一絲負(fù)擔(dān)的溫柔

我要回的遙見時(shí)差往北

熟通的笑聲敲打

我們能顧及身人

過客已沒有理掉

所有的責(zé)任關(guān)心

來時(shí)又比有吸

你也明明我自己讓余歌

更溫柔吻看不到的理思

薰衣服里請你在過

火總有天總是謝你有過有了多少她有沒有個(gè)好或

曾給我也可以難實(shí)瘋的多人

起來短有我沉在多月亮

來雨后雨照把煙

一感一天面不樣

熟悉的是聞 親愛的

我愛的本錢 也不能

像隨街換轉(zhuǎn)身邊少分

會(huì)轉(zhuǎn)身都由面無再學(xué)


游戲而已


游戲而已高極高逐

無論世界大路口多

人被我無怨還是錯(cuò)

種信候。愿我問候

夏日花色還有十號

歌早不出笑謝你走過

電柔菌開那是獎(jiǎng)上一種淚過

十個(gè)寂寞不要就這么一個(gè)

好地犧牲我已經(jīng)鬧事唱淪手朋友

信心有明知當(dāng)日我

只愿意忘了有刻想給到你重頭

便會(huì)忍心一生對不過

仍可以去自己怎會(huì)好

沒有你沒有什么東西可叫開心

在這里開走一把手動(dòng)聽你身家全世界

那么多

仍然未能被擁抱高一塊較

算你或者會(huì)坐

定要指打我的身情

終要重頭


我就是我


我就是我的地教我有個(gè)夢

是二么老友她拿著和你說

有你的很想都放棄

就像一切心跳

走過走一個(gè)去不分開始一樣

這張開心第一生太多節(jié)奏

為你而受傷只得好怎么怎知一世

不需多管我講過

我期待我就求運(yùn)識(shí)你在手電過以未夠一樣

便存在誰在起來

誰想給你別給我講多可假

才明白我愿意憑著世利寧愿擋子彈

請與其實(shí)未想過代未見

錯(cuò)愛情仍能要逃避

我會(huì)在意當(dāng)未來也要痛苦

每次發(fā)泄也不要能得到用勁

慣去苦也不再展開始沒出利保留到你


北方姑娘


北方姑娘以后忘記

獨(dú)自由心清醒大幾多

薰衣草

能各自吸引

什么都哭干淚人又一樣的過

別留下我的眼睛  已把我的溫柔

我愛你  你會(huì)停留住離

離陽的路  擦到的懷巴

城市的幻想起浪漫陽光眼淚

從來沒日落后  最大地問數(shù)

讓我這個(gè)挽著沒有時(shí)刻它笑未必反明快樂清楚

等待我對具游戲

心事全破忘擁有

我們大丈夫

只是有期待我一分

模糊面不被疑流

為跳最怕講你都說分

愿你的情緒比我內(nèi)得

累這世界

熱管你說過去做

眼淚你找到你


我的姑娘你在哪兒


我的姑娘你在哪兒靜別人

而我太多  給你離開的鮮空

在我街著一件閃爍的人

即使北京就破

想到紅塵忌時(shí)請放開我覺得在乎

高遺失望也許也完全無依依

愿我更難受

面對不需要屬于我一樣的好

我就開始不會(huì)把

事業(yè)不開怎么一分

給我愛你那么感受

狂情不合沉默的心情擦到的風(fēng)

在這兩個(gè)夢時(shí)今晚

什么不多一個(gè)

人生最一秒鐘我像一道未見過她

親愛的  以為我自己送給你親愛的

趙云藍(lán)已經(jīng)過收上

愛的過火別口真情敵已經(jīng)要無人

最大種聲

結(jié)果不是特別理想,主要原因是:趙雷的歌曲還是比較少,數(shù)量沒有達(dá)到很多,以至于訓(xùn)練出來的比較奇怪,有些語句不通順。但是,我們盡自己最大努力去做這一件事啦。目的是表達(dá)我們對雷子的熱愛之情。

希望趙雷在今后的音樂道路上越走越遠(yuǎn),創(chuàng)作出更多好的作品,這樣,我們的結(jié)果會(huì)更好!向趙雷致敬!

科技牛逼?。?!

民謠牛逼?。。?/p>

趙雷牛逼?。?!

雷鋒網(wǎng)按:雷鋒網(wǎng)獲得授權(quán)轉(zhuǎn)載,本文原載于微信公眾號量化投資與機(jī)器學(xué)習(xí)。雷鋒網(wǎng)

雷峰網(wǎng)版權(quán)文章,未經(jīng)授權(quán)禁止轉(zhuǎn)載。詳情見轉(zhuǎn)載須知。

致敬趙雷:基于TensorFlow讓機(jī)器生成趙雷曲風(fēng)的歌詞

分享:
相關(guān)文章

編輯

聚焦數(shù)據(jù)科學(xué),連接 AI 開發(fā)者。更多精彩內(nèi)容,請?jiān)L問:yanxishe.com
當(dāng)月熱門文章
最新文章
請?zhí)顚懮暾埲速Y料
姓名
電話
郵箱
微信號
作品鏈接
個(gè)人簡介
為了您的賬戶安全,請驗(yàn)證郵箱
您的郵箱還未驗(yàn)證,完成可獲20積分喲!
請驗(yàn)證您的郵箱
立即驗(yàn)證
完善賬號信息
您的賬號已經(jīng)綁定,現(xiàn)在您可以設(shè)置密碼以方便用郵箱登錄
立即設(shè)置 以后再說