NLP入门——RNN、LSTM模型的搭建、训练与预测

在卷积语言模型建模时,我们选取上下文长度ctx_len进行训练,预测时选取句子的最后ctx_len个分词做预测,这样句子的前0~seql-1-ctx_len个词对于预测没有任何帮助,这对于语言处理来说显然是不利的。
在词袋语言模型建模时,我们舍弃ctx_len的概念,利用前缀和,用前seql-1个词作为训练数据,后seql-1个词作为标签训练。最终用这句话所有的分词求前缀和做预测,这样句子中所有的分词都参与了预测,是更准确的。
但是,词袋语言模型没有考虑到分词的顺序带来的影响,例如:0 1 2 -> 3,将0 1 2装入词袋、张量求前缀和的结果和 0 2 1、 1 0 2、 1 2 0等等没有区别,没有办法区分分词的顺序。

在此基础上,如果考虑分词间的前后次序对预测的影响,有:
h ( x t ) = C e l l ( h ( x ( t − 1 ) + x t ) h(x_t) = Cell(h(x(t-1) + x_t) h(xt)=Cell(h(x(t1)+xt)
这样类似于二叉树结构顺序输入的模型,输入的不同次序带来的结果也不同。
在这里插入图片描述
如上图所示的二叉树结构,输入X_0 、X_1和X_2次序的不同,对于每层的影响也不同。这样就有效的保留了原句中分词间次序对于预测的影响。

RNN模型的搭建

#RNNLM.py
#encoding: utf-8
import torch
from torch import nn

class RNNCell(nn.Module):
    def __init__(self, isize, hsize, osize, dropout, **kwargs):
    
        super(RNNCell, self,).__init__()   ##调用父类的初始化函数
        self.net = nn.Sequential(nn.Linear(isize + osize, hsize),
        nn.ReLU(inplace=True), #设置relu激活函数,inplace=True在原始张量上进行
        nn.Dropout(p=dropout, inplace=False),#设置丢弃率防止过拟合,同时创建一个新的张量
        nn.Linear(hsize, osize, bias=False))       
    
    # x_t:(bsize, isize)
    def forward(self, x_t, h_p): # x_t为当前词向量,h_p是h_x(t-1)即上一步的结果
        
        return self.net(torch.cat([h_p, x_t], dim=-1))
    
    
    
class RNNLayer(nn.Module):
    def __init__(self, isize, hsize, dropout,norm_residual=True, **kwargs):
        super(RNNLayer, self,).__init__()   ##调用父类的初始化函数
        self.init_hx = nn.Parameter(torch.zeros(1, isize)) #h_0即最初初始化状态
        #初始化时第一维加1维,便于后续的拼接
        self.cell = RNNCell(isize, hsize, isize, dropout)
        self.drop = nn.Dropout(p=dropout, inplace=True)
        self.normer = nn.LayerNorm(isize) #做归一化
        self.norm_residual = norm_residual #设置变量存储做判断
        
    # input: (bsize, seql-1, isize)
    def forward(self, input, hx=None):#hx是None说明在训练,hx不是None是Tensor说明在解码
                                      #解码时候就用前一步的hx隐状态
        
        _ = self.normer(input) #稳定之后的结果 
        bsize = input.size(0)   #存一下bsize
        _hx = hx if isinstance(hx, torch.Tensor) else self.init_hx.expand(bsize, -1)
        #如果hx已经是一个张量不是None说明已开始计算,否则用初始化的值
        #expand函数将init_hx:(1,isize) -> (bsize, isize),设置参数为-1表示大小保留原状
        rs = []
        for xu in input.unbind(1): #将input在第一维展开 xu:(bsize, isize)
            _hx = self.cell(xu, _hx) #xu为新分词,hx为前一步隐状态
            rs.append(_hx)
            
        rs = (_ if self.norm_residual else input) + self.drop(torch.stack(rs, dim=1))
        #将rs在指定维度拼起来:rs:(bsize, seq-1, isize)
        #如果参数初始化做的好,就用LayerNorm后的值,否则用原始值
        if hx is None:
            return rs
        else:
            return (rs, _hx)   

class NNLM(nn.Module):
    
    def __init__(self, vcb_size, isize, hsize, dropout,
    nlayer, bindemb=True, **kwargs): #有多少个词,就分多少类,类别数为vcb_size
        super(NNLM, self).__init__()
        self.emb = nn.Embedding(vcb_size, isize,
        padding_idx=0)                #<pad>的索引为0
        #self.comp = nn.Linear(ctx_len * isize, isize, bias=False) #将4个词的向量降维为isize
        self.drop = nn.Dropout(p=dropout, inplace=True) #embedding后dropout
        self.nets = nn.Sequential(*[RNNLayer(isize, hsize, dropout)
        for _ in range(nlayer)])
        self.classifier = nn.Linear(isize, vcb_size)
        if bindemb:
            self.classifier.weight = self.emb.weight#将emb的权重赋给分类器
        self.normer = nn.LayerNorm(isize)
        self.out_normer = nn.LayerNorm(isize)
                
    # input: (bsize, seql-1) 句数,句长-1 由于最后一个词是预测不作为输入
    def forward(self, input):
        
        out = self.emb(input)
        # out: (bsize, seql-1, isize)   
        out = self.drop(out) 
        out = self.normer(out) #使用归一化,使模长均匀
        out = self.out_normer(self.nets(out))
        return self.classifier(out) #分类产生参数

RNN模型的训练

#RNNtrain.py
#encoding: utf-8

import torch
from torch import nn
from RNNLM import NNLM #导入模型
from h5py import File as h5File #读训练数据
from math import sqrt
from random import shuffle #使输入数据乱序,使模型更均衡
from lrsch import SqrtDecayLR
from tqdm import tqdm

train_data = "train.h5"#之前已经张量转文本的h5文件
isize = 64              
hsize = isize * 2       #设置初始参数
dropout = 0.3           #设置丢弃率
nlayer = 4              #设置层数
gpu_id = -1             #设置是否使用gpu
lr = 1e-3               #设置初始学习率
max_run = 8           #设置训练轮数

nreport = 5000          #每训练5000个batch打印一次
tokens_optm = 25000     #设置更新参数的词数阈值

def init_model_parameters(modin): #初始化模型参数
    with torch.no_grad():         #不是训练不用求导
        for para in modin.parameters():
            if para.dim() > 1:          #若维度大于1,说明是权重参数
                _ = 1.0 / sqrt(para.size(-1))
                para.uniform_(-_,_)     #均匀分布初始化
        for _m in modin.modules():      #遍历所有小模型
            if isinstance(_m, nn.Linear):#如果小模型是linear类型
                if _m.bias is not None: #初始化bias
                    _m.bias.zero_()
                elif isinstance(_m, nn.LayerNorm):#初始化LayerNorm参数
                    _m.weight.fill_(1.0)
                    _m.bias.zero_()
                elif isinstance(_m, nn.Embedding): #如果是嵌入层则进入初始化权重矩阵,将<pad>的向量初始化为零向量
                    if _m.padding_idx >= 0:
                        _m.weight[_m.padding_idx].zero_()
    return modin

def train(train_data, tl, model, lossf, optm, cuda_device,
 nreport=nreport, tokens_optm=tokens_optm):#nreport为每训练一部分词打一次epoch
    
    model.train() #设置模型在训练的模式
    src_grp = train_data["src"] #从输入数据中取出句子
    _l = 0.0  #_l用来存当前loss
    _t = 0    #_t用来存句子数
    _lb = 0.0
    _tb = 0
    _tom = 0
    
    for _i, _id in tqdm(enumerate(tl, 1)):
        seq_batch = torch.from_numpy(src_grp[_id][()])
        #seq_batch:[bsize, seql]
        _seqlen = seq_batch.size(-1)  #取出每个batch的句长

        if cuda_device is not None:
            seq_batch = seq_batch.to(cuda_device, non_blocking=True)
             #将数据放在同一gpu上
        seq_batch = seq_batch.long()   #数据转换为long类型
        seq_i = seq_batch.narrow(1, 0, _seqlen - 1) #训练数据读取前seql-1的数据
        #seq_i:[bsize, seql-1]
        seq_o = seq_batch.narrow(1, 1, _seqlen - 1) #预测数据读取后seql-1的数据做标签
        #seq_o:[bsize, seql-1]
        
        
        out = model(seq_i)          #获得模型结果
        #out: {bsize, seql-1, vcb_size} vcb_size即预测类别数
        
        loss = lossf(out.view(-1, out.size(-1)), seq_o.contiguous().view(-1)) 
        #转换out维度为[bsize*(seql-1),vcb_size],seq_o:[bsize*(seql-1)]
        _lossv = loss.item()
        _l += _lossv        #整个训练集的loss
        _lb += _lossv       #每个batch的loss
        _n = seq_o.ne(0).int().sum().item() #seq_o中不是<pad>的位置的数量
        _t += _n            #整个训练集的分词数
        _tb += _n           #每个batch的分词数
        _tom += _n
        loss.backward()                 #反向传播求导
        if _tom > tokens_optm:          #当词数大于时更新参数
            optm.step()                     #参数的更新
            optm.zero_grad(set_to_none=True)#参数更新后清空梯度
            _tom = 0
        if _i % nreport == 0:   #每训练5000个batch打印一次
            print("Average loss over %d tokens: %.2f"%(_tb, _lb/_tb))
            _lb = 0.0
            _tb = 0
            save_model(model, "checkpoint.rnn.pt") #暂存检查点模型
            
        
    return _l / _t #返回总的loss


def save_model(modin, fname): #保存模型所有内容 权重、偏移、优化
    
    torch.save({name: para.cpu() for name, para in
    model.named_parameters()}, fname)

t_data = h5File(train_data, "r")#以读的方式打开训练数据

vcb_size = t_data["nword"][()].tolist()[0] #将返回的numpy的ndarray转为list
#在我们的h5文件中存储了nword: 总词数

model = NNLM(vcb_size, isize, hsize, dropout, nlayer)
model = init_model_parameters(model) #在cpu上初始化模型
lossf = nn.CrossEntropyLoss(reduction='sum', ignore_index=0,
label_smoothing=0.1)
#设置ignore_index=0,即忽略<pad>的影响

if (gpu_id >= 0) and torch.cuda.is_available(): #如果使用gpu且设备支持cuda
    cuda_device = torch.device("cuda", gpu_id)  #配置gpu
    torch.set_default_device(cuda_device)
else:
    cuda_device = None

if cuda_device is not None:                     #如果要用gpu
    model.to(cuda_device)                       #将模型和损失函数放在gpu上
    lossf.to(cuda_device)

optm = torch.optim.Adam(model.parameters(), lr=lr, 
betas=(0.9, 0.98), eps=1e-08)
#使用model.parameters()返回模型所有参数,
lrm = SqrtDecayLR(optm, lr) #将优化器和初始学习率传入

tl = [str(_) for _ in range(t_data["ndata"][()].item())] #获得字符串构成的训练数据的list

#save_model(model, "eva.rnn.pt")
for i in range(1, max_run + 1):
    shuffle(tl)         #使数据乱序
    _tloss = train(t_data, tl, model, lossf, optm,
    cuda_device)  #获取每轮训练的损失
    print("Epoch %d: train loss %.2f"%(i, _tloss)) #打印日志
    save_model(model, "eva.rnn.pt")
    lrm.step() #每轮训练后更新学习率
    
t_data.close()

在命令行输入:

:~/nlp/lm$ python RNNtrain.py 

RNN模型的解码与预测

模型的解码

#RNNLM.py
#encoding: utf-8
 # input: (bsize, seql)
    def decode(self, input, maxlen=50): #设置最多生成50个分词
        
        rs = [input]
        bsize =input.size(0)
     
        hxd = {} #存每一层产生的hx
        out = self.normer(self.drop(self.emb(input)))
        # out:(bsize, seql, isize)
        for i, layer in enumerate(self.nets):
            out, hxd[i] = layer(out, hx=hxd.get(i, "init")) #手动遍历每一层
            #得到了每层的结果rs以及隐状态hxd[i]
        # out:(bsize, seql, isize)
        out = self.classifier(self.out_normer(out.narrow(
        1, out.size(1) - 1, 1))).argmax(-1)
        # narrow:(bsize, 1, isize) -> (bsize, 1, vcb_size) 
        # -> (bsize, 1)
        rs.append(out)
        done_trans = out.squeeze(1).eq(2)   #记录是否完成生成
        # done_trans:(bsize)
        
        if not done_trans.all().item():
            for i in range(maxlen - 1): #已经生成一个词并添加到rs中了
                
                out = self.normer(self.drop(self.emb(out)))
                # out:(bsize, 1, isize)
                for _, layer in enumerate(self.nets): #将前一层结果给下一层做输入
                    out, hxd[_] = layer(out, hx=hxd[_])
                    #将之前的隐状态输入生成下一层的隐状态
                
                out = self.classifier(self.out_normer(out)).argmax(-1) #最后的输出预测下一个分词
                # out:(bsize, 1, vcb_size) -> (bsize, 1)
                rs.append(out) #将新预测分词添加到结果

                done_trans |= out.squeeze(1).eq(2) 
                if done_trans.all().item(): #当全都为True,说明此batch中所有句子预测都为<eos>,即解码完成
                    break
        
        return torch.cat(rs, dim=1)

模型的预测

预测脚本与前面的词袋模型脚本基本一样,我们在命令行中输入:

:~/nlp/lm$ python RNNpredict.py test.h5 zh.vcb pred.txt checkpoint.rnn.pt 
:~/nlp/lm$ less pred.txt 

在这里插入图片描述
我们可以看到经过若干个epoch训练出的rnn模型对于文本的预测续写。

LSTM模型的搭建

在这里插入图片描述

但是,由于上图这样的结构,h_3是由h_2和X_2得出,而h_2是由h_1和X_1得出。那么对于h_3来说,X_2的影响就要比X_1大很多。于是在进行预测时,句末的分词对预测的影响就要大于句子中间或者开头的分词。
而在实际句子中,

回顾 安理会 主席 以 安理会 名义 在 1994 年 4 月 7 日 发表 的 声明 ( S / PRST / 1994 / 16 ) 和 在 1994 年 4 月 30 日 发表 的 声明 ( S / PRST / 1994 / 21 ) ,

上句中“声明”的预测很依赖于动词“发表”,这符合我们的模型的偏好即句末的分词影响更大。但是像本句句尾的“1994”是该年发表声明的时间,显然前面的“1994”对它影响更大,而不是句末的分词。
这就不符合我们模型的偏好,因此我们需要重新设计cell,使得模型的偏好更均衡,而不是句末的分词影响最大。

sigmoid激活函数

S i g m o i d Sigmoid Sigmoid 函数的公式如下所示,其可以将神经网络的输出映射到(0,1)之中,公式如下所示。
s i g m o i d ( x ) = 1 1 + e − x sigmoid(x)=\frac{1}{1+e^{-x}} sigmoid(x)=1+ex1
函数图像如下:
在这里插入图片描述
如图上图所示,当输入的值趋于正无穷或负无穷时,梯度会趋近零,神经网络学习不到特征,从而导致深度神经网络无法进行训练。

>>> import torch
>>> a=torch.randn(4,5)
>>> a.sigmoid()
tensor([[0.4421, 0.6359, 0.5781, 0.4000, 0.4741],
        [0.4456, 0.4710, 0.0643, 0.3690, 0.4005],
        [0.4287, 0.7616, 0.6746, 0.1963, 0.6259],
        [0.4268, 0.8297, 0.4883, 0.4696, 0.6682]])
>>> b=a.exp()
>>> b/(1+b)
tensor([[0.4421, 0.6359, 0.5781, 0.4000, 0.4741],
        [0.4456, 0.4710, 0.0643, 0.3690, 0.4005],
        [0.4287, 0.7616, 0.6746, 0.1963, 0.6259],
        [0.4268, 0.8297, 0.4883, 0.4696, 0.6682]])

我们利用sigmoid函数来使得学习的权重进行变化,通过0或1的变化有助于更新或忘记信息。
我们通过门来控制变化,要么是1则记住,要么是0则忘掉。因记忆能力有限,记住重要的,忘记无关紧要的。
LSTM的基本结构如下:

h t , c t = L S T M C e l l ( x t , h t − 1 , c t − 1 ) h_t, c_t = LSTMCell(x_t, h_{t-1}, c_{t-1}) htct=LSTMCell(xt,ht1,ct1)
h = x t ∣ h t − 1 _h = x_t | h_{t-1} h=xtht1
h i d d e n = a c t ( L i n e a r ( h ) ) hidden=act(Linear(_h)) hidden=act(Linear(h))
i g a t e = s i g m o i d ( L i n e a r ( h ) ) igate=sigmoid(Linear(_h)) igate=sigmoid(Linear(h))
o g a t e = s i g m o i d ( L i n e a r ( h ) ) ogate=sigmoid(Linear(_h)) ogate=sigmoid(Linear(h))
f g a t e = s i g m o i d ( L i n e a r ( h ) ) fgate=sigmoid(Linear(_h)) fgate=sigmoid(Linear(h))
c t = c t − 1 ∗ f g a t e + h i d d e n ∗ i g a t e c_t=c_{t-1}*fgate+hidden*igate ct=ct1fgate+hiddenigate
h t = c t ∗ o g a t e h_t=c_t*ogate ht=ctogate

#LSTMLM.py
#encoding: utf-8
import torch
from torch import nn

class LSTMCell(nn.Module):
    def __init__(self, isize, osize, dropout, **kwargs):
    
        super(LSTMCell, self,).__init__()   
        self.net = nn.Linear(isize + osize, 4 * osize) #4*osize后分块,分别得到hidden、igate、ogate和fgate
        self.drop = nn.Dropout(p=dropout, inplace=False)
        
    def forward(self, x_t, h_p):
        
        hx, cx = h_p
        out = self.net(torch.cat([hx, x_t], dim=-1))#将h_x和x_t向量拼接
    
        # out:(bsize, 4, osize)
        out = out.view(out.size(0), 4, -1)
        hidden = out.select(1, 0).relu() #取out第1维的第0个张量过relu激活函数
        igate, fgate, ogate = out.narrow(1, 1, 3).sigmoid().unbind(1) 
        #取out第1维的1,2,3个张量过sigmoid激活函数再按照第一维拆分
        #i/f/o gate:(bsize, 1, osize) hidden(bsize, 1, osize)
        cx = (cx * fgate).addcmul(hidden, igate) #cx*fgate+hidden*igate
        hx = cx * ogate
        
        return hx, cx #hx是本层输出,cx是向后传的cell
        
class LSTMLayer(nn.Module):
    def __init__(self, isize, dropout,norm_residual=True, **kwargs):
        super(LSTMLayer, self,).__init__()   
        self.init_hx = nn.Parameter(torch.zeros(1, isize))
        self.init_cx = nn.Parameter(torch.zeros(1, isize))
        self.cell = LSTMCell(isize, isize, dropout)
        self.drop = nn.Dropout(p=dropout, inplace=True)
        self.normer = nn.LayerNorm(isize) #做归一化
        self.norm_residual = norm_residual #设置变量存储做判断
        
    # input: (bsize, seql-1, isize)
    def forward(self, input, hx=None): #hx若不为None则为(hx, cx)
        
        _ = self.normer(input) #稳定之后的结果 
        bsize = input.size(0)
        _hx = hx if isinstance(hx, (tuple, list)) else (self.
        init_hx.expand(bsize, -1), self.init_cx.expand(bsize, -1))
        rs = []
        for xu in input.unbind(1):
            _hx = self.cell(xu, _hx)
            rs.append(_hx[0]) #只添加_hx中的hx
        rs = (_ if self.norm_residual else input) + self.drop(torch.stack(rs, dim=1))
        
        if hx is None:
            return rs
        else:
            return (rs, _hx)
        #如果参数初始化做的好,就用LayerNorm后的值,否则用原始值

class NNLM(nn.Module):
    
    def __init__(self, vcb_size, isize, hsize, dropout,
    nlayer, bindemb=True, **kwargs): #有多少个词,就分多少类,类别数为vcb_size
        super(NNLM, self).__init__()
        self.emb = nn.Embedding(vcb_size, isize,
        padding_idx=0)                #<pad>的索引为0
        #self.comp = nn.Linear(ctx_len * isize, isize, bias=False) #将4个词的向量降维为isize
        self.drop = nn.Dropout(p=dropout, inplace=True) #embedding后dropout
        self.nets = nn.Sequential(*[LSTMLayer(isize, dropout)
        for _ in range(nlayer)])
        self.classifier = nn.Linear(isize, vcb_size)
        if bindemb:
            self.classifier.weight = self.emb.weight#将emb的权重赋给分类器
        self.normer = nn.LayerNorm(isize)
        self.out_normer = nn.LayerNorm(isize)
        
        
    # input: (bsize, seql-1) 句数,句长-1 由于最后一个词是预测不作为输入
    def forward(self, input):
        
        out = self.emb(input)
        # out: (bsize, seql-1, isize)   
        out = self.drop(out) 
        out = self.normer(out) #使用归一化,使模长均匀
        out = self.out_normer(self.nets(out))
        return self.classifier(out) #分类产生参数
    
    # input: (bsize, seql)
    def decode(self, input, maxlen=50): #设置最多生成50个分词
        
        rs = [input]
        bsize =input.size(0)
     
        hxd = {}
        out = self.normer(self.drop(self.emb(input)))
        # out:(bsize, seql, isize)
        for i, layer in enumerate(self.nets):
            out, hxd[i] = layer(out, hx=hxd.get(i, "init"))
        out = self.classifier(self.out_normer(out.narrow(
        1, out.size(1) - 1, 1))).argmax(-1)
        # narrow:(bsize, 1, isize) -> (bsize, 1, vcb_size) 
        # -> (bsize, 1)
        rs.append(out)
        done_trans = out.squeeze(1).eq(2)   #记录是否完成生成
        # done_trans:(bsize)
        
        if not done_trans.all().item():
            for i in range(maxlen - 1):
                
                out = self.normer(self.drop(self.emb(out)))
                # out:(bsize, 1, isize)
                for _, layer in enumerate(self.nets):
                    out, hxd[_] = layer(out, hx=hxd[_])
                out = self.classifier(self.out_normer(out)).argmax(-1)
                # out:(bsize, 1, vcb_size) -> (bsize, 1)
                rs.append(out)

                done_trans |= out.squeeze(1).eq(2) 
                if done_trans.all().item(): #当全都为True,说明此batch中所有句子预测都为<eos>,即解码完成
                    break
        
        return torch.cat(rs, dim=1)

LSTM模型的预测

我们可以看出,LSTM的解码与RNN一样,只是在cell的设计上不同,训练脚本与预测脚本都是相同的,我们在命令行输入:

:~/nlp/lm$ python LSTMpredict.py test.h5 zh.vcb pred.txt checkpoint.lstm.pt 
:~/nlp/lm$ less pred.txt 

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/802185.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

C语言 底层逻辑详细阐述指针(一)万字讲解 #指针是什么? #指针和指针类型 #指针的解引用 #野指针 #指针的运算 #指针和数组 #二级指针 #指针数组

文章目录 前言 序1&#xff1a;什么是内存&#xff1f; 序2&#xff1a;地址是怎么产生的&#xff1f; 一、指针是什么 1、指针变量的创建及其意义&#xff1a; 2、指针变量的大小 二、指针的解引用 三、指针类型存在的意义 四、野指针 1、什么是野指针 2、野指针的成因 a、指…

【HarmonyOS】关于鸿蒙消息推送的心得体会 (一)

【HarmonyOS】关于鸿蒙消息推送的心得体会&#xff08;一&#xff09; 前言 这几天调研了鸿蒙消息推送的实现方式&#xff0c;形成了开发设计方案&#xff0c;颇有体会&#xff0c;与各位分享。 虽然没做之前觉得很简单的小功能&#xff0c;貌似只需要和华为服务器通信&…

玩转HarmonyOS NEXT之AppStorage应用全局UI状态存储

概述 AppStorage是应用全局的UI状态存储&#xff0c;是和应用的进程绑定的&#xff0c;由UI框架在应用程序启动时创建&#xff0c;为应用程序UI状态属性提供中央存储。 AppStorage是在应用启动的时候会被创建的单例。它的目的是为了提供应用状态数据的中心存储&#xff0c;这…

【HarmonyOS学习】Calendar Kit日历管理

简介 Calendar Kit提供日历与日程管理能力&#xff0c;包括日历的获取和日程的创建能力。 Calendar Kit为用户提供了一系列接口来获取日历账户&#xff0c;并使用特定的接口向日历账户中写入日程。 如果写入的日程带有提醒时间则系统会在时间到达时向用户发送提醒。 约束点…

Linux编程(通信协议---udp)

UDP&#xff08;用户数据报协议&#xff09;是一种无连接的网络协议&#xff0c;主要用于快速传输数据。以下是UDP协议的一些主要特点&#xff1a; 1. **无连接**&#xff1a;UDP是无连接的协议&#xff0c;这意味着在数据传输之前不需要建立连接。每个UDP数据包都是独立的&am…

remote: ERROR: commit b81ea84: missing Change-Id in message footer

首次拉取代码后,在本地已经编辑添加了代码并且想要提交到远端仓库 git add . git commit 当commit之后想要pull的时候报错了 git pull 执行到git pull 时出现这个问题,这是由于Change-Id没了,提示: ! [remote rejected] HEAD -> refs/for/master (commit b81ea84: mis…

git回退分支版本git reset --hard HEAD

git回退分支版本git reset --hard HEAD git reset --hard HEAD 上面命令清除本地所有修改&#xff0c;与下面相似&#xff1a; git reset --hard origin/master 等同于&#xff1a; git reset --hard HEAD~0 说明&#xff1a; HEAD 当前版本 HEAD^ 上一个版本 HEAD^^ 上上…

JVM---对象是否存活及被引用的状态

1.如何判断对象是否存活 1.1 引用计数算法 概念&#xff1a;在对象头部增加一个引用计数器,每当有一个地方引用它时&#xff0c;计数器值就加一&#xff1b;当引用失效时&#xff0c;计数器值就减一&#xff1b;任何时刻计数器为零的对象就是不可能再被使用的。 优点&#xff1…

LabVIEW学习-LabVIEW储存Excel表格

上述实现了将格式化的时间和正弦波的频率振幅相位以及正弦波数据输入到excel表格中。 下面介绍其中使用到的函数&#xff1a; 1. 所在位置&#xff0c;函数选板->定时->获取日期/时间(秒) 2. 将获取的时间进行格式化处理&#xff0c;输出格式化的日期/时间字符串。 函…

AI赋能基础设施巡检,技术革新助力水泥建筑缺陷检测分析,基于YOLOv8模型开发构建水泥建筑场景下裂缝缺陷分割检测识别系统

在现代化城市建设的宏伟蓝图中&#xff0c;公路、隧道、桥梁、大坝等水泥类基础设施如同城市的血脉&#xff0c;支撑着社会的正常运转与经济的蓬勃发展。然而&#xff0c;时间的侵蚀与自然的考验使得这些建筑不可避免地面临老化与损坏的问题&#xff0c;裂缝作为其中最为常见的…

AV1 编码标准环路滤波和后处理技术概述

AV1 环路滤波 去块滤波器 在视频编码的环路滤波管道中&#xff0c;去块滤波器&#xff08;deblocking filter&#xff09;用于减少量化引起的变换块边界处的块状伪影。 总结&#xff1a; 去块滤波器的应用&#xff1a; 对于亮度&#xff08;luma&#xff09;色度分量&#xff…

minIO集成springboot

问题 minIO与spring集成。 步骤 创建桶 创建key 找到创建账号页面&#xff0c;如下图&#xff1a; 点击创建&#xff0c;如下图&#xff1a; 设置如下权限&#xff1a; {"Version": "2012-10-17","Statement": [{"Effect": &q…

华为OD算法题汇总

60、计算网络信号 题目 网络信号经过传递会逐层衰减&#xff0c;且遇到阻隔物无法直接穿透&#xff0c;在此情况下需要计算某个位置的网络信号值。注意:网络信号可以绕过阻隔物 array[m][n]&#xff0c;二维数组代表网格地图 array[i][j]0&#xff0c;代表i行j列是空旷位置 a…

如何在所有docker命令前加上一个sudo

如果当前登录用户不是root不用&#xff0c;使用docker命令的时候&#xff0c;需要在前面加上一个sudo 提升权限。 但是每次都加&#xff0c;就感觉特别的麻烦&#xff0c;如何简化呢&#xff1f; 解决办法 打开你的shell配置文件&#xff08;例如&#xff0c;如果你使用bash&am…

Spring Cloud Eureka快读入门Demo

1.什么是Eureka&#xff1f; Eureka 由 Netflix 开发&#xff0c;是一种基于REST&#xff08;Representational State Transfer&#xff09;的服务&#xff0c;用于定位服务&#xff08;服务注册与发现&#xff09;&#xff0c;以实现中间层服务的负载均衡和故障转移&#xff…

C语言 | Leetcode C语言题解之第239题滑动窗口最大值

题目&#xff1a; 题解&#xff1a; int* maxSlidingWindow(int* nums, int numsSize, int k, int* returnSize) {int prefixMax[numsSize], suffixMax[numsSize];for (int i 0; i < numsSize; i) {if (i % k 0) {prefixMax[i] nums[i];} else {prefixMax[i] fmax(pref…

甄选范文“论软件维护方法及其应用”软考高级论文,系统架构设计师论文

论文真题 软件维护是指在软件交付使用后,直至软件被淘汰的整个时间范围内,为了改正错误或满足 新的需求而修改软件的活动。在软件系统运行过程中,软件需要维护的原因是多种多样的, 根据维护的原因不同,可以将软件维护分为改正性维护、适应性维护、完善性维护和预防性 维护…

Mindspore框架CycleGAN模型实现图像风格迁移|(三)损失函数计算

Mindspore框架&#xff1a;CycleGAN模型实现图像风格迁移算法 Mindspore框架CycleGAN模型实现图像风格迁移|&#xff08;一&#xff09;CycleGAN神经网络模型构建 Mindspore框架CycleGAN模型实现图像风格迁移|&#xff08;二&#xff09;实例数据集&#xff08;苹果2橘子&…

JAVA 异步编程(异步,线程,线程池)一

目录 1.概念 1.1 线程和进程的区别 1.2 线程的五种状态 1.3 单线程,多线程,线程池 1.4 异步与多线程的概念 2. 实现异步的方式 2.1 方式1 裸线程&#xff08;Thread&#xff09; 2.1 方式2 线程池&#xff08;Executor&#xff09; 2.1.1 源码分析 2.1.2 线程池创建…

新的“SCALE”软件允许为 AMD GPU 原生编译 CUDA 应用程序

虽然已经有各种努力&#xff0c;如HIPIFY来帮助将CUDA源代码转换为AMD GPU的可移植C代码&#xff0c;然后是之前AMD资助的ZLUDA&#xff0c;允许CUDA二进制文件通过CUDA库的直接替代品在AMD GPU上运行&#xff0c;但有一个新的竞争者&#xff1a;SCALE。SCALE现在作为GPGPU工具…