基于RNNs+Attention的红点位置检测(pytorch)

文章目录

  • 1 项目背景
  • 2 数据集
  • 3 思路
    • 3.1 一些小实验
      • Ⅰ 多通道输入
      • Ⅱ 位置编码
      • Ⅲ 实验结果
    • 3.2 Attention
      • Ⅰ Additive Attention
      • Ⅱ Dot-Product Attention
      • Ⅲ Scaled Dot-Product Attention
      • Ⅳ Multi-Head Attention (self-attention)
      • Ⅴ Criss-Cross Attention
      • Ⅵ Result
  • 4 代码

1 项目背景

需要在图片精确识别三跟红线所在的位置,并输出这三个像素的位置。

在这里插入图片描述
其中,每跟红线占据不止一个像素,并且像素颜色也并不是饱和度和亮度极高的红黑配色,每个红线放大后可能是这样的。

在这里插入图片描述

而我们的目标是精确输出每个红点的位置,需要精确到像素。也就是说,对于每根红线,模型需要输出橙色箭头所指的像素而不是蓝色箭头所指的像素的位置。

在之前尝试过 LSTM 和 GRU 检测红点,但是准确率感人,在噪声极低的情况下并不能精准识别位置。但是可以看出它们还是能找到大致位置的,我打算尝试添加注意力机制从模型的角度来提升精度。

在这里插入图片描述

2 数据集

还是之前那个代码合成的数据集数据集,每个数据集规模在15000张图片左右,在没有加入噪音的情况下,每个样本预览如图所示:
在这里插入图片描述
加入噪音后,每个样本的预览如下图所示:

在这里插入图片描述

图中黑色部分包含比较弱的噪声,并非完全为黑色。

数据集包含两个文件,一个是文件夹,里面包含了jpg压缩的图像数据:
在这里插入图片描述
另一个是csv文件,里面包含了每个图像的名字以及3根红线所在的像素的位置。

在这里插入图片描述

3 思路

之前的模型流程是这样的:

在这里插入图片描述

3.1 一些小实验

直觉上我认为问题可能出在FC2的架构不够复杂,无法很好地将Bi-LSTM学习到的序列信息转化成位置,也怀疑过Position Info, FC1的设计问题等等,在一系列实验之后,我发现可能真的是因为前面的RNN模型不能很好地学习到提取序列信息。罗列下方案:

  1. 优化FC1, FC2:把原来只有1层的FC1, FC2复杂化。
  2. 加入Position embedding:之前我们做的是[0,1]之内的数字来标记位置,其实这并不合理,我现在选择transformer中的思路来做,也就是用 s i n ( t ) sin(t) sin(t), c o s ( t ) cos(t) cos(t)来做。
  3. 多通道输入:将原来的1080 * 3的RGB序列化数据变成1080 * (3+3+3+1+8)的多通道(RGB+HSV+LAB+L+FFBB)的数据。其中,RGB、HSV、LAB、L分别是4种经典的色彩模式,FFBB是某个在R通道和H通道分别与前后两个像素的差值(图片首端和末端用0填补)。这种想法本质上是将一张图片的多种表达方式拼接到一起,发挥神经网络在高维 数据中的优势。
  4. 全明星方案:把上述方案结合到一起。

Ⅰ 多通道输入

RGB三通道的输入能表达的信息有限,因此尝试拼接多种色彩模式,依靠多通道表达像素颜色。考虑到红色区域包含多个像素且像素颜色相近,为了区别一段的像素,引入FFFBBB通道,即每个颜色在R通道和H通道分别与前后三个像素的差值(红色激光在这两个通道中相较背景颜色差异显著)。
在小样本数据集下测试:

实验train lossval losstest losstest 完全准确样本
3通道0.28990.37510.5955482.0/600(80%)
10通道0.35130.30930.5839491.0/600(82%)
22通道0.16540.22060.4754528.0/600(88%)

多通道的有效性在后面的多头注意力机制的实验中也被验证,该实验是在高噪声、长序列的数据集中进行的:

实验train lossval losstest losstest 完全准确样本点1平均偏移量点2平均偏移量点3平均偏移量
10通道203.076013.9310199.6459510.0/4500 (11%)3.08312.92733.2598
22通道165.644116.9843162.2208555.0/4500 (12%)2.72563.20012.7711
23通道174.501918.1041149.0297645.0/4500 (14%)2.65983.22422.4309

Ⅱ 位置编码

之前的思路是通过RNNs对每一个step做评分,得到scores,再将位置信息拼接到scores上通过另一层神经网络预测出红点所在的位置。这种方法的问题很明显:

  1. 用0-1的数字直接表达位置,信息传递不明确;
  2. 拼接矩阵会导致后面的神经网络赋予位置信息太多权重从而忽略颜色(可以通过降低位置信息的权重解决,但是权重的设立又需要大量实验);
  3. 局限于绝对位置信息,不能学会像“第1个像素在第2个像素前面1个位置”这样的相对位置信息。

因此,我想引入transformer中的位置编码,直接把position info与22通道信息相加,这样能一定程度上解决上述的三个问题。我是直接参考论文中的编码方式:
在这里插入图片描述在这里插入图片描述

Ⅲ 实验结果

汇总下上文提到的几个优化方案,对比实验结果如下:

实验loss完全准确的点
GRU129.66411762.0/9000 (20%)
LSTM249.20531267.0/9000 (14%)
优化F2的GRU30.23633486.0/9000 (39%)
优化F2的LSTM37.36693485.0/9000 (39%)
优化F1的GRU1752.6486459.0/9000 (5%)
优化F1的LSTM4578.7138326.0/9000 (4%)
Position embedding + GRU16.34035025.0/9000 (56%)
Position embedding + LSTM204.15511603.0/9000 (18%)
多通道 + GRU144.37371953.0/9000 (22%)
多通道 + LSTM958.9135713.0/9000 (8%)

其中,优化F2的效果不错,但是训练时间较长,GRU的训练时间是原来的9倍; FC1优化起反效果。改良Position Info效果一般,可能是我设计的问题。

以上实验都是在低噪声、长序列的实验中完成的。

3.2 Attention

从我个人的经验来说,Attention一直是一个很万能的东西,不过这样算是比较早的技术了,早就衍生出了好多不同的种类。我打算尝试我认为比较有用的几种。

因为使用了Attention,这里就不采取低噪声的背景了,直接上高噪声的,不过还是小数据集,只有15000张图。

目前的流程是这样的:

在这里插入图片描述

Ⅰ Additive Attention

在 GRU 输出后添加一个加性注意力 (Additive Attention) 层,计算每个时间步的权重,然后对 GRU 的输出进行加权平均。但是因为这个问题不是针对整个序列的问题,而是针对单个step的,因此省略对 output 的 sum。

不过Additive Attention在某些任务上会对特征的选择更加敏感,但如果特征间的关系较弱,模型可能会过分关注一些不重要的信息,导致预测偏差。
在这里插入图片描述

Ⅱ Dot-Product Attention

点积注意力机制是比较常见的注意力机制,其核心步骤是:

  • 计算 query 和 key 的点积。
  • 对点积结果进行 softmax 以生成注意力权重。
  • 用注意力权重加权 value,得到最终的 context vector。

我采用它的原因是该任务更加依赖于局部上下文相关性,而不是全局信息,这种 Attention 相对擅长这个任务。加性注意力是基于一个单独的权重投影,而 Dot-Product Attention 会直接操作序列的隐藏状态。

虽然是个长序列的问题,但是我暂时还没打算引入masking填充。
在这里插入图片描述

Ⅲ Scaled Dot-Product Attention

Scaled Dot-Product Attention 是 Dot-Product Attention 修改版本。与之前的 Additive Attention 不同,Scaled Dot-Product Attention 利用了向量之间的点积计算和归一化机制来生成注意力权重。因为在流程上跟 Dot-Product Attention 比较相似,就不做流程图了。

此外,该注意力机制还是多头注意力机制的简化版本。

我个人是认为寻找红点的中心这个任务是非常依赖时间步之间的强相关性的,而 Scaled Dot-Product Attention 依赖于 query 和 key 的相似性,这是一个切入点。

Ⅳ Multi-Head Attention (self-attention)

实现多头注意力机制其实就是对上一个注意力机制的优化:

  • 将输入分割成多个注意力头。
  • 每个头单独计算 Scaled Dot-Product Attention。
  • 将所有头的输出拼接并通过一个线性变换。

在这里插入图片描述
在初始的实验中,注意力机制效果非常差。在图片长度为100的时候,多头注意力机制能够比较精确地识别到红点的中心,但是当图片长度扩展到1080的时候,就算没有噪音,多头注意力机制也很难找到红点。

我个人觉得是因为在长注意力机制里,计算复杂度为 O ( n 2 ) O(n^2) O(n2),在传播梯度的时候比较困难,特别是在模型层数比较深的情况时;此外因为序列比较长,局部注意力也可能被稀释。

能想到的可能比较好用的缓解方法基本上是1. 放弃全局注意力采用局部窗口注意力和滑动窗口注意力;2. 把RNN层数拉上去或者在注意力层之间加FFNs来增加特征提取能力。我个人倾向于第一个想法。

此外,当input中的query,key,value都为同一个向量的时候,多头注意力机制变为自注意力机制。

Ⅴ Criss-Cross Attention

尝试这个注意力机制是我还是执着于要把精力放在局部的信息上,因此想做一个带有滑动窗口的注意力机制。之前尝试过带滑动窗口的多头注意力机制,感觉效果一般,就重新尝试这个。
在这里插入图片描述

Ⅵ Result

低噪声实验

实验train lossval losstest losstest 完全准确样本
Position embedding + GRU0.28990.375116.34035025.0/9000 (56%)
Additive Attention368.9917344.913611018.8885291.0/9000 (3%)

高噪声实验

实验train lossval losstest losstest 完全准确样本点1平均偏移量点2平均偏移量点3平均偏移量
Position embedding + GRU664.6360412.02332173.8701119.0/3000 (4%)///
Additive Attention378.769023.7633369.3527403.0/4500 (9%)4.78893.22194.4258
Dot-Product Attention199.632214.1448216.6723544.0/4500 (12%)3.18443.16333.4102
Scaled Dot-Product Attention179.053113.0236209.7830517.0/4500 (11%)2.72063.57543.1143
Multi-Head Attention174.501918.1041149.0297645.0/4500 (14%)2.65983.22432.4309
Criss-Cross Attention192.415734.9980205.0001556.0/4500 (12%)3.18313.23403.1561

精确度感觉还是不是很充足:

在这里插入图片描述
在这里插入图片描述

4 代码

多通道色彩

class RedDotDataset(Dataset):def __init__(self, csv_file, img_dir, transform=None):self.data_frame = pd.read_csv(csv_file, dtype={'Stripe_Number': str}, encoding='utf-8-sig')self.img_dir = img_dirself.transform = transform or transforms.Compose([transforms.ToTensor(),  # Automatically converts and normalizestransforms.Normalize((0.5,), (0.5,))  # Example normalization, adjust based on dataset])def __len__(self):return len(self.data_frame)def __getitem__(self, idx):img_path = os.path.join(self.img_dir, str(self.data_frame.iloc[idx, 0]) + '.jpg')img_rgb = np.array(Image.open(img_path).convert('RGB'))img_hsv = cv2.cvtColor(img_rgb, cv2.COLOR_RGB2HSV)img_lab = cv2.cvtColor(img_rgb, cv2.COLOR_RGB2LAB)img_l = Image.fromarray(img_rgb).convert('L')height, width, _ = img_rgb.shaper_channel = img_rgb[:, :, 0].astype('float32')h_channel = img_hsv[:, :, 0].astype('float32')def calculate_diff(channel_data):diff_map = []for offset in range(-3, 4):if offset == 0:continueoffset_data = np.zeros_like(channel_data)for x in range(width):new_x = min(max(0, x + offset), width - 1)offset_data[:, x] = channel_data[:, new_x]diff_map.append(offset_data - channel_data)return np.stack(diff_map, axis=-1)r_diff_map = calculate_diff(r_channel)h_diff_map = calculate_diff(h_channel)# [H, W, C] nampy array to [C, H, W] torch tensorimg_rgb = torch.from_numpy(np.array(img_rgb)).permute(2, 0, 1).float() / 255.0img_hsv = torch.from_numpy(np.array(img_hsv)).permute(2, 0, 1).float() / 255.0img_lab = torch.from_numpy(np.array(img_lab)).permute(2, 0, 1).float() / 255.0img_l = torch.from_numpy(np.array(img_l)).unsqueeze(0).float() / 255.0r_diff_map_n_channel = torch.from_numpy(r_diff_map).permute(2, 0, 1).float() / 255.0h_diff_map_n_channel = torch.from_numpy(h_diff_map).permute(2, 0, 1).float() / 255.0# zero_tensor = torch.zeros((1, 1, img_rgb.shape[2]), dtype=img_rgb.dtype)combined_img = torch.cat((img_rgb, img_hsv, img_lab, img_l, r_diff_map_n_channel, h_diff_map_n_channel), dim=0)labels = self.data_frame.iloc[idx, 1:].values.astype('float32')# if self.transform:#     img = self.transform(img)return combined_img, labels

位置编码

    def generate_positional_encoding(self, seq_length, d_model):pos_encoding = torch.zeros(seq_length, d_model)for pos in range(seq_length):for i in range(0, d_model, 2):pos_encoding[pos, i] = math.sin(pos / (10000 ** ((2 * i) / d_model)))if i + 1 < d_model:pos_encoding[pos, i + 1] = math.cos(pos / (10000 ** ((2 * (i + 1)) / d_model)))return pos_encoding.unsqueeze(0)  # Add batch dimension for broadcasting

Attention结构

class AdditiveAttention(nn.Module):def __init__(self, hidden_size):super(AdditiveAttention, self).__init__()self.attn = nn.Linear(hidden_size * 2, hidden_size)self.v = nn.Parameter(torch.rand(hidden_size))def forward(self, x):# x: (batch_size, seq_length, hidden_size * 2)u = torch.tanh(self.attn(x))  # (batch_size, seq_length, hidden_size)attn_weights = torch.matmul(u, self.v)  # (batch_size, seq_length)attn_weights = F.softmax(attn_weights, dim=1).unsqueeze(-1)  # (batch_size, seq_length, 1)output = x * attn_weights  # (batch_size, seq_length, hidden_size * 2)return output  # Aggregate context vector (batch_size, hidden_size * 2)class DotProductAttention(nn.Module):def __init__(self, hidden_size):super(DotProductAttention, self).__init__()self.query_proj = nn.Linear(hidden_size * 2, hidden_size)self.key_proj = nn.Linear(hidden_size * 2, hidden_size)self.value_proj = nn.Linear(hidden_size * 2, hidden_size)self.scale = math.sqrt(hidden_size)def forward(self, x):# x: (batch_size, seq_length, hidden_size * 2)query = self.query_proj(x)  # (batch_size, seq_length, hidden_size)key = self.key_proj(x)      # (batch_size, seq_length, hidden_size)value = self.value_proj(x)  # (batch_size, seq_length, hidden_size)# Compute attention scoresscores = torch.matmul(query, key.transpose(-2, -1)) / self.scale  # (batch_size, seq_length, seq_length)attn_weights = F.softmax(scores, dim=-1)  # (batch_size, seq_length, seq_length)# Compute context vectorcontext = torch.matmul(attn_weights, value)  # (batch_size, seq_length, hidden_size)return contextclass ScaledDotProductAttention(nn.Module):def __init__(self, hidden_size):super(ScaledDotProductAttention, self).__init__()self.scale = math.sqrt(hidden_size)def forward(self, x):# x: (batch_size, seq_length, hidden_size * 2)query = x  # Use the GRU output directly as query, key, and valuekey = xvalue = x# Compute attention scoresscores = torch.matmul(query, key.transpose(-2, -1)) / self.scale  # (batch_size, seq_length, seq_length)attn_weights = F.softmax(scores, dim=-1)  # (batch_size, seq_length, seq_length)# Compute context vectorcontext = torch.matmul(attn_weights, value)  # (batch_size, seq_length, hidden_size * 2)return context# 偷懒了哈哈
class MultiHeadAttention(nn.Module):def __init__(self, embed_size, num_heads):super(MultiHeadAttention, self).__init__()self.multihead_att = nn.MultiheadAttention(embed_dim=embed_size, num_heads=num_heads, batch_first=True)def forward(self, query, key, value):out, _ = self.multihead_att(query, key, value)return outclass CrissCrossAttention(nn.Module):def __init__(self, embed_size, window_size):super(CrissCrossAttention, self).__init__()self.window_size = window_sizeself.linear_q = nn.Linear(embed_size, embed_size)self.linear_k = nn.Linear(embed_size, embed_size)self.linear_v = nn.Linear(embed_size, embed_size)self.scale = embed_size ** 0.5def forward(self, x):# x: (batch_size, seq_length, embed_size)batch_size, seq_length, embed_size = x.size()query = self.linear_q(x)key = self.linear_k(x)value = self.linear_v(x)# Sliding windowkey = F.pad(key, (0, 0, self.window_size // 2, self.window_size // 2), mode="constant", value=0)value = F.pad(value, (0, 0, self.window_size // 2, self.window_size // 2), mode="constant", value=0)key_windows = key.unfold(dimension=1, size=self.window_size, step=1)value_windows = value.unfold(dimension=1, size=self.window_size, step=1)scores = torch.einsum("bqe,bqew->bqw", query, key_windows) / self.scale  # (batch_size, seq_length, window_size)attn_weights = F.softmax(scores, dim=-1)# Weighted sum for context vectorscontext = torch.einsum("bqw,bqew->bqe", attn_weights, value_windows)return context

路过的大佬有什么建议 ball ball 在评论区打出来

都看到这儿了,不妨给个三连吧~

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

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

相关文章

RTSP摄像头、播放器为什么需要支持H.265?

H.264还是H.265&#xff1f; 好多开发者在做选RTSP播放器的时候&#xff0c;经常问我们的问题是&#xff0c;用H.264好还是H.265好&#xff1f;本文我们就H.264 和 H.265的主要区别和适用场景&#xff0c;做个大概的交流。 一、压缩效率 H.265 更高的压缩比 H.265 在相同视频…

Qt桌面应用开发 第八天(读写文件 文件编码 文件流)

目录 1.读文件 2.写文件及编码格式 2.1写文件 2.2编码格式 3.文件信息读取 4.文件流 4.1QTextStream 4.2QDataStream 1.读文件 需求&#xff1a;一个pushButton&#xff0c;点击之后可以选择一个txt文件的路径&#xff0c;路径会显示在lineEdit上&#xff0c;txt文件的…

chrome允许http网站打开摄像头和麦克风

第一步 chrome://flags/#unsafely-treat-insecure-origin-as-secure 第二步 填入网址&#xff0c;点击启用 第三步 重启 Chrome&#xff1a;设置完成后&#xff0c;点击页面底部的 “Relaunch” 按钮&#xff0c;重新启动 Chrome 浏览器&#xff0c;使更改生效。

IntelliJ IDEA(2024版) 的安装、配置与使用教程:常用配置、创建工程等操作(很详细,你想要的都在这里)

IDEA的安装、配置与使用&#xff1a; Ⅰ、IDEA 的安装&#xff1a;1、IDEA 的下载地址(官网)&#xff1a;2、IDEA 分为两个版本&#xff1a;旗舰版 (Ultimate) 和 社区版 (Community)其一、两个不同版本的安装文件&#xff1a;其二、两个不同版本的详细对比&#xff1a; 3、IDE…

AI前景分析展望——GPTo1 SoraAI

引言 人工智能&#xff08;AI&#xff09;领域的飞速发展已不仅仅局限于学术研究&#xff0c;它已渗透到各个行业&#xff0c;影响着从生产制造到创意产业的方方面面。在这场技术革新的浪潮中&#xff0c;一些领先的AI模型&#xff0c;像Sora和OpenAI的O1&#xff0c;凭借其强大…

基于SpringBoot实现的民宿管理系统(代码+论文)

&#x1f389;博主介绍&#xff1a;Java领域优质创作者&#xff0c;阿里云博客专家&#xff0c;计算机毕设实战导师。专注Java项目实战、毕设定制/协助 &#x1f4e2;主要服务内容&#xff1a;选题定题、开题报告、任务书、程序开发、项目定制、论文辅导 &#x1f496;精彩专栏…

ComfyUI | ComfyUI桌面版发布,支持winmac多平台体验,汉化共享等技巧!(内附安装包)

ComfyUI 桌面版正式推出&#xff0c;支持 Windows 与 macOS 等多平台&#xff0c;为 AI 绘画爱好者带来全新体验。其安装包便捷易用&#xff0c;开启了轻松上手之旅。汉化共享功能更是一大亮点&#xff0c;打破语言障碍&#xff0c;促进知识交流与传播。在操作上&#xff0c;它…

基于 Vue2.0 + Nest.js 全栈开发的后台应用

☘️ 项目简介 Vue2 Admin 是一个前端基于 Ant Design Pro 二次开发&#xff0c;后端基于 Nest.js 的全栈后台应用&#xff0c;适合学习全栈开发的同学参考学习。 &#x1f341; 前端技术栈&#xff1a; Vue2、Ant Design Vue、Vuex &#x1f341; 后端技术栈&#xff1a; Ne…

RabbitMQ 应用问题

文章目录 1. 幂等性保障什么是幂等性MQ 的幂等性如何处理消息重复的问题1. 全局唯一ID2. 业务逻辑判断 2. 顺序性保障什么是顺序性保障什么情况会打破RabbitMQ的顺序性顺序性保障方案 3. 消息积压什么是消息积压造成消息积压的原因解决消息积压的方案 结论 1. 幂等性保障 什么…

【数据库系列】MySQL基础知识:深入理解DDL、DML与DQL操作

MySQL是一个开源的关系型数据库管理系统&#xff08;RDBMS&#xff09;&#xff0c;广泛用于数据存储和管理。理解MySQL的基本操作至关重要&#xff0c;尤其是数据定义语言&#xff08;DDL&#xff09;、数据操作语言&#xff08;DML&#xff09;和数据查询语言&#xff08;DQL…

PAT1085 Perfect Sequence(25)

//判断是否是连续的数 //判断是否只能第一个数是最小值 #include <cstdio> #include <algorithm> typedef long long ll; using namespace std; int n,p; const int maxn 100010; int arr[maxn];int binary(int l, int r, ll tgt){if(arr[n-1] < tgt) return n…

Shell 编程基础知识

为什么要学 Shell&#xff1f; 学一个东西&#xff0c;我们大部分情况都是往实用性方向着想。从工作角度来讲&#xff0c;学习 Shell 是为了提高我们自己工作效率&#xff0c;提高产出&#xff0c;让我们在更少的时间完成更多的事情。 很多人会说 Shell 编程属于运维方面的知…

深入浅出UART驱动开发与调试:从基础调试到虚拟驱动实现

往期内容 本专栏往期内容&#xff1a;Uart子系统 UART串口硬件介绍深入理解TTY体系&#xff1a;设备节点与驱动程序框架详解Linux串口应用编程&#xff1a;从UART到GPS模块及字符设备驱动 解UART 子系统&#xff1a;Linux Kernel 4.9.88 中的核心结构体与设计详解IMX 平台UART驱…

Linux网络——IO模型和多路转接

通常所谓的IO&#xff0c;其本质就是等待通信和进行通信&#xff0c;即IO 等 拷贝。 那么想要做到高效的IO&#xff0c;就要在单位时间内&#xff0c;减少“等”的比重。 一.五种IO模型 阻塞 IO: 在内核将数据准备好之前, 系统调用会一直等待. 所有的套接字, 默认都是阻塞方…

VM Virutal Box的Ubuntu虚拟机与windows宿主机之间设置共享文件夹(自动挂载,永久有效)

本文参考如下链接 How to access a shared folder in VirtualBox? - Ask Ubuntu &#xff08;1&#xff09;安装增强功能&#xff08;Guest Additions&#xff09; 首先&#xff0c;在网上下载VBoxGuestAdditions光盘映像文件 下载地址&#xff1a;Index of http://…

AI的魔力:如何为开源软件注入智慧,开启无限可能

“AI的魔力&#xff1a;如何为开源软件注入智慧&#xff0c;开启无限可能” 引言&#xff1a; 在科技发展的浪潮中&#xff0c;开源软件生态一直扮演着推动创新与共享的重要角色。从Linux到Python&#xff0c;开源项目赋予了开发者全球协作的机会&#xff0c;推动了技术的飞速…

IThenticate 查重有无免费午餐?深度解析

经历过论文“折磨”的过来人&#xff0c;深知查重工具是写论文不可或缺的助手。而 iThenticate 查重系统&#xff0c;深受出版商、学术机构和研究人员喜爱。不过&#xff0c;每次看到它那昂贵的价格&#xff0c;就让很多小伙伴直呼&#xff0c;IThenticate查重系统就没有免费的…

启动SpringBoot

前言&#xff1a;大家好我是小帅&#xff0c;今天我们来学习SpringBoot 文章目录 1. 环境准备2. Maven2.1 什么是Maven2.2 创建⼀个Maven项⽬2.3 依赖管理2.3.1 依赖配置2.3.2 依赖传递2.3.4 依赖排除2.3.5 Maven Help插件&#xff08;plugin&#xff09; 2.4 Maven 仓库2.6 中…

DHCP服务(包含配置过程)

目录 一、 DHCP的定义 二、 使用DHCP的好处 三、 DHCP的分配方式 四、 DHCP的租约过程 1. 客户机请求IP 2. 服务器响应 3. 客户机选择IP 4. 服务器确定租约 5. 重新登录 6. 更新租约 五、 DHCP服务配置过程 一、 DHCP的定义 DHCP&#xff08;Dynamic Host Configur…

使用 Certbot 为 Nginx 自动配置 SSL 证书

1.安装Certbot和Nginx插件 sudo apt-get update sudo apt-get install certbot python3-certbot-nginx 2.获取和安装证书 运行Certbot自动安装SSL证书。注意替换 your_domain sudo certbot --nginx -d your_domain Certbot将自动与Lets Encrypt的服务器通信&#xff0c;验证域…