基于横纵向的混合联邦学习原理分析

        近期陆续接触到关于混合联邦学习的概念,但基于横纵向的混合联邦实际的应用案例却几乎没有看到,普遍是一些实验性的课题,因此这一领域知识没有被很好普及。本篇文章的目的,主要是分析讨论关于横纵向混合联邦学习的业务场景、应用架构、算法执行模式、以及混合联邦学习的算法实现等内容。

        混合联邦学习在一个建模任务中同时包含了横向联邦与纵向联邦,由于包含纵向联邦,所以所有参与者都只能得到与己方特征相关的部分的模型。混合联邦以同时扩充特征数和样本数为目的,是横向联邦与纵向联邦的结合,训练后所有成员都只能得到与己方特征相关的部分模型。以图1为例,模型的后续使用需要 A + B 或者 C + B 才能拼合出一个完整的模型进行预测。

图1. 混合联邦学习面向的数据切分形态

一、业务背景

        在不同的行业中,大多数应用程序只能访问小型或低质量的数据集。标注数据非常昂贵,尤其在需要专业技能和领域知识的领域。此外,特定任务所需的数据可能并非集中存储在一个地方。许多组织可能只有未标注的数据,而其他一些组织可能只有非常有限的标注数据。在当前应用的双方联邦学习场景中,参与者Guest和参与者Host进行隐私求交后的联邦建模数据量非常小,导致模型的性能和精度不足。例如,双方可能有10万条样本数据,但求交后只有5000条数据,双方使用这5000条数据进行建模,这样的数据量不足以构建高质量的模型。因此,提高联合模型的精度是一个亟待解决的问题。

        在隐私计算领域,to B场景中做纵向联邦学习,很少会去考虑双方数据交集量级的问题,一般都会假设已经有足够的样本量级。但现实往往会有很多的不足。例如应用联邦学习的场景中,经常会遇到参与者A和和B拥有的数据,虽然能形成互补,可以联合构建机器学习模型,但是参与者A和B拥有的数据量仍然非常少,构建的联合模型的性能难以达到预期指标,从而联合模型的精确度也不够高。因此在考虑特征互补的基础上,如何去补充更多的样本是混合联邦学习要解决的关键点。

二、应用架构

        因为同时会涉及到横向与纵向的学习,因此混合联邦学习方法适用于具有多组参与者的联邦模型训练。其中,同一组内的参与者的数据集之间包含有相同的样本对象及不同的样本特征(即纵向场景)。不同组间的参与者的数据集之间包含有相同的样本特征及不同的样本对象(即横向场景)。        

        针对每个组,首先根据组内参与者的数据集联合训练每组的纵向联邦学习模型,训练过程中组内每个参与者都与组内其他参与者交换了训练的中间结果,对各组的纵向联邦学习模型进行融合得到横向联邦学习模型,并将横向联邦学习模型发送给每个组内参与者。针对每个组,根据横向联邦学习模型及组内参与者的数据集训练得到更新后的纵向联邦学习模型,返回对各组的纵向联邦学习模型进行融合得到横向联邦学习模型的步骤,直至模型训练结束。可以看到,整个训练过程是组内纵向联邦学习与组间横向联邦学习交替反复迭代,直至完成最终的模型参数的学习。

       图2. 任意组内纵向联邦学习架构

        图2所展示的架构,训练出适用于各组参与者的联邦学习模型过程的一个子训练过程。纵向联邦学习目前主流的是有两种模式:(1)带协调方的可信第三方版本;(2)纯两方学习版本(更推荐这种模式)。训练得到的纵向联邦学习是一个阶段性的联邦学习模型。纵向联邦学习适用于参与者的数据特征重叠较小,而用户重叠较多的情况下,取出参与者用户相同而用户数据特征不同的那部分用户及数据进行联合机器学习训练。比如有属于同一个地区的两个参与者A和B,其中参与者A是一家银行,参与者B是一个电商平台。参与者A和B在同一地区拥有较多相同的用户,但是A与B的业务不同,记录的用户数据特征是不同的。然而A和B记录的用户数据特征可能是互补的。在这样的场景下,可以使用纵向联邦学习方法来帮助A和B构建联合机器学习预测模型,帮助A和B向客户提供更好的服务。

        这里给出一个基于协调者模式的计算流程,帮助理解纵向联邦学习的过程。对于无可信第三方的模式计算流程,有兴趣可以单独与我私信沟通。在具有协调者场景中,A和B联合建模,需要协调者C参与。第一部分:参与者A和B实现加密样本对齐。由于两家企业A和B的用户群体并非完全重合,系统利用基于加密的用户样本对齐技术(隐私集合求交),在A和B不公开各自数据的的前提下确认双方的共有用户,并且不暴露非交集的用户,以便联合这些用户的特征进行建模。纵向联邦学习的加密模型训练过程如下(以下步骤仅以梯度下降算法为例说明训练过程):在确定共有用户群体后,就可以利用这些数据训练机器学习模型。为了保证训练过程中数据的保密性,需要借助协调者C进行加密训练。以线性回归模型为例,训练过程可分为以下4步。第1步,协调者C把公钥分发给A和B,用以对训练过程中需要交换的数据进行加密。第2步,参与者A和B之间以加密形式交互用于计算梯度的中间结果。第3步:参与者A和B分别基于加密的梯度值进行计算,同时参与者B根据其标签数据计算损失函数,并把结果汇总给协调者C。协调者C通过汇总结果计算总梯度值并将其解密。第4步:协调者C将解密后的梯度分别回传给参与者A和B,参与者A和B根据梯度更新各自模型的参数。参与者和协调者选代上述步骤直至损失函数收敛或者是模型参数收敛或者是达到最大选代次数或者是达到最大训练时间,这样就完成了整个模型训练过程。

          图3. 组间结构(协调者作为各纵向联邦学习系统内的组内协调者)

        当协调者作为各纵向联邦学习系统内的组内协调者时,如图3所示,混合联邦学习架构包括2个纵向联邦学习系统(仅以图3示出的2个纵向联邦学习系统为例说明,但纵向联邦学习系统数量可以不限于2个),协调者C1和协调者C2为组内协调者,由协调者C1和协调者C2,对各组的纵向联邦学习模型权重系数进行融合得到横向联邦学习模型,具体如下:

  • 协调者C1和参与者A1、B1训练纵向联邦学习模型M1;与此同时,协调者C2和参与者A2、B2训练纵向联邦学习模型M2。纵向联邦学习模型训练过程可以参考图2所示的纵向联邦学习的架构和流程。
  • 协调者C1和C2分别将各自的纵向联邦学习模型M1和M2发送给对方。
  • 协调者C1和C2分别进行模型融合,例如,对模型M1和]M2参数的值的加权平均值,作为横向联邦学习模型M的对应参数值。
  • 协调者C1和C2分别将横向联邦学习模型M分发给参与者:A1、B1、A2、B2,当然这里各方获得的权重系数都是仅对应其自身所持有的特征而言,其他特征对应的参数不发送。
  • 协调者C1和参与者A1、B1在横向联邦学习模型M的基础上,继续训练新的纵向联邦学习模型,并更新纵向联邦学习模型M1;与此同时,协调者C2和参与者A2、B2在横向联邦学习模型M的基础上继续训练模型,并更新对应的联邦学习模型M2。

        选代以上过程直到横向联邦学习模型M收敛或者达到最大选代次数或者达到最大模型训练时间。在训练好横向联邦学习模型M后,协调者C1将横向联邦学习模型M分发给参与者A1和B1,协调者C2将横向联邦学习模型M分发给参与者A2和B2。参与者A1、B1、A2、B2最终获得的是相同的横向联邦学习模型M,当然是各自持有特征对应的权重系数。

图4. 组间结构(当协调者为各纵向联邦学习系统间的组间协调者)

        当协调者为各纵向联邦学习系统间的组间协调者时,如图4听示,混合联邦学习架构包括n个纵向联邦学习系统,n为大于或等于2的整数,由组内协调者C1~Cn以及组间协调者C0,对各组的纵向联邦学习模型进行融合得到横向联邦学习模型,具体如下:

  • 协调者Cj和参与者Aj、Bj训练纵向联邦学习模型Mj,j=1,2,...,n具体过程可以参考图2所示的架构和流程。
  • 协调者Cj将纵向联邦学习模型Mj发送给组间协调者C0,j=1,2,...,n。
  • 组间协调者C0对收到的纵向联邦学习模型Mj进行模型融合,例如,对纵向联邦学习模型M1~Mj参数值的加权平均值,获得适用于各组参与者的横向联邦学习模型M。
  • 组间协调者C0将横向联邦学习模型更新M分发给各个协调者Cj,j=1,2,...n。也可以,组间协调者C0将横向联邦学习模型更新M直接分发给参与者Aj和Bj,j=1,2,...,n。
  • 协调者Cj将横向联邦学习模型更新M转发给参与者Aj和Bj, j=1,2,...,n。
  • 协调者Cj和参与者Aj、Bj在横向联邦学习模型M的基础上继续训练纵向联邦学习模型,并更新纵向联邦学习模型Mj,1,2,......, n。具体过程可以参考图2所示的联邦学习架构和模型训练流程。

        选代以上过程, 直到横向联邦学习模型M收敛或者达到最大选代次数或者达到最大训练时间。在训练好横向联邦学习模型M后,组间协调者C0将训练好的横向联邦学习模型M分发给协调者Cj,再由协调者Cj将横向联邦学习模型M分发给参与者Aj和Bj,j=1,2,...,n。参与者Aj和Bj最终获得的是相同的横向联邦学习模型M, j=1,2....,n。也可以,组间协调者C0直接将训练好的横向联邦学习模型M分发给参与者Aj和Bj,j=1,2,.....n。

三、算法执行模式

        基于上述讨论,可以得出混合联邦的算法执行流程,通过分级进行联邦学习模型训练:先训练得到各纵向联邦学习系统的纵向联邦学习模型,再根据各纵向联邦学习模型进行横向融合,得到横向联邦学习模型。因此,可以通过上述方法及架构来使用多个参与者拥有的数据,而且纵向联邦学习系统的扩展性较好,可以有效解决参与者拥有的数据量太小的问题。
        训练过程得到纵向联邦学习模型的过程具体包括: 参与者将根据参与者的数据集训练的初始模型为中间结果发送给其他参与者;参与者根据其他参与者反馈的中间结果,得到初始模型的训练结果,并发送给组内协调者:组内协调者根据各参与者的训练结果,确定更新参数并发送给各参与者:参与者根据更新参数更新初始模型,得到纵向联邦学习模型。

        将各组的纵向联邦学习模型中同一参数的参数值进行加权平均,作为横向联邦学习模型中该参数的值。也可以,通过组间协调者,将各组的纵向联邦学习模型中同一参数的参数值进行加权平均,作为横向联邦学习模型中该参数的值;通过组间协调者,将横向联邦学习模型发过送给各组内协调者。组内协调者将横向联邦学习模型发送给组内参与者。
        横向联邦学习适用于各个参与者的数据特征重叠较多,而用户重叠较少的情况下,取出参与者数据特征相同而用户不完全相同的那部分数据进行联合机器学习。比如有两家不同地区的银行,它们的用户群样体分别来自各自所在的地区,相互的交集很小。但是它们的业务很相似,该己录的用户数据特征很大部分是相同的。可以使用横向联邦学习来帮助两家银行构效联合模型来预测他们的客户行为。

        关于数据的切分,这里再讨论一下,给出两种形式,这两种形式应该都会存在应用场景。第一种切分形式更适合不同地区的机构,而第二种切分形式更适合同地区的机构。

        图5展示的是第一种数据切分形式,在这种场景中,A1、B1、C1、A2、B2、C2、An、Bn、Cn都是不同的节点,比如A1、B1、C1分别为上海地区的运营商、电商、银行机构,A2、B2、C2分别为北京地区的运营商、电商、银行机构,An、Bn、Cn是深圳地区的运营商、电商、银行机构。同地区内的机构,共同用户会更多,不同业务机构的特征互补性更高。因此组内更容易构建纵向联邦学习,而组间样本用户数重叠更少,特征的重叠更多,因此更容易采用横向联邦学习计算。

图5.  第一种数据切分形式

        图6展示的是第二种数据切分形式,在这种场景中,参与者只有A、B、C、D、E,都是不同的节点,A中的第一部分样本id与B方的第一部分样本、C方存在重叠,A中的第二部分样本id与B方第二部分样本、D方存在重叠,A中的第n部分样本id与B方第n部分样本、E方存在重叠。这种情况更契合类似的场景,上海地区的电商、保险分别为A和B,然后C、D、E分别为上海地区的三家不同银行。同地区内的机构,共同用户会更多,不同业务机构的特征互补性更高, 然后交集可能会因为金融机构的差异有所差别,需要涉及多家金融机构。组内更容易构建纵向联邦学习,而组间样本用户数重叠更少,特征的重叠更多,因此更容易采用横向联邦学习计算。

图6.  第二种数据切分形式

四、算法实现

        混合切分指的是数据同时包含了水平和垂直切分。此处以图6的数据切分为例:A和B拥有相同的样本但是不同的特征,同时C、D、E拥有不同的样本但是特征相同。

算法:

  1. 对纵向切分数据的多个数据分块进行纵向联邦逻辑回归。
  2. 对多个纵向数据进行横向联邦逻辑回归。

针对混合切分数据,基于随机梯度下降的联邦逻辑回归算法如下:

(假设A持有标签)

以隐语代码实现为例:

1. 初始化

import secretflow as sfsf.init(['alice', 'bob', 'carol', 'dave', 'eric'], address='local', num_cpus=64)alice, bob, carol, dave, eric = (sf.PYU('alice'),sf.PYU('bob'),sf.PYU('carol'),sf.PYU('dave'),sf.PYU('eric'),
)

2. 数据准备

标签列

特征1~特征10

特征11~特征20

特征21~特征30

alice_y0

alice_x0

bob_x0

carol_x0

alice_y1

alice_x1

bob_x1

dave_x0

alice_y2

alice_x2

bob_x2

eric_x0

Alice持有所有的标签数据以及特征1~特征10,bob持有特征11~特征20,carol、dave、eric分别持有特征21~特征30的一部分。

# 从 sklearn.datasets 导入乳腺癌数据集
from sklearn.datasets import load_breast_cancer
# 从 sklearn.preprocessing 导入标准化工具
from sklearn.preprocessing import StandardScaler# 加载乳腺癌数据集,将特征和标签分别存储在 features 和 label 中
features, label = load_breast_cancer(return_X_y=True, as_frame=True)# 对特征数据进行标准化处理
features.iloc[:, :] = StandardScaler().fit_transform(features)# 将标签转换为 DataFrame 格式
label = label.to_frame()# 将特征数据分成三个子列表
feat_list = [features.iloc[:, :10],  # 前10列特征features.iloc[:, 10:20],  # 中间10列特征features.iloc[:, 20:],  # 后10列特征
]# 将标签数据分为三部分
alice_y0, alice_y1, alice_y2 = label.iloc[0:200], label.iloc[200:400], label.iloc[400:]# 将第一个子列表的特征数据分为三部分
alice_x0, alice_x1, alice_x2 = (feat_list[0].iloc[0:200, :],feat_list[0].iloc[200:400, :],feat_list[0].iloc[400:, :],
)# 将第二个子列表的特征数据分为三部分
bob_x0, bob_x1, bob_x2 = (feat_list[1].iloc[0:200, :],feat_list[1].iloc[200:400, :],feat_list[1].iloc[400:, :],
)# 将第三个子列表的特征数据分为三部分
carol_x, dave_x, eric_x = (feat_list[2].iloc[0:200, :],feat_list[2].iloc[200:400, :],feat_list[2].iloc[400:, :],
)
# 导入 tempfile 模块,用于创建临时目录
import tempfile
tmp_dir = tempfile.mkdtemp()# 用于生成临时目录中的文件路径
def filepath(filename):return f'{tmp_dir}/{filename}'# 为 Alice 的标签数据生成文件路径
alice_y0_file, alice_y1_file, alice_y2_file = (filepath('alice_y0'),filepath('alice_y1'),filepath('alice_y2'),
)# 为 Alice 的特征数据生成文件路径
alice_x0_file, alice_x1_file, alice_x2_file = (filepath('alice_x0'),filepath('alice_x1'),filepath('alice_x2'),
)# 为 Bob 的特征数据生成文件路径
bob_x0_file, bob_x1_file, bob_x2_file = (filepath('bob_x0'),filepath('bob_x1'),filepath('bob_x2'),
)# 为 Carol、Dave 和 Eric 的特征数据生成文件路径
carol_x_file, dave_x_file, eric_x_file = (filepath('carol_x'),filepath('dave_x'),filepath('eric_x'),
)# 将 Alice 的特征数据保存到对应文件
alice_x0.to_csv(alice_x0_file, index=False)
alice_x1.to_csv(alice_x1_file, index=False)
alice_x2.to_csv(alice_x2_file, index=False)# 将 Bob 的特征数据保存到对应文件
bob_x0.to_csv(bob_x0_file, index=False)
bob_x1.to_csv(bob_x1_file, index=False)
bob_x2.to_csv(bob_x2_file, index=False)# 将 Carol、Dave 和 Eric 的特征数据保存到对应文件
carol_x.to_csv(carol_x_file, index=False)
dave_x.to_csv(dave_x_file, index=False)
eric_x.to_csv(eric_x_file, index=False)# 将 Alice 的标签数据保存到对应文件
alice_y0.to_csv(alice_y0_file, index=False)
alice_y1.to_csv(alice_y1_file, index=False)
alice_y2.to_csv(alice_y2_file, index=False)
# 从多个参与者的CSV文件中读取垂直分割的数据
vdf_x0 = sf.data.vertical.read_csv({alice: alice_x0_file, bob: bob_x0_file, carol: carol_x_file}
)
vdf_x1 = sf.data.vertical.read_csv({alice: alice_x1_file, bob: bob_x1_file, dave: dave_x_file}
)
vdf_x2 = sf.data.vertical.read_csv({alice: alice_x2_file, bob: bob_x2_file, eric: eric_x_file}
)# 从Alice的CSV文件中读取标签数据
vdf_y0 = sf.data.vertical.read_csv({alice: alice_y0_file})
vdf_y1 = sf.data.vertical.read_csv({alice: alice_y1_file})
vdf_y2 = sf.data.vertical.read_csv({alice: alice_y2_file})# 将多个垂直分割的数据合并为一个混合数据框
x = sf.data.mix.MixDataFrame(partitions=[vdf_x0, vdf_x1, vdf_x2])
y = sf.data.mix.MixDataFrame(partitions=[vdf_y0, vdf_y1, vdf_y2])

3. 构建 HEU 和 SecureAggregator 用于后续训练。

from typing import List# 导入安全聚合器
from secretflow.security.aggregation import SecureAggregator
import spu# 生成 HEU 配置
def heu_config(sk_keeper: str, evaluators: List[str]):return {'sk_keeper': {'party': sk_keeper},  # 秘钥持有者'evaluators': [{'party': evaluator} for evaluator in evaluators],  # 评估者列表'mode': 'PHEU',  # 模式设定为PHEU'he_parameters': {'schema': 'paillier',  # 同态加密方案为 paillier'key_pair': {'generate': {'bit_size': 2048}},  # 密钥生成参数},}# 创建三个 HEU 实例,分别使用不同的评估者配置
heu0 = sf.HEU(heu_config('alice', ['bob', 'carol']), spu.spu_pb2.FM128)
heu1 = sf.HEU(heu_config('alice', ['bob', 'dave']), spu.spu_pb2.FM128)
heu2 = sf.HEU(heu_config('alice', ['bob', 'eric']), spu.spu_pb2.FM128)# 创建三个安全聚合器实例,分别使用不同的参与者配置
aggregator0 = SecureAggregator(alice, [alice, bob, carol])
aggregator1 = SecureAggregator(alice, [alice, bob, dave])
aggregator2 = SecureAggregator(alice, [alice, bob, eric])

4.训练模型

import logginglogging.root.setLevel(level=logging.INFO)# 导入混合联邦逻辑回归模型
from secretflow.ml.linear import FlLogisticRegressionMix# 创建一个联邦逻辑回归模型实例
model = FlLogisticRegressionMix()# 训练模型
model.fit(x,  # 特征数据y,  # 标签数据batch_size=64,  # 批处理大小epochs=3,  # 训练轮数learning_rate=0.1,  # 学习率aggregators=[aggregator0, aggregator1, aggregator2],  # 聚合器列表heus=[heu0, heu1, heu2],  # HEU 实例列表
)
INFO:root:MixLr epoch 0: loss = 0.22200048124132613
INFO:root:MixLr epoch 1: loss = 0.10997288443236536
INFO:root:MixLr epoch 2: loss = 0.08508413270494121
INFO:root:MixLr epoch 3: loss = 0.07325763613227645

5.模型预测

import numpy as np
from sklearn.metrics import roc_auc_scorey_pred = np.concatenate(sf.reveal(model.predict(x)))auc = roc_auc_score(label.values, y_pred)
acc = np.mean((y_pred > 0.5) == label.values)
print('auc:', auc, ', acc:', acc)
auc: 0.98755 , acc: 0.93849

五、参考文献

【1】Li, Wenguo, et al. "Vertical Federated Learning Hybrid Local Pre-training." arXiv preprint arXiv:2405.11884 (2024).

【2】PCT/CN2019/117518《一种混合联邦学习方法及架构》

【3】SECRETFLOW 《混合联邦逻辑回归》

【4】CN113689003A 《一种安全的去除第三方的混合联邦学习框架及方法》

【5】WeFe《联邦学习》

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

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

相关文章

nginx: [warn] 20240 worker_connections exceed open file resource limit: 1024

nginx: [warn] 20240 worker_connections exceed open file resource limit: 1024 报错翻译过来就是: nginx:[警告] 20240 worker_connections超出打开文件资源限制:1024 解决方法: 1.查看当前文件打开数量的限制 ulimit -S…

2024软博会

2024年,金秋的十月,青岛国际会展中心迎来了一年一度的科技盛宴——青岛国际软件融合创新博览会(简称“青岛软博会”)。这场为期三天的展会,不仅吸引了全球软件产业的目光,更成为了展示中国软件产业发展成果…

OpenHarmony南向驱动开发实战-Input

简介 该仓下主要包含Input模块HDI(Hardware Driver Interface)接口定义及其实现,对上层输入服务提供操作input设备的驱动能力接口,HDI接口主要包括如下三大类: InputManager:管理输入设备,包括…

数据库系统概论(超详解!!!) 第十四节 数据库恢复技术

1.事务的基本概念 1.事务 事务(Transaction)是用户定义的一个数据库操作序列,这些操作要么全做,要么全不做,是一个不可分割的工作单位。 事务和程序是两个概念, 在关系数据库中,一个事务可以是一条SQL语句&#xff…

rockey linux rpm安装mysql 8.4.0

背景介绍: 系统 rockey linux 9.4 mysql 8.4.0 我一开始想在系统上安装5.7的着,因为我有这个包,但是通过rpm安装的时候,到最后一步提示我没有/usbin/chkconfig 这个目录,怀疑是系统的问题,然后想安装chk…

未来先行!MWC 2024 世界移动通信大会盛大开幕!!!

2024MWC上海世界移动通信大会,在上海新国际博览中心(SNIEC)盛大开幕。 今年,MWC的主办方GSMA(全球移动通信系统协会)为这届MWC定下了一个主题——“Future First(未来先行)”。各大…

内网穿透技术

内网穿透(NAT traversal)是一种技术,用于实现公网与内网之间的通信连接。当内网中的设备无法直接从公网访问时,内网穿透技术可以通过一些手段,让公网上的设备能够穿透到内网中的设备,建立起通信连接。 说白…

盘点7款适合团队使用的知识库工具

作为一名技术爱好者和企业管理者,我深知知识库工具在日常工作中的重要性。 无论是个人笔记管理还是企业知识共享,知识库工具都能极大地提升我们的工作效率和信息管理水平。 根据麦肯锡全球研究院报告显示,使用知识库工具可以帮助个人或者企…

windows11 OneDrive禁止开机自启动。

1、先上个图: 开机默认自启,然后设置中,也没有找到可以设置的。 2、然后我们通过任务管理器来处理,右键任务栏: 打开任务管理器: 选中OneDrive,然后点击【禁 用】按钮即可。 或者鼠标右键&…

【C++:list】

list概念 list是一个带头的双向循环链表,双向循环链表的特色:每一个节点拥有两 个指针进行维护,俩指针分别为prev和next,prev指该节点的前一个节点,next为该节点的后一个节点 list的底层实现中为什么对迭代器单独写一个结构体进行…

视频均衡驱动器,SDI产品PIN LMH0387

视频均衡驱动器,功能仿制 TI公司 LMH0387产品。本期间支持 DVB-ASI,作为驱动器能够选择输出速率,作为均衡接收器能支持100m 以上传输距离(线缆类型 Belden1694A)。 工作温度范围:-40℃~85℃:a) 电源电压:3.14V~3.46V: 驱动器输出信号:单端 CML 信号: 均衡器输出信号:LVDS 电平…

【C++/STL】:优先级队列的使用及底层剖析仿函数

目录 💡前言一,优先级队列的使用二,仿函数1,什么是仿函数2,仿函数的简单示例 三,优先级队列的底层剖析 💡前言 优先队列(priority_queue)是一种容器适配器,默认使用vector作为其底层…

Pbootcms留言“提交成功”的提示语怎么修改

我们在用到pbootcms建站时候,其中有个留言功能,提交成功后会提示:提交成功(如下图所示),那么我们要修改这个提示语要怎么操作呢? 如果需要修改的话,直接找到文件/apps/home/control…

【Android】【Compose】Compose里面的Row和Column的简单使用

内容 Row和Column的简单使用方式和常用属性含义 Row 在 Jetpack Compose 中,Row 是一种用于在水平方向排列子元素的布局组件。它类似于传统 Android 中的 LinearLayout,但更加灵活和强大。 Row的代码 Composable inline fun Row(modifier: Modifier…

最新扣子(Coze)实战案例:图像流工具之空间风格化,完全免费教程

🧙‍♂️ 大家好,我是斜杠君,手把手教你搭建扣子AI应用。 📜 本教程是《AI应用开发系列教程之扣子(Coze)实战教程》,完全免费学习。 👀 关注斜杠君,可获取完整版教程。👍&#x1f3f…

Python的Django部署uwsgi后自签名实现的HTTPS

通过SSL/TLS来加密和客户端的通信内容。提高网络安全性,但是会损耗部分的服务器资源。 HTTPS 的原理图。 web.key 是打死也不能给其他人的。一定要保存好。里面主要是私钥。是各种认证的根基。本地测试的话生成1024的即可,如果是生产环境推荐使用2048。…

高级运维工程师讲述银河麒麟V10SP1服务器加固收回权限/tmp命令引起生产mysql数据库事故实战

高级运维工程师讲述银河麒麟V10SP1服务器加固收回权限/tmp命令引起生产MySql数据库事故实战 一、前言 作为运维工程师经常会对生产服务器进行安全漏洞加固,一般服务厂商、或者甲方信息安全中心提供一些安全的shell脚本,一般这种shell脚本都是收回权限&…

查看当前服务器Kafka是否已启动

# 查看当前系统中的java进程 # -ml 详细内容 jps -ml | grep Kafka

《人人都是产品经理》:项目坎坷的一生(下)

《人人都是产品经理》:项目坎坷的一生(下) 文档只是手段模板的作用多人协作与版本管理 流程只不过是手段这么多评审,可以省嘛? 敏捷更是手段有计划、更拥抱变化迭代周期内尽量不增加任务 集中工作、小步快跑持续细化需…

开放式耳机哪个牌子好?2024热门红榜开放式耳机测评真实篇!

当你跟朋友们聊天时,他们经常抱怨说长时间戴耳机会令耳朵感到不适,后台也有很多人来滴滴我,作为一位致力于开放式耳机的测评博主,在对比了多款开放式耳机之后,你开放式耳机在保护听力方面确实有用。开放式的设计有助于减轻耳道内的…