PyTorch-->image classification(图像分类)
使用深度学习框架的流程:
模型定义(包括损失函数的选择)-> 数据处理和加载 -> 训练(可能包含训练过程可视化)-> 测试
以下是根据官方教程的练手,其中卷积神经网络的部分会单独开一篇去写原理,目前俺还不太懂,哈哈哈哈!冲鸭!!!
1 # 使用torchvision来加载并归一化CIFAR10数据集 2 3 import torch 4 import torchvision # 保存了一些数据集 5 import torchvision.transforms as transforms # 进行数据预处理 6 import torch.nn as nn 7 import torch.nn.functional as F 8 import torch.optim as optim 9 10 from torch.autograd import Variable 11 12 13 # 定义网络一般继承torch.nn.Module创建新子类 14 class Net(nn.Module): 15 16 def __init__(self): 17 super(Net, self).__init__() 18 # 1 input image channel, 6 output channels, 5*5 square conwolution 19 # kernel 20 # 添加卷积层 21 self.conv1 = nn.Conv2d(3, 6, 5) # 定义一个输入深度为3,输出为6,卷积核大小为 5*5 的 conv1 变量 22 self.pool = nn.MaxPool2d(2, 2) # 最大池化层 23 self.conv2 = nn.Conv2d(6, 16, 5) # 输入通道数为6 输出通道数为16 24 # an affine operation: y = Wx + b 25 # 3个全连接层 26 self.fc1 = nn.Linear(16 * 5 * 5, 120) 27 self.fc2 = nn.Linear(120, 84) 28 self.fc3 = nn.Linear(84, 10) # 最终属于10类中的一个 29 30 # 定义前向传播的方法:即定义了使用给定的层和函数计算输出的方式 31 def forward(self, x): 32 # 定义forward()函数:可以在此函数中使用任何Tensor操作 33 # backward()函数被autograd自动定义 34 # Max pooling over a (2,2) window 35 # 输入x -> conv1 -> relu -> 2*2窗口的最大池化 36 # x = F.max_pool2d(F.relu(self.conv1(x)), (2, 2)) 37 # If the size is a square you can only specify a single number 38 # x = F.max_pool2d(F.relu(self.conv2(x)), 2) 39 # view 函数将张量x变形成一维向量形式,总特征数不变,为全连接层做准备 40 # 为什么这里只关心列数不关心行数呢,因为马上就要进入全连接层了,而全连接层说白了就是矩阵乘法! 41 # 第一个全连接层的首参数是16*5*5,所以要保证能够相乘,在矩阵乘法之前就要把x调到正确的size 42 x = self.pool(F.relu(self.conv1(x))) 43 x = self.pool(F.relu(self.conv2(x))) 44 x = x.view(-1, 16 * 5 * 5) 45 x = F.relu(self.fc1(x)) 46 x = F.relu(self.fc2(x)) 47 x = self.fc3(x) 48 return x 49 50 51 # torch.nn中大多数layer在torch.nn.funtional中都有一个与之对应的函数。 52 # 二者的区别在于: 53 # torch.nn.Module中实现layer的都是一个特殊的类,都是以class xx来定义的, 会自动提取可学习的参数 54 # 而 nn.functional中的函数,更像是纯函数,由def function( )定义,只进行简单的数学运算。 55 # 即二者的区别是functional中的函数是一个确定的不变的运算公式,输入数据产生输出就ok, 56 # 而深度学习中会有很多权重是在不断更新的,不可能每进行一次forward就用新的权重重新来定义一遍函数来进行计算,所以说就会采用类的方式,以确保能在参数发生变化时仍能使用我们之前定好的运算步骤。 57 # 从这个分析就可以看出什么时候应该用nn.Module中的layer了: 58 # 如果模型有可学习的参数,最好使用nn.Module对应的相关layer,否则二者都可以使用,没有什么区别。 59 # 比如此例中的Relu其实没有可学习的参数,只是进行一个运算而已,所以使用的就是functional中的relu函数, 60 # 而卷积层和全连接层都有可学习的参数,所以用的是nn.Module中的类。 61 # 不具备可学习参数的层,将它们用函数代替,这样可以不用放在构造函数中进行初始化。 62 63 64 # torchvision dataset 输出范围:[0,1]的PILImage,首先需要归一化为[-1,1]的Tensors 65 # define a transform :可以将多个变换组合在一起,此处为组合了totensor & normalize 66 transform = transforms.Compose( 67 [transforms.ToTensor(), 68 transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))]) # first RGB三个通道上的均值 second RGB三个通道上的标准差 用于对RGB图像归一化 69 # define a trainset,加载后使用上面定义的transform进行变换 (存放位置 为True代表创建的是训练集 为True代表需要从网上下载 使用的变换) 70 trainset = torchvision.datasets.CIFAR10(root='./data', train=True, download=True, transform=transform) 71 # 后面会通过loader将数据传入网络 72 # 组合数据集和采样器 在数据集上提供单进程或多进程的迭代器(数据的来源 每批次进入的数据数量 为True代表打乱数据顺序 默认为0代表在主进程中加载,此处为2代表选用用来加载数据的子进程个数) 73 # 有些时候使用固定RAM(通过pin_memory)加速RAM到GPU的传输 74 trainloader = torch.utils.data.DataLoader(trainset, batch_size=4, shuffle=True, num_workers=2) 75 # define a testset 76 testset = torchvision.datasets.CIFAR10(root='./data', train=False, download=True, transform=transform) 77 78 testloader = torch.utils.data.DataLoader(testset, batch_size=4, shuffle=False, num_workers=2) 79 # 类别需要给定 80 classes = ('plane', 'car', 'bird', 'cat', 'deer', 'dog', 'frog', 'horse', 'ship', 'truck') 81 82 # show some examples 83 import matplotlib.pyplot as plt 84 import numpy as np 85 86 87 # functions to show an image 88 89 def imshow(img): 90 img = img / 2 + 0.5 91 npimg = img.numpy() 92 plt.imshow(np.transpose(npimg, (1, 2, 0))) 93 plt.show() 94 95 96 if __name__ == '__main__': 97 # get some random training images 98 dataiter = iter(trainloader) 99 images, labels = dataiter.next() 100 101 # # show 102 # imshow(torchvision.utils.make_grid(images)) 103 # # print labes 104 # print(' '.join('%5s' % classes[labels[j]] for j in range(4))) 105 106 net = Net() 107 108 # use a Classification Cross-Entropy 分类交叉熵 loss(损失函数) and SGD 动量 with momentum(优化器) 109 # 想把模型搬到GPU上跑,要在定义优化器之前完成.cuda()这一步 因为在将网络参数传递给优化器之前,将它们传输到适当的设备是很重要的,否则优化器将无法以正确的方式跟踪它们 110 criterion = nn.CrossEntropyLoss() 111 optimizer = optim.SGD(net.parameters(), lr=0.001, momentum=0.9) 112 113 # loop over the dataset multiple times 将数据集循环传给net & momentum 114 # 下面进行两次训练 115 for epoch in range(2): 116 117 running_loss = 0.0 # 方便对损失函数的值进行输出 118 # enumerate() 既获得索引也获得数据 119 for i, data in enumerate(trainloader, 0): 120 # get the inputs 121 inputs, labels = data # 从enumerate返回的data 包含数据和标签信息 122 # inputs, labels = Variable(inputs),Variable(labels) 将数据转换成Variable 123 # don't forget!!! 124 optimizer.zero_grad() 125 126 # forward + backward + optimize 127 outputs = net(inputs) 128 loss = criterion(outputs, labels) 129 loss.backward() 130 # 在定义网络时定义了前向传播函数,但是并没有定义反向传播函数,深度学习是需要反向传播求导的, 131 # Pytorch其实利用的是Autograd模块来进行自动求导,反向传播。 132 # Autograd中最核心的类就是Variable了,它封装了Tensor,并几乎支持所有Tensor的操作,这里可以参考官方给的详细解释: 133 # http: // pytorch.org / tutorials / beginner / blitz / autograd_tutorial.html # sphx-glr-beginner-blitz-autograd-tutorial-py 134 # 以上链接详细讲述了variable究竟是怎么能够实现自动求导的,怎么用它来实现反向传播的。 135 # 这里涉及到计算图的相关概念 136 # 总结:要计算各个variable的梯度,只需调用根节点的backward方法,Autograd就会自动沿着整个计算图进行反向计算 137 # 而在此例子中,根节点就是loss,所以程序中的loss.backward() 138 # 代码就是在实现反向传播,自动计算所有的梯度。 139 optimizer.step() # 执行完反向传播之后,更新优化器参数,以便进行下一轮训练 140 141 # print statistics 142 running_loss += loss.item() 143 # print every 2000 mini-batches 144 if i % 2000 == 1999: 145 print('[%d, %5d] loss: %.3f' % (epoch + 1, i + 1, running_loss / 2000)) 146 running_loss = 0.0 147 148 print('Finished!!!') 149 150 # save the model 151 PATH = './cifar_net.pth' 152 torch.save(net.state_dict(), PATH) 153 154 # test the network on the test data 155 156 dataiter = iter(testloader) 157 images, labels = dataiter.next() # 根据设置应该是四张图片 158 159 # print images 160 imshow(torchvision.utils.make_grid(images)) 161 print('Ground_Truth: ', ' '.join('%5s' % classes[labels[j]] for j in range(4))) 162 163 net_1 = Net() 164 165 net_1.load_state_dict(torch.load(PATH)) 166 outputs = net_1(images) 167 _, predited = torch.max(outputs, 1) # 会返回两个值,但是对第一个不感兴趣 168 # .max() 返回输入Tensor中每行的最大值,并转换成指定的dim(维度) 169 # 指的 the dimension to reduce!并不是在the dimension上去返回最大值。 170 # 理解为:返回最大的索引,即预测出的类别 171 print('Predicted: ', ' '.join('%5s' % classes[predited[j]] for j in range(4))) 172 173 # performs on the whole dataset 174 175 correct = 0 176 total = 0 177 178 with torch.no_grad(): 179 for data in testloader: 180 images, labels = data 181 outputs = net_1(images) 182 _, predited = torch.max(outputs.data, 1) 183 total += labels.size(0) 184 correct += (predited == labels).sum().item() 185 186 print('Accuracy of the network on the 10000 test images: %d %%' % ( 187 100 * (correct / total))) 188 189 # performs on different classes 190 class_correct = list(0. for i in range(10)) 191 class_total = list(0. for i in range(10)) 192 with torch.no_grad(): 193 for data in testloader: 194 images, labels = data 195 outputs = net_1(images) 196 _, predited = torch.max(outputs, 1) 197 c = (predited == labels).squeeze() 198 for i in range(4): # 每个batch有4个图片! 199 label = labels[i] 200 class_correct[label] += c[i].item() 201 class_total[label] += 1 202 203 for i in range(10): 204 print('Accuracy of %5s : %2d %%' % (classes[i], 205 100 * (class_correct[i] / class_total[i]))) 206 207 # run the neural network on the GPU 208 device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu") 209 print(device) 210 211 net.to(device) 212 inputs, labels = data[0].to(device), data[0].to(device)
To see I can not see,
to know I do not know.