Java最新【NLP学习计划】万字吃透NER_万字 cndn,2024年Java笔试题总

最后

总而言之,面试官问来问去,问的那些Redis知识点也就这么多吧,复习的不够到位,知识点掌握不够熟练,所以面试才会卡壳。将这些Redis面试知识解析以及我整理的一些学习笔记分享出来给大家参考学习

还有更多学习笔记面试资料也分享如下:

都是“Redis惹的祸”,害我差点挂在美团三面,真是“虚惊一场”

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

需要这份系统化的资料的朋友,可以点击这里获取

隐马尔可夫模型描述由一个隐藏的马尔科夫链随机生成不可观测的状态随机序列,再由各个状态生成一个观测而产生观测随机序列的过程。隐马尔可夫模型由初始状态分布,状态转移概率矩阵以及观测概率矩阵所确定。
命名实体识别本质上可以看成是一种序列标注问题,在使用HMM解决命名实体识别这种序列标注问题的时候,我们所能观测到的是字组成的序列(观测序列),观测不到的是每个字对应的标注(状态序列)。

2.1.2 程序代码
import torch


class HMM(object):
    def \_\_init\_\_(self, N, M):
        self.N = N  # N: 状态数,这里对应存在的标注的种类
        self.M = M  # M: 观测数,这里对应有多少不同的字

        # 状态转移概率矩阵
        self.A = torch.zeros(N, N)
        # 观测概率矩阵
        self.B = torch.zeros(N, M)
        # 初始状态概率
        self.Pi = torch.zeros(N)

    def train(self, word_lists, tag_lists, word2id, tag2id):    # word2id: 将字映射为ID; tag2id: 将标注映射为ID
        # 使用极大似然估计的方法来估计隐马尔可夫模型的参数

        # 估计转移概率矩阵
        for tag_list in tag_lists:
            seq_len = len(tag_list)
            for i in range(seq_len - 1):
                current_tagid = tag2id[tag_list[i]]
                next_tagid = tag2id[tag_list[i+1]]
                self.A[current_tagid][next_tagid] += 1

        self.A[self.A == 0.] = 1e-10    # 将未出现元素设置一个小数
        self.A = self.A / self.A.sum(dim=1, keepdim=True)

        # 估计观测概率矩阵
        for tag_list, word_list in zip(tag_lists, word_lists):
            assert len(tag_list) == len(word_list)
            for tag, word in zip(tag_list, word_list):
                tag_id = tag2id[tag]
                word_id = word2id[word]
                self.B[tag_id][word_id] += 1

        self.B[self.B == 0.] = 1e-10
        self.B = self.B / self.B.sum(dim=1, keepdim=True)

        # 估计初始状态概率
        for tag_list in tag_lists:
            init_tagid = tag2id[tag_list[0]]
            self.Pi[init_tagid] += 1

        self.Pi[self.Pi == 0.] = 1e-10
        self.Pi = self.Pi / self.Pi.sum()

    def test(self, word_lists, word2id, tag2id):
        pred_tag_lists = []
        for word_list in word_lists:
            pred_tag_list = self.decoding(word_list, word2id, tag2id)
            pred_tag_lists.append(pred_tag_list)
        return pred_tag_lists

    def decoding(self, word_list, word2id, tag2id):
        """
 使用维特比算法,其本质是用动态规划解隐马尔可夫模型预测问题(求概率最大路径)
 """

        # 对数化防止下溢
        A = torch.log(self.A)
        B = torch.log(self.B)
        Pi = torch.log(self.Pi)

        # 初始化维比特矩阵viterbi
        seq_len = len(word_list)
        viterbi = torch.zeros(self.N, seq_len)

        # backpointer[i,j]: 标注序列的第j个标注为i时,第j-1个标注的id
        backpointer = torch.zeros(self.N, seq_len).long()


        start_wordid = word2id.get(word_list[0], None)
        Bt = B.t()
        if start_wordid is None:    
            bt = torch.log(torch.ones(self.N) / self.N)  # 如果字不再字典里,则假设状态的概率分布是均匀的
        else:
            bt = Bt[start_wordid]                        # 否则从观测概率矩阵中取bt
        viterbi[:, 0] = Pi + bt
        backpointer[:, 0] = -1

        # 递推公式:viterbi[tag\_id, step] = max(viterbi[:, step-1]\* self.A.t()[tag\_id] \* Bt[word])
        for step in range(1, seq_len):
            wordid = word2id.get(word_list[step], None)
            if wordid is None:
               
                bt = torch.log(torch.ones(self.N) / self.N)
            else:
                bt = Bt[wordid]
            for tag_id in range(len(tag2id)):
                max_prob, max_id = torch.max(
                    viterbi[:, step-1] + A[:, tag_id],
                    dim=0
                )
                viterbi[tag_id, step] = max_prob + bt[tag_id]
                backpointer[tag_id, step] = max_id

        # 最优路径的概率
        best_path_prob, best_path_pointer = torch.max(
            viterbi[:, seq_len-1], dim=0
        )

        # 求最优路径
        best_path_pointer = best_path_pointer.item()
        best_path = [best_path_pointer]
        for back_step in range(seq_len-1, 0, -1):
            best_path_pointer = backpointer[best_path_pointer, back_step]
            best_path_pointer = best_path_pointer.item()
            best_path.append(best_path_pointer)

        # 将序列tag\_id转化为tag
        id2tag = dict((id_, tag) for tag, id_ in tag2id.items())
        tag_list = [id2tag[id_] for id_ in reversed(best_path)]

        return tag_list


2.1.3 运行结果

在这里插入图片描述

2.2 Conditional Random Field

2.2.1 算法原理

条件随机场(CRF)是NER目前的主流模型,它的目标函数不仅考虑输入的状态特征函数,而且还包含了标签转移特征函数。CRF为一个位置进行标注的过程中可以利用丰富的内部及上下文特征信息,有效克服了HMM模型面临的问题。

在这里插入图片描述

(图2.2 线性链条件随机场)

2.2.2 程序代码
from sklearn_crfsuite import CRF

# 抽取单个字的特征
def word2features(sent, i):
    word = sent[i]
    prev_word = "<s>" if i == 0 else sent[i-1]
    next_word = "</s>" if i == (len(sent)-1) else sent[i+1]
    features = {
        'w': word,                  # 当前词
        'w-1': prev_word,           # 前一个词
        'w+1': next_word,           # 后一个词
        'w-1:w': prev_word+word,    # 前一个词+当前词
        'w:w+1': word+next_word,    # 当前词+后一个词
        'bias': 1
    }
    return features


# 抽取序列特征
def sent2features(sent):
    return [word2features(sent, i) for i in range(len(sent))]


class CRFModel(object):
    def \_\_init\_\_(self,
                 algorithm='lbfgs',
                 c1=0.1,
                 c2=0.1,
                 max_iterations=100,
                 all_possible_transitions=False
                 ):

        self.model = CRF(algorithm=algorithm,
                         c1=c1,
                         c2=c2,
                         max_iterations=max_iterations,
                         all_possible_transitions=all_possible_transitions)

    def train(self, sentences, tag_lists):
        features = [sent2features(s) for s in sentences]
        self.model.fit(features, tag_lists)

    def test(self, sentences):
        features = [sent2features(s) for s in sentences]
        pred_tag_lists = self.model.predict(features)
        return pred_tag_lists


2.2.3 运行结果

在这里插入图片描述

3 深度学习算法

3.1 Bi-LSTM

3.1.1 算法原理

通过依靠神经网络超强的非线性拟合能力,LSTM在训练时将样本通过高维空间中的复杂非线性变换,学习到从样本到标注的函数,之后使用这个函数为指定的样本预测每个token的标注。
而双向长短期有着比普通LSTM更好的捕捉序列之间的依赖关系的能力,能更好地用于捕捉上下文关系。

在这里插入图片描述

3.1.2 程序代码
import torch
import torch.nn as nn
from torch.nn.utils.rnn import pad_packed_sequence, pack_padded_sequence
from torchinfo import summary
import pickle


class BiLSTM(nn.Module):
    def \_\_init\_\_(self, vocab_size, emb_size, hidden_size, out_size):
        super(BiLSTM, self).__init__()
        self.embedding = nn.Embedding(vocab_size, emb_size)
        self.bilstm = nn.LSTM(emb_size, hidden_size,
                              batch_first=True,
                              bidirectional=True)

        self.lin = nn.Linear(2\*hidden_size, out_size)

    def forward(self, sents_tensor, lengths):
        emb = self.embedding(sents_tensor)
        packed = pack_padded_sequence(emb, lengths, batch_first=True)
        rnn_out, _ = self.bilstm(packed)
        rnn_out, _ = pad_packed_sequence(rnn_out, batch_first=True)

        scores = self.lin(rnn_out) 

        return scores



3.1.3 运行结果

在这里插入图片描述

3.2 Bi-LSTM+CRF

3.2.1 算法原理

Bi-LSTM:

  • 优点是能够根据目标(比如识别实体)自动提取观测序列的特征
  • 缺点是无法学习到状态序列(输出的标注)之间的关系(比如B类标注后面不会再接一个B类标注,而通常接M类标注或E类标注)

CRF:

  • 优点就是能对隐含状态建模,学习状态序列的特点
  • 缺点是需要手动提取序列特征

Bi-LSTM+CRF:在Bi-LSTM后面再加一层CRF,以获得两者的优点

3.2.2 程序代码
from itertools import zip_longest
from copy import deepcopy

import torch
import torch.nn as nn
import torch.optim as optim


class BiLSTM\_CRF(nn.Module):
    def \_\_init\_\_(self, vocab_size, emb_size, hidden_size, out_size):
        super(BiLSTM_CRF, self).__init__()
        self.bilstm = BiLSTM(vocab_size, emb_size, hidden_size, out_size)

        # 转移矩阵,初始化为均匀分布
        self.transition = nn.Parameter(torch.ones(out_size, out_size) \* 1/out_size)


    def forward(self, sents_tensor, lengths):
        emission = self.bilstm(sents_tensor, lengths)

        # 计算CRF scores
        batch_size, max_len, out_size = emission.size()
        crf_scores = emission.unsqueeze(
            2).expand(-1, -1, out_size, -1) + self.transition.unsqueeze(0)

        return crf_scores

    
    def decode(self, test_sents_tensor, lengths, tag2id):
        
        start_id = tag2id['<start>']
        end_id = tag2id['<end>']
        pad = tag2id['<pad>']
        tagset_size = len(tag2id)

        crf_scores = self.forward(test_sents_tensor, lengths)
        device = crf_scores.device
        B, L, T, _ = crf_scores.size()
		
		# 使用维特比算法进行解码
		tagids = viterbi(B, L, T, device)

        return tagids


3.2.3 运行结果

在这里插入图片描述

3.3 Bert+BiLSTM+CRF

3.3.1 算法原理

BERT中蕴含了大量的通用知识,利用预训练好的BERT模型,再用少量的标注数据进行FINETUNE是一种快速的获得效果不错的NER的方法。
因此,通常可以采用Bert+BiLSTM+CRF的模型结构,以期获取更高的预测精度。

在这里插入图片描述

3.3.2 训练模型

了解到kashgari是一个简单而强大的NLP框架,其内包括cnnmodel、blstm模型、cnnlstm模型、avcnnmodel、KMaxnn模型、RCNN模型等序列(文本)分类模型以及cnnlstm模型、blstm模型、BLSTMCRFModel等序列(文本)标签模型,同时提供GPU支持/多GPU支持,因此选择了kashgari库进行Bert模型的预训练以及微调

from kashgari.tasks.labeling import BiLSTM_CRF_Model
import kashgari
from kashgari.embeddings.bert_embedding import  BertEmbedding
import warnings
warnings.filterwarnings('ignore')


bert_embed = BertEmbedding(r'\\chinese\_L-12\_H-768\_A-12',
                           sequence_length=100)

model = BiLSTM_CRF_Model(bert_embed)
model.fit(train_x,
          train_y,
          x_validate=valid_x,
          y_validate=valid_y,
          epochs=5,
          batch_size=512)
model.save('ner.h5')

model.evaluate(test_x, test_y)

3.3.3 训练结果

由于模型训练时间过长(一个epoch需要400s),难以有效地进行fine tune,因此模型的效果并不十分理想。
在这里插入图片描述

下面是模型训练5个epoch的结果,可以发现,模型的精度一直在提高,并没有达到收敛,这说明模型的潜力值得进一步挖掘。
在这里插入图片描述

3.3.4 模型结构

请添加图片描述

4 模型融合

4.1 融合策略

考虑到Bert+BiLSTM+CRF结构由于机器的原因,远未发挥应有的效果,因此只将前4个模型进行融合。
融合策略为:voting

4.2 程序代码

def flatten\_lists(lists):
    flatten_list = []
    for l in lists:
        if type(l) == list:
            flatten_list += l
        else:
            flatten_list.append(l)
    return flatten_list



### 最后

很多程序员,整天沉浸在业务代码的 CRUD 中,业务中没有大量数据做并发,缺少实战经验,对并发仅仅停留在了解,做不到精通,所以总是与大厂擦肩而过。

我把私藏的这套并发体系的笔记和思维脑图分享出来,理论知识与项目实战的结合,我觉得只要你肯花时间用心学完这些,一定可以快速掌握并发编程。

不管是查缺补漏还是深度学习都能有非常不错的成效,需要的话记得帮忙点个赞支持一下

**整理不易,觉得有帮助的朋友可以帮忙点赞分享支持一下小编~**

> **本文已被[CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】](https://bbs.csdn.net/forums/4f45ff00ff254613a03fab5e56a57acb)收录**

**[需要这份系统化的资料的朋友,可以点击这里获取](https://bbs.csdn.net/forums/4f45ff00ff254613a03fab5e56a57acb)**

_list.append(l)
    return flatten_list



### 最后

很多程序员,整天沉浸在业务代码的 CRUD 中,业务中没有大量数据做并发,缺少实战经验,对并发仅仅停留在了解,做不到精通,所以总是与大厂擦肩而过。

我把私藏的这套并发体系的笔记和思维脑图分享出来,理论知识与项目实战的结合,我觉得只要你肯花时间用心学完这些,一定可以快速掌握并发编程。

不管是查缺补漏还是深度学习都能有非常不错的成效,需要的话记得帮忙点个赞支持一下

**整理不易,觉得有帮助的朋友可以帮忙点赞分享支持一下小编~**

> **本文已被[CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】](https://bbs.csdn.net/forums/4f45ff00ff254613a03fab5e56a57acb)收录**

**[需要这份系统化的资料的朋友,可以点击这里获取](https://bbs.csdn.net/forums/4f45ff00ff254613a03fab5e56a57acb)**

  • 4
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值