LSTM 长短期记忆递归神经网络

1、神经网络简介

1.1 神经网络起源

人工神经网络(Aritificial Neural Networks, ANN)是一种仿生的网络结构,起源于对人类大脑的研究。人工神经网络(Aritificial Neural Networks)也常被简称为神经网络(Neural Networks, NN),基本思想是通过大量简单的神经元之间的相互连接来构造复杂的网络结构,信号(数据)可以在这些神经元之间传递,通过激活不同的神经元和对传递的信号进行加权来使得信号被放大或衰减,经过多次的传递来改变信号的强度和表现形式。

1.2 传统神经网络原理

传统神经网络结构示意图:

在这里插入图片描述

其中:

  • 输入层:可以包含多个神经元,可以接收多维的信号输入(特征信息);
  • 输出层:可以包含多个神经元,可以输出多维信号;
  • 隐含层:可以包含多个神经网络层,每一层包含多个神经元。

每层的神经元与上一层神经元和下一层神经元连接(类似生物神经元的突触),这些连接通路用于信号传递。每个神经元接收来自上一层的信号输入,使用一定的加和规则将所有的信号输入汇聚到一起,并使用激活函数将输入信号激活为输出信号,再将信号传递到下一层。

影响神经网络表现能力的主要因素有神经网络的层数、神经元的个数、神经元之间的连接方式以及神经元所采用的激活函数。神经元之间以不同的连接方式(全连接、部分连接)组合,可以构成不同神经网络,对于不同的信号处理效果也不一样。

神经网络优点很明显,给我们提供了构建模型的便利,你大可不用顾及模型本身是如何作用的,只需要按照规则构建网络,然后使用训练数据集不断调整参数,在许多问题上都能得到一个比较“能接受”的结果,然而我们对其中发生了什么是未可知的。在深度学习领域,许多问题都可以通过构建深层的神经网络模型来解决。

1.3 传统神经网络结构的缺陷

从传统的神经网络结构我们可以看出,信号流从输入层到输出层依次流过,同一层级的神经元之间,信号是不会相互传递的。这样就会导致一个问题,输出信号只与输入信号有关,而与输入信号的先后顺序无关。并且神经元本身也不具有存储信息的能力,整个网络也就没有“记忆”能力,当输入信号是一个跟时间相关的信号时,如果我们想要通过这段信号的“上下文”信息来理解一段时间序列的意思,传统的神经网络结构就显得无力了。与我们人类的理解过程类似,我们听到一句话时往往需要通过这句话中词语出现的顺序以及我们之前所学的关于这些词语的意思来理解整段话的意思,而不是简单的通过其中的几个词语来理解。

例如,在自然语言处理领域,我们要让神经网络理解这样一句话:“地球上最高的山是珠穆朗玛峰”,按照传统的神经网络结构,它可能会将这句话拆分为几个单独的词(地球、上、最高的、山、是、珠穆朗玛峰),分别输入到模型之中,而不管这几个词之间的顺序。然而,直观上我们可以看到,这几个词出现的顺序是与最终这句话要表达的意思是密切相关的,但传统的神经网络结构无法处理这种情况。

因此,我们需要构建具有“记忆”能力的神经网络模型,用来处理需要理解上下文意思的信号,也就是时间序列数据。循环神经网络(RNN)就是用来处理这类信号的,RNN之所以能够有效的处理时间序列数据,主要是基于它比较特殊的运行原理。

2 循环神经网络RNN

2.1 RNN 的构造过程

RNN是一种特殊的神经网路结构,其本身是包含循环的网络,允许信息在神经元之间传递:

在这里插入图片描述

图示是一个 RNN 结构示意图,图中的 A A A 表示神经网络模型, X t X_t Xt 表示模型的输入信号, h t h_t ht 表示模型的输出信号。如果去掉 A 的输出信号传递到 A 的箭头,这个网络模型与普通的神经网络结构相同。 A 的输出信号传递到 A 的箭头,允许 A 将信息传递给 A,神经网络将自己的输出作为输入。

在输入信号是一个时间序列信号的时候,输入信号和时间 t 相关。在 t 时刻,输入信号 X t X_t Xt 作为神经网络 A 的输入, A 的输出分流成两部分,一部分输出给 h t h_t ht ,一部分作为一个隐藏信号流被输入到 A 中,在下一时刻输入信号 X t + 1 X_{t+1} Xt+1 时,这部分隐藏分信号流也被作为输入信号输入到了 A 中。此时神经网络 A 就同时接受了 t 时刻和 t+1 时刻的信号输入了,此时的输出信号又将被传递到下一时刻的 A 中,把上面那个图根据时间 t 展开:

在这里插入图片描述

如上图所示, t = 0 t=0 t=0 时刻的输出给 t = 1 t=1 t=1 时刻的模型 A 作为输入, t = 1 t=1 t=1 时刻的输出给 t = 2 t=2 t=2 时刻的模型 A 作为 输入……。相当于 RNN 在时间序列上把自己复制了很多遍,每个模型都对应一个时刻的输入,并且当前时刻的输出还作为下一时刻的模型的输入信号。

这样链式的结构揭示了RNN本质上是与序列相关的,是对于时间序列数据最自然的神经网络架构。并且理论上,RNN可以保留以前任意时刻的信息。

2.2 RNN 的局限性

RNN利用了神经网络的“内部循环”来保留时间序列的上下文信息,可以使用过去的信号数据来推测对当前信号的理解,这是非常重要的进步,并且理论上RNN可以保留过去任意时刻的信息。但实际使用RNN时往往遇到问题。

假如我们构造了一个语言模型,可以通过当前这一句话的意思来预测下一个词语。现在有这样一句话:“我是一个中国人,出生在普通家庭,我最常说汉语,也喜欢写汉字。我喜欢妈妈做的菜”。我们的语言模型在预测“我最常说汉语”的“汉语”这个词时,它要预测“我最长说”这后面可能跟的是一个语言,可能是英语,也可能是汉语,那么它需要用到第一句话的“我是中国人”这段话的意思来推测我最常说汉语,而不是英语、法语等。而在预测“我喜欢妈妈做的菜”的最后的词“菜”时并不需要“我是中国人”这个信息以及其他的信息,它跟我是不是一个中国人没有必然的关系。

在这个例子中可以看出,想要精确地处理时间序列,有时候我们只需要用到最近的时刻的信息。例如预测“我喜欢妈妈做的菜”最后这个词“菜”,此时信息传递是这样的:

在这里插入图片描述

“菜”这个词与“我”、“喜欢”、“妈妈”、“做”、“的”这几个词关联性比较大,距离也比较近,所以可以直接利用这几个词进行最后那个词语的推测。

有时候我们又需要用到很早以前时刻的信息,例如预测“我最常说汉语”最后的这个词“汉语”。此时信息传递是这样的:

在这里插入图片描述

此时,我们要预测“汉语”这个词,仅仅依靠“我”、“最”、“常”、“说”这几个词还不能得出我说的是汉语,必须要追溯到更早的句子“我是一个中国人”,由“中国人”这个词语来推测我最常说的是汉语。因此,这种情况下,我们想要推测“汉语”这个词的时候就比前面那个预测“菜”这个词所用到的信息就处于更早的时刻。

RNN虽然在理论上可以保留所有历史时刻的信息,但在实际使用时,信息的传递往往会因为时间间隔太长而逐渐衰减,传递一段时刻以后其信息的作用效果就大大降低了。因此,普通RNN对于信息的长期依赖问题没有很好的处理办法。

为了克服这个问题,Hochreiter等人在1997年改进了RNN,提出了一种特殊的RNN模型——LSTM网络,可以学习长期依赖信息,在后面的20多年被改良和得到了广泛的应用,并且取得了极大的成功。

3 长短时间记忆网络(LSTM)

3.1 LSTM 与 RNN 的关系

长短期记忆(Long Short Term Memory,LSTM)网络是一种特殊的RNN模型,其特殊的结构设计使得它可以避免长期依赖问题,记住很早时刻的信息是LSTM的默认行为,而不需要专门为此付出很大代价。

普通的RNN模型中,其重复神经网络模块的链式模型如下图所示,这个重复的模块只有一个非常简单的结构,一个单一的神经网络层(例如tanh层),这样就会导致信息的处理能力比较低。

在这里插入图片描述

LSTM在此基础上将这个结构改进了,不再是单一的神经网络层,而是4个,并且以一种特殊的方式进行交互。

在这里插入图片描述

图中的模块分为以下几种:

在这里插入图片描述

  • 黄色方块:表示一个神经网络层(Neural Network Layer);
  • 粉色圆圈:表示按位操作或逐点操作(pointwise operation),例如向量加和、向量乘积等;
  • 单箭头:表示信号传递(向量传递);
  • 合流箭头:表示两个信号的连接(向量拼接);
  • 分流箭头:表示信号被复制后传递到2个不同的地方。
3.2 LSTM 基本思想

LSTM的关键是细胞状态(直译:cell state),表示为 C t C_t Ct,用来保存当前LSTM的状态信息并传递到下一时刻的LSTM中,也就是RNN中那根“自循环”的箭头。当前的LSTM接收来自上一个时刻的细胞状态 C t − 1 C_{t-1} Ct1,并与当前LSTM接收的信号输入 x t x_t xt,共同作用产生当前LSTM的细胞状态 C t C_t Ct

在这里插入图片描述

在LSTM中,采用专门设计的“门”来引入或者去除细胞状态 C t C_t Ct 中的信息。门是一种让信息选择性通过的方法。有的门跟信号处理中的滤波器有点类似,允许信号部分通过或者通过时被门加工了;有的门也跟数字电路中的逻辑门类似,允许信号通过或者不通过。这里所采用的门包含一个 sigmoid 神经网络层和一个按位的乘法操作,如下图所示:

在这里插入图片描述

其中黄色方块表示sigmoid神经网络层,粉色圆圈表示按位乘法操作。sigmoid神经网络层可以将输入信号转换为 0 到 1 之间的数值,用来描述有多少量的输入信号可以通过。0 表示“不允许任何量通过”,1 表示“允许所有量通过”。sigmoid神经网络层起到类似下图的sigmoid函数所示的作用:

在这里插入图片描述

其中,横轴表示输入信号,纵轴表示经过sigmoid函数以后的输出信号。

LSTM主要包括三个不同的门结构:遗忘门、记忆门和输出门。这三个门用来控制LSTM的信息保留和传递,最终反映到细胞状态 C t C_t Ct 和输出信号 h t h_t ht,如下图所示:

在这里插入图片描述

图中标示了LSTM中各个门的构成情况和相互之间的关系,其中:

  • 遗忘门由一个sigmoid神经网络层和一个按位乘操作构成;
  • 记忆门由输入门(input gate)与tanh神经网络层和一个按位乘操作构成;
  • 输出门(output gate)与 tanh 函数(注意:这里不是 tanh 神经网络层)以及按位乘操作共同作用将细胞状态和输入信号传递到输出端。
3.3 遗忘门

顾名思义,遗忘门的作用就是用来“忘记”信息的。在LSTM的使用过程中,有一些信息不是必要的,因此遗忘门的作用就是用来选择这些信息并“忘记”它们。遗忘门决定了细胞状态 C t − 1 C_{t-1} Ct1 中的哪些信息将被遗忘。

在这里插入图片描述

  • 左边高亮的结构就是遗忘门,包含一个sigmoid神经网络层(黄色方框,神经网络参数为 W f , b f W_f, b_f Wf,bf
  • 接收 t 时刻的输入信号 x t x_t xt t − 1 t-1 t1 时刻LSTM的上一个输出信号 h t − 1 h_{t-1} ht1,这两个信号进行拼接以后共同输入到sigmoid神经网络层中,然后输出信号 f t f_t ft f t f_t ft 是一个 0 到1之间的数值,并与 C t − 1 C_{t-1} Ct1 相乘来决定 C t − 1 C_{t-1} Ct1 中的哪些信息将被保留,哪些信息将被舍弃。

假设 C t − 1 = [ 0.5 , 0.6 , 0.4 ] , h t − 1 = [ 0.3 , 0.8 , 0.69 ] , x t = [ 0.2 , 1.3 , 0.7 ] C_{t-1} = [0.5, 0.6, 0.4], h_{t-1} = [0.3, 0.8, 0.69], x_t = [0.2, 1.3, 0.7] Ct1=[0.5,0.6,0.4],ht1=[0.3,0.8,0.69],xt=[0.2,1.3,0.7],遗忘门的输入信号就是 h t − 1 h_{t-1} ht1 x t x_t xt 的组合,即 [ t t − 1 , x t ] = [ 0.3 , 0.8 , 0.69 , 0.2 , 1.3 , 0.7 ] [t_{t-1}, x_t] = [0.3, 0.8, 0.69, 0.2, 1.3, 0.7] [tt1,xt]=[0.3,0.8,0.69,0.2,1.3,0.7],然后通过 sigmoid 神经网络层输出每一个元素都处于0到1之间的向量 f t = [ 0.5 , 0.1 , 0.8 ] f_t = [0.5, 0.1, 0.8] ft=[0.5,0.1,0.8]。此时 f t f_t ft 是一个与 C t − 1 C_{t-1} Ct1 维数相同的向量,此处为3维。

这里需要注意的是,这里的 simgmoid 是 sigmoid 神经网络层 ,而不是 sigmoid 激活函数,因此输入信号是6维的向量,而 f t f_t ft 可以是3维的。

3.4 记忆门

记忆门的作用与遗忘门相反,它将决定新输入的信息 x t x_t xt h t − 1 h_{t-1} ht1 中哪些信息将被保留。

在这里插入图片描述

如上图所示,记忆门包含2个部分。第一个是包含sigmoid神经网络层(输入门,神经网络网络参数为 W i , b i W_i, b_i Wi,bi)和一个 tanh 神经网络层(神经网络参数为 W c , b c W_c, b_c Wc,bc)。

  • sigmoid 神经网络层的作用很明显,跟遗忘门一样,它接收 x t x_t xt h t − 1 h_{t-1} ht1 作为输入,然后输出一个 0 到 1 之间的数值 i t i_t it 来决定哪些信息需要被更新;
  • Tanh神经网络层的作用是将输入的 x t x_t xt h t − 1 h_{t-1} ht1 整合,然后通过一个tanh神经网络层来创建一个新的状态候选向量 C t ~ \tilde{C_t} Ct~ C t ~ \tilde{C_t} Ct~ 的值范围在 -1 到 1 之间。

记忆门的输出由上述两个神经网络层的输出决定, i t i_t it C t ~ \tilde{C_t} Ct~ 相乘来选择哪些信息将被新加入到 t 时刻的细胞状态 C t C_t Ct 中。

3.5 更新细胞状态

有了遗忘门和记忆门,就可以更新细胞状态 C t C_t Ct 了。

在这里插入图片描述

这里将遗忘门的输出 f t f_t ft 与上一时刻的细胞状态 C t − 1 C_{t-1} Ct1 相乘来选择遗忘和保留一些信息,将记忆门的输出与从遗忘门选择后的信息加和得到新的细胞状态 C t C_t Ct。这表示 t 时刻的细胞状态 C t C_t Ct 包含了此时需要丢弃的 t-1 时刻传递的信息和 t 时刻从输入信号获取的需要新加入的信息 i t ⋅ C t ~ i_t \cdot \tilde{C_t} itCt~ C t C_t Ct 将继续传递到 t+1 时刻的LSTM网络中,作为新的细胞状态传递下去。

3.6 输出门

在这里插入图片描述

如上面左图所示,输出门就是将t-1时刻传递过来并经过了前面遗忘门与记忆门选择后的细胞状态 C t − 1 C_{t-1} Ct1,与 t-1 时刻的输出信号 h t − 1 h_{t-1} ht1 和 t 时刻的输入信号 x t x_t xt 整合到一起作为当前时刻的输出信号。

整合的过程如上图所示, x t x_t xt h t − 1 h_{t-1} ht1 经过一个sigmoid神经网络层(神经网络参数为 W o , b o W_o, b_o Wo,bo)输出一个 0 到 1 之间的数值 o t o_t ot C t C_t Ct 经过一个tanh函数(注意:这里不是 tanh 神经网络层)得到一个在 -1 到 1 之间的数值,并与 o t o_t ot 相乘得到输出信号 h t h_t ht,同时 h t h_t ht 也作为下一个时刻的输入信号传递到下一阶段。

其中,tanh 函数是激活函数的一种,函数图像为:

在这里插入图片描述

3.7 LSTM 的一些变体
3.7.1 在门上增加窥视孔

在这里插入图片描述

这是2000年Gers和Schemidhuber教授提出的一种LSTM变体。图中,在传统的LSTM结构基础上,每个门(遗忘门、记忆门和输出门)增加了一个“窥视孔”(Peephole),也可以在使用时选择只对部分门加入窥视孔。

3.7.2 整合遗忘门和输入门

在这里插入图片描述

与传统的LSTM不同的是,这个变体不需要分开来确定要被遗忘和记住的信息,采用一个结构搞定。在遗忘门的输出信号值(0 到 1之间)上,用 1 减去该数值来作为记忆门的状态选择,表示只更新需要被遗忘的那些信息的状态。

3.7.3 GRU

改进比较大的一个LSTM变体叫Gated Recurrent Unit (GRU),目前应用较多。结构图如下

在这里插入图片描述

GRU主要包含2个门:重置门和更新门。GRU混合了细胞状态 C t C_t Ct 和隐藏状态 h t − 1 h_{t-1} ht1 为一个新的状态,使用 h t h_t ht 来表示。 该模型比传统的标准LSTM模型简单。

4 基于Pytorch的LSTM代码实现

使用正弦函数和余弦函数来构造时间序列,而正余弦函数之间是成导数关系,所以我们可以构造模型来学习正弦函数与余弦函数之间的映射关系,通过输入正弦函数的值来预测对应的余弦函数的值。

每一个正弦函数的值都对应一个余弦函数值。如果只关心正弦函数的值本身而不考虑当前值所在的时间,那么正弦函数值和余弦函数值不是一一对应关系。例如,当 t=2.5 和 t=6.8 时,sin(t)=0.5 ,但在这两个不同的时刻,cos(t) 的值却不一样,也就是说如果不考虑时间,同一个正弦函数值可能对应了不同的几个余弦函数值。对于传统的神经网络来说,它仅仅基于当前的输入来预测输出,对于这种同一个输入可能对应多个输出的情况不再适用。

取正弦函数的值作为LSTM的输入,来预测余弦函数的值。基于Pytorch来构建LSTM模型,采用1个输入神经元,1个输出神经元,16个隐藏神经元作为LSTM网络的构成参数,平均绝对误差(LMSE)作为损失误差,使用Adam优化算法来训练LSTM神经网络。

# -*- coding:UTF-8 -*-
import numpy as np
import torch
from torch import nn
import matplotlib.pyplot as plt# Define LSTM Neural Networks
class LstmRNN(nn.Module):"""Parameters:- input_size: feature size- hidden_size: number of hidden units- output_size: number of output- num_layers: layers of LSTM to stack"""def __init__(self, input_size, hidden_size=1, output_size=1, num_layers=1):super().__init__()self.lstm = nn.LSTM(input_size, hidden_size, num_layers) # utilize the LSTM model in torch.nn self.forwardCalculation = nn.Linear(hidden_size, output_size)def forward(self, _x):x, _ = self.lstm(_x)  # _x is input, size (seq_len, batch, input_size)s, b, h = x.shape  # x is output, size (seq_len, batch, hidden_size)x = x.view(s*b, h)x = self.forwardCalculation(x)x = x.view(s, b, -1)return xif __name__ == '__main__':# create databasedata_len = 200t = np.linspace(0, 12*np.pi, data_len)sin_t = np.sin(t)cos_t = np.cos(t)dataset = np.zeros((data_len, 2))dataset[:,0] = sin_tdataset[:,1] = cos_tdataset = dataset.astype('float32')# plot part of the original datasetplt.figure()plt.plot(t[0:60], dataset[0:60,0], label='sin(t)')plt.plot(t[0:60], dataset[0:60,1], label = 'cos(t)')plt.plot([2.5, 2.5], [-1.3, 0.55], 'r--', label='t = 2.5') # t = 2.5plt.plot([6.8, 6.8], [-1.3, 0.85], 'm--', label='t = 6.8') # t = 6.8plt.xlabel('t')plt.ylim(-1.2, 1.2)plt.ylabel('sin(t) and cos(t)')plt.legend(loc='upper right')# choose dataset for training and testingtrain_data_ratio = 0.5 # Choose 80% of the data for testingtrain_data_len = int(data_len*train_data_ratio)train_x = dataset[:train_data_len, 0]train_y = dataset[:train_data_len, 1]INPUT_FEATURES_NUM = 1OUTPUT_FEATURES_NUM = 1t_for_training = t[:train_data_len]# test_x = train_x# test_y = train_ytest_x = dataset[train_data_len:, 0]test_y = dataset[train_data_len:, 1]t_for_testing = t[train_data_len:]# ----------------- train -------------------train_x_tensor = train_x.reshape(-1, 5, INPUT_FEATURES_NUM) # set batch size to 5train_y_tensor = train_y.reshape(-1, 5, OUTPUT_FEATURES_NUM) # set batch size to 5# transfer data to pytorch tensortrain_x_tensor = torch.from_numpy(train_x_tensor)train_y_tensor = torch.from_numpy(train_y_tensor)# test_x_tensor = torch.from_numpy(test_x)lstm_model = LstmRNN(INPUT_FEATURES_NUM, 16, output_size=OUTPUT_FEATURES_NUM, num_layers=1) # 16 hidden unitsprint('LSTM model:', lstm_model)print('model.parameters:', lstm_model.parameters)loss_function = nn.MSELoss()optimizer = torch.optim.Adam(lstm_model.parameters(), lr=1e-2)max_epochs = 10000for epoch in range(max_epochs):output = lstm_model(train_x_tensor)loss = loss_function(output, train_y_tensor)loss.backward()optimizer.step()optimizer.zero_grad()if loss.item() < 1e-4:print('Epoch [{}/{}], Loss: {:.5f}'.format(epoch+1, max_epochs, loss.item()))print("The loss value is reached")breakelif (epoch+1) % 100 == 0:print('Epoch: [{}/{}], Loss:{:.5f}'.format(epoch+1, max_epochs, loss.item()))# prediction on training datasetpredictive_y_for_training = lstm_model(train_x_tensor)predictive_y_for_training = predictive_y_for_training.view(-1, OUTPUT_FEATURES_NUM).data.numpy()# torch.save(lstm_model.state_dict(), 'model_params.pkl') # save model parameters to files# ----------------- test -------------------# lstm_model.load_state_dict(torch.load('model_params.pkl'))  # load model parameters from fileslstm_model = lstm_model.eval() # switch to testing model# prediction on test datasettest_x_tensor = test_x.reshape(-1, 5, INPUT_FEATURES_NUM) # set batch size to 5, the same value with the training settest_x_tensor = torch.from_numpy(test_x_tensor)predictive_y_for_testing = lstm_model(test_x_tensor)predictive_y_for_testing = predictive_y_for_testing.view(-1, OUTPUT_FEATURES_NUM).data.numpy()# ----------------- plot -------------------plt.figure()plt.plot(t_for_training, train_x, 'g', label='sin_trn')plt.plot(t_for_training, train_y, 'b', label='ref_cos_trn')plt.plot(t_for_training, predictive_y_for_training, 'y--', label='pre_cos_trn')plt.plot(t_for_testing, test_x, 'c', label='sin_tst')plt.plot(t_for_testing, test_y, 'k', label='ref_cos_tst')plt.plot(t_for_testing, predictive_y_for_testing, 'm--', label='pre_cos_tst')plt.plot([t[train_data_len], t[train_data_len]], [-1.2, 4.0], 'r--', label='separation line') # separation lineplt.xlabel('t')plt.ylabel('sin(t) and cos(t)')plt.xlim(t[0], t[-1])plt.ylim(-1.2, 4)plt.legend(loc='upper right')plt.text(14, 2, "train", size = 15, alpha = 1.0)plt.text(20, 2, "test", size = 15, alpha = 1.0)plt.show()

该模型在训练集和测试集上的结果如下:

在这里插入图片描述

图中,红色虚线的左边表示该模型在训练数据集上的表现,右边表示该模型在测试数据集上的表现。可以看到,使用LSTM构建训练模型,可以仅仅使用正弦函数在 t 时刻的值作为输入来准确预测 t 时刻的余弦函数值,不用额外添加当前的时间信息、速度信息等。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

展览展会媒体传播的必要性,有哪些宣传方式?

传媒如春雨&#xff0c;润物细无声&#xff0c;大家好&#xff0c;我是51媒体网胡老师。 展览展会媒体传播的必要性在于扩大影响力、吸引观众和促进行业交流。通过媒体宣传&#xff0c;可以快速传递展会信息&#xff0c;提升品牌知名度&#xff0c;吸引更多潜在参展商和观众。…

【GIS人必看】如何手动更改ArcGIS矢量文件的编码方式

前面一篇文章&#xff0c;给大家免费免费开源了一款ArcGIS超级工具---一键扩展矢量文件字段名长度脚本工具 工具的具体获取及使用方法请点击链接&#xff1a;【ArcPy工具】【GIS人必备超级工具】【免费开源】ArcGIS超级工具-一键扩展矢量文件字段名长度-CSDN博客 其实工…

品牌有窜货可以这样治理

窜货是品牌渠道中的常见问题&#xff0c;也是品牌发展中必然要面对的&#xff0c;只要品牌没有做好前期的出货管控&#xff0c;窜货会非常容易出现&#xff0c;对区域的销售保护制度是很普遍的&#xff0c;经销商利用区域保护策略&#xff0c;钻品牌漏洞&#xff0c;进行窜货销…

【MySQL使用】show processlist 命令详解

show processlist 命令详解 一、命令含义二、命令返回参数三、Command值解释四、State值解释五、参考资料 一、命令含义 对于一个MySQL连接&#xff0c;或者说一个线程&#xff0c;任何时刻都有一个状态&#xff0c;该状态表示了MySQL当前正在做什么。SHOW PROCESSLIST 命令的…

SpringBoot接口防抖(防重复提交)的一些实现方案

前言 啥是防抖 思路解析 分布式部署下如何做接口防抖&#xff1f; 具体实现 请求锁 唯一key生成 重复提交判断 前言 作为一名老码农&#xff0c;在开发后端Java业务系统&#xff0c;包括各种管理后台和小程序等。在这些项目中&#xff0c;我设计过单/多租户体系系统&a…

springboot+bootstrap+jsp校园二手书交易平台mlg86

考虑到实际生活中在校园二手书交易系统方面的需要以及对该系统认真的分析,将系统权限按管理员和学生这两类涉及用户划分。 (a) 管理员&#xff1b;管理员使用本系统涉到的功能主要有个人中心、学生管理、图书类型管理、二手图书管理、通知公告管理、管理员管理、用户留言、系统…

Windows Docker 部署 MySQL

部署 MySQL 打开 Docker Desktop&#xff0c;切换到 Linux 容器。然后在 PowerShell 执行下面命令&#xff0c;即可启动一个 MySQL 服务。这里安装的是 8.3.0 Tag版本&#xff0c;如果需要安装其他或者最新版本&#xff0c;可以到 Docker Hub 进行查找。 docker run -itd --n…

DC电源模块的 PCB设计和布局指南

BOSHIDA DC电源模块的 PCB设计和布局指南 DC电源模块的PCB设计和布局是一个关键的步骤&#xff0c;它直接影响到电源的性能和稳定性。下面是一些DC电源模块的PCB设计和布局的指南&#xff1a; 1. 选择合适的PCB尺寸和层数&#xff1a;根据电源模块的尺寸和功能需求&#xff0…

蓝桥杯前端Web赛道-新鲜的蔬菜

蓝桥杯前端Web赛道-新鲜的蔬菜 题目链接&#xff1a;1.新鲜的蔬菜 - 蓝桥云课 (lanqiao.cn) 题目要求如下&#xff1a; 其实很容易联想到使用flex布局&#xff0c;这是flex布局一种非常经典的骰子布局&#xff0c;推荐Flex 布局教程&#xff1a;实例篇 - 阮一峰的网络日志 (r…

Android m/mm/mmm/make编译模块

一.编译成模块的前置条件 Android编译环境初始化完成后&#xff0c;我们就可以用m/mm/mmm/make命令编译源代码了。lunch命令其实是定义在build/envsetup.sh文件中的函数lunch提供的。与lunch命令一样&#xff0c;m、mm和mmm命令也分别是由定义在build/envsetup.sh文件中的函数…

C#,电话数字键盘问题(Mobile Numeric Keypad problem)的算法与源代码

1 电话数字键盘问题 提供移动数字键盘。您只能按向上、向左、向右或向下至当前按钮的按钮。不允许您按最下面一行的角点按钮&#xff08;即.*和#&#xff09;。 移动键盘 给定一个数N&#xff0c;找出给定长度的可能数。 示例&#xff1a; 对于N1&#xff0c;可能的数字数为…

迷不迷糊?前后端、三层架构和MVC傻傻分不清

现在的项目都讲究前后端分离&#xff0c;那到底什么是前后端&#xff0c;前后端和以前的MVC以及三层架构啥关系呢&#xff1f;今天就这个问题展开一下&#xff0c;方面后面的学习&#xff0c;因为前面讲的jsp、servlet和javabean根据实例&#xff0c;基本上有一个框架的理解了&…

经纬恒润线控制动系统,新能源智能汽车的未来标配

随着汽车电动化、智能化的发展&#xff0c;制动系统迎来新一轮变革。在新能源车型尤其是新能源智能汽车中&#xff0c;智能底盘系统在底盘融合控制、整车安全方面重要性越来越高&#xff0c;线控制动属于智能底盘的重要部分&#xff0c;凭借着快速响应和精确执行的优势&#xf…

YOLOV9论文解读

代码&#xff1a;https://github.com/WongKinYiu/yolov9论文&#xff1a;https://arxiv.org/abs/2402.1361本文提出可编程梯度信息(PGI)和基于梯度路径规划的通用高效层聚合网络(GELAN)&#xff0c;最终铸成YOLOv9目标检测全新工作&#xff01;性能表现SOTA&#xff01;在各个方…

PCL中的点云分割模型的部分常用参数含义

PCL中的SacModel类别常用参数含义 1、SACMODEL_PLANE2、SACMODEL_LINE&#xff08;三维直线&#xff09;3、SACMODEL_CIRCLE2D&#xff08;二维圆&#xff09;4、SACMODEL_CIRCLE3D&#xff08;三维圆&#xff09;5、SACMODEL_SPHERE&#xff08;球&#xff09;6、SACMODEL_CYL…

《数字图像处理(MATLAB版)》相关算法代码及其分析(2)

目录 1 将8连通边界转换为4连通边界 1.1 移除对角线转折 1.2 插入额外像素 2 将边界信息转换为二进制图像 2.1 函数定义 2.2 参数处理和验证 2.3 默认大小参数设置 2.4 根据参数调整边界位置 2.5 生成二进制图像 2.6 错误处理 3 对二值图像边界的跟踪和提取 3.1 函…

Mybatis学习记录

Mybatis学习记录 1.快速开始示例1.1 什么是MyMyBatis&#xff1f;1.2 代码示例 2.基本使用2.1 #{}和${} 2.2 参数传入2.2.1 概念说明2.2.2 单个简单类型2.2.3 实体类型2.2.4 零散简单数据类型2.2.5 Map类型 2.3 返回值2.3.1 单个简单类型2.3.2 实体类型2.3.3 Map类型2.3.4 List…

java 版本企业招标投标管理系统源码+功能描述+tbms+及时准确+全程电子化

功能描述 1、门户管理&#xff1a;所有用户可在门户页面查看所有的公告信息及相关的通知信息。主要板块包含&#xff1a;招标公告、非招标公告、系统通知、政策法规。 2、立项管理&#xff1a;企业用户可对需要采购的项目进行立项申请&#xff0c;并提交审批&#xff0c;查看所…

基于MVO优化的Bi-LSTM多输入回归预测(Matlab)多元宇宙算法优化长短期神经网络回归预测

目录 一、程序及算法内容介绍&#xff1a; 基本内容&#xff1a; 亮点与优势&#xff1a; 二、实际运行效果&#xff1a; 三、算法介绍&#xff1a; 四、完整程序下载&#xff1a; 一、程序及算法内容介绍&#xff1a; 基本内容&#xff1a; 本代码基于Matlab平台编译&am…

探索Java开发面试笔记:以听为目的,助力编程技术提升与面试准备

文章目录 一、制作背景介绍二、 Java开发面试笔记&#xff1a;为你的编程之路加速2.1 公众号主题和目标读者群体2.2 为什么面试笔记对于提高编程视野和技术至关重要2.3 親測效率 三、形式案例3.1 文章形式3.2 手机案例3.3 电脑案例 一、制作背景介绍 做公众号的背景&#xff1a…