【作业九】RNN-SRN-Seq2Seq

点击查看作业内容


目录

1 实现SRN

(1)使用numpy实现

(2)在(1)的基础上,增加激活函数tanh

(3)使用nn.RNNCell实现

(4)使用nn.RNN实现

 2 使用RNNCell实现“序列到序列”

3 实现“编码器-解码器”-Seq2Seq

4 简单总结nn.RNNCell、nn.RNN

(1)nn.RNNCell

(2)nn.RNN

5 谈一谈对“序列”、“序列到序列”的理解

6 总结本周理论课和作业,写心得体会


1 实现SRN

        先回顾一下什么是SRN。

        SRN全称Simple Recurrent Network,即简单循环网络。SRN在传统的前馈神经网络的基础上,增加了一个隐藏层,这个隐藏层的输出会反馈到自身,形成一个循环。这样,网络就能够在时间上保持状态,即“记忆”之前的输入信息。()

(1)使用numpy实现

'''
@Function: 使用numpy实现SRN
@Author: lxy
@Date: 2024/11/24
'''
import numpy as np
# 初始化输入序列
inputs = np.array([[1.,1.],[1.,1.],[2.,2.]])
print(f"inputs is:\n{inputs}")
# 初始化存储器
state_t = np.zeros(2)
print(f"states_t is:\n{state_t}")
# 初始化权重参数,这里所有权重都为1,bias = 0
w1, w2, w3, w4, w5, w6, w7, w8 = 1., 1., 1., 1., 1., 1., 1., 1.
U1, U2, U3, U4 = 1., 1., 1., 1.
print('============================================')
for t,input_t in enumerate(inputs):print(f"第{t+1}时刻:")print(f'input_t is: {input_t}')print(f'state_t is: {state_t}')# 隐藏层的输入=当前输入 + 前一时刻的状态input_h1 = np.dot([w1,w3],input_t) + np.dot([U2,U4],state_t)input_h2 = np.dot([w2,w4],input_t) + np.dot([U1,U3],state_t)state_t = input_h1, input_h2 # 更新状态(无激活 输入=输出):直接将计算得到的隐藏层输入赋值给state_t# 输入为隐藏层的输出,计算最终输出output_y1 = np.dot([w5, w7], state_t)output_y2 = np.dot([w6, w8], state_t)print(f"outputs is: {output_y1}、{output_y2}")print('============================================')

运行结果:

inputs is:
[[1. 1.][1. 1.][2. 2.]]
states_t is:
[0. 0.]
============================================
第1时刻:
input_t is: [1. 1.]
state_t is: [0. 0.]
outputs is: 4.0、4.0
============================================
第2时刻:
input_t is: [1. 1.]
state_t is: (2.0, 2.0)
outputs is: 12.0、12.0
============================================
第3时刻:
input_t is: [2. 2.]
state_t is: (6.0, 6.0)
outputs is: 32.0、32.0
============================================

可见与手动计算的结果一致 。

(图源) 

(2)在(1)的基础上,增加激活函数tanh

'''
@Function: 使用numpy实现SRN
@Author: lxy
@Date: 2024/11/24
'''
import numpy as np
# 初始化输入序列 3个对应三个时刻
inputs = np.array([[1.,1.],[1.,1.],[2.,2.]])
print(f"inputs is:\n{inputs}")
# 初始化存储器
state_t = np.zeros(2)
print(f"states_t is:\n{state_t}")
# 初始化权重参数,这里所有权重都为1,bias = 0
w1, w2, w3, w4, w5, w6, w7, w8 = 1., 1., 1., 1., 1., 1., 1., 1.
U1, U2, U3, U4 = 1., 1., 1., 1.
print('============================================')
for t,input_t in enumerate(inputs):print(f"第{t+1}时刻:")print(f'input_t is: {input_t}')print(f'state_t is: {state_t}')# 隐藏层的输入=当前输入 + 前一时刻的状态input_h1 = np.dot([w1,w3],input_t) + np.dot([U2,U4],state_t)input_h2 = np.dot([w2,w4],input_t) + np.dot([U1,U3],state_t)# state_t = input_h1, input_h2 # 更新状态(无激活 输出=输入):直接将计算得到的隐藏层输入赋值给state_tstate_t = np.tanh(input_h1),np.tanh(input_h2) # 更新状态(有激活 输出=tanh(输入) )# 输入为隐藏层的输出,计算最终输出output_y1 = np.dot([w5, w7], state_t)output_y2 = np.dot([w6, w8], state_t)print(f"outputs is: {output_y1}、{output_y2}")print('============================================')

运行结果:

inputs is:
[[1. 1.][1. 1.][2. 2.]]
states_t is:
[0. 0.]
============================================
第1时刻:
input_t is: [1. 1.]
state_t is: [0. 0.]
outputs is: 1.9280551601516338、1.9280551601516338
============================================
第2时刻:
input_t is: [1. 1.]
state_t is: (0.9640275800758169, 0.9640275800758169)
outputs is: 1.9984510891336251、1.9984510891336251
============================================
第3时刻:
input_t is: [2. 2.]
state_t is: (0.9992255445668126, 0.9992255445668126)
outputs is: 1.9999753470497836、1.9999753470497836
============================================

 对比(1)(2)输出 结果可以看到无激活函数时,输出值随着时间步的增加而线性增长,从4.0增加到32.0。有激活函数时,输出值在经过激活函数后,增长速度明显减缓。

(3)使用nn.RNNCell实现

'''
@Function: 使用nn.RNNCell实现SRN
@Author: lxy
@Date: 2024/11/24
'''
import torch
batch_size = 1
seq_len = 3  # 序列长度(多少时间步)
input_size = 2  # 输入序列维度
hidden_size = 2  # 隐藏层维度
output_size = 2  # 输出层维度# # 创建RNNCell实例
cell = torch.nn.RNNCell(input_size=input_size,hidden_size=hidden_size)
# 初始化参数 https://zhuanlan.zhihu.com/p/342012463
'''
RNN的weight和bias封装在parameters中,且需要对weight和bias分开初始化
'''
for name,param in cell.named_parameters():if name.startswith('weight'): # 初始化weighttorch.nn.init.ones_(param)else: # 初始化biastorch.nn.init.zeros_(param)# 线性层-> 将隐藏状态映射到输出
linear = torch.nn.Linear(hidden_size,output_size)
# 初始化线性层的权重为1
linear.weight.data = torch.Tensor([[1,1],[1,1]])
# 初始化线性层的偏置为0
linear.bias.data = torch.Tensor([0.0])# 定义输入序列
'''
三维,形状(3, 1, 2)
第一维:序列长度(seq_len),这里是3
第二维:批次大小(batch_size),这里是1
第三维:输入特征的维度(input_size),这里是2
'''
seq = torch.Tensor([[[1,1]],[[1,1]],[[2,2]]])
# 初始化隐藏状态和输出为0
hidden = torch.zeros(batch_size,hidden_size)
output = torch.zeros(batch_size,output_size)
# 遍历序列中的每一个时间步
for idx,input in enumerate(seq):print('===========================')print(f"第{idx+1}时刻:")print(f'Input :{input}')print(f'hidden :{hidden}')hidden = cell(input,hidden) # 使用RNNCell处理当前输入,并更新隐藏状态output = linear(hidden) # # 使用线性层将隐藏状态转换为输出print(f"output :{output}")

运行结果:

===========================
第1时刻:
Input :tensor([[1., 1.]])
hidden :tensor([[0., 0.]])
output :tensor([[1.9281, 1.9281]], grad_fn=<AddmmBackward0>)
===========================
第2时刻:
Input :tensor([[1., 1.]])
hidden :tensor([[0.9640, 0.9640]], grad_fn=<TanhBackward0>)
output :tensor([[1.9985, 1.9985]], grad_fn=<AddmmBackward0>)
===========================
第3时刻:
Input :tensor([[2., 2.]])
hidden :tensor([[0.9992, 0.9992]], grad_fn=<TanhBackward0>)
output :tensor([[2.0000, 2.0000]], grad_fn=<AddmmBackward0>)

(4)使用nn.RNN实现

'''
@Function: 使用nn.RNN实现SRN
@Author: lxy
@Date: 2024/11/24
'''
import torch
# 设置批处理大小
batch_size = 1
# 设置序列长度
seq_len = 3
# 输入序列的维度
input_size = 2
# 隐藏层的维度
hidden_size = 2
# 输出层的维度
output_size = 2
# RNN层的数量
num_layers = 1# 创建RNN实例
cell = torch.nn.RNN(input_size=input_size, hidden_size=hidden_size, num_layers=num_layers)
# 初始化参数
for name, param in cell.named_parameters():if name.startswith('weight'):torch.nn.init.ones_(param)else:torch.nn.init.zeros_(param)# 线性层 将隐藏状态映射到输出
linear = torch.nn.Linear(hidden_size, output_size)
# 初始化线性层的权重为1
linear.weight.data = torch.Tensor([[1,1],[1,1]])
# 初始化线性层的偏置为0
linear.bias.data = torch.Tensor([0.0])# 创建输入序列
'''
三维,形状(3, 1, 2)
第一维:序列长度(seq_len),这里是3
第二维:批次大小(batch_size),这里是1
第三维:输入特征的维度(input_size),这里是2
'''
inputs = torch.Tensor([[[1,1]],[[1,1]],[[2,2]]])
# 初始化隐藏状态为0,这里需要考虑RNN层的数量
hidden = torch.zeros(num_layers, batch_size, hidden_size)
# 通过RNN处理输入序列,并更新隐藏状态
out, hidden = cell(inputs, hidden)# 打印第一个时间步的输入、隐藏状态和输出
print("第1时刻:")
print(f'Input : {inputs[0]}')
print(f'hidden: {[0 , 0]}')
print(f'Output: {linear(out[0])}')
print('======================================')
# 打印第二个时间步的输入、隐藏状态和输出
print("第2时刻:")
print(f'Input : {inputs[1]}')
print(f'hidden: {out[0]}')
print(f'Output: {linear(out[1])}')
print('=======================================')
# 打印第三个时间步的输入、隐藏状态和输出
print("第3时刻:")
print(f'Input : {inputs[2]}')
print(f'hidden: {out[1]}')
print(f'Output: {linear(out[2])}')

【这里不使用循环来打印的原因是因为RNN是一个封装好的循环神经网络层,处理整个序列的前向传播,并返回每个时间步的输出和最后一个时间步的隐藏状态,而RNNCell 是一个基本的循环神经网络单元,只处理单个时间步的输入,所以在(3)中需要循环调用】 

运行结果:

第1时刻:
Input : tensor([[1., 1.]])
hidden: [0, 0]
Output: tensor([[1.9281, 1.9281]], grad_fn=<AddmmBackward0>)
======================================
第2时刻:
Input : tensor([[1., 1.]])
hidden: tensor([[0.9640, 0.9640]], grad_fn=<SelectBackward0>)
Output: tensor([[1.9985, 1.9985]], grad_fn=<AddmmBackward0>)
=======================================
第3时刻:
Input : tensor([[2., 2.]])
hidden: tensor([[0.9992, 0.9992]], grad_fn=<SelectBackward0>)
Output: tensor([[2.0000, 2.0000]], grad_fn=<AddmmBackward0>)

 2 使用RNNCell实现“序列到序列”

b站循环神经网络讲解--刘二大人  实现视频P12中的教学案例 hello--->ohlol

 

'''
@Function: 实现序列到序列:给出hello -->预测ohlol
@Author: lxy
@Date: 2024/11/24
'''
import torch
import torch.nn as nn
import torch.optim as optim# 定义参数
input_size = 4 # 输入特征的维度:对应的是 one-hot 编码的大小(4个字符)
hidden_size = 4 # 隐藏层维度
batch_size = 1  # 批次大小(这里是1,表示一次只处理一个字符)# 字符到索引的映射
idx2char = ['e', 'h', 'l', 'o']# 输入和标签数据
x_data = [1, 0, 2, 2, 3] # 对应字符 ['h', 'e', 'l', 'l', 'o']
y_data = [3, 1, 2, 3, 2] # 对应目标字符 ['o', 'h', 'l', 'o', 'l']# 将字符映射到one-hot编码
one_hot_lookup = [[1, 0, 0, 0],  # 'e' 对应 [1, 0, 0, 0][0, 1, 0, 0],  # 'h' 对应 [0, 1, 0, 0][0, 0, 1, 0],  # 'l' 对应 [0, 0, 1, 0][0, 0, 0, 1]   # 'o' 对应 [0, 0, 0, 1]
]
# 将输入数据 x_data 转换为 one-hot 编码形式
x_one_hot = [one_hot_lookup[x] for x in x_data]# 将输入和标签转换为PyTorch张量
inputs = torch.Tensor(x_one_hot).view(-1, batch_size, input_size)  # (序列长度, 批次大小, 输入维度)
labels = torch.LongTensor(y_data).view(-1, 1)  # (序列长度, 1),每个标签对应一个字符的索引# 定义RNN模型
class Model(nn.Module):def __init__(self, input_size, hidden_size, batch_size):super(Model, self).__init__()self.batch_size = batch_sizeself.input_size = input_sizeself.hidden_size = hidden_sizeself.rnncell = nn.RNNCell(input_size=self.input_size, hidden_size=self.hidden_size)def forward(self, input, hidden):# RNNCell 处理每个输入,并返回新的隐藏状态hidden = self.rnncell(input, hidden)return hiddendef init_hidden(self):return torch.zeros(self.batch_size, self.hidden_size)# 初始化模型、损失函数和优化器
net = Model(input_size, hidden_size, batch_size)
criterion = nn.CrossEntropyLoss()  # 交叉熵损失函数
optimizer = optim.Adam(net.parameters(), lr=0.1)  # 使用 Adam 优化器# 训练模型
for epoch in range(15):loss = 0optimizer.zero_grad()hidden = net.init_hidden() # 初始化隐藏状态print('Predicted string: ', end='')# 遍历输入序列和标签for input, label in zip(inputs, labels):hidden = net(input, hidden)  # 将输入传入网络并获取新的隐藏状态loss += criterion(hidden, label)  # 计算损失并累加_, idx = hidden.max(dim=1)   # 获取预测的字符索引(最大值)print(idx2char[idx.item()], end='')  # 输出对应的字符loss.backward()optimizer.step()print(', Epoch [%d/15] loss=%.4f' % (epoch + 1, loss.item()))

运行结果:

Predicted string: lllll, Epoch [1/15] loss=7.3776
Predicted string: lllll, Epoch [2/15] loss=6.0633
Predicted string: lhlll, Epoch [3/15] loss=4.9989
Predicted string: ohlol, Epoch [4/15] loss=4.0542
Predicted string: ohlol, Epoch [5/15] loss=3.3809
Predicted string: ohlol, Epoch [6/15] loss=3.0262
Predicted string: ohlol, Epoch [7/15] loss=2.7807
Predicted string: ohlol, Epoch [8/15] loss=2.5577
Predicted string: ohlol, Epoch [9/15] loss=2.3574
Predicted string: ohlol, Epoch [10/15] loss=2.2286
Predicted string: ohlol, Epoch [11/15] loss=2.1521
Predicted string: ohlol, Epoch [12/15] loss=2.0685
Predicted string: ohlol, Epoch [13/15] loss=1.9959
Predicted string: ohlol, Epoch [14/15] loss=1.9489
Predicted string: ohlol, Epoch [15/15] loss=1.9217

         可见模型的预测从初期的随机性lllll到 ohlol逐步改进,最终在第 4 个 epoch 开始预测正确的字符顺序。随着训练进行,损失逐渐下降,模型逐步学会了如何更准确地预测下一个字符。实现从hello到ohlol的转换。

3 实现“编码器-解码器”-Seq2Seq

Seq2Seq的Pytorch实现  ----b站配套讲解

seq2seq的PyTorch实现_哔哩哔哩_bilibili  ---代码实战

跟着问题学13——seq2seq详解及代码实战-CSDN博客

Seq2Seq原理详解 - 早起的小虫子 - 博客园

         Seq2seq模型通常用于将一个序列转换为另一个序列。这种模型由两部分组成:编码器(encoder)和解码器(decoder)。编码器将输入序列编码成一个固定长度的向量,解码器则从这个向量生成输出序列。

'''
@功能: 使用PyTorch实现Seq2Seq编码器-解码器: 将输入的英语单词翻译成西班牙语
@作者: lxy
@日期: 2024/11/24
'''import torch
import numpy as np
import torch.nn as nn
import torch.utils.data as Datadevice = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
# S: 表示解码输入的开始符号
# E: 表示解码输出的结束符号
# ?: 用于填充空白序列,当当前批次的数据长度不足n_step时使用letter = [c for c in 'SE?abcdefghijklmnopqrstuvwxyz']
letter2idx = {n: i for i, n in enumerate(letter)}# 示例数据: 英语单词与对应的西班牙语翻译
seq_data = [['hello', 'hola'],  # 'hello' -> 'hola'(英语到西班牙语)['cat', 'gato'],    # 'cat' -> 'gato'['good', 'bueno'],  # 'good' -> 'bueno'
]# Seq2Seq 参数
n_step = max([max(len(i), len(j)) for i, j in seq_data])  # 最大长度(=5)
n_hidden = 128  # 隐藏层维度
n_class = len(letter2idx)  # 类别数(即字符集大小)
batch_size = 3  # 批次大小# 函数:构建训练数据
def make_data(seq_data):enc_input_all, dec_input_all, dec_output_all = [], [], []for seq in seq_data:for i in range(2):seq[i] = seq[i] + '?' * (n_step - len(seq[i]))  # 如 'man??', 'women'# 编码输入:将字符转为索引,并在末尾添加结束符'E'enc_input = [letter2idx[n] for n in (seq[0] + 'E')]  # ['m', 'a', 'n', '?', '?', 'E']# 解码输入:在开头添加起始符'S'dec_input = [letter2idx[n] for n in ('S' + seq[1])]  # ['S', 'w', 'o', 'm', 'e', 'n']# 解码输出:在末尾添加结束符'E'dec_output = [letter2idx[n] for n in (seq[1] + 'E')]  # ['w', 'o', 'm', 'e', 'n', 'E']# 将每个输入转为独热编码enc_input_all.append(np.eye(n_class)[enc_input])dec_input_all.append(np.eye(n_class)[dec_input])dec_output_all.append(dec_output)  # 解码输出不进行独热编码# 返回Tensor格式数据return torch.Tensor(enc_input_all), torch.Tensor(dec_input_all), torch.LongTensor(dec_output_all)# 获取训练数据
enc_input_all, dec_input_all, dec_output_all = make_data(seq_data)# 自定义数据集
class TranslateDataSet(Data.Dataset):def __init__(self, enc_input_all, dec_input_all, dec_output_all):self.enc_input_all = enc_input_allself.dec_input_all = dec_input_allself.dec_output_all = dec_output_alldef __len__(self):  # 返回数据集的大小return len(self.enc_input_all)def __getitem__(self, idx):return self.enc_input_all[idx], self.dec_input_all[idx], self.dec_output_all[idx]# 数据加载器
loader = Data.DataLoader(TranslateDataSet(enc_input_all, dec_input_all, dec_output_all), batch_size, True)# Seq2Seq模型
class Seq2Seq(nn.Module):def __init__(self):super(Seq2Seq, self).__init__()# 编码器:RNN模型self.encoder = nn.RNN(input_size=n_class, hidden_size=n_hidden, dropout=0.5)# 解码器:RNN模型self.decoder = nn.RNN(input_size=n_class, hidden_size=n_hidden, dropout=0.5)# 全连接层,用于输出分类self.fc = nn.Linear(n_hidden, n_class)def forward(self, enc_input, enc_hidden, dec_input):# enc_input:输入的编码数据 [batch_size, n_step+1, n_class]# dec_input:输入的解码数据 [batch_size, n_step+1, n_class]enc_input = enc_input.transpose(0, 1)  # 转置为 [n_step+1, batch_size, n_class]dec_input = dec_input.transpose(0, 1)  # 转置为 [n_step+1, batch_size, n_class]# 编码器输出:h_t 是最后的隐藏状态_, h_t = self.encoder(enc_input, enc_hidden)# 解码器输出:outputs 是解码过程中的所有输出outputs, _ = self.decoder(dec_input, h_t)# 通过全连接层输出最终结果model = self.fc(outputs)  # [n_step+1, batch_size, n_class]return model# 实例化模型
model = Seq2Seq().to(device)
criterion = nn.CrossEntropyLoss().to(device)  # 交叉熵损失函数
optimizer = torch.optim.Adam(model.parameters(), lr=0.001)  # Adam优化器# 训练过程
for epoch in range(5000):for enc_input_batch, dec_input_batch, dec_output_batch in loader:# 初始化隐藏状态h_0 = torch.zeros(1, batch_size, n_hidden).to(device)# 将数据移至设备enc_input_batch, dec_input_batch, dec_output_batch = (enc_input_batch.to(device), dec_input_batch.to(device), dec_output_batch.to(device))# 训练模型,获取预测结果pred = model(enc_input_batch, h_0, dec_input_batch)# 计算损失pred = pred.transpose(0, 1)  # [batch_size, n_step+1, n_class]loss = 0for i in range(len(dec_output_batch)):loss += criterion(pred[i], dec_output_batch[i])  # 计算每一批次的损失# 每1000次迭代输出一次损失if (epoch + 1) % 1000 == 0:print('Epoch:', '%04d' % (epoch + 1), 'lost =', '{:.6f}'.format(loss))# 反向传播并优化optimizer.zero_grad()loss.backward()optimizer.step()# 测试函数:翻译单词
def translate(word):enc_input, dec_input, _ = make_data([[word, '?' * n_step]])enc_input, dec_input = enc_input.to(device), dec_input.to(device)hidden = torch.zeros(1, 1, n_hidden).to(device)  # 初始化隐藏状态output = model(enc_input, hidden, dec_input)# 获取最大概率的预测值predict = output.data.max(2, keepdim=True)[1]decoded = [letter[i] for i in predict]translated = ''.join(decoded[:decoded.index('E')])  # 直到'E'为止return translated.replace('?', '')  # 去掉填充符号# 测试翻译效果
print('测试')
for seq in seq_data:word = seq[0]  # 获取英语单词translated_word = translate(word)  # 获取翻译结果print(f'{word} -> {translated_word}')

运行结果;

Epoch: 1000 lost = 0.001182
Epoch: 2000 lost = 0.000338
Epoch: 3000 lost = 0.000147
Epoch: 4000 lost = 0.000075
Epoch: 5000 lost = 0.000041
测试
hello -> hola
cat?? -> gato
good? -> bueno

lost逐渐减小,这表明模型在不断优化,误差在不断减小 。

4 简单总结nn.RNNCell、nn.RNN

(1)nn.RNNCell

  • 单步处理RNNCell 是最基本的RNN单元,只处理单个时间步的数据。要处理整个序列,需要在外部循环中逐个时间步地调用RNNCell
  • 主要参数:input_size (int): 输入特征的维度。hidden_size (int): 隐藏状态的维度。

                        rnn_cell = nn.RNNCell(input_size, hidden_size)

  • 返回结果:当前时间步的输出 +更新后的隐藏状态

(2)nn.RNN

  • 批量处理RNN 是一个完整的RNN层,可以一次性处理整个序列。
  • 主要参数:

        input_size (int): 输入特征的维度。

        hidden_size (int): 隐藏状态的维度。

        num_layers (int, optional): RNN层的数量,默认为1。

        batch_first (bool, optional): 输入和输出张量的形状,默认为False。

        rnn = nn.RNN(input_size, hidden_size, num_layers=1, batch_first=True)

  • 返回结果: 整个序列的输出 +  最后一个时间步的隐藏状态
  •    🗣 batch_first,默认是 False,输入数据的格式为(seq_len, batch, input_size)。
         当batch_first=True时,输入序列的格式应该为( batch,seq_len, input_size)

当num_layers=1时: 

 当num_layers=3时:

 

5 谈一谈对“序列”、“序列到序列”的理解

序列:一组按照一定顺序排列的元素(数字、元素、字符等)。每个元素在序列中的位置是固定的,并且具有一定的顺序性。

序列到序列:一种深度学习模型,通过深度学习的方式,将一个序列转化为另一个序列。通过编码器提取输入序列的特征,再通过解码器生成输出序列。

6 总结本周理论课和作业,写心得体会

        

   通过实现作业的任务真正理解了RNN的原理,同时继卷积神经网络的Conv2d之后又认识到了循环神经网络中的nn.RNNCell和nn.RNN。另一个比较大的收获就是编码器和解码器的实现,第一次听说是在读“Attention is all you need”这篇论文中见到的(不过当时只是简单了解理论),这次作业中实现编码是用的one-hot编码(比较简单),还有更高级的词嵌入(Word Embeeding)方式。根据“编码器-解码器”架构的设计, 可以使用两个循环神经网络来设计一个序列到序列(Seq2Seq)学习的模型。

(部分笔记) 

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

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

相关文章

利用Docker容器技术部署发布web应用程序

Docker是什么&#xff1f; docker 是一个开源的应用容器引擎&#xff0c;可以帮助开发者打包应用以及依赖包到一个可移植的容器中&#xff0c;然后发布到任何流行的Linux机器上&#xff0c;也可以实现虚拟化&#xff0c;容器是完全使用沙箱机制&#xff0c;相互之间不会有任何…

【AI学习】Mamba学习(十八):S6的硬件感知设计

上一篇Mamba的文章提到&#xff0c;S6 models这个名称的由来是&#xff1a;S4 models with a selection mechanism and computed with a scan。 所以&#xff0c;S6模型首先是选择机制&#xff1a;先前模型的一个关键限制对选择性复制和归纳等重要合成任务不够适用&#xff0c…

Bug Fix 20241122:缺少lib文件错误

今天有朋友提醒才突然发现 gitee 上传的代码存在两个很严重&#xff0c;同时也很低级的错误。 因为gitee的默认设置不允许二进制文件的提交&#xff0c; 所以PH47框架下的库文件&#xff08;各逻辑层的库文件&#xff09;&#xff0c;以及Stm32Cube驱动的库文件都没上传到Gi…

实现Excel文件和其他文件导出为压缩包,并导入

导出 后端&#xff1a; PostMapping("/exportExcelData")public void exportExcelData(HttpServletRequest request, HttpServletResponse response, RequestBody ResData resData) throws IOException {List<Long> menuIds resData.getMenuIds();List<Co…

4.4 JMeter 请求参数类型详解

欢迎大家订阅【软件测试】 专栏&#xff0c;开启你的软件测试学习之旅&#xff01; 文章目录 前言1 参数&#xff08;键值对形式&#xff09;2 消息体数据&#xff08;JSON/XML 格式&#xff09;3 文件上传 前言 在使用 JMeter 进行接口测试时&#xff0c;常见的请求参数类型主…

【山大909算法题】2014-T1

文章目录 1.原题2.算法思想3.关键代码4.完整代码5.运行结果 1.原题 为带表头的单链表类Chain编写一个成员函数Reverse&#xff0c;该函数对链表进行逆序操作&#xff08;将链表中的结点按与原序相反的顺序连接&#xff09;&#xff0c;要求逆序操作就地进行&#xff0c;不分配…

Apache OFBiz xmlrpc XXE漏洞(CVE-2018-8033)

目录 1、漏洞描述 2、EXP下载地址 3、EXP利用 1、漏洞描述 Apache OFBiz是一套企业资源计划&#xff08;ERP&#xff09;系统。它提供了广泛的功能&#xff0c;包括销售、采购、库存、财务、CRM等。 Apache OFBiz还具有灵活的架构和可扩展性&#xff0c;允许用户根据业务需求…

路由传参、搜索、多选框勾选、新增/编辑表单复用

前言&#xff1a; 记录添加运动员页面功能的具体实现 ①由赛事管理页面跳转时路由传参&#xff08;携带该页面表格中莫某条数据对应的赛事id到另一个页面&#xff09;&#xff1b; ②搜索框实时搜索&#xff1b; ③多选框勾选搜索&#xff1b; ④新增表单和编辑表单复用&a…

【11-20期】Java面试进阶:深入解析核心问题与实战案例

&#x1f680; 作者 &#xff1a;“码上有前” &#x1f680; 文章简介 &#xff1a;Java &#x1f680; 欢迎小伙伴们 点赞&#x1f44d;、收藏⭐、留言&#x1f4ac; 文章题目&#xff1a;Java面试进阶&#xff1a;深入解析11-20期核心问题与实战案例 摘要&#xff1a; 本篇…

Java后端如何进行文件上传和下载 —— 本地版

简介&#xff1a; 本文详细介绍了在Java后端进行文件上传和下载的实现方法&#xff0c;包括文件上传保存到本地的完整流程、文件下载的代码实现&#xff0c;以及如何处理文件预览、下载大小限制和运行失败的问题&#xff0c;并提供了完整的代码示例。 大体思路 1、文件上传 …

基于SpringBoot的工程教育认证的计算机课程管理系统【附源码】

基于SpringBoot的工程教育认证的计算机课程管理系统 效果如下&#xff1a; 系统登录页面 教师主页面 学生管理页面 课程信息页面 通知公告页面 学生课程管理页面 学生课程信息页面 研究背景 随着信息技术的快速发展&#xff0c;计算机课程管理系统的应用在教育领域变得愈发重…

适用于学校、医院等低压用电场所的智能安全配电装置

引言 电力&#xff0c;作为一种清洁且高效的能源&#xff0c;极大地促进了现代生活的便捷与舒适。然而&#xff0c;与此同时&#xff0c;因使用不当或维护缺失等问题&#xff0c;漏电、触电事件以及电气火灾频发&#xff0c;对人们的生命安全和财产安全构成了严重威胁&#xf…

如何编写一个 Vue 3 应用:模板插值示例

Vue.js 是一个渐进式的 JavaScript 框架&#xff0c;用于构建用户界面。在本篇博客中&#xff0c;我们将通过一个简单的示例来学习如何使用 Vue 3 创建一个基本的应用。这个示例将展示如何使用 Vue 的模板插值和事件处理来构建一个简单的点击计数器。 步骤 1: 准备工作 首先&…

PostgreSQL详细安装教程

#安装PostgreSQL的yum仓库 sudo yum install -y https://download.postgresql.org/pub/repos/yum/reporpms/EL-7-x86_64/pgdg-redhat-repo-latest.noarch.rpm#安装PostgreSQL 15版本 sudo yum install -y postgresql15-server#初始化数据库&#xff08;若要自定义数据库存储目录…

uniapp中使用Mescroll实现下拉刷新与上拉加载项目实战

如何在UniApp中使用Mescroll实现下拉刷新与上拉加载 前言 下拉刷新和上拉加载更多成为了提升用户体验不可或缺的功能。UniApp作为一个跨平台的应用开发框架&#xff0c;支持使用Vue.js语法编写多端&#xff08;iOS、Android、H5等&#xff09;应用。Mescroll作为一款专为Vue设…

js:基础

js是什么 JavaScript是一种运行在客户端的编程语言&#xff0c;实现人机交互的效果 js只要有个浏览器就能跑 js可以做网页特效、表单验证、数据交互、服务端编程 服务端编程是前端人拿他们特有的后端语言node.js来干后端干的事情 js怎么组成 JavaScriptECMAScript(语言基…

Cannot find a valid baseurl for repo: centos-sclo-rh/x86_64

yum install 报错: Cannot find a valid baseurl for repo: centos-sclo-rh/x86_64 CentOS7的SCL源在2024年6月30日停止维护了。 当scl源里面默认使用了centos官方的地址&#xff0c;无法连接&#xff0c;需要替换为阿里云。 cd /etc/yum.repos.d/ 找到 CentOS-SCLo-scl.repo 和…

35 基于单片机的精确电压表DA-AD转换

目录 一、主要功能 二、硬件资源 三、程序编程 四、实现现象 一、主要功能 基于51单片机&#xff0c;采用DAC0832和ADC0832检测电压&#xff0c;0到8.5V&#xff0c;设计复位电路 LED管显示实际稳压值&#xff0c;初始电压0 二、硬件资源 基于KEIL5编写C代码&#xff0c…

微信小程序2-地图显示和地图标记

一、index修改页面&#xff0c;让页面能够显示地图和一个添加标记的按钮。 index.wxml <scroll-view class"scrollarea" scroll-y type"list"><view class"index_container"><map id"map" style"width: 100%; h…

【一篇搞定配置】网络分析工具WireShark的安装与入门使用

&#x1f308; 个人主页&#xff1a;十二月的猫-CSDN博客 &#x1f525; 系列专栏&#xff1a; &#x1f3c0;各种软件安装与配置_十二月的猫的博客-CSDN博客 &#x1f4aa;&#x1f3fb; 十二月的寒冬阻挡不了春天的脚步&#xff0c;十二点的黑夜遮蔽不住黎明的曙光 目录 1.…