精华内容
下载资源
问答
  • Github:Github下载地址 RNN在自然语言处理的文本处理上取得了很大的成功。...在本文的这次分类中,本文使用了IMDB电影评价的数据集,最终的模型可以将正面情感和负面情感通过双向LSTM分类出来。

    前言

    Github:Github下载地址
    RNN在自然语言处理的文本处理上取得了很大的成功。
    双向LSTM可以捕获上下文的内容,从而使得分类效果更佳。
    在本文的这次分类中,本文使用了IMDB电影评价的数据集,最终的模型可以将正面情感和负面情感通过双向LSTM给分类出来。

    实现

    这次的主要的类是我们的Detection。
    这里只贴部分代码进行诠释,更多代码请访问Github
    首先定义超参数大小,比如有词典大小,批次的大小,词向量的维度,隐层点,最大句子长度(我们的数据集小于最大长度的进行补0),输出类别数(正面负面),保存节点数(dropout,防止节点太多过拟合)

    self.num_emb = vocab_size  # vocab size
    self.batch_size = batch_size  # batch size
    self.emb_dim = emb_dim  # dimision of embedding
    self.hidden_dim = hidden_dim  # hidden size
    self.sequence_length = sequence_length  # sequence length
    self.output_dim = 2
    self.output_keep_prob = output_keep_prob #to prevent overfit
    

    定义占用符,其中self.x是句子,self.targets是类别标签,两个维度,[0, 1]代表是正类,[1, 0]代表是负类。
    并且定义词向量。

            with tf.variable_scope("placeholder"):
                self.x = tf.placeholder(shape=[self.batch_size, self.sequence_length], dtype=tf.int32)
                self.targets = tf.placeholder(shape=[self.batch_size, self.output_dim], dtype=tf.int64)
            with tf.variable_scope("embedding"):
                self.g_embeddings = tf.Variable(tf.random_uniform([self.num_emb, self.emb_dim], -1.0, 1.0), name="W_text")
                self.inputs= tf.nn.embedding_lookup(self.g_embeddings, self.x)  # seq_length x batch_size x emb_dim
    

    定义两个lstm,一个是输入的是句子(A, B, C),一个把输入的颠倒的句子(C, B, A)。

            with tf.variable_scope("rnn"):
                cell_bw = tf.contrib.rnn.BasicLSTMCell(self.hidden_dim, state_is_tuple=False)  # single lstm unit
                cell_bw = tf.contrib.rnn.DropoutWrapper(cell_bw, output_keep_prob=self.output_keep_prob)
                cell_fw = tf.contrib.rnn.BasicLSTMCell(self.hidden_dim, state_is_tuple=False)  # single lstm unit
                cell_fw = tf.contrib.rnn.DropoutWrapper(cell_fw, output_keep_prob=self.output_keep_prob)
    

    使用bidirectional_dynamic_rnn将会返回两个lstm的结果,并用一个元组保存([batch_size, seq_length, cell_fw.output_size],[batch_size, seq_length, cell_bw.output_size]),从上可以看出总共有[2, batch_size, seq_length, output_size]的大小,在此先转成[2 * batch_size, seq_length, output_size]大小。
    接下来在此我将各个step(step就是句子在生成每个词时算一步)的隐含层输出进行求和取平均得到[batch_size,output_size]的矩阵,并且最后使用全连接层在经过softmax输出结果。

           with tf.variable_scope("output"):
                self.outputs, self.states = tf.nn.bidirectional_dynamic_rnn(cell_bw, cell_fw, self.inputs, dtype=tf.float32) #[2, batch_size, seq_length, output_size]
                self.outputs = tf.reshape(self.outputs, shape=[-1, seq_length, output_size]) #[2 * batch_size, seq_length, cell.output_size]
                self.outputs = tf.transpose(self.outputs, perm=[1, 0, 2])  # [seq_length, batch_size,output_size]
                self.outputs = tf.reduce_mean(self.outputs, 0) # [batch_size,output_size]
                self.outputs = self.outputs[:self.batch_size] + self.outputs[self.batch_size:]
                self.logits = tf.layers.dense(self.outputs, self.output_dim, name="logits")
                self.prob = tf.nn.softmax(self.logits, name="softmax_output")
    

    最后部分我们采用了交叉熵作为损失函数,并使用Adam优化器进行训练。

    with tf.variable_scope("train"):
                self.loss = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(labels=self.targets, logits=self.logits))
                tvars = tf.trainable_variables()
                max_grad_norm = 5
                # We clip the gradients to prevent explosion
                grads, _ = tf.clip_by_global_norm(tf.gradients(self.loss, tvars), max_grad_norm)
                gradients = list(zip(grads, tvars))
                self.train_op = tf.train.AdamOptimizer(0.005).apply_gradients(gradients)
    
    展开全文
  • 摘要:【从官方案例学框架Keras】双向LSTM解决文本分类任务,训练一个两层的双向LSTM,并在IMDB电影评论数据集上进行情感二分类(pos/neg,正向/负向)

    【从官方案例学框架Tensorflow/Keras】双向LSTM解决文本分类任务

    Keras官方案例链接
    Tensorflow官方案例链接
    Paddle官方案例链接
    Pytorch官方案例链接

    注:本系列仅帮助大家快速理解、学习并能独立使用相关框架进行深度学习的研究,理论部分还请自行学习补充,每个框架的官方经典案例写的都非常好,很值得进行学习使用。可以说在完全理解官方经典案例后加以修改便可以解决大多数常见的相关任务。

    摘要:【从官方案例学框架Keras】双向LSTM解决文本分类任务,训练一个两层的双向LSTM,并在IMDB电影评论数据集上进行情感二分类(pos/neg,正向/负向)


    1 Setup

    导入所需包,和超参数的设置

    import numpy as np
    from tensorflow import keras
    from tensorflow.keras import layers
    
    max_features = 20000  # Only consider the top 20k words
    maxlen = 200  # Only consider the first 200 words of each movie review
    

    2 Load the IMDB movie review sentiment data

    导入训练数据

    • 官方示例
    (x_train, y_train), (x_val, y_val) = keras.datasets.imdb.load_data(
        num_words=max_features
    )
    print(len(x_train), "Training sequences")
    print(len(x_val), "Validation sequences")
    x_train = keras.preprocessing.sequence.pad_sequences(x_train, maxlen=maxlen)
    x_val   = keras.preprocessing.sequence.pad_sequences(x_val, maxlen=maxlen)
    
    • 本地示例
      推荐大家使用本地数据读取方式解决任务,因为使用官方示例只能解决官方数据包里的数据任务,而忽略了通用性解决数据问题的方法,如读取、预处理等操作

    本地数据格式
    /input/
    …/train/
    … …/pos/1.txt
    … …/neg/1.txt
    …/test/
    … …/pos/1.txt
    … …/neg/1.txt

    本例将展示一种DataFrame格式文本读取解决方法,故将其读为DataFrame。
    以上面的文件格式可以使用更简单的方法是tf.keras.preprocessing.text_dataset_from_directory

    import os
    import re
    import string
    import numpy as np
    from tqdm import tqdm
    import pandas as pd
    from tensorflow.keras.layers.experimental.preprocessing import TextVectorization
    import tensorflow as tf
    
    
    def get_text_list_from_files(files):
        text_list = []
        files_name = os.listdir(files)
        for name in tqdm(files_name):
            with open(files+name,encoding='utf-8') as f:
                for line in f:
                    text_list.append(line)
        return text_list
    
    def get_data_from_text_files(folder_name):
        pos_files = "./input/aclImdb/{}/pos/".format(folder_name)
        pos_texts = get_text_list_from_files(pos_files)
        neg_files = "./input/aclImdb/{}/neg/".format(folder_name)
        neg_texts = get_text_list_from_files(neg_files)
        df = pd.DataFrame(
            {
                "review": pos_texts + neg_texts,
                "sentiment": [0] * len(pos_texts) + [1] * len(neg_texts),
            }
        )
        df = df.sample(len(df)).reset_index(drop=True)
        return df
    
    
    train_df = get_data_from_text_files("train")
    test_df = get_data_from_text_files("test")
    all_data = train_df.append(test_df)
    

    custom_standardization:自定义数据预处理
    vectorize_layer:构建词汇表,并将文本映射为词汇表中的索引,同时截断/填充序列长度一致

    def custom_standardization(input_data):
        lowercase = tf.strings.lower(input_data)
        stripped_html = tf.strings.regex_replace(lowercase, "<br />", " ")
        return tf.strings.regex_replace(
            stripped_html, "[%s]" % re.escape(string.punctuation), ""
        )
    
    vectorize_layer = TextVectorization(
        standardize=custom_standardization,
        max_tokens=max_features,
        output_mode="int",
        output_sequence_length=maxlen,
    )
    vectorize_layer.adapt(all_data.review.values.tolist())
    
    def encode(texts):
        encoded_texts = vectorize_layer(texts)
        return encoded_texts.numpy()
    
    x_train = encode(train_df.review.values) 
    y_train = train_df.sentiment.values
    x_val   = encode(test_df.review.values)
    y_val   = test_df.sentiment.values
    

    可以看出词汇表的前三个词分别是用于padding填充序列的""、用于表示未登录词的[UNK]和定冠词the

    vectorize_layer.get_vocabulary()[:10]
    

    在这里插入图片描述

    3 Build the model

    建立两层LSTM模型,需要重点掌握return_sequences
    若不了解的请参照【从官方案例学框架Keras】基于字符LSTM的seq2seq4 Build the model部分进行学习,可以说搞懂return_sequencesreturn_state两个参数就搞懂了Keras中LSTM的使用

    # Input for variable-length sequences of integers
    inputs = keras.Input(shape=(None,), dtype="int32")
    # Embed each integer in a 128-dimensional vector
    x = layers.Embedding(max_features, 128)(inputs)
    # Add 2 bidirectional LSTMs
    x = layers.Bidirectional(layers.LSTM(64, return_sequences=True))(x)
    x = layers.Bidirectional(layers.LSTM(64))(x)
    # Add a classifier
    outputs = layers.Dense(1, activation="sigmoid")(x)
    model = keras.Model(inputs, outputs)
    model.summary()
    

    在这里插入图片描述

    4 Train and evaluate the model

    model.compile("adam", "binary_crossentropy", metrics=["accuracy"])
    model.fit(x_train, y_train, batch_size=32, epochs=2, validation_data=(x_val, y_val))
    

    在这里插入图片描述

    官方示例效果
    在这里插入图片描述

    使用自定义预处理方法,能使同样参数设定下的模型效果(验证集)更好,可以看出是优于官方示例方法的

    5 Summary

    完整代码如下

    import numpy as np
    from tensorflow import keras
    from tensorflow.keras import layers
    import os
    import re
    import string
    from tqdm import tqdm
    import pandas as pd
    from tensorflow.keras.layers.experimental.preprocessing import TextVectorization
    import tensorflow as tf
    
    '''设定超参数'''
    max_features = 20000  # Only consider the top 20k words
    maxlen = 200  # Only consider the first 200 words of each movie review
    
    '''读取数据'''
    def get_text_list_from_files(files):
        text_list = []
        files_name = os.listdir(files)
        for name in tqdm(files_name):
            with open(files+name,encoding='utf-8') as f:
                for line in f:
                    text_list.append(line)
        return text_list
    def get_data_from_text_files(folder_name):
    
        pos_files = "./input/aclImdb/{}/pos/".format(folder_name)
        pos_texts = get_text_list_from_files(pos_files)
        neg_files = "./input/aclImdb/{}/neg/".format(folder_name)
        neg_texts = get_text_list_from_files(neg_files)
        df = pd.DataFrame(
            {
                "review": pos_texts + neg_texts,
                "sentiment": [0] * len(pos_texts) + [1] * len(neg_texts),
            }
        )
        df = df.sample(len(df)).reset_index(drop=True)
        return df
    
    train_df = get_data_from_text_files("train")
    test_df = get_data_from_text_files("test")
    all_data = train_df.append(test_df)
    
    '''数据预处理,并序列化文本'''
    def custom_standardization(input_data):
        lowercase = tf.strings.lower(input_data)
        stripped_html = tf.strings.regex_replace(lowercase, "<br />", " ")
        return tf.strings.regex_replace(
            stripped_html, "[%s]" % re.escape(string.punctuation), ""
        )
    
    vectorize_layer = TextVectorization(
        standardize=custom_standardization,
        max_tokens=max_features,
        output_mode="int",
        output_sequence_length=maxlen,
    )
    vectorize_layer.adapt(all_data.review.values.tolist())
    
    def encode(texts):
        encoded_texts = vectorize_layer(texts)
        return encoded_texts.numpy()
    x_train = encode(train_df.review.values) 
    y_train = train_df.sentiment.values
    x_val   = encode(test_df.review.values)
    y_val   = test_df.sentiment.values
    
    '''定义LSTM模型'''
    # Input for variable-length sequences of integers
    inputs = keras.Input(shape=(None,), dtype="int32")
    # Embed each integer in a 128-dimensional vector
    x = layers.Embedding(max_features, 128)(inputs)
    # Add 2 bidirectional LSTMs
    x = layers.Bidirectional(layers.LSTM(64, return_sequences=True))(x)
    x = layers.Bidirectional(layers.LSTM(64))(x)
    # Add a classifier
    outputs = layers.Dense(1, activation="sigmoid")(x)
    model = keras.Model(inputs, outputs)
    model.summary()
    
    '''模型训练与验证'''
    model.compile("adam", "binary_crossentropy", metrics=["accuracy"])
    model.fit(x_train, y_train, batch_size=32, epochs=2, validation_data=(x_val, y_val))
    
    展开全文
  • LSTM: nn.LSTM(input_size, hidden_size, num_layers=1, nonlinearity=tanh, bias=True, batch_first=False, dropout=0, bidirectional=False)   input_size:表示输入 xt 的特征维度 ...

    LSTM原理请看这:点击进入

    LSTM:

    在这里插入图片描述

    nn.LSTM(input_size, 
    		hidden_size, 
    		num_layers=1, 
    		nonlinearity=tanh, 
    		bias=True, 
    		batch_first=False, 
    		dropout=0, 
    		bidirectional=False)
    


    input_size:表示输入 xt 的特征维度
    hidden_size:表示输出的特征维度
    num_layers:表示网络的层数
    nonlinearity:表示选用的非线性激活函数,默认是 ‘tanh’
    bias:表示是否使用偏置,默认使用
    batch_first:表示输入数据的形式,默认是 False,就是这样形式,(seq, batch, feature),也就是将序列长度放在第一位,batch 放在第二位
    dropout:表示是否在输出层应用 dropout
    bidirectional:表示是否使用双向的 LSTM,默认是 False。

    import  torch
    from    torch import nn
    from    torch.nn import functional as F 
    lstm = nn.LSTM(input_size=10, hidden_size=20, num_layers=2,bidirectional=False)
    # 可理解为一个字串长度为5, batch size为3, 字符维度为10的输入
    input_tensor  = torch.randn(5, 3, 10)
    # 两层LSTM的初始H参数,维度[layers, batch, hidden_len]
     #在lstm中c和h是不一样的,而RNN中是一样的
    h0,c0 = torch.randn(2,3, 20),torch.randn(2,3, 20)
    # output_tensor最后一层所有的h输出, hn表示两层最后一个时序的输出, cn表示两层最后一个时刻的状态的输出
    output_tensor, (hn,cn) =lstm(input_tensor, (h0,c0))
    print(output_tensor.shape, hn.shape,cn.shape)
    

    torch.Size([5, 3, 20]) torch.Size([2, 3, 20]) torch.Size([2, 3, 20])

    从上面可以看到输出的h,x,和输入的h,x维度一致。
    上面的参数中,num_layers=2相当于有两个rnn cell串联,即一个的输出h作为下一个的输入x。也可单独使用两个nn.LSTMCell实现

    而当我们设置成双向LSTM时,即bidirectional=True

    lstm = nn.LSTM(input_size=10, hidden_size=20, num_layers=2,bidirectional=True) 
    h0,c0 = torch.randn(4,3, 20),torch.randn(4,3, 20)
    

    torch.Size([5, 3, 40]) torch.Size([4, 3, 20]) torch.Size([4, 3, 20])

    一共5个时刻,可以看到最后一时刻的output维度是[3, 40],因为nn.LSTM模块他在最后会将正向和反向的结果进行拼接concat。而hn中的4是指正反向,还有因为num_layers是两层所以为4。

    output_tenso只输出最后一层!!!的所有时刻的状态输出(且正向和反向拼接好了。而hn和cn包含所有层,所有方向的最后时刻的输出。

    (🚀具体输出output、h和c的索引是指哪一层和哪一个方向,可以看这个链接解释:PyTorch 多层双向LSTM输入、输出、隐藏状态、权重、偏置的维度分析

    基于LSTM(多层LSTM、双向LSTM只需修改两个参数即可实现)的英文文本分类:

    数据集:英文电影评论(积极、消极)二分类

    分词表是我自己修改了nltk路径:
    C:\用户\AppData\Roaming\nltk_data\corpora\stopwords里的english文件。
      然后你把我的my_english文件放进里面就可以,或者你直接用它的english
      数据集链接和my_english分词表都在以下网盘链接:
    链接:https://pan.baidu.com/s/1vhh5FmU01KqyjRtxByyxcQ
    提取码:bx4k
    关于词嵌入是用了nn.embedding(),它的用法请看这:点击进入

    import torch
    import numpy as np
    import pandas as pd
    import torch.nn as nn
    import torch.optim as optim
    import torch.utils.data as Data
    import torch.nn.functional as F
    

    读入数据集,数据集为电影评论数据(英文,一个24500条数据),分为积极和消极两类:

    df = pd.read_csv('Dataset.csv')
    print('一共有{}条数据'.format(len(df)))
    df.info()
    

    分词去停用词,我的csv文件里已经处理完保存好了分词结果,可以不运行这一部分:

    from nltk.corpus import stopwords
    import nltk
    def separate_sentence(text):
        disease_List = nltk.word_tokenize(text)
        #去除停用词
        filtered = [w for w in disease_List if(w not in stopwords.words('my_english'))]
        #进行词性分析,去掉动词、助词等
        Rfiltered =nltk.pos_tag(filtered)
        #以列表的形式进行返回,列表元素以(词,词性)元组的形式存在
        filter_word = [i[0] for i in Rfiltered]
        return " ".join(filter_word)
    df['sep_review'] = df['review'].apply(lambda x:separate_sentence(x))
    

    根据需要筛选数据(这里我使用了1000条):

    #用xxx条玩玩
    use_df = df[:1000]
    use_df.head(10)
    sentences = list(use_df['sep_review'])
    labels = list(use_df['sentiment'])
    

    小于最大长度的补齐:

    max_seq_len = max(use_df['sep_review'].apply(lambda x: len(x.split())))
    PAD = ' <PAD>'  # 未知字,padding符号用来填充长短不一的句子(用啥符号都行,到时在nn.embedding的参数设为padding_idx=word_to_id['<PAD>'])即可
    
    #小于最大长度的补齐
    for i in range(len(sentences)):
        sen2list = sentences[i].split()
        sentence_len = len(sen2list)
        if sentence_len<max_seq_len:
            sentences[i] += PAD*(max_seq_len-sentence_len)  
    

    制作词表(后面用来给单词编号):

    num_classes = len(set(labels))  # num_classes=2
    word_list = " ".join(sentences).split()
    vocab = list(set(word_list))
    word2idx = {w: i for i, w in enumerate(vocab)}
    vocab_size = len(vocab)
    

    给单词编号(编完号后续还要在embeding层将其转成词向量):

    def make_data(sentences, labels):
        inputs = []
        for sen in sentences:
            inputs.append([word2idx[n] for n in sen.split()])
    
        targets = []
        for out in labels:
            targets.append(out) # To using Torch Softmax Loss function
        return inputs, targets
    
    input_batch, target_batch = make_data(sentences, labels)
    input_batch, target_batch = torch.LongTensor(input_batch), torch.LongTensor(target_batch)
    

    用Data.TensorDataset(torch.utils.data)对给定的tensor数据(样本和标签),将它们包装成dataset,
    然后用Data.DataLoader(torch.utils.data)数据加载器,组合数据集和采样器,并在数据集上提供单进程或多进程迭代器。它可以对我们上面所说的数据集dataset作进一步的设置(比如可以设置打乱,对数据裁剪,设置batch_size等操作,很方便):

    from sklearn.model_selection import train_test_split
    # 划分训练集,测试集
    x_train,x_test,y_train,y_test = train_test_split(input_batch,target_batch,test_size=0.2,random_state = 0)
    
    train_dataset = Data.TensorDataset(torch.tensor(x_train), torch.tensor(y_train))
    test_dataset = Data.TensorDataset(torch.tensor(x_test), torch.tensor(y_test))
    dataset = Data.TensorDataset(input_batch, target_batch)
    
    batch_size = 16
    train_loader = Data.DataLoader(
        dataset=train_dataset,      # 数据,封装进Data.TensorDataset()类的数据
        batch_size=batch_size,      # 每块的大小
        shuffle=True,               # 要不要打乱数据 (打乱比较好)
        num_workers=2,              # 多进程(multiprocess)来读数据
    )
    test_loader = Data.DataLoader(
        dataset=test_dataset,      # 数据,封装进Data.TensorDataset()类的数据
        batch_size=batch_size,      # 每块的大小
        shuffle=True,               # 要不要打乱数据 (打乱比较好)
        num_workers=2,              # 多进程(multiprocess)来读数据
    )
    

    搭建网络:

    device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
    print(device,'能用')
    class LSTM(nn.Module):
        def __init__(self,vocab_size, embedding_dim, hidden_size, num_classes, num_layers,bidirectional):
            super(LSTM, self).__init__()
            self.vocab_size = vocab_size
            self.embedding_dim = embedding_dim
            self.hidden_size = hidden_size
            self.num_classes = num_classes
            self.num_layers = num_layers
            self.bidirectional = bidirectional
            
            self.embedding = nn.Embedding(self.vocab_size, embedding_dim, padding_idx=word2idx['<PAD>'])
            self.lstm = nn.LSTM(input_size=self.embedding_dim, hidden_size=self.hidden_size,batch_first=True,num_layers=self.num_layers,bidirectional=self.bidirectional)
            if self.bidirectional:
                self.fc = nn.Linear(hidden_size*2, num_classes)
            else:
                self.fc = nn.Linear(hidden_size, num_classes)
            
        def forward(self, x):
            batch_size, seq_len = x.shape
            #初始化一个h0,也即c0,在RNN中一个Cell输出的ht和Ct是相同的,而LSTM的一个cell输出的ht和Ct是不同的
            #维度[layers, batch, hidden_len]
            if self.bidirectional:
                h0 = torch.randn(self.num_layers*2, batch_size, self.hidden_size).to(device)
                c0 = torch.randn(self.num_layers*2, batch_size, self.hidden_size).to(device)
            else:
                h0 = torch.randn(self.num_layers, batch_size, self.hidden_size).to(device)
                c0 = torch.randn(self.num_layers, batch_size, self.hidden_size).to(device)
            x = self.embedding(x)
            out,(_,_)= self.lstm(x, (h0,c0))
            output = self.fc(out[:,-1,:]).squeeze(0) #因为有max_seq_len个时态,所以取最后一个时态即-1层
            return output
            
              
    

    实例化网络:
    要实现多层LSTM只需修改参数:num_layers。要实现双向LSTM只需修改参数:bidirectional=True。

    model = LSTM(vocab_size=vocab_size,embedding_dim=300,hidden_size=20,num_classes=2,num_layers=2,bidirectional=True).to(device)
    criterion = nn.CrossEntropyLoss().to(device)
    optimizer = optim.Adam(model.parameters(), lr=1e-3)
    

    模型训练过程:

    model.train()
    for epoch in range(1):
        for batch_x, batch_y in train_loader:
            batch_x, batch_y = batch_x.to(device), batch_y.to(device)
            pred = model(batch_x)
            loss = criterion(pred, batch_y)  #batch_y类标签就好,不用one-hot形式   
            
            if (epoch + 1) % 10 == 0:
                print('Epoch:', '%04d' % (epoch + 1), 'loss =', '{:.6f}'.format(loss))
            optimizer.zero_grad()
            loss.backward()
            optimizer.step()
    
    

    模型测试过程:这里只测试准确率

    test_acc_list = []
    model.eval()
    test_loss = 0
    correct = 0
    with torch.no_grad():
        for data, target in test_loader:
            data, target = data.to(device), target.to(device)
            output = model(data)
            pred = output.max(1, keepdim=True)[1]                           # 找到概率最大的下标
            correct += pred.eq(target.view_as(pred)).sum().item()
    
    # test_loss /= len(test_loader.dataset)
    # test_loss_list.append(test_loss)
    test_acc_list.append(100. * correct / len(test_loader.dataset))
    print('Accuracy: {}/{} ({:.0f}%)\n'.format(correct, len(test_loader.dataset),100. * correct / len(test_loader.dataset)))
    
    展开全文
  • 本篇博客主要介绍基于多层双向LSTM with Attention的文本分类算法的原理及实现细节。 目录 1. 分类原理 2. 实现细节 1. 分类原理 多层双向LSTM with Attention对多层双向LSTM模型做了一些改进,不再单纯地只...

    项目Github地址

    本篇博客主要介绍基于多层双向LSTM with Attention的文本分类算法的原理及实现细节。

    目录

    1. 分类原理

    2. 实现细节


    1. 分类原理

    多层双向LSTM with Attention对多层双向LSTM模型做了一些改进,不再单纯地只利用最后时刻的隐藏状态(两个方向最后时刻隐藏状态拼接)进行分类,而是考虑每个时间步的隐藏状态,对每个时间步的隐藏状态(两个方向拼接)进行加权求和,然后对加权求和结果进行分类。其分类流程如上图所示。

    Attention机制的原理:

    其中,W_w,b_w,u_w都是需要自己设置的权重参数(随模型训练),u_w可以认为是Attention机制中的Query,u_t可以认为是Attention机制中的Key,h_t 是第 t个时间步的隐藏状态(两个方向拼接),同时也是Attention机制中的Value,\alpha_t为每个时间步隐藏状态对应的权重,s是加权求和后得到的特征向量,用于最后接全连接层进行分类。

    对于Attention机制不了解的同学,可以查看我的另一篇博客Attention机制总结。 

    2. 实现细节

    class MulBiLSTM_Atten(BasicModule):#继承自BasicModule 其中封装了保存加载模型的接口,BasicModule继承自nn.Module
    
        def __init__(self,vocab_size,opt):#opt是config类的实例 里面包括所有模型超参数的配置
    
            super(MulBiLSTM_Atten, self).__init__()
            # 嵌入层
            self.embedding = nn.Embedding(vocab_size, opt.embed_size)#词嵌入矩阵 每一行代表词典中一个词对应的词向量;
            # 词嵌入矩阵可以随机初始化连同分类任务一起训练,也可以用预训练词向量初始化(冻结或微调)
    
            #多层双向LSTM 默认seq_len作为第一维 也可以通过batch_first=True 设置batch_size 为第一维
            self.lstm = nn.LSTM(opt.embed_size,opt.recurrent_hidden_size, opt.num_layers,
                                bidirectional=True, batch_first=True, dropout=opt.drop_prop)
    
            self.tanh1 = nn.Tanh()
            self.u = nn.Parameter(torch.Tensor(opt.recurrent_hidden_size * 2, opt.recurrent_hidden_size * 2))
            #定义一个参数(变量) 作为Attention的Query
            self.w = nn.Parameter(torch.Tensor(opt.recurrent_hidden_size*2))
            
            #均匀分布 初始化
            nn.init.uniform_(self.w, -0.1, 0.1)
            nn.init.uniform_(self.u, -0.1, 0.1)
            
            #正态分布 初始化
            #nn.init.normal_(self.w, mean=0, std=0.01)
            self.tanh2 = nn.Tanh()
            #最后的全连接层
            self.content_fc = nn.Sequential(
                nn.Linear(opt.recurrent_hidden_size*2, opt.linear_hidden_size),
                nn.BatchNorm1d(opt.linear_hidden_size),
                nn.ReLU(inplace=True),
                nn.Dropout(opt.drop_prop),
                # 可以再加一个隐层
                # nn.Linear(opt.linear_hidden_size,opt.linear_hidden_size),
                # nn.BatchNorm1d(opt.linear_hidden_size),
                # nn.ReLU(inplace=True),
                # 输出层
                nn.Linear(opt.linear_hidden_size, opt.classes)
            )
    
        def forward(self, inputs):
            #由于batch_first = True 所有inputs不用转换维度
            embeddings = self.embedding(inputs)  # (batch_size, seq_len, embed_size)
    
            outputs,_ = self.lstm(embeddings)   #(batch_size,seq_len,recurrent_hidden_size*2)
    
            #M = self.tanh1(outputs) #(batch_size,seq_len,recurrent_hidden_size*2)
            M = torch.tanh(torch.matmul(outputs, self.u)) #也可以先做一个线性变换 再通过激活函数  作为Key
    
            #M (batch_size,seq_len,recurrent_hidden_size*2) self.w (recurrent_hidden_size*2,)
            #torch.matmul(M, self.w) (batch_size,seq_len) w作为Query与各个隐藏状态(Key) 做内积
            #再对第一维seq_len做softmax 转换为概率分布 (batch_size,seq_len)  得到权重
            alpha = F.softmax(torch.matmul(M, self.w), dim=1).unsqueeze(-1)  # (batch_size,seq_len,1)
    
            #对各个隐藏状态和权重 对应相乘
            out = alpha * outputs #(batch_size,seq_len,recurrent_hidden_size*2)
    
            #对乘积求和 out为加权求和得到的特征向量
            out = torch.sum(out,dim=1) #(batch_size,recurrent_hidden_size*2)
    
            #out = F.relu(out)
    
            out = self.content_fc(out)  #(batch_size,classes)
    
            return out
    

     

    展开全文
  • 文本分类(一) | (6) 多层双向LSTM

    千次阅读 2019-12-21 22:16:48
    本篇博客主要介绍基于多层双向LSTM文本分类算法的原理及实现细节。 目录 1. 分类原理 2. 实现细节 1. 分类原理 对于输入文本序列,在LSTM的每个时间步输入序列中一个单词的嵌入表示,计算当前时间步的隐藏...
  • Pytorch Bert+BiLstm文本分类

    热门讨论 2021-07-18 14:08:01
    昨天按照该文章(自然语言处理(NLP)Bert与Lstm结合)跑bert+bilstm分类的时候,没成功跑起来,于是自己修改了一下,成功运行后,记录在这篇博客中。 一、运行环境 python==3.7 pandas==1.3.0 numpy==1.20.3 ...
  • 【Tensorflow】Bi-LSTM文本分类

    千次阅读 2018-06-07 10:50:01
    用于训练的计算图#训练图 train_graph = tf.Graph() ... #输入文本维度为[time_step,batch_size,embedding_size] encoder_inputs = tf.placeholder(shape=[None,None,input_size], dtype=tf.float3...

空空如也

空空如也

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

双向lstm文本分类