递归神经网络简介

一、说明

说起递归神经网络,递归神经网络(RNN)主要包括以下几种类型:

  1. 简单的RNN(Simple RNN):最基本的RNN类型,每个时刻的输出都与前面时刻的状态有关。

  2. 循环神经网络(Recurrent Neural Networks,RNN):在简单RNN的基础上添加可学习的“记忆单元”,可以处理变长的输入序列并且具有时序特征提取的能力。

  3. 长短时记忆网络(Long Short-Term Memory,LSTM):针对普通RNN存在的梯度消失问题,通过引入“门控机制”来控制信息的流动和记忆的更新,强化记忆功能和忘记功能。

  4. 双向循环神经网络(Bidirectional RNN):一种能够同时考虑上下文信息的循环神经网络,将输入序列分别从前向后和从后向前分别输入到两个RNN中,最终将两个RNN输出的结果进行拼接。

  5. 门控循环神经网络(Gated Recurrent Unit,GRU):与LSTM类似,通过引入“门控机制”来控制信息的流动和记忆的更新,但相比LSTM,GRU只有两个门控单元,计算量更小,收敛速度更快。

        本文将它们的演进历史梳理一遍。限于篇幅问题,我们将其分成两个部分描述。

二、递归神经网络简介

RNN,LSTM和GRU细胞。

        如果你想对顺序或时间序列数据(例如,文本、音频等)进行预测,传统的神经网络是一个糟糕的选择。但是为什么?

        在时间序列数据中,当前观测值依赖于先前的观测值,因此观测值并非彼此独立。然而,传统的神经网络认为每个观察都是独立的,因为网络无法保留过去或历史信息。基本上,他们对过去发生的事情没有记忆。

        这导致了递归神经网络(RNN)的兴起,它通过包括数据点之间的依赖关系将记忆的概念引入神经网络。有了这个,可以训练RNN根据上下文记住概念,即学习重复的模式。

        但是RNN是如何实现这种记忆的呢?

        RNN通过细胞中的反馈回路实现记忆。这是RNN和传统神经网络之间的主要区别。反馈循环允许信息在层内传递,而前馈神经网络则信息仅在层之间传递。

        然后,RNN必须定义哪些信息足够相关以保留在内存中。为此,进化了不同类型的RNN:

  • 传统递归神经网络 (RNN)
  • 长期短期记忆循环神经网络 (LSTM)
  • 门控循环单元循环神经网络 (GRU)

        在本文中,我将向您介绍RNN,LSTM和GRU。我将向您展示它们的异同以及一些优点和缺点。除了理论基础之外,我还向您展示了如何使用 在 Python 中实现每种方法。tensorflow

三、递归神经网络 (RNN)

        通过反馈循环,一个RNN单元的输出也用作同一单元的输入。因此,每个单元格有两个输入:过去和现在。使用过去的信息会导致短期记忆。

        为了更好地理解,我们展开/展开RNN细胞的反馈回路。展开单元格的长度等于输入序列的时间步长数。

展开的递归神经网络。

        我们可以看到过去的观察结果是如何作为隐藏状态通过展开的网络传递的。在每个单元格中,当前时间步长x(现值)、前一时间步的隐藏状态h(过去值)和偏差的输入被组合在一起,然后通过激活函数进行限制,以确定当前时间步的隐藏状态。

        在这里,粗体小字母表示向量,而大写粗体字母表示矩阵。

        RNN 的权重 W 通过反向时间传播 (BPTT) 算法进行更新。

        RNN 可用于一对一、一对多、多对一和多对多预测。

3.1 RNN 的优势

        由于其短期记忆,RNN可以处理顺序数据并识别历史数据中的模式。此外,RNN能够处理不同长度的输入。

3.2 RNN的缺点

        RNN遭受消失梯度下降的影响。在这种情况下,用于在反向传播期间更新权重的梯度变得非常小。将权重与接近零的梯度相乘会阻止网络学习新的权重。这种学习的停止导致RNN忘记了在较长的序列中看到的内容。梯度下降消失的问题随着网络的层数增加。

        由于RNN只保留最近的信息,因此模型在考虑遥远过去的观察结果方面存在问题。因此,RNN倾向于在长序列上丢失信息,因为它只存储最新的信息。因此,RNN只有短期记忆,而不是长期记忆。

        此外,由于RNN及时使用反向传播来更新权重,网络也受到梯度爆炸的影响,如果使用ReLu激活函数,则会出现死ReLu单元。前者可能导致收敛问题,而后者可能会停止学习。

3.3 RNN在张量流中的实现

        我们可以使用 .为此,我们使用允许我们堆叠 RNN 层的模型,即层类和层类。tensorflowSequentialSimpleRNNDense

from tensorflow.keras import Sequential
from tensorflow.keras.layers import SimpleRNN, Dense
from tensorflow.keras.optimizers import Adam

        只要我们想使用默认参数,就不需要导入优化器。但是,如果我们想自定义优化器的任何参数,我们还需要导入优化器。

        为了构建网络,我们定义一个模型,然后使用该方法添加RNN层。为了添加 RNN 层,我们使用类和传递参数,例如单元数、辍学率或激活函数。对于第一层,我们还可以传递输入序列的形状。Sequentialadd()SimpleRNN

        如果我们堆叠RNN层,我们需要将前一层的参数设置为。这可确保层的输出具有下一个 RNN 层的正确格式。return_sequenceTrue

        为了生成输出,我们使用一个层作为我们的最后一层,传递输出的数量。Dense

# define parameters
n_timesteps, n_features, n_outputs = X_train.shape[1], X_train.shape[2], y_train.shape[1]# define model
rnn_model = Sequential()
rnn_model.add(SimpleRNN(130, dropout=0.2, return_sequences=True, input_shape=(n_timesteps, n_features)))
rnn_model.add(SimpleRNN(110, dropout=0.2, activation="tanh", return_sequences=True))
rnn_model.add(SimpleRNN(130, dropout=0.2, activation="tanh", return_sequences=True))
rnn_model.add(SimpleRNN(100, dropout=0.2, activation="sigmoid", return_sequences=True))
rnn_model.add(SimpleRNN(40, dropout=0.3, activation="tanh"))
rnn_model.add(Dense(n_outputs))

        定义 RNN 后,我们可以使用该方法编译模型。在这里,我们传递损失函数和我们要使用的优化器。 提供一些内置的损失函数和优化器。compile()tensorflow

rnn_model.compile(loss='mean_squared_error', optimizer=Adam(learning_rate=0.001))

在训练 RNN 之前,我们可以使用该方法查看模型和参数数量。这可以让我们了解模型的复杂性。summary()

我们使用该方法训练模型。在这里,我们需要传递训练数据和不同的参数来自定义训练,包括 epoch 数、批量大小、验证拆分和提前停止。fit()

stop_early = tf.keras.callbacks.EarlyStopping(monitor='val_loss', patience=5)
rnn_model.fit(X_train, y_train, epochs=30, batch_size=32, validation_split=0.2, callbacks=[stop_early])

        要对我们的测试数据集或任何看不见的数据进行预测,我们可以使用该方法。该参数仅说明我们是否要获取有关预测过程状态的任何信息。在这种情况下,我不希望打印出任何状态。predict()verbose

y_pred = rnn_model.predict(X_test, verbose=0)

张量流中 RNN 的超参数调优

        正如我们所看到的,RNN的实现非常简单。然而,找到正确的超参数,例如每层的单元数、辍学率或激活函数,要困难得多。

        但是,我们可以使用库,而不是手动更改超参数。该库有四个调谐器、、 和 ,用于从给定的搜索空间中识别正确的超参数组合。keras-tunerRandomSearchHyperbandBayesianOptimizationSklearn

        要运行调谐器,我们首先需要导入和 Keras 调谐器。tensorflow

import tensorflow as tf
import keras_tuner as kt

然后,我们构建超调谐模型,在其中定义超参数搜索空间。我们可以使用一个函数构建超模型,在该函数中,我们以与上述相同的方式构建模型。唯一的区别是我们为要调整的每个超参数添加搜索空间。在下面的示例中,我想调整每个 RNN 层的单元数、激活函数和辍学率。

def build_RNN_model(hp):# define parametersn_timesteps, n_features, n_outputs = X_train.shape[1], X_train.shape[2], y_train.shape[1]# define modelmodel = Sequential()model.add(SimpleRNN(hp.Int('input_unit',min_value=50,max_value=150,step=20), return_sequences=True, dropout=hp.Float('in_dropout',min_value=0,max_value=.5,step=0.1), input_shape=(n_timesteps, n_features)))model.add(SimpleRNN(hp.Int('layer 1',min_value=50,max_value=150,step=20), activation=hp.Choice("l1_activation", values=["tanh", "relu", "sigmoid"]), dropout=hp.Float('l1_dropout',min_value=0,max_value=.5,step=0.1), return_sequences=True))model.add(SimpleRNN(hp.Int('layer 2',min_value=50,max_value=150,step=20), activation=hp.Choice("l2_activation", values=["tanh", "relu", "sigmoid"]), dropout=hp.Float('l2_dropout',min_value=0,max_value=.5,step=0.1), return_sequences=True))model.add(SimpleRNN(hp.Int('layer 3',min_value=20,max_value=150,step=20), activation=hp.Choice("l3_activation", values=["tanh", "relu", "sigmoid"]), dropout=hp.Float('l3_dropout',min_value=0,max_value=.5,step=0.1), return_sequences=True))model.add(SimpleRNN(hp.Int('layer 4',min_value=20,max_value=150,step=20), activation=hp.Choice("l4_activation", values=["tanh", "relu", "sigmoid"]), dropout=hp.Float('l4_dropout',min_value=0,max_value=.5,step=0.1)))# output layermodel.add(Dense(n_outputs))model.compile(loss='mean_squared_error', optimizer=Adam(learning_rate=1e-3))return model

        要定义每个变量的搜索空间,我们可以使用不同的方法,例如、 和 。前两者的用法非常相似。我们给它们一个名称、一个最小值、一个最大值和一个步长。该名称用于标识超参数,而最小值和最大值定义我们的值范围。步长参数定义了我们用于调优的范围内的值。可用于调整分类超参数,例如激活函数。在这里,我们只需要传递要测试的选项列表。hp.Inthp.Floathp.Choicehp.Choice

        构建超模型后,我们需要实例化调谐器并执行超调谐。尽管我们可以在不同的调优算法之间进行选择,但它们的实例化非常相似。我们通常需要指定要优化的目标和要训练的最大 epoch 数。在这里,建议将 epochs 设置为略高于我们预期的 epoch 数的数字,然后使用提前停止。

        例如,如果我们想使用调谐器和验证损失作为目标,我们可以将调谐器构建为Hyperband

tuner = kt.Hyperband(build_RNN_model,objective="val_loss",max_epochs=100,factor=3,hyperband_iterations=5,directory='kt_dir',project_name='rnn',overwrite=True)

        在这里,我还传递了存储结果的目录,以及调谐器迭代完整超带算法的频率。

        实例化调谐器后,我们可以使用该方法执行超参数调优。search()

stop_early = tf.keras.callbacks.EarlyStopping(monitor='val_loss', patience=5)
tuner.search(X_train, y_train, validation_split=0.2, callbacks=[stop_early])

为了提取最佳超参数,我们可以使用该方法并使用我们调整的每个超参数的方法和名称。get_best_hyperparameters()get()

best_hps=tuner.get_best_hyperparameters(num_trials=1)[0]
print(f"input: {best_hps.get('input_unit')}")
print(f"input dropout: {best_hps.get('in_dropout')}")
(未完待续)

四、长期短期记忆 (LSTM)

LSTM是一种特殊类型的RNN,它解决了简单RNN的主要问题,即梯度消失的问题,即过去进一步的信息丢失。

展开的长短期记忆细胞。

LSTM 的关键是单元状态,它从单元的输入传递到输出。因此,细胞状态允许信息沿着整个链流动,只需通过三个门的微小线性动作。因此,细胞状态代表 LSTM 的长期记忆。这三个门称为遗忘门、输入门和输出门。这些门用作过滤器并控制信息流,并确定保留或忽略哪些信息。

遗忘门决定保留多少长期记忆。为此,使用了一个sigmoid函数来说明细胞状态的重要性。输出在 0 到 1 之间变化,并说明保留了多少信息,即 0,不保留任何信息,1,保留单元格状态的所有信息。输出由当前输入x、前一个时间步的隐藏状态h和偏置b组合确定。

输入门决定将哪些信息添加到细胞状态中,从而添加到长期记忆中。在这里,sigmoid 层决定更新哪些值。

输出决定单元状态的哪些部分构建输出。因此,输出门负责短期记忆。

可以看出,所有三个门都由相同的功能表示。只有权重和偏差不同。单元格状态通过遗忘门和输入门更新。

上述等式中的第一个项决定了保留了多少长期记忆,而第二项则向细胞状态添加新信息。

然后,当前时间步长的隐藏状态由输出门和将单元状态限制在 -1 和 1 之间的 tanh 函数确定。

4.1 LSTM的优势

LSTM的优点与RNN相似,主要优点是它们可以捕获序列的长期和短期模式。因此,它们是最常用的RNN。

4.2 LSTM的缺点

由于其更复杂的结构,LSTM 的计算成本更高,导致训练时间更长。

由于 LSTM 还使用时间反向传播算法来更新权重,因此 LSTM 存在反向传播的缺点(例如,死 ReLu 元素、梯度爆炸)。

4.3 LSTM在张量流中的实现

        LSTM的实现与简单的RNN非常相似。唯一的区别是我们导入类而不是类。tensorflowLSTMSimpleRNN

from tensorflow.keras import Sequential
from tensorflow.keras.layers import LSTM, Dense
from tensorflow.keras.optimizers import Adam

我们可以像简单的RNN一样将LSTM网络组合在一起。

# define parameters
n_timesteps, n_features, n_outputs = X_train.shape[1], X_train.shape[2], y_train.shape[1]# define model
lstm_model = Sequential()
lstm_model.add(LSTM(130, return_sequences=True, dropout=0.2, input_shape=(n_timesteps, n_features)))
lstm_model.add(LSTM(70, activation="relu", dropout=0.1, return_sequences=True))
lstm_model.add(LSTM(100, activation="tanh", dropout=0))# output layer
lstm_model.add(Dense(n_outputs, activation="tanh"))lstm_model.compile(loss='mean_squared_error', optimizer=Adam(learning_rate=0.001))stop_early = tf.keras.callbacks.EarlyStopping(monitor='val_loss', patience=5)
lstm_model.fit(X_train, y_train, epochs=30, batch_size=32, validation_split=0.2, callbacks=[stop_early])

        超参数调整也与简单 RNN 相同。因此,我们只需要对我上面显示的代码片段进行微小的更改。

五、门控循环单元 (GRU)

        与LSTM类似,GRU解决了简单RNN的梯度消失问题。然而,与LSTM的区别在于GRU使用较少的门,并且没有单独的内部存储器,即单元状态。因此,GRU 仅依靠隐藏状态作为内存,从而简化架构。

展开的门控循环单元 (GRU)。

        复位门负责短期记忆,因为它决定保留和忽略多少过去的信息。

        向量 r 中的值由 sigmoid 函数限定在 0 和 1 之间,并取决于前一个时间步长的隐藏状态 h 和当前输入 x。两者都使用权重矩阵 W 进行加权。此外,还添加了偏差 b

        相比之下,更新门负责长期记忆,可与LSTM的遗忘相媲美。

        正如我们所看到的,重置门和更新门之间的唯一区别是权重 W

        当前时间步的隐藏状态是根据两步过程确定的。首先,确定候选隐藏状态。候选状态是当前输入和前一个时间步的隐藏状态以及激活函数的组合。在此示例中,使用 tanh 函数。先前隐藏状态对候选隐藏状态的影响由复位门控制

        在第二步中,将候选隐藏状态与前一个时间步的隐藏状态相结合,以生成当前隐藏状态。以前的隐藏状态和候选隐藏状态的组合方式由更新入口确定。

        如果更新门给出的值为 0,则完全忽略以前的隐藏状态,当前隐藏状态等于候选隐藏状态。如果更新门给出的值为 <>,反之亦然。

5.1 GRU 的优势

        由于与 LSTM 相比架构更简单(即两个而不是三个门和一个状态而不是两个),GRU 在计算上更有效率,训练速度更快,因为它们需要更少的内存。

        此外,GRU已被证明对较小的序列更有效。

5.2 GRU 的缺点

        由于GRU没有单独的隐藏和细胞状态,它们可能无法像LSTM那样考虑过去的观测结果。

        与RNN和LSTM类似,GRU也可能遭受反向传播的缺点,及时更新权重,即死ReLu元素,爆炸梯度。

5.3 GRU在张量流中的实现

        至于LSTM,GRU的实现与简单的RNN非常相似。我们只需要导入类,而其余部分保持不变。GRU

from tensorflow.keras import Sequential
from tensorflow.keras.layers import GRU, Dense
from tensorflow.keras.optimizers import Adam# define parameters
n_timesteps, n_features, n_outputs = X_train.shape[1], X_train.shape[2], y_train.shape[1]# define model
gru_model = Sequential()
gru_model.add(GRU(90,return_sequences=True, dropout=0.2, input_shape=(n_timesteps, n_features)))
gru_model.add(GRU(150, activation="tanh", dropout=0.2, return_sequences=True))
gru_model.add(GRU(60, activation="relu", dropout=0.5))
gru_model.add(Dense(n_outputs))gru_model.compile(loss='mean_squared_error', optimizer=Adam(learning_rate=0.001))stop_early = tf.keras.callbacks.EarlyStopping(monitor='val_loss', patience=5)
gru_model.fit(X_train, y_train, epochs=30, batch_size=32, validation_split=0.2, callbacks=[stop_early])

这同样适用于超参数优化。

六、结论

        递归神经网络将记忆引入神经网络。这样,观测值在顺序和时间序列数据中的依赖性就包含在我们的预测中。

        在本文中,我向您展示了三种类型的递归神经网络,即简单RNN,LSTM和GRU。我已经向您展示了它们是如何工作的,它们的优点和缺点是什么,以及如何使用 在 Python 中实现它们。tensorflow

        请让我知道您对这篇文章的看法!

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

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

相关文章

Blazor前后端框架Known-V1.2.10

V1.2.10 Known是基于C#和Blazor开发的前后端分离快速开发框架&#xff0c;开箱即用&#xff0c;跨平台&#xff0c;一处代码&#xff0c;多处运行。 Gitee&#xff1a; https://gitee.com/known/KnownGithub&#xff1a;https://github.com/known/Known 概述 基于C#和Blazo…

Kafka:springboot集成kafka收发消息

kafka环境搭建参考Kafka&#xff1a;安装和配置_moreCalm的博客-CSDN博客 1、springboot中引入kafka依赖 <dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId><…

Python-OpenCV中的图像处理-形态学转换

Python-OpenCV中的图像处理-形态学转换 形态学转换腐蚀膨胀开运算闭运算形态学梯度礼帽黑帽形态学操作之间的关系 形态学代码例程 形态学转换 形态学操作:腐蚀&#xff0c;膨胀&#xff0c;开运算&#xff0c;闭运算&#xff0c;形态学梯度&#xff0c;礼帽&#xff0c;黑帽等…

B树的插入与删除过程

B树的插入 原树&#xff1a; 插入key后&#xff0c;若导致原节点关键字数超过上限&#xff0c;则从中间位置&#xff08; ⌈ m 2 ⌉ \lceil\frac{m}{2}\rceil ⌈2m​⌉&#xff09;将关键字分成两部分&#xff0c;左部分包含的关键字放在原节点中&#xff0c;右部分包含的关键…

前端下载文化部几种方法(excel,zip,html,markdown、图片等等)和导出 zip 压缩包

文章目录 1、location.href2、location.href3、a标签4、请求后端的方式5、文件下载的方式6、Blob和Base647、下载附件方法(excel,zip,html,markdown)8、封装下载函数9、导出 zip 压缩包相关方法(流方式) 总结 1、location.href //get请求 window.location.href url;2、locati…

死锁的成因,和解决方案总结

何为死锁 死锁是多线程或并发程序中的一种情况&#xff0c;当多个线程因为竞争资源而相互等待&#xff0c;并且无法继续执行的情况。在死锁中&#xff0c;每个线程都在等待其他线程释放资源&#xff0c;从而导致所有线程都陷入无限等待状态&#xff0c;无法继续向前执行&#…

0805hw

1. #include <myhead.h> void Bub_sort(int *arr,int n)//冒泡排序 {for(int i1;i<n;i){int count0;for(int j0;j<n-i;j){if(arr[j]>arr[j1]){int temparr[j];arr[j]arr[j1];arr[j1]temp;count;}}if(count0){break;}}printf("冒泡排序后输出结果:\n"…

uni-app离线打包高德地图导入android studio不能正常显示

本人使用的uni-app SDK版本&#xff1a;Android-SDK3.8.7.81902_20230704 1.导入以上文件&#xff0c;依赖已经自动添加了 2.确保这个正常引入 3.修改AndroidMainifest.xml,添加自己的密钥

整理mongodb文档:删

个人博客 整理mongodb文档:删 求关注&#xff0c;哪儿不足&#xff0c;求大佬们指出&#xff0c;哪儿写的不够通俗易懂跟清晰&#xff0c;也求指出 文章概叙 本文主要是介绍了删除数据的几个方法&#xff0c;主要还是在介绍deleteMany、deleteOne以及remove&#xff0c;对于…

JAVA基础之放弃使用Random

随机是日常生活中经常遇到的非常有趣的东西&#xff0c;比如说抛硬币&#xff0c;他的不可预知性总是让我们特别着迷&#xff0c;在拿不定主意时&#xff0c;有些人就喜欢用抛硬币的方式来帮助我们做决定。体育领域也喜欢用喜欢用抛硬币的方式来猜先。随机数功能是Java非常非常…

14个前端开发者应该知道的实用网站

在本文中&#xff0c;我将分享一些非常有用的网站合集&#xff0c;这些网站可以在你的日常工作中极大地帮助你。这些网站已经成为我各种任务的首选资源&#xff0c;节省了我的时间&#xff0c;提高了工作效率 文档自动化 Documatic 是一款专为开发人员设计的非常高效的搜索引擎…

Pytorch深度学习-----现有网络模型的使用及修改(VGG16模型)

系列文章目录 PyTorch深度学习——Anaconda和PyTorch安装 Pytorch深度学习-----数据模块Dataset类 Pytorch深度学习------TensorBoard的使用 Pytorch深度学习------Torchvision中Transforms的使用&#xff08;ToTensor&#xff0c;Normalize&#xff0c;Resize &#xff0c;Co…

STM32 CubeMX USB_MSC(存储设备U盘)

STM32 CubeMX STM32 CubeMX USB_MSC(存储设备U盘&#xff09; STM32 CubeMX前言 《使用内部Flash》——U盘一、STM32 CubeMX 设置USB时钟设置USB使能UBS功能选择FATFS功能 二、代码部分修改代码"usbd_storage_if.c"修改代码"user_diskio.c"main函数初始化插…

每天一道leetcode:剑指 Offer 27. 二叉树的镜像(适合初学者递归树)

今日份题目&#xff1a; 请完成一个函数&#xff0c;输入一个二叉树&#xff0c;该函数输出它的镜像。 例如输入&#xff1a; 4 / \ 2 7 / \ / \ 1 3 6 9 镜像输出&#xff1a; 4 / \ 7 2 / \ / \ 9 6 3 1 示例 输入&#xff1a;root [4,2,7…

c基础扫雷

和三子棋一样&#xff0c;主函数先设计游戏菜单界面&#xff0c;这里就不做展示了。 初始化棋盘 初级扫雷大小为9*9的棋盘&#xff0c;但排雷是周围一圈进行排雷(8格)&#xff0c;而边界可能会越界。数组扩大了一圈,行和列都加了2&#xff0c;所以我们用一个11*11的数组来初始化…

数据结构—树和二叉树

5.树和二叉树 5.1树和二叉树的定义 树形结构&#xff08;非线性结构&#xff09;&#xff1a;结点之间有分支&#xff0c;具有层次关系。 5.1.1树的定义 树&#xff08;Tree&#xff09;是n&#xff08;n≥0&#xff09;个结点的有限集。 若n0&#xff0c;称为空树&#x…

Vue2嵌入HTML页面空白、互相传参、延迟加载等问题解决方案

一、需求分析 最近做的一个用H5加原生开发的html项目&#xff0c;现需要集成到Vue2.0项目里面来。遇到的相关问题做个记录和总结&#xff0c;以便能帮到大家避免踩坑。 二、问题记录 1、页面空白问题 将html页面通过iframe的方式嵌入进来之后&#xff0c;发现页面是空白的&am…

面试热题(倒数第k个结点)

输入一个链表&#xff0c;输出该链表中倒数第k个节点。为了符合大多数人的习惯&#xff0c;本题从1开始计数&#xff0c;即链表的尾节点是倒数第1个节点。 例如&#xff0c;一个链表有 6 个节点&#xff0c;从头节点开始&#xff0c;它们的值依次是 1、2、3、4、5、6。这个链表…

opencv动态目标检测

文章目录 前言一、效果展示二、实现方法构造形态学操作所需的核:创建背景减除模型:形态学操作:轮廓检测: 三、代码python代码C代码 总结参考文档 前言 很久没更新文章了&#xff0c;这次因为工作场景需要检测动态目标&#xff0c;特此记录一下。 一、效果展示 二、实现方法 基…

图的深度优先遍历和广度优先遍历

目录 图的创建和常用方法 深度优先遍历&#xff08;Depth First Search&#xff09; 广度优先遍历&#xff08;Broad First Search&#xff09; 图的创建和常用方法 //无向图 public class Graph {//顶点集合private ArrayList<String> vertexList;//存储对应的邻接…