神经网络-分类(复习篇)
最近觉得基础不扎实,回头复习,查缺补漏,增加对神经网络训练等方面的理解[加油!今天搞懂分类和基础算法为止!]
torchvision.transforms
- torchvision.transforms.Compose(list:[transforms])
- 把一些变换实例以"list"形式组合在一起。
- torchvision.transforms.ToTensor()
- 把具有(L,LA,P,I,F,RGB,YCbCr,RGBA,CMYK,1)其中一种模式的"PIL Image"或值在[0,255]之间类型为np.uint8、大小为(HXWXC)的"numpy.ndarray"转成值在[0.0,1.0]之间类型为torch.FloatTensor、大小为(CXHXW)的"Tensor",如果为其他类型的输入,返回的Tensor将不会缩放为[0.0,1.0]。
- torchvision.transforms.Normalize(mean, std, inplace=False)
- 用均值、标准差对图像归一化,它不支持PIL图像类型。n个通道对应mean:(mean[1],…mean[n])和std:(std[1],…std[n]),此变换归一化输入"Tensor"的每一个通道。例如:output[channel] = (input[channel]-mean[channel]) / std[channel]。
- torchvision.transforms.Grayscale(num_output_channels=1)
- 把图像转化成灰度图,输入图像可以是PIL Image或Tensor类型,若输入图像是torch Tensor类型,大小应为[…,3,H,W],"…"代表任意数字。num_output_channels == 1,返回一通道图像;num_output_channels == 3,返回三通道图像且r == g == b。
- torchvision.transforms.RandomGrayscale(p=1)
- 基本用法同上,p指图像被转为灰度图的概率,1-p的概率不会转换。
为什么torch.nn.Module类里的forward函数被直接调用?
1). 根据一些资料理解,类对象里面若实现一个特殊方法"__ call __",则此类实例化后变成为可调用对象。而 “__call __” 函数里面调用了方法"forward",因此,nn.Module类实例可以直接调用"forward"函数。示例如下,example1(“a secret”)语句其实相当于example1. __ call __(“a secret”)。
class example:
def __init__(self, name):
self.name = name
def __call__(self, gender):
print('My name is %s' % self.name)
print('My gender is %s' % gender)
if __name__ == '__main__':
example1 = example("kitty")
example1("a secret")
输出为:
My name is kitty
My gender is a secret
" __ repr __ " 方法作用是什么?
2). 一般可以用来描述当前类、函数或者变量等。看下面两个例子~
class example:
def __init__(self, name):
self.name = name
def __call__(self, gender):
print('My name is %s' % self.name)
if __name__ == '__main__':
example1 = example("kitty")
print(example1)
输出为:<__main__.example object at 0x7fc6ee2a6ac8>
class example:
def __init__(self, name):
self.name = name
def __call__(self, gender):
print('My name is %s' % self.name)
def __repr__(self):
return 'example(%s)'%self.name
if __name__ == '__main__':
example1 = example("kitty")
print(example1)
输出为:example(kitty)
" __ item __ " 方法很重要,比如经常在torch.utils.data.Dataset中用来重写,如何理解它?
3). for循环为了兼容有两种机制,若对象有 __ iter __ 和 __ next __ 则会使用迭代器,若没有 __ iter __ 但实现了 __ getitem __ 则会改用下标迭代方式,默认从0开始依次读取相应下标。如下示例:
class NotIterable(object):
def __init__(self, baselist):
self.baselist = baselist
def __getitem__(self, item):
return self.baselist[item]
if __name__ == '__main__':
example = NotIterable([1,2,3])
for i in example:
print(i)
输出:
1
2
3
实现了__ iter __ 或 __ getitem __的对象都是可迭代对象,但只有同时实现 __ iter __和 __ next __的对象才是迭代器,可以按如下方式判断是否是可迭代对象和迭代器。(迭代器和生成器也是Dataloader里面用到的重要python语法,真正实战再来补充!)
class NotIterable(object):
def __init__(self, end):
self.start = 0
self.end = end
def __iter__(self):
return self
if __name__ == '__main__':
from collections.abc import *
a = NotIterable(5)
print(isinstance(a,Iterable))
print(isinstance(a,Iterator))
输出为:
True
False
了解了上述基础内容,就可以更好的明白为什么pytracking一气呵成这么大的工程了,里面用到的继承、其中的类以及对PyTorch中的类重写(子类对父类方法作一定修改)诸如一些__ call __、 __ getitem __等方法[重载(一个类里面方法名字相同,参数不同)],从而使得较多实例对象简洁串联处理数据,形成一条内部处理数据流。
import os
import torch
from torch import nn, optim
import torch.nn.functional as F
from torch.utils.data import DataLoader
from torchvision import datasets, transforms
class classifier(nn.Module):
def __init__(self):
super().__init__()
self.fc1 = nn.Linear(784,256)
self.fc2 = nn.Linear(256,128)
self.fc3 = nn.Linear(128,64)
self.fc4 = nn.Linear(64,10)
self.dropout = nn.Dropout(p=0.2)
def forward(self, x):
x = x.view(x.shape[0], -1)
x = self.dropout(F.relu(self.fc1(x)))
x = self.dropout(F.relu(self.fc2(x)))
x = self.dropout(F.relu(self.fc3(x)))
x = F.log_softmax(self.fc4(x), dim=1)
return x
#数据预处理,标准化图像数据,使得灰度数据在-1-+1之间
transform = transforms.Compose([transforms.ToTensor(), transforms.Normalize((0.5,),(0.5,))])
#下载训练数据集数据,构建训练数据集载入器trainloader,每次从训练集载入64张图片,每次载入打乱顺序
trainset_root = os.path.join(os.path.dirname(__file__),'dataset')
if not os.path.exists(trainset_root):
os.makedirs(trainset_root)
trainset = datasets.FashionMNIST(trainset_root, download=True, train=True, transform=transform)
trainloader = DataLoader(trainset,batch_size=64, shuffle=True)
#下载测试数据集数据,构建测试数据集载入器testloader,每次从训练集载入64张图片,每次载入打乱顺序
testset_root = os.path.join(os.path.dirname(__file__),'dataset')
testset = datasets.FashionMNIST(testset_root, download=True, train=False, transform=transform)
testloader = DataLoader(testset,batch_size=64, shuffle=True)
model = classifier().cuda()
criterion = nn.NLLLoss()
optimizer = optim.Adam(model.parameters(), lr=0.003)
epochs = 20
train_losses = []
test_losses = []
print('Start training!')
for e in range(epochs):
running_loss = 0
#训练一遍数据集
for images, labels in trainloader:
#将优化器的每次求导结果设为0,否则每次反向传播之后叠加之前的
optimizer.zero_grad()
log_ps = model(images.cuda())
loss = criterion(log_ps, labels.cuda())
loss.backward()
optimizer.step()
running_loss += loss.item()
#每次训练一遍数据集,都进行一次测试
test_loss = 0
accuracy = 0
#测试时不需要开自动求导和反向传播
with torch.no_grad():
model.eval()
for images, labels in testloader:
log_ps = model(images.cuda())
test_loss += criterion(log_ps, labels.cuda()).item()
ps = torch.exp(log_ps)
top_p, top_class = ps.topk(1, dim=1)
equals = top_class == labels.cuda().view(*top_class.shape)
accuracy += torch.mean(equals.type(torch.FloatTensor))
model.train()
train_losses.append(running_loss/len(trainloader))
test_losses.append(test_loss/len(testloader))
print("epoch: {}/{}.".format(e+1, epochs),
"train loss: {:.3f}.".format(running_loss/len(trainloader)),
"test loss: {:.3f}.".format(test_loss/len(testloader)),
"accuracy: {:.3f}".format(accuracy/len(testloader)))
简单的分类网络,功能没那么全面,但基本训练模型的思想步骤有了,搭配简单网络和复杂的pytracking总结复习了一下,有时间还是要多理解多写代码,为了梦想继续加油!