机器学习周记(第四十三周:MCformer)2024.6.10~2024.6.16

目录

  • 摘要
  • ABSTRACT
  • 1 论文信息
    • 1.1 论文标题
    • 1.2 论文摘要
    • 1.3 论文引言
    • 1.4 论文贡献
  • 2 论文模型
    • 2.1 问题定义
    • 2.2 可逆实例归一化(Reversible Instance Normalization)
    • 2.3 混合通道块 (Mixed-Channels Block)
    • 2.4 编码器(Decoder)
  • 3 相关代码

摘要

本周阅读了一篇关于多变量时间序列的论文。论文模型 MCformer 结合了 CI 策略的数据扩展优势,同时减轻了通道间相关性遗忘的问题。MCformer 使用了一种称为混合通道模块的方法,以增强多变量时间序列数据集的表示能力,同时使用原生的 Transformer 编码器来建模序列的长期和跨变量特征。实验结果表明,在多变量时间序列预测任务中,混合通道策略优于纯 CI 策略。

ABSTRACT

This week, We read a paper on multivariate time series. The paper’s model, MCformer, combines the data expansion advantages of the CI strategy while alleviating the issue of inter-channel correlation forgetting. MCformer employs a method called the Mixed Channels module to enhance the representational capacity of multivariate time series datasets, and it uses a native Transformer encoder to model both long-term and cross-variable features of the sequence. Experimental results demonstrate that the Mixed Channels strategy outperforms the pure CI strategy in multivariate time series forecasting tasks.

1 论文信息

1.1 论文标题

MCformer: Multivariate Time Series Forecasting with Mixed-Channels Transformer

1.2 论文摘要

大规模物联网(IoT)设备会生成大量时间序列数据,迫使人们探索更有效的多变量时间序列预测模型。在以前的模型中,主要采用的是通道依赖(CD)策略(其中每个通道代表一个单变量序列)。当前最先进的(SOTA)模型主要依赖于通道独立(CI)策略。CI 策略将多通道序列视为独立的单通道序列,从而扩展数据集以提高泛化性能,并避免了破坏长期特征的通道间相关性。然而,CI 策略面临着通道间相关性遗忘的问题。为了解决这个问题,我们提出了一种创新的混合通道策略,该策略结合了 CI 策略的数据扩展优势,同时减轻了通道间相关性遗忘的问题。基于这种策略,论文引入了 MCformer,这是一种具有混合通道特征的多变量时间序列预测模型。该模型融合了特定数量的通道,利用注意力机制在建模长期特征时有效捕捉通道间的相关信息。实验结果表明,在多变量时间序列预测任务中,混合通道策略优于纯 CI 策略。

1.3 论文引言

随着 IoT 设备在气象学、交通和电力等领域的广泛应用,设备数量的增加也导致大量时间序列数据的生成。这些数据可以用于决策、资源分配以及预测未来趋势,从而提高物联网系统的效率和可靠性。源自 IoT 设备的时间序列预测任务旨在根据历史数据预测未来状态。鉴于物联网数据通常具有非线性、快速采样和多通道等特性,这一任务存在一定的挑战。

由于物联网设备生成的时间序列数据通常具有较长的采样间隔和多采样通道,在处理这些设备的多变量时间序列时,必须考虑长序列建模和多个通道之间的复杂相互关系。鉴于 Transformer 在自然语言处理领域中展示的长序列建模的杰出能力,这种能力在时间序列预测任务中也至关重要,促使了诸如 LogTransInformerReformerAutoformerFEDformerScaleFormerPyraformerFPPformer等模型的出现。这些模型在长时间序列建模方面取得了显著进展。

近年来,一些研究将重点转向多变量时间序列的挑战,例如 CrossformerSageFormer 等模型。这些模型在所有通道上进行学习,特别关注捕捉多个变量之间的依赖关系。所有这些模型都可以被视为通道依赖(CD)策略模型,其中将单变量序列视为一个通道。这种方法将多变量数据视为一个整体输入,并允许模型学习通道之间的相关性,如下图1所示。

然而,这些 CD 策略模型也存在局限性。DLinear 以其简单的架构超过了现有模型。PatchTST 引入了一种通道独立(CI)策略模型,进一步提升了最先进的水平(SOTA)。CI 策略将多变量时间序列数据视为独立的单变量序列,如下图1所示。CI策略的成功引起了人们对 CD 和 CI 策略对模型影响的关注,可见于 PRRegPETformerCSformeritransformer 等模型。随后,TiDE(一种基于MLP的CI模型) 不仅表现与PatchTST相似,而且在时空效率上表现出色。PETformer 的研究发现,通道独立优于通道依赖,可能是因为多变量特征会干扰长序列特征的提取。这一结果与直觉相悖,因为在深度学习中,更多的信息通常会提高模型的泛化能力。

总的来说,现有 SOTA 模型主要依赖 CI 策略有两个主要原因:首先,CI 策略可以扩展数据集以提高模型的泛化性能;其次,CI 策略可以避免因通道间相关信息而破坏长期特征信息,正如PETformer 所展示的。然而,CI 策略也有缺点,因为它倾向于忽略通道间的特征信息。在通道数量较多的情况下,可能会出现通道间相关性遗忘的问题,类似于 RNN 中的长期信息遗忘。在这种背景下,论文提出了一种混合通道策略。该策略保留了 CI 策略在数据集扩展方面的优势,同时通过选择性地混合有限数量的通道,有效地缓解了长期特征信息的破坏和过拟合问题,也解决了通道间相关性遗忘的问题。
在这里插入图片描述
上图1展示了 混合通道策略通道独立(CI)通道依赖(CD) 策略的区别。CD 策略通过考虑所有通道的历史数据来预测未来值,而 CI 策略则在预测未来值时不考虑其他通道的历史数据,且所有通道共享模型参数。与通道依赖策略和通道独立策略相比,混合通道策略保持了通道独立策略的优势,同时混合了有限数量的随机选择的通道,以增强模型捕捉通道间相关信息的能力。

基于上述策略,论文提出了一种具有混合通道特征的多通道时间序列预测模型。图2提供了该模型的概述。具体来说,论文的模型首先使用 CI 策略扩展数据,然后混合特定数量的通道,并允许注意力机制在建模长期特征信息时有效捕捉通道间的相关信息。最后,将编码器的结果还原以获得所有通道的预测值。
在这里插入图片描述

1.4 论文贡献

① 提出了一种混合通道策略,旨在最小化通道特征在 CD 策略下破坏长期信息的缺点,同时保留CI 策略下扩展数据集的优势。这使模型能够更有效地学习通道间的依赖信息。

② 基于混合通道策略,提出了一种具有混合通道特征的多变量时间序列预测模型。通过使用混合通道块,模型扩展了数据集,并通过混合方法整合了通道间的依赖信息。

2 论文模型

2.1 问题定义

在多变量时间序列预测任务中,历史观测值表示为 X = x 1 , x 2 , . . . , x t ∈ R t × M X={x_{1}, x_{2}, ..., x_{t}} \in \R^{t \times M} X=x1,x2,...,xtRt×M,其中 t 是时间步,M 是变量的数量。每个时间步 t 的观测值表示为一个 M 维向量: x t = [ x t 1 , x t 2 , . . . , x t M ] ⊤ x_{t}=[x_{t}^{1},x_{t}^{2},...,x_{t}^{M}]^{\top} xt=[xt1,xt2,...,xtM]。论文的目标是基于过去的观测值预测未来时间步的多变量观测值。这个问题可以形式化为:给定时间步 t 之前的观测序列 { x 1 , x 2 , . . . , x t } \{x_{1},x_{2},...,x_{t}\} {x1,x2,...,xt},预测时间步 t+1 到 t+h 的多变量观测序列 { x t + 1 , . . . , x t + h } \{x_{t+1},...,x_{t+h}\} {xt+1,...,xt+h},其中 h 是要预测的时间步数。论文将混合通道模块 (Mixed-Channels Block) 集成到基础 Transformer 编码器中,以扩展数据集并融合通道间的依赖信息。论文的 MCformer 模型的架构如下图3所示。
在这里插入图片描述

2.2 可逆实例归一化(Reversible Instance Normalization)

引入 可逆实例归一化(Reversible Instance Normalization,RevIN) 旨在解决训练和测试数据之间非均匀时间分布的问题,这通常被称为分布偏移。在混合通道模块之前,应用实例归一化来归一化每个通道的数据。对于每个实例 x t i x_{t}^{i} xti 的单个通道 x i = [ x 1 i , x 2 i , . . . , x t i ] x^{i}=[x_{1}^{i},x_{2}^{i},...,x_{t}^{i}] xi=[x1i,x2i,...,xti],计算其均值和标准差。在获得预测结果后,这些非平稳性信息组件会被加回到预测值中。

R e v I N ( x i ) = { γ i x i − M e a n ( x i ) V a r ( x i ) + ε } , i = 1 , 2 , . . . , M        ( 1 ) RevIN(x^{i})=\{\gamma_{i} \frac {x^{i}-Mean(x^{i}) }{\sqrt{Var}(x^{i})+\varepsilon} \}, i=1,2,...,M\ \ \ \ \ \ (1) RevIN(xi)={γiVar (xi)+εxiMean(xi)},i=1,2,...,M      (1)

2.3 混合通道块 (Mixed-Channels Block)

论文引入了一种称为混合通道模块的方法,以增强多变量时间序列数据集的表示能力。

Flatten: 论文采用通道独立(CI)策略,将来自 M 个通道的数据展平。对于给定的样本 X,展平后得到 X F = F l a t t e n ( X ) ∈ R t M × 1 X_F=Flatten(X) \in \R ^{tM \times1} XF=Flatten(X)RtM×1。然后将展平后的 X F X_F XF 视为M个独立的样本。

Mixed Channels: Mixed-Channels Block是在 Flatten 之后将不同通道的数据进行组合,通过以下步骤执行混合通道操作:

  1. 计算区间大小: 首先计算区间大小 ⌊ M m ⌋ \lfloor \frac M m \rfloor mM,其中 m 是要混合的通道数。

  2. 混合通道操作: 对于给定的时间步 t,从目标通道开始,以区间步长堆叠其他所有通道来形成 U i ∈ R t × m U^i \in \R^{t \times m} UiRt×m。具体而言,混合通道模块的输出定义为:

U i = M i x e d C h a n n e l s ( x i , m ) = [ s t a c k ( x i , C 1 , C 2 , . . . , C m ) ]        ( 2 ) U^{i}=MixedChannels(x^{i},m)=[stack(x^{i},C^{1},C^{2},...,C^{m})]\ \ \ \ \ \ (2) Ui=MixedChannels(xi,m)=[stack(xi,C1,C2,...,Cm)]      (2)

其中, C i C^{i} Ci 表示在第 i 个间隔处获取的第 i 个通道,1 ≤ i ≤ m。通过引入混合通道模块,其目的是增加输入数据的表示能力,加入多通道信息,以更好地捕捉时间序列数据的特征。

补丁和投影: 在当前的研究中,已经观察到,相比于使用时间点数据作为输入,使用补丁可以更好地捕捉局部信息,并且捕捉到更丰富的变量间依赖关系。因此,利用补丁在混合通道后聚合序列,并采用单层 MLP 来投影通道依赖性以及相邻的时间依赖性。表示为:

P i = P r o j e c t i o n ( P a t c h ( U i ) )        ( 3 ) P^{i}=Projection(Patch(U^{i}))\ \ \ \ \ \ (3) Pi=Projection(Patch(Ui))      (3)

这里 P i ∈ R P × N P^{i}\in\R^{P \times N} PiRP×N ,其中P表示投影后的长度,补丁的数量为 N, N = ⌊ ( L − p ) S ⌋ + 2 N=\lfloor \frac{(L-p)}{S} \rfloor+ 2 N=S(Lp)+2,p 表示补丁的长度,S 表示步长。使用补丁的方法可以保留时间依赖性和通道间依赖性。它不仅保留了 Transformer 输入的标记,还进一步增加了预测窗口的大小,同时保持了时间依赖性。图 4 说明了在时间序列任务中使用补丁的方法。
在这里插入图片描述
在这里插入图片描述

2.4 编码器(Decoder)

论文采用原生的 Transformer 编码器来建模序列的长期和跨变量特征,这类似于 PatchTST 中使用的方法。由于 Transformer 没有明确建模序列的顺序,为了提供位置信息,论文使用了可学习的加权位置编码 W p o s ∈ R P × N W_{pos} \in \R^{P \times N} WposRP×N。位置编码被添加到输入序列的嵌入表示 x i n i = P i + W p o s x_{in}^{i}=P^{i}+W_{pos} xini=Pi+Wpos 上。通过这种方式,模型可以区分不同位置的元素。

编码器由多个具有相同结构的层组成,每一层包括两个子层:多头自注意力层和前馈神经网络层。多头自注意力层是编码器的第一个子层。在这个层中,输入序列中的每个元素都可以与其他所有元素进行交互,而不仅仅是与其相邻的元素。这里使用三个权重矩阵 W Q W^{Q} WQ W K W^{K} WK W V W^{V} WV 来计算 Q i = ( x i n i ) T W Q Q^{i}=(x_{in}^{i})^{T}W^{Q} Qi=(xini)TWQ K i = ( x i n i ) T W K K^{i}=(x_{in}^{i})^{T}W^{K} Ki=(xini)TWK V i = ( x i n i ) T W V V^{i}=(x_{in}^{i})^{T}W^{V} Vi=(xini)TWV。这是通过计算注意力分数实现的,每个元素会接收到一组权重,指示其对其他元素的重要性。通过使用多头注意力机制,模型可以学习不同方面的注意力,从而更好地捕捉序列中的信息:

A t t e n t i o n ( Q i , K i , V i ) = s o f t m a x ( Q i ( K i ) T d k ) V i        ( 4 ) Attention(Q^{i},K^{i},V^{i})=softmax(\frac{Q^{i}(K^{i})^{T}}{\sqrt {d_{k}}})V^{i}\ \ \ \ \ \ (4) Attention(Qi,Ki,Vi)=softmax(dk Qi(Ki)T)Vi      (4)

在多头自注意力层之后是前馈神经网络层。每个位置的表示通过一个全连接的前馈神经网络进一步处理。这个网络通常包括两个全连接层,其输出被添加到输入中,形成一个残差连接。这有助于在训练过程中缓解梯度消失问题。

在每个子层的输入和输出之间,使用残差连接和层归一化。残差连接的引入使得模型更容易学习恒等映射,从而有助于深度网络的训练。此外,每个子层的输出还经过层归一化,以稳定训练过程。

3 相关代码

Transformer:

class PositionalEncoding(nn.Module):
    def __init__(self, outfea, max_len):
        super(PositionalEncoding, self).__init__()

        # 计算位置编码
        pe = torch.zeros(max_len, outfea)
        position = torch.arange(0, max_len, dtype=torch.float).unsqueeze(1)
        div_term = torch.exp(torch.arange(0, outfea, 2).float() * (-torch.log(
            torch.tensor(10000.0)) / outfea))
        pe[:, 0::2] = torch.sin(position * div_term)
        pe[:, 1::2] = torch.cos(position * div_term)
        pe = pe.unsqueeze(0)
        self.register_buffer('pe', pe)

    def forward(self, x):
        x = x + x + self.pe[:, :x.size(1)]
        return x


class MultiHeadAttention(nn.Module):
    def __init__(self, outfea, num_heads):
        super(MultiHeadAttention, self).__init__()
        self.num_heads = num_heads
        self.outfea = outfea
        assert outfea % num_heads == 0
        self.depth = outfea // num_heads

        # 查询、键和值的线性投影
        self.query_linear = nn.Linear(outfea, outfea)
        self.key_linear = nn.Linear(outfea, outfea)
        self.value_linear = nn.Linear(outfea, outfea)

        # 输出线性投影
        self.output_linear = nn.Linear(outfea, outfea)

    def split_heads(self, x):
        batch_size, seq_length, outfea = x.size()
        return x.view(batch_size, seq_length, self.num_heads, self.depth).transpose(1, 2)

    def forward(self, query, key, value, mask=None):
        # 线性投影
        query = self.query_linear(query)  # [batch_size, seq_length, outfea]
        key = self.key_linear(key)
        value = self.value_linear(value)

        # 分割头部
        query = self.split_heads(query)  # [batch_size, num_heads, seq_length, depth]
        key = self.split_heads(key)
        value = self.split_heads(value)

        # 缩放点积注意力
        scores = torch.matmul(query, key.transpose(-2, -1)) / math.sqrt(
            self.depth)  # [batch_size, num_heads, seq_length, seq_length]

        # 如果提供了掩码,则应用掩码
        if mask is not None:
            scores += scores.masked_fill(mask == 0, -1e9)

        # 计算注意力权重并应用softmax
        attention_weights = torch.softmax(scores, dim=-1)

        # 应用注意力到值
        attention_output = torch.matmul(attention_weights, value)

        # 合并头部
        batch_size, _, seq_length, d_k = attention_output.size()
        attention_output = attention_output.transpose(1, 2).contiguous().view(batch_size,
                                                                              seq_length, self.outfea)

        # 线性投影
        attention_output = self.output_linear(attention_output)

        return attention_output


class FeedForward(nn.Module):
    def __init__(self, outfea, hiddenfea):
        super(FeedForward, self).__init__()
        self.linear1 = nn.Linear(outfea, hiddenfea)
        self.linear2 = nn.Linear(hiddenfea, outfea)
        self.relu = nn.ReLU()

    def forward(self, x):
        # 线性变换1
        x = self.linear1(x)
        # 非线性激活
        x = self.relu(x)
        # 线性变换2
        x = self.linear2(x)

        return x


class EncoderLayer(nn.Module):
    def __init__(self, outfea, num_heads, hiddenfea, dropout):
        super(EncoderLayer, self).__init__()
        self.self_attention = MultiHeadAttention(outfea, num_heads)
        self.feed_forward = FeedForward(outfea, hiddenfea)
        self.norm1 = nn.LayerNorm(outfea)
        self.norm2 = nn.LayerNorm(outfea)
        self.dropout = nn.Dropout(dropout)

    def forward(self, x, mask):
        # 自注意力层
        attention_output = self.self_attention(x, x,
                                               x, mask)
        attention_output = self.dropout(attention_output)
        x = x + attention_output
        x = self.norm1(x)

        # 前馈层
        feed_forward_output = self.feed_forward(x)
        feed_forward_output = self.dropout(feed_forward_output)
        x = x + feed_forward_output
        x = self.norm2(x)

        return x


class DecoderLayer(nn.Module):
    def __init__(self, outfea, num_heads, hiddenfea, dropout):
        super(DecoderLayer, self).__init__()
        self.masked_self_attention = MultiHeadAttention(outfea, num_heads)
        self.enc_dec_attention = MultiHeadAttention(outfea, num_heads)
        self.feed_forward = FeedForward(outfea, hiddenfea)
        self.norm1 = nn.LayerNorm(outfea)
        self.norm2 = nn.LayerNorm(outfea)
        self.norm3 = nn.LayerNorm(outfea)
        self.dropout = nn.Dropout(dropout)

    def forward(self, x, encoder_output, src_mask, tgt_mask):
        # 掩码的自注意力层
        self_attention_output = self.masked_self_attention(x, x, x, tgt_mask)
        self_attention_output = self.dropout(self_attention_output)
        x = x + self_attention_output
        x = self.norm1(x)

        # 编码器-解码器注意力层
        enc_dec_attention_output = self.enc_dec_attention(x, encoder_output,
                                                          encoder_output, src_mask)
        enc_dec_attention_output = self.dropout(enc_dec_attention_output)
        x = x + enc_dec_attention_output
        x = self.norm2(x)

        # 前馈层
        feed_forward_output = self.feed_forward(x)
        feed_forward_output = self.dropout(feed_forward_output)
        x = x + feed_forward_output
        x = self.norm3(x)

        return x


class Transformer(nn.Module):
    def __init__(self, outfea, num_heads, num_layers, hiddenfea,
                 max_len, dropout):
        super(Transformer, self).__init__()

        # 定义位置编码层
        self.positional_encoding = PositionalEncoding(outfea, max_len)

        # 定义编码器和解码器的多层堆叠
        self.encoder_layers = nn.ModuleList([EncoderLayer(outfea, num_heads, hiddenfea, dropout)
                                             for i in range(num_layers)])
        self.decoder_layers = nn.ModuleList([DecoderLayer(outfea, num_heads, hiddenfea, dropout)
                                             for i in range(num_layers)])

        # 定义线性层
        self.linear = nn.Linear(outfea, outfea)
        self.dropout = nn.Dropout(dropout)

    # 生成掩码
    def generate_mask(self, src, tgt):
        src_mask = (src != 0).unsqueeze(1).unsqueeze(2)
        tgt_mask = (tgt != 0).unsqueeze(1).unsqueeze(3)
        seq_length = tgt.size(1)
        nopeak_mask = (1 - torch.triu(torch.ones(1, seq_length, seq_length), diagonal=1)).bool()
        tgt_mask = tgt_mask & nopeak_mask
        return src_mask, tgt_mask

    # 前向传播
    def forward(self, src, tgt):
        src_mask, tgt_mask = self.generate_mask(src, tgt)

        # 编码器输入的词嵌入和位置编码
        encoder_embedding = self.encoder_embedding(src)
        en_positional_encoding = self.positional_encoding(encoder_embedding)
        src_embedded = self.dropout(en_positional_encoding)

        # 解码器输入的词嵌入和位置编码
        decoder_embedding = self.decoder_embedding(tgt)
        de_positional_encoding = self.positional_encoding(decoder_embedding)
        tgt_embedded = self.dropout(de_positional_encoding)

        enc_output = src_embedded
        for enc_layer in self.encoder_layers:
            enc_output = enc_layer(enc_output, src_mask)

        dec_output = tgt_embedded
        for dec_layer in self.decoder_layers:
            dec_output = dec_layer(dec_output, enc_output, src_mask, tgt_mask)

        output = self.linear(dec_output)
        return output

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

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

相关文章

安全可靠跨国传输的前提下,如何兼顾数据跨国快速传输?

在全球化的商业环境中,跨国公司在与国际客户、合作伙伴或海外分支机构进行数据跨国快速传输时,不可避免会遇到一系列挑战。比如网络延迟、数据包丢失、带宽限制以及数据安全和合规性问题,一定程度上都会影响数据传输的效率,业务的…

项目的打包

一:打包到微信小程序 1)vscode打包 2)在微信小程序开发工具中打开路径,上传. 疑问:为什么pnpm bulid:mp-weixin用于打包,pnpm dev:mp-weixin也可生成对应路径下的文件?? 打包的是没有热重载,且打包体积更小. 二:条件编译 vscode可以打包成能在不同平台上运行的代码.但是有…

大数据关联规则算法

关联性(Association) 定义:指一个变量能够提供有关另一个变量的信息。特点:关联性是一个广泛的概念,它可以包括直接的、间接的、强的或弱的联系。 相关性(Correlation) 定义:指两个…

新手搭建Magic-API

项目场景: 我本是一个前端和GIS开发工程师,但新单位并没有配置完整的开发团队,确切说目前只有我一个人做开发,那么肯定避免不了要研究下后端。最近有一个小程序要开发,管理平台我直接用的fastAdminthinkphp写完了页面…

IAM风险CTF挑战赛

wiz启动了一个名为“The Big IAM Challenge”云安全CTF挑战赛。旨在让白帽子识别和利用 IAM错误配置,并从现实场景中学习,从而更好的认识和了解IAM相关的风险。比赛包括6个场景,每个场景都专注于各种AWS服务中常见的IAM配置错误。 Challenge…

企业工程图纸很多,应该如何进行图纸管理?

企业工程图纸很多,应该如何进行图纸管理? 设计制造企业在实际设计和生产过程中会产生大量的工程图纸,图纸一多管理起来就会十分麻烦,管理不当则是会影响整体的工作效率。对于大量工程图纸的管理,有多种方式方法来进行…

数据结构-算法和算法分析

目录 前言一、算法1.1 算法与程序1.2 算法描述方法1.3 算法特性1.4 算法设计的要求 二、算法分析2.1 算法时间效率的度量2.1.1 事前分析方法算法的渐进时间复杂度算法时间复杂度分析例子算法最坏时间复杂度时间复杂度的计算规则 2.2 算法空间效率的度量 总结 前言 程序 数据结…

深度优先遍历解决迷宫问题(顺序栈的应用)

学习贺利坚老师课程 数据结构例程——迷宫问题(用栈结构)_数据结构迷宫问题-CSDN博客文章浏览阅读3.1w次,点赞25次,收藏118次。本文针对数据结构基础系列网络课程(3):栈和队列中第6课时栈的应用2-迷宫问题。例&#x…

品牌为什么要做电商控价

消费者购买产品的途径愈发多样,抖音、快手等直播电商的兴起进一步拓宽了品牌的销售渠道。市场形态越是丰富,品牌所要应对的问题自然也就越多。主流电商平台如淘宝、拼多多,依然是消费者主要的选购之处,即便不购物,电商…

使用nvm管理nodejs版本,设置淘宝NPM镜像源

nvm-windows https://github.com/coreybutler/nvm-windows nvm配置文件的路径 C:\Users\用户名\AppData\Roaming\nvm 修改 settings.txt 文件,添加淘宝镜像源地址 node_mirror: https://npmmirror.com/mirrors/node/ npm_mirror: https://npmmirror.com/mirrors…

tauri嵌入外部二进制文件,以及sidecar是什么意思?

sidecar是什么意思 有时,为了使应用程序正常运行或防止用户安装额外的依赖项(例如Node.js或Python或者ffmpeg等),你可能需要嵌入依赖的二进制文件,我们将这种二进制文件称为"sidecar",中文意思就…

Navicat 重装 查找 保存的查询sql文件

背景:Navicat 一个收费的软件,存在的最大缺点就是收费,所以我们为了优化它会遇到卸载重装这些复杂的过程,但是我们保存的查询sql会跟随卸载Navicat而删除,为了节省时间省去不必要的麻烦,我们可以查到我们保…

基于STM32和人工智能的智能楼宇安防系统

目录 引言环境准备智能楼宇安防系统基础代码实现:实现智能楼宇安防系统 4.1 数据采集模块4.2 数据处理与分析4.3 控制系统4.4 用户界面与数据可视化应用场景:智能楼宇安防管理与优化问题解决方案与优化收尾与总结 1. 引言 随着物联网和人工智能技术的…

后端数据null前端统一显示成空

handleNullValues方法在封装请求接口返回数据时统一处理 // null 转 function handleNullValues(data) {// 使用递归处理多层嵌套的对象或数组function processItem(item) {if (Array.isArray(item)) {return item.map(processItem);} else if (typeof item object &&…

开源的语音合成项目-EdgeTTS,无需部署无需Key

前几天和大家分享了:全网爆火的AI语音合成工具-ChatTTS。 有很多小伙伴反应模型下载还有点麻烦~ 今天再给大家带来一款开源的语音合成 TTS 项目-EdgeTTS,相比ChatTTS,操作起来对小白更友好。 因为其底层是使用微软 Edge 的在线语音合成服务…

Java数据结构与算法——稀疏数组和队列

一、稀疏数组sparsearray数组 该二维数组的很多值是默认值0,因此记录了很多没有意义的数据,可以采用稀疏数组进行压缩 1.基本介绍: 当一个数组中大部分元素为0,或者为同一个值的数组时,可以使用稀疏数组来保存该数组。 稀疏数组的处理方法…

c++文件io,字符串io简单介绍

目录 c文件io 介绍 采用文件流对象操作文件的一般步骤 示例 注意点 利用字节流特性 字符串io 介绍 istringstream ostringstream 示例 c文件io 介绍 c根据文件内容的数据格式分为二进制文件和文本文件 基本上和c一样 c 标准库中有许多不同的标志 用于指定流对象的…

Ollama(docker)+ Open Webui(docker)+Comfyui

Windows 系统可以安装docker desktop 相对比较好用一点,其他的应该也可以 比如rancher desktop podman desktop 安装需要windows WSL 安装ollama docker docker run -d --gpusall -v D:\ollama:/root/.ollama -p 11434:11434 --name ollama ollama/ollama 这里…

CI /CD学习

CI/CD概述 CI/CD 是持续集成和持续交付/部署的缩写,旨在简化并加快软件开发生命周期。 持续集成(CI)是指自动且频繁地将代码更改集成到共享源代码存储库中的做法。持续交付和/或持续部署(CD)是一个由两部分组成的过程…

Paper Reading: PAMS:通过参数化最大尺度量化超分辨率

PAMS: Quantized Super-Resolution via Parameterized Max Scale PAMS:通过参数化最大尺度量化超分辨率, ECCV 2020 paper: https://arxiv.org/pdf/2011.04212.pdf GitHub: https://github.com/colorjam/PAMS 摘要 深度卷积神经网络(DCNNs)…