联邦学习实战——用FATE从零实现横向逻辑回归

  • 前言
  • 1. FATE平台
    • 1.1 FATE平台架构概述
  • 2. Docker镜像安装FATE
    • 2.1 Docker的安装
    • 2.2 FATE的安装
      • 2.2.1 拉取镜像
      • 2.2.2 启动
      • 2.2.3 测试
  • 3. FATE编程范式
  • 4. 用FATE从零实现横向逻辑回归
    • 4.1 数据集
    • 4.2 逻辑回归
    • 4.3 横向数据切分
    • 4.4 横向联邦模型训练
      • 4.4.1 数据输入
      • 4.4.2 上传配置文件
      • 4.4.3 模型训练
      • 4.4.4 模型评估
      • 4.4.5 多参与方环境配置
  • 阅读总结
  • 参考链接

前言

FATE是微众银行开发的联邦学习平台,是全球首个工业级的联邦学习开源框架,在github上拥有近4000stars,可谓是相当有名气的,该平台为联邦学习提供了完整的生态和社区支持,为联邦学习初学者提供了很好的环境,否则利用python从零开发,那将会是一件非常痛苦的事情。本篇博客内容涉及《联邦学习实战》第四章和第五章内容,使用的fate版本为1.6.0,下面就让我们开始吧。


1. FATE平台

1.1 FATE平台架构概述


FATE的官方文档提供了FATE的架构图,可以看到FATE的架构自上而下分为了四层,最上面一层是FATE提供的服务,包括FATE云服务,FATE面板,FATE任务调度管理,生命周期管理等,往下一层是FATE的机器学习核心组件,包括横向联邦学习、纵向联邦学习等,再往下一层是应用在FATE中的安全协议(隐私保护算法),包括同态加密,FedAvg,RSA等,最后一层是底层框架,又可以分为平行的三层,包括计算框架(TensorFlow,Pytorch,Spark),消息队列协议,以及存储框架。
FATE的主要功能如下:

  • 提供了一种基于数据隐私保护的分布式安全计算框架;
  • 为机器学习、深度学习等常用算法提供高性能的安全计算支持;
  • 支持同态加密、秘密共享等多种多方安全计算协议,确保数据和模型的安全;
  • 提供友好的跨域交互信息管理方案和开发文档,极大方便开发人员使用。

2. Docker镜像安装FATE

FATE支持Linux和Mac系统,支持单机部署、集群部署和KubeFATE部署三种方式,具体的部署安装请查看FATE的官方文档,这里只提供利用Docker镜像配置FATE单机部署。

2.1 Docker的安装

Docker的安装见我的另一篇博客,里面安装步骤很详细,官方文档推荐的是18.09版本,但是在Ubuntu20.04中,只提供了19以上的版本,所以我下载的是19版本,但是不影响FATE的使用。

2.2 FATE的安装

2.2.1 拉取镜像

设置部署的环境变量

$ export version={本次部署的FATE版本号, 如1.7.0}

通过镜像包

$ wget https://webank-ai-1251170195.cos.ap-guangzhou.myqcloud.com/fate/${version}/release/standalone_fate_docker_image_${version}_release.tar;
$ docker load < standalone_fate_docker_image_${version}_release.tar;
$ docker images | grep federatedai/standalone_fate

能看到对应的版本则镜像下载成功。

通过公共镜像拉取

$ docker pull federatedai/standalone_fate:${version}

2.2.2 启动

$ docker run -d --name standalone_fate -p 8080:8080 federatedai/standalone_fate:${version};
$ docker ps -a | grep standalone_fate

端口映射到8080,启动的容器名称为standalone_fate ,后面是镜像名(仓库+镜像名)。后面的指令输入后能看到对应的版本容器启动则成功。
ps:在启动的时候我遇到过端口占用的情况,用指令杀死占用端口的进程,还要注意删除容器,再重复run,不然会显示容器重复,如果杀死进程还是有端口占用,建议重启。

2.2.3 测试

进入容器

$ docker exec -it $(docker ps -aqf "name=standalone_fate") bash

如果报错(参数不匹配),就把$中的内容换成容器的ID即可,后面的bash表示用shell界面操作容器。
Toy测试

$ flow test toy -gid 10000 -hid 10000

如果成功,屏幕会显示

$ success to calculate secure_sum, it is 2000.0

单元测试

$ fate_test unittest federatedml --yes

如果成功,会显示下方语句

$ there are 0 failed test

有些用例算法在examples文件夹下,可以自行使用。

3. FATE编程范式

FATE构建联邦学习模型有两种不同编程范式。

  • 组件化配置:模型训练拆分为不同的任务,每个任务以组建的形式通过有向无环图相连,联邦学习所需的配置参数在配置文件中定义。该模式中,用户只需自定义和提交配置文件,就可以直接执行联邦训练。
  • 脚本编程:FATE提供API接口,用户通过脚本编程的方式实现联邦模型,类似直接使用Python编程。

由于脚本编程不够稳定,这里只使用组件化配置阐述。组件化配置需要提供两个配置文件。

  • dsl配置文件:FATE内置的一套自定义领域特定语言,在dsl中,常见的机器学习任务划分为不同的模块,如数据读写、模型训练、模型评估可以通过一个有向无环图连接。
  • conf配置文件:设置dsl中组件模块的参数。

利用FATE组件化配置的优点:

  • 提供多种安全策略机制;
  • 部署简单方便;
  • 提供可视化界面;
  • 支持常用的机器学习算法。

4. 用FATE从零实现横向逻辑回归

4.1 数据集

本章实验使用的数据集是威斯康星州临床科学中心开源的乳腺癌肿瘤数据集,该数据集内置在sklearn库中,可以直接加载查看。

from sklearn.datasets import load_breast_cancer
import pandas as pdbreast_dataset = load_breast_cancer()
breast = pd.DataFrame(breast_dataset.data, columns=breast_dataset.feature_names)
breast['y'] = breast_dataset.target
# 查看前五组数据
breast.head()

可以看到,数据总共有30个特征(10个属性分别以均值、标准差、最大差值出现三次),569个样本中,恶性肿瘤样本212个,良性有357个。

4.2 逻辑回归

这是一个典型的二分类模型的训练,数据集并不大,使用简单的逻辑回归作为实验模型。
传统的线性回归定义:

y=WTX+by=W^{T}X+by=WTX+b
但是对于二分类模型来说,简单的函数线性映射是不能够分类的,还需要一个非线性函数映射到离散标签,

y=f(WTX+b)y=f(W^{T}X+b)y=f(WTX+b)

在逻辑回归中,使用logistic函数进行非线性映射,logistic表示为:

f(z)=11+e−zf(z)=\frac{1}{1+e^{-z} } f(z)=1+e−z1​

图像表示为:

可以看出,z=WTX+b≥0z=W^{T}X+b\ge 0z=WTX+b≥0,则判断为正例,否则判断为反例。

4.3 横向数据切分

假设当前有两方参与横向联邦学习训练,取乳腺癌数据集前469条数据作为训练集,后100条作为测试集,数据切分的策略如下:

  • 训练数据切分:前200条作为公司A的本地数据,存为breast_1_train.csv,剩余269条作为公司B的本地数据,存为breast_2_train.csv
  • 测试数据集:不切分,由双方共用,存为breast_eval.csv

横向数据切分代码:

from sklearn.datasets import load_breast_cancer
import pandas as pd# 导入并查看数据
breast_dataset = load_breast_cancer()
breast = pd.DataFrame(breast_dataset.data, columns=breast_dataset.feature_names)
breast.head()# z-score标准化
breast = (breast-breast.mean()) / (breast.std())
# 获取列名
col_names = breast.columns.values.tolist()# 更换列名
columns = {}
for idx, n in enumerate(col_names):columns[n] = "x%d"%idx
breast = breast.rename(columns=columns)# 插入每行序列和y
breast['y'] = breast_dataset.target
idx = range(breast.shape[0])
breast.insert(0, 'idx', idx)# 打乱数据并生成csv
breast = breast.sample(frac=1)
train = breast.iloc[:469]
eval =  breast.iloc[469:]
breast_1_train = breast.iloc[:200]
breast_2_train = breast.iloc[200:]
breast_1_train.to_csv('breast_1_train.csv', index=False, header=True)
breast_2_train.to_csv('breast_2_train.csv', index=False, header=True)
eval.to_csv('breast_eval.csv', index=False, header=True)

4.4 横向联邦模型训练

FATE构建联邦学习模型工作:

  • 数据输入:将文件转换为支持的DTable格式。DTable是一个分布式数据集合,FATE所有运算都基于DTable格式进行。
  • 模型训练:FATE为模型训练构建流水线。
  • 模型评估:将训练好的模型在测试集上评估。

4.4.1 数据输入

首先确定基目录,在运行FATE后,可以输入pwd查看,我的基目录为:

fate_dir=/data/projects/fate/

根据数据切分的结果,把三组数据集(.csv)上传到$fate_dir/examples/data/中。
由于文件需要上传到docker容器中,所以要特殊的文件上传工具,这里使用rz,如果docker中没有,则在docker中输入:

$ sudo apt-get install lrzsz

如果是在ubuntu本机上运行的,建议更换设备用xshell远程连接,否则在本机上输入:

$ rz -be

会出现一堆乱码,等半天也不跳出文件框,而在xshell中瞬间弹出,如果出现传输失败,那就要检查要传输的文件是否被使用。

用ls查看,左边一列即是上传的文件。

4.4.2 上传配置文件

由于FATE所有运算都基于DTable格式进行,所以要把上传的文件转换为DTable。首先上传配置文件,配置文件的实例文件有两种,对应v1和v2两个版本。

  • example/dsl/v1
    upload_data.jsonupload_host.jsonupload_guest.json,结构如下:
{"file": "examples/data/breast_hetero_guest.csv",    // 数据文件路径,相对于当前所在路径"head": 1,  // 指定数据文件是否包含表头,1: 是,0: 否"partition": 16,   // 指定用于存储数据的分区数"work_mode": 0,     // 指定工作模式,0: 单机版,1: 集群版"table_name": "breast_hetero_guest",  // 需要转换为DTable格式的表名(相当于后续需要使用的表)"namespace": "experiment" // DTable格式的表名对应的命名空间
}
  • example/dsl/v2/upload
{"file": "/data/projects/fate/examples/data/breast_hetero_guest.csv",    // 数据文件路径,相对于当前所在路径"table_name": "breast_hetero_guest",  // 需要转换为DTable格式的表名"namespace": "experiment",// DTable格式的表名对应的命名空间"head": 1,  // 指定数据文件是否包含表头,1: 是,0: 否"partition": 8,    // 指定用于存储数据的分区数"work_mode": 0,     // 指定工作模式,0: 单机版,1: 集群版"backend": 0    // 指定后端,0:EggRoll, 1: Spark _ RabbitMQ, 2: Spark + Pulsar
}

在FATE-1.6.0版本中,fate_flow的目录存放在fateflow/python/中,所以执行upload的过程和书中不一样,为:

python /fate/python/fate_flow/fate_flow_client.py -f upload -c upload_data.json

从FATE-1.5开始,推荐使用FATE-Flow-Client Command Line执行FATE-Flow任务,上传命令格式为:

$ flow data upload -c example/dsl/v2/upload/upload_conf.json

本文使用v1版本的上传数据操作,如果有想使用v2的朋友,可以访问参考链接的第一个链接。

配置文件如下:

  • 上传数据至公司A。
{"file": "/fate/example/data/breast_1_train.csv","head": 1,"partition": 8,"work_mode": 0,"table_name": "homo_breast_1_train","namespace": "homo_host_breast_train"
}
  • 上传数据至公司B。
{"file": "/fate/example/data/breast_2_train.csv","head": 1,"partition": 8,"work_mode": 0,"table_name": "homo_breast_2_train","namespace": "homo_guest_breast_train"
}
  • 上传测试数据至公司A 。
{"file": "/fate/example/data/breast_eval.csv","head": 1,"partition": 8,"work_mode": 0,"table_name": "homo_breast_1_eval","namespace": "homo_host_breast_eval"
}
  • 上传测试数据至公司B 。

在根目录下,输入python /fate/python/fate_flow/fate_flow_client.py -f upload -c upload_data.json (视每个人的环境而定)。返回如下代码则成功

{"data": {"board_url": "http://127.0.0.1:8080/index.html#/dashboard?job_id=202203110325129735931&role=local&party_id=0","job_dsl_path": "/fate/jobs/202203110325129735931/job_dsl.json","job_id": "202203110325129735931","job_runtime_conf_on_party_path": "/fate/jobs/202203110325129735931/local/job_runtime_on_party_conf.json","job_runtime_conf_path": "/fate/jobs/202203110325129735931/job_runtime_conf.json","logs_directory": "/fate/logs/202203110325129735931","model_info": {"model_id": "local-0#model","model_version": "202203110325129735931"},"namespace": "homo_host_breast_train","pipeline_dsl_path": "/fate/jobs/202203110325129735931/pipeline_dsl.json","table_name": "homo_breast_1_train","train_runtime_conf_path": "/fate/jobs/202203110325129735931/train_runtime_conf.json"},"jobId": "202203110325129735931","retcode": 0,"retmsg": "success"
}

还可以在FATEboard上查看可视化结果。

4.4.3 模型训练

FATE支持常用的机器学习模型,如:

  • 线性模型:横向、纵向的线性回归、logistic回归等线性模型。
  • 树模型:基于纵向的GBDT实现。
  • 神经网络:支持横向的深度神经网络模型DNN。


可以看到在fate dsl_conf的V2版本,模型种类又比书中所描述的详细不少。
V2版本在预设任务配置上有一些改变和提升,最直观的一点是不会自动为训练任务生成预测dsl,需要用户自定义,也可以使用flow命令自动配置预测dsl。
为了在V2中使用命令行客户端,即flow命令,需要进行一系列配置。参考官方文档,FATE Client包含了FATE项目多个客户端:Pipeline, FATE Flow ClientFATE Test
获取所有命令分类和子命令:

    [IN]flow[OUT]Usage: flow COMMAND [OPTIONS]Fate Flow ClientOptions:-h, --help  Show this message and exit.Commands:component   Component Operationsdata        Data Operationsinit        Flow CLI Init Commandjob         Job Operationsmodel       Model Operationsqueue       Queue Operationstable       Table Operationstask        Task Operations

安装FATE CLient

$ pip install fate-client
# 或者
$ pip install fate-client==${version}

集群上安装请移步官方文档。
初始化

# 指定fateflow的IP地址和端口进行初始化
$ flow init --ip 192.168.0.1 --port 9380

获得如下返回视为初始化成功:

{"retcode": 0,"retmsg": "Fate Flow CLI has been initialized successfully."
}

验证
查询任务情况

$ flow job query

返回以下即可:

{"data": [],"retcode": 0,"retmsg": "no job could be found"
}

本章使用逻辑回归模型,进入$fate_dir/examples/dsl/v1/homo_logistic_regression目录,该目录下已经有很多预设的dsl文件和conf文件。修改homo_lr_train_dsl.jsonhomo_lr_train_conf.json。修改推荐用vim,如果没有,用yumapt-get进行安装:

$ yum install -y vim
# 如果yum没有
$ apt-get install -y vim
  • test_homolr_train_dsl.json:描述任务模块,将任务模块以有向无环图形式组合。包括dataio_0homo_lr_0evaluation_0,这些组件分别用作数据格式转换、自带横向逻辑回归组件、模型评估。

  • test_homolr_train_conf.json:设置各个组建的参数。找到role字段,修改三个参数,train_data下的namenamespace,以及表示标签列对应的属性名label_name
    接着是algorithm_parameters字段,它是用来设置训练的超参数信息,包括学习率,优化函数,迭代次数等,可以根据实际需要自行修改。

文件配置结束,在当前位置输入:

$ python /fate/python/fate_flow/fate_flow_client.py -f submit_job -d test_homolr_train_job_dsl.json -c test_homolr_train_job_conf.json

在FATEboard上查看任务运行情况。

4.4.4 模型评估

常用的模型评估方法包括留出法和交叉验证法。

  • 留出法:将数据按照一定比例切分,预留一部分数据作为评估模型数据。
  • 交叉验证法: 将数据集D切分为k份,D1,D2,…,Dk,这样可以获得k组不同的训练数据集和评估数据集,得到k个评估的结果,取其平均值作为最终模型评估结果。

由于之前已经有了额外的数据集作为评估数据集,这里用留出法。为了将留出的数据用于模型评估,需要修改dsl组建配置。具体来说,在test_homolr_train_job_dsl文件中,在components组件下添加一个新的数据输入组件dataio_1,用来读取测试数据,如下所示。

{"components" : {"dataio_0": {"module": "DataIO","input": {"data": {"data": ["args.train_data"]}},"output": {"data": ["train"],"model": ["dataio"]}},"dataio_1": {"module": "DataIO","input": {"data": {"data": ["args.eval_data"] # 表示测试数据采用conf文件中的args.eval_data设置的文件},"model": ["dataio_0.dataio"] # 使用数据训练模块"dataio_0.dataio"的输出作为"dataio_1"的模型输入},"output": {"data": ["eval_data"] # 设置输出的data名称,可任意设定}},"feature_scale_0": {"module": "FeatureScale","input": {"data": {"data": ["dataio_0.train"]}},"output": {"data": ["train"],"model": ["feature_scale"]}},"feature_scale_1": {"module": "FeatureScale","input": {"data": {"data": ["dataio_1.eval_data"]}},"output": {"data": ["eval_data"],"model": ["feature_scale"]}},        "homo_lr_0": {"module": "HomoLR","input": {"data": {"train_data": ["feature_scale_0.train"]}},"output": {"data": ["train"],"model": ["homolr"]}},"homo_lr_1": {"module": "HomoLR","input": {"data": {"eval_data": ["feature_scale_1.eval_data"]  # 指定训练数据},"model": ["homo_lr_0.homolr"]},"output": {"data": ["eval_data"],"model": ["homolr"]}},        "evaluation_0": {"module": "Evaluation","input": {"data": {"data": ["homo_lr_0.train"]}}},"evaluation_1": {"module": "Evaluation","input": {"data": {"data": ["homo_lr_1.eval_data"]}}}        }
}

然后修改conf文件,在role_parameters字段中为guest和host添加测试数据的DTable表名。

{"initiator": {"role": "guest","party_id": 10000},"job_parameters": {"work_mode": 0},"role": {"guest": [10000],"host": [10000],"arbiter": [10000]},"role_parameters": {"guest": {"args": {"data": {"train_data": [{"name": "homo_breast_2_train","namespace": "homo_guest_breast_train"}],"eval_data": [{"name": "homo_breast_2_eval","namespace": "homo_guest_breast_eval"}]}},"dataio_0": {"with_label": [true],"label_name": ["y"],"label_type": ["int"],"output_format": ["dense"]}},"host": {"args": {"data": {"train_data": [{"name": "homo_breast_1_train","namespace": "homo_host_breast_train"}],"eval_data": [{"name": "homo_breast_1_eval","namespace": "homo_host_breast_eval"}]}},"dataio_0": {"with_label": [true],"label_name": ["y"],"label_type": ["int"],"output_format": ["dense"]},"evaluation_0": {"need_run": [false]},"evaluation_1": {"need_run": [false]}}},"algorithm_parameters": {"homo_lr_0": {"penalty": "L2","optimizer": "sgd","tol": 1e-05,"alpha": 0.01,"max_iter": 20,"early_stop": "diff","batch_size": 320,"learning_rate": 0.05,"validation_freqs": 1,"init_param": {"init_method": "zeros"},"encrypt_param": {"method": null},"cv_param": {"n_splits": 4,"shuffle": true,"random_seed": 33,"need_cv": false}},"evaluation_0": {"eval_type": "binary"}}
}

执行submit_job命令。

$ python /fate/python/fate_flow/fate_flow_client.py -f submit_job -d test_homolr_train_job_dsl.json -c test_homolr_train_job_conf.json

可以查看带有模型评估算法模块的有向无环图,该图由训练模块和评估模块两部分构成。

4.4.5 多参与方环境配置

对于多个客户端参与的场景,需要在配置文件中修改部分参数值。

  • 首先在conf文件中找到role字段,在默认情况下只有两方,多客户端就是在role下host子字段方添加新的客户端ID即可,各个客户端ID逗号分隔。
  • role_parameters字段中,host子字段,添加对应新的客户端DTable表名和命名空间。

阅读总结

本来这篇博客因该早在半个月前就能写完,但是编写到一半,代码跑不出来了。。。当时用的是FATE1.7.0,单元测试都没问题,但是只要运行甚至是上传数据,都会报错,报错如下:

不知道访问了多少论坛,在FATE的各个交流群都问了个遍,至今杳无音讯,只能作罢,其中重装fate,重装docker,换fate的旧版本,就差重装系统了。终于测试fate1.6.0版本时,没有这个问题了(虽然有其他小问题但不影响结果)。这半个多月来对我的心智是一个很大的考验,我甚至一度摆烂觉得永远不可能跑通了,好在功夫不负有心人,最后我想对环境有问题的小伙伴们提个建议,如果怎么都没办法跑通,沉淀一下自己,歇几天做别的事情,没准就能转换思路了。最后的最后,如果有朋友知道上图问题如何解决,请务必告诉我,万分感谢!

参考链接

https://blog.csdn.net/Sisyphus_98/article/details/122933110
https://blog.csdn.net/qq_41841524/article/details/117662143?spm=1001.2014.3001.5502

【阅读笔记】联邦学习实战——用FATE从零实现横向逻辑回归相关推荐

  1. 联邦学习实战-2-用FATE从零实现横向逻辑回归

    一.安装FATE平台 FATE是微众银行开源的联邦学习框架 官方地址:https://fate.fedai.org 文档:https://fate.readthedocs.io/en/latest/_ ...

  2. 联邦学习实战-1:用python从零开始实现横向联邦学习

    什么是联邦学习? 简单来说就是在一个多方的环境中,数据集是零散的(在各个不同的客户端中),那么怎样实现机器学习算法呢? 首先想到的就是将多个数据集合并合并起来,然后统一的使用传统的机器学习或者深度学习 ...

  3. 【赠书】重磅好书联邦学习实战来袭!你值得拥有一本

    我们以前给大家介绍过杨强教授团队所著的业界首本联邦学习的书籍,现在这本书的实战版来了,5月刚刚出版,本次给大家赠送3本新书,即<联邦学习实战>. 这是一本什么样的书 所谓联邦学习技术,是一 ...

  4. 【阅读笔记】联邦学习实战——联邦个性化推荐案例

    联邦学习实战--联邦个性化推荐案例 前言 1. 引言 2. 传统的集中式个性化推荐 2.1 矩阵分解 2.2 因子分解机 3. 联邦矩阵分解 3.1 算法详解 3.2 详细实现 4 联邦因子分解机 4 ...

  5. 【阅读笔记】联邦学习实战——联邦学习智能用工案例

    联邦学习实战--联邦学习智能用工案例 前言 1. 智能用工简介 2. 智能用工平台 2.1 智能用工的架构设计 2.2 智能用工的算法设计 3. 利用横向联邦提升智能用工模型 4. 设计联邦激励机制 ...

  6. 【阅读笔记】联邦学习实战——联邦学习攻防实战

    联邦学习实战--联邦学习攻防实战 前言 1. 后门攻击 1.1 问题定义 1.2 后门攻击策略 1.3 详细实现 2. 差分隐私 2.1 集中式差分隐私 2.2 联邦差分隐私 2.3 详细实现 3. ...

  7. 【阅读笔记】联邦学习实战——联邦学习在智能物联网中的应用案例

    联邦学习实战--联邦学习在智能物联网中的应用案例 前言 1. 案例背景与动机 2. 历史数据分析 3. 出行时间预测模型 3.1 问题定义 3.2 构造训练数据集 3.3 模型结构 4 联邦学习实现 ...

  8. 【阅读笔记】联邦学习实战——构建公平的大数据交易市场

    联邦学习实战--构建公平的大数据交易市场 前言 1. 大数据交易 1.1 数据交易定义 1.2 数据确权 1.3 数据定价 2. 基于联邦学习构建新一代大数据交易市场 3. 联邦学习激励机制助力数据交 ...

  9. 【阅读笔记】联邦学习实战——联邦学习医疗健康应用案例

    联邦学习实战--联邦学习医疗健康应用案例 前言 1. 医疗健康数据概述 2. 联邦医疗大数据与脑卒中预测 2.1 联邦数据预处理 2.2 联邦学习脑卒中预测系统 3. 联邦学习在医疗影像中的应用 3. ...

  10. 【阅读笔记】联邦学习实战——联邦学习平台介绍

    前言 FATE是微众银行开发的联邦学习平台,是全球首个工业级的联邦学习开源框架,在github上拥有近4000stars,可谓是相当有名气的,该平台为联邦学习提供了完整的生态和社区支持,为联邦学习初学 ...

最新文章

  1. 渣本毕业两年经验,大厂面试题汇总
  2. 实体与标识符的理解 0125
  3. python开源项目贡献_如何通过为开源项目做贡献来发展自己的职业
  4. java 线程锁概念_Java多线程——锁概念与锁优化
  5. HDOJ--1728--逃离迷宫(广搜)
  6. IBM大力抢占网络宣传阵地,拿下百度“SOA”关键字的第一位
  7. Notepad++常用插件
  8. 十分钟倒计时flash_Flash 101-第6部分:最终倒计时
  9. 想进外企你应该知道的七大基本面试知识
  10. 7-2 前序序列创建二叉树
  11. 智汀指南:如何搭建摄像头远程监控系统?如何用手机远程监控摄像头?
  12. Java SE 12_Frequently-used Class Library(2)
  13. python实现简单的词形统计
  14. 二值形态学之击中击不中变换
  15. 【新知实验室】实时音视频(TRTC)之初体验
  16. 2018 Android开发面试经历
  17. 编写简单的在线考试系统
  18. 基于jsp+mysql+ssm大学校园宿舍零食便利店-计算机毕业设计
  19. 并不简单的翻页时钟(二):JavaScript篇
  20. Vegas为什么能够那么秀?

热门文章

  1. 浙江大学三位一体招生?
  2. 多表连接查询与高级查询上(第三天)
  3. 香港各个大学计算机类专业
  4. 世界上最大的计算机硬盘,三星:2.5英寸16TB,我是史上最大SSD硬盘-消费电子-与非网...
  5. Kafka【问题 02】KafkaTemplate 报错 Bootstrap broker localhost:9092 (id: -1 rack: null) disconnected 问题解决
  6. liu系统的混沌特性及其matlab仿真,毕业论文-Liu系统的混沌特性及其Matlab仿真
  7. php 将中文字符转英文字母_php 中英文语言转换类
  8. c语言中随机排序函数,利用随机函数产生N个随机整数(20000以上),对这些数进行多种方法进行排序。 用C语言编程...
  9. 阿里靠什么武功秘籍渡过“双十一“的天量冲击
  10. 基于CentOS 6.10的Oracle 11g RAC安装手册