序列模型的使用示例

news2024/12/17 15:47:48

序列模型的使用示例

  • 1 RNN原理
    • 1.1 序列模型的输入输出
    • 1.2 循环神经网络(RNN)
    • 1.3 RNN的公式表示
    • 2 数据的尺寸
  • 3 PyTorch中查看RNN的参数
  • 4 PyTorch中实现RNN
    • (1)RNN实例化
    • (2)forward函数
    • (3)多层RNN的参数尺寸
  • 5 实战

1 RNN原理

1.1 序列模型的输入输出

假如你想要建立一个序列模型,它的输入语句是这样的: “Harry Potter and Herminoe Granger invented a new spell.”,(句中的人名都是出自于 J.K.Rowling 笔下的系列小说 Harry Potter)。假如你想要建立一个能够自动识别句中人名的序列模型,那么这就是一个命名实体识别问题。命名实体识别系统可以用来查找不同类型的文本中的人名、公司名、时间、地点、国家名和货币名等等。

将输入的句子定义为x,上述的输入语句中共有9个单词,可以对输入进行编号,用 x < t > x^{<t>} x<t> 来索引句子中第t个单词的位置。假如这个序列模型有一个输出序列,使得输入的每个单词都对应一个输出值,这个值为1表示对应的输入单词是人名的一部分,为0表示不是人名的一部分,同时用 y < t > y^{<t>} y<t> 对输出进行编号,如下图所示:
在这里插入图片描述

同时我们用 T x T_x Tx 来表示输入序列的长度,这个例子中输入是9个单词,所以 T x = 9 T_x= 9 Tx=9 ,同样,可用 T y T_y Ty 来表示输出序列的长度。输出序列的长度可以和输入序列的相同,也可以不同。

之前我们在DNN中用 x ( i ) x^{(i)} x(i) 表示第i个训练样本,这里我们可以用 x ( i ) < t > x^{(i)<t>} x(i)<t> 来表示第i个样本的第t个元素, T x ( i ) T{x}^{(i)} Tx(i) 代表第i个训练样本的输入序列长度。同样的,用 y ( i ) < t > y^{(i)<t>} y(i)<t> 来表示第i个样本的第t个元素对应的输出值, T y ( i ) T{y}^{(i)} Ty(i) 代表第i个训练样本的输出序列长度。

我们该怎样表示序列中的一个单词呢,比如Harry这个单词,对应的索引 x ( 1 ) x^{(1)} x(1) 是什么?自然语言处理(Natural Language Process,简称NLP)中,想要表示一个句子里的单词,第一件事是做一张词表,有时也称为词典,我们这里用10000个单词的词典举例。
在这里插入图片描述

当词典构建好之后,遍历数据集,给数据集中的每一个单词匹配一个one-hot编码,即用一个10000维的向量表示,例如单词a出现在词典中的第一个位置,那么当输入a时,就生成一个10000维的向量,该向量的第一个元素是1,其他都是0。

因此,给定上述输入语句,可以生成如下的向量:
在这里插入图片描述

图中其实就是One-hot向量,因为向量中只有一个位置是1,其余都是0。

如果遇到不在词典中的单词,那么就创建一个叫做 Unknow Word 的伪单词,用<UNK>作为标记,来表示不在词表中的单词。

这里我们用的词典只有一万个单词,正常商业应用的词典,词汇量3万至5万比较常见,10万的也有,某些大型互联网公司用的词典,其单词数量甚至超过百万。

1.2 循环神经网络(RNN)

这节我们来谈一谈怎样建立模型,来实现从X到Y的映射。

我们先尝试一下标准神经网络。前面的例子,“Harry Potter and Herminoe Granger invented a new spell.”有9个单词,因此需要把这9个单词一次性输入网络,如图所示:
在这里插入图片描述

为了方便输入,可以将单词索引成一个整数,但使用标准神经网络有以下两个问题:

  1. 上述例子是9个单词,所以输入单元是9个,但如果另一条句子有10个单词,那么这张网络将不再适用。
  2. 这种网络并不共享从文本的不同位置上学到的特征。具体来说,经过训练后的神经网络,当Harry出现在位置 x < 1 > x^{<1>} x<1> 时,会将其判定为人名的一部分,那么如果Harry 出现在其他位置,比如 x < 8 > x^{<8>} x<8> 时,很有可能因为位置不同,权重参数不同,神经网络不会将其认为是人名的组成部分。

为了解决上述问题,这里引入循环神经网络的概念。

还是“Harry Potter and Herminoe Granger invented a new spell.”这句话,如果从左往右逐个读取单词,当读到第一个单词Harry时,将其对应的One-hot向量 x < 1 > x^{<1>} x<1> 输入到神经网络,输出 y ^ < 1 > \hat{y} ^{<1>} y^<1>,并判断其是否为人名的一部分,用下面的图表示:

在这里插入图片描述

这里只有两层神经网络,一个隐藏层(图中的小圆圈可以认为是神经元),一个输出层。设隐藏层的激活值为 a < 1 > a^{<1>} a<1> ,输入第一个单词的过程称为时间步1。

接着读取第二个单词,将 x < 2 > x^{<2>} x<2> 输入神经网络, x < 2 > x^{<2>} x<2> 乘以权重之后,并不是马上激活,而是要先加上来自时间步1的激活值 a < 1 > a^{<1>} a<1>,然后再将相加后的结果激活,获得激活值 a < 2 > a^{<2>} a<2> ,最后计算输出层,获得预测值 y ^ < 2 > \hat{y} ^{<2>} y^<2>。(这样说不太准确,但这样说方便理解,后面会详细讲解)

依此类推,在计算激活值的时候,加入上一时步的激活值,直至整条语句读取结束,这就是循环神经网络,结构如下:
在这里插入图片描述

为了使各个部分结构一致,在零时刻,需要构造一个激活值 a < 0 > a^{<0>} a<0> ,这通常是个零向量,加上激活值 a < 0 > a^{<0>} a<0> 后的结构如下图所示:
在这里插入图片描述

在某些文献中,你看到的循环神经网络有可能是下面这种形式:
在这里插入图片描述

在每一个时间步中,输入 x < t > x ^{<t>} x<t> 输出 y < t > y ^{<t>} y<t> ,用一个带箭头的圆圈,表示把激活值重新输入网络,圆圈上面加上一个方块,表示激活值输回网络会延迟一个时间步。

循环神经网络每个时间步的参数也是一致的,也就是说,第一次输入时的网络和第二次、第三次、第t次输入时,网络的参数完全一致。

因为只有一个隐藏层,因此我们设隐藏层的权重参数为Wax,各个时步的Wax相同。我们设水平联系的权重参数为Waa,在计算本时步激活值的时候,把上一层的激活值乘以Waa,然后再加到本时步,各个时步的Waa相同。本时步的激活值算出来之后,需要计算输出值,这时需要乘以一个输出权重Wya,再将结果激活作为输出值。Wax、Waa、Wya这些参数,第一个下标表示用来计算什么,第二个下标表示这个参数该乘什么。

将以上参数标在图片中,则是下面这个结果:
在这里插入图片描述

标在简化循环图中,则是下面的结果:
在这里插入图片描述

1.3 RNN的公式表示

如果把上面的过程用公式表达,则如下所示:
a < 1 > = g ( W a a a < 0 > + W a x x < 1 > + b a ) y < 1 > = g ( W y a a < 1 > + b y ) \begin{array}{l} a^{<1>}=g\left(W_{a a} a^{<0>}+W_{a x} x^{<1>}+b_{a}\right) \\ y^{<1>}=g\left(W_{y a} a^{<1>}+b_{y}\right) \end{array} a<1>=g(Waaa<0>+Waxx<1>+ba)y<1>=g(Wyaa<1>+by)

这里面g表示激活函数,一般情况下,隐藏层使用tanh作为激活函数,有时也用Relu,输出层看网络要解决的问题,如果是二分类问题,就用sigmoid,如果是k分类问题,就用softmax。如果想区分隐藏层和输出层的激活函数,那么可以对g标号,如下所示:
a < t > = g 1 ( W a a a < t − 1 > + W a x x < t > + b a ) y < t > = g 2 ( W y a a < t > + b y ) \begin{array}{l} a^{<t>}=g_1\left(W_{a a} a^{<t-1>}+W_{a x} x^{<t>}+b_{a}\right) \\ y^{<t>}=g_2\left(W_{y a} a^{<t>}+b_{y}\right) \end{array} a<t>=g1(Waaa<t1>+Waxx<t>+ba)y<t>=g2(Wyaa<t>+by)

其中,计算激活值的公式可以简化表示如下:
a ⟨ t ⟩ = g 1 ( W a [ a ⟨ t − 1 ⟩ , x ⟨ t ⟩ ] T + b a ) a^{\langle t\rangle}=g_{1}\left(W_{a}\left[a^{\langle t-1\rangle}, x^{\langle t\rangle}\right]^{T}+b_{a}\right) at=g1(Wa[at1,xt]T+ba)
这里 W a = [ W a a , W a x ] W_{a}=\left[W_{a a}, W_{a \mathrm{x}}\right] Wa=[Waa,Wax]

假设单词库里有一万个单词,那么 x < t > x^{<t>} x<t> 的维度为(10000, 1),若隐藏层有100个神经元,那么 a < t > a^{<t>} a<t> 的维度为(100, 1), [ a < t − 1 > , x < t > ] T \left[a^{<t-1>}, x^{<t>}\right]^{T} [a<t1>,x<t>]T 的维度为(10100, 1)。Waa的维度就是(100,100),Wax的维度就是(100,10000),把这两个矩阵整合之后,Wa就是(100,10100)。

同样对于输出层的公式,也可以使用更简单的方式重写: y < t > = g 2 ( W y a < t > + b y ) y^{<t>}=g_{2}\left(W_{y} a^{<t>}+b_{y}\right) y<t>=g2(Wya<t>+by)
现在 W y W_y Wy b y b_y by W a W_a Wa b a b_a ba,下标表示的是分别会输出什么类型的量。

使用上述记法,当我们建立更复杂模型时就能够简化我们要用到的符号。

如果将循环神经网络的结果画得更工整一点,将是下面的图形:
在这里插入图片描述
这种结构的循环神经网络有一个缺点,就是在计算t时步时,仅仅使用了t时步以前的计算结果,没有使用t时步之后的结果。比如,现在有两个句子,“Teddy Roosevelt was a great President.”(中文意思是 西奥多罗斯福是一位伟大的总统,因为他的小名也叫泰迪,因此也经常被称为“Teddy Roosevel”),“Teddy bears are on sale!”(正在销售泰迪熊),如果使用相同的循环神经网络从左往右扫描句子,那么获得的第一个单词都是Teddy,因为是第一个,都没有从上一层传过来的激活值,那么会获得相同的输出,前者Teddy是人名,后者是熊,因此两者的输出,必有一个有错。
针对这个问题,可以使用双向循环神经网络。不过本文的重点是RNN的原理和过程,以及在PyTorch中的数据处理,目的是了解这一类模型的调用的和数据处理方式,讲原理只是为了后面讲接口时能更直观。

2 数据的尺寸

这里先说明一下,本节的资料来源和上节不一样,因此符号定义和上面的有所不同,了解原理即可。

RNN每次输入一个单词,那么 x t x_t xt 就是该单词对应的One-hot向量,尺寸为[vector_length][1,vector_length],向量长度也是特征长度,因此也可以用[feature len]或者[1, feature len]来表示。

使用PyTorch的并行技术,给模型喂数据的时候,每次都喂一个batch。假设每个batch由5个句子各出一个单词组成,则batch_size=5,那么就相当于有5条生产线(这5条生产线共享参数),每次喂数据的时候,都是给这5条生产线各喂一个单词,所以 x t x_t xt 的尺寸为[batch_size, vector_length]

如果觉得每次输入一个单词太慢,那么可以一次输入, 假如每条句子都是8个单词,那么x的尺寸是[8, 5, feature len],如果每次输入的单词个数是seq len,那么x的尺寸是[batch_size, seq len, feature len]seq len表示句子长度。

为了便于探讨RNN中参数的尺寸,下面我们不对输出层加激活函数,假设第t时步的计算过程如下图所示:
在这里插入图片描述

这里 W x h W_{xh} Wxh W h h W_{hh} Whh W h y W_{hy} Why ,第一个下标表示这个参数该乘什么,第二个下标表示这个参数用来计算什么,和前面介绍原理时刚好相反。

上图中 x t x_t xt 是每个时步输入的数据,它的尺寸是[batch_size, feature len],假设隐藏层的神经元个数为hidden length,那么隐藏层的输出 h t h_t ht 的尺寸就是[batch_size, hidden len]

那么 W x h W_{xh} Wxh 参数的尺寸为[feature len, hidden len],它的意义是压缩数据的维度,将 x t x_t xt 的特征长度由feature len压缩为hidden len
因为循环神经网络每个时间步的参数一致,所以 h t − 1 h_{t-1} ht1 的尺寸也为[batch_size, hidden len] h t − 1 W h h h_{t-1} W_{h h} ht1Whh 的尺寸要和 x t W x h + b h x_{t} W_{x h}+b_{h} xtWxh+bh 的一致,因此 W h h W_{hh} Whh 的尺寸为[hidden len, hidden len]

一般情况下, y t y_t yt 的特征长度为1,因此其尺寸为[batch_size, 1],那么 W h h W_{h h} Whh 的尺寸为[hidden len, 1]

3 PyTorch中查看RNN的参数

import torch
import torch.nn as nn

model = nn.RNN(input_size=10, hidden_size=5, num_layers=1)
'''循环层的输入是10,即表示输入数据对应的One-hot向量长度是10,
    隐藏层里有5个元素,循环层有1层'''
print(model)    # 打印网络的结构
print(model._parameters.keys())     # 打印参数结构
'''打印参数的尺寸'''
print('----------------------------------------')
print(model.weight_ih_l0.size())    # W_xh.T
print(model.weight_hh_l0.size())    # W_hh.T
print(model.bias_hh_l0.size())
print(model.bias_ih_l0.size())
'''参数名的末尾l0表示第0层,0 layer'''
'''如果RNN有3层,那么会有l0,l1,l2'''

输出

RNN(10, 5)
odict_keys(['weight_ih_l0', 'weight_hh_l0', 'bias_ih_l0', 'bias_hh_l0'])
----------------------------------------
torch.Size([5, 10])
torch.Size([5, 5])
torch.Size([5])
torch.Size([5])

上面这段程序有几点需要注意:
1 model.weight_ih_l0并不是 W x h W_{xh} Wxh,而是 W x h T W_{xh}{ }^{T} WxhT,即是转置后的结果。
2 隐藏层使用的结构是这样的: h t = tanh ⁡ ( x t W x h + b x h + h t − 1 W h h + b h h ) h_{t}=\tanh \left(x_{t} W_{x h}+b_{x h}+h_{t-1} W_{h h}+b_{h h}\right) ht=tanh(xtWxh+bxh+ht1Whh+bhh),我们一般写式子的时候,常常把 b x h b_{x h} bxh b h h b_{h h} bhh 合并成 b h b_{h} bh
3 上面的model中,没有计算 y t y_t yt 的模块,如果需要计算 y t y_t yt 的模块,需要增加一个线性层模块(见实战环节)。

4 PyTorch中实现RNN

(1)RNN实例化

调用nn.RNN可以创建RNN模型,它的__init__有四个参数:

input_size,单词的编码长度
hidden_size,隐藏层的单元个数
num_layer,RNN的层数,默认为1
batch_first,表示batch_size这个维度是否在最前面,默认为False,False表示输入数据的结构是[seq len,batch_size,feature],为True则表示输入数据的结构是[batch_size,seq len,feature]

通过nn.RNN创建的RNN模型,没有线性层,需要自己手动添加。
因为我们习惯将batch_size维度放到最前面,下面的关于输入输出尺寸的讨论,都是在batch_first=True的情形下。

(2)forward函数

输入输出分别为

out, ht = forward(self, x, h0)

假如有3句话(三条生产线),每句话都有5个单词,单词用长度为100的向量来编码,那么x的尺寸为[3, 5, 100],RNN中,可以使用并行化技术,一次性把所有的x输入到RNN中,不需要一个单词一个单词地喂。

out是每个时间步的最后一个隐藏层的输出,尺寸为[batch_size, Seq len, hidden],其中Seq len是每句话的单词数量,比如可以是5。
无论RNN有多少层,out的尺寸都是[batch_size, Seq len, hidden],也就是说,RNN中各个隐藏层的神经元数量都是hidden。

ht是最后一个时间步的每一层的输出,尺寸为[batch_size, num_layers, hidden len],而第一个时间步由于没有上一轮的输入,因此第一时步使用h0,h0的尺寸也为[batch_size, num_layers, hidden len]

(3)多层RNN的参数尺寸

rnn = nn.RNN(100, 10, 2, batch_first=True)实例化一个2层的RNN,2层指的是隐藏层数量,这个两层的模型,输入样本的特征长度为100,输出长度为10,那么第一层的输出数据的特征长度是多少?
每个时步第一层的输出的长度就是10,也就是说,多层RNN,在第一层就完成了数据的维度压缩。

5 实战

房价预测模型:已知某地区连续50个月的房价,根据已有数据,预测第51个月的房价。
使用RNN来预测,代码如下:

import numpy as np
import torch
from matplotlib import pyplot as plt
import torch.nn as nn
import torch.optim as optim
import torch.functional as F
'''房价预测模型'''

'''超参数'''
num_time_steps = 50     # 50个时步
input_size = 1          # 数据编码长度是1
hidden_size = 16        # 隐藏层有16个单元
output_size = 1         # 输出层只有1个单元
lr = 0.01               # 学习率是0.01


class Net(nn.Module):

    def __init__(self, ):
        super(Net, self).__init__()

        '''循环层'''
        self.rnn = nn.RNN(
            input_size=input_size,
            hidden_size=hidden_size,
            num_layers=1,
            batch_first=True,
        )
        '''batch_first默认为False,False表示输入数据的结构是[seq len,batch_size,feature],
        为True表示输入数据的结构是[batch_size,seq len,feature]'''

        '''参数初始化'''
        # for p in self.rnn.parameters():
        #     nn.init.normal_(p, mean=0.0, std=0.001)

        '''线性层'''
        self.linear = nn.Linear(hidden_size, output_size)

    def forward(self, x, hidden_prev):

        out, hidden_prev = self.rnn(x, hidden_prev)
        '''x的尺寸为[batch_size, seq len, feature len],
        out的尺寸为[batch_size, seq len, hidden_size]'''

        out = out.view(-1, hidden_size)  # 打平的目的是为了送到线性层
        '''这里假设标签是二维的,其尺寸为[50, 16]
        为了让标签值和预测值能够进行比较,因此将out打平,打平后,50等效于“batch_size维度为50”'''
        out = self.linear(out)
        out = out.unsqueeze(dim=0)       # 插入真正的batch_size维度
        '''如果不插入一个新的维度,那么out的输出将变成[50, 1],
        而在定义数据时,y被定义成了[1, 50, 1],因此需要插入一个新的维度'''

        '''其实上面的步骤可以先不打平,而是先输入到线性层,输出的结果直接是[1,50,1]'''
        return out, hidden_prev


'''需要注意的是,房价预测模型,其自变量不是月份,而是前面若干个月的房价,
也就是说,上述Net中的x,是房价,不是时间。
因此'''

model = Net()
criterion = nn.MSELoss()
optimizer = optim.Adam(model.parameters(), lr)

hidden_prev = torch.zeros(1, 1, hidden_size)	# h0的初始值

# 训练
for iter in range(1000):
    start = np.random.randint(3, size=1)[0]
    '''从0,1,2里面随机抽取一个数,为什么要从3个数字里面抽一个?
    因为如果每次都是从0开始,RNN很容易记住'''

    time_steps = np.linspace(start, start + 10, num_time_steps)
    data = np.sin(time_steps)
    data = data.reshape(num_time_steps, 1)
    x = torch.tensor(data[:-1]).float().view(1, num_time_steps - 1, 1)
    y = torch.tensor(data[1:]).float().view(1, num_time_steps - 1, 1)

    '''由于前面实例化的时候,batch_first=True,这里将 x 和 y 重塑成[1, 49, 1]之后,
    数据的batch_size = 1,但有49条生产线,房价可以有一个数来表征,因此feature len=1
    '''

    '''因为num_time_steps=50,因此有50个时步,所以data的下标是0-49,
    data[:-1]表示从data[0]到data[48],
    data[1:]表示从data[1]到data[49]
    
    该算法的思路就是,根据第1天的房价(x[0])预测第2天的房价(output[0]),
    再将其与第二天的真实房价y[0]进行对比,求出第一天的误差。
    其他天的误差也是类似,最后根据前49天的房价x,求出从第2-50天的房价预测值,
    利用第2-50天的房价真实值与预测值之间的误差,训练模型。
    这就是为什么训练的时候 x 和 y 的长度是49的原因。
    '''

    output, hidden_prev = model(x, hidden_prev)
    hidden_prev = hidden_prev.detach()
    '''detach()的作用是脱离原来的计算图,使其不再需要梯度信息,这样参数反向传播
    因为该算法每轮迭代都使用上一轮迭代的hidden_prev,如果hidden_prev不与原来的计算图脱钩,
    那么求导的时候,会沿着hidden_prev进入上一轮迭代的计算图,这样就会报错,说视图在计算图上求两次导'''

    loss = criterion(output, y)
    model.zero_grad()
    loss.backward()
    # for p in model.parameters():
    #     print(p.grad.norm())
    # torch.nn.utils.clip_grad_norm_(p, 10)
    optimizer.step()

    '''没迭代100次,输出一次'''
    if iter % 100 == 0:
        print("Iteration: {} loss {}".format(iter, loss.item()))

'''其实可以在每一轮循环的时候,都使用0作为hidden_prev,因为你不知道从什么位置开始,
所以无法估计出第-1时步的结果是多少,因此hiiden_prev选择什么值都是不完美的'''

# 预测房价的趋势
start = np.random.randint(3, size=1)[0]
time_steps = np.linspace(start, start + 10, num_time_steps)
data = np.sin(time_steps)
data = data.reshape(num_time_steps, 1)
x = torch.tensor(data[:-1]).float().view(1, num_time_steps - 1, 1)
y = torch.tensor(data[1:]).float().view(1, num_time_steps - 1, 1)

predictions = []
input = x[:, 0, :]
hidden_prev = torch.zeros(1, 1, hidden_size)

'''只需要知道第一天的值,就能求得后面49天的房价'''
for _ in range(x.shape[1]):
    '''循环49次,一个一个喂数据
    将上一轮的输出(预测值)作为本轮的输入'''
    input = input.view(1, 1, 1)
    (pred, hidden_prev) = model(input, hidden_prev)
    input = pred
    '''将每一轮的输出存进列表'''
    predictions.append(pred.detach().numpy().ravel()[0])

x = x.data.numpy().ravel()
y = y.data.numpy()
plt.scatter(time_steps[:-1], x.ravel(), s=90)
plt.plot(time_steps[:-1], x.ravel())

plt.scatter(time_steps[1:], predictions)
plt.show()

输出:

Iteration: 0 loss 0.8515104055404663
Iteration: 100 loss 0.0022286889143288136
Iteration: 200 loss 0.0038173357024788857
Iteration: 300 loss 0.004501968156546354
Iteration: 400 loss 0.0035968958400189877
Iteration: 500 loss 0.0017438693903386593
Iteration: 600 loss 0.002284669317305088
Iteration: 700 loss 0.0019050785340368748
Iteration: 800 loss 0.0011780565837398171
Iteration: 900 loss 0.00046253044274635613

显示
在这里插入图片描述

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

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

相关文章

Elasticsearch8.17.0在mac上的安装

1、下载并安装 下载8.17版本es(目前最新版本)&#xff1a;Download Elasticsearch | Elastic 也可以通过历史版本列表页下载&#xff1a;Past Releases of Elastic Stack Software | Elastic 当然也可以指定具体版本号进行下载&#xff1a;Elasticsearch 8.17.0 | Elastic …

【自动控制原理】学习地图

分值分布 选择+填空+判断:50分 大题:50分 概念 控制系统的数学模型 在控制系统的分析和设计中,首先要建立系统的数学模型。控制系统的数学模型是描述系统内部物理量(或变量)之间关系的数学表达式。 在静态条件下(即变量各阶导数为零),描述变量之间关系的代数方程叫静态…

Synchronous Serial Port 协议详解

1、简介 Synchronous Serial Port (SSP) &#xff0c;基于下图文档的设计标准 1.1、包含3种数据帧格式&#xff1a; a Motorola SPI-compatible interface&#xff08;以下简称SPI&#xff09;a Texas Instruments synchronous serial interface&#xff08;简写SSI&#xff…

前端OpenAPI根据后端Swagger自动生成前端接口报错

测试之后发现是因为Map<Long,List<CommentVO>>的返回值类型的锅&#xff0c;改成Page<List<CommentVO>>即可解决。 前端使用的umiMAX的openapi&#xff0c;报错如下&#xff1a; originalRef: BaseResponseboolean\n "401&q…

在线预约陪诊小程序

一、前言 随着社会老龄化加剧以及人们健康意识的提高&#xff0c;就医过程中的陪伴需求日益增长。许多患者在面对复杂的医院环境、繁琐的就医流程时&#xff0c;需要有人协助挂号、候诊、取药等&#xff0c;而家属可能因工作繁忙无法全程陪同。同时&#xff0c;异地就医的患者更…

信号滤波分析-低通分析(Matlab)

Matlab低通滤波 信号滤波分析-低通分析&#xff08;Matlab&#xff09; 【标价是仅源码的价格】 【有课程设计答辩PPT和设计文档报告】 需要或感兴趣可以随时联系博主哦&#xff0c;常在线秒回&#xff01; 低通滤波分析方案的设计包括&#xff1a; 1.信号生成原理 2.低通滤波…

全国青少年信息学奥林匹克竞赛(信奥赛)备考实战之复合运算符

&#xff08;一&#xff09;、复合运算符 在C中&#xff0c;可以通过“赋值语句”来修改变量的值。赋值语句的格式&#xff1a; 变量名 值或者表达式&#xff1b;其中""称为"赋值运算符"。 除此之外&#xff0c;在赋值运算符当中&#xff0c;C有复合赋…

半导体器件与物理篇5 1~4章课后习题

热平衡时的能带和载流子浓度 例 一硅晶掺入每立方厘米10^{16}个砷原子&#xff0c;求室温下(300K)的载流子浓度与费米能级。 需要用到的公式包括1.本征载流子浓度公式 2.从导带底算起的本征费米能级 2.从本征费米能级算起的费米能级 载流子输运现象 例1:计算在300K下&#x…

Qt-Advanced-Docking-System配置及使用、心得

Qt-Advanced-Docking-System 1. Qt-Advanced-Docking-System描述2. 功能特点2.1. 灵活的停靠方式2.2. 嵌套停靠2.3. 自定义布局保存与恢复2.4. 外观和行为定制 3. 与Qt原生停靠系统的比较4. 使用场景4.1. 集成开发环境&#xff08;IDE&#xff09;4.2. 图形设计软件4.3. 数据分…

Vue中纯前端实现导出简单Excel表格的功能

Vue 前端Excel导出 Vue中纯前端导出简单Excel表格的方法(使用vue-json-excel插件) 前言 在许多的后台系统中少不了导出Excel表格的功能&#xff0c;在项目中纯前端使用vue-json-excel插件来实现简单Excel表格的导出功能。 使用方法 1、安装依赖 npm install vue-json-exc…

【系统分析师】-收官整理-已考过

1、考试历程 之前从未参加过软考 2023年11月系统架构师【未通过】 2024年05月系统架构师【已通过】 2024年11月系统分析师【已通过】 鉴于架构师与分析师在基础知识上比较相似&#xff0c;在看到架构考试通过后&#xff0c;就准备往系分方面。 2、考题分析 今年下半年系分考试…

暂停window11自动更新

window11 的自动更新功能&#xff0c;一方面在后台占用资源&#xff0c;容易导致电脑卡顿&#xff1b;另一方面&#xff0c;“更新并关机” 和 “更新并重启” 的设置令人极其反感。很多补丁兼容性很差&#xff0c;更新后极易引发电脑蓝屏、闪屏等意想不到的 bug。 1.winR打开运…

opengauss架构图

资料来源&#xff1a;04轻松上手openGauss之openGauss对象管理&#xff08;上&#xff09;_哔哩哔哩_bilibili 资料来源&#xff1a;04轻松上手openGauss之openGauss对象管理&#xff08;上&#xff09;_哔哩哔哩_bilibili

2024,大模型杀进“决赛圈”

Henry Chesbrough在著作《通过技术创新盈利势在必行》中&#xff0c;曾提出过一个创新的“漏斗模型”。开放式创新一开始鼓励百花齐放&#xff0c;但最终只有10%的技术能够通过这个漏斗&#xff0c;成功抵达目标市场target market&#xff0c;进入到商业化与产业化的下一个阶段…

C++重点和练习-----多态

rpg.cpp: #include <iostream>using namespace std;/*模拟一个游戏场景有一个英雄&#xff1a;初始所有属性为1atk,def,apd,hp游戏当中有以下3种武器长剑Sword&#xff1a; 装备该武器获得 1atx&#xff0c;1def短剑Blade&#xff1a; 装备该武器获得 1atk&#xff0c;1…

细说STM32F407单片机SPI基础知识

目录 一、 SPI接口和通信协议 1、 SPI硬件接口 &#xff08;1&#xff09;MOSI(Master Output Slave Input) &#xff08;2&#xff09;MISO(Master Input Slave Output) &#xff08;3&#xff09;SCK 2、SPI传输协议 &#xff08;1&#xff09;CPHA0时的数据传输时序 …

种草电商系统APP功能需求开发案例

种草电商系统‌是一种结合了社区互动和电商交易的平台&#xff0c;能看到其他小伙伴分享的各种真实购物心得、超美商品图片和超实用视频&#xff0c;让我们去发现好物。可以随心关注感兴趣的人&#xff0c;跟他们畅聊购物体验&#xff0c;点赞、评论、转发那些心动的分享。其主…

EnumMap:让Java Map更高效的技巧

前言 摘要 内容 什么是EnumMap 如何使用EnumMap EnumMap的实现原理 EnumMap的例子 测试用例 小结 前言 在Java中&#xff0c;枚举类型是一种非常有用的数据类型&#xff0c;它可以用于定义一组固定的常量。枚举类型在很多场景中都有广泛的应用&#xff0c;例如状态码、…

测试工程师八股文05|功能测试、业务测试

一、基础概念 1、软件测试分类 1️⃣按照软件产生的阶段划分 单元测试&#xff1a;针对程序源代码进行测试【开发自测】集成测试&#xff1a;针对模块之间功能交互进行测试系统测试&#xff1a;对整个系统&#xff08;功能、非功能&#xff09;进行全面测试验收测试&#xff…

中国计算机学会计算机视觉专委会携手合合信息举办企业交流活动,为AI安全治理打开“新思路”

近期&#xff0c;《咬文嚼字》杂志发布了2024年度十大流行语&#xff0c;“智能向善”位列其中&#xff0c;过去一年时间里&#xff0c;深度伪造、AI诈骗等话题屡次登上热搜&#xff0c;AI技术“野蛮生长”引发公众担忧。今年9月&#xff0c;全国网络安全标准化技术委员会发布了…