纵向联邦线性回归实现-Federated Machine Learning Concept and Applications论文复现

news2024/11/16 22:49:03

本实验的算法实现思路来自这篇论文Federated Machine Learning Concept and Applications

文章目录

  • 场景介绍
  • 同态加密算法
    • python的phe库实现了加法同态加密
      • 角色1
      • 角色2
  • 传统的线性回归
  • 纵向联邦线性回归
  • 纵向联邦线性回归代码实现
    • 导入工具包
    • 准备数据
    • 使用普通线性回归训练
      • 搭建训练过程
    • 纵向联邦线性回归实现
      • 垂直切分数据集
      • 客户端父类实现
      • 客户端A类实现
      • 客户端B类实现
      • 调度发C类实现
      • 搭建训练过程
      • 训练流程实现
      • 预测

场景介绍

现有同一个城市的两家公司A和B,A公司和B公司的业务范围不同,但所服务的业务对象大多都是该城市的人,所以A公司和B公司所持与的数据,样本ID重叠较多,样本特征重叠较少(因为业务逻辑不同)。而且B公司拥有标签y,A公司没有,所以A公司是无法单边建模的。B公司虽然可以单边建模,但由于样本特征数较少,建模效果不理想。现在出现了一种纵向联邦建模的技术,可以在保护参与方数据隐私的前提下进行联合学习,A和B这种情况的联邦学习属于纵向联邦学习。

同态加密算法

同态加密(英语:Homomorphic encryption)是一种加密形式,它允许人们对密文进行特定形式的代数运算得到仍然是加密的结果,将其解密所得到的结果与对明文进行同样的运算结果一样。分为半同态加密和全同态加密。
联邦学习通常采用半同态加密中的加法同态加密,即加密后加法和数乘的结果,与加密前一样
u + v = [ [ u ] ] + [ [ v ] ] k ∗ u = k ∗ [ [ u ] ] \begin{array}{l} u + v = [[u]] + [[v]]\\ k*u = k*[[u]] \end{array} u+v=[[u]]+[[v]]ku=k[[u]]

python的phe库实现了加法同态加密

使用以下命令进行安装

pip install phe

主要有两个角色,角色1:控制私钥和公钥。角色2:没有私钥,只有公钥。使用以下命令导入paillier包

from phe import paillier

角色1

生成公钥和私钥

public_key, private_key = paillier.generate_paillier_keypair()

有了公钥之后,可以对数字进行加密

secret_number_list = [3.141592653, 300, -4.6e-12]
encrypted_number_list = [public_key.encrypt(x) for x in secret_number_list]
encrypted_number = public_key.encrypt(3)

解密需要通过私钥

[keyring.decrypt(x) for x in encrypted_number_list]
>>>[3.141592653, 300, -4.6e-12]

角色2

角色2没有私钥,通常接受加密后的数字,然后与自己的未加密的数据进行运算,然后发送给角色1进行解密。可以通过一下两种方式实现:
1.显式的将加密数字的公钥取出,然后对自己的数据加密,进行运算

# 取出公钥
public_key = encrypted_number.public_key
# 对本地未加密的数据用公钥加密,然后将加密后的数据相加,发送给角色1进行解密
local_number = 10
encrypted_local_number = public_key.encrypt(local_number)
encrypted_sum = encrypted_local_number + encrypted_number

2.将为加密的数据与加密数据直接运算,phe库会帮你隐式的进行转换,这种运算支持以下三种方式

  • 加密数字与标量相加
  • 加密数字与加密数字相加
  • 加密数字与标量相乘
 a, b, c = encrypted_number_list
 a_plus_5 = a + 5
 a_plus_b = a + b
 a_times_3_5 = a * 3.5

Numpy的array操作也支持,矩阵乘向量,向量加法等

import numpy as np
nc_mean = np.mean(encrypted_number_list)
enc_dot = np.dot(encrypted_number_list, [2, -400.1, 5318008])

传统的线性回归

传统的机器学习,若要联合多方进行建模,需要把数据合并到数据中心,然后再训练模型。设X为A和B合并后的数据, θ \theta θ为模型的参数,y为数据的标签。损失函数如下所示:
L = 1 2 n ∑ i = 1 n ( θ T x i + b − y i ) 2 + λ 2 ∣ ∣ θ ∣ ∣ 2 {\rm{L = }}\frac{1}{2n}\sum\limits_{i = 1}^n {{{({\theta ^T}{x_i} + b - {y_i})}^2} +\frac{ \lambda}{2} } ||\theta |{|^2} L=2n1i=1n(θTxi+byi)2+2λ∣∣θ2
损失函数对 θ \theta θ的梯度为:
∂ L ∂ θ = 1 n ∑ i = 1 n ( θ T x i + b − y i ) x i + λ θ \frac{{\partial {\rm{L}}}}{{\partial \theta }} = \frac{1}{n}\sum\limits_{i = 1}^n {({\theta ^T}{x_i} + b - {y_i}){x_i} + \lambda } \theta θL=n1i=1n(θTxi+byi)xi+λθ
最后采用梯度下降法,对参数进行更新
θ t + 1 = θ t − η ∂ L ∂ θ {\theta ^{t + 1}} = {\theta ^t} - \eta \frac{{\partial {\rm{L}}}}{{\partial \theta }} θt+1=θtηθL

纵向联邦线性回归

由于上述方法需要将数据移动到数据中心,对参与方的数据隐私造成了破坏,联邦学习的思想就是在不动参与发的数据的前提下将模型训练出来。对上面损失函数进行调整,令
θ T = ( θ A T θ B T ) x i = ( x i A , x i B ) \begin{array}{l} {\theta ^T} = \left( \begin{array}{l} \theta _A^T\\ \theta _B^T \end{array} \right)\\ \\ {x_i} = (x_i^{\rm{A}},x_i^B) \end{array} θT=(θATθBT)xi=(xiA,xiB)
x i A x_i^{\rm{A}} xiA x i B x_i^{\rm{B}} xiB分别表示A公司、B公司的本地数据, θ A T \theta _A^T θAT θ B T \theta _B^T θBT分别表示A、B公司训练的本地模型
则目标函数变成:
L = 1 2 n ∑ i = 1 n ( θ A T x i A + θ B T x i B + b − y i ) 2 + λ 2 ( ∣ ∣ θ A T ∣ ∣ 2 + ∣ ∣ θ B T ∣ ∣ 2 ) {\rm{L = }}\frac{1}{{2n}}\sum\limits_{i = 1}^n {{{(\theta _A^Tx_i^{\rm{A}} + \theta _B^Tx_i^B + b - {y_i})}^2} + \frac{\lambda }{2}} (||\theta _A^T|{|^2} + ||\theta _B^T|{|^2}) L=2n1i=1n(θATxiA+θBTxiB+byi)2+2λ(∣∣θAT2+∣∣θBT2)
可以进一步简化 ,令
θ B T = ( θ B T b ) x i B = ( x i B , 1 ) \begin{array}{l} \theta _B^T = \left( \begin{array}{l} \theta _B^T\\ b \end{array} \right)\\ \\ x_i^B = (x_i^B,1) \end{array} θBT=(θBTb)xiB=(xiB,1)
将b合并到 θ B T \theta _B^T θBT
损失函数变成:
L = 1 2 n ∑ i = 1 n ( θ A T x i A + θ B T x i B − y i ) 2 + λ 2 ( ∣ ∣ θ A T ∣ ∣ 2 + ∣ ∣ θ B T ∣ ∣ 2 ) {\rm{L = }}\frac{1}{{2n}}\sum\limits_{i = 1}^n {{{(\theta _A^Tx_i^{\rm{A}} + \theta _B^Tx_i^B - {y_i})}^2} + \frac{\lambda }{2}} (||\theta _A^T|{|^2} + ||\theta _B^T|{|^2}) L=2n1i=1n(θATxiA+θBTxiByi)2+2λ(∣∣θAT2+∣∣θBT2)

u i A = θ A T x i A u i B = θ B T x i B \begin{array}{l} u_i^A = \theta _A^Tx_i^{\rm{A}}\\ u_i^B = \theta _B^Tx_i^B \end{array} uiA=θATxiAuiB=θBTxiB
则加密后的损失函数为
[ [ L ] ] = [ [ 1 2 n ∑ i = 1 n ( u i A + u i B − y i ) 2 + λ 2 ( ∣ ∣ θ A T ∣ ∣ 2 + ∣ ∣ θ B T ∣ ∣ 2 ) ] ] {\rm{[[L]] = [[}}\frac{1}{{2n}}\sum\limits_{i = 1}^n {{{(u_i^A + u_i^B - {y_i})}^2} + \frac{\lambda }{2}} (||\theta _A^T|{|^2} + ||\theta _B^T|{|^2})]] [[L]]=[[2n1i=1n(uiA+uiByi)2+2λ(∣∣θAT2+∣∣θBT2)]]

[ [ L A ] ] = [ [ 1 2 ∑ i = 1 n ( u i A ) 2 + λ 2 ∣ ∣ θ A T ∣ ∣ 2 ] ] [ [ L B ] ] = [ [ 1 2 ∑ i = 1 n ( u i B − y i ) 2 + λ 2 ∣ ∣ θ B T ∣ ∣ 2 ] ] [ [ L A B ] ] = [ [ ∑ i = 1 n u i A ( u i B − y i ) ] ] \begin{array}{l} [[{L_A}]] = {\rm{[[}}\frac{1}{2}\sum\limits_{i = 1}^n {{{(u_i^A)}^2} + \frac{\lambda }{2}} ||\theta _A^T|{|^2}]]\\ \\ [[{L_B}]] = {\rm{[[}}\frac{1}{2}\sum\limits_{i = 1}^n {{{(u_i^B - {y_i})}^2} + \frac{\lambda }{2}} ||\theta _B^T|{|^2}]]\\ \\ [[{L_{AB}}]] = {\rm{[[}}\sum\limits_{i = 1}^n {u_i^A(u_i^B - {y_i})} ]] \end{array} [[LA]]=[[21i=1n(uiA)2+2λ∣∣θAT2]][[LB]]=[[21i=1n(uiByi)2+2λ∣∣θBT2]][[LAB]]=[[i=1nuiA(uiByi)]]

[ [ L ] ] = 1 n ( [ [ L A ] ] + [ [ L B ] ] + [ [ L A B ] ] ) [[L]] = \frac{1}{n}([[{L_A}]] + [[{L_B}]] + [[{L_{AB}}]]) [[L]]=n1([[LA]]+[[LB]]+[[LAB]])

[ [ d i ] ] = [ [ u i A ] ] + [ [ u i B − y i ] ] [[{d_i}]] = [[u_i^A]] + [[u_i^B - {y_i}]] [[di]]=[[uiA]]+[[uiByi]]
则损失函数对 θ A T \theta _A^T θAT θ B T \theta _B^T θBT的梯度分别为:
[ [ ∂ L ∂ θ A ] ] = ∑ i [ [ d i ] ] x i A + [ [ λ θ A ] ] [ [ ∂ L ∂ θ B ] ] = ∑ i [ [ d i ] ] x i B + [ [ λ θ B ] ] \begin{array}{l} [[\frac{{\partial L}}{{\partial {\theta _A}}}]] = \sum\limits_i {[[{d_i}]]x_i^{\rm{A}} + [[\lambda {\theta _A}]]} \\ \\ [[\frac{{\partial L}}{{\partial {\theta _B}}}]] = \sum\limits_i {[[{d_i}]]x_i^B + [[\lambda {\theta _B}]]} \end{array} [[θAL]]=i[[di]]xiA+[[λθA]][[θBL]]=i[[di]]xiB+[[λθB]]
因为涉及到解密,所以私钥存放在调度方C,A和B都没有私钥进行解密,所以在交换中间结果是不会泄露隐私,发给C的加密梯度可以用一个随机数进行掩码,这个随机数只有A和B自己知道,所以梯度也不会直接暴露给C
训练过程整个算法流程如图所示
在这里插入图片描述

纵向联邦线性回归代码实现

导入工具包

from sklearn.datasets import load_diabetes
from sklearn.model_selection import train_test_split
from sklearn.linear_model import Ridge
import numpy as np
from sklearn.metrics import r2_score
from VFL_LinearRegression import *
from sklearn.metrics import mean_squared_error
from phe import paillier

准备数据

使用sklearn自带糖尿病数据集

dataset = load_diabetes()
X,y = dataset.data,dataset.target
X_train, X_test, y_train, y_test  = train_test_split(X,y,test_size=0.3)
# 堆叠一列1,把偏置合并到w中
X_train = np.column_stack((X_train,np.ones(len(X_train))))
X_test = np.column_stack((X_test,np.ones(len(X_test))))
# 打印数据形状
for temp in [X_train, X_test, y_train, y_test]:
    print(temp.shape)
>>>
(309, 11)
(133, 11)
(309,)
(133,)

使用普通线性回归训练

先不切分数据,用传统的线性回归训练一遍,相当于把数据集中到数据中心,记录损失
训练参数配置

config = {
    'lambda':0.4, #正则项系数
    'lr':1e-2,    # 学习率
    'n_iters':10, # 训练轮数
}

搭建训练过程

weights = np.zeros(X_train.shape[1])
loss_history = []
for i in range(config['n_iters']):
    L = 0.5 * np.sum(np.square(X_train.dot(weights) - y_train)) + 0.5 * config['lambda'] * np.sum(np.square(weights))
    dL_w = X_train.T.dot(X_train.dot(weights) - y_train) + config['lambda'] * weights
    weights = weights - config['lr'] * dL_w / len(X_train)
    loss_history.append(L)
    print('*'*8,L,'*'*8)
    print('weights:{}'.format(weights))

在这里插入图片描述

纵向联邦线性回归实现

垂直切分数据集

A获得X_train的前6个特征
B获得X_train剩下的特征和训练标签y

idx_A = list(range(6))
idx_B = list(range(6,11))
XA_train,XB_train = X_train[:,idx_A], X_train[:,idx_B]
XA_test,XB_test = X_test[:,idx_A], X_test[:,idx_B]
# 打印形状
for name,temp in zip(['XA_train','XB_train','XA_test','XB_test'],[XA_train,XB_train,XA_test,XB_test]):
    print(name,temp.shape)

客户端父类实现

定义所以客户端的父类,具有共同的行为

class Client(object):
    def __init__(self,config):
        # 模型训练过程中产生的所有数据
        self.data = {}
        self.config = config
        self.other_clinet = {}
    def send_data(self,data,target_client):
        target_client.data.update(data)

客户端A类实现

class ClientA(Client):
    def __init__(self,X,config):
        super().__init__(config)
        self.X = X
        # 初始化参数
        self.weights = np.zeros(self.X.shape[1])
    # 计算u_a
    def compute_u_a(self):
        u_a = self.X.dot(self.weights)
        return u_a
    # 计算加密梯度
    def compute_encrypted_dL_a(self,encrypted_d):
        encrypted_dL_a = self.X.T.dot(encrypted_d) + self.config['lambda'] * self.weights
        return encrypted_dL_a
    # 做predict
    def predict(self,X_test):
        u_a = X_test.dot(self.weights)
        return u_a
    # 计算[[u_a]],[[L_a]]发送给B方
    def task_1(self,client_B_name):
        dt = self.data
        # 获取公钥
        assert 'public_key' in dt.keys(),"Error: 'public_key' from C in step 1 not receive successfully"
        public_key = dt['public_key']
        u_a = self.compute_u_a()
        encrypted_u_a = np.array([public_key.encrypt(x) for x in u_a])
        u_a_square = u_a ** 2
        L_a = 0.5*np.sum(u_a_square) + 0.5 * self.config['lambda'] * np.sum(self.weights**2)
        encrypted_L_a = public_key.encrypt(L_a)
        data_to_B = {'encrypted_u_a':encrypted_u_a,'encrypted_L_a':encrypted_L_a}
        self.send_data(data_to_B,self.other_clinet[client_B_name])
    # 计算加密梯度[[dL_a]],加上随机数之后,发送给C
    def task_2(self,client_C_name):
        dt = self.data
        assert 'encrypted_d' in dt.keys(),"Error: 'encrypted_d' from B in step 1 not receive successfully"
        encrypted_d = dt['encrypted_d']
        encrypted_dL_a = self.compute_encrypted_dL_a(encrypted_d)
        mask = np.random.rand(len(encrypted_dL_a))
        encrypted_masked_dL_a = encrypted_dL_a + mask
        self.data.update({'mask':mask})
        data_to_C = {'encrypted_masked_dL_a':encrypted_masked_dL_a}
        self.send_data(data_to_C,self.other_clinet[client_C_name])
    # 获取解密后的masked梯度,减去mask,梯度下降更新
    def task_3(self):
        dt = self.data
        assert 'mask' in dt.keys(),"Error: 'mask' form A in step 2 not receive successfully"
        assert 'masked_dL_a' in dt.keys(), "Error: 'masked_dL_a' from C in step 1 not receive successfully"
        mask = dt['mask']
        masked_dL_a = dt['masked_dL_a']
        dL_a = masked_dL_a - mask
        # 注意这里的1/n
        self.weights = self.weights - self.config['lr'] * dL_a / len(self.X)
        print("A weights : {}".format(self.weights))

客户端B类实现

class ClientB(Client):
    def __init__(self,X,y,config):
        super().__init__(config)
        self.X = X
        self.y = y
        self.weights = np.zeros(self.X.shape[1])
    # 计算u_b
    def compute_u_b(self):
        u_b = self.X.dot(self.weights)
        return u_b
    # 计算加密梯度
    def compute_encrypted_dL_b(self,encrypted_d):
        encrypted_dL_b = self.X.T.dot(encrypted_d) + self.config['lambda'] * self.weights
        return encrypted_dL_b
    # 做predict
    def predict(self,X_test):
        u_b = X_test.dot(self.weights)
        return u_b
    # 计算[[d]] 发送给A方;计算[[L]],发送给C方
    def task_1(self,client_A_name,client_C_name):
        dt = self.data
        assert 'encrypted_u_a' in dt.keys(),"Error: 'encrypted_u_a' from A in step 1 not receive successfully"
        encrypted_u_a = dt['encrypted_u_a']
        u_b = self.compute_u_b()
        z_b = u_b - self.y
        z_b_square = z_b**2
        encrypted_d = encrypted_u_a + z_b
        data_to_A = {'encrypted_d':encrypted_d}
        self.data.update({'encrypted_d':encrypted_d})
        assert 'encrypted_L_a' in dt.keys(),"Error,'encrypted_L_a' from A in step 1 not receive successfully"
        encrypted_L_a = dt['encrypted_L_a']
        L_b = 0.5 * np.sum(z_b_square) + 0.5 * self.config['lambda'] * np.sum(self.weights**2)
        L_ab = np.sum(encrypted_u_a * z_b)
        encrypted_L = encrypted_L_a + L_b + L_ab
        data_to_C = {'encrypted_L':encrypted_L}
        self.send_data(data_to_A,self.other_clinet[client_A_name])
        self.send_data(data_to_C, self.other_clinet[client_C_name])
    # 计算加密梯度[[dL_b]],mask之后发给C方
    def task_2(self,client_C_name):
        dt = self.data
        assert 'encrypted_d' in dt.keys(),"Error: 'encrypted_d' from B in step 1 not receive successfully"
        encrypted_d = dt['encrypted_d']
        encrypted_dL_b = self.compute_encrypted_dL_b(encrypted_d)
        mask = np.random.rand(len(encrypted_dL_b))
        encrypted_masked_dL_b = encrypted_dL_b + mask
        self.data.update({'mask':mask})
        data_to_C = {'encrypted_masked_dL_b':encrypted_masked_dL_b}
        self.send_data(data_to_C,self.other_clinet[client_C_name])
    # 获取解密后的梯度,解mask,模型更新
    def task_3(self):
        dt = self.data
        assert 'mask' in dt.keys(), "Error: 'mask' form B in step 2 not receive successfully"
        assert 'masked_dL_b' in dt.keys(), "Error: 'masked_dL_b' from C in step 1 not receive successfully"
        mask = dt['mask']
        masked_dL_b = dt['masked_dL_b']
        dL_b = masked_dL_b - mask
        self.weights = self.weights - self.config['lr'] * dL_b / len(self.X)
        print("B weights : {}".format(self.weights))

调度发C类实现

class ClientC(Client):
    def __init__(self,config):
        super().__init__(config)
        self.loss_history = []
        self.public_key = None
        self.private_key = None
    # 产生钥匙对,将公钥发送给A,B方
    def task_1(self,client_A_name,client_B_name):
        self.public_key,self.private_key = paillier.generate_paillier_keypair()
        data_to_AB = {'public_key':self.public_key}
        self.send_data(data_to_AB,self.other_clinet[client_A_name])
        self.send_data(data_to_AB, self.other_clinet[client_B_name])
    # 解密[[L]]、[[masked_dL_a]],[[masked_dL_b]],分别发送给A、B
    def task_2(self,client_A_name,client_B_name):
        dt = self.data
        assert 'encrypted_L' in dt.keys(),"Error: 'encrypted_L' from B in step 2 not receive successfully"
        assert 'encrypted_masked_dL_b' in dt.keys(), "Error: 'encrypted_masked_dL_b' from B in step 2 not receive successfully"
        assert 'encrypted_masked_dL_a' in dt.keys(), "Error: 'encrypted_masked_dL_a' from A in step 2 not receive successfully"
        encrypted_L = dt['encrypted_L']
        encrypted_masked_dL_b = dt['encrypted_masked_dL_b']
        encrypted_masked_dL_a = dt['encrypted_masked_dL_a']
        L = self.private_key.decrypt(encrypted_L)
        print('*'*8,L,'*'*8)
        self.loss_history.append(L)
        masked_dL_b = np.array([self.private_key.decrypt(x) for x in encrypted_masked_dL_b])
        masked_dL_a = np.array([self.private_key.decrypt(x) for x in encrypted_masked_dL_a])
        data_to_A = {'masked_dL_a':masked_dL_a}
        data_to_B = {'masked_dL_b':masked_dL_b}
        self.send_data(data_to_A, self.other_clinet[client_A_name])
        self.send_data(data_to_B, self.other_clinet[client_B_name])

搭建训练过程

初始化客户端对象

clientA = ClientA(XA_train,config)
clientB = ClientB(XB_train,y_train,config)
clientC = ClientC(config)

建立连接

for client1 in [clientA,clientB,clientC]:
    for name,client2 in zip(['A','B','C'],[clientA,clientB,clientC]):
        if client1 is not client2:
            client1.other_clinet[name] = client2
# 打印连接
for client1 in [clientA,clientB,clientC]:
    print(client1.other_clinet)
>>>
{'B': <VFL_LinearRegression.ClientB object at 0x7ffae4ed6a90>, 'C': <VFL_LinearRegression.ClientC object at 0x7ffae52737f0>}
{'A': <VFL_LinearRegression.ClientA object at 0x7ffae52730a0>, 'C': <VFL_LinearRegression.ClientC object at 0x7ffae52737f0>}
{'A': <VFL_LinearRegression.ClientA object at 0x7ffae52730a0>, 'B': <VFL_LinearRegression.ClientB object at 0x7ffae4ed6a90>}

训练流程实现

  1. 初始化A的参数weights,初始化B的参数weights,C创建公钥和私钥,并将公钥发送给A,B

  1. A方计算[[u_a]] , [[L_a]]发送给B方
  2. B方计算[[d]]发送给A, 计算[[L]]发给C

  1. A方计算[[dL_a]],将[[masked_dL_a]] 发送给C
  2. B方计算[[dL_b]],将[[maksed_dL_b]]发送给C
  3. C方解密[[L]],[[masked_dL_a]]解密发送给A,[[maksed_dL_b]]发送给B
for i in range(config['n_iters']):
    # 1.C创建钥匙对,分发公钥给A和B
    clientC.task_1('A','B')
    # 2.1 A方计算[[u_a]] , [[L_a]]发送给B方
    clientA.task_1('B')
    # 2.2 B方计算[[d]]发送给A, 计算[[L]]发给C
    clientB.task_1('A','C')
    # 3.1 A方计算[[dL_a]],将[[masked_dL_a]] 发送给C
    clientA.task_2('C')
    # 3.2 B方计算[[dL_b]],将[[maksed_dL_b]]发送给C
    clientB.task_2('C')
    # 3.3 解密[[L]],[[masked_dL_a]]解密发送给A,[[maksed_dL_b]]发送给B
    clientC.task_2('A','B')
    # 4.1 A、B方更新模型
    clientA.task_3()
    clientB.task_3()

在这里插入图片描述

比较两种方法的损失

np.array(loss_history) - np.array(clientC.loss_history)
>>>
array([ 0.00000000e+00, -9.31322575e-10, -9.31322575e-10, -9.31322575e-10,
        0.00000000e+00, -9.31322575e-10,  0.00000000e+00,  0.00000000e+00,
        0.00000000e+00,  4.65661287e-10])

可以看的误差非常小,所以损失是无损的

预测

根据论文的表述,在预测过程,需要A和B联合预测

y_pred = XA_test.dot(clientA.weights) + XB_test.dot(clientB.weights)

打印均方误差

mean_squared_error(y_test,y_pred)
>>>
25033.750801867864

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

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

相关文章

什么神仙操作,用代码能画这样的图

大家好&#xff0c;我是车辙。不知道同学们画流程图或者时序图一般用的什么软件&#xff1f;Visio 还是 Process On 或者语雀&#xff1f; 因为公司原因&#xff0c;在很多情况下&#xff0c;我一般用语雀画流程图或者思维导图。不过凡事也有例外&#xff0c;对于比较简单的图…

你的电路是抄来的还是算出来的?

在你看这篇文章之前&#xff0c;我想提出几点说明&#xff1a; &#xff08;1&#xff09;最近在看拉扎维的书&#xff0c;写下来这些东西&#xff0c;这也只是我个人在学习过程中的一点总结&#xff0c;有什么观点大家可以相互交流&#xff1b;&#xff08;2&#xff09;不断的…

立创eda专业版学习笔记(3)(隐藏部分飞线)

又到了喜闻乐见的隐藏gnd飞线环节&#xff0c;我发现这个专业版的操作和标志版不一样&#xff0c;我想试一试这个标题的搜索结果&#xff0c;发现有用的结果还是很少&#xff0c;于是我也随便总结了一下&#xff0c;算是添砖加瓦吧。 原来的飞线是这个样子的&#xff1a; 现在我…

巧妙解决appleid问题答案忘了的问题

先说下这个问题解决办法的目标——主要是为了释放被占用的appleid邮箱&#xff0c;而如果你想保留该appleid并且正常使用的话&#xff0c;那么需要付出一点代价&#xff0c;也是可以做到的。 我最近就碰到这种情况&#xff0c;某个邮箱被appleid占用了&#xff0c;问题答案因为…

从实战出发,聊聊缓存数据库一致性

在云服务中&#xff0c;缓存是极其重要的一点。所谓缓存&#xff0c;其实是一个高速数据存储层。当缓存存在后&#xff0c;日后再次请求该数据就会直接访问缓存&#xff0c;提升数据访问的速度。但是缓存存储的数据通常是短暂性的&#xff0c;这就需要经常对缓存进行更新。而我…

Linux常用命令——lsb_release命令

在线Linux命令查询工具 lsb_release 显示发行版本信息 补充说明 LSB是Linux Standard Base的缩写&#xff0c;lsb_release命令用来显示LSB和特定版本的相关信息。如果使用该命令时不带参数&#xff0c;则默认加上-v参数。 -v 显示版本信息。 -i 显示发行版的id。 -d 显示该…

2023 Real World CTF 体验赛 --- wp

文章目录misc&#x1f411;了拼&#x1f411;webEvil MySQL ServerBe-a-Language-ExpertBe-a-Wiki-HackerYummy ApiApacheCommandTextmisc &#x1f411;了拼&#x1f411; 游戏类题目&#xff0c;直接打开js文件搜索rwctf&#xff0c;发现flag rwctf{wellcome_to_the_rwct…

跟着开源项目学java8-从支持最大密码重试次数的提交看redis的场景化使用和基于jdk的schedule的异步延迟日志记录策略

我们这里要实现的功能是登录时添加账号登录错误时最大错误次数和锁定时间&#xff0c;功能不复杂&#xff0c;这次提交里面我们主要来看下一个项目里面一个业务功能怎样写更加优雅 核心实现 我们先来看核心实现的思路 首先是 login 方法重写&#xff0c;进入 loadUserByUser…

【vue2】Vue Cli脚手架与VueTools的安装详解

&#x1f973;博 主&#xff1a;初映CY的前说(前端领域) &#x1f31e;个人信条&#xff1a;想要变成得到&#xff0c;中间还有做到&#xff01; &#x1f918;本文核心&#xff1a;Vue Cli脚手架与VueTools的安装详解 目录 一、vue-cli脚手架工具的安装及文件介绍 1.v…

ArcGIS基础实验操作100例--实验72土地利用变化分析

本实验专栏参考自汤国安教授《地理信息系统基础实验操作100例》一书 实验平台&#xff1a;ArcGIS 10.6 实验数据&#xff1a;请访问实验1&#xff08;传送门&#xff09; 高级编辑篇--实验72 土地利用变化分析 目录 一、实验背景 二、实验数据 三、实验步骤 &#xff08;1&…

ArcGIS 制图流程 非常详细

如何在ArcMap中从头到尾制作一幅专题图&#xff1f;你可以看本编教程&#xff0c;从准备数据到最终导出成图&#xff0c;一步一步进行操作&#xff0c;一定可以教会你。 至于为何使用英文版软件&#xff0c;你如果作图就会知道一般经纬度都是利用英文显示&#xff0c;不然最终…

微信小程序实现左边图片右边文字效果

实现的效果&#xff1a; xml布局文件&#xff1a; <view class"chuxingItem"> <image class"img" src"/pages/image/banche.png"></image> <view style"font-size: 30rpx;margin-left: 15rpx;">班车查询</…

Leetcode:513. 找树左下角的值(C++)

目录 问题描述&#xff1a; 实现代码与解析&#xff1a; 递归&#xff1a; 原理思路&#xff1a; 层序遍历&#xff1a; 原理思路&#xff1a; 问题描述&#xff1a; 给定一个二叉树的 根节点 root&#xff0c;请找出该二叉树的 最底层 最左边 节点的值。 假设二叉树中至…

kaggle平台学习复习笔记 | Data Visualization | Seaborn

目录1.hello Seaborn2.Line Charts3.Bar Charts and Heatmaps4.Scatter PlotsDistributions5.Choosing Plot Types and Custom Styles1.hello Seaborn import pandas as pd pd.plotting.register_matplotlib_converters() import matplotlib.pyplot as plt %matplotlib inline…

阿里云服务器安装mysql数据库教程

阿里云服务器怎么安装mysql数据库&#xff1f;阿里云服务器ECS如何安装mysql数据库教程。主机教程网下面就来分享一下阿里云服务器安装mysql数据库教程。 第一步 1、登录个人的阿里云服务管理终端 2、点击进入远程连接&#xff0c;输入之前设置的远程登录密码&#xff08;如…

能够激发创作灵感的笔记软件,强大在哪里? #RoamResearch

今天的人类知识体系&#xff0c;已经汇聚成了一个浩瀚的信息与思想的海洋&#xff0c;信息量呈指数级增长&#xff0c;如果能够解决潜在的协作问题&#xff0c;这会给个体带来巨大的机会。怎么有效利用信息&#xff1f;如何搭建自己的知识体系&#xff1f;这些都是信息爆炸的时…

在ubuntu系统上用pyinstaller打包yolov5项目代码

目录0. 背景1. 创建虚拟环境2. pyinstaller打包2.1. 生成并修改spec文件2.2. 重新生成二进制文件3. 测试0. 背景 最近需要在ubuntu 18.04上将自己写的一些基于yolov5的项目代码打包成二进制文件&#xff0c;方便部署的同时也尽量减少暴露源码。 参考网上的很多教程&#xff0…

Node.JS(4)--模块、exports和module

文章目录模块核心模块文件模块基本数据类型引用数据类型exports和module.exports的关系模块 分为两大类 核心模块 由node引擎提供的模块 核心模块的标识就是模块的名字 var fsrequire("fs");文件模块 由用户自己创建的模块文件模块的标识就是文件的路径&#x…

一个专注推荐.Net开源项目的榜单

大家好&#xff0c;我是编程乐趣&#xff0c;从7月份开始推荐开源项目&#xff0c;已经推荐了接近100个开源项目了&#xff0c;其中绝大部分是有关.Net的开源项目&#xff0c;也受到大家非常多人的喜欢。 由于公众号不方便查询&#xff0c;很多人又想了解更多的开源项目&#…

C++【多线程】

文章目录一、什么是线程二、创建线程一、什么是线程 线程在进程内部执行&#xff0c;是OS调度的基本单位。 在堆区上存在下面一种数据结构 struct vm_area_struct{ //用来记录这块空间的起始和终止。unsigned long vm_start;unsigned long vm_end;//其实这是一个双向链表中的结…