PyTorch是一个开源的Python机器学习库,基于Torch,用于自然语言处理等应用程序。
2017年1月,由Facebook人工智能研究院(FAIR)基于Torch推出了PyTorch。它是一个基于Python的可续计算包,提供两个高级功能:
1、具有强大的GPU加速的张量计算(如NumPy)。
2、包含自动求导系统的深度神经网络。
这是一个基于Pytorch的框架
在神经网络中,最难的就是对于张量Size的掌控,而这个框架可以对Tensor的Size进行自适应调整,让工作重点从对Size的调整转到对网络主干的理解
Size类,包括channel,width,height三个属性,可以储存图片和向量的信息
Layer类,储存层级的单位,包括了in_size和out_size两个数据
Options类,储存所有的层级信息和其他信息
Block类,代替nn.Module,有自动前馈的功能
Utils类,包括了Train和Draw两个函数,可以实现低代码调参训练
CNN网络
class CNN(Block):
def __init__(self, options):
super(CNN, self).__init__(options)
self.UseConv2d(8) \
.UseReLU() \
.UseBatchNorm2d() \
.UseMaxPool2d()
self.UseConv2d(16) \
.UseReLU() \
.UseBatchNorm2d() \
.UseMaxPool2d()
self.UseConv2d(32) \
.UseReLU() \
.UseBatchNorm2d() \
.UseMaxPool2d()
self.UseView(0)
self.UseLinear(1000) \
.UseLinear(100) \
.UseLinear(10)
训练代码
data_tf = Compose([ToTensor(), Normalize([0.5], [0.5])])
data_path = 'C:\\0_data\\MNIST_DATA_PyTorch' # minist数据集的下载路径
# 获取数据集
train_data = mnist.MNIST(data_path, train=True, transform=data_tf, download=False)
test_data = mnist.MNIST(data_path, train=False, transform=data_tf, download=False)
train_loader = data.DataLoader(train_data, batch_size=128, shuffle=True)
test_loader = data.DataLoader(test_data, batch_size=128, shuffle=True)
model, loss, accuracy = Utils.Train(model_name=CNN,
options=Options(1, 28, 28, 'cuda'),
train_loader=train_loader,
test_loader=test_loader,
accuracy_calculate=lambda x, y: (max(x, 1)[1].numpy() == y.numpy()).mean(),
epoch_cout=10,
loss_func_name=nn.CrossEntropyLoss,
optimizer_name=optim.Adam,
learn_rate=0.001)
Utils.Draw('LOSS', Loss=loss, Accuracy=accuracy)
save(model, 'model/cnn')
一维向量,二维图片
名称 | 介绍 | 对象 |
---|---|---|
Conv2d | 2维卷积层 | 图片 |
MaxPool2d | 2维最大池化 | 图片 |
AvgPool2d | 2维平均池化 | 图片 |
BatchNorm2d() | 2为标准化 | 图片 |
Linear | 全连接层 | 向量 |
ReLU | ReLU激活函数 | * |
Sigmoid | Sigmoid激活函数 | * |
View | 对数据进行拉伸 | 只支持图片和向量的转换 |
from matplotlib import pyplot as plt
from torch import nn
from torch.autograd import Variable
import numpy as np
class Size:
def __init__(self, channel, width, height):
self.channel = channel
self.width = width
self.height = height
def __str__(self):
return f'channel:{self.channel}\twidth:{self.width}\theight:{self.height}'
class Layer:
def __init__(self, typename, **kwargs):
self.name = typename
self.args = kwargs
@property
def in_size(self) -> Size:
return self.args['in_size']
@property
def out_size(self) -> Size:
return self.args['out_size']
class Options:
def __init__(self, channel, width, height, device='cpu'):
self.__options = {}
self.__layers = [Layer('Input',
out_size=Size(channel, width, height),
in_size=Size(channel, width, height))]
self.__device = device
def Device(self, model=None):
if model is not None:
model = model.to(self.__device)
return model
else:
return self.__device
@property
def Layers(self):
return self.__layers
def __getitem__(self, item):
if item not in self.__options.keys():
raise Exception('Unknown Key')
return self.__options[item]
def __setitem__(self, key, value):
self.__options[key] = value
self.__dict__[key] = value
def __len__(self):
return len(self.__layers)
def AddProperty(self, **kwargs):
for item in kwargs.keys():
self.__options[item] = kwargs[item]
self.__dict__[item] = kwargs[item]
def AddLayer(self, typename, **kwargs):
data = Layer(typename, **kwargs)
self.__layers.append(data)
def GetIndexLayer(self, index) -> Layer:
return self.__layers[index]
def GetLastLayerSize(self) -> Size:
return self.GetIndexLayer(-1).out_size
class Block(nn.Module):
def __init__(self, options: Options = None):
super(Block, self).__init__()
self.options = options if options is not None else Options()
self.layer = nn.Sequential()
self._start = len(self.options)
self._count = 0
def SetDevice(self):
self.options.Device(self)
def GetOptions(self):
return self.options
def UseConv2d(self, out_channels, kernel_size=3, padding=0, stride=1):
in_size = self.options.GetLastLayerSize()
self.layer.append(nn.Conv2d(in_channels=in_size.channel,
out_channels=out_channels,
kernel_size=kernel_size,
padding=padding,
stride=stride))
self.options.AddLayer('Conv2d',
in_channels=in_size.channel,
out_channels=out_channels,
kernel_size=kernel_size,
padding=padding,
stride=stride,
in_size=in_size,
out_size=Size(out_channels,
int((in_size.width + 2 * padding - kernel_size) / stride + 1),
int((in_size.height + 2 * padding - kernel_size) / stride + 1)))
self._count += 1
return self
def UseMaxPool2d(self, kernel_size=2, padding=0, stride=1):
self.layer.append(nn.MaxPool2d(kernel_size=kernel_size, padding=padding, stride=stride))
in_size = self.options.GetLastLayerSize()
self.options.AddLayer('MaxPool2d',
kernel_size=kernel_size,
padding=padding,
stride=stride,
in_size=in_size,
out_size=Size(in_size.channel,
int((in_size.width + 2 * padding - kernel_size) / stride + 1),
int((in_size.height + 2 * padding - kernel_size) / stride + 1)))
self._count += 1
return self
def UseAvgPool2d(self, kernel_size=2, padding=0, stride=1):
self.layer.append(nn.AvgPool2d(kernel_size=kernel_size, padding=padding, stride=stride))
in_size = self.options.GetLastLayerSize()
self.options.AddLayer('AvgPool2d',
kernel_size=kernel_size,
padding=padding,
stride=stride,
in_size=in_size,
out_size=Size(in_size.channel,
int((in_size.width + 2 * padding - kernel_size) / stride + 1),
int((in_size.height + 2 * padding - kernel_size) / stride + 1)))
self._count += 1
return self
def UseLinear(self, out_features):
in_size = self.options.GetLastLayerSize()
in_features = in_size.width
if in_size.channel != 0: # 如果是0通道,代表是线性的
raise Exception('in_features is None')
self.layer.append(nn.Linear(in_features=in_features, out_features=out_features))
self.options.AddLayer('Linear',
in_features=in_features,
out_features=out_features,
in_size=in_size,
out_size=Size(0, out_features, 0))
self._count += 1
return self
def UseBatchNorm2d(self, channel=None):
in_size = self.options.GetLastLayerSize()
if in_size.channel == 0:
raise Exception('This Layer Could Not Be 0')
channel = in_size.channel
self.layer.append(nn.BatchNorm2d(channel))
self.options.AddLayer('BatchNorm2d',
channel=channel,
in_size=in_size,
out_size=in_size)
self._count += 1
return self
def UseView(self, channel, width=0, height=0):
# 只支持图像或者向量
in_size = self.options.GetLastLayerSize()
if channel == 0:
width = in_size.channel * in_size.width * in_size.height
self.options.AddLayer('View',
channel=channel,
width=width,
height=height,
in_size=in_size,
out_size=Size(channel, width, height))
self._count += 1
return self
def UseReLU(self):
in_size = self.options.GetLastLayerSize()
self.layer.append(nn.ReLU())
self.options.AddLayer('ReLU',
in_size=in_size,
out_size=in_size)
self._count += 1
return self
def UseSigmoid(self):
in_size = self.options.GetLastLayerSize()
self.layer.append(nn.Sigmoid())
self.options.AddLayer('Sigmoid',
in_size=in_size,
out_size=in_size)
self._count += 1
return self
def forward(self, data):
cnt = 0
for index in range(self._count):
layer = self.options.Layers[self._start + index]
if layer.name == 'View':
if layer.args['channel'] == 0:
# 转换为向量
data = data.view(data.size(0), -1)
else:
data = data.view(data.size(0), layer.args['width'], layer.args['height'])
elif layer.name == 'Input':
data = data.to(self.options.Device)
continue
else:
data = self.layer[cnt](data)
cnt += 1
return data
class Utils:
@staticmethod
def Train(model_name,
options: Options,
train_loader,
test_loader,
accuracy_calculate,
epoch_cout,
loss_func_name,
optimizer_name,
learn_rate):
loss_func = loss_func_name()
model = model_name(options)
model.SetDevice()
optimizer = optimizer_name(model.parameters(), lr=learn_rate)
loss_value = []
accuracy_value = []
for epoch in range(epoch_cout):
for index, (x, y) in enumerate(train_loader):
batch_x = options.Device(Variable(x))
batch_y = options.Device(Variable(y))
out = model(batch_x)
loss = loss_func(out, batch_y)
optimizer.zero_grad()
loss.backward()
optimizer.step()
loss = loss.cpu()
loss_value.append(loss.data)
accuracy = []
accuracy.clear()
for index, (x, y) in enumerate(test_loader):
batch_x = options.Device(Variable(x))
batch_y = Variable(y)
out = model(batch_x).cpu()
accuracy.append(accuracy_calculate(out, batch_y))
accuracy_value.append(np.array(accuracy).mean())
return model, loss_value, accuracy_value
@staticmethod
def Draw(name, **kwargs):
plt.figure('LOSS')
for key in kwargs.keys():
plt.plot(kwargs[key], label=key)
plt.legend()
plt.show()