视频学习
深度学习的数学基础
-
自编码器变种
- 正则自编码器:使提取的特征表达符合某种性质——惩罚大权重
- 稀疏自编码器:提取稀疏特征表达——限制神经元平均激活度在一个很小的值
- 去噪自编码器:提取鲁棒特征表达
- 变分自编码器:数据生成、缺失数据填补、图像超高分辨率——基于隐层特征表达空间Z,通过解码曾,生成样本
-
数学基础
-
概率论(统计)——基础、信息论
-
线性代数
-
矩阵线性变换
- Ax=λx (x:特征向量、λ:特征值)
- 矩阵相乘对原始向量同时世家方向和尺度变化,而对于特征向量只有尺度变化,没有方向变化,变化系数就是特征值
-
秩
-
定义
- 度量矩阵行列之间的相关性,线性无关——满秩
- 表示数据需要的最小的基的数量
- 数据分布模式容易被捕捉——基少,秩小
- 数据冗余度越大——基少,秩小
- 矩阵表达的表达结构化信息相关性大——秩小
-
机器学习:数据降维
- 奇异值,较大奇异值包含了矩阵的主要信息
-
机器学习:低秩近似
- 矩阵低秩近似
- 低秩近似的意义:保留决定数据分布的最主要的模式/方向(丢弃噪声或其他不关键信息)
- 图像去噪
-
-
-
最优化、微积分
-
梯度下降是神经网络共同的基础
-
-
机器学习三要素
-
模型——对要学习问题映射的假设(问题建模,确定假设空间)
-
策略——从假设空间中学习/选择最优模型的准则(确定目标函数)
-
训练误差—>泛化误差(衡量两者差异:计算学习理论)
-
”最合适“的模型:机器学习从有限的观测数据中学习出规律,并将总结的规律推广应用到未观测样本上——追求泛化性能
-
机器学习目的是获得小的泛化误差
-
PAC给出实际训练学习器的目标:从合理数量的训练数据中通过合理计算量学习到可靠的知识
- 合理数量的训练数据:数据集大小
- 合理计算量:学习训练的时间
- 可靠的知识:概率的置信度,近似的误差上界
-
策略设计定理:
- 无免费午餐定理:在所有问题上的平均性能是,任意两个模型相同
- 奥卡姆剃刀原理:如无必要,勿增实体,即简单有效原理,选择同等程度符合一个问题观测结果模型中使用假设最少的->最简单的模型
-
欠拟合VS过拟合
- 欠拟合:训练集的一般性质尚未被学习器学好(训练误差大)
- 解决方案:提高模型复杂度
- 决策树:拓展分支
- 神经网络:增加训练轮数
- 解决方案:提高模型复杂度
- 过拟合:学习器把训练集特点当作样本的一般特点(训练误差小,测试误差大)
- 解决方案:降低模型复杂度
- 优化目标加正则项
- 决策树:剪枝
- 神经网路:early stop、dropout
- 数据增广(扩大训练集)
- 计算机视觉:图像旋转、缩放、剪切
- 自然语言处理:同义词替换
- 语音识别:添加随机噪声
- 解决方案:降低模型复杂度
- 欠拟合:训练集的一般性质尚未被学习器学好(训练误差大)
-
-
算法——根据目标函数求解最优模型的具体计算方法(求解模型参数)
-
概率/函数形式的统一
-
-
损失函数
- BP神经网络和损失函数
- 平方损失
- 交叉熵
-
频率学派VS贝叶斯学派
- 频率学派
- 关注可独立重复的随机实验中单个事件发生的频率
- 可能性:事件发生频率的极限值->重复实验次数趋近无穷大,事件发生的频率会收敛到真实的概率
- 假设概率是客观存在且固定的
- 模型参数是唯一的,需要从有限的观测数据中估计参数值
- 贝叶斯学派
- 关注随机事件的”可信程度“,如天气预报下雨概率
- 可能性=假设+数据:数据的作用是对初始假设做出修正,使观测折对概率的主观认识(先验)更接近客观实际(观测)
- 模型参数本身使随机变量,需要估计参数的整个概率分布
- 频率学派
-
概率学派VS机器学习方法
-
Beyond深度学习
-
因果推断
-
统计机器学习:寻找相关性
X—>Y
-
相关性不可靠:Yule-Simpson悖论
相关关系可能由于CONTEXT等混杂因素而改变
-
联结主义 vs 贝叶斯:相关性 vs 因果性
-
-
群体智能
- 验证码:
- ESP:图像标注
- Peekaboom:图像分割
卷积神经网络
-
-
卷积神经网络
-
全连接网络处理图像问题:权重矩阵参数太多导致过拟合
卷积神经网络解决方式:局部关联,参数共享
-
一个经典的卷积神经网络由卷积层、池化层、全连接层交叉堆叠而成
-
搭建神经网络结构
-
找到一个合适的损失函数
-
- 找到一个合适的优化函数
-
卷积
- 一维卷积
-
二维卷积
卷积是对两个实变函数的一种数学操作
在图像处理中,图像是以二维矩阵输入到神经网络的,因此需要二维卷积
y=WX+b(W:卷积核参数,X:卷积区域,b:偏置项)
-
基本概念
-
输入,input
-
卷积核/滤波器,kernel/filter
-
权重,weights
-
感受野,receptive field:卷积核进行卷积时对应的区域
-
特征图,activation map:一次卷积后的输出结果,大小:(N-F)/stride+1,
有padding时:(N+padding*2-F)/stride+1
-
padding:零填充
-
深度,depth/channel
-
输出:output
-
-
池化-Pooling Layer
- Pooling:
- 保留了主要特征的同时减少参数和计算量, 防止过拟合,提高模型泛化能力
- 一般处于卷积层与卷积层之间,全连接层与全连接层之间
- 类型:
- Max pooling:最大值池化——分类
- Average pooling:平均池化
- 概念
- filer,区域,一般2*2,3x3
- 步长stride,一般2
- Pooling:
-
全连接—Fully Connected Layer
- 两层之间所有神经元都有权重链接
- 通常全连接层再卷积神经网络尾部
- 全连接层参数量通常最大
-
AlexNet
- 模型结构
+ 成功原因:
+ 大数据训练:百万级ImageNet图像数据
+ 非线性激活函数:ReLU
+ 防止过拟合:Dropout,Data augmentation
- DropOut(随机失活):训练时随即关闭部分神经元,测试时整合所有神经元
- Data augmentation(数据增强):
+ 平移、翻转、对称
+ 随机crop。
+ 水平翻转,相当将样本倍增
+ 改变RGB通道强度:对RGB空间做一个高斯扰动。
+ 其他:双Gpu实现
+ 分层解析
+ 第一次卷积:卷积-ReLU-池化 224x224x3—55x55x96—27x27x96—27x27x96
+ 第二次卷积:卷积-ReLU-池化 27x27x96—27x27x256—13x13x256—13x13x256
+ 第三次卷积:卷积-ReLU 13x13x256—13x13x384—13x13x384
+ 第四次卷积:卷积-ReLU 13x13x384—13x13x384—13x13x384
+ 第五次卷积:卷积-ReLU-池化 13x13x384—13x13x256—13x13x256—6x6x256
+ 第六次卷积:全连接-ReLU-DropOut 6x6x256—4096—4096—4096
+ 第七次卷积:全连接-ReLU-DropOut 4096—4096—4096—4096
+ 第八次卷积:全连接-SoftMax 4096—1000
+ 参数数量
-
ZFNet
- 网络结构与AlexNet相同
- 将卷积层1中的感受野大小由11x11改为7x7,步长由4改为2
- 卷积层3,4,5中的滤波器个数由384,384,256改为512,512,1024
-
VGG—更深的网络
-
GoogleNet
- 网络总体结构:
- 网络包含22个带参数的层(考虑pooling层就是27层),独立成快的层总共约有100个
- 参数量大概是AlexNet的1/12
- 没有FC层
- Naive Inception
- 初衷:多卷积核增加特征多样性
- 计算复杂度过高
- Inception V2
- 解决思路:插入1x1卷积核进行降维
- Inception V3
- 进一步对V2的参数两进行降低,用小的卷积核替代打的卷积核
- 降低参数量
- 增加非线性激活函数:使网络产生更多独立特征,表征能力更强,训练更快
- 结构
- Stem部分(stem network):卷积-池化-卷积-卷积-池化
- 多个Inception结构堆叠
- 输出:没有额外的全连接层(除了最后的类别输出层)
- 辅助分类器:解决由于模型深度过深导致的梯度消失问题
- 网络总体结构:
-
ResNet
- 残差学习网络(deep residual leaning network)
- 残差的思想:去掉相同的主体部分,从而突出微小的变化,防止梯度消失
- 可以被用来训练非常深的网络
- 深度有152层
- 除了输出层没有其他全连接层
- 残差学习网络(deep residual leaning network)
-
二维卷积和三维卷积区别
二维卷积可以处理任何尺寸图像,三维卷积无视图像输入的通道数
pytorch代码讲解ResNet
-
ResNet解决网络层数加深导致网络退化
-
残差学习
CNN可以拟合任何一个函数
网络退化——H(x)难以训练,F(x)=H(x)-x可以实现,称为残差。
数学公式:
网络实现:
-
ResNet网络结构:
-
全局平均池化——替代全连接层——参数少,更少出现过拟合
-
50层上下的ResNet结构最大的区别
50层以上的block有三层:BottleNeck(入口1x1降维,出口1x1升回),50层以下两层,
- ResNeXt:分组卷积
- SENet——注意力机制
- ResNeXt-Attention
-
ResNeXt WSL
弱监督训练
-
分组卷积最早出现在AlexNet
代码练习
-
MNIST数据集分类
import torch import torch.nn as nn import torch.nn.functional as F import torch.optim as optim from torchvision import datasets, transforms import matplotlib.pyplot as plt import numpy # 一个函数,用来计算模型中有多少参数 def get_n_params(model): np=0 for p in list(model.parameters()): np += p.nelement() return np # 使用GPU训练,可以在菜单 "代码执行工具" -> "更改运行时类型" 里进行设置 device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu") #加载数据MNIST input_size = 28*28 # MNIST上的图像尺寸是 28x28 output_size = 10 # 类别为 0 到 9 的数字,因此为十类 train_loader = torch.utils.data.DataLoader( datasets.MNIST('./data', train=True, download=True, transform=transforms.Compose( [transforms.ToTensor(), transforms.Normalize((0.1307,), (0.3081,))])), batch_size=64, shuffle=True) test_loader = torch.utils.data.DataLoader( datasets.MNIST('./data', train=False, transform=transforms.Compose([ transforms.ToTensor(), transforms.Normalize((0.1307,), (0.3081,))])), batch_size=1000, shuffle=True)
#显示数据集中部分图像
plt.figure(figsize=(8, 5))
for i in range(20):
plt.subplot(4, 5, i + 1)
image, _ = train_loader.dataset.__getitem__(i)
plt.imshow(image.squeeze().numpy(),'gray')
plt.axis('off');
- 创建网络
class FC2Layer(nn.Module):
def __init__(self, input_size, n_hidden, output_size):
# nn.Module子类的函数必须在构造函数中执行父类的构造函数
# 下式等价于nn.Module.__init__(self)
super(FC2Layer, self).__init__()
self.input_size = input_size
# 这里直接用 Sequential 就定义了网络,注意要和下面 CNN 的代码区分开
self.network = nn.Sequential(
nn.Linear(input_size, n_hidden),
nn.ReLU(),
nn.Linear(n_hidden, n_hidden),
nn.ReLU(),
nn.Linear(n_hidden, output_size),
nn.LogSoftmax(dim=1)
)
def forward(self, x):
# view一般出现在model类的forward函数中,用于改变输入或输出的形状
# x.view(-1, self.input_size) 的意思是多维的数据展成二维
# 代码指定二维数据的列数为 input_size=784,行数 -1 表示我们不想算,电脑会自己计算对应的数字
# 在 DataLoader 部分,我们可以看到 batch_size 是64,所以得到 x 的行数是64
# 大家可以加一行代码:print(x.cpu().numpy().shape)
# 训练过程中,就会看到 (64, 784) 的输出,和我们的预期是一致的
# forward 函数的作用是,指定网络的运行过程,这个全连接网络可能看不啥意义,
# 下面的CNN网络可以看出 forward 的作用。
x = x.view(-1, self.input_size)
return self.network(x)
class CNN(nn.Module):
def __init__(self, input_size, n_feature, output_size):
# 执行父类的构造函数,所有的网络都要这么写
super(CNN, self).__init__()
# 下面是网络里典型结构的一些定义,一般就是卷积和全连接
# 池化、ReLU一类的不用在这里定义
self.n_feature = n_feature
self.conv1 = nn.Conv2d(in_channels=1, out_channels=n_feature, kernel_size=5)
self.conv2 = nn.Conv2d(n_feature, n_feature, kernel_size=5)
self.fc1 = nn.Linear(n_feature*4*4, 50)
self.fc2 = nn.Linear(50, 10)
# 下面的 forward 函数,定义了网络的结构,按照一定顺序,把上面构建的一些结构组织起来
# 意思就是,conv1, conv2 等等的,可以多次重用
def forward(self, x, verbose=False):
x = self.conv1(x)
x = F.relu(x)
x = F.max_pool2d(x, kernel_size=2)
x = self.conv2(x)
x = F.relu(x)
x = F.max_pool2d(x, kernel_size=2)
x = x.view(-1, self.n_feature*4*4)
x = self.fc1(x)
x = F.relu(x)
x = self.fc2(x)
x = F.log_softmax(x, dim=1)
return x
# 训练函数
def train(model):
model.train()
# 主里从train_loader里,64个样本一个batch为单位提取样本进行训练
for batch_idx, (data, target) in enumerate(train_loader):
# 把数据送到GPU中
data, target = data.to(device), target.to(device)
optimizer.zero_grad()
output = model(data)
loss = F.nll_loss(output, target)
loss.backward()
optimizer.step()
if batch_idx % 100 == 0:
print('Train: [{}/{} ({:.0f}%)]\tLoss: {:.6f}'.format(
batch_idx * len(data), len(train_loader.dataset),
100. * batch_idx / len(train_loader), loss.item()))
def test(model):
model.eval()
test_loss = 0
correct = 0
for data, target in test_loader:
# 把数据送到GPU中
data, target = data.to(device), target.to(device)
# 把数据送入模型,得到预测结果
output = model(data)
# 计算本次batch的损失,并加到 test_loss 中
test_loss += F.nll_loss(output, target, reduction='sum').item()
# get the index of the max log-probability,最后一层输出10个数,
# 值最大的那个即对应着分类结果,然后把分类结果保存在 pred 里
pred = output.data.max(1, keepdim=True)[1]
# 将 pred 与 target 相比,得到正确预测结果的数量,并加到 correct 中
# 这里需要注意一下 view_as ,意思是把 target 变成维度和 pred 一样的意思
correct += pred.eq(target.data.view_as(pred)).cpu().sum().item()
test_loss /= len(test_loader.dataset)
accuracy = 100. * correct / len(test_loader.dataset)
print('\nTest set: Average loss: {:.4f}, Accuracy: {}/{} ({:.0f}%)\n'.format(
test_loss, correct, len(test_loader.dataset),
accuracy))
- 在小型全连接网络上训练
n_hidden = 8 # number of hidden units
model_fnn = FC2Layer(input_size, n_hidden, output_size)
model_fnn.to(device)
optimizer = optim.SGD(model_fnn.parameters(), lr=0.01, momentum=0.5)
print('Number of parameters: {}'.format(get_n_params(model_fnn)))
train(model_fnn)
test(model_fnn)
- 在卷积神经网络上训练
# Training settings
n_features = 6 # number of feature maps
model_cnn = CNN(input_size, n_features, output_size)
model_cnn.to(device)
optimizer = optim.SGD(model_cnn.parameters(), lr=0.01, momentum=0.5)
print('Number of parameters: {}'.format(get_n_params(model_cnn)))
train(model_cnn)
test(model_cnn)
- 打乱像素顺序再次在两个网络上训练与测试
# 这里解释一下 torch.randperm 函数,给定参数n,返回一个从0到n-1的随机整数排列
perm = torch.randperm(784)
plt.figure(figsize=(8, 4))
for i in range(10):
image, _ = train_loader.dataset.__getitem__(i)
# permute pixels
image_perm = image.view(-1, 28*28).clone()
image_perm = image_perm[:, perm]
image_perm = image_perm.view(-1, 1, 28, 28)
plt.subplot(4, 5, i + 1)
plt.imshow(image.squeeze().numpy(), 'gray')
plt.axis('off')
plt.subplot(4, 5, i + 11)
plt.imshow(image_perm.squeeze().numpy(), 'gray')
plt.axis('off')
#全连接网络
perm = torch.randperm(784)
n_hidden = 8 # number of hidden units
model_fnn = FC2Layer(input_size, n_hidden, output_size)
model_fnn.to(device)
optimizer = optim.SGD(model_fnn.parameters(), lr=0.01, momentum=0.5)
print('Number of parameters: {}'.format(get_n_params(model_fnn)))
train_perm(model_fnn, perm)
test_perm(model_fnn, perm)
#卷积神经网络
perm = torch.randperm(784)
n_features = 6 # number of feature maps
model_cnn = CNN(input_size, n_features, output_size)
model_cnn.to(device)
optimizer = optim.SGD(model_cnn.parameters(), lr=0.01, momentum=0.5)
print('Number of parameters: {}'.format(get_n_params(model_cnn)))
train_perm(model_cnn, perm)
test_perm(model_cnn, perm)
-
CIFAR10数据集分类
import torch import torchvision import torchvision.transforms as transforms import matplotlib.pyplot as plt import numpy as np import torch.nn as nn import torch.nn.functional as F import torch.optim as optim # 使用GPU训练,可以在菜单 "代码执行工具" -> "更改运行时类型" 里进行设置 device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu") transform = transforms.Compose( [transforms.ToTensor(), transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))]) # 注意下面代码中:训练的 shuffle 是 True,测试的 shuffle 是 false # 训练时可以打乱顺序增加多样性,测试是没有必要 trainset = torchvision.datasets.CIFAR10(root='./data', train=True, download=True, transform=transform) trainloader = torch.utils.data.DataLoader(trainset, batch_size=64, shuffle=True, num_workers=2) testset = torchvision.datasets.CIFAR10(root='./data', train=False, download=True, transform=transform) testloader = torch.utils.data.DataLoader(testset, batch_size=8, shuffle=False, num_workers=2) classes = ('plane', 'car', 'bird', 'cat', 'deer', 'dog', 'frog', 'horse', 'ship', 'truck') def imshow(img): plt.figure(figsize=(8,8)) img = img / 2 + 0.5 # 转换到 [0,1] 之间 npimg = img.numpy() plt.imshow(np.transpose(npimg, (1, 2, 0))) plt.show() # 得到一组图像 images, labels = iter(trainloader).next() # 展示图像 imshow(torchvision.utils.make_grid(images)) # 展示第一行图像的标签 for j in range(8): print(classes[labels[j]])
class Net(nn.Module):
def __init__(self):
super(Net, self).__init__()
self.conv1 = nn.Conv2d(3, 6, 5)
self.pool = nn.MaxPool2d(2, 2)
self.conv2 = nn.Conv2d(6, 16, 5)
self.fc1 = nn.Linear(16 * 5 * 5, 120)
self.fc2 = nn.Linear(120, 84)
self.fc3 = nn.Linear(84, 10)
def forward(self, x):
x = self.pool(F.relu(self.conv1(x)))
x = self.pool(F.relu(self.conv2(x)))
x = x.view(-1, 16 * 5 * 5)
x = F.relu(self.fc1(x))
x = F.relu(self.fc2(x))
x = self.fc3(x)
return x
# 网络放到GPU上
net = Net().to(device)
criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(net.parameters(), lr=0.001)
for epoch in range(10): # 重复多轮训练
for i, (inputs, labels) in enumerate(trainloader):
inputs = inputs.to(device)
labels = labels.to(device)
# 优化器梯度归零
optimizer.zero_grad()
# 正向传播 + 反向传播 + 优化
outputs = net(inputs)
loss = criterion(outputs, labels)
loss.backward()
optimizer.step()
# 输出统计信息
if i % 100 == 0:
print('Epoch: %d Minibatch: %5d loss: %.3f' %(epoch + 1, i + 1, loss.item()))
print('Finished Training')
网络训练:
# 得到一组图像
images, labels = iter(testloader).next()
# 展示图像
imshow(torchvision.utils.make_grid(images))
# 展示图像的标签
for j in range(8):
print(classes[labels[j]])
correct = 0
total = 0
for data in testloader:
images, labels = data
images, labels = images.to(device), labels.to(device)
outputs = net(images)
_, predicted = torch.max(outputs.data, 1)
total += labels.size(0)
correct += (predicted == labels).sum().item()
print('Accuracy of the network on the 10000 test images: %d %%' % (
100 * correct / total))
测试验证准确率结果:
Accuracy of the network on the 10000 test images: 64 %
-
使用VGG16对CIFAR10分类
import torch import torchvision import torchvision.transforms as transforms import matplotlib.pyplot as plt import numpy as np import torch.nn as nn import torch.nn.functional as F import torch.optim as optim # 使用GPU训练,可以在菜单 "代码执行工具" -> "更改运行时类型" 里进行设置 device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu") transform_train = transforms.Compose([ transforms.RandomCrop(32, padding=4), transforms.RandomHorizontalFlip(), transforms.ToTensor(), transforms.Normalize((0.4914, 0.4822, 0.4465), (0.2023, 0.1994, 0.2010))]) transform_test = transforms.Compose([ transforms.ToTensor(), transforms.Normalize((0.4914, 0.4822, 0.4465), (0.2023, 0.1994, 0.2010))]) trainset = torchvision.datasets.CIFAR10(root='./data', train=True, download=True, transform=transform_train) testset = torchvision.datasets.CIFAR10(root='./data', train=False, download=True, transform=transform_test) trainloader = torch.utils.data.DataLoader(trainset, batch_size=128, shuffle=True, num_workers=2) testloader = torch.utils.data.DataLoader(testset, batch_size=128, shuffle=False, num_workers=2) classes = ('plane', 'car', 'bird', 'cat', 'deer', 'dog', 'frog', 'horse', 'ship', 'truck') class VGG(nn.Module): def __init__(self): super(VGG, self).__init__() self.cfg = [64, 'M', 128, 'M', 256, 256, 'M', 512, 512, 'M', 512, 512, 'M'] self.features = self._make_layers(self.cfg) #此处提示cfg未定义,添加self self.classifier = nn.Linear(512, 128) #由(2048, 10)修改 def forward(self, x): out = self.features(x) out = out.view(out.size(0), -1) out = self.classifier(out) return out def _make_layers(self, cfg): layers = [] in_channels = 3 for x in cfg: if x == 'M': layers += [nn.MaxPool2d(kernel_size=2, stride=2)] else: layers += [nn.Conv2d(in_channels, x, kernel_size=3, padding=1), nn.BatchNorm2d(x), nn.ReLU(inplace=True)] in_channels = x layers += [nn.AvgPool2d(kernel_size=1, stride=1)] return nn.Sequential(*layers) # 网络放到GPU上 net = VGG().to(device) criterion = nn.CrossEntropyLoss() optimizer = optim.Adam(net.parameters(), lr=0.001) for epoch in range(10): # 重复多轮训练 for i, (inputs, labels) in enumerate(trainloader): inputs = inputs.to(device) labels = labels.to(device) # 优化器梯度归零 optimizer.zero_grad() # 正向传播 + 反向传播 + 优化 outputs = net(inputs) #报错,前后匹配,修改VGG loss = criterion(outputs, labels) loss.backward() optimizer.step() # 输出统计信息 if i % 100 == 0: print('Epoch: %d Minibatch: %5d loss: %.3f' %(epoch + 1, i + 1, loss.item())) print('Finished Training')
网路训练:
测试验证准确率:
correct = 0
total = 0
for data in testloader:
images, labels = data
images, labels = images.to(device), labels.to(device)
outputs = net(images)
_, predicted = torch.max(outputs.data, 1)
total += labels.size(0)
correct += (predicted == labels).sum().item()
print('Accuracy of the network on the 10000 test images: %.2f %%' % (
100 * correct / total))
Accuracy of the network on the 10000 test images: 83.67 %
-
使用VGG模型迁移学习进行猫狗大战
import numpy as np import matplotlib.pyplot as plt import os import torch import torch.nn as nn import torchvision from torchvision import models,transforms,datasets import time import json import rarfile # 判断是否存在GPU设备 device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu") print('Using gpu: %s ' % torch.cuda.is_available())
Using gpu: True
# 下载AI研习社的数据集并解压 ! wget https://static.leiphone.com/cat_dog.rar path = "./cat_dog.rar" path2 = "./" rf = rarfile.RarFile(path) #待解压文件 rf.extractall(path2) #解压指定文件路径
修改数据集路径格式,如下所示:
#datasets 是 torchvision 中的一个包,可以用做加载图像数据。它可以以多线程(multi-thread)的形式从硬盘中读取数据,使用 mini-batch 的形式,在网络训练中向 GPU 输送。在使用CNN处理图像时,需要进行预处理。图片将被整理成 $224x 224 x3 的大小,同时还将进行归一化处理
normalize = transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])
vgg_format = transforms.Compose([
transforms.CenterCrop(224),
transforms.ToTensor(),
normalize,
])
data_dir = './cat_dog'
dsets = {x: datasets.ImageFolder(os.path.join(data_dir, x), vgg_format)
for x in ['train', 'val']}
dset_sizes = {x: len(dsets[x]) for x in ['train', 'val']}
dset_classes = dsets['train'].classes
tsets = {y: datasets.ImageFolder(os.path.join(data_dir, y), vgg_format)
for y in ['test']}
tset_sizes = {y: len(tsets[y]) for y in ['test']}
loader_train = torch.utils.data.DataLoader(dsets['train'], batch_size=64, shuffle=True, num_workers=6)
loader_valid = torch.utils.data.DataLoader(dsets['val'], batch_size=5, shuffle=False, num_workers=6)
loader_test=torch.utils.data.DataLoader(tsets['test'],batch_size=5,shuffle=False,num_workers=6)
'''
val 数据一共有2000张图,每个batch是5张,因此,下面进行遍历一共会输出到 400
同时,把第一个 batch 保存到 inputs_try, labels_try,分别查看
'''
count = 1
for data in loader_valid:
print(count, end='\n')
if count == 1:
inputs_try,labels_try = data
count +=1
# 显示图片的小程序
def imshow(inp, title=None):
# Imshow for Tensor.
inp = inp.numpy().transpose((1, 2, 0))
mean = np.array([0.485, 0.456, 0.406])
std = np.array([0.229, 0.224, 0.225])
inp = np.clip(std * inp + mean, 0,1)
plt.imshow(inp)
if title is not None:
plt.title(title)
plt.pause(0.001) # pause a bit so that plots are updated
# 显示 labels_try 的5张图片,即valid里第一个batch的5张图片
out = torchvision.utils.make_grid(inputs_try)
imshow(out, title=[dset_classes[x] for x in labels_try])
!wget https://s3.amazonaws.com/deep-learning-models/image-models/imagenet_class_index.json
model_vgg = models.vgg16(pretrained=True)
with open('./imagenet_class_index.json') as f:
class_dict = json.load(f)
dic_imagenet = [class_dict[str(i)][1] for i in range(len(class_dict))]
inputs_try , labels_try = inputs_try.to(device), labels_try.to(device)
model_vgg = model_vgg.to(device)
outputs_try = model_vgg(inputs_try)
print(outputs_try)
print(outputs_try.shape)
'''
可以看到结果为5行,1000列的数据,每一列代表对每一种目标识别的结果。
但是我也可以观察到,结果非常奇葩,有负数,有正数,
为了将VGG网络输出的结果转化为对每一类的预测概率,我们把结果输入到 Softmax 函数
'''
m_softm = nn.Softmax(dim=1)
probs = m_softm(outputs_try)
vals_try,pred_try = torch.max(probs,dim=1)
print( 'prob sum: ', torch.sum(probs,1))
print( 'vals_try: ', vals_try)
print( 'pred_try: ', pred_try)
print([dic_imagenet[i] for i in pred_try.data])
imshow(torchvision.utils.make_grid(inputs_try.data.cpu()),
title=[dset_classes[x] for x in labels_try.data.cpu()])
'''
第一步:创建损失函数和优化器
损失函数 NLLLoss() 的 输入 是一个对数概率向量和一个目标标签.
它不会为我们计算对数概率,适合最后一层是log_softmax()的网络.
'''
criterion = nn.NLLLoss()
# 学习率
lr = 0.001
# 随机梯度下降
optimizer_vgg = torch.optim.SGD(model_vgg_new.classifier[6].parameters(),lr = lr)
'''
第二步:训练模型
'''
def train_model(model,dataloader,size,epochs=1,optimizer=None):
model.train()
for epoch in range(epochs):
running_loss = 0.0
running_corrects = 0
count = 0
for inputs,classes in dataloader:
inputs = inputs.to(device)
classes = classes.to(device)
outputs = model(inputs)
loss = criterion(outputs,classes)
optimizer = optimizer
optimizer.zero_grad()
loss.backward()
optimizer.step()
_,preds = torch.max(outputs.data,1)
# statistics
running_loss += loss.data.item()
running_corrects += torch.sum(preds == classes.data)
count += len(inputs)
print('Training: No. ', count, ' process ... total: ', size)
epoch_loss = running_loss / size
epoch_acc = running_corrects.data.item() / size
print('Loss: {:.4f} Acc: {:.4f}'.format(
epoch_loss, epoch_acc))
# 模型训练
train_model(model_vgg_new,loader_train,size=dset_sizes['train'], epochs=1,
optimizer=optimizer_vgg)
'''
第三步:测试模型
'''
def test_model(model,dataloader,size):
model.eval()
predictions = np.zeros(size)
all_classes = np.zeros(size)
all_proba = np.zeros((size,2))
i = 0
running_loss = 0.0
running_corrects = 0
for inputs,classes in dataloader:
inputs = inputs.to(device)
classes = classes.to(device)
outputs = model(inputs)
loss = criterion(outputs,classes)
_,preds = torch.max(outputs.data,1)
# statistics
running_loss += loss.data.item()
running_corrects += torch.sum(preds == classes.data)
predictions[i:i+len(classes)] = preds.to('cpu').numpy()
all_classes[i:i+len(classes)] = classes.to('cpu').numpy()
all_proba[i:i+len(classes),:] = outputs.data.to('cpu').numpy()
i += len(classes)
print('Testing: No. ', i, ' process ... total: ', size)
epoch_loss = running_loss / size
epoch_acc = running_corrects.data.item() / size
print('Loss: {:.4f} Acc: {:.4f}'.format(
epoch_loss, epoch_acc))
return predictions, all_proba, all_classes
predictions, all_proba, all_classes test_model(model_vgg_new,loader_valid,size=dset_sizes['val'])
训练test数据集,将结果写入results.csv
import re
result = []
# 测试数据集,获到识别结果
for item,lable in loader_test:
item = item.to(device)
outputs = model_vgg_new(item)
_,pre = torch.max(outputs.data,1)
result += pre
result_end =list()
count1 = 0
for item in result:
string = tsets['test'].imgs[count1][0]
num = re.sub("\D", "", string)
result_end.append((num,item.tolist()))
count1 += 1
result_sort = sorted(result_end,key=lambda x:int(x[0]))
# 写入csv文件
import csv
f = open('results.csv','w')
writer = csv.writer(f)
for i in result_sort:
writer.writerow(i)
f.close()
提交AI研习社,查看结果评测