[toc]
本人博客https://qinzheng7575.github.io/
Pytorch与PySyft安装 基本上按照这个 教程 来就可以,注意的是要查看自己的CUDA版本,我的就是10.1,所有安装了CUDA版的pytorch。会像如下报很多包的缺失也没关系:
一个一个安装就好了,值得注意的是某些包安的时候可能安不上去,应该是因为依赖的关系,先装其它包就好了,就像我是最后安装Flask和requests的,要先把其它的包安装完。
实验代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 import syft as syimport torchimport sysfrom torch.nn import Parameterimport torch.nn as nnimport torch.nn.functional as Fhook = 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 ]) 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' )
1 2 3 4 5 6 7 8 9 10 11 12 13 <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
还是先理解一些基本函数是在做什么 初始化 我们先初始化所需的基本东西:
1 2 3 import torchimport syft as syhook = sy.TorchHook(torch)
发送tensor 然后创建一个远程的虚拟打工人(==卑微的小qin==),并创建一些数据,才能分发给他
1 2 3 4 5 qin = sy.VirtualWorker(hook=hook,id ='qin' ) data = torch.tensor([0 , 1 , 2 , 1 , 2 ]) data_ptr = data.send(qin) print(data_ptr)
(Wrapper)>[PointerTensor | me:15176171534 -> qin:98939698572]
看到这个指针,从我me(pysyft自动生成的打工人)
指向了qin,并且每个打工人还有一个id
现在qin拥有了我们给他发的tensor
。我们能用qin._objects
来看qin
现在有了哪些东西:
{98939698572: tensor([0, 1, 2, 1, 2])}
发现这个数据和data_ptr
里面的一样,说明发送过去了
返还tensor 远处的打工人qin算好了数据,应该把数据传回来,我们通过.get()
从远处的打工人那里拿
1 2 3 4 data = data_ptr.get() print(data) print(qin._objects)
tensor([0, 1, 2, 1, 2]) {}
但是我们仅仅发送取回数据显然不是机器学习,但是后面例子告诉你,我们可以用指针在pytorch里做算法。
通过指针张量(Pointer Tensor)做深度学习 1 2 3 4 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代码上,些许改变得到想要的结果。
1 2 3 4 5 6 7 8 9 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识别为例 源码
我们假定一个场景,现在要训练一个手写字幕识别的网络,但是我们现在没有训练数据,现在有两个人在远处,叫qin
和zheng
,他们手上是数据结合起来,恰好能够训练,但是分别训练各自的数据就无法达到效果。我们还需要注意隐私。
我们就用联邦学习的思想,组合他们的数据去训练一个很棒的模型,同时又不违反隐私。接下来,我们把MINIST的数据分成两部分给他们,来模拟真实场景。
必要的初始化 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 import torchimport torch.nn as nnimport torch.nn.functional as Fimport torch.optim as optimfrom torchvision import datasets, transformsimport loggingimport syft as syimport torchvisionhook = 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" )
搭建所需网络 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 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 ), nn.ReLU(), nn.Conv2d(in_channels=32 , out_channels=64 ,kernel_size=3 , stride=1 ), 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) x = F.max_pool2d(x, 2 ) x = x.view(-1 , 64 *12 *12 ) x = self.fc(x) x = F.log_softmax(x, dim=1 ) return x
数据加载和发送 这里需要加载数据集,然后发往各个打工人那里。然后,迭代训练我们的远程模型。
讲道理接下来就按照正常的Minist步骤就好了,然而我自己还不是很熟悉minist。。。哎又要从头学
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 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
确定我们要发送的正确位置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 def train (args, model, device, train_loader, optimizer, epoch ): model.train() for batch_idx, (data, target) in enumerate (train_loader): 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() ) )
测试代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 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)))
主函数:
1 2 3 4 5 6 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 ==
还没结束! 接下来要做的:
详细的看一遍代码,把每个函数都吃透,像这样 。
参照上面博客,做一下可视化、模型保存的东西。
基于学会的知识,用syft在别的机器学习问题上做一做,增加代码熟悉度。
暂时没想好,或许可以放一放pysyft的东西,考虑下毕设的数学模型了。