精华内容
下载资源
问答
  • RNN LSTM语言模型

    2020-02-23 11:55:00
    2. RNN LSTM语言模型 (梯度权重) (1)one to one : 图像分类 (2)one to many:图片描述 (3)many to one:文本情感分析、分类 (4)many to many(N vs M):Seq2Seq(机器翻译) (5)many to many(N vs...

    1. 语言模型

     

    2. RNN LSTM语言模型

     

    (梯度权重)

     

    (1)one to one : 图像分类

    (2)one to many:图片描述

    (3)many to one:文本情感分析、分类

    (4)many to many(N vs M):Seq2Seq(机器翻译)

    (5)many to many(N vs N):字符预测、视频分类(按帧)

     

     (根据上下文,精确度更高,计算量更大)

    3. LSTM

    (门)

     

     

     

     

     

     

     

    展开全文
  • 非常好奇强大的生成技术,写了这个小项目。模型优化无止境,这只是...主要使用了基于概率语言模型的方法和基于seq2seq的方法进行生成。 本文所有代码地址:[Github](https://github.com/Nana0606/Lyrics-generation)

    写在前面

    非常好奇强大的生成技术,写了这个小项目。模型优化无止境,这只是初步的模型,有时间我也会不断优化。
    本文主要对中文歌词进行自动生成。主要使用了基于概率语言模型的方法和基于seq2seq的方法进行生成。

    本文所有代码地址:Github

    一、数据爬取

    1、数据内容分析

    本次爬取的是网易云音乐网页版的数据,这里以罗大佑的主页举例,其他歌手类似。

    最开始想要通过歌手主页的歌曲列表爬取,但是发现此页面(https://music.163.com/#/artist?id=3686 )只包含50首热门歌曲,这个歌曲数量有点少,所以这里通过另一种途径进行了爬取:

    (1)先爬取歌手的专辑信息(https://music.163.com/#/artist/album?id=3686 ),并获取专辑id
    (2)通过专辑id获取专辑详情页面,再爬取每张专辑中的歌曲信息。

    2、代码地址

    Github代码地址:爬取代码

    3、爬取代码分析

    (1)首先,我们需要爬取歌手页面的专辑信息,前端展示的网址为:https://music.163.com/#/artist/album?id=3686 ,但是这里要说明,爬取的网址并非此网址,而是通过浏览器Network分析出的网址:https://music.163.com/artist/album?id=3686 ,下面的图片可以理解的更清晰:

    (2)通过上一步得到的专辑的id,构建专辑详情地址(这里url需要注意的问题和上面的问题一样)进行爬取。
    (3)通过上一步得到的歌曲的id进行歌词的爬取。

    代码详见github

    二、基于概率语言模型的模型

    1、概率语言模型思想

    我们是基于n-gram语言模型的思想,n-gram模型作了一个n1n-1阶的Markov假设,即认为一个词出现的概率只与它前面的n1n-1个词相关,所以我们可以通过前nn个词预测下一个词,公式表示为:
    et^=argmaxetP(etetn1t1) \hat{e_t} = argmax_{e_t}P(e_t|e_{t-n-1}^{t-1})
    其中et^\hat{e_t}表示基于前nn个词预测出来的tt时刻词的概率,在本次练习中,n=10n=10(参数可调)。
    LSTM可以考虑序列之间的联系,所以我们选择LSTM作为本次练习的网络。

    2、网络搭建

    (1)概览

    这里首先讲下网络构建的流程,下面再就每一个流程进行详细讲解。训练网络的流程如下:

    Step1: 读取训练文件,并分词。
    Step2: 将分词结果和下标对应,得到char-to-index、index-to-char(或者phrase-to-index、word-to-index)用于后续索引
    Step3: 生成训练数据和验证数据用于后续训练和验证。
    Step4: 模型构建、训练和保存
    Step5: 调参
    Step6: 数据accuracy分析
    Step7: 歌词生成,加载已训练模型生成数据。

    下面从这六个步骤说明歌词生成的过程。说明:这里将歌词生成的部分和歌词训练的部分分开了,所以需要对训练数据得到的模型进行存储。
    (这里只对主要代码进行copy,其他代码详见github,都有注释)

    (2)分词和索引

    ①分词主要就是将句子分开。在中文里,可以将其分成“字”和“短语”,比如“我爱中国”,按照“字”进行分割的结果是:[“我”,“爱”,“中”, “国”];按照“短语”进行分割的结果是:[“我”,“爱”,“中国”]。可以使用jieba分词。

    ②jieba分词的举例如下:

    texts = [“你爱中国”, “你是小宝宝”]
    cut_res = [[“你”, “爱”, “中国”],[“你”, “是”, “小宝宝”]]
    freq_words = {“你”:2, “爱”: 1, “中国”: 1, “是”: 1, “小宝宝”: 1}
    word_to_index is = {‘你’: 1, ‘爱’: 2, ‘中国’: 3, ‘是’: 4, ‘小宝宝’: 5}(频率大的在前面)
    index_to_word = {1: ‘你’, 2: ‘爱’, 3: ‘中国’, 4: ‘是’, 5: ‘小宝宝’}(频率大的在前面)
    sequences is = [[1, 2, 3], [1, 4, 5]]

    索引的目的便是在网络模型中方便输入序列,以便进行数据表示。

    ③本文主要基于“字”的分割,实验结果证明基于字的分割比基于“词语”的分割更有效。

    (3)训练集和测试集生成

    由上述模型的思想可知,我们需要生成训练数据,其输入数据XX是前10个词,输出标签数据是第11个词。为了方便处理,我们将所有的句子连接成一个整体,通过上述思想生成数据。例如:

    连接后的句子:“柔情万种本色难改胭脂内的你难解的胸怀”
    生成的数据如下:
    ①x1: “柔情万种本色难改胭脂”, y1: “内”
    ②x2: “情万种本色难改胭脂内”, y2: “的”
    ③x3: “万种本色难改胭脂内的”, y3: “你”
    ④x4: “种本色难改胭脂内的你”, y4: “难”
    ⑤x5: “本色难改胭脂内的你难”, y5: “解”
    ⑥x6: “色难改胭脂内的你难解”, y6: “的”
    ⑦x7: “难改胭脂内的你难解的”, y7: “胸”
    ⑧x8: “改胭脂内的你难解的胸”, y8: “怀”

    构建过程如下:

    def generateTrainData(cut_word_list, word_to_index):
        """
        构造训练集,并处理成keras可以接受的输入
        :param cut_word_list: 分词之后的list
        :param seq_length: 指定的序列长度,即输入X的长度
        :return:
        """
        X_data = []
        y_data = []
        data_index = []
        n_all_words = len(cut_word_list)
        for i in range(0, n_all_words - SEQ_LENGTH - 1):
            seq_x_y = cut_word_list[i: i+SEQ_LENGTH + 1]   # 最后一个词是y
            index_x_y = [word_to_index[elem] for elem in seq_x_y]    # word to index
            data_index.append(index_x_y)
        np.random.shuffle(data_index)
        for i in range(0, len(data_index)):
            X_data.append(data_index[i][:SEQ_LENGTH])
            y_data.append(data_index[i][SEQ_LENGTH])
    
        # 将X_data变换成需要输入的tensor模式,将y_data变成one-hot模式
        X = np.reshape(X_data, (len(X_data), SEQ_LENGTH))
        y = np_utils.to_categorical(y_data)
    
        X_train, X_val, y_train, y_val = train_test_split(X, y, test_size=0.2, random_state=33)
    
        return X_train, X_val, y_train, y_val
    

    (4)模型构建和训练

    基于语言的概率模型本质就是分类模型,基于前面10个字对下一个字进行预测,找到概率最大的字便是预测结果。其基本模型如下:

    从图中可以看出,本模型使用了2层LSTM,一层全连接层。

    代码如下:

    input_shape = (SEQ_LENGTH,)
    x_train_in = Input(input_shape, dtype='int32', name="x_train")
    
    # word_index存储的是所有vocabulary的映射关系
    nb_words = min(MAX_NB_WORDS, len(word_to_index))
    print("nb_words is::", nb_words)
    embedding_layer = Embedding(nb_words, EMBEDDING_DIM, input_length=SEQ_LENGTH)(x_train_in)
    print("embedding layer is::", embedding_layer)
    print("build model.....")
    
    # return_sequences=True表示返回的是序列,否则下面的LSTM无法使用,但是如果下一层不是LSTM,则可以不写
    lstm_1 = LSTM(EMBEDDING_DIM, name="LSTM_1", return_sequences=True, kernel_regularizer=regularizers.l2(0.01))(embedding_layer)
    lstm_2 = LSTM(EMBEDDING_DIM_2, name="LSTM_2", kernel_regularizer=regularizers.l2(0.01))(lstm_1)
    dense = Dense(nb_words, activation="softmax", name="Dense_1", kernel_regularizer=regularizers.l2(0.01))(lstm_2)
    
    model = Model(inputs=x_train_in, outputs=dense)
    
    print(model.summary())
    
    adam = Adam(lr=0.0001, beta_1=0.9, beta_2=0.99, epsilon=1e-08)
    model.compile(loss='categorical_crossentropy',
    			  optimizer=adam,
    			  metrics=['accuracy'])
    print("Train....")
    
    history_record = model.fit(X_train,
    						  y_train,
    						  batch_size=BATCH_SIZE,
    						  epochs=EPOCHS,
    						  validation_data=(X_val, y_val))
    model.save('./model_epoch50_2lstm_1dense_seq50_phrase_based_best.h5')
    
    

    (5)调参

    针对以上模型主要进行了以下调参:
    ①学习率调整
    学习率设置为0.001的时候,训练情况出现了如下图所示的情况:

    即训练过程出现了学习率过大的问题,训练进入不到最优解的状态,于是将学习率调整为0.0001,问题得到解决。
    ②lstm层数调整
    A. 2层lstm换成3层lstm,结果:2层的好于3层的lstm
    B. 2层lstm换成1层lstm,结果:2层lstm情况下best的结果val-loss≈3.31,val-acc≈0.55;1层lstm情况下best的结果val-loss≈4.34,val-acc≈0.43。
    ③优化算法调整
    Adam优化算法换成RMSprop算法,结果:Adam算法效果更好。
    best的情况下:Adam算法val-loss≈3.31,RMSprop算法val-loss≈4.56;Adam算法val-acc≈0.55,RMSprop算法val-acc≈0.42。
    Adam和Rmsprop的配置如下:

    Adam(lr=0.0001, beta_1=0.9, beta_2=0.99, epsilon=1e-08)
    RMSprop(lr=0.0001, rho=0.9)
    

    ④添加正则项
    具体:将LSTM和Dense都加上kernel_regularizer=regularizers.l2(0.01)或者都加上kernel_regularizer=regularizers.l1(0.01),结果:算法效果都不好,两个的结果都是val-loss≈6.19,val-acc≈0.13(从第一个epoch开始val-loss就一直不变)。
    ⑤添加dropout
    代码如下,给中间层dropout:

        lstm_1 = LSTM(EMBEDDING_DIM, name="LSTM_1", return_sequences=True)(embedding_layer)
        dropout_1 = Dropout(0.2)(lstm_1)
        lstm_2 = LSTM(EMBEDDING_DIM_2, name="LSTM_2")(dropout_1)
        dropout_2 = Dropout(0.2)(lstm_2)
        dense = Dense(nb_words, activation="softmax", name="Dense_1")(dropout_2)
    

    结果:在这种情况下,和没加dropout的性能基本类似,相比较于2lstm不加dropout的情况,val-loss减少0.1,但是相应的val-acc减少0.2,而且dropout会使得训练速度降低,所以这里选择不使用。
    ⑥调节隐层大小
    将隐藏层大小调整为:EMBEDDING_DIM = 256, EMBEDDING_DIM_2 = 512
    结果:EMBEDDING_DIM = 512, EMBEDDING_DIM_2 = 1024效果比较好,在此情况下,val-loss≈3.31,val-acc≈0.55。而在256-512的情况下val-loss≈3.44,val-acc≈0.53。
    将隐藏层大小调整为:EMBEDDING_DIM = 800, EMBEDDING_DIM_2 = 1000
    结果:EMBEDDING_DIM = 800, EMBEDDING_DIM_2 = 1000效果比较好,val-loss≈3.11,val-acc≈0.56。
    ⑦歌词分割标准
    使用基于词语的分割(jieba分词)代替基于字的分割。结果:基于字的分割效果更好。

    综上,2层lstm较好,优化算法使用Adam,基于字分割,学习率为0.0001,适当调大隐藏层维度可能会使效果变得更好。

    其余可调节点:
    ①优化函数的参数
    ②loss function中加入正则项
    ③lstm换成双向lstm
    ④seq_length的调节
    ⑤调节MAX_NB_WORDS的值
    ⑥sample方法,使用Beam Search等
    ⑦加入attention机制
    ⑧将onehot表示方法替换成embedding

    (6)结果分析

    下面以2层lstm,1层dense,Adam优化算法和EMBEDDING_DIM = 800, EMBEDDING_DIM_2 = 1000为例分析算法结果。
    训练集上的accuracy和loss以及验证集上的val-accuracy和val-loss如下所示:

    从上图可以看出,当loss降低的时候,accuracy增加,在训练的时候,大概20个epoch左右就已经达到最好的状态。

    (7)生成歌词

    生成歌词的时候需要上一步训练得到的模型,除此之外,还需要word2index和index2word的索引,所以需要将其也存储下来(为了方便重用,代码中直接将其存储到了文件中)。
    生成歌词的思路步骤如下:

    ①加载模型、word2index和index2word
    ②将给定的开头编码成输入模式(和model中的输入相同),若字数不够10,则使用“<PAD>”填充(下标为0)。
    ③使用①加载到的模型预测下一个输出,不断迭代,直到达到我们设定的字数(本实验使用的是200)。

    代码如下:

    x_pred = np.zeros((1, SEQ_LENGTH))    # 使用PAD填充
    
    min_index = max(0, len(sentence) - SEQ_LENGTH)
    for idx in range(min_index, len(sentence)):
    	x_pred[0, SEQ_LENGTH - len(sentence) + idx] = word2index.get(sentence[idx], 1)   # '<UNK>' is 1
    
    preds = model.predict(x_pred, verbose=0)[0]
    next_index = sample(preds, diversity)
    next_word = index2word[next_index]
    sentence = sentence + next_word   # 每次都往后取一个
    

    (8)效果展示

    下面是使用上述模型训练的参数生成歌词的例子,生成的歌词长度是200。
    ①以“喧嚣的人群”开头:

    喧嚣的人群
    单内时化的约影
    隐血在约学
    城朝笔连别伊气错
    狗吃阿辉仔西饲
    一个阿仔仔嘟色
    来喝老伤情不管眼睛
    飞得阳色的一通深得到
    我的书像南才是一样
    有一个梦的理车
    在家停倾萄
    悔给你的爱人
    请开气吧
    我靠不要岁下比防民
    你的美豪必处发
    每天不知从意紧每个人都好
    好笑心心静却怎么偷也不存变
    照了土玫笼的麦道
    只来蚂印
    微少那万秋月的门上
    江倒院角飞
    这目光
    辉光期处拳在破上
    陪你终于失去
    雨成秋
    

    ②以“痴情”开头

    痴情的冲角
    却走出时间很嚣
    有此有想像一年
    铁科铁岸的笔河节某坡入灯
    缓睡如灯分如果
    生著弟
    大古悲是那此的黄空
    护着后还已不机
    等到心世将你
    眼泪运内一个
    你说你已不该归为爱在我写的歌
    只像我早跳了岁三山字等
    粉凡的地方
    温作的时间
    今眼已那一纪到
    往下血为
    你成了却幸福依险
    写情做难来
    同难需要做话
    去走就这样乡呢没关来
    为现来总有叶句正临到上要证望的唱时纷去
    故样照去
    几夜落清
    忘了有些什么
    

    ③以“自由”开头

    自由
    过去有几靠
    接了时泪
    却想收声
    想起我却不想
    不曾一样的俗物
    爱过你
    不是在这一个一个梦里
    热处海望海间好头
    越不可限当月
    让平持潮灭
    左心无影的结局
    随你本心
    一直到于你的情流我开始
    还是那么由过了
    原来我回忆满去去来
    再谢你已买化里的意录
    洋颗变园的音声境凉
    每天却将都一起
    你怀忘
    这旧古鲜的属于大街
    椅作页咧念出熟
    死了青角
    春起成开台折哭红的末脸
    为些母冰光的围契平个很察以答展
    我们
    

    三、基于序列模型的模型

    1、序列模型seq2seq思想

    seq2seq模型是编码-解码模型的一种,主要思想是通过一个Encoder将输入编码成一个语义向量cc,接着将cc作为Decoder的输入,从来产生输出结果。
    其具体原理请参见博客:seq2seq model和Attention-based seq2seq Model(动图展示)

    2、网络搭建

    (1)概览

    基于序列模型的歌词生成和基于概率语言模型的歌词生成步骤类似,只是模型改变,相应的训练集数据应该改变。分词和索引和上面基于概率语言模型的思想一样,训练集的生成与之前有些不同,下面就从训练集合测试集的生成部分开始说明。

    (2)训练集和测试集生成

    在seq2seq模型中,输入是一句话,输出也是一句话,所以其样本构造如下:

    歌词句子:
    -柔情万种
    -本色难改
    -胭脂内的你难解的胸怀
    -洋场十里
    生成的数据如下:
    ①x1: “柔情万种”, y1: “本色难改”
    ②x2: “本色难改”, y2: “胭脂内的你难解的胸怀”
    ③x3: “胭脂内的你难解的胸怀”, y3: “洋场十里”

    (3)模型构建和训练

    基于序列的模型基于前一句预测下一句。其模型结构如下:

    decoder和encoder部分都是基于lstm,在decoder后面有一层dense层,之后又接了一个softmax层,用于预测。

    代码实现如下:

    encoder_inputs = Input(shape=(None, len(word_to_index_input)), dtype='float32', name="encoder_inputs")
    encoder_inputs_masking = Masking(mask_value=0)(encoder_inputs)
    print("build model.....")
    
    # return_sequences=True表示返回的是序列,否则下面的LSTM无法使用,但是如果下一层不是LSTM,则可以不写
    encoder = LSTM(LATENT_DIM, name="encoder_outputs", return_state=True)
    encoder_outputs, state_h, state_c = encoder(encoder_inputs_masking)
    # We discard `encoder_outputs` and only keep the states.
    encoder_states = [state_h, state_c]
    
    decoder_inputs = Input(shape=(None, len(word_to_index_target)), dtype='float32', name="decoder_inputs")
    decoder_inputs_masking = Masking(mask_value=0)(decoder_inputs)
    decoder_LSTM = LSTM(LATENT_DIM, name="decoder_LSTM", return_sequences=True, return_state=True)
    decoder_outputs, _, _ = decoder_LSTM(decoder_inputs_masking, initial_state=encoder_states)
    decoder_dense = Dense(len(word_to_index_target), activation='softmax', name="Dense_1")
    decoder_outputs = decoder_dense(decoder_outputs)
    
    model = Model(inputs=[encoder_inputs, decoder_inputs], outputs=decoder_outputs)
    print(model.summary())
    
    adam = Adam(lr=0.001, beta_1=0.9, beta_2=0.99, epsilon=1e-08)
    model.compile(loss='categorical_crossentropy',
    			  optimizer=adam,
    			  metrics=['accuracy'])
    
    print("Train....")
    
    history_record = model.fit([encoder_input_data_train, decoder_input_data_train],
    						   decoder_target_data_train,
    						   batch_size=BATCH_SIZE,
    						   epochs=EPOCHS,
    						   validation_data=(
    						   [encoder_input_data_val, decoder_input_data_val], decoder_target_data_val))
    model.save('./model_seq2seq_300epoch_final.h5')_record
    

    (4)调参

    seq2seq的调参过程和方法和上面基于语言模型的方法类似。除此之外,也可以尝试如下小变动:
    ①加入attention机制
    ②将前一个词的输入结果+encoder的输入结果当做输入进行下一个词的预测等。
    ③增大数据集(因为测试中发现大的数据集生成效果更好)
    ④将其他相似任务模型的参数迁移到此任务,或者将数据分批,后一批加载前一批的参数数据进行参数迁移。

    测试发现,在基于语言的模型中,数据量不要求非常多,但是seq2seq模型相对来说,需要更大的数据量,训练过程所需要的时间也更多,这里就不再进行调参结果的分析。

    (5)结果分析

    下面以seq2seq模型,隐层向量维度为1000,限制最大句子长度为20,优化函数为Adam为例分析算法结果。
    训练集上的accuracy和loss以及验证集上的val-accuracy和val-loss如下所示:

    从上图可以看出,训练集的acc还在持续增加,loss持续降低;但是验证集上val-loss在先减小再减小(尽管val-acc一直在增加),这是典型的过拟合(分析详见:[交叉熵损失和accuracy关系] 验证集上val-loss先降低再增加,而val-accuracy一直在增加),但是我们存储的val-loss最小时的模型,在epoch=15,val-loss≈3.1,val-acc≈0.45。

    (6)生成歌词

    生成歌词部分使用上述训练的模型和参数进行模型生成,但是需要注意,我们需要将输入数据通过encoder模型进行编码,通过decoder对其进行预测,所以这里将encoder和decoder分开,然后基于这2个分开的子模型进行歌词的生词。
    代码如下:

    # Encode the input as state vectors.
    states_value = encoder_model.predict(input_seq_rep)
    
    # Generate empty target sequence of length 1.
    target_seq = np.zeros((1, 1, len(word2index_target)))
    # Populate the first character of target sequence with the start character.
    target_seq[0, 0, word2index_target['\t']] = 1.
    
    # Sampling loop for a batch of sequences
    # (to simplify, here we assume a batch of size 1).
    
    decoded_sentence_all = ''
    max_decoder_seq_length = 20   # 生成的每个句子的最大长度
    count = 0
    while count < 10:   # 生成10个句子
    	stop_condition = False    # 到达行结束符
    	decoded_sentence = ""
    	while not stop_condition:
    		output_tokens, h, c = decoder_model.predict(
    			[target_seq] + states_value)
    
    		# Sample a token1, :])
    		sampled_token_index = sample(output_tokens[0, -1, :], 1)
    		sampled_char = index2word_target[sampled_token_index]    # 生成的新的字
    		decoded_sentence += sampled_char
    
    		if(sampled_char == '\n' or len(decoded_sentence) > max_decoder_seq_length):
    			if sampled_char != '\n':
    				print("长度为20,直接结束,不用等待生成换行符!")
    				decoded_sentence += '\n'   # 若不自动换行,则手动添加一个结束符
    			stop_condition = True
    
    		if decoded_sentence != '\n':   # 防止出现没有内容的行
    			# Update the target sequence (of length 1).
    			target_seq = np.zeros((1, 1, len(word2index_target)))
    			target_seq[0, 0, sampled_token_index] = 1.
    			# Update states
    			states_value = [h, c]
    	if decoded_sentence != '\n':   # 防止出现没有内容的行
    		count += 1
    		decoded_sentence_all += decoded_sentence
    return decoded_sentence_all
    

    代码主要实现10句歌词的生成,对于每句歌词结束的条件是遇到换行符或者长度等于20。随着歌词的不断生成,下一个预测字的输入长度也在不断增加,代码中使用target_seq控制。

    (7)效果展示

    下面是使用上述模型训练的参数生成歌词的例子,生成10个句子。
    ①以“喧嚣的人群”开头:

    喧嚣的人群
    别让我受伤悲哀
    可以
    折拿
    效
    发谈
    根皮
    满
    下根
    道
    根
    

    ②以“痴情”开头:

    痴情
    晚到
    更的与片
    千彼生活成
    段
    子有大海
    泥遍
    花
    甘台仙仙
    度
    拼下泥
    

    ③以“自由”开头

    自由
    我决定
    生鱼的浪旁
    烂
    泥术羿回光
    射下讨
    光亮亮
    角角有角空
    予回起角
    度
    光角度限
    

    四、总结

    从上面可以看出,基于概率语言模型的生成效果好于基于序列模型,可能有以下原因:
    ①基于序列的模型主要对有前后关联信息的上下文有利,而歌词和诗歌还有些不同,诗歌的前后关联更强一些,而歌词很多上下句是关联不大的,如下面的歌词(罗大佑《倒影》中的歌词):

    会吗会吗
    只因为软弱的自己
    总不能说我不理
    要嘛要嘛
    

    ②对于序列模型这里因为没有采取过多的方式进行控制,在模型不断训练的过程中,很快出现了过拟合,如果采取一些方式,可能效果更好。
    ③在seq2seq模型中,最开始使用一个歌手的数据,结果非常差,后来扩大了数据集,效果好很多,想让结果更好,可以考虑继续增大训练集。

    展开全文
  • 本篇博客我们将使用pytorch实现一下循环神经网络模型(LSTM). 完整代码 1.数据预处理 # 实现参考 https://github.com/pytorch/examples/tree/master/word_language_model #! pip install torch #安装torch import...

    本篇博客我们将使用pytorch实现一下循环神经网络模型(LSTM).

    完整代码

    1.数据预处理

    # 实现参考 https://github.com/pytorch/examples/tree/master/word_language_model
    #! pip install torch #安装torch
    import torch
    import torch.nn as nn
    import numpy as np
    from torch.nn.utils import clip_grad_norm_
    
    class Dictionary(object):
        '''
        构建word2id,id2word两个字典
        '''
        def __init__(self):
            self.word2idx = {} #字典 词到索引的映射
            self.idx2word = {} #字典  索引到词的映射
            self.idx = 0
        
        def add_word(self, word):
            if not word in self.word2idx: #如果词到索引的映射字典中 不包含该词 则添加
                self.word2idx[word] = self.idx 
                self.idx2word[self.idx] = word #同时创建索引到词的映射
                self.idx += 1
        
        def __len__(self):
            return len(self.word2idx) #词到索引映射的字典大小
    
    
    class Corpus(object):
        '''
        基于训练语料,构建字典(word2id,id2word)
        '''
        def __init__(self):
            self.dictionary = Dictionary() #创建字典类对象
    
        def get_data(self, path, batch_size=20):
            # 添加词到字典
            with open(path, 'r') as f:#读取文件
                tokens = 0
                for line in f:  #遍历文件中的每一行
                    words = line.split() + ['<eos>'] #以空格分隔 返回列表 并添加一个结束符<eos>
                    tokens += len(words)
                    for word in words: #将每个单词添加到字典中
                        self.dictionary.add_word(word)  
            
            # 对文件做Tokenize
            ids = torch.LongTensor(tokens)
            token = 0
            with open(path, 'r') as f:
                for line in f:
                    words = line.split() + ['<eos>']
                    for word in words:
                        ids[token] = self.dictionary.word2idx[word]
                        token += 1
            num_batches = ids.size(0) // batch_size
            ids = ids[:num_batches*batch_size]
            return ids.view(batch_size, -1)

    2.RNN(LSTM)语言模型

    # 有gpu的情况下使用gpu
    device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
    #device = torch.device('cpu')
    • 超参数设置
    # 超参数的设定
    embed_size = 128    # 词嵌入的维度
    hidden_size = 1024  # 使用RNN变种LSTM单元   LSTM的hidden size
    num_layers = 1      #循环单元/LSTM单元的层数
    num_epochs = 5      # 迭代轮次
    num_samples = 1000  # 测试语言模型生成句子时的样本数
    batch_size = 20     # 一批样本的数量
    seq_length = 30     # 一个样本/序列长度
    learning_rate = 0.002 # 学习率
    • 加载数据集
    # 加载数据集
    corpus = Corpus()
    ids = corpus.get_data('data/train.txt', batch_size)
    vocab_size = len(corpus.dictionary)
    num_batches = ids.size(1) // seq_length
    • LSTM语言模型
    # RNN语言模型
    class RNNLM(nn.Module): #RNNLM类继承nn.Module类
        def __init__(self, vocab_size, embed_size, hidden_size, num_layers):
            super(RNNLM, self).__init__()
            #嵌入层 one-hot形式(vocab_size,1) -> (embed_size,1)
            self.embed = nn.Embedding(vocab_size, embed_size)
            #LSTM单元/循环单元
            self.lstm = nn.LSTM(embed_size, hidden_size, num_layers, batch_first=True)
            #输出层的全联接操作  
            self.linear = nn.Linear(hidden_size, vocab_size)
            
        def forward(self, x, h):
            # 词嵌入
            x = self.embed(x)
            
            # LSTM前向运算
            out,(h,c) = self.lstm(x,h)
    
            # 每个时间步骤上LSTM单元都会有一个输出,batch_size个样本并行计算(每个样本/序列长度一致)  out (batch_size,sequence_length,hidden_size)
            # 把LSTM的输出结果变更为(batch_size*sequence_length, hidden_size)的维度
            out = out.reshape(out.size(0)*out.size(1),out.size(2))
            # 全连接
            out = self.linear(out) #(batch_size*sequence_length, hidden_size)->(batch_size*sequence_length, vacab_size)
            
            return out,(h,c)
    
    model = RNNLM(vocab_size, embed_size, hidden_size, num_layers).to(device)
    • 损害函数和优化
    # 损失构建与优化
    criterion = nn.CrossEntropyLoss() #交叉熵损失
    #使用Adam优化方法 最小化损失 优化更新模型参数 
    optimizer = torch.optim.Adam(model.parameters(), lr=learning_rate)
    • 训练
    # 反向传播过程“截断”(不复制gradient)
    def detach(states):
        return [state.detach() for state in states] 
    
    # 训练模型
    for epoch in range(num_epochs):
        # 初始化为0
        states = (torch.zeros(num_layers,batch_size,hidden_size).to(device),
                 torch.zeros(num_layers,batch_size,hidden_size).to(device))
        
        for i in range(0, ids.size(1) - seq_length, seq_length):
            # 获取一个mini batch的输入和输出(标签)
            inputs = ids[:,i:i+seq_length].to(device)
            targets = ids[:,(i+1):(i+1)+seq_length].to(device) # 输出相对输入错一位,往后顺延一个单词
            
            # 前向运算
            states = detach(states)
            outputs,states = model(inputs,states)
            loss = criterion(outputs,targets.reshape(-1))
            
            # 反向传播与优化
            model.zero_grad()
            loss.backward()
            clip_grad_norm_(model.parameters(),0.5)
    
            step = (i+1) // seq_length
            if step % 100 == 0:
                print ('全量数据迭代轮次 [{}/{}], Step数[{}/{}], 损失Loss: {:.4f}, 困惑度/Perplexity: {:5.2f}'
                       .format(epoch+1, num_epochs, step, num_batches, loss.item(), np.exp(loss.item())))
    • 测试
    # 测试语言模型
    with torch.no_grad():
        with open('sample.txt', 'w') as f:
            # 初始化为0
            state = (torch.zeros(num_layers, 1, hidden_size).to(device),
                     torch.zeros(num_layers, 1, hidden_size).to(device))
    
            # 随机选择一个词作为输入
            prob = torch.ones(vocab_size)
            input = torch.multinomial(prob, num_samples=1).unsqueeze(1).to(device)
    
            for i in range(num_samples):
                # 从输入词开始,基于语言模型前推计算
                output, state = model(input, state)
    
                # 做预测
                prob = output.exp()
                word_id = torch.multinomial(prob, num_samples=1).item()
    
                # 填充预估结果(为下一次预估储备输入数据)
                input.fill_(word_id)
    
                # 写出输出结果
                word = corpus.dictionary.idx2word[word_id]
                word = '\n' if word == '<eos>' else word + ' '
                f.write(word)
    
                if (i+1) % 100 == 0:
                    print('生成了 [{}/{}] 个词,存储到 {}'.format(i+1, num_samples, 'sample.txt'))
    
    # 存储模型的保存点(checkpoints)
    torch.save(model.state_dict(), 'model.ckpt')

     

     

     

     

    展开全文
  • RNN的核心思想: 对RNN输入数据xtxtx_t,然后通过网络计算并得到输出结果hthth_t,再将某些信息(state,状态)传到网络的输入。...LSTM单元上面的那条直线代表了LSTM的状态state,它会贯穿所有串联在一...

    RNN的核心思想:
    对RNN输入数据xtxt,然后通过网络计算并得到输出结果htht,再将某些信息(state,状态)传到网络的输入。
    LSTM可以存储状态,并依靠状态对当前的输入进行处理分析和预测。
    传统RNN编码不了long-term dependecies,间隔太远的输入信息,RNN难以记忆。
    LSTM单元上面的那条直线代表了LSTM的状态state,它会贯穿所有串联在一起的LSTM单元,从第一个LSTM单元一直流向最后一个LSTM单元,其中,只有少量的线性干预与改变。状态state在这条隧道中传递时,LSTM单元可以对其添加或删减信息,这些对信息流的修改操作由LSTM中的Gates控制。这些Gates中包含了一个sigmoid层和一个向量点乘的操作。sigmoid可以直接控制信息传递的比例,如果为0表示不允许信息传递,为1表示让信息全部通过。每个LSTM单元中包含了3个这样的Gates,用来维护和控制单元的状态信息。凭借对状态信息的存储与修改,LSTM单元就可以实现长期记忆。

    展开全文
  • 开篇 这篇文章主要是实战内容,不涉及一些原理介绍,原理介绍为大家提供一些比较好的链接: ...中文汉化版:(译)理解 LSTM 网络 (Understanding LSTM Networks by colah)   2.Recurren...
  • LSTM模型概述

    千次阅读 2017-01-17 16:46:20
    LSTM(Long Short Term Memory)长短期记忆单元模型: 首先介绍一下RNN模型。之前比较熟悉的是CNN网络,但是CNN所处理的对象很大程度上是图像,但是针对自然语言处理的理解,不止对当前的输入有关,还需要记忆和关联...
  • | 全文共8155字,建议阅读时长8分钟 |本文由《开放教育研究》授权发布作者:冯翔邱...本研究旨在通过构建学业情绪自动预测模型,对大量学生反馈文本进行快速有效的学业情绪分类。研究首先利用词向量训练工具,将文...
  • import torchtext from torchtext.vocab import Vectors import torch from torch import nn import numpy as np import random import jieba random.seed(53113) np.random.seed(53113) torch.manual_seed(53113)...
  • 理解长短期记忆(Long Short Term Memory, LSTM模型
  • 命名实体识别是自然语言处理的一项关键技术. 基于深度学习的方法已被广泛应用到中文实体识别研究中. 大多数深度学习模型的预处理主要注重词和字符的特征抽取, 却忽略词上下文的语义信息, 使其无法表征一词多义, 因而...
  • NNLM神经网络语言模型对理解word2vec模型有很大的帮助, 包括对后期理解CNN,LSTM进行文本分析时有很大的帮助. 参考论文:A Neural Probabilistic Language Model(2003) 参考资料:https://www....
  • 斯坦福cs224d 语言模型,RNN,LSTM与GRU

    千次阅读 2017-06-13 11:26:29
    语言模型递归神经网络RNN 1 剃度弥散及梯度爆炸问题2 解决梯度爆炸和弥散3 深度双向RNNs4 应用RNN翻译模型 门限循环单元Gated Recurrent Units长短期记忆神经网络LSTM 翻译:@胡杨(superhy199148@...
  • 今日资料: ...中文版: http://wiki.jikexueyuan.com/project/tensorflow-zh/tutorials/recurrent.html 代码: https://github.com/tensorflow/models/blob/master/tutorials/
  • 目录实战:命名实体识别NER一、命名实体识别(NER)二、BERT的应用NLP基本任务查找相似词语提取文本中的实体问答中的实体对齐三、ALBERTALBERT 的三大改造ALBERT 效果如何总结四、ALBERT+Bi-LSTM模型五、ALBERT+Bi-...
  •     今天主要和大家分享一篇关于中文命名实体识别的文章,本文分析Lattice-LSTM模型,并针对该方法的弊端提出将字符符号信息合并到字符向量表示中,提高了模型的性能(计算量、效果)。 First Blood TILE: ...
  • tensorflow2.0 基于LSTM模型的文本生成

    千次阅读 2020-11-11 15:29:53
    基于LSTM模型的唐诗文本生成实验背景实验数据下载LSTM模型分析实验过程文本预处理编解码模型LSTM模型设置实验代码实验结果总结 实验背景   在自然语言处理(NLP)领域,大多对话机器人的对话形成都会采用基于语料...
  • 1.语言模型 语言模型用于对特定序列的一系列词汇的出现概率进行计算。一个长度为m的词汇序列{w1,…,wm}的联合概率被表示为P(w1,…,wm)。由于在得到具体的词汇之前我们会先知道词汇的数量,词汇wi的属性变化会根据其...
  • lstm 生成文本 在笑话语料库上训练角色级语言模型。 我决定尝试解决此问题的方法,我在OpenAI的Request for Research博客中找到了该方法。 您可以在这里查看代码。 这是用Pytorch编写的,并且受到Fast.ai关于从头...
  • BiLSTM-CRF 模型实现中文命名实体识别

    万次阅读 热门讨论 2018-03-24 08:32:48
    在MSRA的简体中文NER语料(我是从这里下载的,非官方出品,可能不是SIGHAN 2006 Bakeoff-3评测所使用的原版语料)上训练NER模型,识别人名、地名和组织机构名。尝试了两种模型:一种是手工定义特征模板后再用CRF++...
  • BiLSTM-CRF模型理解

    千次阅读 2019-10-01 15:24:28
    中文分词、词性标注、命名实体识别是自然语言理解中,基础性的工作,同时也是非常重要的工作。 在很多NLP的项目中,工作开始之前都要经过这三者中的一到多项工作的处理。 在深度学习中,有一种模型可以同时胜任这...
  • 我的主要工作是字母级embedding的生成,这里我采用了为每个词单独生成最后进行拼接的办法,因此我给这一个LSTM的输入是变长的序列(每个单词长度不同)。 CSDN中有人使用固定长度序列作为输入,对于不足的部分用空格...
  • 文章目录参考文献语言模型word2vec循环神经网络:RNNBi-LSTMLSTM长短期记忆,GRUSeq2seq注意力 参考文献 知乎链接 [1]基于深度学习的智能问答系统研究 语言模型 统计语言模型和神经网络语言模型。 N-gram 是统计语言...
  • 在MSRA的简体中文NER语料(我是从这里下载的,非官方出品,可能不是SIGHAN 2006 Bakeoff-3评测所使用的原版语料)上训练NER模型,识别人名、地名和组织机构名。尝试了两种模型:一种是手工定义特征模板后再用CRF++...
  • LSTM生成文本模型 LSTM生成文本模型将模仿诗人Bertrand Russell的写作风格。 同样,考虑到计算能力和运行时效率,LSTM将逐个字符地学习而不是在已发表文章中逐个单词地学习。 所使用的书籍是《神秘主义和逻辑学》...
  • 基于LSTM神经网络模型的电影评论文本情感分类 import re from bs4 import BeautifulSoup import pandas as pd import csv import tensorflow as tf import numpy as np import matplotlib.pyplot as plt tf.device...
  • 长短时记忆网络(Long Short Term Memory Network, LSTM),是一种改进之后的循环神经网络,可以解决RNN无法处理长距离的依赖的问题,目前比较流行。LSTM 通过三个“门”结构来控制不同时刻的状态和输出,分别为:遗忘...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 7,866
精华内容 3,146
关键字:

中文lstm语言模型