毕设之PySyft学习:联邦学习实现MINIST手写识别

文章目录

  • Pytorch与PySyft安装
  • 还是先理解一些基本函数是在做什么
    • 初始化
    • 发送tensor
    • 返还tensor
    • 通过指针张量(Pointer Tensor)做深度学习
  • 初步练习:以Minist识别为例
    • 必要的初始化
    • 搭建所需网络
    • 数据加载和发送
    • 定义训练过程
  • 还没结束!

本人博客 https://qinzheng7575.github.io/

毕设之PySyft学习:联邦学习实现MINIST手写识别_第1张图片

Pytorch与PySyft安装

基本上按照这个 教程 来就可以,注意的是要查看自己的CUDA版本,我的就是10.1,所有安装了CUDA版的pytorch。会像如下报很多包的缺失也没关系:

毕设之PySyft学习:联邦学习实现MINIST手写识别_第2张图片

一个一个安装就好了,值得注意的是某些包安的时候可能安不上去,应该是因为依赖的关系,先装其它包就好了,就像我是最后安装Flask和requests的,要先把其它的包安装完。

实验代码:

import syft as sy
import torch
import sys
from torch.nn import Parameter
import torch.nn as nn
import torch.nn.functional as F

hook = sy.TorchHook(torch)
print(hook)
print(torch.tensor([1,2,3,4,5]))
x = torch.tensor([1,2,3,4,5])
print('x = ', x)
y = x+x
print('y = ', y)
bob = sy.VirtualWorker(hook, id='bob')
print('bob = ', bob)
x = torch.tensor([1,2,3,4,5])
y = torch.tensor([1,1,1,1,1])
#先展示下bob的objs
print('bob._objects = ', bob._objects)
x_ptr = x.send(bob)
y_ptr = y.send(bob)
print('bob._objects = ', bob._objects, 'after send')
print('x_ptr = ', x_ptr)
print('y_ptr = ', y_ptr)
print('x_ptr.location = ', x_ptr.location)
print('x_ptr.owner = ', x_ptr.owner)
z = x_ptr + y_ptr
print('z = ', z)
print('bob._objects = ', bob._objects, 'after add')
<syft.frameworks.torch.hook.hook.TorchHook object at 0x000001D659883348>
tensor([1, 2, 3, 4, 5])
x =  tensor([1, 2, 3, 4, 5])
y =  tensor([ 2,  4,  6,  8, 10])
bob =  <VirtualWorker id:bob #objects:0>
bob._objects =  {}
bob._objects =  {6365454270: tensor([1, 2, 3, 4, 5]), 93154224848: tensor([1, 1, 1, 1, 1])} after send
x_ptr =  (Wrapper)>[PointerTensor | me:74728596879 -> bob:6365454270]
y_ptr =  (Wrapper)>[PointerTensor | me:41675391015 -> bob:93154224848]
x_ptr.location =  <VirtualWorker id:bob #objects:2>
x_ptr.owner =  <VirtualWorker id:me #objects:0>
z =  (Wrapper)>[PointerTensor | me:89273399291 -> bob:40901367564]
bob._objects =  {6365454270: tensor([1, 2, 3, 4, 5]), 93154224848: tensor([1, 1, 1, 1, 1]), 40901367564: tensor([2, 3, 4, 5, 6])} after add

还是先理解一些基本函数是在做什么

初始化

我们先初始化所需的基本东西:

import torch
import syft as sy
hook = sy.TorchHook(torch)#增加额外的功能

发送tensor

然后创建一个远程的虚拟打工人(卑微的小qin),并创建一些数据,才能分发给他

qin = sy.VirtualWorker(hook=hook,id='qin')

data = torch.tensor([0, 1, 2, 1, 2])#创建tensor数据
data_ptr = data.send(qin)#指针指向这个数据
print(data_ptr)

(Wrapper)>[PointerTensor | me:15176171534 -> qin:98939698572]

看到这个指针,从我me(pysyft自动生成的打工人)指向了qin,并且每个打工人还有一个id

现在qin拥有了我们给他发的tensor。我们能用qin._objects来看qin现在有了哪些东西:

print(qin._objects)

{98939698572: tensor([0, 1, 2, 1, 2])}

发现这个数据和data_ptr里面的一样,说明发送过去了

返还tensor

远处的打工人qin算好了数据,应该把数据传回来,我们通过.get()从远处的打工人那里拿

data = data_ptr.get()#取回数据
print(data)

print(qin._objects)#看看此时打工人qin手上有啥

tensor([0, 1, 2, 1, 2])
{}

但是我们仅仅发送取回数据显然不是机器学习,但是后面例子告诉你,我们可以用指针在pytorch里做算法。

通过指针张量(Pointer Tensor)做深度学习

a = torch.tensor([3.14, 6.28]).send(qin)
b = torch.tensor([6.14, 3.28]).send(qin)
c = a + b
print(c)

(Wrapper)>[PointerTensor | me:11768974079 -> qin:12164024826]

在机器上执行c = a + b的时候,一个指令下发给远处的qin,他创建了新的张量,然后给我们发回了一个指针 c ,使用这个API,我们就可以在原有的pytorch代码上,些许改变得到想要的结果。

train = torch.tensor([2.4, 6.2], requires_grad=True).send(qin)
label = torch.tensor([2, 6.]).send(qin)

loss = (train - label).abs().sum()
loss.backward()
train = train.get()

print(train)
print(train.grad)

tensor([2.4000, 6.2000], requires_grad=True)
tensor([1., 1.])

能够定义两个张量,让他们之间做算术,然后还能backward传梯度回来。

恭喜你已经会了初步的操作了,让我们来点有趣的吧.jpg

初步练习:以Minist识别为例

源码

我们假定一个场景,现在要训练一个手写字幕识别的网络,但是我们现在没有训练数据,现在有两个人在远处,叫qinzheng,他们手上是数据结合起来,恰好能够训练,但是分别训练各自的数据就无法达到效果。我们还需要注意隐私。

我们就用联邦学习的思想,组合他们的数据去训练一个很棒的模型,同时又不违反隐私。接下来,我们把MINIST的数据分成两部分给他们,来模拟真实场景。

必要的初始化

import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
from torchvision import datasets, transforms
import logging
import syft as sy
import torchvision

hook = sy.TorchHook(torch)
qin = sy.VirtualWorker(hook=hook, id="qin")
zheng = sy.VirtualWorker(hook=hook, id="zheng")
#设定参数
args = {
    'use_cuda': True,
    'batch_size': 64,
    'test_batch_size': 1000,
    'lr': 0.01,
    'log_interval': 10,
    'epochs': 10
}
use_cuda = args['use_cuda'] and torch.cuda.is_available()
device = torch.device("cuda" if use_cuda else "cpu")

搭建所需网络

# CNN网络
class Net(nn.module):
    def __init__(self):
        super(Net, self).__init__()
        self.conv = nn.Sequential(
            nn.Conv2d(in_channels=1, out_channels=32, kernel_size=3, stride=1),
            #输出26*26*32
            nn.ReLU(),
            nn.Conv2d(in_channels=32, out_channels=64,kernel_size=3, stride=1),
            #输出24*24*64
            nn.ReLU()
        )
        self.fc = nn.Sequential(
            nn.Linear(in_features=64*12*12, out_features=128),
            nn.ReLU(),
            nn.Linear(in_features=128, out_features=10),
        )
        self.dropout = nn.Dropout2d(0.25)  # 随机丢弃

    def forward(self, x):
        x = self.conv(x)#输入的时候是28*28*1,输出应该是24*24*64
        x = F.max_pool2d(x, 2)#用步长为2的池化,输出12*12*64
        x = x.view(-1, 64*12*12)#此时将其拉成一条直线进入全连接
        x = self.fc(x)
        x = F.log_softmax(x, dim=1)
        return x

数据加载和发送

这里需要加载数据集,然后发往各个打工人那里。然后,迭代训练我们的远程模型。

讲道理接下来就按照正常的Minist步骤就好了,然而我自己还不是很熟悉minist。。。哎又要从头学

# 下面是训练数据,需要分发给远处打工人
federated_train_loader = sy.FederatedDataLoader(
    datasets.MNIST('/minist_data', train=True, download=True,
                   transform=transforms.Compose([
                       transforms.ToTensor(),
                       transforms.Normalize((0.1307,), (0.3081,))
                   ]))
    .federate((qin, zheng)),
    batch_size=args['batch_size'], shuffle=True
)
# 下面是测试数据,在我们本地
test_loader = DataLoader(
    datasets.MNIST('/minist_data', train=False,
                   transforms=transforms.Compose([
                       transforms.ToTensor(),
                       transforms.Normalize((0.1307,), (0.3081,))
                   ])),
    batch_size=args['batch_size'], shuffle=True
)

定义训练过程

下面函数中的(data,target)是一对张量指针,从而使用.location确定我们要发送的正确位置

def train(args, model, device, train_loader, optimizer, epoch):
    model.train()
    # 远程迭代
    for batch_idx, (data, target) in enumerate(train_loader):  # enumrate用来编序号
        model = model.send(data.location)  # 发送模型到远程
    data, target = data.to(device), target.to(device)
    optimizer.zero_grad()
    output = model(data)

    loss = F.null_loss(output, target)
    loss.backward()
    optimizer.step()
    # 以上都是发送命令给远程,下面是取回更新的模型

    model.get()
    if batch_idx % args['log_interval'] == 0:  # 打印间隔时间
        # 由于损失也是在远处产生的,因此我们需要把它取回来
        loss = loss.get()
        print('Train Epoch:{}[{}/{}({:.06f}%)]\tLoss:{:.06f}'.format(
            epoch,
            batch_idx * args['batch_size'],
            len(train_loader)*args['batch_size'],
            100.*batch_idx/len(train_loader),
            loss.item()
        )
        )

测试代码:

def test(model, device, test_loader):
    model.eval()
    '''返回model的返回值以字符串显示,使用PyTorch进行训练和测试时
    一定注意要把实例化的model指定train/eval,eval()时,
    框架会自动把BN和DropOut固定住,不会取平均,而是用训练好的值,不然的话,
    一旦test的batch_size过小,很容易就会被BN层导致生成图片颜色失真极大!!'''
    test_loss = 0 #测试损失
    correct=0 #正确率
    with torch.no_grad():
        for data,target in test_loader:
            data,target = data.to(device),target.to(device)
            output=model(data)
            #将损失加起来
            test_loss+=F.nll_loss(output,target,reduction='sum').item()
            '''nll_loss的解释请看
            https://blog.csdn.net/qq_22210253/article/details/85229988
            和https://www.cnblogs.com/ranjiewen/p/10059490.html'''
            #进行预测最可能的分类
            pred =output.argmax(dim=1,keepdim=True)
            correct+=pred.eq(target.view_as(pred)).sum().item()#???
            test_loss/=len(test_loader.dataset)
            print('\nTest set: Average loss: {:.4f}, Accuracy: {}/{} ({:.0f}%)\n'.format(
                test_loss, correct, len(test_loader.dataset),
                100. * correct / len(test_loader.dataset)))

主函数:

model = Net().to(device)
optimizer = optim.SGD(model.parameters(), lr=args['lr'])
logging.info("开始训练!!\n")
for epoch in range(1, args['epochs']+1):
    train(args, model, device, federated_train_loader, optimizer, epoch)
    test(model, device, test_loader)

最后:

Train Epoch:10[59520/60032(99.147122%)] Loss:0.022604

Test set: Average loss: 0.0000, Accuracy: 9821/10000 (98%)

终于走到了这一步,这是联邦学习的一小步,也是自己毕设开始的一步,不管怎样,还是要对今天的自己说声辛苦了!12.13

还没结束!

接下来要做的:

  1. 详细的看一遍代码,把每个函数都吃透,像这样。
  2. 参照上面博客,做一下可视化、模型保存的东西。
  3. 基于学会的知识,用syft在别的机器学习问题上做一做,增加代码熟悉度。
  4. 暂时没想好,或许可以放一放pysyft的东西,考虑下毕设的数学模型了。

你可能感兴趣的:(毕设GP,pytorch,python,深度学习)