记录学习《手动学习深度学习》这本书的笔记(五)

这一章是循环神经网络,太难了太难了,有很多卡壳的地方理解了好久,比如隐藏层和隐状态的区别、代码的含义(为此专门另写了一篇【笔记】记录对自主实现一个神经网络的步骤的理解)、梯度计算相关(【笔记】记录对python中.grad()的一些理解)。

第八章:循环神经网络

8.1 序列模型

之前介绍了卷积神经网络,了解了怎么将空间结构融入模型,这一章的循环神经网络就是介绍如何将时间序列结构融入模型当中。

通常,在一个有序列结构的模型预测中,对于下一个时间步我们是按:x_{t} \sim P\left \{ x_{t} | x_{t-1} , ... x_{1}\right \}来预测的。

但是这样的话,随着预测步数的推进,复杂度会上升得非常快,所以我们需要想出一些解决方法。

我们想出的方法(自回归模型)有两种:

  • 一种叫自回归模型,是规定取预测步数之前的 \tau 个步数对当前预测步数进行预测,也就是说不需要从 t 步取回第 1 步了,只需要从t步取到第 t-\tau 步,x_{t} \sim P\left \{ x_{t} | x_{t-1} , ... x_{t-\tau }\right \}
  • 一种叫隐变量自回归模型,是我们后面会一直介绍的方法,将第 t 步之前步数的数据用一个隐状态 h_{t} 表示然后预测下一步 t+1 时只要将 h_{t} 和 x_{t+1} 进行线性变化组合得到 h_{t+1} 再用 h_{t+1} 预测第 t+1 步。

 这一章初步实现了一个简单的自回归模型,利用sin函数加一些随机误差生成数据,再利用自回归模型进行训练预测。

内插法是根据现有数据预测单个步的数据,预测数据来自现有数据。

外推法是根据现有数据不断推出后面的数据,每步根据前几步预测出来的数据对未来进行预测。

实验结果表明内推法难度更小,准确率也更高。而外推法得到的结果非常容易偏离正确值,因为预测的错误容易“累计”,不断预测非常容易偏离实际结果。

实验使用k步预测,由k步之前的数据预测第k步的数据,跨度为k,实验结果表明随着k的增加,错误会积累得越多,预测质量急剧下降。

8.2 文本预处理

我们选取《时光机器》中的文本数据。

提取文本步骤包括:

  1. 将文本作为字符串加载到内存中。
  2. 将字符串拆分为词元
  3. 建立词表将词元映射为数字索引
  4. 将文本转换为数字索引

第1步:

#@save
d2l.DATA_HUB['time_machine'] = (d2l.DATA_URL + 'timemachine.txt',
                                '090b5e7e70c295757f55df93cb0a180b9691891a')

def read_time_machine():  #@save
    """将时间机器数据集加载到文本行的列表中"""
    with open(d2l.download('time_machine'), 'r') as f:
        lines = f.readlines()
    return [re.sub('[^A-Za-z]+', ' ', line).strip().lower() for line in lines]

lines = read_time_machine()

读取数据,将除了字母以外的字符转为空格(re.sub()正则匹配),去除首位空格(.strip()),将大写转为小写(.lower())

第2步:

将文本行继续拆分为单个词元。

def tokenize(lines, token='word'):  #@save
    """将文本行拆分为单词或字符词元"""
    if token == 'word':
        return [line.split() for line in lines]
    elif token == 'char':
        return [list(line) for line in lines]
    else:
        print('错误:未知词元类型:' + token)

tokens = tokenize(lines)
for i in range(11):
    print(tokens[i])

这个函数实现了拆分单词和拆分字母两种方式,默认拆分单词。

返回一些列表,每个列表代表一行,每一行有若干个单词(或字母)。

第3步:

这一步要将词元构成字典的形式,建立一个class类,需要实现将输入的词元匹配上一个对应的数字索引。

class Vocab:  #@save
    """文本词表"""
    def __init__(self, tokens=None, min_freq=0, reserved_tokens=None):
        if tokens is None:
            tokens = []
        if reserved_tokens is None:
            reserved_tokens = []
        # 按出现频率排序
        counter = count_corpus(tokens)
        self._token_freqs = sorted(counter.items(), key=lambda x: x[1],
                                   reverse=True)
        # 未知词元的索引为0
        self.idx_to_token = ['<unk>'] + reserved_tokens
        self.token_to_idx = {token: idx
                             for idx, token in enumerate(self.idx_to_token)}
        for token, freq in self._token_freqs:
            if freq < min_freq:
                break
            if token not in self.token_to_idx:
                self.idx_to_token.append(token)
                self.token_to_idx[token] = len(self.idx_to_token) - 1

    def __len__(self):
        return len(self.idx_to_token)

    def __getitem__(self, tokens):
        if not isinstance(tokens, (list, tuple)):
            return self.token_to_idx.get(tokens, self.unk)
        return [self.__getitem__(token) for token in tokens]

    def to_tokens(self, indices):
        if not isinstance(indices, (list, tuple)):
            return self.idx_to_token[indices]
        return [self.idx_to_token[index] for index in indices]

    @property
    def unk(self):  # 未知词元的索引为0
        return 0

    @property
    def token_freqs(self):
        return self._token_freqs

def count_corpus(tokens):  #@save
    """统计词元的频率"""
    # 这里的tokens是1D列表或2D列表
    if len(tokens) == 0 or isinstance(tokens[0], list):
        # 将词元列表展平成一个列表
        tokens = [token for line in tokens for token in line]
    return collections.Counter(tokens)

这个类大概是根据词元出现的频率排序,得出索引。

第4步:

执行:

vocab = Vocab(tokens)
print(list(vocab.token_to_idx.items())[:10])

就可以得到:

[('<unk>', 0), ('the', 1), ('i', 2), ('and', 3), ('of', 4), ('a', 5), ('to', 6), ('was', 7), ('in', 8), ('that', 9)]

执行:

for i in [0, 10]:
    print('文本:', tokens[i])
    print('索引:', vocab[tokens[i]])

就可以得到:

文本: ['the', 'time', 'machine', 'by', 'h', 'g', 'wells']
索引: [1, 19, 50, 40, 2183, 2184, 400]
文本: ['twinkled', 'and', 'his', 'usually', 'pale', 'face', 'was', 'flushed', 'and', 'animated', 'the']
索引: [2186, 3, 25, 1044, 362, 113, 7, 1421, 3, 1045, 1]

整合以上功能:

def load_corpus_time_machine(max_tokens=-1):  #@save
    """返回时光机器数据集的词元索引列表和词表"""
    lines = read_time_machine()
    tokens = tokenize(lines, 'char')
    vocab = Vocab(tokens)
    # 因为时光机器数据集中的每个文本行不一定是一个句子或一个段落,
    # 所以将所有文本行展平到一个列表中
    corpus = [vocab[token] for line in tokens for token in line]
    if max_tokens > 0:
        corpus = corpus[:max_tokens]
    return corpus, vocab

corpus, vocab = load_corpus_time_machine()
len(corpus), len(vocab)

加载数据,切分为字母,构造词典,整合成一个大列表,可选择是否取前max_tokens个字母。

8.3 语言模型和数据集

在构建模型之前,我们要了解一些关于自然语言处理的相关知识。

基本概率规则:比如 P(我想你) = P(你|我,想)P(想|我)P(我) ,为了得出一个句子的概率,我们需要知道单词出现的概率,以及单词在前几个单词出现的情况下出现的概率。

假设我们有一个非常大的语料库,对于上面式子的条件概率,我们可以通过 P(想|我) = P(我想) / P(我) 得到。

但是对于一些不常见的组合,语料库中出现的概率可能是零,于是就要利用拉普拉斯平滑,使所有语料库中没有出现过的组合概率不为零。

进行自然语言统计时,我们将之前的数据中频率最高的词汇打印出来,发现很多都是the、of、and之类的停用词,并且前几个出现的概率比后面的概率要高很多。

打印出图像可以看出词频从某个临界点开始就下降得特别快。

这意味着单词频率符合齐鲁夫定律。

如果采用之前的平滑方法,尾部数量就会大增。

我们再统计两个词汇组成的词组出现的频率、三个词汇组成的词组出现的频率:

可以看出都是这种情况。

说明拉普拉斯平滑并不适合语言建模,很多n元组很少出现,所以我们使用深度学习模型。

我们读取长序列数据,生成小批量数据作为特征,然后移动生成它的标签。

1.随机抽样:

def seq_data_iter_random(corpus, batch_size, num_steps):  #@save
    """使用随机抽样生成一个小批量子序列"""
    # 从随机偏移量开始对序列进行分区,随机范围包括num_steps-1
    corpus = corpus[random.randint(0, num_steps - 1):]
    # 减去1,是因为我们需要考虑标签
    num_subseqs = (len(corpus) - 1) // num_steps
    # 长度为num_steps的子序列的起始索引
    initial_indices = list(range(0, num_subseqs * num_steps, num_steps))
    # 在随机抽样的迭代过程中,
    # 来自两个相邻的、随机的、小批量中的子序列不一定在原始序列上相邻
    random.shuffle(initial_indices)

    def data(pos):
        # 返回从pos位置开始的长度为num_steps的序列
        return corpus[pos: pos + num_steps]

    num_batches = num_subseqs // batch_size
    for i in range(0, batch_size * num_batches, batch_size):
        # 在这里,initial_indices包含子序列的随机起始索引
        initial_indices_per_batch = initial_indices[i: i + batch_size]
        X = [data(j) for j in initial_indices_per_batch]
        Y = [data(j + 1) for j in initial_indices_per_batch]
        yield np.array(X), np.array(Y)

my_seq = list(range(35))
for X, Y in seq_data_iter_random(my_seq, batch_size=2, num_steps=5):
    print('X: ', X, '\nY:', Y)

生成特征和标签:

X:  [[22. 23. 24. 25. 26.]
 [27. 28. 29. 30. 31.]]
Y: [[23. 24. 25. 26. 27.]
 [28. 29. 30. 31. 32.]]
X:  [[ 7.  8.  9. 10. 11.]
 [12. 13. 14. 15. 16.]]
Y: [[ 8.  9. 10. 11. 12.]
 [13. 14. 15. 16. 17.]]
X:  [[17. 18. 19. 20. 21.]
 [ 2.  3.  4.  5.  6.]]
Y: [[18. 19. 20. 21. 22.]
 [ 3.  4.  5.  6.  7.]]

2.顺序分区:

def seq_data_iter_sequential(corpus, batch_size, num_steps):  #@save
    """使用顺序分区生成一个小批量子序列"""
    # 从随机偏移量开始划分序列
    offset = random.randint(0, num_steps)
    num_tokens = ((len(corpus) - offset - 1) // batch_size) * batch_size
    Xs = np.array(corpus[offset: offset + num_tokens])
    Ys = np.array(corpus[offset + 1: offset + 1 + num_tokens])
    Xs, Ys = Xs.reshape(batch_size, -1), Ys.reshape(batch_size, -1)
    num_batches = Xs.shape[1] // num_steps
    for i in range(0, num_steps * num_batches, num_steps):
        X = Xs[:, i: i + num_steps]
        Y = Ys[:, i: i + num_steps]
        yield X, Y

for X, Y in seq_data_iter_sequential(my_seq, batch_size=2, num_steps=5):
    print('X: ', X, '\nY:', Y)

生成特征和标签:

X:  [[ 0.  1.  2.  3.  4.]
 [17. 18. 19. 20. 21.]]
Y: [[ 1.  2.  3.  4.  5.]
 [18. 19. 20. 21. 22.]]
X:  [[ 5.  6.  7.  8.  9.]
 [22. 23. 24. 25. 26.]]
Y: [[ 6.  7.  8.  9. 10.]
 [23. 24. 25. 26. 27.]]
X:  [[10. 11. 12. 13. 14.]
 [27. 28. 29. 30. 31.]]
Y: [[11. 12. 13. 14. 15.]
 [28. 29. 30. 31. 32.]]

将两种方法打包:

class SeqDataLoader:  #@save
    """加载序列数据的迭代器"""
    def __init__(self, batch_size, num_steps, use_random_iter, max_tokens):
        if use_random_iter:
            self.data_iter_fn = d2l.seq_data_iter_random
        else:
            self.data_iter_fn = d2l.seq_data_iter_sequential
        self.corpus, self.vocab = d2l.load_corpus_time_machine(max_tokens)
        self.batch_size, self.num_steps = batch_size, num_steps

    def __iter__(self):
        return self.data_iter_fn(self.corpus, self.batch_size, self.num_steps)
def load_data_time_machine(batch_size, num_steps,  #@save
                           use_random_iter=False, max_tokens=10000):
    """返回时光机器数据集的迭代器和词表"""
    data_iter = SeqDataLoader(
        batch_size, num_steps, use_random_iter, max_tokens)
    return data_iter, data_iter.vocab

8.4 循环神经网络

循环神经网络将按照之前说的隐状态自回归模型,每一步根据上一步的状态h和当前读取数据进行预测。

隐状态H的作用是保留前面步的历史信息,相当于记忆一样。

这里书中提示了隐状态和隐藏层是不同的,想了老半天为什么,直到看到老师的ppt才懂:

其实两者指的是差不多的东西,只不过隐藏层是指那个层(箭头),而隐状态是指那个层输出的状态(h)。

没有隐状态就是普通神经网络,输入x输出y,中间的隐藏层不和前面的输入有关。

有隐状态就是隐藏层结合了前一个h,输出的是隐状态h,记录的前面的历史数据。

然后值得一提的是所有时间步是共享参数的,也就是h_{2}的计算和h_{3}是类似的这使得循环神经网络的开销不会随着预测步数的增大而增大。

预测值y的计算如下:

y_{t} = H_{t} W_{hq} + b_{q}

隐状态h的计算如下:

H_{t} = \phi (X_{t} W_{xh} + H_{t-1}W_{hh} + b_{h})

循环神经网络对于每个批量(比如you ar)和它的标签(ou are),对每个小批量(一个或多个字母)执行上述计算操作,这样就可以推出下一个字母,如此反复。

由上述隐状态的公式可以看出,我们可以将上一个隐状态和这一个输入横向合并(物理),将权重W纵向合并(物理),两者相乘加b得到的也是同样结果。

最后来看评判模型好坏的困惑度,就是判断这个字母在这个位置出现的合理性(概率),可以用如下方法计算:

\frac{1}{n}\prod_{n}^{t = 1}P(x_{t} | x_{t-1}, x_{t-2} ... x_{1})

累乘会导致数字过大或者过小,于是将其取对数:

exp(\frac{1}{n}\sum_{n}^{t = 1}-log P(x_{t} | x_{t-1}, x_{t-2} ... x_{1}))

这就是困惑度。最好情况为1,最坏情况为正无穷大。

8.5 循环神经网络的从零开始实现

这一节单独放在【笔记】记录对自主实现一个神经网络的步骤的理解里了,只需再介绍一个独热编码的实现:

F.one_hot(torch.tensor([0, 2]), len(vocab))

对0,1两个数字独热编码,长度为字母表长度。

运行结果是:

tensor([[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
         0, 0, 0, 0],
        [0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
         0, 0, 0, 0]])

如果传入的是数组,也可以给出对应独热编码,结果上升一个维度。

8.6 循环神经网络的简洁实现

这一章我们构建一个隐藏单元为256的单隐藏层循环神经网络。

像之前那样读取数据:

batch_size, num_steps = 32, 35
train_iter, vocab = d2l.load_data_time_machine(batch_size, num_steps)

定义模型:

num_hiddens = 256
rnn_layer = nn.RNN(len(vocab), num_hiddens)

要注意的是,nn.RNN不是整个循环神经网络,而是只是隐藏层这部分,输出的是隐藏层的预测数据,我们还需要实现从隐状态到最后输出层的代码。

初始化隐状态:

state = torch.zeros((1, batch_size, num_hiddens))
state.shape

由于nn.RNN只包含隐藏层,我们还需要建输出层,并将两个层合并成一个完整的循环神经网络:

#@save
class RNNModel(nn.Module):
    """循环神经网络模型"""
    def __init__(self, rnn_layer, vocab_size, **kwargs):
        super(RNNModel, self).__init__(**kwargs)
        self.rnn = rnn_layer
        self.vocab_size = vocab_size
        self.num_hiddens = self.rnn.hidden_size
        # 如果RNN是双向的(之后将介绍),num_directions应该是2,否则应该是1
        if not self.rnn.bidirectional:
            self.num_directions = 1
            self.linear = nn.Linear(self.num_hiddens, self.vocab_size)
        else:
            self.num_directions = 2
            self.linear = nn.Linear(self.num_hiddens * 2, self.vocab_size)

    def forward(self, inputs, state):
        X = F.one_hot(inputs.T.long(), self.vocab_size)
        X = X.to(torch.float32)
        Y, state = self.rnn(X, state)
        # 全连接层首先将Y的形状改为(时间步数*批量大小,隐藏单元数)
        # 它的输出形状是(时间步数*批量大小,词表大小)。
        output = self.linear(Y.reshape((-1, Y.shape[-1])))
        return output, state

    def begin_state(self, device, batch_size=1):
        if not isinstance(self.rnn, nn.LSTM):
            # nn.GRU以张量作为隐状态
            return  torch.zeros((self.num_directions * self.rnn.num_layers,
                                 batch_size, self.num_hiddens),
                                device=device)
        else:
            # nn.LSTM以元组作为隐状态
            return (torch.zeros((
                self.num_directions * self.rnn.num_layers,
                batch_size, self.num_hiddens), device=device),
                    torch.zeros((
                        self.num_directions * self.rnn.num_layers,
                        batch_size, self.num_hiddens), device=device))

类接收一个隐藏层,词汇表大小作为参数,初始化传入参数,并且设置线性层作为由隐状态到输出的函数。

前向传播连接两个函数,先对输入进行独热编码,然后依次放入隐藏层和线性层中计算输出,输出为当前状态和当前输出。

begin_state函数初始化状态,每次新一轮预测就调用这个函数。

实现完整个模型,然后就可以将模型代入之前实现的预测和训练函数。

预测:

device = d2l.try_gpu()
net = RNNModel(rnn_layer, vocab_size=len(vocab))
net = net.to(device)
d2l.predict_ch8('time traveller', 10, net, vocab, device)

训练:

num_epochs, lr = 500, 1
d2l.train_ch8(net, train_iter, vocab, lr, num_epochs, device)

8.7 通过时间反向传播

这一节分析了循环神经网络的梯度传播。

画出流程图可以看出,越后面的步需要回溯越多,这也是循环神经网络容易梯度爆炸和梯度消失的原因。

对此,我们可以不进行完整计算,而是使用截断时间步减少梯度复杂度,可以采用随机截断或常规截断的方法(一般使用的是常规截断)。

这一节主要是在用链式计算第t步的梯度公式。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【git、gerrit】特性分支合入主分支方法 git rebase 、git cherry-pick、git merge

文章目录 1. 场景描述1.1 分支状态 2. 推荐的操作方式方法 1&#xff1a;git merge&#xff08;保留分支结构&#xff09;方法 2&#xff1a;git rebase&#xff08;线性合并提交历史&#xff09;直接在master分支执行git merge br_feature&#xff0c;再 执行 git pull --reba…

Python-基于Pygame的小游戏(天空之战)(一)

前言:不久前接触了Python的游戏制作的相关第三方库&#xff0c;于是学习了pygame的相关内容&#xff0c;想制作一款基于pygame的小游戏。因为还不太熟悉游戏制作和pygame&#xff0c;部分内容我参考了《Python-从入门到精通》这本书。那么好&#xff0c;话不多说&#xff0c;我…

探索 Cesium 的未来:3D Tiles Next 标准解析

探索 Cesium 的未来&#xff1a;3D Tiles Next 标准解析 随着地理信息系统&#xff08;GIS&#xff09;和 3D 空间数据的快速发展&#xff0c;Cesium 作为领先的开源 3D 地球可视化平台&#xff0c;已成为展示大规模三维数据和进行实时渲染的强大工具。近年来&#xff0c;随着…

Redis和数据库的一致性(Canal+MQ)

想要保证缓存与数据库的双写一致&#xff0c;一共有4种方式&#xff0c;即4种同步策略&#xff1a; 先更新缓存&#xff0c;再更新数据库&#xff1b;先更新数据库&#xff0c;再更新缓存&#xff1b;先删除缓存&#xff0c;再更新数据库&#xff1b;先更新数据库&#xff0c;再…

CNCF云原生生态版图-分类指南(一)- 观测和分析

CNCF云原生生态版图-分类指南&#xff08;一&#xff09;- 观测和分析 CNCF云原生生态版图-分类指南一、观测和分析&#xff08;Observability and Analysis&#xff09;&#xff08;一&#xff09;可观测性&#xff08;Observablility&#xff09;1. 是什么&#xff1f;2. 解决…

JVM运行时数据区内部结构

VM内部结构 对于jvm来说他的内部结构主要分成三个部分&#xff0c;分别是类加载阶段&#xff0c;运行时数据区&#xff0c;以及垃圾回收区域&#xff0c;类加载我们放到之后来总结&#xff0c;今天先复习一下类运行区域 首先这个区域主要是分成如下几个部分 下面举个例子来解释…

C语言学习day22:URLDownloadToFile函数/开发文件下载工具

简言&#xff1a; 在之前我们去下载某个东西都是用的迅雷之类的软件&#xff0c;但是现在&#xff0c;只要提供一个地址&#xff0c;或者一个链接&#xff0c;我们自己去做一个工具去下载。这就是我们这篇的主要内容。 也就是我们的winAPI&#xff1a;URLDownloadToFile函数 …

购物车案例--分模块存储数据,发送请求数据渲染,底部总计数量和价格

shift鼠标右键&#xff0c;打开powershell&#xff0c;新建项目 自定义 只有一个页面&#xff0c;不涉及路由&#xff0c;勾选vuex,css,babel 无需保存预设 回车项目开始创建 项目用vscode打开 将src里的内容全部清空 将第七天的课程准备代码复制粘贴到src中 刷新页面&…

SQL server学习06-查询数据表中的数据(中)

目录 一&#xff0c;聚合函数 1&#xff0c;常用聚合函数 2&#xff0c;具体使用 二&#xff0c;GROP BY子句分组 1&#xff0c;基础语法 2&#xff0c;具体使用 3&#xff0c;加上HAVING对组进行筛选 4&#xff0c;使WHERE记录查询条件 汇总查询&#xff1a;在对数…

YOLOv5-7.0训练过程中出现报错Example: export GIT_PYTHON_REFRESH=quiet

出现报错&#xff1a; This initial message can be silenced or aggravated in the future by setting the $GIT_PYTHON_REFRESH environment variable. Use one of the following values: - quiet|q|silence|s|silent|none|n|0: for no message or exception - warn…

从0到1实现vue3+vite++elementuiPlus+ts的后台管理系统(一)

前言&#xff1a;从这篇文章开始实现vue3vite的后台管理系统&#xff0c;记录下自己搭建后台系统图的过程。 这篇文章完成项目的初始化和基本配置&#xff0c;这一步可以直接跟着vue3官网进行。整个系列只有前端部分&#xff0c;不涉及后端。 vue3官网&#xff1a;https://cn.…

Spring Boot教程之二十五: 使用 Tomcat 部署项目

Spring Boot – 使用 Tomcat 部署项目 Spring Boot 是一个基于微服务的框架&#xff0c;在其中创建可用于生产的应用程序只需很少的时间。Spring Boot 建立在 Spring 之上&#xff0c;包含 Spring 的所有功能。如今&#xff0c;它正成为开发人员的最爱&#xff0c;因为它是一个…

java中操作线程

文章目录 前言创建与运行线程1. 创建线程①、方法1(直接new)②、方法2(使用Runnable配合Thread进行new操作)③、方法3&#xff08;FutureTask对象实现&#xff09;④、线程创建原理特别注意&#xff01; 2查看与杀死线程①、 Windows下 &#xff1a;②、 Java下 &#xff1a; 3…

【redis】redix在Linux下的环境配置和redis的全局命令

˃͈꒵˂͈꒱ write in front ꒰˃͈꒵˂͈꒱ ʕ̯•͡˔•̯᷅ʔ大家好&#xff0c;我是xiaoxie.希望你看完之后,有不足之处请多多谅解&#xff0c;让我们一起共同进步૮₍❀ᴗ͈ . ᴗ͈ აxiaoxieʕ̯•͡˔•̯᷅ʔ—CSDN博客 本文由xiaoxieʕ̯•͡˔•̯᷅ʔ 原创 CSDN 如…

百度智能云千帆AppBuilder升级,百度AI搜索组件上线,RAG支持无限容量向量存储!

百度智能云千帆 AppBuilder 发版升级&#xff01; 进一步降低开发门槛&#xff0c;落地大模型到应用的最后一公里。在千帆 AppBuilder 最新升级的 V1.1版本中&#xff0c;企业级 RAG 和 Agent 能力再度提升&#xff0c;同时组件生态与应用集成分发更加优化。 • 企业级 RAG&am…

uniappp配置导航栏自定义按钮(解决首次加载图标失败问题)

1.引入iconfont的图标&#xff0c;只保留这两个文件 2.App.vue引入到全局中 import "./static/fonts/iconfont.css"3.pages.json中配置text为图标对应的unicode {"path": "pages/invite/invite","style": {"h5": {"…

融云分享基于 Rust 的鸿蒙 SDK 开发实践

12 月 5 日&#xff0c;以“同心聚力&#xff0c;共建共享鸿蒙新生态”为主题的“鸿蒙生态伙伴 SDK 开发者论坛”在京举行。 融云凭借对鸿蒙生态的率先适配和创新贡献&#xff0c;荣获华为鸿蒙生态“HarmonyOS NEXT SDK 星河奖”。 本次论坛邀请了多位行业领导者参与&#xff…

iperf3 带宽性能测量工具

随笔记录 目录 1 背景介绍 2. Iperf3 2.1 基本框架介绍 2.2 Iperf3 发送TCP包 2.3 IPerf 发送UDP 包 1 背景介绍 基于测试USER DMA 压力测试需求。 2. Iperf3 2.1 基本框架介绍 Iperf3 发送数据包 TCP/UDP 包 1. 查看网卡配置信息 2. 此处因共用一张板卡&#xff0…

Java-27 深入浅出 Spring - 实现简易Ioc-03 在上节的业务下手动实现IoC

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; 大数据篇正在更新&#xff01;https://blog.csdn.net/w776341482/category_12713819.html 目前已经更新到了&#xff1a; MyBatis&#xff…

vue使用pdfh5.js插件,显示pdf文件白屏

pdfh5&#xff0c;展示文件白屏&#xff0c;无报错 实现效果图解决方法(降版本)排查问题过程发现问题查找问题根源1、代码写错了&#xff1f;2、预览文件流的问题&#xff1f;3、pdfh5插件更新了&#xff0c;我的依赖包没更新&#xff1f;4、真相大白 彩蛋 实现效果图 解决方法…