11、动手学深度学习——语言模型和数据集:代码详解

我们了解了如何将文本数据映射为词元,以及将这些词元可以视为一系列离散的观测,例如单词或字符。
假设长度为 T T T的文本序列中的词元依次为 x 1 , x 2 , … , x T x_1, x_2, \ldots, x_T x1,x2,,xT。于是, x t x_t xt 1 ≤ t ≤ T 1 \leq t \leq T 1tT)可以被认为是文本序列在时间步 t t t处的观测或标签。在给定这样的文本序列时,语言模型(language model)的目标是估计序列的联合概率

P ( x 1 , x 2 , … , x T ) . P(x_1, x_2, \ldots, x_T). P(x1,x2,,xT).

例如,只需要一次抽取一个词元 x t ∼ P ( x t ∣ x t − 1 , … , x 1 ) x_t \sim P(x_t \mid x_{t-1}, \ldots, x_1) xtP(xtxt1,,x1),一个理想的语言模型就能够基于模型本身生成自然文本。与猴子使用打字机完全不同的是,从这样的模型中提取的文本都将作为自然语言(例如,英语文本)来传递。只需要基于前面的对话片断中的文本,就足以生成一个有意义的对话。显然,我们离设计出这样的系统还很遥远,因为它需要“理解”文本,而不仅仅是生成语法合理的内容。

尽管如此,语言模型依然是非常有用的。例如,短语“to recognize speech”和“to wreck a nice beach”读音上听起来非常相似。这种相似性会导致语音识别中的歧义,但是这很容易通过语言模型来解决,因为第二句的语义很奇怪。同样,在文档摘要生成算法中,“狗咬人”比“人咬狗”出现的频率要高得多,或者“我想吃奶奶”是一个相当匪夷所思的语句,而“我想吃,奶奶”则要正常得多。

1、学习语言模型

显而易见,我们面对的问题是如何对一个文档,甚至是一个词元序列进行建模。假设在单词级别对文本数据进行词元化,我们可以依靠在中对序列模型的分析。让我们从基本概率规则开始:

P ( x 1 , x 2 , … , x T ) = ∏ t = 1 T P ( x t ∣ x 1 , … , x t − 1 ) . P(x_1, x_2, \ldots, x_T) = \prod_{t=1}^T P(x_t \mid x_1, \ldots, x_{t-1}). P(x1,x2,,xT)=t=1TP(xtx1,,xt1).

例如,包含了四个单词的一个文本序列的概率是:

P ( deep , learning , is , fun ) = P ( deep ) P ( learning ∣ deep ) P ( is ∣ deep , learning ) P ( fun ∣ deep , learning , is ) . P(\text{deep}, \text{learning}, \text{is}, \text{fun}) = P(\text{deep}) P(\text{learning} \mid \text{deep}) P(\text{is} \mid \text{deep}, \text{learning}) P(\text{fun} \mid \text{deep}, \text{learning}, \text{is}). P(deep,learning,is,fun)=P(deep)P(learningdeep)P(isdeep,learning)P(fundeep,learning,is).

为了训练语言模型,我们需要计算单词的概率,以及给定前面几个单词后出现某个单词的条件概率。这些概率本质上就是语言模型的参数。

这里,我们假设训练数据集是一个大型的文本语料库。比如,维基百科的所有条目、古登堡计划,或者所有发布在网络上的文本。训练数据集中词的概率可以根据给定词的相对词频来计算。例如,可以将估计值 P ^ ( deep ) \hat{P}(\text{deep}) P^(deep)计算为任何以单词“deep”开头的句子的概率。一种(稍稍不太精确的)方法是统计单词“deep”在数据集中的出现次数,然后将其除以整个语料库中的单词总数。这种方法效果不错,特别是对于频繁出现的单词。接下来,我们可以尝试估计

P ^ ( learning ∣ deep ) = n ( deep, learning ) n ( deep ) , \hat{P}(\text{learning} \mid \text{deep}) = \frac{n(\text{deep, learning})}{n(\text{deep})}, P^(learningdeep)=n(deep)n(deep, learning),

其中 n ( x ) n(x) n(x) n ( x , x ′ ) n(x, x') n(x,x)分别是单个单词和连续单词对的出现次数。不幸的是,由于连续单词对 “deep learning” 的出现频率要低得多,所以估计这类单词正确的概率要困难得多。特别是对于一些不常见的单词组合,要想找到足够的出现次数来获得准确的估计可能都不容易。而对于三个或者更多的单词组合,情况会变得更糟。许多合理的三个单词组合可能是存在的,但是在数据集中却找不到。 除非我们提供某种解决方案,来将这些单词组合指定为非零计数,否则将无法在语言模型中使用它们。如果数据集很小,或者单词非常罕见,那么这类单词出现一次的机会可能都找不到。

一种常见的策略是执行某种形式的拉普拉斯平滑(Laplace smoothing),具体方法是在所有计数中添加一个小常量。用 n n n表示训练集中的单词总数,用 m m m表示唯一单词的数量。此解决方案有助于处理单元素问题,例如通过:

P ^ ( x ) = n ( x ) + ϵ 1 / m n + ϵ 1 , P ^ ( x ′ ∣ x ) = n ( x , x ′ ) + ϵ 2 P ^ ( x ′ ) n ( x ) + ϵ 2 , P ^ ( x ′ ′ ∣ x , x ′ ) = n ( x , x ′ , x ′ ′ ) + ϵ 3 P ^ ( x ′ ′ ) n ( x , x ′ ) + ϵ 3 . \begin{aligned} \hat{P}(x) & = \frac{n(x) + \epsilon_1/m}{n + \epsilon_1}, \\ \hat{P}(x' \mid x) & = \frac{n(x, x') + \epsilon_2 \hat{P}(x')}{n(x) + \epsilon_2}, \\ \hat{P}(x'' \mid x,x') & = \frac{n(x, x',x'') + \epsilon_3 \hat{P}(x'')}{n(x, x') + \epsilon_3}. \end{aligned} P^(x)P^(xx)P^(x′′x,x)=n+ϵ1n(x)+ϵ1/m,=n(x)+ϵ2n(x,x)+ϵ2P^(x),=n(x,x)+ϵ3n(x,x,x′′)+ϵ3P^(x′′).

其中, ϵ 1 , ϵ 2 \epsilon_1,\epsilon_2 ϵ1,ϵ2 ϵ 3 \epsilon_3 ϵ3是超参数。以 ϵ 1 \epsilon_1 ϵ1为例:当 ϵ 1 = 0 \epsilon_1 = 0 ϵ1=0时,不应用平滑;当 ϵ 1 \epsilon_1 ϵ1接近正无穷大时, P ^ ( x ) \hat{P}(x) P^(x)接近均匀概率分布 1 / m 1/m 1/m

然而,这样的模型很容易变得无效,原因如下:首先,我们需要存储所有的计数;其次,这完全忽略了单词的意思。例如,“猫”(cat)和“猫科动物”(feline)可能出现在相关的上下文中,但是想根据上下文调整这类模型其实是相当困难的。最后,长单词序列大部分是没出现过的,因此一个模型如果只是简单地统计先前“看到”的单词序列频率,那么模型面对这种问题肯定是表现不佳的。

2、马尔可夫模型与 n n n元语法

在讨论包含深度学习的解决方案之前,我们需要了解更多的概念和术语。我们在对马尔可夫模型的讨论,并且将其应用于语言建模。如果 P ( x t + 1 ∣ x t , … , x 1 ) = P ( x t + 1 ∣ x t ) P(x_{t+1} \mid x_t, \ldots, x_1) = P(x_{t+1} \mid x_t) P(xt+1xt,,x1)=P(xt+1xt),则序列上的分布满足一阶马尔可夫性质。阶数越高,对应的依赖关系就越长。这种性质推导出了许多可以应用于序列建模的近似公式:

P ( x 1 , x 2 , x 3 , x 4 ) = P ( x 1 ) P ( x 2 ) P ( x 3 ) P ( x 4 ) , P ( x 1 , x 2 , x 3 , x 4 ) = P ( x 1 ) P ( x 2 ∣ x 1 ) P ( x 3 ∣ x 2 ) P ( x 4 ∣ x 3 ) , P ( x 1 , x 2 , x 3 , x 4 ) = P ( x 1 ) P ( x 2 ∣ x 1 ) P ( x 3 ∣ x 1 , x 2 ) P ( x 4 ∣ x 2 , x 3 ) . \begin{aligned} P(x_1, x_2, x_3, x_4) &= P(x_1) P(x_2) P(x_3) P(x_4),\\ P(x_1, x_2, x_3, x_4) &= P(x_1) P(x_2 \mid x_1) P(x_3 \mid x_2) P(x_4 \mid x_3),\\ P(x_1, x_2, x_3, x_4) &= P(x_1) P(x_2 \mid x_1) P(x_3 \mid x_1, x_2) P(x_4 \mid x_2, x_3). \end{aligned} P(x1,x2,x3,x4)P(x1,x2,x3,x4)P(x1,x2,x3,x4)=P(x1)P(x2)P(x3)P(x4),=P(x1)P(x2x1)P(x3x2)P(x4x3),=P(x1)P(x2x1)P(x3x1,x2)P(x4x2,x3).

通常,涉及一个、两个和三个变量的概率公式分别被称为一元语法(unigram)、二元语法(bigram)和三元语法(trigram)模型。下面,我们将学习如何去设计更好的模型。

3、自然语言统计

我们看看在真实数据上如果进行自然语言统计。根据时光机器数据集构建词表,并打印前 10 10 10个最常用的(频率最高的)单词。

import random
import torch
from d2l import torch as d2ltokens = d2l.tokenize(d2l.read_time_machine())		# 将语句token化
# 因为每个文本行不一定是一个句子或一个段落,因此我们把所有文本行拼接到一起
corpus = [token for line in tokens for token in line]
vocab = d2l.Vocab(corpus)
vocab.token_freqs[:10][('the', 2261),('i', 1267),('and', 1245),('of', 1155),('a', 816),('to', 695),('was', 552),('in', 541),('that', 443),('my', 440)]

正如我们所看到的,(出现频率最多词)通常(被称为停用词)(stop words),因此可以被过滤掉。尽管如此,它们本身仍然是有意义的,我们仍然会在模型中使用它们。此外,还有个明显的问题是词频衰减的速度相当地快。例如,最常用单词的词频对比,第 10 10 10个还不到第 1 1 1个的 1 / 5 1/5 1/5。为了更好地理解,我们可以[画出的词频图]:

freqs = [freq for token, freq in vocab.token_freqs]
d2l.plot(freqs, xlabel='token: x', ylabel='frequency: n(x)', xscale='log', yscale='log')

在这里插入图片描述
通过此图我们可以发现:词频以一种明确的方式迅速衰减。将前几个单词作为例外消除后,剩余的所有单词大致遵循双对数坐标图上的一条直线。这意味着单词的频率满足齐普夫定律(Zipf’s law),即第 i i i个最常用单词的频率 n i n_i ni为:

n i ∝ 1 i α , n_i \propto \frac{1}{i^\alpha}, niiα1,

等价于

log ⁡ n i = − α log ⁡ i + c , \log n_i = -\alpha \log i + c, logni=αlogi+c,

其中 α \alpha α是刻画分布的指数, c c c是常数。这告诉我们想要通过计数统计和平滑来建模单词是不可行的,因为这样建模的结果会大大高估尾部单词的频率,也就是所谓的不常用单词。那么[其他的词元组合,比如二元语法、三元语法等等,又会如何呢?]我们来看看二元语法的频率是否与一元语法的频率表现出相同的行为方式。

bigram_tokens = [pair for pair in zip(corpus[:-1], corpus[1:])]		# 将前后相连的单词构造成二元组
bigram_vocab = d2l.Vocab(bigram_tokens)								# 将二元组构建成词表
bigram_vocab.token_freqs[:10][(('of', 'the'), 309),(('in', 'the'), 169),(('i', 'had'), 130),(('i', 'was'), 112),(('and', 'the'), 109),(('the', 'time'), 102),(('it', 'was'), 99),(('to', 'the'), 85),(('as', 'i'), 78),(('of', 'a'), 73)]

这里值得注意:在十个最频繁的词对中,有九个是由两个停用词组成的,只有一个与“the time”有关。我们再进一步看看三元语法的频率是否表现出相同的行为方式。

# 以同样的方式构建三个词相临接的三元组
trigram_tokens = [triple for triple in zip(corpus[:-2], corpus[1:-1], corpus[2:])]
trigram_vocab = d2l.Vocab(trigram_tokens)
trigram_vocab.token_freqs[:10][(('the', 'time', 'traveller'), 59),(('the', 'time', 'machine'), 30),(('the', 'medical', 'man'), 24),(('it', 'seemed', 'to'), 16),(('it', 'was', 'a'), 15),(('here', 'and', 'there'), 15),(('seemed', 'to', 'me'), 14),(('i', 'did', 'not'), 14),(('i', 'saw', 'the'), 13),(('i', 'began', 'to'), 13)]

最后,我们[直观地对比三种模型中的词元频率]:一元语法、二元语法和三元语法。

bigram_freqs = [freq for token, freq in bigram_vocab.token_freqs]
trigram_freqs = [freq for token, freq in trigram_vocab.token_freqs]
d2l.plot([freqs, bigram_freqs, trigram_freqs], xlabel='token: x',ylabel='frequency: n(x)', xscale='log', yscale='log',legend=['unigram', 'bigram', 'trigram'])

在这里插入图片描述
这张图非常令人振奋!原因有很多:

  1. 除了一元语法词,单词序列似乎也遵循齐普夫定律,尽管公式中的指数 α \alpha α更小(指数的大小受序列长度的影响);
  2. 词表中 n n n元组的数量并没有那么大,这说明语言中存在相当多的结构,这些结构给了我们应用模型的希望;
  3. 很多 n n n元组很少出现,这使得拉普拉斯平滑非常不适合语言建模。作为代替,我们将使用基于深度学习的模型。

4、读取长序列数据

由于序列数据本质上是连续的,因此我们在处理数据时需要解决这个问题。 在上一节中我们以一种相当特别的方式做到了这一点:当序列变得太长而不能被模型一次性全部处理时,我们可能希望拆分这样的序列方便模型读取

在介绍该模型之前,我们看一下总体策略。假设我们将使用神经网络来训练语言模型,模型中的网络一次处理具有预定义长度(例如 n n n个时间步)的一个小批量序列。现在的问题是如何[随机生成一个小批量数据的特征和标签以供读取。]

首先,由于文本序列可以是任意长的,例如整本《时光机器》(The Time Machine),于是任意长的序列可以被我们划分为具有相同时间步数的子序列。当训练我们的神经网络时,这样的小批量子序列将被输入到模型中。假设网络一次只处理具有 n n n个时间步的子序列。图画出了从原始文本序列获得子序列的所有不同的方式,其中 n = 5 n=5 n=5,并且每个时间步的词元对应于一个字符。请注意,因为我们可以选择任意偏移量来指示初始位置,所以我们有相当大的自由度。

在这里插入图片描述

因此,我们应该从图中选择哪一个呢?事实上,他们都一样的好。然而,如果我们只选择一个偏移量,那么用于训练网络的、所有可能的子序列的覆盖范围将是有限的。因此,我们可以从随机偏移量开始划分序列,以同时获得覆盖性(coverage)和随机性(randomness)。下面,我们将描述如何实现随机采样(random sampling)和顺序分区(sequential partitioning)策略。

(1)随机采样

(在随机采样中,每个样本都是在原始的长序列上任意捕获的子序列。)在迭代过程中,来自两个相邻的、随机的、小批量中的子序列不一定在原始序列上相邻。对于语言建模,目标是基于到目前为止我们看到的词元来预测下一个词元,因此标签是移位了一个词元的原始序列。

下面的代码每次可以从数据中随机生成一个小批量。在这里,参数batch_size指定了每个小批量中子序列样本的数目,参数num_steps是每个子序列中预定义的时间步数。其中,子序列长度=时间步数-1

def seq_data_iter_random(corpus, batch_size, num_steps):  #@save"""使用随机抽样生成一个小批量子序列"""# 从随机偏移量开始对序列进行分区,随机范围包括num_steps-1corpus = corpus[random.randint(0, num_steps - 1):]          ## 使用random随机生成一个从(0, num_steps-1)中一个数m,然后选取corpus[m:]# 减去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)		## 将各个子序列shuffle打乱def data(pos):# 返回从pos位置开始的长度为num_steps的序列return corpus[pos: pos + num_steps]# 将各个子序列分批,计算共有几批num_batches = num_subseqs // batch_sizefor 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 torch.tensor(X), torch.tensor(Y)

下面我们[生成一个从 0 0 0 34 34 34的序列]。假设批量大小为 2 2 2,时间步数为 5 5 5,这意味着可以生成 ⌊ ( 35 − 1 ) / 5 ⌋ = 6 \lfloor (35 - 1) / 5 \rfloor= 6 ⌊(351)/5=6个“特征-标签”子序列对。如果设置小批量大小为 2 2 2,我们只能得到 3 3 3个小批量。

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:  tensor([[17, 18, 19, 20, 21],[ 7,  8,  9, 10, 11]]) 
Y: tensor([[18, 19, 20, 21, 22],[ 8,  9, 10, 11, 12]])
X:  tensor([[ 2,  3,  4,  5,  6],[12, 13, 14, 15, 16]]) 
Y: tensor([[ 3,  4,  5,  6,  7],[13, 14, 15, 16, 17]])
X:  tensor([[27, 28, 29, 30, 31],[22, 23, 24, 25, 26]]) 
Y: tensor([[28, 29, 30, 31, 32],[23, 24, 25, 26, 27]])

(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 = torch.tensor(corpus[offset: offset + num_tokens])						## 划分出XYs = torch.tensor(corpus[offset + 1: offset + 1 + num_tokens])				## 划分出YXs, Ys = Xs.reshape(batch_size, -1), Ys.reshape(batch_size, -1)				## 转变为(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

这段代码定义了一个函数seq_data_iter_sequential,用于生成一个小批量的子序列数据。函数的输入包括corpus(语料库),batch_size(批量大小)和num_steps(子序列长度)。

函数首先随机选择一个偏移量offset,用于划分序列。然后,计算可以生成的子序列的总长度num_tokens,通过对语料库长度减去偏移量和1,再除以批量大小,再乘以批量大小得到。

接下来,函数将语料库中的子序列划分为输入序列Xs和目标序列Ys,其中Xs包括从偏移量开始的num_tokens个元素,而Ys包括从偏移量加1开始的num_tokens个元素。

之后,函数将XsYs重新形状为形状为(batch_size, -1)的张量,其中-1表示自动计算该维度的大小。接着,函数计算可以生成的子序列的数量num_batches,通过将Xs的第二个维度除以子序列长度得到。

最后,函数使用一个循环来生成小批量的子序列数据。在每次迭代中,函数从XsYs中选择一个子序列XY,从第i列到第i+num_steps列。然后,函数使用yield语句返回XY,并暂停函数的执行,直到下一次迭代。这样,函数可以逐个生成小批量的子序列数据,供模型训练使用。

基于相同的设置,通过顺序分区[读取每个小批量的子序列的特征X和标签Y]。通过将它们打印出来可以发现:迭代期间来自两个相邻的小批量中的子序列在原始序列中确实是相邻的。

for X, Y in seq_data_iter_sequential(my_seq, batch_size=2, num_steps=5):print('X: ', X, '\nY:', Y)X:  tensor([[ 2,  3,  4,  5,  6],[18, 19, 20, 21, 22]]) 
Y: tensor([[ 3,  4,  5,  6,  7],[19, 20, 21, 22, 23]])
X:  tensor([[ 7,  8,  9, 10, 11],[23, 24, 25, 26, 27]]) 
Y: tensor([[ 8,  9, 10, 11, 12],[24, 25, 26, 27, 28]])
X:  tensor([[12, 13, 14, 15, 16],[28, 29, 30, 31, 32]]) 
Y: tensor([[13, 14, 15, 16, 17],[29, 30, 31, 32, 33]])

现在,我们[将上面的两个采样函数包装到一个类中],以便稍后可以将其用作数据迭代器。

class SeqDataLoader:  #@save"""加载序列数据的迭代器"""## use_random_iter:是否否采用随机采样def __init__(self, batch_size, num_steps, use_random_iter, max_tokens):if use_random_iter:self.data_iter_fn = d2l.seq_data_iter_randomelse:self.data_iter_fn = d2l.seq_data_iter_sequentialself.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)

[最后,我们定义了一个函数load_data_time_machine,它同时返回数据迭代器和词表],因此可以与其他带有load_data前缀的函数类似地使用。

def load_data_time_machine(batch_size, num_steps,  #@saveuse_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.3. 语言模型和数据集

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

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

相关文章

GPT与人类:人工智能是否能够真正复制人类语言?

人类语言是一种复杂的系统,它不仅包含着无数单词和语法规则,更重要的是具有丰富的含义和上下文。这些语言特征涉及到常识、文化、情感和经验等方面,是人类在长期进化和文明发展中所积累起来的丰富知识和经验的体现。然而,人工智能…

SpringCloud学习路线(7)—— 统一网关Gateway

一、引言 (一)需求: 服务器中的微服务只允许内部人员调用或是内网人员进行调用,拒绝外网人员访问。 (二)如何实现需求? 网关 (三)网关的功能 身份认证和权限校验服务…

java线上故障排查套路总结

线上故障主要会包括cpu、磁盘、内存以及网络问题,而大多数故障可能会包含不止一个层面的问题,所以进行排查时候尽量四个方面依次排查一遍。同时例如jstack、jmap等工具也是不囿于一个方面的问题的,基本上出问题就是df、free、top 三连&#x…

Principle Component Analysis

简述PCA的计算过程 输入:数据集X{x1,x2,...,xn},需降到k维 ① 去中心化(去均值,即每个特征减去各自的均值) ② 计算协方差矩阵1/nX*X^T(1/n不影响特征向量&#xff09…

day39-Oracle分区表

0目录 Oracle分区表 1.2.3 1. Oracle分区表 1.1 作用: Oracle数据库的分区把表中的数据行按照分区划成几个区域,提高大数据量下表的性能 1.2 应用场景:常应用于数据量大的表 1.3 分类:Oracle中有范围分区(最常见…

vue使用docxtemplater导出word实现使用textarea输入的内容换行

注:本文只做导出word并且换行操作,不做vue引入docxtemplater步骤 先看一下实现效果 这是文本域输入的 这是导出来的结果 可以看出来导出来的结果也是换行的呢 接下来我们手摸手操作一下流程 首先咱们捋一捋思路 知道文本域的换行的换行标识符,我们发…

[深度学习入门]什么是神经网络?[神经网络的架构、工作、激活函数]

目录 一、前言二、神经网络的架构——以手写数字识别三、神经网络的工作1、单输入单输出感知器函数2、二维输入参数3、三维输入参数 四、激活函数1、激活函数2、ReLU激活函数3、非线性激活函数(1)二输入二输出的神经网络的架构(2)…

基于预测控制模型的自适应巡航控制仿真与机器人实现(Matlab代码实现)

目录 💥1 概述 📚2 运行结果 🎉3 参考文献 👨‍💻4 Matlab代码 💥1 概述 自适应巡航控制技术为目前由于汽车保有量不断增长而带来的行车安全、驾驶舒适性及交通拥堵等问题提供了一条有效的解决途径&am…

数据结构(王道)——数据结构之 二叉树的存储结构

一、顺序存储 静态顺序存储 顺序存储的二叉树结构特性: 顺序存储的非完全二叉树特性 不完全二叉树的可能会浪费大量空间,所以一般顺序存储二叉树比较少用。 图示为什么很少用顺序存储来存二叉树 顺序存储的二叉树总结: 二、链式存储 二叉链表…

TCP的三次握手过程

TCP 是面向连接的协议,所以使用 TCP 前必须先建立连接,而建立连接是通过三次握手来进行的。三次握手的过程如下图: 刚开始客户端处于 closed 的状态,服务端处于 listen 状态。 第一次握手:客户端给服务端发一个 SYN 报…

stb_image简单使用

简介stb_image stb_image 是一个非常轻量级的、单文件的图像加载库,用于加载和解码多种图像格式(如BMP、JPEG、PNG、GIF等)的图像数据。它由Sean T. Barrett开发,并以公共领域(Public Domain)许可发布&…

Redis实战案例21-消息队列

1. 基于JVM的阻塞队列的局限 JVM内存限制问题,大量订单出现时,可能会超过JVM阻塞队列上限;阻塞队列并不能持久化,因为内存不能持久化,出现异常或者宕机之类的故障时,出现数据丢失; 所以引出消息…

关于GPT、AI绘画、AI提词器等AI技术的探讨

目前的AI潮流非常火热,CHATGPT可谓是目前大模型人工智能的代表,刚开始听说chatGPT可以写代码,写作,写方案,无所不能。还有AI绘画也很NB作为一个程序员,为了体验这些A&…

基于深度学习的高精度线路板瑕疵目标检测系统(PyTorch+Pyside6+YOLOv5模型)

摘要:基于深度学习的高精度线路板瑕疵目标检测系统可用于日常生活中来检测与定位线路板瑕疵目标,利用深度学习算法可实现图片、视频、摄像头等方式的线路板瑕疵目标检测识别,另外支持结果可视化与图片或视频检测结果的导出。本系统采用YOLOv5…

物理机传输大文件到虚拟机

物理机快速传输大文件到虚拟机 测试使用Tabby传输大文件到虚拟机 1.1 准备大文件 1.2 通过Tabby上传文件到Linux 总耗时约:7分钟 1.3 通过EveryThing配置服务 打开EveryThing,点击工具—> 选项—>http服务器 启用HTTP服务器,配置…

Hyper-V安装Ubuntu-18.04

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言一、准备工作?二、下载指定的Ubuntu ISO镜像三、开始配置1.点击快速创建2.选择安装源 四、开始安装五、配置启动项总结 前言 最近有个很扯淡的问题…

如何快速入门C#编程?

学习C#需要持续努力和实践,但是在一周内入门是有可能的,前提是你愿意付出足够的时间和精力。以下是一周内入门C#的步骤和建议: 设定学习目标: 在一周内学习C#需要专注于基础知识。明确你的学习目标,例如了解语法、变量…

基于Java+SpringBoot+Vue+Uniapp前后端分离考试学习一体机设计与实现(视频讲解,已发布上线)

博主介绍:✌全网粉丝3W,全栈开发工程师,从事多年软件开发,在大厂呆过。持有软件中级、六级等证书。可提供微服务项目搭建与毕业项目实战,博主也曾写过优秀论文,查重率极低,在这方面有丰富的经验…

《面试1v1》Kafka基础

🍅 作者简介:王哥,CSDN2022博客总榜Top100🏆、博客专家💪 🍅 技术交流:定期更新Java硬核干货,不定期送书活动 🍅 王哥多年工作总结:Java学习路线总结&#xf…

React和Vue生命周期、渲染顺序

主要就是命名不同 目录 React 组件挂载 挂载前constructor() 挂载时render() 挂载后componentDidMount():初始化节点 更新 更新时render():prop/state改变 更新后componentDidUpdate() 卸载 卸载前componentWillUnmount():清理 V…