中企动力 > 头条 > python中models

网站性能检测评分

注:本网站页面html检测工具扫描网站中存在的基本问题,仅供参考。

python中models

在C ++中使用TensorFlow训练深度神经网络 推广视频课程

img

毕紫南

关注

你可能知道TensorFlow的核心是用C++构建的,然而只有python的API才能获得多种便利。

当我写上一篇文章时,目标是仅使用TensorFlow的C ++ API实现相同的DNN(深度神经网络),然后仅使用CuDNN。从我入手TensorFlow的C ++版本开始,我意识到即使对于简单DNN来说,也有很多东西被忽略了。

上一篇:https://matrices.io/deep-neural-network-from-scratch/

请记住,使用外部运算训练网络肯定是不可能的。你最可能面临的错误是缺少梯度运算。我目前正在将梯度运算从Python迁移到C ++。

在这个博客文章中,我们将建立一个深度神经网络,使用宝马车的车龄、公里数和发动机使用的燃料类型预测车的价格。我们将只在C ++中使用TensorFlow。目前在C ++中没有优化器,所以你会看到训练代码不那么好看,但是未来会添加优化器。

GitHub:https://github/theflofly/dnn_tensorflow_cpp

安装

我们将在TensorFlow C++ code中运行我们的C ++代码,我们可以尝试使用已编译的库,但是相信有些人会由于其环境的特殊性而遇到麻烦。从头开始构建TensorFlow会避免出现这些问题,并确保我们正在使用最新版本的API。

你需要安装bazel构建工具。

安装:https://docs.bazel.build/versions/master/install.html

在OSX上使用brew就可以了:

brewinstall bazel

我们将从TensorFlow源文件开始构建:

mkdir /path/tensorflow

cd /path/tensorflow

git clone https://github/tensorflow/tensorflow.git

然后你必须对安装进行配置,如选择是否启用GPU,你要运行以下配置脚本:

cd /path/tensorflow

./configure

现在我们创建接收我们模型的代码并首次构建TensorFlow的文件。请注意,第一次构建需要相当长的时间(10 – 15分钟)。

非核心的C ++ TensorFlow代码位于/tensorflow/cc中,这是我们创建模型文件的地方,我们还需要一个BUILD文件,以便bazel可以建立model.cc。

mkdir /path/tensorflow/model

cd /path/tensorflow/model

touchmodel.cc

touchBUILD

我们将bazel指令添加到BUILD文件中:

基本上它会使用model.cc建立一个模型二进制文件。我们现在准备编写我们的模型。

读取数据

这些数据是从法国网站leboncoin.fr中截取,然后清理和归一化并保存到CSV文件中。我们的目标是读取这些数据。用于归一化数据的元数据被保存到CSV文件的第一行,我们需要他们重新构建网络输出的价格。我创建了一个data_set.h和data_set.cc文件以保持代码清洁。他们从CSV文件中产生一个浮点型二维数组,馈送给我们的网络。我把代码粘贴在这里,但这无关紧要,你不需要花时间阅读。

data_set.h

data_set.cc

我们还必须在我们的bazel BUILD文件中添加这两个文件。

load("//tensorflow:tensorflow.bzl", "tf_cc_binary")

tf_cc_binary(

name = "model",

srcs = [

"model.cc",

"data_set.h",

"data_set.cc"

],

deps = [

"//tensorflow/cc:gradients",

"//tensorflow/cc:grad_ops",

"//tensorflow/cc:cc_ops",

"//tensorflow/cc:client_session",

"//tensorflow/core:tensorflow"

],

)

建立模型

第一步是读取CSV文件加入两个张量:x表示输入,y表示预期的结果。我们使用之前定义的DataSet类。访问下方链接下载CSV数据集。

要定义一个张量,我们需要它的类型和形状。在data_set对象中,x数据以平坦(flat)的方式保存,所以我们要将尺寸缩减成3(每辆车有3个特征)。然后,我们正在使用std::copy_n将数据从data_set对象复制到张量(Eigen::TensorMap)的底层数据结构。我们现在将数据作为TensorFlow数据结构,开始构建模型。

你可以使用以下方法调试张量:

LOG(INFO)

C ++ API的独特之处在于,你将需要一个Scope对象来保存图形构造的状态,并将该对象传递给每个操作。

Scope scope = Scope::NewRootScope;

我们将有两个占位符,x包含汽车的特征和y表示每辆车相应的价格。

auto x = Placeholder(scope, DT_FLOAT);

auto y = Placeholder(scope, DT_FLOAT);

我们的网络有两个隐藏层,因此我们将有三个权重矩阵和三个偏置矩阵。而在Python中,它是在底层完成的,在C++中你必须定义一个变量,然后定义一个Assign节点,以便为该变量分配一个默认值。我们使用RandomNormal来初始化我们的变量,这将给我们一个正态分布的随机值。

然后我们使用Tanh作为激活函数来构建我们的三个层。

添加L2正则化。

最后,我们计算损失,我们的预测和实际价格之间y的差异,并且将正则化加入损失。

至此,我们完成了前向传播,并准备做反向传播部分。第一步是使用一个函数调用将前向操作的梯度添加到图中。

所有操作必须计算关于每个变量被添加到图中的损失的梯度,关于,我们初始化一个空的grad_outputs向量,它会TensorFlow会话使用时填充了为变量提供梯度的节点,grad_outputs[0]会给我们关于w1, grad_outputs[1]损失的梯度和关于w2的损失梯度,它顺序为{w1, w2, w3, b1, b2, b3},变量的顺序传递给AddSymbolicGradients。

现在我们在grad_outputs中有一个节点列表。当在TensorFlow会话中使用时,每个节点计算一个变量的损失梯度。我们用它来更新变量。我们将为每个变量设置一行,在这里我们使用最简单的梯度下降进行更新。

Cast操作实际上是学习速率参数,在我们的例子中为0.01。

我们的网络已准备好在会话中启动,Python中的OptimizersAPI的最小化函数基本上封装了在函数调用中计算和应用梯度。这就是我在PR#11377中所做的。

PR#11377:https://github/tensorflow/tensorflow/pull/11377

我们初始化一个ClientSession和一个名为outputs的张量向量,它将接收我们网络的输出。

然后我们初始化我们的变量,在python中调用tf.global_variables_initializer就可以了,因为在构建图的过程中我们保留了所有变量的列表。在C ++中,我们必须列出变量。每个RandomNormal输出将被分配给Assign节点中定义的变量。

在这一点上,我们可以按训练步骤的数量循环。在本例中,我们做5000步。首先使用loss节点运行前向传播部分,输出网络的损失。每隔100步记录一次损失值,减少损失是活动网络的强制性属性。然后我们必须计算我们的梯度节点并更新变量。我们的梯度节点被用作ApplyGradientDescent节点的输入,所以运行我们的apply_节点将首先计算梯度,然后将其应用于正确的变量。

到这里,我们的网络训练完成,可以试着预测(或者说推理)一辆车的价格。我们尝试预测一辆使用7年的宝马1系车的价格,这辆车是柴油发动机里程为11万公里。我们运行我们的layer_3节点吧汽车数据输入x,它本质上是一个前向传播步骤。因为我们已经训练过网络5000步,所以权重有一个学习值,所产生的结果不会是随机的。我们不能直接使用汽车属性,因为我们的网络从归一化的属性中学习的,它们必须经过相同的归一化化过程。DataSet类有一个input方法,使用CSV读取期间加载的数据集的元数据来处理该步骤。

我们的网络产生一个介于0和1之间的值,data_set的output方法还会使用数据集元数据将该值转换为可读的价格。该模型可以使用命令bazel run -c opt //tensorflow/cc/models:model运行,如果最近编译了TensorFlow,你会很快看到如下输出:

它展示了汽车预计价格13377.7欧元。每次运行模型都会得到不同的结果,有时差异很大(8000—17000)。这是由于我们只用三个属性来描述汽车,而我们的网络架构也相对简单。

教程 | 从头开始了解PyTorch的简单实现 行业视频课程

img

谢傲丝

关注

本教程展示了如何从了解张量开始到使用 PyTorch 训练简单的神经网络,是非常基础的 PyTorch 入门资源。PyTorch 建立在 Python 和 Torch 库之上,并提供了一种类似 Numpy 的抽象方法来表征张量(或多维数组),它还能利用 GPU 来提升性能。本教程的代码并不完整,详情请查看原 Jupyter Notebook 文档。

PyTorch 使入门深度学习变得简单,即使你这方面的背景知识不太充足。至少,知道多层神经网络模型可视为由权重连接的节点图就是有帮助的,你可以基于前向和反向传播,利用优化过程(如梯度计算)从数据中估计权重。

必备知识:该教程假设读者熟悉 Python 和 NumPy。必备软件:在运行原 Jupyter Notebook 之前你需要安装 PyTorch。原 Notebook 有代码单元格可供验证你是否做好准备。必备硬件:你需要安装 NVIDIA GPU 和 CUDA SDK。据报告此举可能实现 10-100 的加速。当然,如果你没有进行此设置,那么你仍然可以在仅使用 CPU 的情况下运行 PyTorch。但是,记住,在训练神经网络模型时,生命苦短!所以还是尽可能使用 GPU 吧!

项目地址:https://github/hpcgarage/accelerated_dl_pytorch

1. 必要的 PyTorch 背景

PyTorch 是一个建立在 Torch 库之上的 Python 包,旨在加速深度学习应用。PyTorch 提供一种类似 NumPy 的抽象方法来表征张量(或多维数组),它可以利用 GPU 来加速训练。

1.1 PyTorch 张量

PyTorch 的关键数据结构是张量,即多维数组。其功能与 NumPy 的 ndarray 对象类似,如下我们可以使用 torch.Tensor() 创建张量。

# Generate a 2-D pytorch tensor (i.e., a matrix)pytorch_tensor = torch.Tensor(10, 20)print("type: ", type(pytorch_tensor), " and size: ", pytorch_tensor.shape )

如果你需要一个兼容 NumPy 的表征,或者你想从现有的 NumPy 对象中创建一个 PyTorch 张量,那么就很简单了。

# Convert the pytorch tensor to a numpy array:numpy_tensor = pytorch_tensor.numpy()print("type: ", type(numpy_tensor), " and size: ", numpy_tensor.shape) # Convert the numpy array to Pytorch Tensor:print("type: ", type(torch.Tensor(numpy_tensor)), " and size: ", torch.Tensor(numpy_tensor).shape)

1.2 PyTorch vs. NumPy

PyTorch 并不是 NumPy 的简单替代品,但它实现了很多 NumPy 功能。其中有一个不便之处是其命名规则,有时候它和 NumPy 的命名方法相当不同。我们来举几个例子说明其中的区别:

1 张量创建

t = torch.rand(2, 4, 3, 5)

a = np.random.rand(2, 4, 3, 5)

2 张量分割

a = t.numpy()pytorch_slice = t[0, 1:3, :, 4]numpy_slice = a[0, 1:3, :, 4]print ('Tensor[0, 1:3, :, 4]:\\n', pytorch_slice)print ('NdArray[0, 1:3, :, 4]:\\n', numpy_slice)-------------------------------------------------------------------------Tensor[0, 1:3, :, 4]: 0.2032 0.1594 0.3114 0.9073 0.6497 0.2826[torch.FloatTensor of size 2x3] NdArray[0, 1:3, :, 4]: [[ 0.20322084 0.15935552 0.31143939] [ 0.90726137 0.64966112 0.28259504]]

3 张量

Maskingt = t - 0.5pytorch_masked = t[t > 0]numpy_masked = a[a >0]

4 张量重塑

pytorch_reshape = t.view([6, 5, 4])numpy_reshape = a.reshape([6, 5, 4])

1.3 PyTorch 变量

PyTorch 张量的简单封装帮助建立计算图Autograd(自动微分库)的必要部分将关于这些变量的梯度保存在 .grad 中

结构图:

计算图和变量:在 PyTorch 中,神经网络会使用相互连接的变量作为计算图来表示。PyTorch 允许通过代码构建计算图来构建网络模型;之后 PyTorch 会简化估计模型权重的流程,例如通过自动计算梯度的方式。

举例来说,假设我们想构建两层模型,那么首先要为输入和输出创建张量变量。我们可以将 PyTorch Tensor 包装进 Variable 对象中:

from torch.autograd import Variableimport torch.nn.functional as Fx = Variable(torch.randn(4, 1), requires_grad=False)y = Variable(torch.randn(3, 1), requires_grad=False)

我们把 requires_grad 设置为 True,表明我们想要自动计算梯度,这将用于反向传播中以优化权重。

现在我们来定义权重:

w1 = Variable(torch.randn(5, 4), requires_grad=True)w2 = Variable(torch.randn(3, 5), requires_grad=True)

训练模型:

def model_forward(x):return F.sigmoid(w2 @ F.sigmoid(w1 @ x)) print (w1)print (w1.data.shape)print (w1.grad) # Initially, non-existentVariable containing: 1.6068 -1.3304 -0.6717 -0.6097-0.3414 -0.5062 -0.2533 1.0260-0.0341 -1.2144 -1.5983 -0.1392-0.5473 0.0084 0.4054 0.0970 0.3596 0.5987 -0.0324 0.6116[torch.FloatTensor of size 5x4] torch.Size([5, 4])None

1.4 PyTorch 反向传播

这样我们有了输入和目标、模型权重,那么是时候训练模型了。我们需要三个组件:

损失函数:描述我们模型的预测距离目标还有多远;

import torch.nn as nncriterion = nn.MSELoss()

优化算法:用于更新权重;

import torch.optim as optimoptimizer = optim.SGD([w1, w2], lr=0.001)

反向传播步骤:

for epoch in range(10):loss = criterion(model_forward(x), y) optimizer.zero_grad() # Zero-out previous gradients loss.backward() # Compute new gradients optimizer.step() # Apply these gradients 1.6067 -1.3303 -0.6717 -0.6095-0.3414 -0.5062 -0.2533 1.0259-0.0340 -1.2145 -1.5983 -0.1396-0.5476 0.0085 0.4055 0.0976 0.3597 0.5986 -0.0324 0.6113

1.5 PyTorch CUDA 接口

PyTorch 的优势之一是为张量和 autograd 库提供 CUDA 接口。使用 CUDA GPU,你不仅可以加速神经网络训练和推断,还可以加速任何映射至 PyTorch 张量的工作负载。

你可以调用 torch.cuda.is_available() 函数,检查 PyTorch 中是否有可用 CUDA。

cuda_gpu = torch.cuda.is_available()if (cuda_gpu):print("Great, you have a GPU!")else: print("Life is short -- consider a GPU!")

很好,现在你有 GPU 了。

.cuda()

之后,使用 cuda 加速代码就和调用一样简单。如果你在张量上调用 .cuda(),则它将执行从 CPU 到 CUDA GPU 的数据迁移。如果你在模型上调用 .cuda(),则它不仅将所有内部储存移到 GPU,还将整个计算图映射至 GPU。

要想将张量或模型复制回 CPU,比如想和 NumPy 交互,你可以调用 .cpu()。

if cuda_gpu:x = x.cuda() print(type(x.data)) x = x.cpu()print(type(x.data))

我们来定义两个函数(训练函数和测试函数)来使用我们的模型执行训练和推断任务。该代码同样来自 PyTorch 官方教程,我们摘选了所有训练/推断的必要步骤。

对于训练和测试网络,我们需要执行一系列动作,这些动作可直接映射至 PyTorch 代码:

1. 我们将模型转换到训练/推断模式;

2. 我们通过在数据集上成批获取图像,以迭代训练模型;

3. 对于每一个批量的图像,我们都要加载数据和标注,运行网络的前向步骤来获取模型输出;

4. 我们定义损失函数,计算每一个批量的模型输出和目标之间的损失;

5. 训练时,我们初始化梯度为零,使用上一步定义的优化器和反向传播,来计算所有与损失有关的层级梯度;

6. 训练时,我们执行权重更新步骤。

def train(model, epoch, criterion, optimizer, data_loader):model.train() for batch_idx, (data, target) in enumerate(data_loader): if cuda_gpu: data, target = data.cuda(), target.cuda() model.cuda() data, target = Variable(data), Variable(target) output = model(data) optimizer.zero_grad() loss = criterion(output, target) loss.backward() optimizer.step() if (batch_idx+1) % 400 == 0: print('Train Epoch: {} [{}/{} ({:.0f}%)]\\tLoss: {:.6f}'.format( epoch, (batch_idx+1) * len(data), len(data_loader.dataset), 100. * (batch_idx+1) / len(data_loader), loss.data[0])) def test(model, epoch, criterion, data_loader): model.eval() test_loss = 0 correct = 0 for data, target in data_loader: test_loss += criterion(output, target).data[0] pred = output.data.max(1)[1] # get the index of the max log-probability correct += pred.eq(target.data).cpu().sum() test_loss /= len(data_loader) # loss function already averages over batch size acc = correct / len(data_loader.dataset) print('\\nTest set: Average loss: {:.4f}, Accuracy: {}/{} ({:.0f}%)\\n'.format( test_loss, correct, len(data_loader.dataset), 100. * acc)) return (acc, test_loss)

现在介绍完毕,让我们开始这次数据科学之旅吧!

2. 使用 PyTorch 进行数据分析

使用 torch.nn 库构建模型

使用 torch.autograd 库训练模型

将数据封装进 torch.utils.data.Dataset 库

使用 NumPy interface 连接你的模型、数据和你最喜欢的工具

在查看复杂模型之前,我们先来看个简单的:简单合成数据集上的线性回归,我们可以使用 sklearn 工具生成这样的合成数据集。

from sklearn.datasets import make_regressionimport seaborn as snsimport pandas as pdimport matplotlib.pyplot as pltsns.set() x_train, y_train, W_target = make_regression(n_samples=100, n_features=1, noise=10, coef = True) df = pd.DataFrame(data = {'X':x_train.ravel(), 'Y':y_train.ravel()}) sns.lmplot(x='X', y='Y', data=df, fit_reg=True)plt.show() x_torch = torch.FloatTensor(x_train)y_torch = torch.FloatTensor(y_train)y_torch = y_torch.view(y_torch.size()[0], 1)

PyTorch 的 nn 库中有大量有用的模块,其中一个就是线性模块。如名字所示,它对输入执行线性变换,即线性回归。

class LinearRegression(torch.nn.Module):def __init__(self, input_size, output_size): super(LinearRegression, self).__init__() self.linear = torch.nn.Linear(input_size, output_size) def forward(self, x): return self.linear(x) model = LinearRegression(1, 1)

要训练线性回归,我们需要从 nn 库中添加合适的损失函数。对于线性回归,我们将使用 MSELoss()——均方差损失函数。

我们还需要使用优化函数(SGD),并运行与之前示例类似的反向传播。本质上,我们重复上文定义的 train() 函数中的步骤。不能直接使用该函数的原因是我们实现它的目的是分类而不是回归,以及我们使用交叉熵损失和最大元素的索引作为模型预测。而对于线性回归,我们使用线性层的输出作为预测。

criterion = torch.nn.MSELoss()optimizer = torch.optim.SGD(model.parameters(), lr=0.1)for epoch in range(50): data, target = Variable(x_torch), Variable(y_torch) output = model(data) optimizer.zero_grad() loss = criterion(output, target) loss.backward() optimizer.step() predicted = model(Variable(x_torch)).data.numpy()

现在我们可以打印出原始数据和适合 PyTorch 的线性回归。

plt.plot(x_train, y_train, 'o', label='Original data')plt.plot(x_train, predicted, label='Fitted line')plt.legend()plt.show()

为了转向更复杂的模型,我们下载了 MNIST 数据集至「datasets」文件夹中,并测试一些 PyTorch 中可用的初始预处理。PyTorch 具备数据加载器和处理器,可用于不同的数据集。数据集下载好后,你可以随时使用。你还可以将数据包装进 PyTorch 张量,创建自己的数据加载器类别。

批大小(batch size)是机器学习中的术语,指一次迭代中使用的训练样本数量。批大小可以是以下三种之一:

batch 模式:批大小等于整个数据集,因此迭代和 epoch 值一致;

mini-batch 模式:批大小大于 1 但小于整个数据集的大小。通常,数量可以是能被整个数据集整除的值。

随机模式:批大小等于 1。因此梯度和神经网络参数在每个样本之后都要更新。

from torchvision import datasets, transformsbatch_num_size = 64 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=batch_num_size, shuffle=True) test_loader = torch.utils.data.DataLoader( datasets.MNIST('data',train=False, transform=transforms.Compose([

3. PyTorch 中的 LeNet 卷积神经网络(CNN)

现在我们从头开始创建第一个简单神经网络。该网络要执行图像分类,识别 MNIST 数据集中的手写数字。这是一个四层的卷积神经网络(CNN),一种分析 MNIST 数据集的常见架构。该代码来自 PyTorch 官方教程,你可以在这里(http://pytorch.org/tutorials/)找到更多示例。

我们将使用 torch.nn 库中的多个模块:

1. 线性层:使用层的权重对输入张量执行线性变换;

2. Conv1 和 Conv2:卷积层,每个层输出在卷积核(小尺寸的权重张量)和同样尺寸输入区域之间的点积;

3. Relu:修正线性单元函数,使用逐元素的激活函数 max(0,x);

4. 池化层:使用 max 运算执行特定区域的下采样(通常 2x2 像素);

5. Dropout2D:随机将输入张量的所有通道设为零。当特征图具备强相关时,dropout2D 提升特征图之间的独立性;

6. Softmax:将 Log(Softmax(x)) 函数应用到 n 维输入张量,以使输出在 0 到 1 之间。

class LeNet(nn.Module):def __init__(self): super(LeNet,self).__init__() self.conv1 = nn.Conv2d(1,10,kernel_size=5) self.conv2 = nn.Conv2d(10,20,kernel_size=5) self.conv2_drop = nn.Dropout2d() self.fc1 = nn.Linear(320,50) self.fc2 = nn.Linear(50,10) def forward(self,x): x = F.relu(F.max_pool2d(self.conv1(x),2)) x = F.relu(F.max_pool2d(self.conv2_drop(self.conv2(x)),2)) x = x.view(-1, 320) x = F.relu(self.fc1(x)) x = F.dropout(x, training=self.training) x = self.fc2(x) return F.log_softmax(x, dim=1)

创建 LeNet 类后,创建对象并移至 GPU:

model = LeNet() model.cuda() print ('MNIST_net model:\\n')print (model)MNIST_net model: LeNet( (conv1): Conv2d(1, 10, kernel_size=(5, 5), stride=(1, 1)) (conv2): Conv2d(10, 20, kernel_size=(5, 5), stride=(1, 1)) (conv2_drop): Dropout2d(p=0.5) (fc1): Linear(in_features=320, out_features=50, bias=True) (fc2): Linear(in_features=50, out_features=10, bias=True))

要训练该模型,我们需要使用带动量的 SGD,学习率为 0.01,momentum 为 0.5。

criterion = nn.CrossEntropyLoss()optimizer = optim.SGD(model.parameters(),lr = 0.005, momentum = 0.9)

仅仅需要 5 个 epoch(一个 epoch 意味着你使用整个训练数据集来更新训练模型...

解读Keras在ImageNet中应用:详解5种图像识别模型 流量视频课程

img

青寒

关注

更多深度文章,请关注:https://yq.aliyun/cloud

几个月前,我写了一篇关于如何使用CNN(卷积神经网络)尤其是VGG16来分类图像的教程,该模型能够以很高的精确度识别我们日常生活中的1000种不同种类的物品。

那时,模型还是和Keras包分开的,我们得从free-standingGitHubrepo上下载并手动安装;现在模型已经整合进Keras包,原先的教程也已经不再适用,所以我决定写一篇新的教程。

在教程中,你将学习到如何编写一个Python脚本来分类你自己的图像。

博客结构

1.简要说明一下这几个网络架构;

2.使用Python编写代码:载入训练好的模型并对输入图像分类;

3.审查一些样本图片的分类结果。

Keras中最新的深度学习图像分类器

Keras提供了五种开箱即用型的CNN:

1.VGG16

2.VGG19

3.ResNet50

4.InceptionV3

5.Xception

什么是ImageNet

ImageNet曾是一个计算机视觉研究项目:(人工)打标签并分类成22000个不同物品种类。然而,当我们在讨论深度学习和CNN的时候,“ImageNet”意味着ImageNetLargeScaleVisualRecognitionChallenge,简写为ILSVRC。

ILSVRC的目的是训练一个能够正确识别图像并分类(1000种)的模型:模型使用约120万张图像用作训练,5万张图像用作验证,10万张图像用作测试。

这1000种分类涵盖了我们的日常生活接触到的东西,具体列表请点击。

在图像分类上,ImageNet竞赛已经是计算机视觉分类算法事实上的评价标准——而自2012年以来,排行榜就被CNN和其它深度学习技术所统治。

过去几年中ImageNet竞赛里表现优异的模型在Keras中均有收录。通过迁移学习,这些模型在ImageNet外的数据集中也有着不错的表现。

VGG16和VGG19

图1:VGG网络架构(source)

VGG网络架构于2014年出现在Simonyan和Zisserman中的论文中,《VeryDeepConvolutionalNetworksforLargeScaleImageRecognition》。

该架构仅仅使用堆放在彼此顶部、深度不断增加的3×3卷积层,并通过maxpooling来减小volume规格;然后是两个4096节点的全连接层,最后是一个softmax分类器。“16”和“19”代表网络中权重层的数量(表2中的D和E列):

在2014年的时候,16还有19层网络还是相当深的,Simonyan和Zisserman发现训练VGG16和VGG19很有难度,于是选择先训练小一些的版本(列A和列C)。这些小的网络收敛后被用来作为初始条件训练更大更深的网络——这个过程被称为预训练(pre-training)。

预训练很有意义,但是消耗大量时间、枯燥无味,在整个网络都被训练完成前无法进行下一步工作。

如今大部分情况下,我们已经不再使用预训练,转而采用Xaiver/Glorot初始化或者MSRA初始化(有时也被称作Heetal.初始化,详见《DelvingDeepintoRectifiers:SurpassingHuman-LevelPerformanceonImageNetClassification》)。如果你感兴趣,可以从这篇文章中理解到weightinitialization的重要性以及深度神经网络的收敛——《Allyouneedisagoodinit,MishkinandMatas(2015)》。

VGGNet有两个不足:

1.训练很慢;

2.weights很大。

由于深度以及全连接节点数量的原因,VGG16的weights超过533MB,VGG19超过574MB,这使得部署VGG很令人讨厌。虽然在许多深度学习图像分类问题中我们仍使用VGG架构,但是小规模的网络架构更受欢迎(比如SqueezeNet,GoogleNet等等)。

ResNet

与AlexNet、OverFeat还有VGG这些传统顺序型网络架构不同,ResNet的网络结构依赖于微架构模组(micro-architecturemodules,也被称为network-in-networkarchitectures)。

微架构模组指构成网络架构的“积木”,一系列的微架构积木(连同你的标准CONV,POOL等)共同构成了大的架构(即最终的网络)。

ResNet于2015年出现在Heetal的论文《DeepResidualLearningforImageRecognition》中,它的出现很有开创性意义,证明极深的网络也可以通过标准SGD(以及一个合理的初始化函数)来训练:

图3:Heetal.于2015年提出的残差模组

在2016年的著作《IdentityMappingsinDeepResidualNetworks》中,他们证实了可以通过更新残差模组(residualmodule)来使用标志映射(identifymappings),达到提高精度的目的。

图4:(左)原始残差模组(右)使用预激活(pre-activation)更新的残差模组

尽管ResNet比VGG16还有VGG19要深,weights却要小(102MB),因为使用了全局平均池化(globalaveragepooling),而不是全连接层。

InceptionV3

“Inception”微架构于2014年出现在Szegedy的论文中,《GoingDeeperwithConvolutions》。

图5:GoogleNet中使用的Inception模组原型

Inception模组的目的是扮演一个“多级特征提取器”,在网络相同的模组内计算1×1、3×3还有5×5的卷积——这些过滤器的输出在输入至网络下一层之前先被堆栈到channeldimension。

该架构的原型被称为GoogleNet,后继者被简单的命名为InceptionvN,N代表Google推出的数字。

Keras中的InceptionV3架构来自于Szegedyetal.的后续论文,《RethinkingtheInceptionArchitectureforComputerVision(2015)》,该论文打算通过更新inception模组来提高ImageNet分类的准确度。

InceptionV3比VGG还有ResNet都要小,约96MB。

Xception

图6:Xception架构

Xception是被FranoisChollet提出的,后者是Keras库的作者和主要维护者。

Xception是Inception架构的扩展,用depthwise独立卷积代替Inception标准卷积。

关于Xception的出版物《DeepLearningwithDepthwiseSeparableConvolutions》可以在这里找到。

Xception最小仅有91MB。

SqueezeNet

Figure7:“fire”模组,由一个“squeeze”和一个“expand”模组组成。(Iandolaetal.,2016)

仅仅4.9MB的SqueezeNet架构能达到AlexNet级别的精确度(~57%rank-1and~80%rank-5),这都归功于“fire”模组的使用。然而SqueezeNet的训练很麻烦,我会在即将出版的书——《DeepLearningforComputerVisionwithPython》——中介绍如何训练SqueezeNet来处理ImageNet数据集。

使用Python和Keras通过VGGNet,ResNet,Inception和Xception对图像分类

新建一个文件,命名为classify_image.py,编辑插入下列代码1#importthenecessarypackages2fromkeras.applicationsimportResNet503fromkeras.applicationsimportInceptionV34fromkeras.applicationsimportXception#TensorFlowONLY5fromkeras.applicationsimportVGG166fromkeras.applicationsimportVGG197fromkeras.applicationsimportimagenet_utils8fromkeras.applications.inception_v3importpreprocess_input9fromkeras.preprocessing.imageimportimg_to_array10fromkeras.preprocessing.imageimportload_img11importnumpyasnp12importargparse13importcv2

第2-13行导入需要的包,其中大部分都属于Keras。

第2-6行分别导入ResNet,InceptionV3,Xception,VGG16,还有VGG19——注意Xception只兼容TensorFlow后端。

第7行导入的image_utils包包含了一系列函数,使得对图片进行前处理以及对分类结果解码更加容易。

余下的语句导入其它有用的函数,其中NumPy用于数学运算,cv2用于与OpenCV结合。15#constructtheargumentparseandparsethearguments16ap=argparse.ArgumentParser()17ap.add_argument("-i","--image",required=True,18help="pathtotheinputimage")19ap.add_argument("-model","--model",type=str,default="vgg16",20help="nameofpre-trainednetworktouse")21args=vars(ap.parse_args())

--image为希望进行分类的图像的路径。

--model为选用的CNN的类别,默认为VGG16。23#defineadictionarythatmapsmodelnamestotheirclasses24#insideKeras25MODELS={26"vgg16":VGG16,27"vgg19":VGG19,28"inception":InceptionV3,29"xception":Xception,#TensorFlowONLY30"resnet":ResNet5031}3233#esnureavalidmodelnamewassuppliedviacommandlineargument34ifargs["model"]notinMODELS.keys():35raiseAssertionError("The--modelcommandlineargumentshould"36"beakeyinthe`MODELS`dictionary")

第25-31行定义了一个词典,将类映射到对应的模型名称。

如果没有在该词典中找到“--model”,就会报错。

输入一个图像到一个CNN中会返回一系列键值,包含标签及对应的概率。

ImageNet采用的图像尺寸一般为224×224,227×227,256×256,and299×299,但是并不是绝对。

VGG16,VGG19以及ResNet接受224×224的输入图像,而InceptionV3和Xception要求为299×299,如下代码所示:38#initializetheinputimageshape(224x224pixels)alongwith39#thepre-processingfunction(thismightneedtobechanged40#basedonwhichmodelweusetoclassifyourimage)41inputShape=(224,224)42preprocess=imagenet_utils.preprocess_input4344#ifweareusingtheInceptionV3orXceptionnetworks,thenwe45#needtosettheinputshapeto(299x299)[ratherthan(224x224)]46#anduseadifferentimageprocessingfunction47ifargs["model"]in("inception","xception"):48inputShape=(299,299)49preprocess=preprocess_input

这里我们初始化inputShape为224×224像素,初始化预处理函数为keras.preprocess_input——执行meansubtraction运算。

如果使用Inception或者Xception,inputShape需要改为299×299像素,预处理函数改为separatepre-processing函数。

下一步就是从磁盘载入网络架构的weights,并实例化模型:51#loadourthenetworkweightsfromdisk(NOTE:ifthisisthe52#firsttimeyouarerunningthisscriptforagivennetwork,the53#weightswillneedtobedownloadedfirst--dependingonwhich54#networkyouareusing,theweightscanbe90-575MB,sobe55#patient;theweightswillbecachedandsubsequentrunsofthis56#scriptwillbe*much*faster)57print("[INFO]loading{}...".format(args["model"]))58Network=MODELS[args["model"]]59model=Network(weights="imagenet")

注意:VGG16和VGG19的weights大于500MB,ResNet的约等于100MB,Inception和Xception的介于90-100MB之间。如果这是你第一次运行某个网络,这些weights会自动下载到你的磁盘。下载时间由你的网络速度决定,而且下载完成后,下一次运行代码不再需要重新下载。61#loadtheinputimageusingtheKerashelperutilitywhileensuring62#theimageisresizedto`inputShape`,therequiredinputdimensions63#fortheImageNetpre-trainednetwork64print("[INFO]loadingandpre-processingimage...")65image=load_img(args["image"],target_size=inputShape)66image=img_to_array(image)6768#ourinputimageisnowrepresentedasaNumPyarrayofshape69#(inputShape[0],inputShape[1],3)howeverweneedtoexpandthe70#dimensionbymakingtheshape(1,inputShape[0],inputShape[1],3)71#sowecanpassitthroughthenetwork72image=np.expand_dims(image,axis=0)7374#pre-processtheimageusingtheappropriatefunctionbasedonthe75#modelthathasbeenloaded(i.e.,meansubtraction,scaling,etc.)76image=preprocess(image)

第65行从磁盘载入输入图像,并使用提供的inputShape初始化图像的尺寸。

第66行将图像从PIL/Pillow实例转换成NumPy矩阵,矩阵的shape为(inputShape[0],inputShape[1],3)。

因为我们往往使用CNN来批量训练/分类图像,所以需要使用np.expand_dims在矩阵中添加一个额外的维度,如第72行所示;添加后矩阵shape为(1,inputShape[0],inputShape[1],3)。如果你忘记添加这个维度,当你的模型使用.predict时会报错。

最后,第76行使用合适的预处理函数来执行meansubtraction/scaling。

下面将我们的图像传递给网络并获取分类结果:78#classifytheimage79print("[INFO]classifyingimagewith'{}'...".format(args["model"]))80preds=model.predict(image)81P=imagenet_utils.decode_predictions(preds)8283#loopoverthepredictionsanddisplaytherank-5predictions+84#probabilitiestoourterminal85for(i,(imagenetID,label,prob))inenumerate(P[0]):86print("{}.{}:{:.2f}%".format(i+1,label,prob*100))

第80行调用.predict函数,并从CNN返回预测值。

第81行的.decode_predictions函数将预测值解码为易读的键值对:标签、以及该标签的概率。

第85行和86行返回最可能的5个预测值并输出到终端。

案例的最后一件事,是通过OpenCV从磁盘将输入图像读取出来,在图像上画出最可能的预测值并显示在我们的屏幕上。88#loadtheimageviaOpenCV,drawthetoppredictionontheimage,89#anddisplaytheimagetoourscreen90orig=cv2.imread(args["image"])91(imagenetID,label,prob)=P[0][0]92cv2.putText(orig,"Label:{},{:.2f}%".format(label,prob*100),93(10,30),cv2.FONT_HERSHEY_SIMPLEX,0.8,(0,0,255),2)94cv2.imshow("Classification",orig)95cv2.waitKey(0)

VGGNet,ResNet,Inception,和Xception的分类结果

所有的例子都是使用2.0以上版本的Keras以及TensorFlow后台做的。确保你的TensorFlow版本大于等于1.0,否则会报错。所有例子也都使用Theano后端做过测试,工作良好。

案例需要的图片以及代码请前往原文获取。

使用VGG16分类:1$pythonclassify_image.py--imageimages/soccer_ball.jpg--modelvgg16

图8:使用VGG16来分类足球(source)

输出为:soccer_ball,精确度为93.43%。

如果要使用VGG19,只需要替换下--network参数。1$pythonclassify_image.py--imageimages/bmw.png--modelvgg19

图9:使用VGG19来分类汽车(source)

输出为:convertible(敞篷车),精确度为91.76%。然而,我们看一下其它的4个结果:sportscar(跑车),4.98%(也对);limousine(豪华轿车),1.06%(不正确,但也合理);carwheel(车轮),0.75%(技术上正确,因为图中确实出现了轮子)。

从下面的例子,我们可以看到类似的结果:1$pythonclassify_image.py--imageimages/clint_eastwood.jpg--modelresnet

图10:使用ResNet分类(source).

ResNet成功将图像分类为revolver(左轮手枪),精确度69.79%。有趣的是rifle(步枪)为7.74%,assaultrifle(突击步枪)为5.63%。考虑到revolver的观察角度还有相对于手枪来说巨大的枪管,CNN得出这么高的概率也是合理的。1$pythonclassify_image.py--imageimages/jemma.png--modelresnet

图11:使用ResNet对狗进行分类

狗的种类被正确识别为beagle(小猎兔狗),精确度94.48%。

然后我试着分类《加勒比海盗》中的图片:1$pythonclassify_image.py--imageimages/boat.png--modelinception

图12:使用ResNet对沉船进行分类(source)

尽管ImageNet中有“boat”(船)这个类别,Inception网络仍然正确地将该场景识别为“(ship)wreck”(沉船),精确度96.29%。其它的标签,比如“seashore”(海滩),“canoe”(独木舟),“paddle”(桨),还有“breakwater”(...

终于!Keras官方中文版文档正式发布了 推广视频课程

img

仇亚男

关注

今年 1 月 12 日,Keras 作者 Franois Chollet 在推特上表示因为中文读者的广泛关注,他已经在 GitHub 上展开了一个 Keras 中文文档项目。而昨日,Franois Chollet 再一次在推特上表示 Keras 官方文档已经基本完成!他非常感谢翻译和校对人员两个多月的不懈努力,也希望 Keras 中文使用者能继续帮助提升文档质量。

这一次发布的是 Keras 官方中文文档,它得到了严谨的校对而提升了整体质量。但该项目还在进行中,虽然目前已经上线了很多 API 文档和使用教程,但仍然有一部分内容没有完成。其实早在官方中文文档出现以前,就有开发者构建了 Keras 的中文文档,而且很多读者都在使用 MoyanZitto 等人构建的中文文档。

Keras 官方文档:https://keras.io/zh/

Keras 第三方文档:http://keras-cn.readthedocs.io/en/latest/

以下我们将简要介绍这次官方发布的 Keras 文档。

Keras 是一个用 Python 编写的高级神经网络 API,它能够以 TensorFlow、CNTK、或者 Theano 作为后端运行。Keras 的开发重点是支持快速的实验。能够以最小的时延把你的想法转换为实验结果,是做好研究的关键。

如果你有如下需求,请选择 Keras:

允许简单而快速的原型设计(用户友好,高度模块化,可扩展性)。

同时支持卷积神经网络和循环神经网络,以及两者的组合。

在 CPU 和 GPU 上无缝运行与切换。

Keras 兼容的 Python 版本: Python 2.7-3.6。

Keras 相对于其它深度学习库非常容易构建:首先它提供一致和简单的 API;其次,它提供独立的、完全可配置的模块构成序列或图表以完成模型;最后,作为新的类和函数,新的模块很容易扩展。这样说可能比较抽象,但正如文档中所描述的,我们甚至在 30 秒就能快速上手 Keras。所以在坑外徘徊或准备入坑 Keras 的小伙伴可以开心地开始你们的 30 秒。

快速开始:30 秒上手 Keras

Keras 的核心数据结构是 model,一种组织网络层的方式。最简单的模型是 Sequential 模型,它是由多网络层线性堆叠的栈。对于更复杂的结构,你应该使用 Keras 函数式 API,它允许构建任意的神经网络图。

Sequential 模型如下所示:

from keras.models import Sequentialmodel = Sequential()

可以简单地使用 .add() 来堆叠模型:

from keras.layers import Densemodel.add(Dense(units=64, activation='relu', input_dim=100))model.add(Dense(units=10, activation='softmax'))

在完成了模型的构建后, 可以使用 pile() 来配置学习过程:

modelpile(loss='categorical_crossentropy',optimizer='sgd', metrics=['accuracy'])

如果需要,你还可以进一步地配置优化器。Keras 的一个核心原则是使事情变得相当简单,同时又允许用户在需要的时候能够进行完全的控制(终极的控制是源代码的易扩展性)。

modelpile(loss=keras.losses.categorical_crossentropy,optimizer=keras.optimizers.SGD(lr=0.01, momentum=0.9, nesterov=True))

现在,你可以批量地在训练数据上进行迭代了:

# x_train and y_train are Numpy arrays --just like in the Scikit-Learn API.model.fit(x_train, y_train, epochs=5, batch_size=32)

或者,你可以手动地将批次的数据提供给模型:

model.train_on_batch(x_batch, y_batch)

只需一行代码就能评估模型性能:

loss_and_metrics = model.evaluate(x_test, y_test, batch_size=128)

或者对新的数据生成预测:

classes = model.predict(x_test, batch_size=128)

构建一个问答系统,一个图像分类模型,一个神经图灵机,或者其他的任何模型,就是这么的快。深度学习背后的思想很简单,那么它们的实现又何必要那么痛苦呢?

使用简介

Keras 模型的使用一般可以分为顺序模型(Sequential)和 Keras 函数式 API,顺序模型是多个网络层的线性堆叠,而 Keras 函数式 API 是定义复杂模型(如多输出模型、有向无环图,或具有共享层的模型)的方法。以下将简要介绍两种模型的使用方法:

1.Keras 顺序模型

你可以通过将层的列表传递给 Sequential 的构造函数,来创建一个 Sequential 模型:

from keras.models import Sequentialfrom keras.layers import Dense, Activationmodel = Sequential([Dense(32, input_shape=(784,)),Activation('relu'),Dense(10),Activation('softmax'),])

也可以使用 .add() 方法将各层添加到模型中:

model = Sequential()model.add(Dense(32, input_dim=784))model.add(Activation('relu'))

如下展示了一个完整的模型,即基于多层感知器 (MLP) 的 softmax 多分类:

import kerasfrom keras.models import Sequentialfrom keras.layers import Dense, Dropout, Activationfrom keras.optimizers import SGD# 生成虚拟数据import numpy as npx_train = np.random.random((1000, 20))y_train = keras.utils.to_categorical(np.random.randint(10, size=(1000, 1)), num_classes=10)x_test = np.random.random((100, 20))y_test = keras.utils.to_categorical(np.random.randint(10, size=(100, 1)), num_classes=10)model = Sequential()# Dense(64) 是一个具有 64 个隐藏神经元的全连接层。# 在第一层必须指定所期望的输入数据尺寸,在这里是一个 20 维的向量。model.add(Dense(64, activation='relu', input_dim=20))model.add(Dropout(0.5))model.add(Dense(64, activation='relu'))model.add(Dropout(0.5))model.add(Dense(10, activation='softmax'))sgd = SGD(lr=0.01, decay=1e-6, momentum=0.9, nesterov=True)modelpile(loss='categorical_crossentropy',optimizer=sgd,metrics=['accuracy'])model.fit(x_train, y_train,epochs=20,batch_size=128)score = model.evaluate(x_test, y_test, batch_size=128)

2. Keras 函数式 API

利用函数式 API,可以轻易地重用训练好的模型:可以将任何模型看作是一个层,然后通过传递一个张量来调用它。注意,在调用模型时,您不仅重用模型的结构,还重用了它的权重。

以下是函数式 API 的一个很好的例子:具有多个输入和输出的模型。函数式 API 使处理大量交织的数据流变得容易。

来考虑下面的模型。我们试图预测 Twitter 上的一条新闻标题有多少转发和点赞数。模型的主要输入将是新闻标题本身,即一系列词语,但是为了增添趣味,我们的模型还添加了其他的辅助输入来接收额外的数据,例如新闻标题的发布的时间等。该模型也将通过两个损失函数进行监督学习。较早地在模型中使用主损失函数,是深度学习模型的一个良好正则方法。

模型结构如下图所示:

让我们用函数式 API 来实现它(详细解释请查看中文文档):

from keras.layers import Input, Embedding, LSTM, Densefrom keras.models import Model# 标题输入:接收一个含有 100 个整数的序列,每个整数在 1 到 10000 之间。# 注意我们可以通过传递一个 `name` 参数来命名任何层。main_input = Input(shape=(100,), dtype='int32', name='main_input')# Embedding 层将输入序列编码为一个稠密向量的序列,每个向量维度为 512。x = Embedding(output_dim=512, input_dim=10000, input_length=100)(main_input)# LSTM 层把向量序列转换成单个向量,它包含整个序列的上下文信息lstm_out = LSTM(32)(x)auxiliary_output = Dense(1, activation='sigmoid', name='aux_output')(lstm_out)auxiliary_input = Input(shape=(5,), name='aux_input')x = keras.layers.concatenate([lstm_out, auxiliary_input])# 堆叠多个全连接网络层x = Dense(64, activation='relu')(x)x = Dense(64, activation='relu')(x)x = Dense(64, activation='relu')(x)# 最后添加主要的逻辑回归层main_output = Dense(1, activation='sigmoid', name='main_output')(x)model = Model(inputs=[main_input, auxiliary_input], outputs=[main_output, auxiliary_output])modelpile(optimizer='rmsprop', loss='binary_crossentropy',loss_weights=[1., 0.2])model.fit([headline_data, additional_data], [labels, labels],epochs=50, batch_size=32)modelpile(optimizer='rmsprop',loss={'main_output': 'binary_crossentropy', 'aux_output': 'binary_crossentropy'},loss_weights={'main_output': 1., 'aux_output': 0.2})# 然后使用以下方式训练:model.fit({'main_input': headline_data, 'aux_input': additional_data},{'main_output': labels, 'aux_output': labels},epochs=50, batch_size=32)

以上只是一个简单的案例,Keras 函数式 API 还有非常多的应用案例,包括层级共享、有向无环图和残差网络等顶尖视觉模型,读者可以继续阅读中文文档了解更多

文档的后一部分更多是描述 Keras 中常用的函数与 API,包括 Keras 模型、层级函数、预处理过程、损失函数、最优化方法、数据集和可视化等。这些 API 和对应实现的功能其实很多时候可以在实际使用的时候再查找,当然最基本的 API 我们还是需要了解的。以下将简要介绍 Keras 模型和层级 API,其它的模块请查阅原中文文档。

Keras 模型

在 Keras 中有两类模型,顺序模型 和 使用函数式 API 的 Model 类模型。这些模型有许多共同的方法:

model.summary(): 打印出模型概述信息。它是 utils.print_summary 的简捷调用。model.get_config(): 返回包含模型配置信息的字典。通过以下代码,就可以根据这些配置信息重新实例化模型:

config = model.get_config()model = Model.from_config(config)# or, for Sequential:model = Sequential.from_config(config)

model.get_weights(): 返回模型权重的张量列表,类型为 Numpy array。model.set_weights(weights): 从 Nympy array 中为模型设置权重。列表中的数组必须与 get_weights() 返回的权重具有相同的尺寸。model.to_json(): 以 JSON 字符串的形式返回模型的表示。请注意,该表示不包括权重,只包含结构。你可以通过以下代码,从 JSON 字符串中重新实例化相同的模型(带有重新初始化的权重):

from keras.models import model_from_jsonjson_string = model.to_json()model = model_from_json(json_string)

model.to_yaml(): 以 YAML 字符串的形式返回模型的表示。请注意,该表示不包括权重,只包含结构。你可以通过以下代码,从 YAML 字符串中重新实例化相同的模型(带有重新初始化的权重):

from keras.models import model_from_yamlyaml_string = model.to_yaml()model = model_from_yaml(yaml_string)

model.save_weights(filepath): 将模型权重存储为 HDF5 文件。model.load_weights(filepath, by_name=False): 从 HDF5 文件(由 save_weights 创建)中加载权重。默认情况下,模型的结构应该是不变的。如果想将权重载入不同的模型(部分层相同),设置 by_name=True 来载入那些名字相同的层的权重。

Keras 层级

所有 Keras 层都有很多共同的函数:

layer.get_weights(): 以 Numpy 矩阵的形式返回层的权重。layer.set_weights(weights): 从 Numpy 矩阵中设置层的权重(与 get_weights 的输出形状相同)。layer.get_config(): 返回包含层配置的字典。此图层可以通过以下方式重置:

layer = Dense(32)config = layer.get_config()reconstructed_layer = Dense.from_config(config)

如果一个层具有单个节点 (i.e. 如果它不是共享层), 你可以得到它的输入张量,输出张量,输入尺寸和输出尺寸:

layer.inputlayer.outputlayer.input_shapelayer.output_shape

如果层有多个节点,您可以使用以下函数:

layer.get_input_at(node_index)layer.get_output_at(node_index)layer.get_input_shape_at(node_index)layer.get_output_shape_at(node_index)

这些是 Keras 模型与层级基本的函数,文档的中心内容也是这一部分和下面描述的 API 用途与参数,它包括完整模型所需要的各个模块,包括数据、预处理、网络架构、训练、评估和可视化等。但这一部分我们并不会介绍,因为很多时候我们只有在遇到未知的函数时才会详细查阅。

Keras 官方中文文档,欢迎各位徘徊者入坑

教程:利用Tensorflow目标检测API确定图像中目标的位置 互联网视频课程

img

真朋友

关注

深度学习提供了另一种解决“Wally在哪儿”(美国漫画)问题的方法。与传统的图像处理计算机视觉方法不同的是,它只使用了少量的标记出Wally位置的示例。

在我的Github repo上发布了具有评估图像和检测脚本的最终训练模型。

Github repo地址:https://github/tadejmagajna/HereIsWally

这篇文章描述了使用Tensorflow目标检测API来训练神经网络的过程,并使用围绕它构建的Python脚本来寻找Wally。它由以下步骤组成:

通过创建一组标记训练图像来准备数据集,其中标签代表图像中Wally的xy位置;

读取和配置模型以使用Tensorflow目标检测API;

在我们的数据集上训练模型;

使用导出的图形对评估图像的模型进行测试。

开始之前,请确保按照说明安装Tensorflow目标检测API。

准备数据集

神经网络是深度学习的过程中最值得注意的过程,但遗憾的是,科学家们花费大量时间的准备和格式化训练数据。

最简单的机器学习问题的目标值通常是标量(比如数字检测器)或分类字符串。Tensorflow目标检测API训练数据使用两者的结合。它包括一组图像,并附有特定目标的标签和它们在图像中出现的位置。位置用两点(二维空间)定义,两点足够画一个物体周围的包围盒。

因此,为了创建训练集,我们需要提出一组Wally出现地点的图片。

虽然我可以用LabelImg这样的注释工具,花费数周的时间通过手工标记图像来解决问题,但我发现了一个已经解决了Where’s Wally这个问题的训练集。

Wally训练数据集,最后四列描述了Wally出现在图像中的位置

准备数据集的最后一步是将我们的标签(保存为文本文件)和图像(.jpeg)打包成一个二进制.tfrecord文件(该过程的解释代码地址见段末),但可以找到训练和重新运算求出Wally位置的参数内容。 .tfecord文件在我的Github repo上。

Github repo地址:https://github/tadejmagajna/HereIsWally

解释地址:http://warmspringwinds.github.io/tensorflow/tf-slim/2016/12/21/tfrecords-guide/

准备模型

Tensorflow目标检测API提供了一组经过多次公开数据集训练的具有不同性能(通常为速度 – 精度折衷)的预训练模型。

虽然模型可以从头开始随机初始化网络权值,但这个过程可能需要几周的时间。我们使用一种称为转移学习的方法来替换该过程。

转移学习包含采用通常训练的模型解决一些一般问题并且重新训练模型以解决我们的问题。转移学习的工作原理是,通过使用在预先训练的模型中获得的知识并将其转移到新的模型中,来代替从头开始训练模型这些无用的重复工作。这为我们节省了大量的时间,将花费在训练上的时间用于获得针对我们问题的知识。

我们使用带有经过COCO数据集训练的Inception v2模型的RCNN,以及它的管道配置文件。该模型包含一个检查点.ckpt文件,我们可以使用该文件开始训练。

RCNN地址:http://download.tensorflow.org/models/object_detection/faster_rcnn_inception_v2_coco_2017_11_08.tar.gz

管道配置文件地址:https://github/tensorflow/models/blob/master/research/object_detection/samples/configs/ssd_inception_v2_coco.config

下载配置文件后,请确保用指向检查点文件、训练以及评估.tfrecord文件与标签映射文件的路径代替“PATH_TO_BE_CONFIGURED”字段。

需要配置的最终文件是labels.txt映射文件,其中包含所有不同目标的标签。由于我们只是在寻找一种类型的目标,我们的标签文件看起来像这样:

item {id: 1name: 'waldo'}

最后,我们最终应该:

具有.ckpt检查点文件的预训练模型;

训练和评估.tfrecord数据集;

标记映射文件;

指向以上文件的管道配置文件。

现在,我们准备开始训练。

训练

Tensorflow目标检测API提供了一个简单易用的Python脚本来重新训练我们的模型。它位于models / research / object_detection中,可以利用下列路径运行:

python train.py –logtostderr –pipeline_config_path= PATH_TO_PIPELINE_CONFIG –train_dir=PATH_TO_TRAIN_DIR

其中PATH_TO_PIPELINE_CONFIG是到管道配置文件的路径,PATH_TO_TRAIN_DIR是一个新创建的目录,我们的新检查点和模型将被存储在该目录中。

train.py的输出应该如下所示:

用最重要的信息来查找损失。这是在训练或验证集中每个示例错误的总和。当然,你希望它尽可能低,这意味着,缓慢下降表示你的模型正在学习(或过度拟合你的训练数据)。

你还可以使用Tensorboard来更详细地显示训练数据。

该脚本将在一定数量的步骤后自动存储检查点文件,以便你随时恢复保存的检查点,以防计算机在学习过程中崩溃。

这意味着当你想结束模型的训练时,你可以终止脚本。

但是什么时候停止学习?关于何时停止训练,原则上是当评估集的损失减少或非常低时(在我们的例子中低于0.01)。

测试

现在我们可以通过在一些示例图像上进行测试来实际使用我们的模型。

首先,我们需要使用models/research/object_detection脚本中存储的检查点(位于我们的训练目录中)导出推理图:

python export_inference_graph.py — pipeline_config_path PATH_TO_PIPELINE_CONFIG --trained_checkpoint_prefix PATH_TO_CHECPOINT --output_directory OUTPUT_PATH

我们的Python脚本可以用导出的推理图来查找Wally的位置。

我写了一些简单的Python脚本(基于Tensorflow 目标检测API),你可以在模型上使用它们执行目标检测,并在检测到的目标周围绘制框或将其暴露。

find_wally.py和find_wally_pretty.py都可以在我的Github仓库中找到,可以简单地运行:

find_wally.py地址:https://github/tadejmagajna/HereIsWally/blob/master/find_wally.py

find_wally_pretty.py地址:https://github/tadejmagajna/HereIsWally/blob/master/find_wally_pretty.py

Github repo 地址:https://github/tadejmagajna/HereIsWally

python find_wally.py

或者

python find_wally_pretty.py

在自己的模型或自己的评估图像上使用脚本时,请确保修改model_path和image_path变量。

结语

在我的Github repo 上发布的模型表现非常出色。

模型设法在评估图像中找到Wally,并且对网络上的一些额外的随机例子处理得很好。它未能找到很大的Wally,直观来说,找到小的walley应该更容易解决。这表明我们的模型可能过度适合我们的训练数据,主要是因为训练图像较少。

本文为编译文章,转载请注明出处。

来源:atyun_com

来源网址:http://atyun/12934_教程:利用tensorflow目标检测api确定图像中目标的位置.html

在Python 2.7即将停止支持时,我们为你准备了一份3.x迁移指南 企业视频课程

img

干尸

关注

机器之心编译

目前,Python 科学栈中的所有主要项目都同时支持 Python 3.x 和 Python 2.7,不过,这种情况很快即将结束。去年 11 月,Numpy 团队的一份声明引发了数据科学社区的关注:这一科学计算库即将放弃对于 Python 2.7 的支持,全面转向 Python 3。Numpy 并不是唯一宣称即将放弃 Python 旧版本支持的工具,pandas 与 Jupyter notebook 等很多产品也在即将放弃支持的名单之中。对于数据科学开发者而言,如何将已有项目从 Python 2 转向 Python 3 成为了正在面临的重大问题。来自莫斯科大学的 Alex Rogozhnikov 博士为我们整理了一份代码迁移指南。

Python 3 功能简介

Python 是机器学习和其他科学领域中的主流语言,我们通常需要使用它处理大量的数据。Python 兼容多种深度学习框架,且具备很多优秀的工具来执行数据预处理和可视化。

但是,Python 2 和 Python 3 长期共存于 Python 生态系统中,很多数据科学家仍然使用 Python 2。2019 年底,Numpy 等很多科学计算工具都将停止支持 Python 2,而 2018 年后 Numpy 的所有新功能版本将只支持 Python 3。

为了使 Python 2 向 Python 3 的转换更加轻松,我收集了一些 Python 3 的功能,希望对大家有用。

使用 pathlib 更好地处理路径

pathlib 是 Python 3 的默认模块,帮助避免使用大量的 os.path.joins:

from pathlib importPath

dataset ='wiki_images'

datasets_root =Path('/path/to/datasets/')

train_path = datasets_root / dataset /'train'

test_path = datasets_root / dataset /'test'

for image_path in train_path.iterdir():

with image_path.open()as f:# note, open is a method of Path object

# do something with an image

Python 2 总是试图使用字符串级联(准确,但不好),现在有了 pathlib,代码安全、准确、可读性强。

此外,pathlib.Path 具备大量方法,这样 Python 新用户就不用每个方法都去搜索了:

p.exists()

p.is_dir()

p.parts()

p.with_name('sibling.png')# only change the name, but keep the folder

p.with_suffix('.jpg')# only change the extension, but keep the folder and the name

p.chmod(mode)

p.rmdir()

pathlib 会节约大量时间,详见:

文档:https://docs.python.org/3/library/pathlib.html;

参考信息:https://pymotw/3/pathlib/。

类型提示(Type hinting)成为语言的一部分

PyCharm 中的类型提示示例:

Python 不只是适合脚本的语言,现在的数据流程还包括大量步骤,每一步都包括不同的框架(有时也包括不同的逻辑)。

类型提示被引入 Python,以帮助处理越来越复杂的项目,使机器可以更好地进行代码验证。而之前需要不同的模块使用自定义方式在文档字符串中指定类型(注意:PyCharm 可以将旧的文档字符串转换成新的类型提示)。

下列代码是一个简单示例,可以处理不同类型的数据(这就是我们喜欢 Python 数据栈之处)。

def repeat_each_entry(data):

""" Each entry in the data is doubled

"""

index = numpy.repeat(numpy.arange(len(data)),2)

return data[index]

上述代码适用于 numpy.array(包括多维)、astropy.Table 和 astropy.Column、bcolz、cupy、mxnet.ndarray 等。

该代码同样可用于 pandas.Series,但是方式是错误的:

repeat_each_entry(pandas.Series(data=[0,1,2], index=[3,4,5]))# returns Series with Nones inside

这是一个两行代码。想象一下复杂系统的行为多么难预测,有时一个函数就可能导致错误的行为。明确了解哪些类型方法适合大型系统很有帮助,它会在函数未得到此类参数时给出提醒。

def repeat_each_entry(data:Union[numpy.ndarray, bcolz.carray]):

如果你有一个很棒的代码库,类型提示工具如 MyPy 可能成为集成流程中的一部分。不幸的是,提示没有强大到足以为 ndarrays/tensors 提供细粒度类型,但是或许我们很快就可以拥有这样的提示工具了,这将是 DS 的伟大功能。

类型提示 → 运行时的类型检查

默认情况下,函数注释不会影响代码的运行,不过它也只能帮你指出代码的意图。

但是,你可以在运行时中使用 enforce 等工具强制进行类型检查,这可以帮助你调试代码(很多情况下类型提示不起作用)。

@enforce.runtime_validation

def foo(text: str)->None:

print(text)

foo('Hi')# ok

foo(5)# fails

@enforce.runtime_validation

def any2(x:List[bool])->bool:

return any(x)

any ([False,False,True,False])# True

any2([False,False,True,False])# True

any (['False'])# True

any2(['False'])# fails

any ([False,None,"",0])# False

any2([False,None,"",0])# fails

函数注释的其他用处

如前所述,注释不会影响代码执行,而且会提供一些元信息,你可以随意使用。

例如,计量单位是科学界的一个普遍难题,astropy 包提供一个简单的装饰器(Decorator)来控制输入量的计量单位,并将输出转换成所需单位。

# Python 3

from astropy import units as u

@u.quantity_input()

def frequency(speed: u.meter / u.s, wavelength: u.m)->u.terahertz:

return speed / wavelength

frequency(speed=300_000 * u.km / u.s, wavelength=555* u.nm)

# output: 540.5405405405404 THz, frequency of green visible light

如果你拥有 Python 表格式科学数据(不必要太多),你应该尝试一下 astropy。你还可以定义针对某个应用的装饰器,用同样的方式来控制/转换输入和输出。

通过 @ 实现矩阵乘法

下面,我们实现一个最简单的机器学习模型,即带 L2 正则化的线性回归:

# l2-regularized linear regression: || AX - b ||^2 + alpha * ||x||^2 ->min

# Python 2

X = np.linalg.inv(np.dot(A.T, A)+ alpha * np.eye(A.shape[1])).dot(A.T.dot(b))

# Python 3

X = np.linalg.inv(A.T @ A + alpha * np.eye(A.shape[1]))@(A.T @ b)

下面 Python 3 带有 @ 作为矩阵乘法的符号更具有可读性,且更容易在深度学习框架中转译:因为一些如 X @ W + b[None, :] 的代码在 numpy、cupy、pytorch 和 tensorflow 等不同库下都表示单层感知机。

使用 ** 作为通配符

递归文件夹的通配符在 Python2 中并不是很方便,因此才存在定制的 glob2 模块来克服这个问题。递归 flag 在 Python 3.6 中得到了支持。

import glob

# Python 2

found_images = \

glob.glob('/path*.jpg') \

+ glob.glob('/path*.jpg') \

+ glob.glob('/path***.jpg')

# Python 3

found_images = glob.glob('/path*.jpg', recursive=True)

python3 中更好的选择是使用 pathlib:

# Python 3

found_images = pathlib.Path('/path/').glob('**/*.jpg')

Print 在 Python3 中是函数

Python 3 中使用 Print 需要加上麻烦的圆括弧,但它还是有一些优点。

使用文件描述符的简单句法:

print>>sys.stderr,"critical error"# Python 2

print("critical error", file=sys.stderr)# Python 3

在不使用 str.join 下输出 tab-aligned 表格:

# Python 3

print(*array, sep='\t')

print(batch, epoch, loss, accuracy, time, sep='\t')

修改与重新定义 print 函数的输出:

# Python 3

_print =print# store the original print function

defprint(*args,**kargs):

pass# do something useful, e.g. store output to some file

在 Jupyter 中,非常好的一点是记录每一个输出到独立的文档,并在出现错误的时候追踪出现问题的文档,所以我们现在可以重写 print 函数了。

在下面的代码中,我们可以使用上下文管理器暂时重写 print 函数的行为:

@contextlib.contextmanager

def replace_print():

import builtins

_print =print# saving old print function

# or use some other function here

builtins.print=lambda*args,**kwargs: _print('new printing',*args,**kwargs)

yield

builtins.print= _print

with replace_print():

上面并不是一个推荐的方法,因为它会引起系统的不稳定。

print 函数可以加入列表解析和其它语言构建结构。

# Python 3

result = process(x)if is_valid(x)elseprint('invalid item: ', x)

f-strings 可作为简单和可靠的格式化

默认的格式化系统提供了一些灵活性,且在数据实验中不是必须的。但这样的代码对于任何修改要么太冗长,要么就会变得很零碎。而代表性的数据科学需要以固定的格式迭代地输出一些日志信息,通常需要使用的代码如下:

# Python 2

print('{batch:3} {epoch:3} / {total_epochs:3} accuracy: {acc_mean:0.4f}±{acc_std:0.4f} time: {avg_time:3.2f}'.format(

batch=batch, epoch=epoch, total_epochs=total_epochs,

acc_mean=numpy.mean(accuracies), acc_std=numpy.std(accuracies),

avg_time=time / len(data_batch)

))

# Python 2 (too error-prone during fast modifications, please avoid):

print('{:3} {:3} / {:3} accuracy: {:0.4f}±{:0.4f} time: {:3.2f}'.format(

batch, epoch, total_epochs, numpy.mean(accuracies), numpy.std(accuracies),

time / len(data_batch)

))

样本输出:

12012/300 accuracy:0.8180±0.4649 time:56.60

f-strings 即格式化字符串在 Python 3.6 中被引入:

# Python 3.6+

print(f'{batch:3} {epoch:3} / {total_epochs:3} accuracy: {numpy.mean(accuracies):0.4f}±{numpy.std(accuracies):0.4f} time: {time / len(data_batch):3.2f}')

另外,写查询语句时非常方便:

query = f"INSERT INTO STATION VALUES (13, '{city}', '{state}', {latitude}, {longitude})"

「true pision」和「integer pision」之间的明显区别

对于数据科学来说这种改变带来了便利(但我相信对于系统编程来说不是)。

data = pandas.read_csv('timing.csv')

velocity = data['distance']/ data['time']

Python 2 中的结果依赖于『时间』和『距离』(例如,以米和秒为单位)是否被保存为整数。

在 Python 3 中,结果的表示都是精确的,因为除法的结果是浮点数。

另一个案例是整数除法,现在已经作为明确的运算:

n_gifts = money // gift_price # correct for int and float arguments

注意,该运算可以应用到内建类型和由数据包(例如,numpy 或 pandas)提供的自定义类型。

严格排序

# All these comparisons are illegal in Python 3

3<'3'

2

(3,4)<(3,None)

(4,5)<[4,5]

# False in both Python 2 and Python 3

(4,5)==[4,5]

防止不同类型实例的偶然性的排序。

sorted([2,'1',3])# invalid for Python 3, in Python 2 returns [2, 3, '1']

在处理原始数据时帮助发现存在的问题。

旁注:对 None 的合适检查是(两个版本的 Python 都适用):

if a isnotNone:

pass

if a:# WRONG check for None

pass

自然语言处理的 Unicode

s ='您好'

print(len(s))

print(s[:2])

输出:

Python 2: 6\n

Python 3: 2\n 您好.

x = u'со'

x +='co'# ok

x +='со'# fail

Python 2 在此失败了,而 Python 3 可以如期工作(因为我在字符串中使用了俄文字母)。

在 Python 3 中 strs 是 Unicode 字符串,对非英语文本的 NLP 处理更加方便。

还有其它有趣的方面,例如:

'a'< type < u'a'# Python 2: True

'a'< u'a'# Python 2: False

from collections importCounter

Counter('Mbelstück')

Python 2: Counter({'\xc3': 2, 'b': 1, 'e': 1, 'c': 1, 'k': 1, 'M': 1, 'l': 1, 's': 1, 't': 1, '\xb6': 1, '\xbc': 1})

Python 3: Counter({'M': 1, '': 1, 'b': 1, 'e': 1, 'l': 1, 's': 1, 't': 1, 'ü': 1, 'c': 1, 'k': 1})

这些在 Python 2 里也能正确地工作,但 Python 3 更为友好。

保留词典和**kwargs 的顺序

在 CPython 3.6+ 版本中,字典的默认行为类似于 OrderedDict(在 3.7+版本中已得到保证)。这在字典理解(和其他操作如 json 序列化/反序列化期间)保持顺序。

import json

x ={str(i):i for i in range(5)}

json.loads(json.dumps(x))

# Python 2

{u'1':1, u'0':0, u'3':3, u'2':2, u'4':4}

# Python 3

{'0':0,'1':1,'2':2,'3':3,'4':4}

它同样适用于**kwargs(在 Python 3.6+版本中):它们的顺序就像参数中显示的那样。当设计数据流程时,顺序至关重要,以前,我们必须以这样繁琐的方式来编写:

from torch import nn

# Python 2

model = nn.Sequential(OrderedDict([

('conv1', nn.Conv2d(1,20,5)),

('relu1', nn.ReLU()),

('conv2', nn.Conv2d(20,64,5)),

('relu2', nn.ReLU())

]))

# Python 3.6+, how it *can* be done, not supported right now in pytorch

model = nn.Sequential(

conv1=nn.Conv2d(1,20,5),

relu1=nn.ReLU(),

conv2=nn.Conv2d(20,64,5),

relu2=nn.ReLU())

)

注意到了吗?名称的唯一性也会被自动检查。

迭代地拆封

# handy when amount of additional stored info may vary between experiments, but the same code can be used in all cases

model_paramteres, optimizer_parameters,*other_params = load(checkpoint_name)

# picking two last values from a sequence

*prev, next_to_last, last = values_history

# This also works with any iterables, so if you have a function that yields e.g. qualities,

# below is a simple way to take only last two values from a list

*prev, next_to_last, last = iter_train(args)

默认的 pickle 引擎为数组提供更好的压缩

# Python 2

import cPickle as pickle

import numpy

print len(pickle.dumps(numpy.random.normal(size=[1000,1000])))

# result: 23691675

# Python 3

import pickle

import numpy

len(pickle.dumps(numpy.random.normal(size=[1000,1000])))

# result: 8000162

节省 3 倍空间,而且速度更快。实际上,类似的压缩(不过与速度无关)可以通过 protocol=2 参数来实现,但是用户...

Python学习之路Django之ModelForm,快速理解入门及注意点(附源码) 流量视频课程

img

沛文

关注

一、ModelForm

自己定义的form--->Form--->BaseForm

自己定义的ModelForm--->ModelForm--->BaseModelForm--->BaseForm

从上面可以看出form和ModelForm都是继承BaseForm,所以在Form中有的方法在ModelForm中也是有的,包括is_valid(),cleaned_data,errors

下面是ModelForm的中Meta的使用方法

下面是关于Meta重要参数的使用的例子:

views.py中的代码为:

前端index.html中代码如下:

这样默认访问index页面效果如下:

关于labels参数

可以看出默认情况下输入框左边的lable显示的是列名,如果不在models.py的类中定义字段时添加verbose_name="用户名"参数,默认则是显示的列名,当然如果通过verbose_name="用户名"参数设定,则可以显示自定义的名字,当然这里如果不在models.py中定义的话,可以在ModelForm中通过lables参数指定,注意:lables后面的参数值是字典类型,代码如下:

这样当再次访问页面时效果如下:

关于help_texts参数

注意:help_texts后面的参数值是字典类型代码如下:

这样当再次访问页面时:

关于wigets参数

Modelform本身没有widgets,需要借助于forms,所以当在ModelForm中需要通过widgets参数自定义插件的时候,需要from django.forms import widgets as MFwidgets 这里通过as 将名字进行改变是因为和widgets参数冲突,注意:help_texts后面的参数值是字典类型,具体用法例子如下:

这样当再次访问页面时:

关于error_messages参数

error_messages用于自定义错误信息,注意:error_messages后面的参数值是字典类型,具体使用例子如下:

默认当点击页面提交时,页面的错误信息如下:

通过error_messages用于自定义错误信息后的代码如下:

这样当再次提交的时候,错误信息效果如下:

关于field_classes参数

在model.py的类中我们已经对字段类型进行了设置,当我们想在ModelForm中对字段类型进行修改的时候,首先需要导入from django.forms import fields as MFfields 这里通过通过as 改名防止冲突,注意:field_classes后面的参数值是字典类型,代码例子如下:

上述代码例子中将原本为email邮箱格式的字段更改为了URL字段

以上是关于生成html的用法,下面ModelForm在其他方面也可以方便

二、ModelForm对于数据库的操作

对多的数据的保存

我们将上述页面还原为如下:

我们需要实现的是当用户淑如用户名和邮箱以及选择用户类型后,点击提交后,将信息保存到数据库,并且这里实现了一对多数据库的数据的保存,我们需要将views.py中的index函数的代码进行更改:

这里用的是obj.save()方法,这样当页面信息正确后,点击提交就会将数据保存到数据库中

多对多数据的保存

在上面演示了通过obj.save()可以保存一对多的数据到数据库,同样的,也是可以将多对多数据保存到数据库

现在models.py文件中添加一个用户组的类,并创建多对多关系,修改后的代码如下:

这样页面显示效果如下:

这样当我们填写信息点击提交后,数据库中也就将数据保存在多对多关系

对obj.save()方法的详解

我们点击代码中obj后面的save,查看源码如下:

分析源码我们可以看出,默认参数commit=True,代码中if commit判断中,如果为commit=True,则执行self.instance.save()和self._save_m2m()

self.instance:为当前model的对象,所以可以保存当前表

Self._save_m2m():则表示保存多对多的数据

所以默认情况下commit=True,则当前表数据和多对多表的数据都会保存,当然在这里我们也可以进行拆分,如果commit=False,这个时候,else里只有self.save_m2m=self._save_m2m,进行了赋值,并没有执行任何操作,最终返回self.instance,下面演示拆开的代码例子:

修改后views.py中index函数:

这里instance=obj.save(False),instance.save()这样如果提交数据,就不会保存多对多表的数据,只保存当前表的数据,如果想要保存多对多的数据,则再添加obj.save_m2m()

所以这里我们可以看出

instance=obj.save(False)

instance.save()

obj.save_m2m()

就相当于obj.save()

关于select_related的一个知识点

当在页面中想要列出所有用户时:

Views.py中添加如下代码:

这里有个问题需要注意,这里用select_related时后面只能填写一对多的表跨表,不能填写多对多表进行跨表。

下面是对ModelForm使用的一个小例子:

Views.py中的代码为:

ModelForm.py中的代码为:

两个前端页面,user_edit.html和user_list.html

User_list.html代码如下:

user_edit.html代码如下:

当登陆用户列表页面时:

点击编辑:

上述代码中有几个重要的地方:

当点击编辑的时候,编辑页面会将点击的当前行的用户信息显示出来。

Views.py代码中,当用户通过get访问页面时,

mf= ModelForm.UserInfoModelForm(instance=user_obj)

这里通过instance参数将用户对象传入到ModelForm中,从而显示将当前行的用户信息显示到form页面。

当用户更改用户信息后,点击提交,则可以将更改的用户信息保存到数据库中

mf = ModelForm.UserInfoModelForm(request.POST,instance=user_obj)

这里首先传入request.POST参数,这里是将用户的信息提交,而将用户信息提交到数据库中哪一行,则需要通过instance参数将用户对象user_obj传入,这样当用户更改用户信息后,点击提交则可以将更改后的用户信息保存到数据库中相应的行,如果没有instance参数,则会创建一条新的数据在数据库中

注意:代码中用的is_valid() 这里同样和Form一样预留有钩子:

_clean_fields()

_clean_from()

_post_clean()

注意:在ModelForm中可以定义额外的字段

ModelForm小结:

1、生成HTML标签:在ModelForm类中的class Meta中定义

2、Mf =xxxModelForm(instance=Modelobj)

3、额外的标签

4、和Form中存在的验证,预留的钩子

_clean_fields()

_clean_from()

_post_clean()

5、mf.save()保存数据

通过传入False参数将mf.save()进行分开

Instance = mf.save(False)

Instance.save()

Mf.save_m2m()。

解读Keras在ImageNet中应用:详解5种图像识别模型 行业视频课程

img

袁青寒

关注

更多深度文章,请关注:https://yq.aliyun/cloud

几个月前,我写了一篇关于如何使用CNN(卷积神经网络)尤其是VGG16来分类图像的教程,该模型能够以很高的精确度识别我们日常生活中的1000种不同种类的物品。

那时,模型还是和Keras包分开的,我们得从free-standingGitHubrepo上下载并手动安装;现在模型已经整合进Keras包,原先的教程也已经不再适用,所以我决定写一篇新的教程。

在教程中,你将学习到如何编写一个Python脚本来分类你自己的图像。

博客结构

1.简要说明一下这几个网络架构;

2.使用Python编写代码:载入训练好的模型并对输入图像分类;

3.审查一些样本图片的分类结果。

Keras中最新的深度学习图像分类器

Keras提供了五种开箱即用型的CNN:

1.VGG16

2.VGG19

3.ResNet50

4.InceptionV3

5.Xception

什么是ImageNet

ImageNet曾是一个计算机视觉研究项目:(人工)打标签并分类成22000个不同物品种类。然而,当我们在讨论深度学习和CNN的时候,“ImageNet”意味着ImageNetLargeScaleVisualRecognitionChallenge,简写为ILSVRC。

ILSVRC的目的是训练一个能够正确识别图像并分类(1000种)的模型:模型使用约120万张图像用作训练,5万张图像用作验证,10万张图像用作测试。

这1000种分类涵盖了我们的日常生活接触到的东西,具体列表请点击。

在图像分类上,ImageNet竞赛已经是计算机视觉分类算法事实上的评价标准——而自2012年以来,排行榜就被CNN和其它深度学习技术所统治。

过去几年中ImageNet竞赛里表现优异的模型在Keras中均有收录。通过迁移学习,这些模型在ImageNet外的数据集中也有着不错的表现。

VGG16和VGG19

图1:VGG网络架构(source)

VGG网络架构于2014年出现在Simonyan和Zisserman中的论文中,《VeryDeepConvolutionalNetworksforLargeScaleImageRecognition》。

该架构仅仅使用堆放在彼此顶部、深度不断增加的3×3卷积层,并通过maxpooling来减小volume规格;然后是两个4096节点的全连接层,最后是一个softmax分类器。“16”和“19”代表网络中权重层的数量(表2中的D和E列):

在2014年的时候,16还有19层网络还是相当深的,Simonyan和Zisserman发现训练VGG16和VGG19很有难度,于是选择先训练小一些的版本(列A和列C)。这些小的网络收敛后被用来作为初始条件训练更大更深的网络——这个过程被称为预训练(pre-training)。

预训练很有意义,但是消耗大量时间、枯燥无味,在整个网络都被训练完成前无法进行下一步工作。

如今大部分情况下,我们已经不再使用预训练,转而采用Xaiver/Glorot初始化或者MSRA初始化(有时也被称作Heetal.初始化,详见《DelvingDeepintoRectifiers:SurpassingHuman-LevelPerformanceonImageNetClassification》)。如果你感兴趣,可以从这篇文章中理解到weightinitialization的重要性以及深度神经网络的收敛——《Allyouneedisagoodinit,MishkinandMatas(2015)》。

VGGNet有两个不足:

1.训练很慢;

2.weights很大。

由于深度以及全连接节点数量的原因,VGG16的weights超过533MB,VGG19超过574MB,这使得部署VGG很令人讨厌。虽然在许多深度学习图像分类问题中我们仍使用VGG架构,但是小规模的网络架构更受欢迎(比如SqueezeNet,GoogleNet等等)。

ResNet

与AlexNet、OverFeat还有VGG这些传统顺序型网络架构不同,ResNet的网络结构依赖于微架构模组(micro-architecturemodules,也被称为network-in-networkarchitectures)。

微架构模组指构成网络架构的“积木”,一系列的微架构积木(连同你的标准CONV,POOL等)共同构成了大的架构(即最终的网络)。

ResNet于2015年出现在Heetal的论文《DeepResidualLearningforImageRecognition》中,它的出现很有开创性意义,证明极深的网络也可以通过标准SGD(以及一个合理的初始化函数)来训练:

图3:Heetal.于2015年提出的残差模组

在2016年的著作《IdentityMappingsinDeepResidualNetworks》中,他们证实了可以通过更新残差模组(residualmodule)来使用标志映射(identifymappings),达到提高精度的目的。

图4:(左)原始残差模组(右)使用预激活(pre-activation)更新的残差模组

尽管ResNet比VGG16还有VGG19要深,weights却要小(102MB),因为使用了全局平均池化(globalaveragepooling),而不是全连接层。

InceptionV3

“Inception”微架构于2014年出现在Szegedy的论文中,《GoingDeeperwithConvolutions》。

图5:GoogleNet中使用的Inception模组原型

Inception模组的目的是扮演一个“多级特征提取器”,在网络相同的模组内计算1×1、3×3还有5×5的卷积——这些过滤器的输出在输入至网络下一层之前先被堆栈到channeldimension。

该架构的原型被称为GoogleNet,后继者被简单的命名为InceptionvN,N代表Google推出的数字。

Keras中的InceptionV3架构来自于Szegedyetal.的后续论文,《RethinkingtheInceptionArchitectureforComputerVision(2015)》,该论文打算通过更新inception模组来提高ImageNet分类的准确度。

InceptionV3比VGG还有ResNet都要小,约96MB。

Xception

图6:Xception架构

Xception是被FranoisChollet提出的,后者是Keras库的作者和主要维护者。

Xception是Inception架构的扩展,用depthwise独立卷积代替Inception标准卷积。

关于Xception的出版物《DeepLearningwithDepthwiseSeparableConvolutions》可以在这里找到。

Xception最小仅有91MB。

SqueezeNet

Figure7:“fire”模组,由一个“squeeze”和一个“expand”模组组成。(Iandolaetal.,2016)

仅仅4.9MB的SqueezeNet架构能达到AlexNet级别的精确度(~57%rank-1and~80%rank-5),这都归功于“fire”模组的使用。然而SqueezeNet的训练很麻烦,我会在即将出版的书——《DeepLearningforComputerVisionwithPython》——中介绍如何训练SqueezeNet来处理ImageNet数据集。

使用Python和Keras通过VGGNet,ResNet,Inception和Xception对图像分类

新建一个文件,命名为classify_image.py,编辑插入下列代码1#importthenecessarypackages2fromkeras.applicationsimportResNet503fromkeras.applicationsimportInceptionV34fromkeras.applicationsimportXception#TensorFlowONLY5fromkeras.applicationsimportVGG166fromkeras.applicationsimportVGG197fromkeras.applicationsimportimagenet_utils8fromkeras.applications.inception_v3importpreprocess_input9fromkeras.preprocessing.imageimportimg_to_array10fromkeras.preprocessing.imageimportload_img11importnumpyasnp12importargparse13importcv2

第2-13行导入需要的包,其中大部分都属于Keras。

第2-6行分别导入ResNet,InceptionV3,Xception,VGG16,还有VGG19——注意Xception只兼容TensorFlow后端。

第7行导入的image_utils包包含了一系列函数,使得对图片进行前处理以及对分类结果解码更加容易。

余下的语句导入其它有用的函数,其中NumPy用于数学运算,cv2用于与OpenCV结合。15#constructtheargumentparseandparsethearguments16ap=argparse.ArgumentParser()17ap.add_argument("-i","--image",required=True,18help="pathtotheinputimage")19ap.add_argument("-model","--model",type=str,default="vgg16",20help="nameofpre-trainednetworktouse")21args=vars(ap.parse_args())

--image为希望进行分类的图像的路径。

--model为选用的CNN的类别,默认为VGG16。23#defineadictionarythatmapsmodelnamestotheirclasses24#insideKeras25MODELS={26"vgg16":VGG16,27"vgg19":VGG19,28"inception":InceptionV3,29"xception":Xception,#TensorFlowONLY30"resnet":ResNet5031}3233#esnureavalidmodelnamewassuppliedviacommandlineargument34ifargs["model"]notinMODELS.keys():35raiseAssertionError("The--modelcommandlineargumentshould"36"beakeyinthe`MODELS`dictionary")

第25-31行定义了一个词典,将类映射到对应的模型名称。

如果没有在该词典中找到“--model”,就会报错。

输入一个图像到一个CNN中会返回一系列键值,包含标签及对应的概率。

ImageNet采用的图像尺寸一般为224×224,227×227,256×256,and299×299,但是并不是绝对。

VGG16,VGG19以及ResNet接受224×224的输入图像,而InceptionV3和Xception要求为299×299,如下代码所示:38#initializetheinputimageshape(224x224pixels)alongwith39#thepre-processingfunction(thismightneedtobechanged40#basedonwhichmodelweusetoclassifyourimage)41inputShape=(224,224)42preprocess=imagenet_utils.preprocess_input4344#ifweareusingtheInceptionV3orXceptionnetworks,thenwe45#needtosettheinputshapeto(299x299)[ratherthan(224x224)]46#anduseadifferentimageprocessingfunction47ifargs["model"]in("inception","xception"):48inputShape=(299,299)49preprocess=preprocess_input

这里我们初始化inputShape为224×224像素,初始化预处理函数为keras.preprocess_input——执行meansubtraction运算。

如果使用Inception或者Xception,inputShape需要改为299×299像素,预处理函数改为separatepre-processing函数。

下一步就是从磁盘载入网络架构的weights,并实例化模型:51#loadourthenetworkweightsfromdisk(NOTE:ifthisisthe52#firsttimeyouarerunningthisscriptforagivennetwork,the53#weightswillneedtobedownloadedfirst--dependingonwhich54#networkyouareusing,theweightscanbe90-575MB,sobe55#patient;theweightswillbecachedandsubsequentrunsofthis56#scriptwillbe*much*faster)57print("[INFO]loading{}...".format(args["model"]))58Network=MODELS[args["model"]]59model=Network(weights="imagenet")

注意:VGG16和VGG19的weights大于500MB,ResNet的约等于100MB,Inception和Xception的介于90-100MB之间。如果这是你第一次运行某个网络,这些weights会自动下载到你的磁盘。下载时间由你的网络速度决定,而且下载完成后,下一次运行代码不再需要重新下载。61#loadtheinputimageusingtheKerashelperutilitywhileensuring62#theimageisresizedto`inputShape`,therequiredinputdimensions63#fortheImageNetpre-trainednetwork64print("[INFO]loadingandpre-processingimage...")65image=load_img(args["image"],target_size=inputShape)66image=img_to_array(image)6768#ourinputimageisnowrepresentedasaNumPyarrayofshape69#(inputShape[0],inputShape[1],3)howeverweneedtoexpandthe70#dimensionbymakingtheshape(1,inputShape[0],inputShape[1],3)71#sowecanpassitthroughthenetwork72image=np.expand_dims(image,axis=0)7374#pre-processtheimageusingtheappropriatefunctionbasedonthe75#modelthathasbeenloaded(i.e.,meansubtraction,scaling,etc.)76image=preprocess(image)

第65行从磁盘载入输入图像,并使用提供的inputShape初始化图像的尺寸。

第66行将图像从PIL/Pillow实例转换成NumPy矩阵,矩阵的shape为(inputShape[0],inputShape[1],3)。

因为我们往往使用CNN来批量训练/分类图像,所以需要使用np.expand_dims在矩阵中添加一个额外的维度,如第72行所示;添加后矩阵shape为(1,inputShape[0],inputShape[1],3)。如果你忘记添加这个维度,当你的模型使用.predict时会报错。

最后,第76行使用合适的预处理函数来执行meansubtraction/scaling。

下面将我们的图像传递给网络并获取分类结果:78#classifytheimage79print("[INFO]classifyingimagewith'{}'...".format(args["model"]))80preds=model.predict(image)81P=imagenet_utils.decode_predictions(preds)8283#loopoverthepredictionsanddisplaytherank-5predictions+84#probabilitiestoourterminal85for(i,(imagenetID,label,prob))inenumerate(P[0]):86print("{}.{}:{:.2f}%".format(i+1,label,prob*100))

第80行调用.predict函数,并从CNN返回预测值。

第81行的.decode_predictions函数将预测值解码为易读的键值对:标签、以及该标签的概率。

第85行和86行返回最可能的5个预测值并输出到终端。

案例的最后一件事,是通过OpenCV从磁盘将输入图像读取出来,在图像上画出最可能的预测值并显示在我们的屏幕上。88#loadtheimageviaOpenCV,drawthetoppredictionontheimage,89#anddisplaytheimagetoourscreen90orig=cv2.imread(args["image"])91(imagenetID,label,prob)=P[0][0]92cv2.putText(orig,"Label:{},{:.2f}%".format(label,prob*100),93(10,30),cv2.FONT_HERSHEY_SIMPLEX,0.8,(0,0,255),2)94cv2.imshow("Classification",orig)95cv2.waitKey(0)

VGGNet,ResNet,Inception,和Xception的分类结果

所有的例子都是使用2.0以上版本的Keras以及TensorFlow后台做的。确保你的TensorFlow版本大于等于1.0,否则会报错。所有例子也都使用Theano后端做过测试,工作良好。

案例需要的图片以及代码请前往原文获取。

使用VGG16分类:1$pythonclassify_image.py--imageimages/soccer_ball.jpg--modelvgg16

图8:使用VGG16来分类足球(source)

输出为:soccer_ball,精确度为93.43%。

如果要使用VGG19,只需要替换下--network参数。1$pythonclassify_image.py--imageimages/bmw.png--modelvgg19

图9:使用VGG19来分类汽车(source)

输出为:convertible(敞篷车),精确度为91.76%。然而,我们看一下其它的4个结果:sportscar(跑车),4.98%(也对);limousine(豪华轿车),1.06%(不正确,但也合理);carwheel(车轮),0.75%(技术上正确,因为图中确实出现了轮子)。

从下面的例子,我们可以看到类似的结果:1$pythonclassify_image.py--imageimages/clint_eastwood.jpg--modelresnet

图10:使用ResNet分类(source).

ResNet成功将图像分类为revolver(左轮手枪),精确度69.79%。有趣的是rifle(步枪)为7.74%,assaultrifle(突击步枪)为5.63%。考虑到revolver的观察角度还有相对于手枪来说巨大的枪管,CNN得出这么高的概率也是合理的。1$pythonclassify_image.py--imageimages/jemma.png--modelresnet

图11:使用ResNet对狗进行分类

狗的种类被正确识别为beagle(小猎兔狗),精确度94.48%。

然后我试着分类《加勒比海盗》中的图片:1$pythonclassify_image.py--imageimages/boat.png--modelinception

图12:使用ResNet对沉船进行分类(source)

尽管ImageNet中有“boat”(船)这个类别,Inception网络仍然正确地将该场景识别为“(ship)wreck”(沉船),精确度96.29%。其它的标签,比如“seashore”(海滩),“canoe”(独木舟),“paddle”(桨),还有“breakwater”(...

img

TOP