页面树结构

版本比较

标识

  • 该行被添加。
  • 该行被删除。
  • 格式已经改变。
评论: 修正翻译文档
通过验证其对ImageNet的工作,研究人员已经证明了计算机视觉的稳步进展,这是计算机视觉 的学术基准。连续模式继续显示改进,每次实现新的最先进的结果: 

我们的大脑使视觉看起来很容易。人类不会分解一只狮子和一只美洲虎,看一个标志,或认出一个人的脸。但这些实际上是用计算机解决的难题:他们看起来很容易,因为我们的大脑非常好地理解图像。

近几年机器学习领域在解决这些困难问题上取得了巨大进步。特别地,我们发现一种称为深卷积神经网络的模型 可以在硬性视觉识别任务上实现合理的性能 - 匹配或超过某些领域的人类表现。

校对:Ngxin

我们的大脑视觉看上去很容易。人类可以毫不费力的辨别一只狮子和一只美洲虎,识别一个标志,或认出一个人的脸。但这些用计算机解决实际上是很困难的:因为我们的大脑能非常好地理解图像,所以这些事看起来很容易,。

近几年机器学习领域在解决这些困难问题上取得了巨大进步。特别地,我们发现一种称为深度卷积神经网络的模型 可以在难度较高的视觉识别任务上达到可接受的性能 - 达到或超过某些领域的人类表现。

研究人员通过在ImageNet上试验验证,计算机视觉已取得稳步进展,ImageNet是计算机视觉的一个学术基准。模型一直在被改善,每次都能达到当前最高水准的结果: QuocNetAlexNetInception(GoogLeNet)BN-Inception-v2。Google内部和外部的研究人员发表了描述所有这些模型的论文,但结果仍难以重现。我们现在正在采取下一步,发布在最新型号。Google内部和外部的研究人员发表了描述这些模型的论文,但结果仍难以重现。我们下一步打算开源在最新模型Inception-v3上运行图像识别的代码。

Inception-v3 使用2012年的数据对v3 对ImageNet大型视觉识别挑战进行了培训。这是计算机视觉中的一项标准任务,其中模型尝试将整个图像分为大型视觉识别挑战在2012年采用的数据集进行训练。这是计算机视觉中的一项标准任务,其中模型尝试将整个图像分为1000个类,如“斑马”,“达尔马提亚”和“洗碗机”。例如,以下是,如“Zebra”,“Dalmatian”和“Dishwasher”。例如,以下是AlexNet对一些图像进行分类的结果:

为了比较模型,我们检查了模型无法预测正确答案作为他们前5个猜测之一的频率 - 称为“前5个错误率”。 AlexNet通过在2012年验证数据集上设置了15%的前5个错误率来实现; 初创(GoogLeNet)为了比较模型,我们将模型预测的前5个最有可能是正确答案的错误率 - 称为“top-5 error rate”。 AlexNet在2012年验证数据集上的top-5 error rate达到15.3%; Inception (GoogLeNet)达到6.67%; BN-Inception-v2达到4.9%; 初创v3Inception-v3 达到3.46%。

人类对ImageNet挑战有多好?Andrej 人类对ImageNet挑战精度提升还能做得多好?Andrej Karpathy 发表一篇博客,他试图衡量自己的表现。他达到了5.1%的前五名错误率。,他试图测量其性能。他达到了5.1%的 top-5 error rate。

本教程将教你如何使用Inception-v3。您将学习如何使用Python或C ++ 将图像分类为1000个类。我们还将讨论如何从此模型中提取更高级别的功能,这些功能可能被重用于其他视觉任务。。我们还将讨论如何从此模型中提取更高级别的features,这些features可能被重新用于其他视觉任务。

我们很高兴看到社区将如何处理这种模式。我们很高兴看到社区人员将用这个模型去实现什么。

 

使用Python API

classify_image.py从tensorflow在程序第一次运行时从tensorflow.org 第一次运行程序时下载训练有素的模型。您的硬盘上可能需要大约200M的可用空间。org下载训练模型。您的硬盘上可能需要大约200M的可用空间。

首先从GitHub 克隆TensorFlow模型回购TensorFlow models repo。运行以下命令:

代码块
languagepy
cd models/tutorials/image/imagenet
python classify_image.py

上述命令将对所提供的熊猫的图像进行分类。

如果模型运行正确,脚本将产生以下输出:

代码块
languagepy
giant panda, panda, panda bear, coon bear, Ailuropoda melanoleuca (score = 0.88493)
indri, indris, Indri indri, Indri brevicaudatus (score = 0.00878)
lesser panda, red panda, panda, bear cat, cat bear, Ailurus fulgens (score = 0.00317)
custard apple (score = 0.00149)
earthstar (score = 0.00127)

如果您想提供其他JPEG图像,您可以通过编辑--image_file参数来实现。

如果将模型数据下载到不同的目录,则需要指向--model_dir 使用的目录。

 

使用C ++ API

您可以在C ++ 中运行相同的Inception-v3模型,以便在生产环境中使用。您可以下载包含定义模型的GraphDef的存档(从TensorFlow存储库的根目录运行):模型,以便在生产环境(production environments)中使用。您可以下载包含定义模型的GraphDef的存档(从TensorFlow存储库的根目录运行):

代码块
languagepy
curl -L "https://storage.googleapis.com/download.tensorflow.org/models/inception_v3_2016_08_28_frozen.pb.tar.gz" |
tar -C tensorflow/examples/label_image/data -xz 
接下来,我们需要编译包含加载和运行图形代码的C接下来,我们需要编译包含加载和运行图代码的C ++二进制文件。如果您按照 说明下载适用 于您的平台的TensorFlow的源代码安装,您应该能够通过从您的shell终端运行此命令来构建该示例:二进制文件。如果您已经在开发平台按照 源代码安装TensorFlow的说明安装好tensorflow,您应该能够
通过从您的shell终端运行此命令来构建该示例:
代码块
languagepy
bazel build tensorflow/examples/label_image/...
那应该创建一个二进制可执行文件,然后你可以这样运行:创建一个二进制可执行文件,然后你可以这样运行:
代码块
languagepy
bazel-bin/tensorflow/examples/label_image/label_image 
这使用框架附带的默认示例图像,并应输出类似于此的内容:
代码块
languagepy
I tensorflow/examples/label_image/main.cc:206] military uniform (653): 0.834306
I tensorflow/examples/label_image/main.cc:206] mortarboard (668): 0.0218692
I tensorflow/examples/label_image/main.cc:206] academic gown (401): 0.0103579
I tensorflow/examples/label_image/main.cc:206] pickelhaube (716): 0.00800814
I tensorflow/examples/label_image/main.cc:206] bulletproof vest (466): 0.00535088
在这种情况下,我们正在使用海军上将魔法师的默认图像 ,您可以看到网络正确识别她穿着军装,得分高达0在这种情况下,我们正在使用默认的Admiral Grace Hopper图像 ,您可以看到网络正确识别她穿着军装,得分高达0.8。

接下来,通过提供接下来,尝试在自己的图像应用--image =参数,例如,尝试在自己的图像上参数,例如,上

代码块
languagepy
bazel-bin/tensorflow/examples/label_image/label_image --image=my_image.png
如果您查看tensorflow/examples/label_image/main.cc 文件内容,可以了解它的工作原理。我们希望这段代码可以帮助您将TensorFlow集成到您自己的应用程序中,因此我们将逐步介绍主要功能: 文件内容,可以了解它的工作原理。我们希望这段代码可以帮助您将TensorFlow集成到您自己
命令行标志控制文件的加载位置以及输入图像的属性。该模型希望获得299x299的RGB图像,所以这些是
的应用程序中,因此我们将逐步介绍主要功能:

命令行标志控制从哪加载的文件以及输入图像的属性。该模型希望获得299x299的RGB图像,这是input_widthinput_height标志。我们还需要将从0到255之间的整数的像素值缩放到图形运算的浮点值。我们使用input_meaninput_std标志控制缩放:我们首先标志控制缩放:我们首先从每个像素值中减去input_mean从每个像素值中减去 ,然后除以 ,然后除以input_std

这些值可能看起来有点神奇,但是它们只是由原始模型作者根据他这些值可能看起来有点奇怪,但是它们只是由原始模型的作者根据他/她想用作输入图像进行培训而定义的。如果您有一个自己训练过的图表,那么您只需要调整这些值,使其与您在培训过程中使用的任何值相匹配。她想用输入图像进行训练而定义的。如果您有一个自己训练过的图表,那么您只需要调整这些值,使其与您在训练过程中使用的任何值相匹配。

您可以看到它们如何应用于您可以看到它们在ReadTensorFromImageFile() 函数中的图像 。 函数中如何应用图像 。

代码块
languagepy
// Given an image file name, read in the data, try to decode it as an image,
// resize it to the requested size, and then scale the values as desired.
Status ReadTensorFromImageFile(string file_name, const int input_height,
                               const int input_width, const float input_mean,
                               const float input_std,
                               std::vector<Tensor>* out_tensors) {
  tensorflow::GraphDefBuilder b;
我们首先创建一个GraphDefBuilder可以用来指定运行或加载的模型的对象。
代码块
languagepy
  string input_name = "file_reader";
  string output_name = "normalized";
  tensorflow::Node* file_reader =
      tensorflow::ops::ReadFile(tensorflow::ops::Const(file_name, b.opts()),
                                b.opts().WithName(input_name));
然后,我们开始创建我们要运行的小型模型的节点,以加载,调整大小和缩放像素值,以获得主模型期望作为其输入的结果。我们创建的第一个节点只是一个Constop,它包含我们要加载的图像的文件名的张量。那就是作为第一个输入ReadFile。您可能会注意到我们然后,我们开始创建我们要运行的小型模型的节点,通过加载、调整大小和缩放像素值,以获得主模型期望作为输入的结果。我们创建的第一个节点只
是一个Const操作,它包含我们要加载的图片文件名的张量。把第一个输入传给ReadFile操作。您可能会注意到我们把b.opts()作为最后一个参数传递给所有op创建函数。该参数确保将节点添加到模型定义中GraphDefBuilder。我们也打电话给ReadFile 运营商。这给了一个节点的名字,这并不是绝对必要的,因为如果你不这样做,就会分配一个自动的名字,但这样会使调试变得更容易一些。作为最后一个参数传递给
所有操作创建的函数。该参数确保在 GraphDefBuilder 中,将节点添加到模型定义中
。我们通过给 WithName() 传入  b.opts() 命名ReadFile 操作。
这并不是必要的,因为如果你不这样做,就会分配一个自动的名字,但这样做会使调试变得更容易一些。
 
代码块
languagepy
  // Now try to figure out what kind of file it is and decode it.
  const int wanted_channels = 3;
  tensorflow::Node* image_reader;
  if (tensorflow::StringPiece(file_name).ends_with(".png")) {
    image_reader = tensorflow::ops::DecodePng(
        file_reader,
        b.opts().WithAttr("channels", wanted_channels).WithName("png_reader"));
  } else {
    // Assume if it's not a PNG then it must be a JPEG.
    image_reader = tensorflow::ops::DecodeJpeg(
        file_reader,
        b.opts().WithAttr("channels", wanted_channels).WithName("jpeg_reader"));
  }
  // Now cast the image data to float so we can do normal math on it.
  tensorflow::Node* float_caster = tensorflow::ops::Cast(
      image_reader, tensorflow::DT_FLOAT, b.opts().WithName("float_caster"));
  // The convention for image ops in TensorFlow is that all images are expected
  // to be in batches, so that they're four-dimensional arrays with indices of
  // [batch, height, width, channel]. Because we only have a single image, we
  // have to add a batch dimension of 1 to the start with ExpandDims().
  tensorflow::Node* dims_expander = tensorflow::ops::ExpandDims(
      float_caster, tensorflow::ops::Const(0, b.opts()), b.opts());
  // Bilinearly resize the image to fit the required dimensions.
  tensorflow::Node* resized = tensorflow::ops::ResizeBilinear(
      dims_expander, tensorflow::ops::Const({input_height, input_width},
                                            b.opts().WithName("size")),
      b.opts());
  // Subtract the mean and divide by the scale.
  tensorflow::ops::Div(
      tensorflow::ops::Sub(
          resized, tensorflow::ops::Const({input_mean}, b.opts()), b.opts()),
      tensorflow::ops::Const({input_std}, b.opts()),
      b.opts().WithName(output_name));
然后,我们继续添加更多的节点,将文件数据解码为图像,将整数转换为浮点值,调整其大小,然后最终对像素值执行减法和除法运算。
代码块
languagepy
  // This runs the GraphDef network definition that we've just constructed, and
  // returns the results in the output tensor.
  tensorflow::GraphDef graph;
  TF_RETURN_IF_ERROR(b.ToGraphDef(&graph)); 
最后,我们有一个存储在b变量中的模型定义,我们将其转换成具有该ToGraphDef()函数的完整图形定义。
代码块
languagepy
  std::unique_ptr<tensorflow::Session> session(
      tensorflow::NewSession(tensorflow::SessionOptions()));
  TF_RETURN_IF_ERROR(session->Create(graph));
  TF_RETURN_IF_ERROR(session->Run({}, {output_name}, {}, out_tensors));
  return Status::OK();
然后我们创建一个tf.Session 对象,它是实际运行图形的接口,并运行它,指定要获取输出的节点以及输出数据的位置。

这给我们一个给我们一个Tensor对象的向量,在这种情况下,我们知道只有一个对象很长。对象的向量,在这种情况下,我们知道只有一个long对象。在这种情况下,您可以将Tensor在这种情况下,您可以将其视为多维数组,并将其作为浮点值保存299像素高,299像素宽,3通道图像。如果您已经在产品中拥有自己的图像处理框架,那么只要在将图像输入主图形之前应用相同的变换即可使用。视为多维数组,并将299像素高,299像素宽,3通道的图像作为浮点值。如果您已经拥有自己的图像处理框架,那么只要在将图像输入主图形之前应用相同的变换即可使用。

这是一个在C ++中动态创建小TensorFlow图的简单示例,但是对于预先训练的Inception模型,我们要从文件中加载更大的定义。你可以看到我们如何在中动态创建小型TensorFlow图的简单示例,但是我们要从文件中加载更大的预训练的Inception模型。你可以看看我们在LoadGraph()函数中这样做。函数中是如何做的。

代码块
languagepy
// Reads a model graph definition from disk, and creates a session object you
// can use to run it.
Status LoadGraph(string graph_file_name,
                 std::unique_ptr<tensorflow::Session>* session) {
  tensorflow::GraphDef graph_def;
  Status load_graph_status =
      ReadBinaryProto(tensorflow::Env::Default(), graph_file_name, &graph_def);
  if (!load_graph_status.ok()) {
    return tensorflow::errors::NotFound("Failed to load compute graph at '",
                                        graph_file_name, "'");
  }
如果您浏览了图像加载代码,则很多术语应该看起来很熟悉。而不是使用a 如果您浏览了图像加载代码,那么很多术语应该看起来很熟悉。而不是使用GraphDefBuilder来生成一个GraphDef对象,我们加载一个直接包含的protobuf文件对象,我们加载一个包含GraphDef的protobuf文
代码块
languagepy
  session->reset(tensorflow::NewSession(tensorflow::SessionOptions()));
  Status session_create_status = (*session)->Create(graph_def);
  if (!session_create_status.ok()) {
    return session_create_status;
  }
  return Status::OK();
}
然后我们创建一个Session对象,然后我们从GraphDef 创建一个Session对象,并将其传递给调用者,以便以后可以运行它。

GetTopLabels()功能非常像图像加载,除了在这种情况下,我们要获取运行主图的结果,并将其转换为最高评分标签的排序列表。就像图像加载器一样,它创建一个 函数非常像图像加载,除了我们要获取运行主图的结果,并将其转换为最高评分标签的排序列表。就像图像加载器一样,它创建一个 GraphDefBuilder,添加了几个节点,然后运行短图来获得一对输出张量。在这种情况下,它们表示最高结果的排序分数和索引位置。

代码块
languagepy
// Analyzes the output of the Inception graph to retrieve the highest scores and
// their positions in the tensor, which correspond to categories.
Status GetTopLabels(const std::vector<Tensor>& outputs, int how_many_labels,
                    Tensor* indices, Tensor* scores) {
  tensorflow::GraphDefBuilder b;
  string output_name = "top_k";
  tensorflow::ops::TopK(tensorflow::ops::Const(outputs[0], b.opts()),
                        how_many_labels, b.opts().WithName(output_name));
  // This runs the GraphDef network definition that we've just constructed, and
  // returns the results in the output tensors.
  tensorflow::GraphDef graph;
  TF_RETURN_IF_ERROR(b.ToGraphDef(&graph));
  std::unique_ptr<tensorflow::Session> session(
      tensorflow::NewSession(tensorflow::SessionOptions()));
  TF_RETURN_IF_ERROR(session->Create(graph));
  // The TopK node returns two outputs, the scores and their original indices,
  // so we have to append :0 and :1 to specify them both.
  std::vector<Tensor> out_tensors;
  TF_RETURN_IF_ERROR(session->Run({}, {output_name + ":0", output_name + ":1"},
                                  {}, &out_tensors));
  *scores = out_tensors[0];
  *indices = out_tensors[1];
  return Status::OK();
PrintTopLabels()功能采用这些排序结果,并以友好的方式打印出来。该函数接受这些排序结果,并以用户友好的方式打印出来。CheckTopLabel()功能非常相似,但只是确保顶级标签是我们期望的标签,用于调试目的。函数非常相似,但只是确保顶级标签是我们期望的标签,以便
用于调试。
最后,main()
 将所有这些电话联系在一起。
 将所有函数调用联系在一起。
代码块
languagepy
int main(int argc, char* argv[]) {
  // We need to call this to set up global state for TensorFlow.
  tensorflow::port::InitMain(argv[0], &argc, &argv);
  Status s = tensorflow::ParseCommandLineFlags(&argc, argv);
  if (!s.ok()) {
    LOG(ERROR) << "Error parsing command line flags: " << s.ToString();
    return -1;
  }

  // First we load and initialize the model.
  std::unique_ptr<tensorflow::Session> session;
  string graph_path = tensorflow::io::JoinPath(FLAGS_root_dir, FLAGS_graph);
  Status load_graph_status = LoadGraph(graph_path, &session);
  if (!load_graph_status.ok()) {
    LOG(ERROR) << load_graph_status;
    return -1;
  }
我们加载主图。
代码块
languagepy
  // Get the image from disk as a float array of numbers, resized and normalized
  // to the specifications the main graph expects.
  std::vector<Tensor> resized_tensors;
  string image_path = tensorflow::io::JoinPath(FLAGS_root_dir, FLAGS_image);
  Status read_tensor_status = ReadTensorFromImageFile(
      image_path, FLAGS_input_height, FLAGS_input_width, FLAGS_input_mean,
      FLAGS_input_std, &resized_tensors);
  if (!read_tensor_status.ok()) {
    LOG(ERROR) << read_tensor_status;
    return -1;
  }
  const Tensor& resized_tensor = resized_tensors[0];
加载,调整大小并处理输入图像。
代码块
languagepy
  // Actually run the image through the model.
  std::vector<Tensor> outputs;
  Status run_status = session->Run({ {FLAGS_input_layer, resized_tensor}},
                                   {FLAGS_output_layer}, {}, &outputs);
  if (!run_status.ok()) {
    LOG(ERROR) << "Running model failed: " << run_status;
    return -1;
  }
在这里,我们运行加载的图形作为输入图像。
代码块
languagepy
  // This is for automated testing to make sure we get the expected result with
  // the default settings. We know that label 866 (military uniform) should be
  // the top label for the Admiral Hopper image.
  if (FLAGS_self_test) {
    bool expected_matches;
    Status check_status = CheckTopLabel(outputs, 866, &expected_matches);
    if (!check_status.ok()) {
      LOG(ERROR) << "Running check failed: " << check_status;
      return -1;
    }
    if (!expected_matches) {
      LOG(ERROR) << "Self-test failed!";
      return -1;
    }
  } 
为了测试的目的,我们可以检查以确保我们在这里得到我们预期的输出。为了测试,我们可以检查以确保我们在这里得到我们预期的输出。
代码块
languagepy
  // Do something interesting with the results we've generated.
  Status print_status = PrintTopLabels(outputs, FLAGS_labels);
最后我们打印出我们发现的标签。最后我们打印出标签。
代码块
languagepy
  if (!print_status.ok()) {
    LOG(ERROR) << "Running print failed: " << print_status;
    return -1;
  } 
这里的错误处理是使用TensorFlow的Status 对象,这是非常方便的,因为它可以让您知道ok()检查器是否发生任何错误,然后可以打印出来,提供可读的错误消息。检查器是否发生任何错误,然后可以打印出来,

在这种情况下,我们正在演示对象识别,但是您应该可以在各种领域中使用与您已经找到或训练过的其他型号相似的代码。我们希望这个小例子为您提供如何在您自己的产品中使用TensorFlow的一些想法。

练习:转移学习是一个想法,如果你知道如何解决一个很好的任务,你应该能够转移一些理解来解决相关的问题。执行传输学习的一种方法是去除网络的最终分类层,并提取CNN下一层(最后一层),在这种情况下为2048维向量。在how-to部分中有一个指导。
使错误消息更具可读性。

本例中,我们在演示对象识别,但是您应该可以在各种领域中使用与您已经找到或训练过的其他模型相似的代码。我们希望这个小例子为您提供如何在您自己的平台上使用TensorFlow的一些想法。

EXERCISE:迁移学习是一个想法,如果你知道如何很好的解决一个任务,你应该能够转移一些理解来解决相关的问题。迁移学习的一种方法是去除网络的最后一个分类层,并提取next-to-last layer of the CNN,在这种情况下为2048维向量。 在 how-to section中有一个关于这样操作的指导。

学习资源更多

要了解一般的神经网络,Michael Nielsen的 免费在线书籍 是一个很好的资源。对于卷积神经网络,克里斯·奥拉(Chris Nielsen的 free online book是一个很好的资源。对于卷积神经网络,克里斯·奥拉(Chris Olah)有一些 不错的博客文章,迈克尔·尼尔森(Michael Nielsen)的着作 涵盖了 Nielsen)的书中 也有很多章节涵盖了这些。

要了解有关实现卷积神经网络的更多信息,您可以跳转到TensorFlow 深卷积网络教程,或者使用,或者参考ML初学者ML专家 MNIST启动器教程轻轻点 一下。最后,如果你想加快在这方面的研究速度,你可以阅读本教程中引用的所有论文的最新作品。ML进阶 等关于MNIST开始的教程。最后,如果你想加快在这方面的研究速度,你可以阅读本教程中引用的所有论文的最新作品。