IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 人工智能 -> Python实现横向联帮Cifar10图像分类 -> 正文阅读

[人工智能]Python实现横向联帮Cifar10图像分类

参考书目《联邦学习实战》 杨强
在阅读本书的过程中,我尝试根据书中的代码,自己实现横向联邦学习中的图像分类任务,这里是我对代码和逻辑的理解还有出现的问题,希望对大家的学习有所帮助。
下面的表格是一些实验基本信息:

配置信息解释
数据集Cifar10(其将样本划分后给每个客户端作为本地数据)
全局迭代次数服务器和客户端的通信次数
本地模型迭代次数每一次客户端训练的轮数,各个客户端可以相同,也可以不同

????一些其它基础的模型配置信息在json文件中给出:

{
  "model_name" : "resnet18",
  "no_models" : 10,
  "type" : "cifar",
  "global_epochs" : 20,
  "local_epochs": 3,
  "k" : 6,
  "batch_size" : 32,
  "lr" : 0.001,
  "momentum" : 0.0001,
  "lambda" : 0.1
}

获取训练数据集函数dataset.py:

import torchvision.datasets as dataset
import torchvision.transforms as transform


def get_dataset(dir, name):
    if name == 'mnist':
        # 获取训练集和测试集
        train_dataset = dataset.MNIST(dir, train=True, download=True, transform=transform.ToTensor())  # 设置下载数据集并转
        # 换为torch识别的tensor数据类型
        eval_dataset = dataset.MNIST(dir, train=False, transform=transform.ToTensor())  # 测试集
    elif name == 'cifar':
        transform_train = transform.Compose([  # 数据增强操作,训练集的预处理
            transform.RandomCrop(32, padding=4),  # 随机剪裁,大小为32*32,添加4个像素的填充内容
            transform.RandomHorizontalFlip(),  # 随机垂直方向的翻转
            transform.ToTensor(),
            transform.Normalize((0.4914, 0.4822, 0.4465), (0.2023, 0.1994, 0.2010))  # 归一化操作,数值是抽样得到的,无需考虑太
            # 多,分别是均值和标准差
        ])
        transform_test = transform.Compose([  # 对测试集进行预处理
            transform.ToTensor(),
            transform.Normalize((0.4914, 0.4822, 0.4465), (0.2023, 0.1994, 0.2010))
        ])
        train_dataset = dataset.CIFAR10(dir, train=True, download=True, transform=transform_train)  # 获得训练集
        eval_dataset = dataset.CIFAR10(dir, train=False, transform=transform_test)  # 获得测试集

    return train_dataset, eval_dataset

这是一个简单的测试,采用本地模拟的方式进行客户端和服务器的交互,现在定义一个服务端类Server,其中的聚合函数采用的是FedAvg算法更新全局模型,公式如下:
G t + 1 = G t + λ ∑ i = 1 m ( L i t + 1 ? G i t ) G_{t+1}=G_t+\lambda\sum_{i=1}^m(L_i^{t+1}-G_i^t) Gt+1?=Gt?+λi=1m?(Lit+1??Git?)
其中 G t G_t Gt?表示第t轮聚合后的全局模型, L i t + 1 L_i^{t+1} Lit+1?表示第i个客户端在第t+1轮时本地更新后的模型, G t + 1 G_{t+1} Gt+1?表示第t+1轮聚合后的全局模型,由于models库可能有所改变,这里只能指明模型的类型为resnet18:

import torch
import torch.utils.data
from torchvision import models


class Server(object):
    def __init__(self, conf, eval_dataset):  # 构造函数
        self.conf = conf
        self.global_model = eval('models.{}()'.format(conf['model_name']))  # 从配置文件获得模型名称并创建服务器模型
        self.eval_loader = torch.utils.data.DataLoader(eval_dataset, batch_size=self.conf["batch_size"],
                                                       shuffle=True)  # 创建测试集加载器用于测试最终的聚合模型
        if torch.cuda.is_available():
            self.global_model = self.global_model.cuda()

    def model_aggregrate(self, weight_accumulator):  # 聚合函数,更新全局模型用
        # 其中weight_accumulator存放客户端上传参数的变化值, 即更新前全局模型和本地更新后模型的参数变化L_t+1-G_t
        for name, data in self.global_model.state_dict().items():  # 获得全局模型的变量名和参数值
            update_per_layer = weight_accumulator[name] * self.conf["lambda"]  # 乘以系数λ(更新步长)
            if data.type() != update_per_layer.type():  # 如果数据类型不符
                data.add_(update_per_layer.to(torch.int64))  # 进行数据转换后再累加
            else:
                data.add_(update_per_layer)  # 直接进行累加

    def model_eval(self):  # 训练结束后,对全局模型进行评估的函数
        self.global_model.eval()  # 标记进入测试模式,模型参数不发生变化

        total_loss = 0.0  # 记录损失和,计算平均损失用
        correct = 0  # 记录正确数目
        dataset_size = 0  # 测试数据总数
        for batch_id, batch in enumerate(self.eval_loader):  # 对测试数据进行编号和按batch提取数据
            data, target = batch  # 解包数据和标签
            dataset_size += data.size()[0]  # 获得当前batch的数据量,进行累加
            if torch.cuda.is_available():
                data, target = data.cuda(), target.cuda()  # 如果pytorch支持GPU,则使用cuda计算
            output = self.global_model(data)  # 获得预测结果
            total_loss += torch.nn.functional.cross_entropy(output, target, reduction='sum').item()  # 把计算的损失进行累加
            pred = output.data.max(1)[1]  # 取预测值最大的类索引
            correct += pred.eq(target.data.view_as(pred)).cpu().sum().item()  # 获得该batch中预测正确的数目
        acc = 100.0 * (float(correct) / float(dataset_size))  # 得到准确率的百分值
        total_l = total_loss / dataset_size  # 计算平均损失值
        return acc, total_l


客户端进行本地的训练,注意数据集采用的是整个数据集中的一部分,最后需要计算出本地模型与之前全局模型的差值用于传输给服务器更新:

import torch


class Client(object):
    def __init__(self, conf, model, train_dataset, id=1):
        self.conf = conf  # 配置信息
        self.local_model = model  # 本地模型
        self.client_id = id  # 客户端id
        self.train_dataset = train_dataset  # 训练集
        all_range = list(range(len(self.train_dataset)))  # 获得整个未分割训练数据集的下标
        data_len = int(len(self.train_dataset) / self.conf['no_models'])  # 计算本地数据集长度
        indices = all_range[id * data_len : (id + 1) * data_len]  # 切分该客户端对应的数据部分
        self.train_loader = torch.utils.data.DataLoader(self.train_dataset, batch_size=conf['batch_size'],
                                                        sampler=torch.utils.data.sampler.SubsetRandomSampler(indices))
        if torch.cuda.is_available():
            self.local_model = self.local_model.cuda()
        # 训练数据加载器

    def local_train(self, model):
        for name, param in model.state_dict().items():  # 获得全局模型参数
            self.local_model.state_dict()[name].copy_(param.clone())  # 复制全局参数到本地
        # 定义优化器
        optimizer = torch.optim.SGD(self.local_model.parameters(), lr=self.conf['lr'], momentum=self.conf['momentum'])
        self.local_model.train()  # 标记为训练模式,参数可以改变

        for e in range(self.conf['local_epochs']):  # 本地轮数
            for batch_id, bach in enumerate(self.train_loader):  # 按batch加载训练数据
                data, target = bach  # 获得本batch数据和标签
                if torch.cuda.is_available():  # 如果GPU可用
                    data, target = data.cuda(), target.cuda()  # 放在GPU计算
                optimizer.zero_grad()  # 优化器置零
                output = self.local_model(data)  # 获得预测结果
                loss = torch.nn.functional.cross_entropy(output, target)  # 获得预测损失
                loss.backward()  # 进行反向传播
                optimizer.step()
            print('本地模型{}完成第{}轮训练'.format(self.client_id, e))  # 打印目前训练进度
        diff = dict()  # 计算参数差异的容器
        for name, data in self.local_model.state_dict().items():  # 计算差异
            diff[name] = (data - model.state_dict()[name])

        return diff

最后是主函数,其中我将准确率信息和loss记录在excel中用于绘图:

import json
import random
import time

import torch
import dataset
from server import Server
from client import Client
import pandas as pd
import matplotlib.pyplot as plt


if __name__ == '__main__':
    # 存储容器,用于绘制图像
    accs = []  # 存放准确率
    losses = []  # 存放损失

    # 载入配置文件
    with open('config.json', 'r') as f:
        conf = json.load(f)

    train_datasets, eval_datasets = dataset.get_dataset('./data/', conf['type'])  # 获取训练数据和测试数据
    server = Server(conf, eval_datasets)  # 创建服务器
    clients = []  # 客户端列表
    for c in range(conf['no_models']):  # 创建客户端
        clients.append(Client(conf, server.global_model, train_datasets, c))

    for e in range(conf['global_epochs']):  # 进行全局轮数
        candidates = random.sample(clients, conf['k'])  # 随机选取k个客户端
        weight_accumulator = {}  # 创建计算好的参数字典,其值为本地模型计算的变化量之和
        for name, params in server.global_model.state_dict().items():
            weight_accumulator[name] = torch.zeros_like(params)  # 初始化上面的参数字典,大小和全局模型相同

        for c in candidates:  # 逐一获取本地模型更新的变化量并进行累加
            diff = c.local_train(server.global_model)  # 进行本地训练并计算差值字典
            for name, params in server.global_model.state_dict().items():
                weight_accumulator[name].add_(diff[name])

        server.model_aggregrate(weight_accumulator)  # 模型聚合
        acc, loss = server.model_eval()  # 进行全局模型测试
        accs.append(acc)
        losses.append(loss)
        print('全局模型:第{}轮完成!准确率:{:.2f} loss: {:.2f}'.format(e, acc, loss))

    # 将准确率信息存储在txt文件中用于绘图
    df = pd.DataFrame([accs, losses])  # 计入表格
    df.to_excel("data_{}.xlsx".format(int(time.time())))  # 存入文件并加上时间戳
  人工智能 最新文章
2022吴恩达机器学习课程——第二课(神经网
第十五章 规则学习
FixMatch: Simplifying Semi-Supervised Le
数据挖掘Java——Kmeans算法的实现
大脑皮层的分割方法
【翻译】GPT-3是如何工作的
论文笔记:TEACHTEXT: CrossModal Generaliz
python从零学(六)
详解Python 3.x 导入(import)
【答读者问27】backtrader不支持最新版本的
上一篇文章      下一篇文章      查看所有文章
加:2022-02-06 13:50:16  更:2022-02-06 13:50:20 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/19 4:47:59-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码