袁修华代码吴洋作家

BiLSTM先容及代码完成

一、先容

1.1 作品构造

本文简明先容了BiLSTM的基本原理,并以句子级心情分类义务为例先容为什么需求运用LSTM或BiLSTM举行修模。作品的着末,我们给出PyTorch下BiLSTM的完成代码,供读者参考。

1.2 心情分类义务

自然言语处理中心情分类义务是对给定文本举行心情偏向分类的义务,大约来看可以认为其是分类义务中的一类。关于心情分类义务,目前一般的做法是先对词或者抖蒿举行外示,再通过某种组合方法把句子中词的外示组合成句子的外示。着末,应用句子的外示对句子举行心情分类。

举一个对句子举行褒贬二分类的例子。

句子:我爱赛尔

心情标签:褒义

1.3 什么是LSTM和BiLSTM

LSTM的全称是Long Short-Term Memory,它是RNN(Recurrent Neural Network)的一种。LSTM因为其计划的特性,十分适适用于对时序数据的修模,如文本数据。BiLSTM是Bi-directional Long Short-Term Memory的缩写,是由前向LSTM与后向LSTM组合而成。两者自然言语处理义务中都常被用来修模上下文新闻。

1.4 为什么运用LSTM与BiLSTM

将词的外示组合成句子的外示,可以采用相加的方法,即将所有词的外示举行加和,或者取平均等方法,可是这些方法没有思索到词语句子中前后序次。如句子“我不认为他好”。“不”字是对后面“好”的否认,即该句子的心情极性是贬义。运用LSTM模子可以更好的捕捉到较长间隔的依赖联系。因为LSTM通过教练进程可以学到记忆哪些新闻和遗忘哪些新闻。

可是应用LSTM对句子举行修模还保管一个题目:无法编码从后到前的新闻。更细粒度的分类时,如关于强程度的褒义、弱程度的褒义、中性、弱程度的贬义、强程度的贬义的五分类义务需求当心心情词、程度词、否认词之间的交互。举一个例子,“这个餐厅脏得不可,没有隔邻好”,这里的“不可”是对“脏”的程度的一种润饰,通过BiLSTM可以更好的捕捉双向的语义依赖。

二、BiLSTM原理简介

2.1 LSTM先容

2.1.1 总体框架

LSTM模子是由时候的输入词,细胞形态 ,暂时细胞形态,隐层形态遗忘门,记忆门,输出门构成。LSTM的盘算进程可以轮廓为,通过对细胞形态中新闻遗忘和记忆新的新闻使得对后续时候盘算有用的新闻得以转达,而无用的新闻被丢弃,并每个时间步都会输出隐层形态,此中遗忘,记忆与输出由通过上个时候的隐层形态和目今输入盘算出来的遗忘门,记忆门,输出门来掌握。

总体框架如图1所示。

图1. LSTM总体框架

2.1.2 精细先容盘算进程

盘算遗忘门,挑选要遗忘的新闻。

输入:前暂时候的隐层形态,目今时候的输入词

输出:遗忘门的值

图2. 盘算遗忘门

盘算记忆门,挑选要记忆的新闻。

输入:前暂时候的隐层形态,目今时候的输入词

输出:记忆门的值,暂时细胞形态

图3. 盘算记忆门和暂时细胞形态

盘算目今时候细胞形态

输入:记忆门的值遗忘门的值,暂时细胞形态,上一刻细胞形态

输出:目今时候细胞形态

图4. 盘算目今时候细胞形态盘算输出门和目今时候隐层形态

输入:前暂时候的隐层形态,目今时候的输入词 ,目今时候细胞形态

输出:输出门的值,隐层形态

图5. 盘算输出门和目今时候隐层形态

最终,我们可以取得与句子长度相同的隐层形态序列{, , ..., }。

2.2 BiLSTM先容

前向的LSTM与后向的LSTM联合成BiLSTM。比如,我们对“我爱中国”这句话举行编码,模子如图6所示。

图6. 双向LSTM编码句子前向的依次输入“我”,“爱”,“中国”取得三个向量{, , }。后向的依次输入“中国”,“爱”,“我”取得三个向量{, , }。着末将前向和后向的隐向量举行拼接取得{[, ], [, ], [, ]},即{, , }。

关于心情分类义务来说,我们采用的句子的外示往往是[, ]。因为其包罗了前向与后向的所有新闻,如图7所示。

图7. 拼接向量用于心情分类

三、BiLSTM代码完成样例

3.1 模子搭修

运用PyTorch搭修BiLSTM样例代码。代码地址为https://github.com/albertwy/BiLSTM/。

  1. class BLSTM(nn.Module):

  2. """

  3. Implementation of BLSTM Concatenation for sentiment classification task

  4. """

  5. def __init__(self, embeddings, input_dim, hidden_dim, num_layers, output_dim, max_len=40, dropout=0.5):

  6. super(BLSTM, self).__init__()

  7. self.emb = nn.Embedding(num_embeddings=embeddings.size(0),

  8. embedding_dim=embeddings.size(1),

  9. padding_idx=0)

  10. self.emb.weight = nn.Parameter(embeddings)

  11. self.input_dim = input_dim

  12. self.hidden_dim = hidden_dim

  13. self.output_dim = output_dim

  14. # sen encoder

  15. self.sen_len = max_len

  16. self.sen_rnn = nn.LSTM(input_size=input_dim,

  17. hidden_size=hidden_dim,

  18. num_layers=num_layers,

  19. dropout=dropout,

  20. batch_first=True,

  21. bidirectional=True)

  22. self.output = nn.Linear(2 * self.hidden_dim, output_dim)

  23. def bi_fetch(self, rnn_outs, seq_lengths, batch_size, max_len):

  24. rnn_outs = rnn_outs.view(batch_size, max_len, 2, -1)

  25. # (batch_size, max_len, 1, -1)

  26. fw_out = torch.index_select(rnn_outs, 2, Variable(torch.LongTensor([0])).cuda())

  27. fw_out = fw_out.view(batch_size * max_len, -1)

  28. bw_out = torch.index_select(rnn_outs, 2, Variable(torch.LongTensor([1])).cuda())

  29. bw_out = bw_out.view(batch_size * max_len, -1)

  30. batch_range = Variable(torch.LongTensor(range(batch_size))).cuda() * max_len

  31. batch_zeros = Variable(torch.zeros(batch_size).long()).cuda()

  32. fw_index = batch_range + seq_lengths.view(batch_size) - 1

  33. fw_out = torch.index_select(fw_out, 0, fw_index) # (batch_size, hid)

  34. bw_index = batch_range + batch_zeros

  35. bw_out = torch.index_select(bw_out, 0, bw_index)

  36. outs = torch.cat([fw_out, bw_out], dim=1)

  37. return outs

  38. def forward(self, sen_batch, sen_lengths, sen_mask_matrix):

  39. """

  40. :param sen_batch: (batch, sen_length), tensor for sentence sequence

  41. :param sen_lengths:

  42. :param sen_mask_matrix:

  43. :return:

  44. """

  45. ''' Embedding Layer | Padding | Sequence_length 40'''

  46. sen_batch = self.emb(sen_batch)

  47. batch_size = len(sen_batch)

  48. ''' Bi-LSTM Computation '''

  49. sen_outs, _ = self.sen_rnn(sen_batch.view(batch_size, -1, self.input_dim))

  50. sen_rnn = sen_outs.contiguous().view(batch_size, -1, 2 * self.hidden_dim) # (batch, sen_len, 2*hid)

  51. ''' Fetch the truly last hidden layer of both sides

  52. '''

  53. sentence_batch = self.bi_fetch(sen_rnn, sen_lengths, batch_size, self.sen_len) # (batch_size, 2*hid)

  54. representation = sentence_batch

  55. out = self.output(representation)

  56. out_prob = F.softmax(out.view(batch_size, -1))

  57. return out_prob

__init__()函数中对收集举行初始化,设定词向量维度,前向/后向LSTM中隐层向量的维度,另有要分类的种别数等。

bi_fetch()函数的感化是将拼接起来并返回拼接后的向量。因为运用了batch,以是需求运用句子长度用来定位开端padding时前一个时候的输出的隐层向量。

forward()函数里举行前向盘算,取得各个种另外概率值。

3.2 模子教练

  1. def train(model, training_data, args, optimizer, criterion):

  2. model.train()

  3. batch_size = args.batch_size

  4. sentences, sentences_seqlen, sentences_mask, labels = training_data

  5. # print batch_size, len(sentences), len(labels)

  6. assert batch_size == len(sentences) == len(labels)

  7. ''' Prepare data and prediction'''

  8. sentences_, sentences_seqlen_, sentences_mask_ = \

  9. var_batch(args, batch_size, sentences, sentences_seqlen, sentences_mask)

  10. labels_ = Variable(torch.LongTensor(labels))

  11. if args.cuda:

  12. labels_ = labels_.cuda()

  13. assert len(sentences) == len(labels)

  14. model.zero_grad()

  15. probs = model(sentences_, sentences_seqlen_, sentences_mask_)

  16. loss = criterion(probs.view(len(labels_), -1), labels_)

  17. loss.backward()

  18. optimizer.step()

代码中training_data是一个batch的数据,此中包罗输入的句子sentences(句子中每个词以词下标外示),输入句子的长度sentences_seqlen,输入的句子对应的心情种别labels。 教练模子前,先清空遗留的梯度值,再依据该batch数据盘算出来的梯度举行更新模子。

  1. model.zero_grad()

  2. probs = model(sentences_, sentences_seqlen_, sentences_mask_)

  3. loss = criterion(probs.view(len(labels_), -1), labels_)

  4. loss.backward()

  5. optimizer.step()

3.3 模子测试

以下是举行模子测试的代码。

  1. def test(model, dataset, args, data_part="test"):

  2. """

  3. :param model:

  4. :param args:

  5. :param dataset:

  6. :param data_part:

  7. :return:

  8. """

  9. tvt_set = dataset[data_part]

  10. tvt_set = yutils.YDataset(tvt_set["xIndexes"],

  11. tvt_set["yLabels"],

  12. to_pad=True, max_len=args.sen_max_len)

  13. test_set = tvt_set

  14. sentences, sentences_seqlen, sentences_mask, labels = test_set.next_batch(len(test_set))

  15. assert len(test_set) == len(sentences) == len(labels)

  16. tic = time.time()

  17. model.eval()

  18. ''' Prepare data and prediction'''

  19. batch_size = len(sentences)

  20. sentences_, sentences_seqlen_, sentences_mask_ = \

  21. var_batch(args, batch_size, sentences, sentences_seqlen, sentences_mask)

  22. probs = model(sentences_, sentences_seqlen_, sentences_mask_)

  23. _, pred = torch.max(probs, dim=1)

  24. if args.cuda:

  25. pred = pred.view(-1).cpu().data.numpy()

  26. else:

  27. pred = pred.view(-1).data.numpy()

  28. tit = time.time() - tic

  29. print " Predicting {:d} examples using {:5.4f} seconds".format(len(test_set), tit)

  30. labels = numpy.asarray(labels)

  31. ''' log and return prf scores '''

  32. accuracy = test_prf(pred, labels)

  33. return accuracy

  1. def cal_prf(pred, right, gold, formation=True, metric_type=""):

  2. """

  3. :param pred: predicted labels

  4. :param right: predicting right labels

  5. :param gold: gold labels

  6. :param formation: whether format the float to 6 digits

  7. :param metric_type:

  8. :return: prf for each label

  9. """

  10. num_class = len(pred)

  11. precision = [0.0] * num_class

  12. recall = [0.0] * num_class

  13. f1_score = [0.0] * num_class

  14. for i in xrange(num_class):

  15. ''' cal precision for each class: right / predict '''

  16. precision[i] = 0 if pred[i] == 0 else 1.0 * right[i] / pred[i]

  17. ''' cal recall for each class: right / gold '''

  18. recall[i] = 0 if gold[i] == 0 else 1.0 * right[i] / gold[i]

  19. ''' cal recall for each class: 2 pr / (p+r) '''

  20. f1_score[i] = 0 if precision[i] == 0 or recall[i] == 0 \

  21. else 2.0 * (precision[i] * recall[i]) / (precision[i] + recall[i])

  22. if formation:

  23. precision[i] = precision[i].__format__(".6f")

  24. recall[i] = recall[i].__format__(".6f")

  25. f1_score[i] = f1_score[i].__format__(".6f")

  26. ''' PRF for each label or PRF for all labels '''

  27. if metric_type == "macro":

  28. precision = sum(precision) / len(precision)

  29. recall = sum(recall) / len(recall)

  30. f1_score = 2 * precision * recall / (precision + recall) if (precision + recall) > 0 else 0

  31. elif metric_type == "micro":

  32. precision = 1.0 * sum(right) / sum(pred) if sum(pred) > 0 else 0

  33. recall = 1.0 * sum(right) / sum(gold) if sum(recall) > 0 else 0

  34. f1_score = 2 * precision * recall / (precision + recall) if (precision + recall) > 0 else 0

  35. return precision, recall, f1_score

四、总结

本文中,我们联合心情分类义务先容了LSTM以及BiLSTM的基本原理,并给出一个BiLSTM样例代码。除了心情分类义务,LSTM与BiLSTM自然言语处理范畴的其它义务上也取得了广泛运用,如板滞翻译义务中运用其举行源言语的编码和目标言语的解码,板滞阅读了解义务中运用其对作品和题目的编码等。

五、参考材料

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

哈工大SCIR
哈工大SCIR

哈尔滨工业大学社会盘算与新闻检索研讨中心

工程代码PyTorch自然言语处理心情分类LSTM
302
相关数据
张量技能

张量是一个可用来外示少许矢量、标量和其他张量之间的线性联系的众线性函数,这些线性联系的基本例子有内积、外积、线性映照以及笛卡儿积。其坐标 维空间内,有 个分量的一种量,此中每个分量都是坐标的函数,而坐标变换时,这些分量也按照某些规矩作线性变换。称为该张量的秩或阶(与矩阵的秩和阶均无联系)。 数学里,张量是一种几何实体,或者说广义上的“数目”。张量看法包罗标量、矢量和线性算子。张量可以用坐标系统来外达,记作标量的数组,但它是定义为“不依赖于参照系的挑选的”。张量物理和工程学中很主要。比如扩散张量成像中,外达器官关于水的各个偏向的微分透性的张量可以用来发生大脑的扫描图。工程上最主要的例子可以便是应力张量和应变张量了,它们都是二阶张量,关于一般线性材料他们之间的联系由一个四阶弹性张量来决议。

板滞翻译技能

板滞翻译(MT)是应用板滞的力气「主动将一种自然言语(源言语)的文本翻译成另一种言语(目标言语)」。板滞翻译方法一般可分成三大类:基于规矩的板滞翻译(RBMT)、统计板滞翻译(SMT)和神经板滞翻译(NMT)。

神经收集技能

(人工)神经收集是一种根源于 20 世纪 50 年代的监视式板滞进修模子,那时分研讨者念象了「感知器(perceptron)」的念法。这一范畴的研讨者一般被称为「勾结主义者(Connectionist)」,因为这种模子模拟了人脑的功用。神经收集模子一般是通过反向传达算法运用梯度下降教练的。目前神经收集有两大主要类型,它们都是前馈神经收集:卷积神经收集(CNN)和轮回神经收集(RNN),此中 RNN 又包罗好坏期记忆(LSTM)、门控轮回单位(GRU)等等。深度进修是一种主要运用于神经收集帮帮其取得更好结果的技能。尽管神经收集主要用于监视进修,但也有少许为无监视进修计划的变体,比如主动编码器和生成对立收集(GAN)。

自然言语处理技能

自然言语处理(英语:natural language processing,缩写作 NLP)是人工智能和言语学范畴的分支学科。此范畴议论如那处理及运用自然言语;自然言语认知则是指让电脑“懂”人类的言语。自然言语生成系统把盘算机数据转化为自然言语。自然言语了解系统把自然言语转化为盘算机顺序更易于处理的方式。

好坏期记忆收集技能

好坏期记忆(Long Short-Term Memory) 是具有恒久记忆才能的一种时间递归神经收集(Recurrent Neural Network)。 其收集构造含有一个或众个具有可遗忘和记忆功用的单位构成。它1997年被提出用于办理古板RNN(Recurrent Neural Network) 的随时间反向传达中权重消逝的题目(vanishing gradient problem over backpropagation-through-time),主要构成部分包罗Forget Gate, Input Gate, 和 Output Gate, 区分认真决议目今输入是否被采用,是否被恒久记忆以及决议记忆中的输入是否目今被输出。Gated Recurrent Unit 是 LSTM 浩繁版本中典范的一个。因为它具有记忆性的功用,LSTM常常被器具有时间序列特征的数据和场景中。

遗忘门技能

LSTM或GRU中特有的机制

引荐作品
你好 请问您这个代码是什么状况下运转的 用的python和pytorch的版天职布是什么呢 还需求安装其它库吗 假如我念运转本人的中文数据集上应当怎样办呢
真的,讲解的很好,可是必定是之前对神经收集言语模子有必定的了解和根底,我关于双向BiLSTM的图不停很纠结该怎样画,这个真的很好的解答了我的题目!好文、好博客,感谢博主!